PyQt5 run Multithreaded processes consicutively on single thread - python-3.x

I am working on an application in PyQT5 which has two docks on either side and an OCC 3d viewer and a TextEdit in the middle. The .ExportToImage() method of the OCC viewer allows taking a screenshot of the viewer. But since the Application has a responsive design, the Viewer is resized to be thin(on certain display resolutions) and thus the screenshot also comes out to be thin.
I've tried to resize the window to a particular size and then hide everything except the 3D viewer. This enlarges the viewer thus saving from a cropped screenshot. But when I hide and resize and then take the screenshot, the screenshot still comes out to be thin. Here's the code:
def take_screenshot(self):
Ww=self.frameGeometry().width()
Wh=self.frameGeometry().height()
self.resize(700,500)
self.outputDock.hide() #Dock on the right
self.inputDock.hide() #Dock on the left
self.textEdit.hide() #TextEdit on the Bottom-Middle
self.display.ExportToImage(fName) #Display is the 3d Viewer's display on the Top-Middle
self.resize(Ww,Wh)
self.outputDock.show()
self.inputDock.show()
self.textEdit.show()
I guess this happens because the above .show(), .hide(), .resize() methods of PyQt5 are multithreaded and as soon as I run them they dont run consicutively but parallely. Thus the screenshot is taken before the other processes complete.
Is there a way to resolve this? Or is there a better way?

no multiple threads. events are processed in loops, so called eventloop.
try this:
def take_screenshot(self):
Ww=self.frameGeometry().width()
Wh=self.frameGeometry().height()
self.resize(700,500)
self.outputDock.hide() #Dock on the right
self.inputDock.hide() #Dock on the left
self.textEdit.hide() #TextEdit on the Bottom-Middle
QTimer.singleShot(0, self._capture)
def _capture(self):
# app.processEvents() # open this line if it still doesn't work, I don't know why.
self.display.ExportToImage(fName) #Display is the 3d Viewer's display on the Top-Middle
self.resize(Ww,Wh)
self.outputDock.show()
self.inputDock.show()
self.textEdit.show()

Related

python3 tkinter entry widget flash green for ~1 sec without blocking ongoing animation?

everything is working for now but I need to signify good input by flashing the entry box green for ~0.5 sec without blocking the ongoing animation.
I create an entry box using the following code
```python
p = tkinter.Entry(master=root, placeholder_text=P_TGT, width=100)
p.bind("<Return>", update_p)
p.bind("<FocusOut>", update_p)
```
where update_p() executes the following -
```python
p.configure({"foreground": "green"})) #signifies good input
time.sleep(5)
p.configure({"foreground": "white"}) #reset color
```
but this code blocks the execution of the whole app, how do i execute such a startegy without blocking the ongoing animation in tkinter?
Thanks
Tkinter widgets have a method named after which can be used to schedule code to run in the future. after takes as arguments a timeout in milliseconds, a callable, and zero or more positional arguments. The callable will be called with the given arguments as soon as possible after the timeout has been reached.
In your case you can use it to change the color back to white after one second like in the following example.
def update_p():
p.configure({"foreground": "green"}) #signifies good input
p.after(1000, p.configure, {"foreground": "white"})

Python - Mouse clicks/Keyboard Clicks on window in background

I wanted to create a metin2 bot, nothing complicated. I am new into that so i just started to look for tutorials on youtube to find something that would lead me to world of coding, how does it work, what language is used to write it ect. So I found a tutorial on youtube about open CV Learn Code By Gaming - OpenCV and thought that's awesome thing to start with.
I got to the point where I can easily detect the names of monsters/metins and wont have any problems in finding the right points to click. After detection started working I thought about automating clicking on the window to farm some items etc. Simple things like clicking e.g 50 pixels below the name to auto attack worked just fine but the problem was that I wanted to make more bots to maximalize farming. So when i started to add more clients I got a problem where e.g you have to hold down SPACE in one window to attack from horse and click on another window which was stopping attacking from horse. So I thought about finding some code that can basically send message directly to window without controlling a mouse or keyboard so you can run multiple bots in one time and each will do perfect meanwhile you can do anything else on pc because your mouse and keyboard aren't used.
Let's start from code I found and none worked for windows in background (even with administrator privileges). Pyautogui doesn't work in background (window has to be in foreground to be clicked on and it controls mouse so there is no point in using that.
From that code I learned that I need to find "window ID" to connect to it and send messages. When i print hWnd it shows the numbers in Terminal and code passes without any fails but does nothing except printing the window ID ( Parent Handle ). Ofc I pip installed pywin32
import win32gui
import win32api
import win32con
def click(x, y):
hWnd = win32gui.FindWindow(None, "Client")
print(hWnd)
lParam = win32api.MAKELONG(x, y)
win32api.SendMessage(hWnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lParam)
win32api.SendMessage(hWnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, lParam)
click(100, 100)
Than i found another code that looked similar but used different function so first i used
wdname = 'Client'
hwnd = win32gui.FindWindow(None, wdname) # parent handle
print(hwnd)
Which printed me a window ID that i used in parameters in function
def control_click(x, y, handle, button):
l_param = win32api.MAKELONG(x, y)
if button == 'left':
win32gui.PostMessage(handle, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, l_param)
win32gui.PostMessage(handle, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, l_param)
elif button == 'right':
win32gui.PostMessage(handle, win32con.WM_RBUTTONDOWN, 0, l_param)
win32gui.PostMessage(handle, win32con.WM_RBUTTONUP, 0, l_param)
control_click(200, 200, 329570, button ='left')
But it still did nothing but code passed clear
Anyone have any ideas about how to make clicks/keyboard clicks in python on window in background without taking controll of mouse/keyboard? If you have any experience in automating and know better ways to create automation for clicking ect. and want to share it please do. If that can be done in another programming language also share your toughts about that of course if you want to.
Windows 10 x64
Python used: 3.9.2
All codes i found in topics were out of date that s why i am asking for help there. Thanks in advance :)

Tkinter and some kind of issue when trying to display images

First off, I'm very new to Python and coding in general. I'm using Python, Tkinter, and Idle version 3.7.3. I'm using an HP Chromebook, with Chrome OS Version 81.0.4044.141.
from tkinter import *
window = Tk()
window.title('Image Example')
img = PhotoImage(file = 'python.gif')
label = Label(window, image = img)
label.pack()
window.mainloop()
As you can see above, this is the small snippet of code that I'm having issues with. As far as I understand, everything is written correctly and the file "python.gif" is in the correct directory. For reference this is what the image should look like:
python.gif (normal)
But when I run the program, this is what I get:
python.gif (screenshot of running program)
That's the result 99% of the time, but I should mention that there have been a RARE number of occasions where the image displayed correctly upon program execution. However, I do not know how to replicate that. Also for more context, I've tried other images to see what happened. I found a free .pgm image to try as an example, and upon execution either I got the same result, or half of the image would appear correctly while the bottom half (also sometimes this would be reversed and the top half would be affected) would be "blacked out".
In conclusion, I wanted to ask if anybody has an idea of what's going on. I'm not sure if this is a hardware issue (because I can view all mentioned images in a normal image viewing app with no problems), or if this has something to do with Python/Tkinter.
Any assistance is very appreciated! Please and Thank You!

Maximize figures before saving

The question about how to do maximize a window before saving has been asked several times and has several questions (still no one is portable, though), How to maximize a plt.show() window using Python
and How do you change the size of figures drawn with matplotlib?
I created a small function to maximize a figure window before saving the plots. It works with QT5Agg backend.
import matplotlib.pyplot as plt
def maximize_figure_window(figures=None, tight=True):
"""
Maximize all the open figure windows or the ones indicated in figures
Parameters
----------
figures: (list) figure numbers
tight : (bool) if True, applies the tight_layout option
:return:
"""
if figures is None:
figures = plt.get_fignums()
for fig in figures:
plt.figure(fig)
manager = plt.get_current_fig_manager()
manager.window.showMaximized()
if tight is True:
plt.tight_layout()
Problems:
I have to wait for the windows to be actually maximized before using the plt.savefig() command, otherwise it is saved with as not maximized. This is a problem if I simply want to use the above function in a script
(minor problems:)
2. I have to use the above function twice in order to get the tight_layout option working, i.e. the first time tight=True has no effect.
The solution is not portable. Of course I can add all the possible backend I might use, but that's kind of ugly.
Questions:
how to make the script wait for the windows to be maximized? I don't want to use time.sleep(tot_seconds) because tot_seconds would be kind of arbitrary and makes the function even less portable
how to solve problem 2 ? I guess it is related to problem 1.
is there a portable solution to "maximize all the open windows" problem?
-- Edit --
For problem 3. #DavidG suggestion sounds good. I use tkinter to automatically get width and height, convert them to inches, and use them in fig.set_size_inches or directly during the figure creation via fig = plt.figure(figsize=(width, height)).
So a more portable solution is, for example.
import tkinter as tk
import matplotlib.pyplot as plt
def maximize_figure(figure=None):
root = tk.Tk()
width = root.winfo_screenmmwidth() / 25.4
height = root.winfo_screenmmheight() / 25.4
if figure is not None:
plt.figure(figure).set_size_inches(width, height)
return width, height
where I allow the figure to be None so that I can use the function to just retrieve width and height and use them later.
Problem 1 is still there, though.
I use maximize_figure() in a plot function that I created (let's say my_plot_func()) but still the saved figure doesn't have the right dimensions when saved on file.
I also tried with time.sleep(5) in my_plot_func() right after the figure creation. Not working.
It works only if a manually run in the console maximize_figure() and then run my_plot_func(figure=maximized_figure) with the figure already maximized. Which means that dimension calculation and saving parameters are correct.
It does not work if I run in the console maximize_figure() and my_plot_func(figure=maximized_figure) altogether, i.e. with one call the the console! I really don't get why.
I also tried with a non-interactive backend like 'Agg', so that the figure doesn't get actually created on screen. Not working (wrong dimensions) no matter if I call the functions altogether or one after the other.
To summarize and clarify (problem 1):
by running these two pieces of code in console, figure gets saved correctly.
plt.close('all')
plt.switch_backend('Qt5Agg')
fig = plt.figure()
w, h = maximize_figure(fig.number)
followed by:
my_plot_func(out_file='filename.eps', figure=fig.number)
by running them together (like it would be in a script) figure is not saved correctly.
plt.close('all')
plt.switch_backend('Qt5Agg')
fig = plt.figure()
w, h = maximize_figure(fig.number)
my_plot_func(out_file='filename.eps', figure=fig.number)
Using
plt.switch_backend('Agg')
instead of
plt.switch_backend('Qt5Agg')
it does not work in both cases.

How to implement picking point by mouse under paraview web

Our company want our paraview web application to show relevant data when users click left mouse button.
The other part like how to extract data from a selected point was straightforward. But I cannot solve the problem: how to pick a point in the coordinates while mouse click?
I added a callback function to handle left button press in the python script. And then I tried to use an vtkPointPicker to cast a ray and pick the point in the coordinates. But the pvpython program which run the python script as the server will crash when click mouse (to be exact when the picker run pick()). Under debugging I found that the VTK code dereferencing null pointer was why the program crash. But the VTK source code is just too complicated to delve into further.
Here is my callback function:
def CustomLeftButtonPress(obj, event):
x = obj.GetEventPosition()[0]
y = obj.GetEventPosition()[1]
picker = vtkPointPicker()
obj.SetPicker(picker)
currentRenderer = obj.FindPokedRenderer(x,y)
picker.Pick(x, y, 0.0, currentRenderer)
...
picker.Pick(x, y, 0.0, currentRenderer) causes the crash.
I should use vtkWorldPointPicker instead of vtkPointPicker.

Resources