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. closeEvent and focus
Forum Updated to NodeBB v4.3 + New Features

closeEvent and focus

Scheduled Pinned Locked Moved Unsolved General and Desktop
15 Posts 6 Posters 2.2k Views 3 Watching
  • 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 james b-s

    @jeremy_k The user clicks the x in the upper right. Is there somewhere other than closeEvent that I can detect that he did that?

    jeremy_kJ Offline
    jeremy_kJ Offline
    jeremy_k
    wrote on last edited by
    #6

    @james-b-s said in closeEvent and focus:

    @jeremy_k The user clicks the x in the upper right. Is there somewhere other than closeEvent that I can detect that he did that?

    class Widget : public QWidget {
        bool m_allowExit = false;
    
    public:
        void closeEvent(QCloseEvent *event) override {
            if (!m_allowExit) {
                QMessageBox *dialog = new QMessageBox(QMessageBox::NoIcon, "Click ok to close", "Click ok to close", QMessageBox::Ok);
                QObject::connect(dialog, &QMessageBox::buttonClicked, [&]() { m_allowExit = true; close(); });
                dialog->show();
            }
            event->setAccepted(m_allowExit);
        }
    };
    

    Asking a question about code? http://eel.is/iso-c++/testcase/

    J 1 Reply Last reply
    1
    • jeremy_kJ jeremy_k

      @james-b-s said in closeEvent and focus:

      @jeremy_k The user clicks the x in the upper right. Is there somewhere other than closeEvent that I can detect that he did that?

      class Widget : public QWidget {
          bool m_allowExit = false;
      
      public:
          void closeEvent(QCloseEvent *event) override {
              if (!m_allowExit) {
                  QMessageBox *dialog = new QMessageBox(QMessageBox::NoIcon, "Click ok to close", "Click ok to close", QMessageBox::Ok);
                  QObject::connect(dialog, &QMessageBox::buttonClicked, [&]() { m_allowExit = true; close(); });
                  dialog->show();
              }
              event->setAccepted(m_allowExit);
          }
      };
      
      J Offline
      J Offline
      james b-s
      wrote on last edited by
      #7

      @jeremy_k I don't see anything in that code that detects whether something has actually changed in the dialog. The problem is not the displaying of the message. The problem is that I don't want the message to be displayed if something has changed.

      There are two copies of the data; the original data and the data currently on the screen. I have code that correctly compares the two. The problem comes about from fields that are in the middle of being edited. When the user presses the OK or APPLY button, a focus out event occurs. Existing code then causes the data in the field to be edited to be committed as if the use had pressed the enter key. When the user presses the x, the focus out does not occur. When I compare the original copy of the data to the copy of data on the screen, the data in the field being edited has not been committed and is lost. I need that focus out event to occur before the code that I placed in closeEvent compares the old and new values.

      I thought putting a call to clearFocus would do it. It does cause a focus out event to occur, but not until closeEvent is done. I tried added an event loop to process events, but the focusOut still occurs after closeEvent completes.

      I'm thinking that if I have closeEvent call clearFocus and then emit another signal that triggers the rest of the code, I can get QT to give me the order of events that I need, but that seems hacky. I'm hoping that there is a better way.

      JonBJ Pl45m4P jeremy_kJ 3 Replies Last reply
      0
      • J james b-s

        @jeremy_k I don't see anything in that code that detects whether something has actually changed in the dialog. The problem is not the displaying of the message. The problem is that I don't want the message to be displayed if something has changed.

        There are two copies of the data; the original data and the data currently on the screen. I have code that correctly compares the two. The problem comes about from fields that are in the middle of being edited. When the user presses the OK or APPLY button, a focus out event occurs. Existing code then causes the data in the field to be edited to be committed as if the use had pressed the enter key. When the user presses the x, the focus out does not occur. When I compare the original copy of the data to the copy of data on the screen, the data in the field being edited has not been committed and is lost. I need that focus out event to occur before the code that I placed in closeEvent compares the old and new values.

        I thought putting a call to clearFocus would do it. It does cause a focus out event to occur, but not until closeEvent is done. I tried added an event loop to process events, but the focusOut still occurs after closeEvent completes.

        I'm thinking that if I have closeEvent call clearFocus and then emit another signal that triggers the rest of the code, I can get QT to give me the order of events that I need, but that seems hacky. I'm hoping that there is a better way.

        JonBJ Online
        JonBJ Online
        JonB
        wrote on last edited by JonB
        #8

        @james-b-s said in closeEvent and focus:

        I'm thinking that if I have closeEvent call clearFocus and then emit another signal that triggers the rest of the code, I can get QT to give me the order of events that I need, but that seems hacky. I'm hoping that there is a better way.

        Given your explanation of what is happening (I have not verified). I was thinking of answering this before I saw this as your last paragraph. You are saying the closeEvent event comes too early, it needs to be delayed till after the focus clear or whatever causes your other signals to be processed. So:

        1. Define a new signal of your own, for hadCloseEvent().
        2. In closeEvent(), either have connected to that as queued signal and emit it, or set a single shot QTimer to emit it after a minimal (0?) delay..
        3. Do your clearFocus() or whatever is required.
        4. From this closeEvent() reject the close event unconditionally.
        5. Handle the "delayed" signal in a slot which asks the user whether to quit/save changes/whatever. If user says "yes" then close() the window.
        6. If that causes cluseEvent() to be called again, you will have to retain some state so you can recognise this is from your hadCloseEvent() and should not do the same code a second time.

        I agree this sounds "not clean". But Qt handles events differently from signals, and I can see why there is a problem here. I don't know why someone else has not encountered your situation.

        S 1 Reply Last reply
        2
        • JonBJ JonB

          @james-b-s said in closeEvent and focus:

          I'm thinking that if I have closeEvent call clearFocus and then emit another signal that triggers the rest of the code, I can get QT to give me the order of events that I need, but that seems hacky. I'm hoping that there is a better way.

          Given your explanation of what is happening (I have not verified). I was thinking of answering this before I saw this as your last paragraph. You are saying the closeEvent event comes too early, it needs to be delayed till after the focus clear or whatever causes your other signals to be processed. So:

          1. Define a new signal of your own, for hadCloseEvent().
          2. In closeEvent(), either have connected to that as queued signal and emit it, or set a single shot QTimer to emit it after a minimal (0?) delay..
          3. Do your clearFocus() or whatever is required.
          4. From this closeEvent() reject the close event unconditionally.
          5. Handle the "delayed" signal in a slot which asks the user whether to quit/save changes/whatever. If user says "yes" then close() the window.
          6. If that causes cluseEvent() to be called again, you will have to retain some state so you can recognise this is from your hadCloseEvent() and should not do the same code a second time.

          I agree this sounds "not clean". But Qt handles events differently from signals, and I can see why there is a problem here. I don't know why someone else has not encountered your situation.

          S Offline
          S Offline
          SamiV123
          wrote on last edited by SamiV123
          #9

          @JonB

          To me it sounds like what they should do is

          1. Populate the UI elements from the data object when the dialog is opened
          2. Retain the state in the UI and not flush any changes to the data object on any "edit/chaged" signal.
          3. Handle dialog closeEvent
            3.1 In close event compare the current data object state to the UI state
            3.2 When changes are detected consult the user about quitting without changes or whatever
            3.3 Sync the changes from the UI to the data object

          On a related note it's crazy how many ways there are to close a dialog and how such a simple thing has been been made super complicated.

          • User clicks on the window decoration's close button
          • User hits some key combo such as Alt+F4 or Ctrl+W
          • User clicks on a button explicitly in the dialog
          • User hits enter and some button has "autoDefault = true" (and QtDesigner often lies about this too)

          When a system is so complicated that simple things become difficult then truly difficult things become impossible.

          1 Reply Last reply
          1
          • J james b-s

            @jeremy_k I don't see anything in that code that detects whether something has actually changed in the dialog. The problem is not the displaying of the message. The problem is that I don't want the message to be displayed if something has changed.

            There are two copies of the data; the original data and the data currently on the screen. I have code that correctly compares the two. The problem comes about from fields that are in the middle of being edited. When the user presses the OK or APPLY button, a focus out event occurs. Existing code then causes the data in the field to be edited to be committed as if the use had pressed the enter key. When the user presses the x, the focus out does not occur. When I compare the original copy of the data to the copy of data on the screen, the data in the field being edited has not been committed and is lost. I need that focus out event to occur before the code that I placed in closeEvent compares the old and new values.

            I thought putting a call to clearFocus would do it. It does cause a focus out event to occur, but not until closeEvent is done. I tried added an event loop to process events, but the focusOut still occurs after closeEvent completes.

            I'm thinking that if I have closeEvent call clearFocus and then emit another signal that triggers the rest of the code, I can get QT to give me the order of events that I need, but that seems hacky. I'm hoping that there is a better way.

            Pl45m4P Offline
            Pl45m4P Offline
            Pl45m4
            wrote on last edited by
            #10

            @james-b-s said in closeEvent and focus:

            Existing code then causes the data in the field to be edited to be committed as if the use had pressed the enter key. When the user presses the x, the focus out does not occur.

            So the whole problem in short is, that you dont get the editingFinished signal if the user aborts and closes the dialog while editing?!
            Everything works if you get the signal?!

            It's stated (you prob. know that already):

            This signal is emitted when the Return or Enter key is pressed, or if the line edit loses focus and its contents have changed since the last time this signal was emitted.

            • https://doc.qt.io/qt-6/qlineedit.html#editingFinished

            All you need is some sort of "dirty data flag" for your dialog.
            Wouldn't it be enough to save/sync new data (considered as "changed" as soon as one widget receives keyboard input or any widget (sliders, comboBoxes, etc) notice any actions?
            Why you want to rely on editingFinished which is only emitted when pressing Enter or the widget "correctly" losing focus?! I assume Alt + F4'ing your app would also "hack" your current logic to save/sync data.
            You could look for that into other projects how "dirty data" is detected and handled there and do the same or similar in your closeEvent or even before.
            QTextDocument for example, has setModified which triggers the dirty flag. You can display it, by adding (IIRC) [ * ] to your saveFile name. (there are plenty of topics dealing with this "dirty data" stuff here, but unfortunately I cant find the one I was thinking of)...


            If debugging is the process of removing software bugs, then programming must be the process of putting them in.

            ~E. W. Dijkstra

            1 Reply Last reply
            0
            • J james b-s

              @jeremy_k I don't see anything in that code that detects whether something has actually changed in the dialog. The problem is not the displaying of the message. The problem is that I don't want the message to be displayed if something has changed.

              There are two copies of the data; the original data and the data currently on the screen. I have code that correctly compares the two. The problem comes about from fields that are in the middle of being edited. When the user presses the OK or APPLY button, a focus out event occurs. Existing code then causes the data in the field to be edited to be committed as if the use had pressed the enter key. When the user presses the x, the focus out does not occur. When I compare the original copy of the data to the copy of data on the screen, the data in the field being edited has not been committed and is lost. I need that focus out event to occur before the code that I placed in closeEvent compares the old and new values.

              I thought putting a call to clearFocus would do it. It does cause a focus out event to occur, but not until closeEvent is done. I tried added an event loop to process events, but the focusOut still occurs after closeEvent completes.

              I'm thinking that if I have closeEvent call clearFocus and then emit another signal that triggers the rest of the code, I can get QT to give me the order of events that I need, but that seems hacky. I'm hoping that there is a better way.

              jeremy_kJ Offline
              jeremy_kJ Offline
              jeremy_k
              wrote on last edited by
              #11

              @james-b-s said in closeEvent and focus:

              @jeremy_k I don't see anything in that code that detects whether something has actually changed in the dialog. The problem is not the displaying of the message. The problem is that I don't want the message to be displayed if something has changed.

              The point of the code presented is to move complex handling out of closeEvent(). Schedule it for processing later, and return to the main event loop.

              As far as comparing the data, do that when the input changes, not when the dialog is about to close. At close time, it can be as simple as checking the value of a bool.

              Asking a question about code? http://eel.is/iso-c++/testcase/

              JonBJ 1 Reply Last reply
              0
              • jeremy_kJ jeremy_k

                @james-b-s said in closeEvent and focus:

                @jeremy_k I don't see anything in that code that detects whether something has actually changed in the dialog. The problem is not the displaying of the message. The problem is that I don't want the message to be displayed if something has changed.

                The point of the code presented is to move complex handling out of closeEvent(). Schedule it for processing later, and return to the main event loop.

                As far as comparing the data, do that when the input changes, not when the dialog is about to close. At close time, it can be as simple as checking the value of a bool.

                JonBJ Online
                JonBJ Online
                JonB
                wrote on last edited by JonB
                #12

                @jeremy_k said in closeEvent and focus:

                do that when the input changes

                The point is that this, or changed signals, happen for him when a widget loses focus. Which is a legitimate event for this (e.g. editingFinished will presumably require/fire on it). But he says focus loss/change does not happen on clicking the "X" close window button (it probably ought to for this case, but apparently not per his report). The input has not changed at the time closeEvent() is called.

                jeremy_kJ 1 Reply Last reply
                0
                • JonBJ JonB

                  @jeremy_k said in closeEvent and focus:

                  do that when the input changes

                  The point is that this, or changed signals, happen for him when a widget loses focus. Which is a legitimate event for this (e.g. editingFinished will presumably require/fire on it). But he says focus loss/change does not happen on clicking the "X" close window button (it probably ought to for this case, but apparently not per his report). The input has not changed at the time closeEvent() is called.

                  jeremy_kJ Offline
                  jeremy_kJ Offline
                  jeremy_k
                  wrote on last edited by jeremy_k
                  #13

                  @JonB said in closeEvent and focus:

                  @jeremy_k said in closeEvent and focus:

                  do that when the input changes

                  The point is that this, or changed signals, happen for him when a widget loses focus. Which is a legitimate event for this (e.g. editingFinished will presumably require/fire on it). But he says focus loss/change does not happen on clicking the "X" close window button (it probably ought to for this case, but apparently not per his report). The input has not changed at the time closeEvent() is called.

                  I am severely confused. OP said:

                  I believe that the one dialog that I am focusing on has 18 fields and there are about a dozen dialogs

                  While it is possible that a widget would have 18 separate fields (item views for example), people usually implement each field as a widget. Use the appropriate changed signal (not editing finished) to track when a field changes, and store whether that individual field differs from its saved version.

                  Asking a question about code? http://eel.is/iso-c++/testcase/

                  JonBJ 1 Reply Last reply
                  1
                  • jeremy_kJ jeremy_k

                    @JonB said in closeEvent and focus:

                    @jeremy_k said in closeEvent and focus:

                    do that when the input changes

                    The point is that this, or changed signals, happen for him when a widget loses focus. Which is a legitimate event for this (e.g. editingFinished will presumably require/fire on it). But he says focus loss/change does not happen on clicking the "X" close window button (it probably ought to for this case, but apparently not per his report). The input has not changed at the time closeEvent() is called.

                    I am severely confused. OP said:

                    I believe that the one dialog that I am focusing on has 18 fields and there are about a dozen dialogs

                    While it is possible that a widget would have 18 separate fields (item views for example), people usually implement each field as a widget. Use the appropriate changed signal (not editing finished) to track when a field changes, and store whether that individual field differs from its saved version.

                    JonBJ Online
                    JonBJ Online
                    JonB
                    wrote on last edited by
                    #14

                    @jeremy_k
                    I do not disagree with you. I just accept this is the OP's question/situation

                    The problem is that the editingFinished signal isn;t generated so the data in the QLineEdit isn't saved.

                    If this is the case the OP has a legitimate difficulty.

                    @james-b-s
                    I would be interested to know how a QDataWidgetMapper Class behaves in your situation. Set up a mapping, does it see data changed before your closeEvent?

                    1 Reply Last reply
                    0
                    • J james b-s

                      I have a QDialog with OK, APPLY, and CANCEL buttons, and a bunch of fields.. When the user presses OK, or APPLY, focus leaves the field being edited. This causes the QLineEdit::editingFinished signal to be generated and a method is called which saves the data that the user was typing.

                      I'm trying to make it so that QDialog::closeEvent asks the user if they want to save the data before exiting. I overrode the close Event, display the confirmation dialog and perform the necessary action. That all works fine.

                      The problem is that the editingFinished signal isn;t generated so the data in the QLineEdit isn't saved.

                      I added a call to clearFocus at the beginning of closeEvent, and now the editingFinished signal is generated, but not until closeEvent completes. That causes the closeEvent method not to work properly. closeEvent needs the data in the field to be saved before checking to see if the data has changed. I tried adding an event loop and a call to processEvents to process the clearFocus event, but that didn't make a difference.

                      What I would like is a way to make it so that clicking on the x in the upper right and triggering the closeEvent would automatically change the focus, just like clicking on any other button. Failing that, a way to get the clearFocus event to be handled before continuing on to the rest of the code in closeEvent.

                      Any suggestions would be helpful.

                      Chris KawaC Offline
                      Chris KawaC Offline
                      Chris Kawa
                      Lifetime Qt Champion
                      wrote on last edited by Chris Kawa
                      #15

                      @james-b-s Starting an event loop in closeEvent won't work. You're already in an event handler and you need to get back from it for the dialog to function properly.

                      You can do what @JonB said - delay closing until you process your focus loss. For example use a QDialog derived class like this:

                      class MyDialog : public QDialog
                      {
                          bool allowClose = false;
                      public:
                          using QDialog::QDialog;
                      
                          void closeEvent(QCloseEvent* evt) override
                          {
                              if (!allowClose)
                              {
                                  evt->ignore();                                  // don't close and let event loop handle focus loss
                                  allowClose = true;                              // let it close next time
                                  setFocus(Qt::OtherFocusReason);                 // switch focus to something else, e.g. this or cancel button
                                  QTimer::singleShot(0, this, &MyDialog::close);  // schedule another close event
                              }
                              else
                              {
                                  askUserToSaveStuff();
                                  QDialog::closeEvent(evt);
                              }
                          }
                      };
                      
                      1 Reply Last reply
                      3

                      • Login

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