Showing hidden top-level window

732 views
Skip to first unread message

Marcus Ottosson

unread,
Jan 5, 2014, 5:28:57 PM1/5/14
to python_in...@googlegroups.com
This might very well be a too specific question for this group and is outside of Maya, and I suspect Justin you are more on the Linux side of Python, and its more than reasonable to assume the problem is not unique to Qt, but I figure asking couldn't hurt.

In Maya, I would fetch a handle to the child window by traversing the existing handles of the parent window and finally run .show(), but how does this work on top-level windows?

So I'm hiding a PyQt main window and showing it using the pywin32 library with its method win32gui.ShowWindow
win32gui.ShowWindow(hsnd, win32con.SW_SHOW)

Which I presume is the equivalent of the ctypes (for those who would rather use it over pywin32)
ctypes.windll.user32.ShowWindow(hsnd, win32con.SW_SHOW)

The window shows up, but I suspect the event loop is still on hold as there is no interacting with the window, its a complete zombie.

How would I return control to the QApplication event loop (if that indeed is the problem) after showing it via the Win32 API?

Here is some sample code (you need pywin32 installed and you need to be on Windows). Minimize and re-run the code to have the window pop back up. Uncomment closeEvent to make it hide when you try and close it. Re-running the code this time will bring up a static window with no ability to interact.

import win32gui, win32con
 
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

class Window(QWidget):
   
def __init__(self, parent=None):
       
super(Window, self).__init__(parent)
       
self.setWindowTitle('Test Window')
       
self.resize(300, 200)

       
QPushButton('Test Button', self)

   
# def closeEvent(self, event):
   
#     self.hide()
   
#     event.ignore()


def new_window():
   
import sys
    app
= QApplication(sys.argv)
    win
= Window()
    win
.show()
    sys
.exit(app.exec_())


HWND
= None
def foreach_window(hwnd, lParam):
   
if 'Test Window' == win32gui.GetWindowText(hwnd):
       
global HWND
        HWND
= hwnd
   
return True


win32gui
.EnumWindows(foreach_window, 0)


if HWND:
   
print "Restoring existing Window"
    win32gui
.ShowWindow(HWND, win32con.SW_RESTORE)
else:
   
print "No existing Window found"
    new_window
()





Justin Israel

unread,
Jan 5, 2014, 6:03:23 PM1/5/14
to python_in...@googlegroups.com
Maybe something to do with widget events not being generated (stab in the dark) when you go through the window system instead of qt?
Out of curiosity, what is the use case for controlling the windows this way?


--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/f094bfcc-a222-4173-b1f0-62b2780ef7db%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Marcus Ottosson

unread,
Jan 6, 2014, 2:46:10 AM1/6/14
to python_in...@googlegroups.com
Right, I completely forgot a use case.

I'm looking to bring up an existing application if one exists when the application is being run twice, such as a launcher type application. If the launcher is already up, I'd like to restore it, rather than create a new instance.

There might be a better way?



For more options, visit https://groups.google.com/groups/opt_out.



--
Marcus Ottosson
konstr...@gmail.com

Justin Israel

unread,
Jan 6, 2014, 2:59:16 AM1/6/14
to python_in...@googlegroups.com
No I suppose you are right. That probably is a platform specific solution to create a singleton application. Can you re-implement the event() method on your window, to print and examine if it is receiving any kind of events when you manipulate the visibility from win32?


Marcus Ottosson

unread,
Jan 6, 2014, 3:42:16 AM1/6/14
to python_in...@googlegroups.com
Good idea. 

The act of showing the window doesn't seem to produce any events, but there then is a QEnterEvent being triggered when entering it with the cursor after being shown via win32 (no leave event), along with events hovering the native part of the ui, such as the titlebar, as well as QMouseEvents when clicking within the static area.

I tried putting a call to show() within event() when isinstance(event, QEnterEvent) == True and the UI came back to life when entering. Getting closer! 

Is there a way to trigger such a signal via win32 you think?

In Nathan's post about getting a QWidget instance via a pointer, it seems I should be able to manipulate the window as though it were a local python object, doing stuff like calling show() myself. However I can't figure out how to get a useful pointer for that method. Could that be something to persue?

I did see mention of creating a singleton for an application somewhere on Google, so perhaps the problem is bigger than it seems.





For more options, visit https://groups.google.com/groups/opt_out.



--
Marcus Ottosson
konstr...@gmail.com

Justin Israel

unread,
Jan 6, 2014, 4:11:46 AM1/6/14
to python_in...@googlegroups.com
Check out this older article. It may give you some ideas on how to rethink the solution to the problem:

An interesting modification that you might try to the socket approach is to actually accept a "raise_()" signal over the socket to tell the existing one to show and raise to the top:

  1. First instance of the application starts up and creates its local socket. It wait on this socket (using signal/slot) for a message
  2. Second instance tries to start, and checks if it can create the local socket. It will fail because the socket exists already
  3. Before the second instance quits, it can connect to the second (as opposed to trying to bind it) and send the signal to raise the existing one



Justin Israel

unread,
Jan 6, 2014, 4:12:49 AM1/6/14
to python_in...@googlegroups.com
I forgot to mention. This approach is also platform independent, and you don't have to mess with window handles outside of the qt event loop.

Marcus Ottosson

unread,
Jan 7, 2014, 3:00:54 AM1/7/14
to python_in...@googlegroups.com
This seems like the right approach. 

For future reference, I also found a working example here along with some more reference of the same approach here

They all seem to circulate around a particular QSingleApplication which is part of an extension to Qt called QtSolutions which sets up a server in the first-born application and connects to it in the following to trigger a show()

It just so happens that in this particular application I was setting up an RPC server anyway and will try triggering show() that way as well.

Thanks Justin for your help.



For more options, visit https://groups.google.com/groups/opt_out.



--
Marcus Ottosson
konstr...@gmail.com

Marcus Ottosson

unread,
Jan 8, 2014, 1:17:04 AM1/8/14
to python_in...@googlegroups.com
For future reference, and for those interested in trying this with the win32 api. This is using pywin32 and pyqt5, the same thing applies using pyqt4 and ctypes.


import win32gui, win32con
 
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

class Window(QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.setWindowTitle('Test Window')
        self.resize(300, 200)

        QPushButton('Test Button', self)

    def closeEvent(self, event):
        self.hide()
        event.ignore()

    def event(self, event):
        if isinstance(event, QEnterEvent):
            self.show()
        return super(Window, self).event(event)

def new_window():
    import sys
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

HWND = None
def foreach_window(hwnd, lParam):
    if 'Test Window' == win32gui.GetWindowText(hwnd):
        global HWND
        HWND = hwnd
    return True

win32gui.EnumWindows(foreach_window, 0)

if HWND:
    print "Restoring existing Window"
    # widget = sip.wrapinstance(HWND, QWidget)
    # print widget
    win32gui.ShowWindow(HWND, win32con.SW_RESTORE)
else:
    print "No existing Window found"
    new_window()

Reply all
Reply to author
Forward
0 new messages