I'm trying to get my empty frame so that when I click the X it just hides the window, and then if I hit the dock icon it'll reveal the window. It's turning out to be more of a challenge than I expected.. I used http://wiki.wxpython.org/Optimizing%20for%20Mac%20OS%20X/ but I can't make ends of it.
Here's my code:
import wx
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "title",style=wx.SYSTEM_MENU | wx.CLOSE_BOX | wx.CAPTION, size=(300,300))
panel = wx.Panel(self)
def MacReopenApp(self, event):
print "Attempting to reveal the window."
def MacHideApp(self, event):
print "Attempting to hide the window."
if __name__ == '__main__':
app = wx.App()
frame = Frame()
frame.Show()
app.MainLoop()
The document you link to states that you need to add those event handlers on the App. You have currently defined them on a frame. So you need to extend wx.App and define those event handlers, and instantiate your own App instead of wx.App.
So (shortened example copied from your link):
class MyApp(wx.App):
def __init__(self, *args, **kwargs):
wx.App.__init__(self, *args, **kwargs)
# This catches events when the app is asked to activate by some other
# process
self.Bind(wx.EVT_ACTIVATE_APP, self.OnActivate)
#.....
def MacReopenApp(self):
"""Called when the doc icon is clicked, and ???"""
self.BringWindowToFront()
app = MyApp(False)
app.MainLoop()
Related
i am trying to make a windows 10 toast notification that will run code if its clicked but my code only shows the notification and gives me an error when i click it
import os
import wx
import wx.adv
class MyApp(wx.App):
def OnInit(self):
sTitle = 'test'
sMsg = 'test'
nmsg = wx.adv.NotificationMessage(title=sTitle, message=sMsg)
nmsg.SetFlags(wx.ICON_INFORMATION)
nmsg.Show(timeout=wx.adv.NotificationMessage.Timeout_Auto)
self.Bind(wx.EVT_NOTIFICATION_MESSAGE_CLICK, self.notifclicked)
return True
def _notifclicked(self, evt):
print("notification has been clicked")
app = MyApp()
app.MainLoop()
error code : AttributeError: module 'wx' has no attribute 'EVT_NOTIFICATION_MESSAGE_CLICK'
Without wishing to say definitely that NotificationMessage is unfinished business, I'm going to suggest it.
I suspect that it is based on notify and notify2 which purport to support action callbacks but don't.
They should be relying on Dbus sending stuff back but it doesn't look like it does or you have to jump through hoops to make it happen. (Dbus MainLoop setup for example)
I have decided to adapt your code slightly, just to show how to add action buttons, although they are nothing more than eye candy and to show how I think the event callbacks should work, if it ever comes to reality.
Of course, if it currently does work and I haven't worked out how to do it, I'll happily eat these words. I code exclusively on Linux, so there's that as a caveat.
import wx
import wx.adv
Act_Id_1 = wx.NewIdRef()
Act_Id_2 = wx.NewIdRef()
class MyFrame(wx.Frame):
def __init__(self, parent=None, id=wx.ID_ANY, title="", size=(360,100)):
super(MyFrame, self).__init__(parent, id, title, size)
self.m_no = 0
self.panel = wx.Panel(self)
self.Mbutton = wx.Button(self.panel, wx.ID_ANY, label="Fire off a message", pos=(10,10))
self.Bind(wx.EVT_BUTTON, self.OnFire, self.Mbutton)
#self.Bind(wx.adv.EVT_NOTIFICATION_MESSAGE_CLICK, self._notifclicked)
#self.Bind(wx.adv.EVT_NOTIFICATION_MESSAGE_DISMISSED, self._notifdismissed)
#self.Bind(wx.adv.EVT_NOTIFICATION_MESSAGE_ACTION, self._notifaction, id=Act_Id_1)
self.Show()
def OnFire(self, event):
self.m_no +=1
sTitle = 'Test heading'
sMsg = 'Test message No '
nmsg = wx.adv.NotificationMessage(title=sTitle, message=sMsg+str(self.m_no))
nmsg.SetFlags(wx.ICON_INFORMATION)
nmsg.AddAction(Act_Id_1, "Cancel")
nmsg.AddAction(Act_Id_2, "Hold")
nmsg.Show(timeout=10)
def _notifclicked(self, event):
print("notification has been clicked")
def _notifdismissed(self, event):
print("notification dismissed")
def _notifaction(self, event):
print("Action")
app = wx.App()
frame = MyFrame()
app.MainLoop()
I have a wxPython GUI, and I am attempting to use unittest to test some of my modal dialogs. I tried to follow the example given here (you have to scroll down to the bottom of the page): http://wiki.wxpython.org/Unit%20Testing%20with%20wxPython, but it does not work for me. It simply freezes in the middle.
I've adapted the code from the wiki to this:
btn_id = wx.NewId()
class MyDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, -1, 'Test')
self.btn = wx.Button(self, btn_id, label="OK!!")
self.btn.Bind(wx.EVT_BUTTON, self.close_dialog)
def close_dialog(self, event):
print 'close me'
class TestMyDialog(unittest.TestCase):
def setUp(self):
self.app = wx.App()
self.frame = wx.Frame(None)
self.frame.Show()
def tearDown(self):
wx.CallAfter(self.app.Exit)
self.app.MainLoop()
def testDialog(self):
def clickOK():
clickEvent = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn_id)
self.dlg.ProcessEvent(clickEvent)
print 'finished clickOK'
wx.CallAfter(clickOK)
self.ShowDialog()
def ShowDialog(self):
self.dlg = MyDialog(self.frame)
self.dlg.ShowModal()
self.dlg.Destroy()
if __name__ == '__main__':
unittest.main()
To my understanding, what should happen is that ShowDialog is called, then gets 'stuck' on ShowModal, at which time clickOk should run (called by wx.CallAfter). This seems to happen, but for some reason the click event isn't actually processed, and the tests hangs. When I run MyDialog not in testing the event binding works fine and the dialog closes when the Ok button is clicked.
I shouldn't need app.mainloop() to be able to ProcessEvent, right? What is going on here?
Have a look at the unittests in Phoenix https://github.com/wxWidgets/Phoenix , look at test_dialog.py and the base staff in wtc.py
I'm trying to build an app that incorporates wxwidgets (just for the tray icon) and Tkinter (for the rest of the GUI).
import wx
import Tkinter
TRAY_TOOLTIP = 'System Tray Icon'
TRAY_ICON = 'icon.png'
frm = False
class TaskBarIcon(wx.TaskBarIcon):
def __init__(self):
super(TaskBarIcon, self).__init__()
self.set_icon(TRAY_ICON)
self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)
def set_icon(self, path):
icon = wx.IconFromBitmap(wx.Bitmap(path))
self.SetIcon(icon, TRAY_TOOLTIP)
def on_left_down(self, event):
createframe()
class Frame(Tkinter.Tk):
def __init__(self, parent):
Tkinter.Tk.__init__(self, parent)
self.parent = parent
self.protocol('WM_DELETE_WINDOW', self.closewindow)
self.grid()
def maximize(self):
# supposed to try to hide and bring a window back up
# full code removes the icon from the task bar, so I needed another way to make the window visible again
self.withdraw()
self.deiconify()
def closewindow(self):
self.destroy()
global frm
frm = False
def createframe():
global frm
if isinstance(frm, Tkinter.Tk): # if a window is open, it goes through this if statement
frm.maximize() # and crashes here.
else:
frm = Frame(None)
frm.title('Frame')
frm.mainloop()
def main():
app = wx.App()
TaskBarIcon()
app.MainLoop()
if __name__ == '__main__':
main()
You can run this code and hopefully see the problem. When you left-click the tray icon, a window pops up, you can close it and reopen it, however if you minimize the window (or just click the tray icon while the window is open), the app crashes. I suppose frm.maximize() is the problem, since I can call self.maximize() from within the class without trouble, but I was not able to find a solution.
I had the same problem when I was trying to do frm.destroy() from the TaskBarIcon class (while frm.quit() worked just fine), so maybe that's a hint?
You can't combine wxpython and tkinter in the same program.
I've boiled my problem down to the example code shown in this post. Note that I'm not calling app.MainLoop() because this isn't an interactive window; I want it to pop up at the beginning, show some progress bars while work happens, and disappear when complete.
My (limited) understanding of wxPython and wx.Yield() led me to believe that calling wx.Yield() after some UI work would flush those changes to the display. That is not occurring -- when I run this script, there is a gray box where "Hello World" should be.
What am I doing wrong?
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, size=(400,400))
self.panel = wx.Panel(self, -1)
wx.StaticText(self.panel, -1, "Hello World", (20,20))
wx.Yield()
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(None, -1)
self.frame.Show(True)
self.SetTopWindow(self.frame)
return True
def run():
app = MyApp(redirect=False)
import time; time.sleep(5)
run()
You need to be yielding or updating on a regular basis, so that when your OS/window manager sends repaint messages to your app, it can handle them. I am not 100% sure about wxPython as I haven't used it recently but I don't think you can do what you want without the main loop to handle the messages appropriately.
You might find something useful here about threading the main loop, however (as well as explanation of why the main loop is important): http://wiki.wxpython.org/MainLoopAsThread
instead of wx.Yield()
just call self.Update()
Without the MainLoop no events will be fired and also .Refresh will not work.
I guess wxSplashscreen may be what you are looking for. Example: http://wiki.wxpython.org/SplashScreen
Not that it will do the original poster any good after all this time but wx.Yield() would have done the job. It just needs to be in the right place as does the self.Show()
The following outputs a progress bar which gets updated.
import wx
import time
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, size=(290,200))
self.panel = wx.Panel(self, -1)
wx.StaticText(self.panel, -1, "Hello World", (20,20))
self.gauge = wx.Gauge(self.panel, -1, 50, pos=(20,50), size=(250, 20))
self.Show()
n = 0
while n < 50:
n = n+1
self.gauge.SetValue(n)
wx.Yield()
time.sleep(1)
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(None, -1)
self.SetTopWindow(self.frame)
return True
def run():
app = MyApp()
run()
I have a wxPython notebook, in this case a wx.aui.AuiNotebook. (but this problem has happened with other kinds of notebooks as well.) In my notebook I have a widget, in this case a subclass of ScrolledPanel, for which I am trying to do some custom event handling (for wx.EVT_KEY_DOWN). However, the events are not being handled. I checked my code outside of the notebook, and the event handling works, but when I put my widget in the notebook, the event handler doesn't seem to get invoked when the event happens.
Does the notebook somehow block the event? How do I solve this?
I tried reproducing your problem but it worked fine for me. The only thing I can think of is that there is one of your classes that also binds to wx.EVT_KEY_DOWN and doesn't call wx.Event.Skip() in its callback. That would prevent further handling of the event. If your scrolled panel happens to be downstream of such an object in the sequence of event handlers it will never see the event.
For reference, here's an example that worked for me (on Windows). Is what you're doing much different than this?
import wx
import wx.aui, wx.lib.scrolledpanel
class AppFrame(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
# The notebook
self.nb = wx.aui.AuiNotebook(self)
# Create a scrolled panel
panel = wx.lib.scrolledpanel.ScrolledPanel(self, -1)
panel.SetupScrolling()
self.add_panel(panel, 'Scrolled Panel')
# Create a normal panel
panel = wx.Panel(self, -1)
self.add_panel(panel, 'Simple Panel')
# Set the notebook on the frame
self.sizer = wx.BoxSizer()
self.sizer.Add(self.nb, 1, wx.EXPAND)
self.SetSizer(self.sizer)
# Status bar to display the key code of what was typed
self.sb = self.CreateStatusBar()
def add_panel(self, panel, name):
panel.Bind(wx.EVT_KEY_DOWN, self.on_key)
self.nb.AddPage(panel, name)
def on_key(self, event):
self.sb.SetStatusText("key: %d [%d]" % (event.GetKeyCode(), event.GetTimestamp()))
event.Skip()
class TestApp(wx.App):
def OnInit(self):
frame = AppFrame(None, -1, 'Click on a panel and hit a key')
frame.Show()
self.SetTopWindow(frame)
return 1
if __name__ == "__main__":
app = TestApp(0)
app.MainLoop()