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

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.

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.

Run thread in main window for both Windows and Linux

I am creating a game to run inside a GUI (text area, button, menu etc) I've created a GUI with wxpython. I create a panel inside the main window, which runs a pygame thread.
Problem:
On Windows, the pygame thread runs perfectly inside the main window. But on Linux, the pygame pop up on a new window. How can I set this such that both windows and Linux run the thread in the main window?
Code:
class SDLPanel(wx.Panel):
def __init__(self,parent,ID,tplSize):
global pygame
global pygame_init_flag
wx.Panel.__init__(self, parent, ID, size=tplSize)
self.Fit()
if (sys.platform == 'win32'):
os.environ['SDL_WINDOWID'] = str(self.GetHandle())
os.environ['SDL_VIDEODRIVER'] = 'windib'
else:
os.environ['SDL_VIDEODRIVER'] = 'x11'
#here is where things change if pygame has already been initialized
#we need to do so again
if pygame_init_flag:
#call pygame.init() on subsaquent windows
pygame.init()
else:
#import if this is the first time
import pygame
pygame_init_flag = True #make sure we know that pygame has been imported
pygame.display.init()
window = pygame.display.set_mode(tplSize)
self.thread = SDLThread(window)
self.thread.Start()
def __del__(self):
self.thread.Stop()
print "thread stoped"
#very important line, this makes sure that pygame exits before we
#reinitialize it other wise we get errors
pygame.quit()
Solved problem.
In main window we must self.Show()
Idk why in linux the main window must be showed . Same code.
Tks all
This is a disclaimer alert, according to https://forums.libsdl.org/viewtopic.php?p=39332, the solution works only with SDL 1.2 and not 2.0.

Threading wxPython app with background process for multitouch detection

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.

LIBDBUSMENU-GLIB-WARNING

I created recently a piece of code that always showed me a message like this twice when I closed it:
(python:11712): LIBDBUSMENU-GLIB-WARNING **: Trying to remove a child that doesn't believe we're it's parent.
What I want is to get rid from that warning.
This piece of code reproduce such behaviour (the message appears once when closed):
import wx
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="Sample")
menuBar = wx.MenuBar()
filemenu = wx.Menu()
filemenu.Append(wx.ID_ANY,"&Pass")
menuBar.Append(filemenu,"&File")
self.SetMenuBar(menuBar)
app = wx.PySimpleApp()
frame = MyFrame(None)
frame.Show()
app.MainLoop()
It works perfectly, yet the warning message appears when I close the app.
That's a Linux-only message, nothing appears when I tried it on Windows.
A work around I used was to bind a close event handler in my frame
def __init__(self, parent):
... # Previous code here
self.Bind(wx.EVT_CLOSE, self.OnClose)
And make such event handler as
def OnClose(self, evt):
for menu in self.GetMenuBar().GetMenus(): # (wx.Menu, caption) tuples
menu[0].Destroy() # Bad parents won't remove you, you'll DIE before!!!
evt.Skip()
That worked ok in that simple app on Linux, however it crashed when I tried on Windows. So I wanted to see what was happening in the runtime while on Linux, replacing the line
menu[0].Destroy()
to
print menu[0].Parent
And it showed me "None" as answer. However I couldn't make that:
menu[0].Parent = self
And neither:
menu[0].Parent = self.GetMenuBar()
Since both raises TypeError:
TypeError: in method 'Menu_SetParent', expected argument 2 of type 'wxMenu *'
This is a GTK warning. You could debug it (with gdb and a debugging variant of your Gtk and Glib libraries - e.g. libgtk-3-0-dbg and libglib2.0-0-dbg packages on Ubuntu or Debian) by running with the environment variable
export G_DEBUG=fatal-warnings
I have no idea if the bug is in Gtk itself, in WxWidget, in the C glues for Python, or in your Python code.

wxWidgets dialog doesn't get a maximize box

I've got a wxPython (via wxGlade) app with a dialog that has wx.MAXIMIZE_BOX set in the style, but the maximize box doesn't appear when I run the app.
Here's a minimal program that exhibits the behavior:
#!/usr/bin/env python
import wx
class MyDialog(wx.Dialog):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_DIALOG_STYLE|wx.MAXIMIZE_BOX
wx.Dialog.__init__(self, *args, **kwds)
self.SetTitle("dialog_1")
self.Layout()
if __name__ == "__main__":
app = wx.PySimpleApp(0)
wx.InitAllImageHandlers()
dialog_1 = MyDialog(None, -1, "")
app.SetTopWindow(dialog_1)
dialog_1.Show()
app.MainLoop()
Note that wx.MAXIMIZE_BOX is set, but when I run this program I don't get a maximize box on the dialog:
Is this a window manager issue?
Is there something I can do to make the maximize box show up? (My real dialog has a bunch of scrolled text from a log file and it's easier to click maximize than it is to manually resize to fill the screen.)
I'm using:
Linux (Ubuntu 10.04 LTS)
python-wxgtk, libwxgtk2.8-0, libwxbase2.8-0: 2.8.10.1-0ubuntu1.2
metacity: 1:2.30.1-0ubuntu1.1
I found this in the wxWidgets docs:
"Under Unix or Linux, MWM (the Motif Window Manager) or other window managers recognizing the MHM hints should be running for any of these styles ( including wxMAXIMIZE_BOX ) to have an effect."
So it sounds like it might well be a window manager issue.

Resources