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 Offline
    J Offline
    james b-s
    wrote on last edited by
    #1

    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 2 Replies 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
      #2

      @james-b-s Not a direct answer, but aren't you complicating it too much? If the data isn't too big just copy it and don't fiddle with focuses and close event and just edit the copy and apply or discard based on the dialog result, i.e.

      auto data = copyExistingData();
      MyDialog dialog(data);
      if (dialog.exec() == QDialog::Accepted)
      {
          applyModifiedData(data);
      }
      
      J 1 Reply Last reply
      1
      • Chris KawaC Chris Kawa

        @james-b-s Not a direct answer, but aren't you complicating it too much? If the data isn't too big just copy it and don't fiddle with focuses and close event and just edit the copy and apply or discard based on the dialog result, i.e.

        auto data = copyExistingData();
        MyDialog dialog(data);
        if (dialog.exec() == QDialog::Accepted)
        {
            applyModifiedData(data);
        }
        
        J Offline
        J Offline
        james b-s
        wrote on last edited by
        #3

        @Chris-Kawa There is a lot of data, of different types, and an indepth class hierarchy, and multiple dialogs, with preexisting code that I don't quite understand. I believe that the one dialog that I am focusing on has 18 fields and there are about a dozen dialogs. I've got my code in a base class so that it is (mostly) implemented in one spot.

        1 Reply Last reply
        0
        • jeremy_kJ Offline
          jeremy_kJ Offline
          jeremy_k
          wrote on last edited by
          #4

          Code, please.

          It sounds like all of this processing is happening within the override of QWidget::closeEvent(). Instead, perform the setup to prompt the user, and then return to the main event loop.

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

          J 1 Reply Last reply
          0
          • jeremy_kJ jeremy_k

            Code, please.

            It sounds like all of this processing is happening within the override of QWidget::closeEvent(). Instead, perform the setup to prompt the user, and then return to the main event loop.

            J Offline
            J Offline
            james b-s
            wrote on last edited by
            #5

            @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 1 Reply Last reply
            0
            • 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 Offline
                  JonBJ Offline
                  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 Online
                      Pl45m4P Online
                      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 Offline
                          JonBJ Offline
                          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 Offline
                              JonBJ Offline
                              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