Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Modeless dialog problem
QtWS25 Last Chance

Modeless dialog problem

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 3 Posters 2.0k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    JonB
    wrote on 30 May 2019, 12:23 last edited by JonB
    #1

    Qt 5.9. Tested under Linux. Target both Linux & Windows (which I cannot test). Hopefully none of this is actually relevant.

    I have a problem whereby a modeless dialog is getting put behind another dialog/window when I intend it to be 100% independent/top-level/free to be clicked to be either behind or on top, like any independent window.

    1. Main window creates QDialog (call it modalDialog) with itself (main window) as parent, and displays it via exec(). Correctly produces a modal dialog:
      0_1559218470470_Screenshot from 2019-05-30 13-12-22.png

    2. That modal dialog creates another QDialog (call it modelessDialog) with None/nullptr as parent, and displays it via show(). Produces a modeless dialog, but...:
      0_1559218533906_Screenshot from 2019-05-30 13-15-17.png
      See how the modeless dialog is behind the main window/currently displayed modal dialog? If I click on the modeless, it briefly brings it up top but then immediately brings the main window/modal dialog back on top of the modeless (this may be Linux/GNOME behaviour, I don't know). It's as though while inside an exec() something checks and brings it back on top if something else is clicked to bring that on top?? That is not what I want: I wish the main+modal to be 100% independent of the modeless, I should be able to click either to be on top as I please.

    3. On a whim, I click the X to close the modal dialog. Now the modeless dialog is indeed fully independent of the main window, and I can click either to bring up-front.
      0_1559218601667_Screenshot from 2019-05-30 13-16-21.png
      This is the behaviour I want, but cannot achieve, even when a modal dialog is currently shown with an exec().

    If anyone wants, here is about the minimal code I am using. See my comments about parent vs no parent and exec() vs show().

    import sys
    from PyQt5 import QtWidgets
    
    
    class Main(QtWidgets.QMainWindow):
        def __init__(self):
            super().__init__()
    
            self.setWindowTitle("Main")
            self.setGeometry(100, 100, 500, 500)
    
            self.btnModal = QtWidgets.QPushButton("Open Modal", self)
            self.btnModal.clicked.connect(self.openModal)
    
        def openModal(self):
            # Note: dlgModal has parent QMainWindow
            self.dlgModal = QtWidgets.QDialog(self)
            self.dlgModal.setWindowTitle("Modal Dialog")
            self.dlgModal.setFixedSize(400, 400)
    
            self.dlgModal.btnModeless = QtWidgets.QPushButton("Open Modeless", self.dlgModal)
            self.dlgModal.btnModeless.clicked.connect(self.openModeless)
    
            # Note: dlgModal is opened modal via exec()
            # If I replace `exec()` with `show()` I do not get unwanted behaviour
            # but I do want this invoking dialog to be modal
            self.dlgModal.exec()
    
        def openModeless(self):
            # Note: dlgModal has parent None
            self.dlgModeless = QtWidgets.QDialog(None)
            self.dlgModeless.setWindowTitle("Modeless Dialog")
            self.dlgModeless.setFixedSize(300, 300)
    
            # Note: dlgModeless is opened modeless via show()
            # It should be "fully independent of anything else", but it is not...?
            self.dlgModeless.show()
    
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
    
        main = Main()
        main.show()
    
        sys.exit(app.exec_())
    
    

    If I replace the self.dlgModal.exec() by self.dlgModal.show() I do not get the unwanted up-fronting behaviour, the modeless can go on top of it. But I do want the invoking dialog to be modal....

    S 1 Reply Last reply 30 May 2019, 16:29
    0
    • J Offline
      J Offline
      JonB
      wrote on 6 Jun 2019, 11:06 last edited by
      #8

      I didn't really get a perfect answer. An application-modeless dialog still gets blocked if you open an application-modal dialog, which is not what I want, but there it is. I do not want to change all my application's modal dialogs to be parent-window-modal to solve.

      To move on I am redesigning so that when the modeless is opened all the modals get closed now. It'll have to do, it's time for me to close this issue now....

      1 Reply Last reply
      0
      • K Offline
        K Offline
        Kent-Dorfman
        wrote on 30 May 2019, 16:24 last edited by Kent-Dorfman
        #2

        IIRC, modal dialogs always have focus, so are always on top. Also, I believe that exec() is the correct way to run a modal dialog, as the modal dialog has its own event loop.

        What you are experiencing is the expected behaviour.

        J 1 Reply Last reply 30 May 2019, 16:41
        1
        • J JonB
          30 May 2019, 12:23

          Qt 5.9. Tested under Linux. Target both Linux & Windows (which I cannot test). Hopefully none of this is actually relevant.

          I have a problem whereby a modeless dialog is getting put behind another dialog/window when I intend it to be 100% independent/top-level/free to be clicked to be either behind or on top, like any independent window.

          1. Main window creates QDialog (call it modalDialog) with itself (main window) as parent, and displays it via exec(). Correctly produces a modal dialog:
            0_1559218470470_Screenshot from 2019-05-30 13-12-22.png

          2. That modal dialog creates another QDialog (call it modelessDialog) with None/nullptr as parent, and displays it via show(). Produces a modeless dialog, but...:
            0_1559218533906_Screenshot from 2019-05-30 13-15-17.png
            See how the modeless dialog is behind the main window/currently displayed modal dialog? If I click on the modeless, it briefly brings it up top but then immediately brings the main window/modal dialog back on top of the modeless (this may be Linux/GNOME behaviour, I don't know). It's as though while inside an exec() something checks and brings it back on top if something else is clicked to bring that on top?? That is not what I want: I wish the main+modal to be 100% independent of the modeless, I should be able to click either to be on top as I please.

          3. On a whim, I click the X to close the modal dialog. Now the modeless dialog is indeed fully independent of the main window, and I can click either to bring up-front.
            0_1559218601667_Screenshot from 2019-05-30 13-16-21.png
            This is the behaviour I want, but cannot achieve, even when a modal dialog is currently shown with an exec().

          If anyone wants, here is about the minimal code I am using. See my comments about parent vs no parent and exec() vs show().

          import sys
          from PyQt5 import QtWidgets
          
          
          class Main(QtWidgets.QMainWindow):
              def __init__(self):
                  super().__init__()
          
                  self.setWindowTitle("Main")
                  self.setGeometry(100, 100, 500, 500)
          
                  self.btnModal = QtWidgets.QPushButton("Open Modal", self)
                  self.btnModal.clicked.connect(self.openModal)
          
              def openModal(self):
                  # Note: dlgModal has parent QMainWindow
                  self.dlgModal = QtWidgets.QDialog(self)
                  self.dlgModal.setWindowTitle("Modal Dialog")
                  self.dlgModal.setFixedSize(400, 400)
          
                  self.dlgModal.btnModeless = QtWidgets.QPushButton("Open Modeless", self.dlgModal)
                  self.dlgModal.btnModeless.clicked.connect(self.openModeless)
          
                  # Note: dlgModal is opened modal via exec()
                  # If I replace `exec()` with `show()` I do not get unwanted behaviour
                  # but I do want this invoking dialog to be modal
                  self.dlgModal.exec()
          
              def openModeless(self):
                  # Note: dlgModal has parent None
                  self.dlgModeless = QtWidgets.QDialog(None)
                  self.dlgModeless.setWindowTitle("Modeless Dialog")
                  self.dlgModeless.setFixedSize(300, 300)
          
                  # Note: dlgModeless is opened modeless via show()
                  # It should be "fully independent of anything else", but it is not...?
                  self.dlgModeless.show()
          
          
          if __name__ == '__main__':
              app = QtWidgets.QApplication(sys.argv)
          
              main = Main()
              main.show()
          
              sys.exit(app.exec_())
          
          

          If I replace the self.dlgModal.exec() by self.dlgModal.show() I do not get the unwanted up-fronting behaviour, the modeless can go on top of it. But I do want the invoking dialog to be modal....

          S Offline
          S Offline
          SamurayH
          wrote on 30 May 2019, 16:29 last edited by
          #3

          Hi @JonB,

          Try replacing self.dlgModal.exec() with self.dlgModal.open(), working perfectly on Windows 10.

          Note: " Unlike exec(), open() is asynchronous, and does not spin an additional event loop. So when using open() you can connect to the finished() signal of QDialog to be notified when the dialog is closed. " - Qt 5.12 docs

          "قال رسول الله صلى الله عليه وسلم : " أحب الناس إلى الله أنفعهم للناس

          J 2 Replies Last reply 30 May 2019, 16:43
          1
          • K Kent-Dorfman
            30 May 2019, 16:24

            IIRC, modal dialogs always have focus, so are always on top. Also, I believe that exec() is the correct way to run a modal dialog, as the modal dialog has its own event loop.

            What you are experiencing is the expected behaviour.

            J Offline
            J Offline
            JonB
            wrote on 30 May 2019, 16:41 last edited by JonB
            #4

            @Kent-Dorfman

            IIRC, modal dialogs always have focus, so are always on top.
            What you are experiencing is the expected behaviour.

            Hmm, you may be correct. Which may not be good for me.

            That would mean I can never make an application's modeless dialog/window be up-front whenever the app is showing a modal somewhere. Say it was a debug window --- I couldn't even do that?

            One thing: from how it behaves under Linux, and what someone said looking at it under Windows, it sounded like this is not an OS windowing system behaviour/limitation. It looks like you can temporarily click to get the modeless up-front, but something in the exec() loop sees that and brings its modal dialog back on top. So a Qt behaviour, rather than a native windowing one. What do you think?

            1 Reply Last reply
            0
            • S SamurayH
              30 May 2019, 16:29

              Hi @JonB,

              Try replacing self.dlgModal.exec() with self.dlgModal.open(), working perfectly on Windows 10.

              Note: " Unlike exec(), open() is asynchronous, and does not spin an additional event loop. So when using open() you can connect to the finished() signal of QDialog to be notified when the dialog is closed. " - Qt 5.12 docs

              J Offline
              J Offline
              JonB
              wrote on 30 May 2019, 16:43 last edited by JonB
              #5

              @SamurayH
              Thanks, will investigate tomorrow and see if open() is required instead of exec() to get what I expect. Also I recall some "window on top" flag, maybe that affects behaviour (though I'm not sure that wouldn't introduce its own misbehaviours). Or there might be another window/dialog flag suitable?

              What I want is: a modeless dialog which belongs to the application but is simply unaffected by what other modal dialogs happen to be currently displayed elsewhere in the app. It's an independent window/dialog sitting there letting user interact with it whenever he wants till he closes it. He may need to interact with it while a modal is displayed, e.g. to garner information from the modeless to paste into the modal, or vice versa.

              1 Reply Last reply
              0
              • K Offline
                K Offline
                Kent-Dorfman
                wrote on 30 May 2019, 16:46 last edited by
                #6

                @JonB said in Modeless dialog problem:

                So a Qt behaviour, rather than a native windowing one. What do you think?

                I would agree with that assertion.

                1 Reply Last reply
                1
                • S SamurayH
                  30 May 2019, 16:29

                  Hi @JonB,

                  Try replacing self.dlgModal.exec() with self.dlgModal.open(), working perfectly on Windows 10.

                  Note: " Unlike exec(), open() is asynchronous, and does not spin an additional event loop. So when using open() you can connect to the finished() signal of QDialog to be notified when the dialog is closed. " - Qt 5.12 docs

                  J Offline
                  J Offline
                  JonB
                  wrote on 31 May 2019, 08:04 last edited by JonB
                  #7

                  @SamurayH

                  Try replacing self.dlgModal.exec() with self.dlgModal.open(), working perfectly on Windows 10.

                  I replaced

                  self.dlgModal.exec()
                  

                  by

                  self.dlgModal.setWindowModality(QtCore.Qt.WindowModal)
                  self.dlgModal.show()
                  # or it turns out I can also still use here:
                  # self.dlgModal.exec()
                  

                  and sure enough that does behave as I wanted --- when the modeless is later opened it is quite independent of the invoking modal and I can switch between them as desired.

                  This shows the problem lies in the dlgModal.windowModality() behaviour. I then tried:

                  self.dlgModal.setWindowModality(QtCore.Qt.ApplicationModal)
                  self.dlgModal.show()
                  # or self.dlgModal.exec()
                  

                  and behaviour reverts to the undesirable. QDialog.exec() uses application modal, hence its behaviour:

                  If the dialog is application modal, users cannot interact with any other window in the same application until they close the dialog. If the dialog is window modal, only interaction with the parent window is blocked while the dialog is open. By default, the dialog is application modal.

                  So this must be the root of my issue. However, I have a problem: application has many, many modal dialogs opened via exec() (without specifying any setWindowModality()), any of which might have a button which wants to open the independent modeless dialog. I do not want to have to change the behaviour in the invoking modal dialogs. What I need is something on opening the modeless dialog which makes it a top-level, independent window, without having to alter the invoking dialogs' code, which I should not have to. But it may be that is not possible... :(

                  Can you think of anything I might try on opening the modeless which would make it independent of any application-modal dialog which might be open or become open at a later point??

                  1 Reply Last reply
                  0
                  • J Offline
                    J Offline
                    JonB
                    wrote on 6 Jun 2019, 11:06 last edited by
                    #8

                    I didn't really get a perfect answer. An application-modeless dialog still gets blocked if you open an application-modal dialog, which is not what I want, but there it is. I do not want to change all my application's modal dialogs to be parent-window-modal to solve.

                    To move on I am redesigning so that when the modeless is opened all the modals get closed now. It'll have to do, it's time for me to close this issue now....

                    1 Reply Last reply
                    0

                    7/8

                    31 May 2019, 08:04

                    • Login

                    • Login or register to search.
                    7 out of 8
                    • First post
                      7/8
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved