How do I make a window (already running task) visible using pywinauto? - python

Besides looking for answers on this site, I checked out
pywinauto.application module
and
Getting Started Guide
but I'm still stumped.
I manually start notepad and want the first while block of the following code to make the notepad window visible. The second while block works but I am confused about the line
dlg_spec = app.UntitledNotepad
What is going on here? What kind of a python method is this?
Question: How do I get the first while block of code make the window titled
Untitled - Notepad
visible?
#--------*---------*---------*---------*---------*---------*---------*---------*
# Desc: Set focus on a window
# #--------*---------*---------*---------*---------*---------*---------*---------*
import sys
import pywinauto
# # Manually started Notepad
# # Want to make it visible (windows focus)
# # Program runs, but...
while 1:
handle = pywinauto.findwindows.find_windows(title='Untitled - Notepad')[0]
app = pywinauto.application.Application()
ac = app.connect(handle=handle)
print(ac)
topWin = ac.top_window_()
print(topWin)
sys.exit()
# # Working Sample Code
while 0:
app = pywinauto.Application().start('notepad.exe')
# describe the window inside Notepad.exe process
# # ?1: '.UntitledNotepad' - huh?
dlg_spec = app.UntitledNotepad
# wait till the window is really open
actionable_dlg = dlg_spec.wait('visible')
sys.exit()
For convenience this code does the trick:
# # Manually started Notepad
# # Want to make it visible (windows focus).
# #
# # Two or three lines solution provided by
# # Vasily Ryabov's overflow answer
# # (wrapper ribbon and bow stuff).
while 1:
app = pywinauto.application.Application().connect(title="Untitled - Notepad")
dlg_spec = app.window(best_match="UntitledNotepad")
dlg_spec.set_focus()
sys.exit()

I would suggest you using the win32gui library for this task as shown below:
import win32gui
hwnd = win32gui.FindWindow(None, 'Notepad')
win32gui.SetForegroundWindow(hwnd)
win32gui.ShowWindow(hwnd, 9)
The number 9 represents SW_RESTORE as shown here

Well, the first while loop should be re-written using the same methods except find_windows (it's low level and not recommended for direct usage). You need method .set_focus() to bring the window to foreground.
app = pywinauto.Application().connect(title="Untitled - Notepad")
app.UntitledNotepad.set_focus()
Creating window specification dlg_spec = app.UntitledNotepad means that app method __getattribute__ is called. Finally this line is equivalent to dlg_spec = app.window(best_match="UntitledNotepad"). To find the actual wrapper you need to call .wait(...) or .wrapper_object().
But when you call an action (like .set_focus()), Python can do the wrapper_object() call for you implicitly (while accessing attribute set_focus dynamically).

Related

PyQt different windows during a script

I developed a script for pcba testing and it works fine.
Now I want to implement a very simple flow like this:
Window popup just to let the user start the test
Run the test without any window (the terminal is just fine)
Window popup to show pass/fail result of the test
I already developed the two windows in two other .py files, which run without any problems when "standalone".
If I implement these two within the script, the first appears normally, but the second opens and closes istantly.
Without being too complicated, the test function is defined in serial.py, the first window in infoBox.py and the final window in getResult.py.
Inside the serial.py I tried:
import infoBox
import getResult
[...]
def work():
[...]
if __name__ == '__main__':
app1 = QApplication([])
win1 = infoBox.infoBox("""Turn on 24V alim and press OK.""")
win1.show()
app.exec()
[...]
work()
[...]
app2 = QApplication([])
generateWindow(esiti=lst_esiti, info=lst_info) # <------- this calls getResult.getResult class and .show() the widget
win2 = getResult.getResult(lst_esito=lst_esiti, lst_info=lst_info)
win2.show()
app.exec()
So, when serial.py is lounched by terminal, the infoBox instance appears, the work() function does what it has to, and the getResult instance (which has a OK button to be closed) is instantly closed.
Maybe the error is the definition of two QApplication? I tried a lot but I'm not able to manage it.
Thank you very much.
Finally I came up with this.
It was easier than expected...
Solved opening a unique QApplication and call app.exec() after each subclass calling.

How to get ownership of QClipboard

I'm using PyQt 5.15 and python 3.10 on Windows 10.
In my application I have a loop which checks if there is a image in the QClipboard. After reading it I like to clear the clipboard and wait for the next image which will be copied by the user from any other application (like screenshot etc.)
But after QApplication.clipboard().clear(QClipboard.Clipboard) the pixmap is not deleted and still has its old size. Tested with QApplication.clipboard().pixmap().height()
The call of QApplication.clipboard().ownsClipboard() returns allways False, so I think I have to take the ownership of the clipboard. I can't find any reference to do that.
According to the documentation:
bool QClipboard::ownsClipboard() const
Returns true if this clipboard object owns the clipboard data; otherwise returns false.
I think the data in the clipboard are protected by the application where I copied the data from. So, how can I get this ownership?
I found some information on this site: How ownership of the Windows clipboard is tracked in Win32, but I have no idea how to adapt this to python.
The basic working loop is like this:
def copyloop(self):
clipboard = QGuiApplication.clipboard()
mimeData = clipboard.mimeData (QClipboard.Clipboard)
loop = True
while loop:
if mimeData.hasImage ():
pixmap = QPixmap(mimeData.imageData ())
# do something with pixmap
#self.label.setPixmap(pixmap)
QMessageBox.information(self, "Clip me", "before clear clipboard: " + str(clipboard.pixmap(QClipboard.Clipboard).width()))
clipboard.clear(QClipboard.Clipboard)
QMessageBox.warning(self, "Clip me", "After clear clipboard: " + str(clipboard.pixmap(QClipboard.Clipboard).width()))
loop = False
else:
#self.label.setPixmap(QPixmap())
loop = True
The code works as long as I use it in standalone (simple GUI with one QDialog class), but in my programm (Stacked layouts with QMainwindow's and mutiple Dialogs) it doesn't clear the clipboard.
EDIT:
Working loop including comment of #Musicamente:
while pushButton.isChecked():
mimeData = clipboard.mimeData()
if mimeData.hasImage ():
pixmap = QPixmap(mimeData.imageData ())
# do something with pixmap
QApplication.processEvents()
From the docs of MS Windows App development
In general, the clipboard owner is the window that last placed data in
clipboard. The EmptyClipboard function assigns clipboard ownership.
Seems to be the nearest answer.
Therefore its not possible to set it ;-)
EDIT:
Finally I got a maximum reduced sample to demonstrate the fault:
import sys
from PyQt5.QtWidgets import *
# if the following import is used, QApplication.clipboard() will not work as expected.
# comment it out, copy any text and run. Enjoy the different output
from pywinauto import application
def clipboard_test():
print("-> clipboard_test")
clipboard = QApplication.clipboard()
print("Owner =", clipboard.ownsClipboard())
print("before clear =", clipboard.text(mode=clipboard.Clipboard))
clipboard.clear(clipboard.Clipboard)
QApplication.processEvents()
print("after clear =", clipboard.text(mode=clipboard.Clipboard))
clipboard.setText(":-)", mode=clipboard.Clipboard)
print("new content (should ;-)) =", clipboard.text(mode=clipboard.Clipboard))
if __name__ == "__main__":
app = QApplication(sys.argv)
clipboard_test()
sys.exit(app.exec_())

Re-launch a software minimized

I run MediaPlayerClassic, as a minimized window in the taskbar (see here and the other answer too), with:
import subprocess, win32con, win32gui, win32process, win32api
info = subprocess.STARTUPINFO()
info.dwFlags = subprocess.STARTF_USESHOWWINDOW
info.wShowWindow = win32con.SW_MINIMIZE
app = subprocess.Popen(r'c:\path\to\mpc-h64.exe "d:\mp3\song.mp3"', startupinfo=info)
It works. But sometimes, I would like to replace the currently-playing-song by another one, and I'm sending the same previous code (with another mp3).
Since MediaPlayerClassic allows only 1 instance, re-running the previous code does replace the currently-playing-song by the new one indeed, and this is what I want, so it works too. Except that the new window is no more minimized. This is probably because no new process is started: it reuses the previously existing process, and MediaPlayerClassic "opens the window" when it receives a new MP3 to play (?).
Adding:
time.sleep(0.2) # required, if not the next action happens before the MP3 changes
def callback(hwnd, hwnds):
text = win32gui.GetWindowText(hwnd)
if win32gui.GetClassName(hwnd) == 'MediaPlayerClassicW':
win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE)
return True
win32gui.EnumWindows(callback, [])
nearly solves it, except that the window quickly flashes and then minimizes.
Question: how to relaunch a .exe already started like MediaPlayerClassic and keep it minimized? Should something else than subprocess.Popen be used here?

vtkRenderWindowInteractor stalling program

I've been working on a prototype application in Python for stereoscopic imaging of a 3D model using VTK, but I'm running into some issues on the interface end of things. The goal of the code below at the moment is to zoom in on both renderWindows when the middlemouse is pressed. However, upon calling the vtkRenderWindowInteractor.Start() function, my vtkRenderWindowInteractors are effectively stalling out the entire program as if they were being run in the same thread. Even more curious is that keyboard interrupts are not being thrown when I use CTRL-C (I'm working in UNIX shell) until I close the render windows manually using the 'x' button. If I just close the window manually without hitting CTRL-C, the program picks up directly after the Start() call (e.g. in the code below, the infinite while loop). I've provided a sequence of screen captures at the end of this post to visualize exactly what is happening in the case that my explanation is confusing.
I've tried multiple workarounds to remedy this but none so far have worked. Threading the renders into isolated threads made no difference even when I tried using ncurses for input, while forking them to a new process resulted in some OS issues that I'd rather not deal with. The most current interactor styles method (shown below) where I just use built-in VTK listeners works to a degree, allowing me to detect inputs when the windows are in focus and the interactors are active, but because of the lack of association between the camera and the MyInteractorStyle class, I can't really access the cameras without the inclusion a loop after the Start() call, which leads me right back to where I started.
Any thoughts? Am I just misunderstanding how VTK's render tools are supposed to be used?
from vtk import*
import os.path
#import thread
#import time
#import threading
#import curses
class MyInteractorStyle(vtk.vtkInteractorStyleTrackballCamera):
pos1 = [0, 0, 200]
foc1 = [0, 0, 0]
pos2 = [40, 0, 200]
foc2 = [0, 0, 0]
def __init__(self,parent=None):
self.AddObserver("MiddleButtonPressEvent", self.middleButtonPressEvent)
self.AddObserver("MiddleButtonReleaseEvent", self.middleButtonReleaseEvent)
def middleButtonPressEvent(self,obj,event):
print "Middle button pressed"
self.pos1[2] += 10
self.pos2[2] += 30
self.OnMiddleButtonDown()
return
def middleButtonReleaseEvent(self,obj,event):
print "Middle button released"
self.OnMiddleButtonUp()
return
def main():
# create two cameras
camera1 = vtkCamera()
camera1.SetPosition(0,0,200)
camera1.SetFocalPoint(0,0,0)
camera2 = vtkCamera()
camera2.SetPosition(40,0,200)
camera2.SetFocalPoint(0,0,0)
# create a rendering window and renderer
ren1 = vtkRenderer()
ren1.SetActiveCamera(camera1)
ren2 = vtkRenderer()
ren2.SetActiveCamera(camera2)
# create source
reader = vtkPolyDataReader()
path = "/home/compilezone/Documents/3DSlicer/SlicerScenes/LegoModel-6_25/Model_5_blood.vtk"
reader.SetFileName(path)
print(path)
reader.Update()
# create render window
renWin1 = vtkRenderWindow()
renWin1.AddRenderer(ren1)
renWin2 = vtkRenderWindow()
renWin2.AddRenderer(ren2)
# create a render window interactor
inputHandler = MyInteractorStyle()
iren1 = vtkRenderWindowInteractor()
iren1.SetRenderWindow(renWin1)
iren1.SetInteractorStyle(inputHandler)
iren2 = vtkRenderWindowInteractor()
iren2.SetRenderWindow(renWin2)
iren2.SetInteractorStyle(inputHandler)
# mapper
mapper = vtkPolyDataMapper()
mapper.SetInput(reader.GetOutput())
# actor
actor = vtkActor()
actor.SetMapper(mapper)
# assign actor to the renderer
ren1.AddActor(actor)
ren2.AddActor(actor)
# enable user interface interactor
iren1.Initialize()
iren2.Initialize()
renWin1.Render()
renWin2.Render()
iren1.Start()
iren2.Start()
print "Test"
while 1:
pos1 = iren1.GetInteractorStyle().pos1
foc1 = iren1.GetInteractorStyle().foc1
pos2 = iren2.GetInteractorStyle().pos2
foc2 = iren2.GetInteractorStyle().foc2
print
if __name__ == '__main__':
main()
Program running
KeyboardInterrupt (CTRL-C hit and echoed in terminal but nothing happens)
Render windows manually closed, KeyboardInterrupt thrown
Calling Start() on a RenderWindowInteractor starts the event loop necessary to execute render events, much as the event loop in a GUI. So what you're trying to do, starting two event loops, doesn't really make sense.
A conceptual workaround would be to not call Start on the RenderWindowInteractors but to write a small GUI with multiple toolkit-specific RenderWindowInteractors and use that GUI's event loop.
As an example, here's how this is done with GUI toolkit-specific code in tvtk's wxVtkRenderWindowInteractor class, which doesn't call start on the RenderWindowInteractor but instead uses the GUI's event loop to manage events:
def wxVTKRenderWindowInteractorConeExample():
"""Like it says, just a simple example
"""
# every wx app needs an app
app = wx.PySimpleApp()
# create the top-level frame, sizer and wxVTKRWI
frame = wx.Frame(None, -1, "wxVTKRenderWindowInteractor", size=(400,400))
widget = wxVTKRenderWindowInteractor(frame, -1)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(widget, 1, wx.EXPAND)
frame.SetSizer(sizer)
frame.Layout()
# It would be more correct (API-wise) to call widget.Initialize() and
# widget.Start() here, but Initialize() calls RenderWindow.Render().
# That Render() call will get through before we can setup the
# RenderWindow() to render via the wxWidgets-created context; this
# causes flashing on some platforms and downright breaks things on
# other platforms. Instead, we call widget.Enable(). This means
# that the RWI::Initialized ivar is not set, but in THIS SPECIFIC CASE,
# that doesn't matter.
widget.Enable(1)
widget.AddObserver("ExitEvent", lambda o,e,f=frame: f.Close())
ren = vtk.vtkRenderer()
widget.GetRenderWindow().AddRenderer(ren)
cone = vtk.vtkConeSource()
cone.SetResolution(8)
coneMapper = vtk.vtkPolyDataMapper()
coneMapper.SetInput(cone.GetOutput())
coneActor = vtk.vtkActor()
coneActor.SetMapper(coneMapper)
ren.AddActor(coneActor)
# show the window
frame.Show()
app.MainLoop()
(Note that this code is not altered and has some clear differences with what you are trying to do.)
Also, the reason that ctrl+C doesn't work is because the VTK event loop doesn't do anything with this event. Some GUIs do respect this event, including wxpython. But if you aren't using a GUI that respects this event (for example, Qt) you can manually tell the python interpreter to intercept this event and crash instead of forwarding the event to the GUI event loop:
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
For anyone who happens to stumble along this with the same problem of being unable to manipulate the camera from the vtkInteractorStyle classes, check out the Dolly(), Pan(), Spin(), Rotate(), Zoom(), and UniformScale(). All of these should let you access the camera from your whichever child class of vtkInteractorStyle you're using. Good luck!
EDIT: Even better, just attach your camera to your class that inherits from vtkInteractorStyle as a property, e.g.:
style = MyInteractorStyleClass()
style.camera = myCam
This way you can access it from anywhere within your custom class! Pretty basic, but it flew right past me.

wxPython: Exit Fullscreen

To display a wxPython window in full screen mode you use:
ShowFullScreen(True)
How do you get out of full screen though? I've tried the obvious way:
ShowFullScreen(True)
sleep(5)
ShowFullScreen(False)
This doesn't work though. When I run the script, nothing appears. After 5 seconds a window roughly 200x250 appears in the top-left corner of the screen, without anything inside of it. It doesn't appear to have any borders either.
If I change this to
showFullScreen(True)
then I get stuck with a full screen window that I have to use Alt + F2 -> xkill to get out of.
It looks like you need to Show() the window first. (According to the documentation, you shouldn't have to. Maybe this is a bug.) I tested on Mac OS X and Windows - they both exhibit issues if you don't call Show() first.
Also note that you shouldn't sleep in the main GUI thread. You'll hang the UI. Using CallLater is one potential solution, as shown in my example.
Working example:
import wx
def main():
app = wx.PySimpleApp()
frame = wx.Frame(None, -1, 'Full Screen Test')
frame.Show()
frame.ShowFullScreen(True)
wx.CallLater(5000, frame.ShowFullScreen, False)
app.MainLoop()
if __name__ == '__main__':
main()
The documentation for ShowFullScreen reads:
ShowFullScreen(show, style=wx.FULLSCREEN_ALL)
Depending on the value of show parameter the window is either shown full screen or restored to its normal state.
Parameters:
show (bool)
style (long): is a bit list containing some or all of the following values, which indicate what elements of the window to hide in full-screen mode:
wx.FULLSCREEN_NOMENUBAR
wx.FULLSCREEN_NOTOOLBAR
wx.FULLSCREEN_NOSTATUSBAR
wx.FULLSCREEN_NOBORDER
wx.FULLSCREEN_NOCAPTION
wx.FULLSCREEN_ALL (all of the above)
So put your Full Screen toggle event/s in a Menu and start full screen mode with:
self.window.ShowFullScreen(True, style=(wx.FULLSCREEN_NOTOOLBAR | wx.FULLSCREEN_NOSTATUSBAR |wx.FULLSCREEN_NOBORDER |wx.FULLSCREEN_NOCAPTION))
Note that I omitted wx.FULLSCREEN_NOMENUBAR, in this way you will still be able to access the menu to turn full screen mode off again.

Categories

Resources