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.
Related
I'm trying to write a program that I can hide and show via hotkeys. I managed to get the application to show and hide using the library "keyboard", however due to the "wait" function of the library, it prevents the Text box from functioning correctly. I have tried using the key bindings within Tkinter, however I had a different problem, whereby once the program was hidden or another application was selected, I couldn't return the focus to the hidden window via the hotkey.
import Tkinter as Tk
import keyboard
class MyApp(object):
def __init__(self, parent):
self.root = parent
self.root.title("Main frame")
self.frame = Tk.Frame(parent)
self.frame.pack()
self.editor = Tk.Text(self.frame)
self.editor.pack()
self.editor.config(font="Courier 12")
self.editor.focus_set()
keyboard.add_hotkey('ctrl+alt+s', self.show)
keyboard.add_hotkey('ctrl+alt+h', self.hide)
keyboard.wait()
self.root.withdraw()
def show(self):
self.root.update()
self.root.deiconify()
def hide(self):
self.root.withdraw()
if __name__ == "__main__":
root = Tk.Tk()
root.geometry("800x600")
app = MyApp(root)
root.mainloop()
Any assistance would be great :)
Just drop this wait command, its an additional mainloop, which is not needed as Tkinter does its job. I tried to fix your problem with threading, but as I wanted to check exactly what is NOT working, I accidentially made what I suppose you wanted to. So the Code is:
import tkinter as tk
import keyboard
class App(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("800x600")
self.title("Main frame")
self.editor = Tk.Text(self)
self.editor.pack()
self.editor.config(font="Courier 12")
self.editor.focus_set()
keyboard.add_hotkey('ctrl+alt+s', self.show)
keyboard.add_hotkey('ctrl+alt+h', self.hide)
def show(self):
self.update()
self.deiconify()
def hide(self):
self.update()
self.withdraw()
if __name__ == "__main__":
App().mainloop()
I hope this works for you. I'd also recommend changing this key settings. Testing with those in PyZo is IMPOSSIBLE! It always tries to "save as...", which I don't want to...
I am currently creating a little program which inserts Tkinter Entry boxes into Google Calendar (after all different kind of checks ofcourse). That part is not the problem.
Since I am running a terminal at the same time that I don't want to 'hold' during the Tkinter window is open.
When I close the window using
def quit(self):
self.thread.stop()
self.destroy()
All the parts of the window disappear, but the window stays on screen.
class window(Frame):
def __init__(self, parent, thread):
Frame.__init__(self, parent)
self.parent = parent
self.w = 600
self.h = 400
self.initUI()
self.center()
self.thread = thread
I use this funciton to create the class:
def main(thread):
root = Tk()
root.resizable(width=False, height=False)
app = window(root, thread)
root.mainloop()
The myThread class.
class myThread(threading.Thread):
def __init__(self,threadType):
threading.Thread.__init__(self)
self.threadType = threadType
self.event = threading.Event()
def stop(self):
self.event.set()
def run(self):
if self.threadType == "new_app":
newappoint.main(self)
elif self.threadType == "main":
main()
Can anybody tell me if I missed something that would make the window close properly.
The thread is closed after calling the self.quit()
instead of self.destroy() do this self.parent.destroy().
As far as my understanding we are passing tk object to the class window and it used as parent in the class.So instead of using root to create widgets we are using parent.
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 have written some Tkinter dialogs that are called from within an app running in a mainloop. My dialog initializer usually looks like this:
class OKCANCEL( Toplevel ):
def __init__( self, parent, title ):
Toplevel.__init__( self, parent )
self.parent = parent
self.title( title )
...
I call this from within my app as:
o = OKCANCEL( self.mainWindow, mtext = QUIT_REALLY )
where self.mainWindowis a Frame within my App window (a class inheriting from Frame).
Now, every time that I am calling the dialog, a smaller window appears and then the intended window replaces the small one. The display duration is short, however, it is annoying to have this appening.
Am I missing the obvious?
Can someone point me to my mistake?
Thanks!
Edit: I just wrote up a small example and tried to reproduced the behaviour, however, either it was not present or it happened too fast. I am kind of confused, the small window that appears even has a window title that is nowhere to be found in the code (the big, huge chunk of code that is not on this page).
# -*- coding: UTF-8 -*-
import os
from Tkinter import *
class WARNING(Toplevel): #ok only dialog
def __init__(self,parent,wtext,wtitle=''):
Toplevel.__init__(self,parent)
self.parent=parent
self.protocol("WM_DELETE_WINDOW",self._NULL) #we need to include a check again...
self.title(wtitle)
self.attributes('-topmost',True)
self.transient()
mainWindow=Frame(self)
tFrame=Frame(mainWindow)
tLabel=Label(tFrame,text=wtext,height=3,bd=10)
tLabel.pack()
bFrame=Frame(mainWindow,bd=5)
bOK=Button(bFrame,bd=1,text='Ok',command=self._DONE)
bOK.pack(side='left')
tFrame.grid(row=0,column=0)
bFrame.grid(row=1,column=0,sticky=E)
mainWindow.pack()
def _DONE(self,Event=None):
self.destroy()
def _NULL():
pass
class WarnApp(Frame):
def __init__(self,parent):
Frame.__init__(self, parent)
self.parent=parent
self.initUI()
def initUI(self):
self.parent.title("Warning test")
self.mainWindow = Frame(self.parent)
B1=Button(self.mainWindow,command=self.Warn,text='Warn-Me')
B1.pack(fill=BOTH)
self.mainWindow.pack(fill=BOTH)
self.pack(fill=BOTH)
def Warn(self):
warn=WARNING(self,wtext='This is a warning',wtitle='generic warning dialog')
self.wait_window(warn)
def main():
root=Tk()
app = WarnApp(root)
app.mainloop()
if __name__ == "__main__":
main()
`
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()