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. Native QPrintDialog does not process events during it's execution
QtWS25 Last Chance

Native QPrintDialog does not process events during it's execution

Scheduled Pinned Locked Moved Solved General and Desktop
9 Posts 3 Posters 351 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    Siemaguysitsme
    wrote on last edited by
    #1

    Hello,

    I've got a quite specific problem I had not found an answer to.
    For the context, my app needs to log off the user who stays inactive for a certain amount of time. This usually works fine, with an exception to the native Windows QPrintDialog.

    The problem is, that during the execution of 'exec()' method, the dialog is impossible to be closed without interaction of the user - user must literally click one of the buttons on the dialog (cancel, accept, close, etc.) for it to close itself. No signals, close methods nor even calling the destructor of QPrintDialog works out. From what I've found out reading the sources, the reason for that is that the creation and destruction of Windows handle to the native dialog is locked within the scope of aforemetioned 'exec()' method.

    I'm looking for ways to come around this issue. I've got some ideas on what I can do but would love to hear from more experienced Qt devs.

    tl;dr
    I want to call 'QPrintDialog::close()' method so it actually closes the dialog if the dialog is shown.

    Cheers!

    JonBJ 1 Reply Last reply
    0
    • jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      What exactly do you mean by "my app needs to log off the user" - what happens then?

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • S Offline
        S Offline
        Siemaguysitsme
        wrote on last edited by
        #3

        In short, all the windows are closed and their state is remembered if needed. It is done by closing the QMainWindow, which closes all it's children windows but the QPrintDialog.

        1 Reply Last reply
        0
        • S Siemaguysitsme

          Hello,

          I've got a quite specific problem I had not found an answer to.
          For the context, my app needs to log off the user who stays inactive for a certain amount of time. This usually works fine, with an exception to the native Windows QPrintDialog.

          The problem is, that during the execution of 'exec()' method, the dialog is impossible to be closed without interaction of the user - user must literally click one of the buttons on the dialog (cancel, accept, close, etc.) for it to close itself. No signals, close methods nor even calling the destructor of QPrintDialog works out. From what I've found out reading the sources, the reason for that is that the creation and destruction of Windows handle to the native dialog is locked within the scope of aforemetioned 'exec()' method.

          I'm looking for ways to come around this issue. I've got some ideas on what I can do but would love to hear from more experienced Qt devs.

          tl;dr
          I want to call 'QPrintDialog::close()' method so it actually closes the dialog if the dialog is shown.

          Cheers!

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by
          #4

          @Siemaguysitsme
          If it only the blocking exec() which is at issue try non-blocking void QPrintDialog::open(QObject *receiver, const char *member)? But not sure whether it will help with being able to "kill" the dialog/handle, you could test.

          1 Reply Last reply
          0
          • S Offline
            S Offline
            Siemaguysitsme
            wrote on last edited by
            #5

            Unfortunatelly it does not work. While the Qt window object is destroyed when calling 'close()' on a dialog made with 'exec()' or 'open()' or even 'show()', the Windows handle to the dialog remains unchanged. Check the source code for the 'exec()' method at QT6.2.4: qtbase\src\printsupport\dialogs\qprintdialog_win.cpp:216. The Windows Print Dialog handle is created, managed and destroyed within the scope of this method up there. This is the reason for which the window can not be closed programmatically.

            1 Reply Last reply
            0
            • JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #6

              If you have looked at the source code then what you see is what you get! I doubt anyone else will know how to "change that behaviour" without changing the source you see!

              I am not a Windows expert. You can make various native Win32 calls to find handles of open windows etc. (e.g. I think there is one to find by window title, but that might be for external programs rather than process-internal ones?). Can you use that to find the native Print Dialog and close/kill it by window handle? Assuming Qt would not then barf upon return.

              Another possibility might be to handle the print dialog yourself with Windows calls instead of the QPrintDialog wrapper? Don't know how much you need the Qt wrapper for your print job.

              1 Reply Last reply
              0
              • S Offline
                S Offline
                Siemaguysitsme
                wrote on last edited by
                #7

                I have tried to rewrite the QPrintDialog but ended up just copying more and more QT files due to dependencies that these dialogs use. I mean the stuff like QPrintDialogPrivate and stuff. I'm quite very unsure of how would I get my hands on it. Can you try to give me some hints on that matter?
                Regarding the your other recommendation - i definitely will try that out, but will give you a feedback on this tomorrow at the soonest.

                jsulmJ 1 Reply Last reply
                0
                • S Siemaguysitsme

                  I have tried to rewrite the QPrintDialog but ended up just copying more and more QT files due to dependencies that these dialogs use. I mean the stuff like QPrintDialogPrivate and stuff. I'm quite very unsure of how would I get my hands on it. Can you try to give me some hints on that matter?
                  Regarding the your other recommendation - i definitely will try that out, but will give you a feedback on this tomorrow at the soonest.

                  jsulmJ Offline
                  jsulmJ Offline
                  jsulm
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  @Siemaguysitsme said in Native QPrintDialog does not process events during it's execution:

                  Can you try to give me some hints on that matter?

                  If you want to modify Qt then don't copy Qt source code files into your project. Instead, change Qt code and rebuild the Qt module you changed. But this is not trivial to do. You should rather look for a solution without changing Qt code. Is the closeEvent called?

                  https://forum.qt.io/topic/113070/qt-code-of-conduct

                  1 Reply Last reply
                  0
                  • S Offline
                    S Offline
                    Siemaguysitsme
                    wrote on last edited by Siemaguysitsme
                    #9

                    Alright, it took me a bit more than I wanted but here is how it went:

                    1. Created a TTimer with Timeout slot to indicate a request for closing the window.
                    2. Subclassed QPrintDialog into TPrintDialog.
                    3. For QT5.7 I've connected the TTimer::Timeout signal to TPrintDialog::close. This is not necessary in QT>=6, this is because for some reason the close method is not invoked when destroying the window in QT5.7.
                    4. Overridden the TPrintDialog::closeEvent - this is where the "hacking" begins - inside I find a HWND handle to a PrintDlg with GetWindow((HWND)this->winId(), GW_HWNDPREV) , which gets the window that is before the this->winId() in Z axis on the screen, and then send a WM_CLOSE message to the handle with PostMessage(handle, WM_CLOSE, 0, 0),
                      and set the handle to nullptr;

                    Feel free to lecture me if you see something concerning about that or something that could be done better :)

                    Here is a source code for the tests, I tried to make it as compact as possible:

                    app.hpp

                    #pragma once
                    #include <QMainWindow>
                    #include <QTime>
                    #include <QPrintDialog>
                    
                    class TAutoLogoff : public QObject
                    {
                    	Q_OBJECT
                    public:
                    	explicit TAutoLogoff(QObject* parent);
                    	~TAutoLogoff();
                    	virtual void timerEvent(QTimerEvent* e) override;
                    	void StopTimer();
                    public: signals:
                    	void Timeout();
                    private:
                    	ULONGLONG m_interval;
                    	ULONGLONG m_begin_tick;
                    	int m_timer_id;
                    };
                    
                    class TMainWindow	: public QMainWindow
                    {
                    	Q_OBJECT
                    public:
                    	explicit TMainWindow(QWidget* parent = nullptr);
                    	~TMainWindow();
                    	void Process();
                    public slots:
                    	void OnTimerExit();
                    private:
                    	TAutoLogoff* m_auto_logoff;
                    };
                    
                    class TPrintDialog : public QPrintDialog
                    {
                    	Q_OBJECT
                    public:
                    	explicit TPrintDialog(QWidget* parent = nullptr);
                    	~TPrintDialog();
                    	virtual void closeEvent(QCloseEvent* e) override;
                    };
                    

                    app.cpp

                    #include "app.hpp"
                    #include <QApplication>
                    #include <QTimer>
                    #include <windows.h>
                    
                    static TAutoLogoff* g_auto_logoff = nullptr;
                    
                    TAutoLogoff::TAutoLogoff(QObject* parent)
                    	: QObject{ parent }
                    	, m_begin_tick{ GetTickCount64() }
                    	, m_interval{ 2500 }
                    	, m_timer_id{ -1 }
                    {
                    	m_timer_id = QObject::startTimer(m_interval, Qt::CoarseTimer);
                    }
                    
                    TAutoLogoff::~TAutoLogoff()
                    {
                    	qDebug() << "TAutoLogoff::~TAutoLogoff";
                    }
                    
                    void TAutoLogoff::timerEvent(QTimerEvent* e)
                    {
                    	if (m_begin_tick + m_interval < GetTickCount64())
                    	{
                    		StopTimer();
                    		emit Timeout();
                    	}
                    }
                    
                    void TAutoLogoff::StopTimer()
                    {
                    	if (m_timer_id != -1)
                    	{
                    		QObject::killTimer(m_timer_id);
                    		m_timer_id = QObject::startTimer(m_interval, Qt::CoarseTimer);
                    	}
                    }
                    
                    TMainWindow::TMainWindow(QWidget* parent)
                    	: QMainWindow{ parent }
                    	, m_auto_logoff{ new TAutoLogoff{ this } }
                    {
                    	g_auto_logoff = m_auto_logoff;
                    	connect(m_auto_logoff, &TAutoLogoff::Timeout, this, &TMainWindow::OnTimerExit, Qt::QueuedConnection);
                    	QTimer::singleShot(0, this, &TMainWindow::Process);
                    }
                    
                    TMainWindow::~TMainWindow()
                    {
                    	qDebug() << "TMainWindow::~TMainWindow";
                    	g_auto_logoff = nullptr;
                    }
                    
                    void TMainWindow::Process()
                    {
                    	TPrintDialog pd{ this };
                    	if (pd.exec() != QPrintDialog::Accepted)
                    		return;
                    }
                    
                    void TMainWindow::OnTimerExit()
                    {
                    	qDebug() << "TMainWindow::OnTimerExit";
                    	close();
                    }
                    
                    TPrintDialog::TPrintDialog(QWidget* parent)
                    	: QPrintDialog{ parent }
                    {
                    	connect(g_auto_logoff, &TAutoLogoff::Timeout, this, &TPrintDialog::close);
                    }
                    
                    TPrintDialog::~TPrintDialog()
                    {
                    	qDebug() << "TPrintDialog::~TPrintDialog";
                    }
                    
                    void TPrintDialog::closeEvent(QCloseEvent* e)
                    {
                    	qDebug() << "TPrintDialog::closeEvent";
                    	auto* handle = GetWindow((HWND)this->winId(), GW_HWNDPREV);
                    	if (handle != nullptr)
                    	{
                    		PostMessage(handle, WM_CLOSE, 0, 0);
                    		handle = nullptr;
                    	}
                    	QPrintDialog::closeEvent(e);
                    }
                    

                    main.cpp

                    #include <QApplication>
                    #include "app.hpp"
                    
                    int main(int argc, char *argv[])
                    {
                        QApplication app{ argc, argv };
                        TMainWindow window{};
                        window.show();
                        return app.exec();
                    }
                    
                    1 Reply Last reply
                    0
                    • S Siemaguysitsme has marked this topic as solved on

                    • Login

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