I recently asked this question in the pyglet-users group, but got response, so I'm trying here instead.
I would like to extend Pyglet to be able to use an infra red input device supported by lirc. I've used pyLirc before ( http://pylirc.mccabe.nu/ ) with PyGame and I want to rewrite my application to use Pyglet instead.
To see if a button was pressed you would typically poll pyLirc to see if there is any button presses in its queue.
My question is, what is the correct way in Pyglet to integrate pyLirc?
I would prefer if it works in the same was as the current window keyboard/mouse events, but I'm not sure where to start.
I know I can create a new EventDispatcher, in which I can register the
new types of events and dispatch them after polling, like so:
class pyLircDispatcher(pyglet.event.EventDispatcher):
def poll(self):
codes = pylirc.nextcode()
if codes is not None:
for code in codes:
self.dispatch_event('on_irbutton', code)
def on_irbutton(self, code):
pass
But how do I integrate that into the application's main loop to keep on calling poll() if I use pyglet.app.run() and how do I attach this eventdispatcher to my window so it works the same as the mouse and keyboard dispatchers?
I see that I can set up a scheduler to call poll() at regular intervals with pyglet.clock.schedule_interval, but is this the correct way to do it?
It's probably too late for the OP, but I'll reply anyway in case it's helpful to anyone else.
Creating the event dispatcher and using pyglet.clock.schedule_interval to call poll() at regular intervals is a good way to do it.
To attach the event dispatcher to your window, you need to create an instance of the dispatcher and then call its push_handlers method:
dispatcher.push_handlers(window)
Then you can treat the events just like any other events coming into the window.
The correct way is whatever works. You can always change it later if you find a better way.
Related
I'm new for python but willing to learn. I have a set of hardware to receive touch coordinators and draw line accordingly to coordinators.
My problem is that the wxpython won't draw line if coordinator changes.
Here is my code : https://github.com/eleghostliu/homework/blob/master/DrawXY_byWxPython/PythonApplication1/PythonApplication1.py
can someone give advise, thanks.
You registered for EVT_PAINT, yet you are not triggering the event as the data changes. The frame has no idea whether data changed or not, unless you specifically inform it.
You can trigger the event simply by calling
frame.Refresh()
You can hook it in several ways. For instance, you could pass frame.Refresh bound method as a parameter to MainProcess so that it can make the function call to refresh the frame. Something like the following:
WARNING: Erroneous code piece
# Start a socket server
def MainProcess(refresh_callback):
while True:
refresh_callback()
***********************************************
frame = DrawPanel()
frame.Show()
start_new_thread(MainProcess, (frame.Refresh,))
Edit:
The above code piece calling UI methods directly is wrong!
Worker thread should not directly manipulate GUI, instead it should inform the GUI thread about a change, and the GUI thread which is the main thread will handle it in its context. There are again several approaches here, the quickest to implement is through wx.CallAfter.
Which you can incorporate as below, instead of directly calling the function:
wx.CallAfter(refresh_callback)
Another way to handle the communication between worker thread and GUI thread is via wx.PostEvent.
class DrawPanel(wx.Frame):
"""Draw a line to a panel."""
def notify(self):
wx.PostEvent(self, wx.PaintEvent())
Then from the secondary thread, you can safely call frame.notify() whenever new data arrives.
For a more elegant solution involving wx.CallAfter, refer to https://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/ where pubsub is used.
I am having trouble understanding kivy's custom events. I am making a simple music player using kivy and I want some basic functionality: when a song ends, the next one plays. Kivy's sound class provides 2 events, on_press and on_stop.
Obviously, the correct choice here would be on_stop. However, this event is triggered both when the song ends naturally and when the user stops it manually. Thing is, I don't want the latter to happen, so I decided I should create a custom event. The resources available online to help me understand custom events are very few and confusing. Here's the example in the kivy docs:
class MyWidget(Widget):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.register_event_type('on_swipe')
def on_swipe(self):
pass
def on_swipe_callback(*largs):
print('my swipe is called', largs)
w = MyWidget()
w.dispatch('on_swipe')
So, we have the event (on_swipe) and an event handler (on_swipe_callback). When is this event supposed to be triggered? How do I define that? Let's say I want my event to be called on_finish, and I want it to be triggered when a song reaches the end of its duration. How and where do I write the details about that? I'd appreciate it if someone can help me understand how these events work.
The event is triggered exactly when dispatch is called. What is missing in the bit of code in your question is the binding. You'd have something like
w = MyWidget()
w.bind(on_swipe=on_swipe_callback)
This tells w to call on_swipe_callback when the event on_swipe occurs.
Regarding your original question, this may be more difficult than it looks, because at the time of an on_stop the current position within a sound is not known.
A rather hackish solution might be to use Clock to schedule a function that checks whether the sound is still playing just before the end of the sound. Of course, this would be off e.g. if the user seeks. In the end, you might have to directly change the Sound class in kivy.audio to generate new events.
Another option would be to check in your on_stop callback if the Stop button was pressed in your UI. If it wasn't, then the song ended “naturally”. You'd set a flag in your on_press handler for the button and clear it in your on_stop handler.
I recently switched from wxPython to PyQT and can't find an equivalent of CallAfter. I need to use pubsub due to some imports and with wx I just sent messages with CallAfter -- is there a way to do something similar in PyQT? Basically, I want to inject something into the mainloop with pyQT.
EDIT FOR MORE INFO:
In my old GUI, using wxPython, I was using python-openzwave which uses an old dispatcher module. I would capture the old dispatcher signals and convert them to pubsub messages (for ease of use) and send the new messages with a CallAfter like this:
wx.CallAfter(pub.sendMessage, messagePack.signal, message = messagePack.message)
And then I was able to update the GUI by capturing the message and working directly on the gui elements because it essentially injected something into the mainloop.
Now, using pyqt, there is no callafter so, I have the same system setup without the callafter but the actions that have to occur after the message is received can't happen because it is in the middle of the mainloop.
The closest thing I can think of is using QTimer.singleShot with a short timeout, which will force it into the next event loop.
def other_function(self):
print 'other'
def my_function(self):
print 'one'
QTimer.singleShot(1, self.other_function)
print 'two'
Qt has the idea of an event loop, where it will check if there are events that need processing, like a button click, or part of a widget needs to be redrawn, etc. Typically, a function gets called as the result of an event. The QTimer.singleShot will stick your function call at the end of the list of things to be processed on the next cycle of the event loop.
But I agree with some of the comments that you probably could just use a separate QObject running in another thread to handle the openzwave events and re-dispatch the messages as Qt Signals, which the main thread can listen for and update the GUI.
I am having difficulty figuring out how to receive events using pywin32. I have created code to do some OPC processing. According to the generated binding in the gen_py folder I should be able to register event handlers and it gives me the prototypes I should use... for example:
# Event Handlers
# If you create handlers, they should have the following prototypes:
# def OnAsyncWriteComplete(.......)
So I have written code that implements the handlers that I am interested in but have not the slightest idea how to get them attached to my client and can not seem to find examples that are making sense to me. Below I create my client and then add an object that should have events associated with it...
self.server = win32com.client.gencache.EnsureDispatch(driver)
# I can now call the methods on the server object like so....
new_group = self.server.OPCGroups.Add(group)
I want to attach my handler to the new_group object (perhaps to the self.server?) but I can not seem to understand how I should be doing that.
So my questions are:
How can I attach my handler code for the events? Any examples around I should look at?
Will the handler code have access to attributes stored on the client "self" in this case?
Any help would be greatly appreciated.
After quite a bit, I was able to find a way to do this. What I did was find that I could attach my Event handler class to the group.
self.server = win32com.client.gencache.EnsureDispatch(driver)
# I can now call the methods on the server object like so....
new_group = self.server.OPCGroups.Add(group)
self._events[group] = win32com.client.WithEvents(
new_group, GroupEvent)
Once I had that going it seems to trigger the events, but the events would not run until the end of the script. In order to get it to process the events that were queued up, I call this which seems to trigger the callbacks to execute.
pythoncom.PumpWaitingMessages()
Don't know if it will help anyone else but it seems to work for what I am doing.
Thank's for this, it was very helpful. To extend the question, I found I could simply register the driver:
import win32com
class MyEvents(object): pass
server=win32com.client.gencache.EnsureDispatch(driver)
win32com.client.WithDispatch(server, MyEvents)
I discovered this by performing help(win32com.client.WithEvents)
How can I get global scroll events with PyObjC? Can it be done with an NSEvent call?
Sample code would be great...
You were on the right track with NSEvent! As long as you don't want to modify the event, but just observe it, it's as easy as calling addGlobalMonitorForEventsMatchingMask:handler:. Your app will get notified via a callback whenever an event of the type you specify with the mask is posted to another app.* The handler argument is a block, but need cause you no worry, because block arguments are almost easier to deal with in PyObjC than in straight Obj-C: you can pass any callable object (function, method, class, etc.) and the bridge will handle the rest. This is all you need to do:
def callback(event):
NSLog(u"%s" % event)
NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(NSScrollWheelMask, callback)
In the callback, you get a copy of the actual event, that you can query for things like its deltaX or whatever you like.
Note, that, like so many other interesting methods in AppKit, this one is new in 10.6, and isn't in the metadata file for older (read: Apple-supplied) versions of PyObjC. This means that if you try to build an app using the default install of the bridge, it will fail. You'll have to use a newer version.
*If you want to get events posted to your app, you must use addLocalMonitorForEventsMatchingMask:handler:. Unfortunately, one event monitor cannot get both events for your app and for others.