How to get ownership of QClipboard - python

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_())

Related

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

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).

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.

How does Clipboard work in wx?

I have following function in python -
def GetClipboardText():
text_obj = wx.TextDataObject()
rtext = ""
if wx.TheClipboard.IsOpened() or wx.TheClipboard.Open():
if wx.TheClipboard.GetData(text_obj):
rtext = text_obj.GetText()
wx.TheClipboard.Close()
return rtext
It works well when I invoke this function from a UI callback handler such as button click (The UI is in wxPython). But if I invoke function directly in a script, the wx.TextDataObject() returns None and the function fails.
Questions -
What particular UI class is the dependency for the clipboard to work? Do I need to show a frame on screen? Is there a work around like creating an invisible frame? Is frame what the clipboard depends on or is it something else?
Is it possible to use clipboard in a command line app without GUI?
Try initializing wx.App in your script. Many wx classes require it.

Gtk.ProgressBar not working in Python

I'm trying to use a progress bar in Python and Gtk3, but it doesn't get updated. I have read this documentation and some questions in this forum (mostly for pygtk) and I really don't get it!
I made a code for testing purposes only. When you click a button, it reads the contents of a directory recursively. My intention is to use a progress bar while reading all these files.
This is the whole code:
import os
from gi.repository import Gtk
class MyWindow(Gtk.Window):
"""Progress Bar"""
def __init__(self):
Gtk.Window.__init__(self, title='Progress Bar')
self.set_default_size(300, 75)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_border_width(10)
# read dir contents
mydir = 'Documents'
home_path = os.environ.get('HOME')
dir_path = os.path.join(home_path, mydir)
self.dir_files_list(dir_path)
# create a grid
self.grid = Gtk.Grid(column_homogeneous=True, row_homogeneous=True,
column_spacing=10, row_spacing=10)
self.add(self.grid)
# create a progress bar
self.progressbar = Gtk.ProgressBar()
self.grid.add(self.progressbar)
# create a button
self.button = Gtk.Button(stock=Gtk.STOCK_APPLY)
self.button.connect('clicked', self.on_button_pressed)
self.grid.attach(self.button, 0, 1, 1, 1)
# function to read the dir contents
def dir_files_list(self, dir_path):
self.dir_list = []
for root, dirs, files in os.walk(dir_path):
for fn in files:
f = os.path.join(root, fn)
self.dir_list.append(f)
# function to update the progressbar
def on_button_pressed(self, widget):
self.progressbar.set_fraction(0.0)
frac = 1.0 / len(self.dir_list)
for f in self.dir_list:
new_val = self.progressbar.get_fraction() + frac
print new_val, f
self.progressbar.set_fraction(new_val)
return True
def main():
"""Show the window"""
win = MyWindow()
win.connect('delete-event', Gtk.main_quit)
win.show_all()
Gtk.main()
return 0
if __name__ == '__main__':
main()
Any help from a more experienced programmer is appreciated!
The problem is you are processing the whole directory when you are creating the window. That is even before you show it (in the line with self.dir_files_list(dir_path)). It seems to me you want to call it after pressing the button apply.
There are at least 2 possible solutions: use threads or use iterators. For your specific use case, I think iterator would be enough, besides than simpler and more pythonic. I would recommend threads only when you really need them.
Instead of walking through the whole directory beforehand and the process them later, you can process each file in each directory at a time.
In your example, I would change the method dir_files_list (renamed to walk) and on_button_pressed as:
from gc import collect
from gi.repository import Gtk, GObject
We now need to import also GObject to use idle_add which calls a callback whenever there are no higher priority events pending. Also, once the task is finished, we need to remove the callback to not call it anymore (we need source_remove for that).
I renamed your method dir_files_list as walk because it seems semantically closer to an iterator. When we walk through each directory and file, we will temporarily "return" (using yield). Remember, yield True means that there are pending items to process. yield False means we stop the iteration.
So, the method would be:
def walk(self, dir_path):
self.dir_list = []
for root, dirs, files in os.walk(dir_path):
i = 0.0
self.set_title(os.path.dirname(root))
for fn in files:
i = i + 1.0
f = os.path.join(root, fn)
self.dir_list.append(f)
self.progressbar.set_fraction(i / len(files))
collect()
yield True
yield True
GObject.source_remove(self.id)
yield False
Now, we update the progressbar here. In this particular part, I am updating the progress bar inside each directory. That is, it will be restarted in every sub-directory and the progress bar will restart in each of them. To have an idea of which directory is visiting, set the window title with the current directory. The important part to understand here is yield. You can adapt this to whatever you want to do.
Once we have walked the whole directory, we have to return yield False, but before that we remove the callback (GObject.source_remove(self.id)).
I consider here that self.dir_list is not useful here anymore, but you might have something different in mind.
You might wonder When and how walk is called? That can be set when the button is pressed:
def on_button_pressed(self, button, *args):
homedir = os.path.expanduser('~')
rootdir = os.path.join(homedir, 'Documents')
self.task = self.walk(rootdir)
self.id = GObject.idle_add(self.task.next)
self.task is an iterator, which has the method next() that retrieves the next item from self.task, which is going to be called whenever there are no pending events (with idle_add). We get the id in order to remove the callback once we are done.
You have to remove the line self.dir_files_list(dir_path) from the __init__ method.
One more thing: I called the garbage collector manually (gc.collect()) because it could be useful when processing a large directory (depending on what you are doing with them).
First, the problem is that the above code runs too fast, so you see the progress bar update so quick that it seems its not getting updated. It does that because you're not searching the directory and displaying the result at the same time. When the class __init__ you do the search, and just then when you click the button the list is read and displayed in the progress bar at full speed. I'm pretty sure that if the directory is huge, when you start the script it will take a few seconds for the window to display, and the progress bar will eventually progress in a matter of milliseconds anyway.
Basically the problem you're having is that you're doing everything in the same thread. Gtk is in it's own thread, listening to events from the GUI and updating the GUI. Most of the time, you will be using Gtk.ProgressBar for something that will require some time to complete, and you need to execute both, the GUI thread and the working thread (the one that is doing something), and send updates from the working thread to the GUI thread so it updates the progress bar. If, like the code above, you run everything in the same thread you will end up having this kind of problems, for example when the GUI freezes, that is, it become unresponsive because suddenly the GUI thread is doing some work not GUI-related.
In PyGTK, you had the method gobject.idle_add(function, parameters) so you can communicate from the working thread to the GUI thread, so when the GUI thread is idle it will execute that function with those parameters, for example, to update the Gtk.ProgressBar.
My approach to that problem is implemented here, please note is for PyGTK: https://github.com/carlos-jenkins/nested/blob/master/src/lib/nested/core/gallery/loading.py
Basically, it is a "LoadingWindow" that the whole application share. When you want to start loading something or perform some heavy work you had to subclass the WorkingThread class (example). Then, you just had to call the show() method with a WorkingThread subclass instance as parameter and done. In the WorkingThread subclass, you had to implement the payload() function, that is, the function that does the heavy work. You could directly call from the WorkingThread the pulse method in the LoadingWindow to update the ProgressBar and should not care about Thread communication because that logic is implemented there.
Hope the explanation helps.
EDIT:
I just ported the above to PyGObject, you can find the example here: https://gist.github.com/carlos-jenkins/5358445

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