pythonpyqtredrawqdialognon-modal

QDialog switch to non-modal after accept not redrawing


So, I'm not sure if the title is the best description, but it's what I came up with. Here's the deal. I'm working on a PyQt app that has a sort of plugin system where you can just add some sub classes to a folder and the app finds them. These commands have the option of being able to create little uis for themselves. Basically, they look like this:

class Command(object):
    def do(self):
        self.setupUi()
        self.pre()
        self.run()
        self.post()

    def pre(self):
        # do setup stuff for run method

    def run(self):
        # do actual work

    def post(self):
        # clean up after run

    def setupUi(self):
        # create a ui for this command
        diag = QDialog()
        diag.exec_()

Now, the issue I'm running into is, I have one Command that creates a dialog, and waits for the user to accept it. Then, I need to switch the dialog to non-modal while the command is running, and up date the dialog. This all seems to work fine. But, the problem is I can't get the dialog to redraw until after the pre, run, and post methods have finished. So, if I have the setupUi like this:

def setupUi(self):
    # create a ui for this command
    diag = QDialog()
    if diag.exec_():
        diag.setModal(False)
        diag.show()

I tried processEvents but that didn't seem to do it. Has anyone else run into this issue, or know of any work arounds?

Thanks


Solution

  • Using diag.exec_() will block until the dialog returns (is closed). So, if you will need to call show() on it's own. There are a few ways to proceed from here.

    1. You can have the dialog accept slot run a reference to the rest of the commands
    2. You can poll the dialog to see if the user has accepted
    3. You can move the pre, run, and post commands to the dialog

    Assuming you want to keep the meat of the code out of the dialog class, and since periodically polling is best to avoid if possible, here is an example of the first strategy:

    import sys, time
    
    from PyQt4 import QtCore, QtGui
    
    class MyDialog(QtGui.QDialog):
        def __init__(self,parent=None):
            QtGui.QDialog.__init__(self,parent)
            layout = QtGui.QVBoxLayout()
            self.msg = QtGui.QLabel('some sort of status')
            self.buttonbox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok, QtCore.Qt.Horizontal, self)
            self.buttonbox.accepted.connect(self.accept)
            layout.addWidget(self.msg)
            layout.addWidget(self.buttonbox)
            self.setLayout(layout)
    
        def set_msg(self, new_msg):
            self.msg.setText(new_msg)
    
        def set_function_on_accept(self,fcn):
            self.function = fcn
    
        def accept(self):
            self.function()
    
    
    class Command(object):
        def do(self):
            self.setupUi()
    
        def do_work(self):
            self.pre()
            self.run()
            self.post()
    
        def pre(self):
            # do setup stuff for run method
            time.sleep(1)
            self.diag.set_msg("stuff setup")
            QtGui.QApplication.processEvents()
    
        def run(self):
            # do actual work
            time.sleep(1)
            self.diag.set_msg("work done")
            QtGui.QApplication.processEvents()
    
        def post(self):
            # clean up after run
            time.sleep(1)
            self.diag.set_msg("cleaned up")
            QtGui.QApplication.processEvents()
    
        def setupUi(self):
            # create a ui for this command
            diag = MyDialog()
            self.diag = diag
            diag.set_function_on_accept(self.do_work)
            diag.show()
    
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        command = Command()
        command.do()
        sys.exit(app.exec_())