I have a custom Event Loop for Pyglet that can be simplified to this simple piece of code
while True:
pyglet.clock.tick()
for window in pyglet.app.windows:
window.switch_to()
# rendering code
window.flip()
window.dispatch_events() # <--- program hangs
When there is only one window, the event loop works fine but when I attempt to have two windows, the loop hangs at window.dispatch_events(). Calling the functions in different order yield the same problem. I also tried debugging:
pyglet.options['debug_win32'] = True
Error Message:
...
_user32.UnregisterHotKey(self._hwnd, 0)
b'Hot key is not registered.\r\n'
After some more tinkering I managed to get the windows responsive with a dirty hack, but still there are quite a few issues that arise with event processing. Fix was calling an update immediately after setting window visible.
Sample Initialisation code:
if __name__=="__main__":
engine = Engine()
engine.run()
class Engine: # test program
def __init__(self):
self.application = customLib.Application()
# define windows, by default they are set as not visible
self.window1 = self.application.guiManager.createWindow("Window 1",
(1000, 700),
cursorPath="../data/cursor.png")
self.window2 = self.application.guiManager.createWindow("Window 2",
(1000, 700),
cursorPath="../data/cursor.png")
# load up code such as loading textures and .obj files
self.window1.pygletWindow.set_visible()
self.window1.update() # calling this after setting self.window2 visible will cause original error
self.window2.pygletWindow.set_visible()
self.window2.update()
def run(self):
self.application.run()
### CUSTOMLIB ###
class Application:
...
def run(self):
while True:
pyglet.clock.tick()
self.guiManager.update()
class GUIManager: # manages windows
...
def update(self):
for window in self.windows:
window.update()
class Window: # pyglet window higher level wrapper
pygletWindow = None
...
def update(self):
e = self.pygletWindow.dispatch_events()
# all events are intercepted by a custom class, it transforms all unhandled events (keyboard, mouse, and close button for now) into a list of events that can then be looped through rather than requiring twenty different functions.
# the event list is acquired, looped through and then cleared
for event in self.eventManager.get():
if event.type == customLib.CLOSE:
# close application
elif event.type == customLib.K_W:
# move player forward
# rendering
self.pygletWindow.switch_to()
# window is cleared
# window content rendered
self.pygletWindow.flip()
The way around the previous problem seems a little foolish and not robust. With the current event loop I have, window feedback sometimes does not work. For example, to close the window I must sometimes click it several times or, when moving the window around it won't always grab the window. I dispatch_events() at every loop and my custom event handler does not handle window motion events. The glitching of event handling only occurs with multiple windows. With a single window the event loop is flawless. This "bug" can be fixed by using varients of pyglet EventLoop.
I have concluded that a custom Event Loops' cons outweigh its pros. It is much easier to subclass EventLoop (since it's optimised for the OS) and override any pesky routines. Even better is to make a whole new event loop copying initialisation code of EventLoop and any backend routines (utilising PlatformEventLoop), which then allows you to very easily implement custom routines. I will be leaving this question up because there is very little documentation about this matter.
Related
I'm currently trying to create a program where it detects the mouse movement on my computer and pops up a window if any movement is detected. However, I've run into some issues with Tkinter threading. I have read multiple other posts on the same issue and understand that Tkinter isn't thread-safe and that it can only be run in the main thread. However, I'm still unsure about the fix for my program. I'd appreciate it if someone could help me take a look.
Essentially, my (simplified) program has two scripts, access_detection.py and password_prompt.py. The first file is responsible for using pynput.mouse to track mouse movement, and the second file is responsible for popping up a Tkinter window and prompting the user for a password whenever mouse movement from the first script is detected.
> password_prompt.py (pops up a window for the user to enter their passcode)
def confirm_passcode(self):
self.access_granted = True
def prompt_password(self) -> bool:
# initialize window
entry = Entry(self.win, width=25, textvariable=self.password, show="*")
entry.pack(pady=((int(self.win.winfo_screenheight()/2) - 100), 0))
button = ttk.Button(self.win, text="Confirm Password", command=self.confirm_passcode)
button.pack(pady=10)
while True:
self.win.attributes('-fullscreen', True)
self.win.attributes('-topmost', True)
if self.access_granted == True:
return True
elif self.access_granted == False and self.access_attempts >= 3:
return False
self.win.update()
> access_detection.py (tracks mouse movement)
def on_move_mouse(self, x, y):
self.security_passed = popup_obj.prompt_password()
if self.security_passed == False:
# do something here
else:
# stops this program
def detection_start(self):
with mouse_listener(on_move=self.on_move_mouse) as mouse_monitor:
mouse_monitor.join()
# driver
if __name__ == "__main__":
monitor = # a class with detection_start()
monitor.detection_start()
The issue raised: RuntimeError: main thread is not in main loop
Currently, the main method is in access_detection.py and I believe the pynput.mouse threading is using the main thread, causing the error since Tkinter isn't thread-safe. Is there a way that I could have these two threads running simultaneously and still accomplish the goal of tracking mouse movement and popping up a window when movement is detected? (with Tkinter in the main thread if it has to be necessary).
The original code is a bit lengthy. It could be found here if you need more info. (ignore main.py)
I am trying to write a function to dynamically resize an image displayed in a tkinter window.
Therefore I bound this function to the Configure event:
connroot.bind( "<Configure>", connresiz)
My problems are:
That the connresiz() function gets called 3 times (why 3?) at program start, and
More troublesome, that dynamically resizing the window calls the function continuously as I drag the mouse! How can avoid this?
I thought about checking at the simultaneous presence of a <Configure> and <ButtonRelease-1> events, but I don't know how to code it.
1) We don't know that, since we can't see your code...
2) Short answer is: you can't, because that's exactly what <Configure> event does! Long answer, you can, with a little trick/hack. Since anytime the window is changing, it will call all the binded functions to <Configure>, and the same happens anytime as the mouse button released (right after the last <Configure> call) we can create a flag/switch which will tell us, if the window was "configured" then we can check that switch anytime the mouse button is released, and switch it back to the default value after we ran some actions.
So if you want the image to resized only, when the mouse was released and the window was changed this is the code you need:
from tkinter import *
class Run:
def __init__(self):
self.root = Tk()
self.clicked = False
self.root.bind('<ButtonRelease-1>', self.image_resize)
self.root.bind('<Configure>', lambda e: self.click(True))
def image_resize(self, event):
if self.clicked:
print("I'm printed after <Configure>.") # the action goes here!
self.click(False)
def click(self, value):
self.clicked = value
app = Run()
app.root.mainloop()
According to the official tk documentation, <Configure> events fire "whenever its size, position, or border width changes, and sometimes when it has changed position in the stacking order." This can happen several times during startup.
It is called continuously while you resize the window because the size of the widget is changing. That's what it's defined to do. You can't prevent it from being called, though you can certainly modify what you do in the callback. For example, you could delay resizing the image until you've not received another <Configure> event for a second or two -- which likely means the user has stopped interactive resizing.
I have decided to finally work on a project, as I've tried to code in python before, with at least some success. In my project, I am trying to build a menu that lets me "Auto-farm" in a game. It uses 3 modules, namely pynput, pause, and PySimpleGUI.
Whenever I run the code, it runs fine, until I click the button that starts the automation part. It runs completely fine, but I have to force close the GUI prompt that shows up as it just completely stops responding to input until you close it.
How I can make a stop button, and stop my program from freezing up?
I am using 2 files to keep this project slightly more organized, although I don't know if this is the best way to go around doing this. These 2 files are main.py and chand.py.
main.py
import PySimpleGUI as sg
import chand
loop = 0
layout = [[sg.Text("Welcome to RedGrowie's autofarm menu!")], [sg.Button("Chandeliers")]]
window = sg.Window("Autofarming Menu", layout)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
if event == "Chandeliers":
loop = 1
if loop == 1:
chand.Chandeliers.start(self=chand.Chandeliers)
window.close
chand.py
from pynput.keyboard import Key, Controller
import pause
keyboard = Controller()
start = "0"
class Chandeliers:
def d_press(self):
keyboard.press("d")
pause.milliseconds(70)
keyboard.release("d")
pause.milliseconds(300)
keyboard.release(Key.space)
def space_press(self):
keyboard.press(Key.space)
pause.milliseconds(1)
#keyboard.release(Key.space)
def start(self):
start = "1"
while start == "1":
self.d_press(self)
self.space_press(self)
Your Chandeliers.start function loops indefinitely, so the call from the main loop in main.py never gets returned to. If you want both loops to be running at the same time, you probably need to use threading or some other means of concurrency. Or you might be able to interleave the two loops somehow, depending on the timing requirements for each one.
As a side note, you are using your Chandeliers class in a very odd way. You're never creating an instance of the class, but rather calling the methods it defines as if they were class methods (but with manual passing of the class, in the misleadingly named self argument.
You should probably not do that. Either treat the class as a normal one, and create an instance:
cha = chand.Chandeliers()
chat.start() # and change start to not manually pass self any more
Or you should do away with the unneeded class all together and just make the methods into top-level functions.
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.
My problem is I want to keep rotating the scene if the checkbox is checked, and stop this rotation immediately once it is unchecked. However, "keep rotating" means an infinite loop...
So after entering the loop, the program gets kind of freezed and no longer react to my "uncheck" signal. Is there a way to interrupt this loop? The following is skeleton of related code.
Thanks!
class Draw(QGLWidget):
def __init__(...):
...
self.rotate=0
self.auto=False
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glRotatef(self.rotate,0.0,0.0,1.0)
draw stuff...
glFlush()
def autoRotate(self,auto): # auto is an integer and used here as true/false
self.auto=auto
while self.auto:
self.rotate+=0.5
if self.rotate>360:
self.rotate-=360
self.updateGL()
if auto==False:
break
class SpiralWidgetDemo(QtGui.QMainWindow):
def __init__(self):
...
auto=QtGui.QCheckBox("Auto")
self.connect(auto,QtCore.SIGNAL("stateChanged(int)"),widget.autoRotate)
You must not implement this as a loop. This is defined to break the interaction of the program, as it prevents the "main loop" of the Qt application from running.
Put your drawing code into an event handler (like redraw event), and use a timer to generate events at regular intervals (e.g. 10/s).