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()
Related
My python application stops responding whenever I do anything "wx" a few times...whether that be clicking buttons, menus, inside the text area, etc.
This seems to be a problem with my use of windows hooks elsewhere in the program. When the two are used together, things go bad.
While the example below might not make much sense outside the context of the real application, it is what I came up with as a minimal example. I took everything out I could in order to try to diagnose the problem with minimal extra complexities.
It really seems like the second windows message pump called from the worker thread is the factor that makes it happen. If I comment that out, the app doesn't hang...or do anything useful...Hooks requires a pump, and I can't put it on my UI thread or it will the hooks will timeout, which led me to this POC in the first place...
If you run this and click the mouse a dozen times or so, you will notice the text blocks get garbled and then the app just hangs. Does anyone have any insight as to why?
I am using
python 2.7.16 32 bit
pyHook 1.5.1
wxPython 3.0.2.0 32bit for python 2.7
(update) I've also tried with the same results
python 2.7.16 32 bit
pyHook 1.5.1
wxPython 4.0.6 32 bit for python 2.7
We use these in the real application, because the my higher ups want to continue to support Windows XP (A whole different conversation)
main.py
import wx
from twisted.internet import wxreactor
from windows_hooks import WindowsHooksWrapper
from main_window import MainWindow
def main():
hook_wrapper = WindowsHooksWrapper()
hook_wrapper.start()
app = wx.App(False)
frame = MainWindow(None, 'Hooks Testing', hook_wrapper)
from twisted.internet import reactor
reactor.registerWxApp(app)
reactor.run()
hook_wrapper.stop()
if __name__ == "__main__":
wxreactor.install()
main()
windows_hooks.py
import pyHook
import threading
import pythoncom
class WindowsHooksWrapper(object):
def __init__(self):
self.hook_manager = None
self.started = False
self.thread = threading.Thread(target=self.thread_proc)
self.window_to_publish_to = None
print "HookWrapper created on Id {}".format(threading.current_thread().ident)
def __del__(self):
self.stop()
def start(self):
if self.started:
self.stop()
self.started = True
self.thread.start()
def stop(self):
if not self.started:
return
self.started = False
self.thread.join()
def on_mouse_event(self, event):
"""
Called back from pyHooks library on a mouse event
:param event: event passed from pyHooks
:return: True if we are to pass the event on to other hooks and the process it was intended
for. False to consume the event.
"""
if self.window_to_publish_to:
from twisted.internet import reactor
reactor.callFromThread(self.window_to_publish_to.print_to_text_box, event)
return True
def thread_proc(self):
print "Thread started with Id {}".format(threading.current_thread().ident)
# Evidently, the hook must be registered on the same thread with the windows msg pump or
# it will not work and no indication of error is seen
# Also note that for exception safety, when the hook manager goes out of scope, the
# documentation says that it unregisters all outstanding hooks
self.hook_manager = pyHook.HookManager()
self.hook_manager.MouseAll = self.on_mouse_event
self.hook_manager.HookMouse()
while self.started:
pythoncom.PumpMessages()
print "Thread exiting..."
self.hook_manager.UnhookMouse()
self.hook_manager = None
main_window.py
import threading
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title, hook_manager):
wx.Frame.__init__(self, parent, title=title, size=(800, 600))
self.hook_manager = hook_manager
self.CreateStatusBar()
menu_file = wx.Menu()
menu_item_exit = menu_file.Append(wx.ID_EXIT, "E&xit", " Terminate the program")
menu_help = wx.Menu()
menu_item_about = menu_help.Append(wx.ID_ABOUT, "&About", " Information about this program")
menu_bar = wx.MenuBar()
menu_bar.Append(menu_file, "&File")
menu_bar.Append(menu_help, "&Help")
self.SetMenuBar(menu_bar)
self.panel = MainPanel(self, hook_manager)
self.Bind(wx.EVT_MENU, self.on_about, menu_item_about)
self.Bind(wx.EVT_MENU, self.on_exit, menu_item_exit)
self.Show(True)
def on_about(self, e):
dlg = wx.MessageDialog(self, "A window to test Windows Hooks", "About Test Windows Hooks",
wx.OK)
dlg.ShowModal()
dlg.Destroy()
def on_exit(self, e):
self.Close(True)
class MainPanel(wx.Panel):
def __init__(self, parent, hook_manager):
self.hook_manager = hook_manager
hook_manager.window_to_publish_to = self
self.consuming = False
wx.Panel.__init__(self, parent)
self.textbox = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY)
self.horizontal = wx.BoxSizer()
self.horizontal.Add(self.textbox, proportion=1, flag=wx.EXPAND)
self.sizer_vertical = wx.BoxSizer(wx.VERTICAL)
self.sizer_vertical.Add(self.horizontal, proportion=1, flag=wx.EXPAND)
self.SetSizerAndFit(self.sizer_vertical)
self.called_back_count = 0
def print_to_text_box(self, event):
self.called_back_count += 1
print "Printing message {} on Thread with Id {}".format(self.called_back_count,
threading.current_thread().ident)
self.textbox.AppendText('MessageName: {}\n'.format(event.MessageName))
self.textbox.AppendText('Message: {}\n'.format(event.Message))
self.textbox.AppendText('Time: {}\n'.format(event.Time))
self.textbox.AppendText('Window: {}\n'.format(event.Window))
self.textbox.AppendText('WindowName: {}\n'.format(event.WindowName))
self.textbox.AppendText('Position: {}\n'.format(event.Position))
self.textbox.AppendText('Wheel: {}\n'.format(event.Wheel))
self.textbox.AppendText('Injected: {}\n'.format(event.Injected))
self.textbox.AppendText('---\n')
I've also tried a version without Twisted and used wxPostEvent with a custom event instead, but we were suspecting that might be the problem, so I changed it to use twisted and it's still no good.
I'll post an edited listing with that in a bit.
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'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()
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()