Threading wxPython app with background process for multitouch detection - events

So basically I have some code for multitouch detection and I would like to run it as a separate thread which sends custom events to a wxPython GUI upon a multitouch event.
Now I could post the code for the multitouch tracking but to use it you would require a trackpad, the server app and the detection code, so I think it's better to consider a simple example for the sake of the question.
So for example lets assume an event simulator sending an event every 10 seconds:
import wx
import wx.lib.newevent
import time
MultiTouchEvent, EVT_MULTITOUCH = wx.lib.newevent.NewEvent()
class EventSimulator:
def __init__(self):
self.eventdata = 0
def Start(self):
self.RunTUIO()
def RunTUIO(self):
while True:
time.sleep(10)
wx.PostEvent(GUI, MultiTouchEvent(self.eventdata))
self.eventdata += 1
if __name__ == '__main__':
MultiTouchTracker = EventSimulator()
MultiTouchTracker.Start()
Now I would like to receive these events whenever they occur within a wxPython GUI (something like this):
import wx
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, size=(350,200))
self.Bind(EVT_MULTITOUCH, self.handler)
def handler(self, event):
data = event.data,
print data
if __name__ == '__main__':
app = wx.App(redirect=False)
window = Frame("My Basic GUI")
window.Show()
app.MainLoop()
The problem is I cannot workout how to combine these two pieces of code into a working example. As I understand it I need to run the event simulator as a separate thread from within the GUI code. Please can someone explain how to do this, thanks!

For details on multi-threading in Python see the docs for the threading module in the stock Python library. There is an example of using it in a GUI in the Threads module in the wxPython demo, although there are other viable approaches as well. (Deriving from the Thread class instead of just using one, etc.) That demo module also shows a convenient way to make a new event class and binder using the wx.lib.newevent module.

Related

Gtk+3 Grid Blocking Background Image In Overlay

I've been trying to get into GUI programming with Python on Xubuntu 14.04 by following a few guides like this one here. I wanted to have a background image in my GUI with whatever buttons on top of it. I searched on stackoverflow and came across a person having a similar issue. His solution worked, but the grid filled up the entire window and I can no longer see the background image. How do I fix this? Here is my code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-:
from gi.repository import Gtk #Here we are importing the GTK3 library to use in our program
class mywindow(Gtk.Window):
def __init__(self):
#In the class's constructor, we have to call the constructor of the super class. In addition, we tell it to set
#the value of the property title to Media Manager v1.0
Gtk.Window.__init__(self, title="Media Manager v1.0") #Creates an Empty Window
#use an overlay for the background image
self.overlay = Gtk.Overlay()
self.add(self.overlay)
self.background = Gtk.Image.new_from_file('Background.jpg')
self.overlay.add(self.background)
self.grid = Gtk.Grid()
self.button1 = Gtk.Button(label="Hello")
self.button1.connect("clicked", self.on_button1_clicked)
self.grid.add(self.button1)
self.overlay.add_overlay(self.grid)
#Gtk.Window.set_default_size(self, 400,325)
Gtk.Window.set_position(self, Gtk.WindowPosition.CENTER)
def on_button1_clicked(self, button):
print "Hello"
def on_button2_clicked(self, button):
print "Exiting..."
def main():
window = mywindow()
#connect to the window's delete event to ensure that the application is terminated if we click on the x to close the window
window.connect("delete-event", Gtk.main_quit)
#Display the window
window.show_all()
#Finally, start the GTK+ processing loop which we quit when the winndow is closed.
Gtk.main()
return 0
if __name__ == '__main__':
main()
Pass values for the valign and halign properties when creating the grid and/or the button. The default for these properties is Gtk.Align.FILL which causes them to cover all the space given to them. Instead, pick Gtk.Align.START, Gtk.Align.END, or Gtk.Align.CENTER.

How can I dynamically add actions to a QMenu while it is open on a Mac?

I have QSystemTrayIcon with a QMenu. In order to fill the menu, I need to fetch some things from the network, so I want to do that in the background.
So I have a QThread with a slot that is connected to the activated signal of the tray icon. Then the thread fetches the resources and updates the menu using another signal.
However, these updates do not show until I close and reopen the menu.
This seems to be a Mac specific problem. I ran my code on Windows, and there it updated more or less correctly. Is there any workaround?
Below is an extracted version of the problem. When the menu is opened, it will sleep 1 second in a thread and then change the menu. This change is not seen.
import sys
import time
from PySide import QtCore, QtGui
class PeerMenu(QtGui.QMenu):
def __init__(self):
QtGui.QMenu.__init__(self)
self.set_peers("prestine")
#QtCore.Slot(object)
def set_peers(self, label):
self.clear()
self.addAction(QtGui.QAction(label, self))
self.addSeparator()
self.addAction(QtGui.QAction("Hello", self))
class GUIListener(QtCore.QObject):
files = QtCore.Signal(object)
def __init__(self):
QtCore.QObject.__init__(self)
self.counter = 0
#QtCore.Slot()
def check(self):
time.sleep(1)
self.counter += 1
self.files.emit(str(self.counter))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
icon = QtGui.QSystemTrayIcon(QtGui.QIcon('images/glyphicons-206-electricity.png'), app)
listener = GUIListener()
t = QtCore.QThread()
t.start()
listener.moveToThread(t)
menu = PeerMenu()
icon.activated.connect(listener.check)
listener.files.connect(menu.set_peers)
icon.setContextMenu(menu)
icon.show()
app.exec_()
After a few hours of extensive googling I finally figured it out.
You can create a borderless window using QtGui.QMainWindow(parent=None, flags=QtCore.Qt.Popup) and then find the location of the icon with icon.geometry().center() and finally move the window there with window.move(icon_point).
There is some hackery involved in deciding how to place the window relative to the icon. Full code can be found at https://github.com/pepijndevos/gierzwaluw/blob/master/gui.py

wxPython Mouse Entering/Leaving event methods don't work on Linux

I am trying to provide a custom button class that needs a hover for it to work properly. I am therefore using StaticBitmap and extending it. But I am finding differences between Windows and Linux using the same exact code between both. The following small simple example doesn't work properly in Linux, but works fine in Windows:
import wx
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Test")
panel = wx.Panel(self, wx.ID_ANY, size=(200,100))
panel.SetBackgroundColour("Black")
# create a normal bitmap button
bitmap = wx.Bitmap("images/power.png", wx.BITMAP_TYPE_ANY)
self.image1 = wx.StaticBitmap(panel, bitmap=bitmap, size=bitmap.GetSize())
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.image1)
panel.SetSizer(sizer)
# This works on Windows, but not on Linux
#self.image1.Bind(wx.EVT_ENTER_WINDOW, self.OnHover)
#self.image1.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveHover)
# This (used by itself) gets events for both Linux/Win, but
# doesn't find e.Entering() or e.Leaving() on Linux!
self.image1.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvents)
def OnHover(self, e):
print "Hover"
def OnLeaveHover(self,e):
print "Leaving Hover"
def OnMouseEvents(self,e):
print "Mouse event"
#print dir(e)
if e.Entering():
print "Hover"
elif e.Leaving():
print "Leaving Hover"
else:
e.Skip()
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
Is this a known issue? Is there some other way to bind the event to get the mouseover/hover operation to work properly in Linux?
Also, no forms of self.Bind() work here on Linux or Windows. Why? i.e.:
self.Bind(wx.EVT_ENTER_WINDOW, self.OnHover, self.image1)
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveHover, self.image1)
Hmmm..., all I can tell you is what I experienced with your code when using it in Ubuntu 12.04
1) My picture never shows up (i.e. a grey window nothing more)
2) when binding the EVT_ENTER/LEAVE_WINDOW to self and not to image1 (see below), it works
# This now works on Linux
#self.Bind(wx.EVT_ENTER_WINDOW, self.OnHover)
#self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveHover)
I hope this helps you in some form. Also I would like to add that I have made many a bad experience with cross-platform compatibility of wxpython unfortunately. I like the library a lot, but once it delves into more obscure widgets, the behaviour may differ wildly between Windows and Linux.
I remember trying to use the MPlayerCtrl once and for the same method in Windows it would return 0 while in Linux it would return -1 (can't mind exactly which method it was, that project got dumped once I noticed it)
Maybe, this is another one of those strange behaviours...
Having thought about it some more.
instead of binding the event to the picture, you could bind it to the widget it is displayed in, such as your panel.
self.panel = wx.Panel(self, wx.ID_ANY, size=(200,100))
self.panel.SetBackgroundColour("Black")
...
#self.panel.Bind(wx.EVT_ENTER_WINDOW, self.OnHover)
#self.pane.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveHover)
you should bind the wx.EVT_ENTER_WINDOW/wx.EVT_LEAVE_WINDOW event to self.panel. that works.
wx.EVT_ENTER_WINDOW/wx.EVT_LEAVE_WINDOW is not the subclass of wx.CommandEvent class. It isn't thrown up to the parent window to process.

How to disable multiple auto-redrawing at resizing widgets in PyQt?

I have a PyQt4 program with widgets whose content redraws very slowly (it's ok, because of my tasks). And when I trying to resize those widgets, program is trying to redraw a lot of times while mouse is not released. That's a lot of freezes.
I want to disable that auto-redrawing and configure PyQt to redraw all widgets only when mouse is released (which means that redraw happens exactly one time per one resize).
How to do that?
Edit1. I'll see it quite simply, like this: you drag the line, and while you dragging, all widgets stand. When you release it, widgets redrawing. But I'm really not sure that it's possible in PyQt4.
First, I would recommend making sure that if you are using custom paint events with your widgets, that you are not doing too heavy of work in each event and simply looking for a band-aid solution. If this is the case, try and find a way to cache or reduce the work. Otherwise...
The decision to draw opaque or not is one made by the window manager of your platform. As far as I know, there is not a simple attribute to toggle this feature. Something similar to this exists on a QSplitter to only draw after the handle is released.
I can offer one workaround approach, which is to delay the update until after no resize has occurred for a period of time. This will give your application some breathing room to reduce the paint events.
from PyQt4 import QtCore, QtGui
import sys
class DelayedUpdater(QtGui.QWidget):
def __init__(self):
super(DelayedUpdater, self).__init__()
self.layout = QtGui.QVBoxLayout(self)
self.label = QtGui.QLabel("Some Text")
self.layout.addWidget(self.label, QtCore.Qt.AlignCenter)
self.delayEnabled = False
self.delayTimeout = 100
self._resizeTimer = QtCore.QTimer(self)
self._resizeTimer.timeout.connect(self._delayedUpdate)
def resizeEvent(self, event):
if self.delayEnabled:
self._resizeTimer.start(self.delayTimeout)
self.setUpdatesEnabled(False)
super(DelayedUpdater, self).resizeEvent(event)
def _delayedUpdate(self):
print "Performing actual update"
self._resizeTimer.stop()
self.setUpdatesEnabled(True)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
win = QtGui.QMainWindow()
view = DelayedUpdater()
win.setCentralWidget(view)
win.show()
view.delayEnabled = True
app.exec_()
You will notice that as you resize the main window quickly, no updates are occurring for the custom widget, because we have turned them off in the resize event. A QTimer is trying to fire every 100 ms to perform the update and stop itself. But each time another resize event occurs, it will restart that timer. The effect is that timer will continue to be reset. leaving updates disabled, until a delay occurs.
Try holding down the mouse, resizing a little, wait, and resize some more. The update should occur even while your mouse is down but you are not resizing. Adjust the delay to suit. And you have control over turning the feature on and off with the bool flag.
This example could also be re-worked to make DelayedUpdater just a QObject, which accepts some QWidget instance as an argument. It would then set itself to be the eventFilter for that object and monitor its resizeEvent. That way you don't have to subclass normal widgets just to add this. You would simply make an instance of DelayedUpdater and use it as a utility object to monitor the widget.
Here is an example of making it a helper object:
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.someWidget = QtGui.QWidget()
self.setCentralWidget(self.someWidget)
self.layout = QtGui.QVBoxLayout(self.someWidget)
self.label = QtGui.QLabel("Some Text")
self.layout.addWidget(self.label, QtCore.Qt.AlignCenter)
self.delayer = DelayedUpdater(self.someWidget)
class DelayedUpdater(QtCore.QObject):
def __init__(self, target, parent=None):
super(DelayedUpdater, self).__init__(parent)
self.target = target
target.installEventFilter(self)
self.delayEnabled = True
self.delayTimeout = 100
self._resizeTimer = QtCore.QTimer()
self._resizeTimer.timeout.connect(self._delayedUpdate)
def eventFilter(self, obj, event):
if self.delayEnabled and obj is self.target:
if event.type() == event.Resize:
self._resizeTimer.start(self.delayTimeout)
self.target.setUpdatesEnabled(False)
return False
def _delayedUpdate(self):
print "Performing actual update"
self._resizeTimer.stop()
self.target.setUpdatesEnabled(True)
Note that we are using this on just some arbitrary widget inside of our main window. We add a delay updater to it with this line:
self.delayer = DelayedUpdater(self.someWidget)
The DelayedUpdater watches the resize events of the target widget, and performs delayed updates. You could expand the eventFilter to also watch for other events, like a move.

Create a PyGTK GUI with event to reload itself

I'm prototyping GUI layout with PyGTK, sometimes using glade/builder sometimes not, and the following scene repeats endlessly:
Modify the code
Run the GUI script
Judge the result
Modify again
Run again...
So, since I heard that Python allows reloading of modules, I would like to know if it is possible to modify the code WITHOUT CLOSING THE WINDOW, and then, from the window itself, say, clicking on a button, "reload" the window reflecting the changes in code.
Since it is a conceptual question, I don't have any specific code to show.
Thanks for the attention
I think it is possible if you do the following:
Identify and isolate the widget W that you want to see updated when you press the button (if you want to see the whole window updated, then make it whatever you add in the window, not the window itself).
Write a function (or class) that creates and returns this widget
Put this function or class in a module that you will reload
Create your button outside W and connect it to a function that does the following
Remove current W from window
Reload the module
Create new instance of W
Add it to the window
Of course, the critical step here is "reload the module". I guess you have to make sure no code from the module is running and no other module depends on variables defined on this module.
EDIT: I had some time, so I made a little prototype. Change the label in widget_module.py and then hit Update
gui.py
# Load in pygtk and gtk
import pygtk
pygtk.require('2.0')
import gtk
import widget_module
# Define the main window
class Whc:
def __init__(self):
# Window and framework
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("destroy", self.destroy)
# A Button, with an action
# Add it to the geometry
# show the button
self.button = gtk.Button("Update")
self.button.connect("clicked", self.update, None)
self.vbox = gtk.VBox()
self.vbox.pack_start(self.button)
self.widget = widget_module.get_widget()
self.vbox.pack_start(self.widget)
self.window.add(self.vbox)
# Show the window
self.window.show_all()
# Callback function for use when the button is pressed
def update(self, widget, data=None):
print "Update"
self.vbox.remove(self.widget)
reload(widget_module)
self.widget = widget_module.get_widget()
self.vbox.pack_start(self.widget)
self.widget.show()
# Destroy method causes appliaction to exit
# when main window closed
def destroy(self, widget, data=None):
gtk.main_quit()
# All PyGTK applicatons need a main method - event loop
def main(self):
gtk.main()
if __name__ == "__main__":
base = Whc()
base.main()
widget_module.py
import pygtk
import gtk
def get_widget():
return gtk.Label("hello")

Resources