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. GUI event blocking
Forum Updated to NodeBB v4.3 + New Features

GUI event blocking

Scheduled Pinned Locked Moved Solved General and Desktop
40 Posts 5 Posters 14.8k Views 5 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.
  • kshegunovK kshegunov

    @JonB said in GUI event blocking:

    But I think (politely) you're missing the point on re-entrancy.

    Probably, but then again I don't have the code at hand, so I'm just more or less guessing. Your point seems warranted though, so the only thing I can add here is: have a light and easy debugging. :)

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

    @kshegunov
    Thanks; as I said, my comment was intended "politely". I have tried to clarify the situation in previous post. Once a single UI operation is underway, it's not safe to allow any other one to proceed, unless you've written code in a very particular way. And I didn't even write this code, let alone know just what it does!

    1 Reply Last reply
    1
    • JonBJ JonB

      @ambershark , and others

      I now understand that "normal" Qt processing should be "block-until-return-from-click-handler". (Which is a relief to my general handling of other aspects of UI!)

      However, in the case of the code (large, spaghetti, and completely uncommented) I am looking at, here is what I experience:

      1. Click button to set off large "computation" of some kind. It's going to take, say, 20 seconds to complete.

      2. Wait, say, a couple of seconds for it to "get going". (Don't know if this is required; there's obviously some delay between clicks anyway.)

      3. Click that button again, or any other button/widget on the "stacked widget page".

      4. Code immediately starts executing in response to this new UI action. Verified in debugger and/or messages.

      The question for me is: what might it be in the original code processing which is allowing the second UI interaction to proceed? I need to track it down. To the best of my knowledge the code does not explicitly do things like create threads or process an event loop. It does, however, do database querying, which is why I keep asking if that's enough to cause the re-entrancy?

      I know all about progress bars and blocking flags. Indeed, with a blocking flag, the blocking flag immediately gets hit in #4, proving the point, but not robust enough as you say, e.g. does not cover clicking any other widget in UI.

      The code is so monolithic & interdependent that, say, commenting out sections to see if behaviour altered is difficult/impossible. So I don't know how to approach debugging to discover what this massive block of unintelligible code is doing which allows this re-entrancy, so that I can deal with it properly.

      Can anyone suggest a technique (Python debugging, I'm afraid, not native C++) to aid me in tracking down where the re-entrancy is coming from?

      A Offline
      A Offline
      ambershark
      wrote on last edited by ambershark
      #16

      @JonB said in GUI event blocking:

      The question for me is: what might it be in the original code processing which is allowing the second UI interaction to proceed?

      Well there are really only 2 things that can do this. Threading and events. So either the signal handler for the click is exiting after sending events or it's exiting after signalling a thread.

      To be honest it should be pretty easy to debug. Just point a breakpoint in the signal handler for that click event, then step through it. Step over function calls and test how long they take. If everything exits quickly (what I'm guessing will happen) and the signal handler exits, before your data is processed, then you just start stepping in to each of the functions in that click handler until you see what they are doing.

      At some point you will find one that signals something to do some work and exits (via thread or event). That is your problem. Address that area and you should be good to go. I would just use something easy like a modal progress dialog that locks up the gui automatically until you signal it to quit when you get back from the processing of the "click".

      Oh and it always sucks working on someone else's code.. Especially confusing/bad code. When I get in messy code I use a tool called SourceTrail to navigate code.

      https://www.sourcetrail.com

      My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

      JonBJ 1 Reply Last reply
      3
      • A ambershark

        @JonB said in GUI event blocking:

        The question for me is: what might it be in the original code processing which is allowing the second UI interaction to proceed?

        Well there are really only 2 things that can do this. Threading and events. So either the signal handler for the click is exiting after sending events or it's exiting after signalling a thread.

        To be honest it should be pretty easy to debug. Just point a breakpoint in the signal handler for that click event, then step through it. Step over function calls and test how long they take. If everything exits quickly (what I'm guessing will happen) and the signal handler exits, before your data is processed, then you just start stepping in to each of the functions in that click handler until you see what they are doing.

        At some point you will find one that signals something to do some work and exits (via thread or event). That is your problem. Address that area and you should be good to go. I would just use something easy like a modal progress dialog that locks up the gui automatically until you signal it to quit when you get back from the processing of the "click".

        Oh and it always sucks working on someone else's code.. Especially confusing/bad code. When I get in messy code I use a tool called SourceTrail to navigate code.

        https://www.sourcetrail.com

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

        @ambershark
        OK, now had a chance to break and examine stack.

        Nobody said this code would be easy...

        The "long running operation" involves producing HTML, and saving to PDF file (think of it like a report). I do this via QWebEngineView (partly because this is in shared code, at other times the generated HTML is displayed to the user and is editable prior to producing the PDF). In this case the view is never displayed interactively to the user, but it still follows that route.

        Because QWebEngineView is asynchronous (its own thread, I believe), and here we need the HTML/PDF synchronously, before we can save the PDF to file we must await QWebEngineView finished loading (else we get empty PDF saved to file). I have code there like:

            def synchronousRenderHtml(self):
                # see synchronousWebViewLoaded() below
                self.rendered = False
                self.webView.loadFinished.connect(self.synchronousWebViewLoaded)
                #
                # see http://doc.qt.io/qt-5/qtwebenginewidgets-qtwebkitportingguide.html
                # because setHtml() is asynchronous, the call to make editable has to be moved into synchronousWebViewLoaded()
                htmlFile = filesystemfunctions.findFileInDataOrCodeDirectory(Paths.s166Html())
                self.webView.setHtml(self.html, QUrl.fromLocalFile(str(htmlFile)))
                #
                # see synchronousWebViewLoaded() below
                if not self.rendered:
                    self.renderLoop.exec()
        
            def synchronousWebViewLoaded(self):
                # This function is called on self.webView.loadFinished() from synchronousRenderHtml() above
                # The code uses this because self.webView.setHtml() is asynchronous
                # so in effect this implements a "blocking" call (with event processing) in synchronousRenderHtml()
                # This is the only place in code where a QEventLoop is used explicitly
                # It's all very complicated, and I did wish this behaviour was not needed
                # but it seems, at minimum in the case where this dialog is used non-interactively, it is :(
                self.qWebEngineView.page().runJavaScript("document.documentElement.contentEditable = true")
                self.rendered = True
                # cause the self.renderLoop.exec() in synchronousRenderHtml() above to exit now
                self.renderLoop.quit()
        

        Turns out, the re-entrant call for the top-level button being pressed a second time is coming from the self.renderLoop.exec() (which is awaiting the self.renderLoop.quit()).

        Cutting a long story short, unless someone can suggest a better way than this self.renderLoop.exec() principle(?), I think I have to stick with that. Hence the button re-entrancy, and I'll have to live with the need to using a re-entrancy blocking flag....

        kshegunovK 1 Reply Last reply
        0
        • JonBJ JonB

          @ambershark
          OK, now had a chance to break and examine stack.

          Nobody said this code would be easy...

          The "long running operation" involves producing HTML, and saving to PDF file (think of it like a report). I do this via QWebEngineView (partly because this is in shared code, at other times the generated HTML is displayed to the user and is editable prior to producing the PDF). In this case the view is never displayed interactively to the user, but it still follows that route.

          Because QWebEngineView is asynchronous (its own thread, I believe), and here we need the HTML/PDF synchronously, before we can save the PDF to file we must await QWebEngineView finished loading (else we get empty PDF saved to file). I have code there like:

              def synchronousRenderHtml(self):
                  # see synchronousWebViewLoaded() below
                  self.rendered = False
                  self.webView.loadFinished.connect(self.synchronousWebViewLoaded)
                  #
                  # see http://doc.qt.io/qt-5/qtwebenginewidgets-qtwebkitportingguide.html
                  # because setHtml() is asynchronous, the call to make editable has to be moved into synchronousWebViewLoaded()
                  htmlFile = filesystemfunctions.findFileInDataOrCodeDirectory(Paths.s166Html())
                  self.webView.setHtml(self.html, QUrl.fromLocalFile(str(htmlFile)))
                  #
                  # see synchronousWebViewLoaded() below
                  if not self.rendered:
                      self.renderLoop.exec()
          
              def synchronousWebViewLoaded(self):
                  # This function is called on self.webView.loadFinished() from synchronousRenderHtml() above
                  # The code uses this because self.webView.setHtml() is asynchronous
                  # so in effect this implements a "blocking" call (with event processing) in synchronousRenderHtml()
                  # This is the only place in code where a QEventLoop is used explicitly
                  # It's all very complicated, and I did wish this behaviour was not needed
                  # but it seems, at minimum in the case where this dialog is used non-interactively, it is :(
                  self.qWebEngineView.page().runJavaScript("document.documentElement.contentEditable = true")
                  self.rendered = True
                  # cause the self.renderLoop.exec() in synchronousRenderHtml() above to exit now
                  self.renderLoop.quit()
          

          Turns out, the re-entrant call for the top-level button being pressed a second time is coming from the self.renderLoop.exec() (which is awaiting the self.renderLoop.quit()).

          Cutting a long story short, unless someone can suggest a better way than this self.renderLoop.exec() principle(?), I think I have to stick with that. Hence the button re-entrancy, and I'll have to live with the need to using a re-entrancy blocking flag....

          kshegunovK Offline
          kshegunovK Offline
          kshegunov
          Moderators
          wrote on last edited by kshegunov
          #18

          @JonB said in GUI event blocking:

          Cutting a long story short, unless someone can suggest a better way than this self.renderLoop.exec() principle(?),

          Not in this particular case. But I have a suggestion:
          Grab the mouse and keyboard in synchronousRenderHtml and discard those events in your event() override while self.rendered is false and you're owning the user input. In synchronousWebViewLoaded release back the mouse and keyboard. Tread carefully though, as grabbing the mouse and keyboard can make your system unusable (i.e. use -nograb with gdb to prevent you from cursing). ;)

          Read and abide by the Qt Code of Conduct

          JonBJ 1 Reply Last reply
          4
          • kshegunovK kshegunov

            @JonB said in GUI event blocking:

            Cutting a long story short, unless someone can suggest a better way than this self.renderLoop.exec() principle(?),

            Not in this particular case. But I have a suggestion:
            Grab the mouse and keyboard in synchronousRenderHtml and discard those events in your event() override while self.rendered is false and you're owning the user input. In synchronousWebViewLoaded release back the mouse and keyboard. Tread carefully though, as grabbing the mouse and keyboard can make your system unusable (i.e. use -nograb with gdb to prevent you from cursing). ;)

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

            @kshegunov
            I did think about that "dirty" way of handling things.

            However (apart from the debugging issue, I use Python + PyCharm debugger, not gdb, and I wouldn't hold your breath that it will have a -nograb functionality!), the problem will be which "event() override" you have in mind.

            Because, as I said, the QWebEngineView, which produces the HTML/PDF asynchronously and has the synchronousRenderHtml() call, is not displayed to the user in this case, the UI events are being received to the original dialog where a button is pressed to produce the output. This in itself has no connection to the ("invisible") QWebEngineView --- it doesn't even know one is being used, the implementation is invisible to it. I would have to modify its event handling. And since that would break encapsulation, I really don't feel it would be a good idea to implement!

            So I will soldier on with the "blocking flag" I currently have to prevent "re-entrancy", which at least works reasonably well in practice. At least I now understand why this whole behaviour is caused.

            kshegunovK 1 Reply Last reply
            0
            • JonBJ JonB

              @kshegunov
              I did think about that "dirty" way of handling things.

              However (apart from the debugging issue, I use Python + PyCharm debugger, not gdb, and I wouldn't hold your breath that it will have a -nograb functionality!), the problem will be which "event() override" you have in mind.

              Because, as I said, the QWebEngineView, which produces the HTML/PDF asynchronously and has the synchronousRenderHtml() call, is not displayed to the user in this case, the UI events are being received to the original dialog where a button is pressed to produce the output. This in itself has no connection to the ("invisible") QWebEngineView --- it doesn't even know one is being used, the implementation is invisible to it. I would have to modify its event handling. And since that would break encapsulation, I really don't feel it would be a good idea to implement!

              So I will soldier on with the "blocking flag" I currently have to prevent "re-entrancy", which at least works reasonably well in practice. At least I now understand why this whole behaviour is caused.

              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by
              #20

              @JonB said in GUI event blocking:

              the problem will be which "event() override" you have in mind.

              That would be the event() override of the class (widget) in which you grab the input. When you grab the input events all of them are redirected to the widget that called the mentioned functions, so you'd need to filter them in that same class. Basically, you grab the input in the widget/dialog/w/e and filter the input there. Whenever the operation's done (i.e. you connect the finished signal to a slot in that same dialog you release the mouse/keyboard.

              Read and abide by the Qt Code of Conduct

              JonBJ 1 Reply Last reply
              1
              • kshegunovK kshegunov

                @JonB said in GUI event blocking:

                the problem will be which "event() override" you have in mind.

                That would be the event() override of the class (widget) in which you grab the input. When you grab the input events all of them are redirected to the widget that called the mentioned functions, so you'd need to filter them in that same class. Basically, you grab the input in the widget/dialog/w/e and filter the input there. Whenever the operation's done (i.e. you connect the finished signal to a slot in that same dialog you release the mouse/keyboard.

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

                @kshegunov
                Sorry, my friend, but as per the explanation I wrote above, I still don't understand which widget's events you mean.

                Here is the architecture of how the code works:

                • The user starts from a dialog, which has buttons on it, like Run long-running report.
                • That button on that dialog makes call produceReportAndWaitForFinish().
                • Behind the scenes, produceReportAndWaitForFinish() goes: create invisible QWebEnginePage and await final HTML/PDF returned, invisible QWebEnginePage calls synchronousRenderHtml(), which in turn has the self.renderLoop.exec() & self.renderLoop.quit() inside itself (the QWebEnginePage), in order to produce the HTML/PDF.
                • You want me to place the "event filter" there (inside the QWebEnginePage), when I call self.renderLoop.exec().
                • However, I don't think that (the QWebEnginePage) receives any inputs. The inputs (which would cause re-entrancy) are directed to buttons on the original top-level dialog where the user originally clicked the Run long-running report button, which I don't want him to click again.

                If that is correct(?), that is why I don't want to fiddle inside QWebEnginePage with the events directed to the top-level dialog, because of encapsulation/separation of code. The QWebEnginePage does not know if it was invoked from a dialog in the first place, and the dialog has no idea that a QWebEnginePage (which will do threads/events) is involved in the production of the HTML/PDF.

                kshegunovK 1 Reply Last reply
                0
                • JonBJ JonB

                  @kshegunov
                  Sorry, my friend, but as per the explanation I wrote above, I still don't understand which widget's events you mean.

                  Here is the architecture of how the code works:

                  • The user starts from a dialog, which has buttons on it, like Run long-running report.
                  • That button on that dialog makes call produceReportAndWaitForFinish().
                  • Behind the scenes, produceReportAndWaitForFinish() goes: create invisible QWebEnginePage and await final HTML/PDF returned, invisible QWebEnginePage calls synchronousRenderHtml(), which in turn has the self.renderLoop.exec() & self.renderLoop.quit() inside itself (the QWebEnginePage), in order to produce the HTML/PDF.
                  • You want me to place the "event filter" there (inside the QWebEnginePage), when I call self.renderLoop.exec().
                  • However, I don't think that (the QWebEnginePage) receives any inputs. The inputs (which would cause re-entrancy) are directed to buttons on the original top-level dialog where the user originally clicked the Run long-running report button, which I don't want him to click again.

                  If that is correct(?), that is why I don't want to fiddle inside QWebEnginePage with the events directed to the top-level dialog, because of encapsulation/separation of code. The QWebEnginePage does not know if it was invoked from a dialog in the first place, and the dialog has no idea that a QWebEnginePage (which will do threads/events) is involved in the production of the HTML/PDF.

                  kshegunovK Offline
                  kshegunovK Offline
                  kshegunov
                  Moderators
                  wrote on last edited by
                  #22

                  @JonB said in GUI event blocking:

                  If that is correct

                  Nope. I mean exactly the dialog with the buttons. See (untested) code below, which I hope will clear this up:

                  class DialogWithButtons : public QDialog
                  {
                  public:
                      DialogWithButtons(QWidget * parent);
                  
                  public slots:
                      void produceReportAndWaitForFinish();
                      void reportProduced();
                  
                  protected:
                      bool event(QEvent *) override;
                  private:
                      QPushButton startLongRunningOpButton;
                      bool inputBlocked;
                  }
                  
                  DialogWithButtons::DialogWithButtons(QWidget * parent)
                      : QWidget(parent), startLongRunningOpButton(this), inputBlocked(false)
                  {
                      // Just connect the button
                      QObject::connect(&startLongRunningOpButton, &QPushButton::clicked, this, &DialogWithButtons::produceReportAndWaitForFinish);
                  }
                  
                  void DialogWithButtons::produceReportAndWaitForFinish()
                  {
                      inputBlocked = true;
                      grabMouse();
                      grabKeyboard();
                  
                      // Create/Init the webengine and so on, put to render and all that goodness. 
                      QWebEngineView webEngine;
                  
                      // Connect the handlers & QEventLoop blocking
                      QObject::connect(&webEngine, &QWebEngineView::loadFinished, this, &DialogWithButtons::reportProduced);
                  
                      QEventLoop loop;
                      QObject::connect(&webEngine, &QWebEngineView::loadFinished, &loop, &QEventLoop::quit);
                      loop.exec();   // Wait for processing to finish
                  }
                  
                  void DialogWithButtons::reportProduced()
                  {
                       releaseMouse();
                       releaseKeyboard();
                       inputBlocked = false;
                  }
                  
                  bool DialogWithButtons::event(QEvent * e)
                  {
                      if (inputBlocked && dynamic_cast<QInputEvent *>(e))
                          return true;   // Filter out input events - we receive all of them if `inputBlocked` is true
                   
                      return QDialog::event(e);  // Input was not blocked or event was not UI input - delegate to the default implementation
                  }
                  

                  Read and abide by the Qt Code of Conduct

                  JonBJ 1 Reply Last reply
                  3
                  • kshegunovK kshegunov

                    @JonB said in GUI event blocking:

                    If that is correct

                    Nope. I mean exactly the dialog with the buttons. See (untested) code below, which I hope will clear this up:

                    class DialogWithButtons : public QDialog
                    {
                    public:
                        DialogWithButtons(QWidget * parent);
                    
                    public slots:
                        void produceReportAndWaitForFinish();
                        void reportProduced();
                    
                    protected:
                        bool event(QEvent *) override;
                    private:
                        QPushButton startLongRunningOpButton;
                        bool inputBlocked;
                    }
                    
                    DialogWithButtons::DialogWithButtons(QWidget * parent)
                        : QWidget(parent), startLongRunningOpButton(this), inputBlocked(false)
                    {
                        // Just connect the button
                        QObject::connect(&startLongRunningOpButton, &QPushButton::clicked, this, &DialogWithButtons::produceReportAndWaitForFinish);
                    }
                    
                    void DialogWithButtons::produceReportAndWaitForFinish()
                    {
                        inputBlocked = true;
                        grabMouse();
                        grabKeyboard();
                    
                        // Create/Init the webengine and so on, put to render and all that goodness. 
                        QWebEngineView webEngine;
                    
                        // Connect the handlers & QEventLoop blocking
                        QObject::connect(&webEngine, &QWebEngineView::loadFinished, this, &DialogWithButtons::reportProduced);
                    
                        QEventLoop loop;
                        QObject::connect(&webEngine, &QWebEngineView::loadFinished, &loop, &QEventLoop::quit);
                        loop.exec();   // Wait for processing to finish
                    }
                    
                    void DialogWithButtons::reportProduced()
                    {
                         releaseMouse();
                         releaseKeyboard();
                         inputBlocked = false;
                    }
                    
                    bool DialogWithButtons::event(QEvent * e)
                    {
                        if (inputBlocked && dynamic_cast<QInputEvent *>(e))
                            return true;   // Filter out input events - we receive all of them if `inputBlocked` is true
                     
                        return QDialog::event(e);  // Input was not blocked or event was not UI input - delegate to the default implementation
                    }
                    
                    JonBJ Online
                    JonBJ Online
                    JonB
                    wrote on last edited by JonB
                    #23

                    @kshegunov
                    Yes, OK, I thought so.

                    The problem is, my produceReportAndWaitForFinish() in the top-level dialog does not have any kind of

                        // Create/Init the webengine and so on, put to render and all that goodness. 
                        QWebEngineView webEngine;
                    

                    inside it (and I don't want it to). It has more like:

                        //  Call *completely opaque* ReportProducer to generate HTML/PDF
                        //  Here we have *no knowledge* of how ReportProducer functions
                        //  and to maintain code separation/encapsulation we do not want to know here
                        //  We might completely change how ReportProducer works at any time
                        //  without affecting any code/behaviour here in this dialog.
                        ReportProducer rp;
                        rp.doWhateverToProduceHtmlAndPdf()
                    

                    So I do understand what your approach requires, but I hope you can see why I do not wish to go down that route. But thanks for your suggestion anyway!

                    kshegunovK 1 Reply Last reply
                    0
                    • JonBJ JonB

                      @kshegunov
                      Yes, OK, I thought so.

                      The problem is, my produceReportAndWaitForFinish() in the top-level dialog does not have any kind of

                          // Create/Init the webengine and so on, put to render and all that goodness. 
                          QWebEngineView webEngine;
                      

                      inside it (and I don't want it to). It has more like:

                          //  Call *completely opaque* ReportProducer to generate HTML/PDF
                          //  Here we have *no knowledge* of how ReportProducer functions
                          //  and to maintain code separation/encapsulation we do not want to know here
                          //  We might completely change how ReportProducer works at any time
                          //  without affecting any code/behaviour here in this dialog.
                          ReportProducer rp;
                          rp.doWhateverToProduceHtmlAndPdf()
                      

                      So I do understand what your approach requires, but I hope you can see why I do not wish to go down that route. But thanks for your suggestion anyway!

                      kshegunovK Offline
                      kshegunovK Offline
                      kshegunov
                      Moderators
                      wrote on last edited by
                      #24

                      @JonB said in GUI event blocking:

                      The problem is, my produceReportAndWaitForFinish() in the top-level dialog does not have any kind of
                      [snip]
                      inside it (and I don't want it to). It has more like:

                      This is of no consequence, I've put the event loop/QWebEngineView here only for completeness. You can connect the grab/release slots directly from the button/custom class that generates the pdf respectively. Basically, the same as what you have now, but including 1 event override and putting in the grabMouse and releaseMouse at the appropriate places.

                      Read and abide by the Qt Code of Conduct

                      JonBJ 1 Reply Last reply
                      1
                      • kshegunovK kshegunov

                        @JonB said in GUI event blocking:

                        The problem is, my produceReportAndWaitForFinish() in the top-level dialog does not have any kind of
                        [snip]
                        inside it (and I don't want it to). It has more like:

                        This is of no consequence, I've put the event loop/QWebEngineView here only for completeness. You can connect the grab/release slots directly from the button/custom class that generates the pdf respectively. Basically, the same as what you have now, but including 1 event override and putting in the grabMouse and releaseMouse at the appropriate places.

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

                        @kshegunov
                        Hmm, so you mean in my dialog button:

                        blockInputs()
                        ReportProducer rp;
                        rp.doWhateverToProduceHtmlAndPdf()
                        releaseInputs()
                        

                        Right?

                        kshegunovK 1 Reply Last reply
                        0
                        • JonBJ JonB

                          @kshegunov
                          Hmm, so you mean in my dialog button:

                          blockInputs()
                          ReportProducer rp;
                          rp.doWhateverToProduceHtmlAndPdf()
                          releaseInputs()
                          

                          Right?

                          kshegunovK Offline
                          kshegunovK Offline
                          kshegunov
                          Moderators
                          wrote on last edited by
                          #26

                          Yep, that should work.

                          Read and abide by the Qt Code of Conduct

                          JonBJ 1 Reply Last reply
                          1
                          • kshegunovK kshegunov

                            Yep, that should work.

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

                            @kshegunov
                            So, to be pedantic, and hoping I'm not annoying you, I want precisely that code/behaviour wrapped around every single button handler I have anywhere in my code, just in case the code the button invokes happens to create a thread or process events which could cause re-entrance. Which was the reason I posted this thread in the very first place.

                            There is supposed to be nothing special about my doWhateverToProduceHtmlAndPdf(), it's a complete accident/coincidence that it happens to use something (a QWebEngineView) which itself has to spin an event loop to achieve its work. I might change that tomorrow. Which is why I wanted to isolate anything about its event behaviour down inside the class which creates the QWebEngineView and not up at the caller dialog level. This approach will work in this particular case, but not in other random places in code where a button's actions might happen to spin an event loop, and that bothers me....

                            Ah well, such are the dirty practicalities of coding...!

                            EDIT: Bear with me, I'm going to post a new thread to ask a particular question which would satisfy me, just to know if it's possible.... See https://forum.qt.io/topic/88481/is-it-possible-to-intercept-a-modal-dialog-s-event-loop

                            kshegunovK 1 Reply Last reply
                            0
                            • JonBJ JonB

                              @kshegunov
                              So, to be pedantic, and hoping I'm not annoying you, I want precisely that code/behaviour wrapped around every single button handler I have anywhere in my code, just in case the code the button invokes happens to create a thread or process events which could cause re-entrance. Which was the reason I posted this thread in the very first place.

                              There is supposed to be nothing special about my doWhateverToProduceHtmlAndPdf(), it's a complete accident/coincidence that it happens to use something (a QWebEngineView) which itself has to spin an event loop to achieve its work. I might change that tomorrow. Which is why I wanted to isolate anything about its event behaviour down inside the class which creates the QWebEngineView and not up at the caller dialog level. This approach will work in this particular case, but not in other random places in code where a button's actions might happen to spin an event loop, and that bothers me....

                              Ah well, such are the dirty practicalities of coding...!

                              EDIT: Bear with me, I'm going to post a new thread to ask a particular question which would satisfy me, just to know if it's possible.... See https://forum.qt.io/topic/88481/is-it-possible-to-intercept-a-modal-dialog-s-event-loop

                              kshegunovK Offline
                              kshegunovK Offline
                              kshegunov
                              Moderators
                              wrote on last edited by
                              #28

                              @JonB said in GUI event blocking:

                              This approach will work in this particular case, but not in other random places in code where a button's actions might happen to spin an event loop, and that bothers me....

                              You could in principle adapt it to the general case. Something along:

                              class UiGuard : public QObject
                              {
                              public:
                                  UiGuard(QWidget * ui)
                                      : widget(ui)
                                  {
                                      widget->grabMouse();
                                      widget->grabKeyboard();
                                      widget->installEventFilter(this);
                                  }
                              
                                  UiGuard::~UiGuard() override
                                  {
                                      widget->releaseKeyboard();
                                      widget->releaseMouse();
                                  }
                              
                                  bool eventFilter(QObject *, QEvent * event) override
                                  {
                                      return dynamic_cast<QInputEvent *>(event);  // Eat up the input events
                                  }
                              
                              private:
                                  QWidget * widget;
                              };
                              

                              And then you use in the places you have threads and/or event loops spinning, by just creating an instance:

                              // ...
                              {
                                  UiGuard guard(this);  // Prevent input events while in this method
                                  ReportProducer rp;
                                  rp.doWhateverToProduceHtmlAndPdf()
                              }

                              Read and abide by the Qt Code of Conduct

                              JonBJ 1 Reply Last reply
                              0
                              • kshegunovK kshegunov

                                @JonB said in GUI event blocking:

                                This approach will work in this particular case, but not in other random places in code where a button's actions might happen to spin an event loop, and that bothers me....

                                You could in principle adapt it to the general case. Something along:

                                class UiGuard : public QObject
                                {
                                public:
                                    UiGuard(QWidget * ui)
                                        : widget(ui)
                                    {
                                        widget->grabMouse();
                                        widget->grabKeyboard();
                                        widget->installEventFilter(this);
                                    }
                                
                                    UiGuard::~UiGuard() override
                                    {
                                        widget->releaseKeyboard();
                                        widget->releaseMouse();
                                    }
                                
                                    bool eventFilter(QObject *, QEvent * event) override
                                    {
                                        return dynamic_cast<QInputEvent *>(event);  // Eat up the input events
                                    }
                                
                                private:
                                    QWidget * widget;
                                };
                                

                                And then you use in the places you have threads and/or event loops spinning, by just creating an instance:

                                // ...
                                {
                                    UiGuard guard(this);  // Prevent input events while in this method
                                    ReportProducer rp;
                                    rp.doWhateverToProduceHtmlAndPdf()
                                }
                                JonBJ Online
                                JonBJ Online
                                JonB
                                wrote on last edited by JonB
                                #29

                                @kshegunov
                                Yes indeed, but as we said:

                                And then you use in the places you have threads and/or event loops spinning, by just creating an instance:

                                In principle I have no idea where code might have "threads and/or event loops". (Seriously, it took me days & debugging to even discover this particular button invoked code which did that, I had no idea it did! Which is my point.) So in effect I'd like this around every single UI slot handler (or probably 99% of them but not 1%), and I'm not prepared to edit thousands of places to do that! :)

                                kshegunovK 1 Reply Last reply
                                0
                                • JonBJ JonB

                                  @kshegunov
                                  Yes indeed, but as we said:

                                  And then you use in the places you have threads and/or event loops spinning, by just creating an instance:

                                  In principle I have no idea where code might have "threads and/or event loops". (Seriously, it took me days & debugging to even discover this particular button invoked code which did that, I had no idea it did! Which is my point.) So in effect I'd like this around every single UI slot handler (or probably 99% of them but not 1%), and I'm not prepared to edit thousands of places to do that! :)

                                  kshegunovK Offline
                                  kshegunovK Offline
                                  kshegunov
                                  Moderators
                                  wrote on last edited by
                                  #30

                                  @JonB said in GUI event blocking:

                                  So in effect I'd like this around every single UI slot handler, and I'm not prepared to edit thousands of places to do that!

                                  That might be rather bad idea, as event filters do hurt performance, even though in this case it's only a single dynamic_cast. You're trying to fight against the way UIs work - i.e. event driven. You could install that kind of class directly on the QCoreApplication object (with the proper modification), where you'd filter the events globally, but seems rather dubious decision.

                                  Read and abide by the Qt Code of Conduct

                                  JonBJ 1 Reply Last reply
                                  1
                                  • kshegunovK kshegunov

                                    @JonB said in GUI event blocking:

                                    So in effect I'd like this around every single UI slot handler, and I'm not prepared to edit thousands of places to do that!

                                    That might be rather bad idea, as event filters do hurt performance, even though in this case it's only a single dynamic_cast. You're trying to fight against the way UIs work - i.e. event driven. You could install that kind of class directly on the QCoreApplication object (with the proper modification), where you'd filter the events globally, but seems rather dubious decision.

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

                                    @kshegunov

                                    You could install that kind of class directly on the QCoreApplication object (with the proper modification), where you'd filter the events globally, but seems rather dubious decision.

                                    That indeed sounds more like it, because I could do that in my own object which tests a flag to cause this behaviour, and where the flag would be set by the called code which knows it needs it and not the calling code which does not know that. This could be precisely what I am looking for....

                                    You might look to look at my new https://forum.qt.io/topic/88481/is-it-possible-to-intercept-a-modal-dialog-s-event-loop now, where I'm asking just that about application versus dialog event loops....

                                    kshegunovK A 2 Replies Last reply
                                    0
                                    • JonBJ JonB

                                      @kshegunov

                                      You could install that kind of class directly on the QCoreApplication object (with the proper modification), where you'd filter the events globally, but seems rather dubious decision.

                                      That indeed sounds more like it, because I could do that in my own object which tests a flag to cause this behaviour, and where the flag would be set by the called code which knows it needs it and not the calling code which does not know that. This could be precisely what I am looking for....

                                      You might look to look at my new https://forum.qt.io/topic/88481/is-it-possible-to-intercept-a-modal-dialog-s-event-loop now, where I'm asking just that about application versus dialog event loops....

                                      kshegunovK Offline
                                      kshegunovK Offline
                                      kshegunov
                                      Moderators
                                      wrote on last edited by
                                      #32

                                      @JonB said in GUI event blocking:

                                      You might look to look at my new

                                      Will do, but later, as I have some work to do now. :)

                                      Read and abide by the Qt Code of Conduct

                                      JonBJ 1 Reply Last reply
                                      1
                                      • kshegunovK kshegunov

                                        @JonB said in GUI event blocking:

                                        You might look to look at my new

                                        Will do, but later, as I have some work to do now. :)

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

                                        @kshegunov
                                        Of course you must, and get back to fixing those nuclear reactors/missiles you work on :) Thanks so much for your comments!

                                        1 Reply Last reply
                                        2
                                        • JonBJ JonB

                                          @kshegunov

                                          You could install that kind of class directly on the QCoreApplication object (with the proper modification), where you'd filter the events globally, but seems rather dubious decision.

                                          That indeed sounds more like it, because I could do that in my own object which tests a flag to cause this behaviour, and where the flag would be set by the called code which knows it needs it and not the calling code which does not know that. This could be precisely what I am looking for....

                                          You might look to look at my new https://forum.qt.io/topic/88481/is-it-possible-to-intercept-a-modal-dialog-s-event-loop now, where I'm asking just that about application versus dialog event loops....

                                          A Offline
                                          A Offline
                                          ambershark
                                          wrote on last edited by
                                          #34

                                          @JonB Another thing you could try is to disable all the controls you don't want being activated during processing. Then enable them when processing is done.

                                          This would be better handled via a modal dialog with a progress bar (even an indefinite one) and a cancel button.

                                          But the other option is just setting QWidget::setEnabled(false). People using the app may be confused why things were disabled all of a sudden though so I still think the modal progress dialog would be the best way.

                                          But you could keep a list of QActions and/or QWidgets you want disabled during that call and just iterate through the list and setEnabled(false) until complete then iterate through again and enable them.

                                          Just another suggestion that may work for you. You have lots of choices on how to handle this and I don't have any great advice on the "best" way so just throwing things out as I think of them. :)

                                          My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

                                          JonBJ 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