Python 3 Wx Change tray icon - python-3.x

I'm using WxPython (phoenix new version) to have an icon on the tray bar while the programs runs, but I would some events to change the icon used.
I found this great example to get things started: Quick and easy: trayicon with python?
But it doesn't have the example to cycle through icons.
Just after the imports it has this line:
TRAY_ICON = 'icon.png'
And I've tried to use that as sort of a variable, and adding the following line to an event (it has some mock events like hellow world on a right click
TRAY_ICON = 'icon2.png'
but it didn't work =//
I only found examples in c, and one in python but using a very complex win32 alternative that I can't figure out

This should provide you with enough to solve your problem.
import wx
import wx.adv
ICON = 'toggle1.png'
ICONS = ["toggle1.png", "toggle2.png"]
X=[1,0]
class TaskBarIcon(wx.adv.TaskBarIcon):
def __init__(self, frame):
self.frame = frame
self.toggle = 0
wx.adv.TaskBarIcon.__init__(self)
self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.OnToggle)
self.OnSetIcon(ICON)
def CreatePopupMenu(self):
menu = wx.Menu()
togglem = wx.MenuItem(menu, wx.NewId(), 'Toggle Icon')
menu.Bind(wx.EVT_MENU, self.OnToggle, id=togglem.GetId())
menu.Append(togglem)
menu.AppendSeparator()
flashm = wx.MenuItem(menu, wx.NewId(), 'Flash Icon')
menu.Bind(wx.EVT_MENU, self.OnTimer, id=flashm.GetId())
menu.Append(flashm)
menu.AppendSeparator()
quitm = wx.MenuItem(menu, wx.NewId(), 'Quit')
menu.Bind(wx.EVT_MENU, self.OnQuit, id=quitm.GetId())
menu.Append(quitm)
return menu
def OnSetIcon(self, path):
icon = wx.Icon(path)
self.SetIcon(icon, path)
def OnToggle(self, event):
self.toggle=X[self.toggle]
use_icon = ICONS[self.toggle]
self.OnSetIcon(use_icon)
def OnTimer(self,event):
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnInUseTimer)
self.timer.Start(1000)
def OnInUseTimer(self,event):
self.OnToggle(None)
def OnQuit(self, event):
self.RemoveIcon()
wx.CallAfter(self.Destroy)
self.frame.Close()
if __name__ == '__main__':
app = wx.App()
frame=wx.Frame(None)
TaskBarIcon(frame)
app.MainLoop()
and the images:
In action:

Related

wxPython application does not exit from systray icon left-click

when I left-click my icon in the system tray it does not exit the application - I can see it does go through the function though when I set a breakpoint, if however I click Exit from the popup menu then the program exits as expected (runs the same function), why is this please?
It is the on-exit function that should close the application:
self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.on_exit)
Thanks very much for any help you can provide.
import wx
import wx.adv
TRAY_TOOLTIP = 'ShutMeDown'
TRAY_ICON = icon.png
def create_menu_item(menu, label, func):
item = wx.MenuItem(menu, -1, label)
menu.Bind(wx.EVT_MENU, func, id=item.GetId())
menu.Append(item)
return item
class TaskBarIcon(wx.adv.TaskBarIcon):
def __init__(self, frame):
self.frame = frame
super(TaskBarIcon, self).__init__()
self.SetIcon(wx.IconFromBitmap(wx.Bitmap(TRAY_ICON)), TRAY_TOOLTIP)
self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.on_exit)
def CreatePopupMenu(self):
menu = wx.Menu()
create_menu_item(menu, 'Exit', self.on_exit)
return menu
def on_exit(self, event):
wx.CallAfter(self.Destroy)
self.frame.Close()
class App(wx.App):
def OnInit(self):
frame = wx.Frame(None)
self.SetTopWindow(frame)
TaskBarIcon(frame)
return True
def main():
app = App(False)
app.MainLoop()
if __name__ == '__main__':
main()

PyQt5 - Can QTabWidget content extend up to Main Window edges, even with no content?

I am new to PyQt5... Simple question here.
I am using PyQt5 to build a simple application. This application has a Main Window containing a QTabWidget with 3 tabs. Once the application starts, all tab pages are empty and get filled later on. When tab pages are empty, I would still like them to appear as blank pages and extend up to the Main Window edges.
I've been trying to achieve this in two ways: using a layout and using the setGeometry function. Yet the tab pages never extend vertically very far, and horizontally they never go beyond the last tab. See code below.
import sys
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Window With Tabs")
self.setGeometry(50,50,400,400)
oTabWidget = QTabWidget(self)
oPage1 = QWidget()
oLabel1 = QLabel("Hello",self)
oVBox1 = QVBoxLayout()
oVBox1.addWidget(oLabel1)
oPage1.setLayout(oVBox1)
oPage2 = QWidget()
oPage2.setGeometry(0,0,400,400)
oPage3 = QWidget()
oPage3.setGeometry(0,0,400,400)
oTabWidget.addTab(oPage1,"Page1")
oTabWidget.addTab(oPage2,"Page2")
oTabWidget.addTab(oPage3,"Page3")
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
oMainwindow = MainWindow()
sys.exit(app.exec_())
Any idea how to modify the code so the empty pages will extend up to the edges of Main Window ?
Set a layout on the main widget:
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Window With Tabs")
self.setGeometry(50,50,400,400)
layout = QVBoxLayout(self)
oTabWidget = QTabWidget(self)
layout.addWidget(oTabWidget)
The setGeometry calls on the other widgets are redundant.
import sys
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
# window object
def __init__(self):
super().__init__()
self.initGUI() # call custom code
def initGUI(self):
self.setWindowTitle("Window With Tabs") # window...
self.setGeometry(50,50,400,400) #...properties
TabW=self.createTabs() # a custom-tab object
layout = QVBoxLayout(self) # main window layout
layout.addWidget(TabW) #populate layout with Tab object
self.show() # display window
def createTabs(self): # create and return Tab object
oPage1 = QWidget() # tabs...
oPage2 = QWidget()
oPage3 = QWidget()
oTabWidget = QTabWidget() # Tabobject
oTabWidget.addTab(oPage1,"Page1") # populate tab object...
oTabWidget.addTab(oPage2,"Page2")
oTabWidget.addTab(oPage3,"Page3")
return oTabWidget # return tab object
if __name__ == "__main__": # Rest is History!
app = QApplication(sys.argv)
oMainwindow = MainWindow()
sys.exit(app.exec_())

How do I thread a single method on a PyQt GUI so that the rest of the GUI is still accessible?

Basically, when the Switch Button is pressed, which is connected to the reconfigure method, I want everything in the reconfigure method to run as a separate thread/process so the main GUI is still accessible and not being blocked. Below is a watered down version of my code.
import sys, time
from PyQt4 import QtGui, QtCore
from PyQt4.Qt import *
#//Popup Class - Will appear when the Switch Button is pressed
class Popup(QWidget):
def __init__(self):
QWidget.__init__(self)
#//nothing here now, it will have a message telling user to wait while program is run
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
grid = QtGui.QGridLayout()
label_header = QtGui.QLabel("TEST RECONFIGURE")
font = label_header.font()
font.setPointSize(24)
label_header.setFont(font)
#//Creating Static Labels that will be placed in the GUI
label_1 = QtGui.QLabel("Menu 1:")
label_2 = QtGui.QLabel("Menu 2:")
label_spacer = QtGui.QLabel("")
label_cfg = QtGui.QLabel("Current Configuration: '/tmp/directory_here' ")
global comboBox1
comboBox1 = QtGui.QComboBox()
comboBox1.addItem("1")
comboBox1.addItem("2")
global comboBox2
comboBox2 = QtGui.QComboBox()
comboBox2.addItem("3")
comboBox2.addItem("4")
#//Switch Button!!!
global switchButton
switchButton = QPushButton("Switch")
switchButton.clicked.connect(self.reconfigure)
quitButton = QtGui.QPushButton('Quit')
quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit)
#//Configure the grid layout
grid.addWidget(label_spacer, 0,0,9,9)
grid.addWidget(label_header, 0,0,1,6)
grid.addWidget(label_1, 1,0,1,1)
grid.addWidget(comboBox1, 2,0,1,1)
grid.addWidget(label_2, 3,0,1,1)
grid.addWidget(comboBox2, 4,0,1,1)
grid.addWidget(switchButton, 5,0,1,2)
grid.addWidget(label_cfg, 6,0,1,9)
grid.addWidget(quitButton, 9,9,1,1)
self.setLayout(grid)
self.setGeometry(640,300,400,600)
self.show()
#//open up the popup window for switch button, and reconfigure
def reconfigure(self):
print "Opening New Window"
self.w = Popup()
self.w.setGeometry(QRect(self.x()+100,self.y()+100,400,200))
self.w.show()
txt1 = str(comboBox1.currentText())
txt2 = str(comboBox2.currentText())
print " reconfiguring to option %s and option %s" %(txt1, txt2)
#
# This is where most of the work is done, and takes about 1/2 an hour for everything to run
# Want to make this method a separate thread/process so the rest of the main GUI is still accessible
# while the program is running as the whole class will be a separate tab in a larger GUI
#
print "all done!"
#//runner
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I feel like I have to use threading and signals to accomplish this, but I am not having much luck. Any help would be greatly appreciated. Thank you!
import sys, time
from PyQt4 import QtGui, QtCore
from PyQt4.Qt import *
class ConfigureThread(QtCore.QThread):
def run(self):
pass
#
# This is where most of the work is done, and takes about 1/2 an hour for everything to run
# Want to make this method a separate thread/process so the rest of the main GUI is still accessible
# while the program is running as the whole class will be a separate tab in a larger GUI
#
#//Popup Class - Will appear when the Switch Button is pressed
class Popup(QWidget):
def __init__(self):
QWidget.__init__(self)
#//nothing here now, it will have a message telling user to wait while program is run
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
grid = QtGui.QGridLayout()
label_header = QtGui.QLabel("TEST RECONFIGURE")
font = label_header.font()
font.setPointSize(24)
label_header.setFont(font)
#//Creating Static Labels that will be placed in the GUI
label_1 = QtGui.QLabel("Menu 1:")
label_2 = QtGui.QLabel("Menu 2:")
label_spacer = QtGui.QLabel("")
label_cfg = QtGui.QLabel("Current Configuration: '/tmp/directory_here' ")
global comboBox1
comboBox1 = QtGui.QComboBox()
comboBox1.addItem("1")
comboBox1.addItem("2")
global comboBox2
comboBox2 = QtGui.QComboBox()
comboBox2.addItem("3")
comboBox2.addItem("4")
#//Switch Button!!!
global switchButton
switchButton = QPushButton("Switch")
switchButton.clicked.connect(self.reconfigure)
quitButton = QtGui.QPushButton('Quit')
quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit)
#//Configure the grid layout
grid.addWidget(label_spacer, 0,0,9,9)
grid.addWidget(label_header, 0,0,1,6)
grid.addWidget(label_1, 1,0,1,1)
grid.addWidget(comboBox1, 2,0,1,1)
grid.addWidget(label_2, 3,0,1,1)
grid.addWidget(comboBox2, 4,0,1,1)
grid.addWidget(switchButton, 5,0,1,2)
grid.addWidget(label_cfg, 6,0,1,9)
grid.addWidget(quitButton, 9,9,1,1)
self.setLayout(grid)
self.setGeometry(640,300,400,600)
self.show()
#//open up the popup window for switch button, and reconfigure
def reconfigure(self):
print("Opening New Window")
self.w = Popup()
self.w.setGeometry(QRect(self.x()+100,self.y()+100,400,200))
self.w.show()
txt1 = str(comboBox1.currentText())
txt2 = str(comboBox2.currentText())
print(" reconfiguring to option %s and option %s" %(txt1, txt2))
self.th = ConfigureThread()
self.th.finished.connect(self.configuring_done)
self.th.start()
def configuring_done(self):
print("all done!")
#//runner
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

wxpython - Menu interaction

I'm having trouble with a menu system - I have a basic example here (below) that shows a basic menu example I've followed; specifically, my issue is how may I make decisions from use menu choices, I'm not sure how to interact user choice with a menu choice?
Could someone point me in the right direction, or ideally give a brief example on this - say input data from a menu and display this?
Thanks
import wx
class myFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id,'Menu', size=(300,200))
panel = wx.Panel(self)
status = self.CreateStatusBar()
menubar = wx.MenuBar()
firstMenu = wx.Menu()
secondMenu = wx.Menu()
# create files
firstMenu.Append(wx.NewId(), 'Save Data' , 'Save data')
firstMenu.Append(wx.NewId(), 'Open Data..', 'Open a new window')
secondMenu.Append(wx.NewId(),'Configure..', 'Input Data here')
# append to menu
menubar.Append(firstMenu, 'File')
menubar.Append(secondMenu,'Options')
#
self.SetMenuBar(menubar)
if( __name__ == '__main__' ):
app = wx.PySimpleApp()
frame = myFrame(parent=None, id=-1)
frame.Show()
app.MainLoop()
You have to bind wx.EVT_MENU event. See wxPython demo for more examples. In your case that would be somethink like:
import wx
SAVE_DATA = wx.NewId()
OPEN_DATA = wx.NewId()
CONFIGURE = wx.NewId()
class myFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id,'Menu', size=(300,200))
panel = wx.Panel(self)
status = self.CreateStatusBar()
menubar = wx.MenuBar()
firstMenu = wx.Menu()
secondMenu = wx.Menu()
# create files
firstMenu.Append(SAVE_DATA, 'Save Data' , 'Save data')
firstMenu.Append(OPEN_DATA, 'Open Data..', 'Open a new window')
secondMenu.Append(CONFIGURE,'Configure..', 'Input Data here')
# append to menu
menubar.Append(firstMenu, 'File')
menubar.Append(secondMenu,'Options')
#
self.SetMenuBar(menubar)
self.Bind(wx.EVT_MENU, self.SaveData, id=SAVE_DATA)
self.Bind(wx.EVT_MENU, self.OpenData, id=OPEN_DATA)
self.Bind(wx.EVT_MENU, self.Configure, id=CONFIGURE)
def SaveData(self, e):
print("Save")
def OpenData(self, e):
print("Open")
def Configure(self, e):
print("Config")
if( __name__ == '__main__' ):
app = wx.PySimpleApp()
frame = myFrame(parent=None, id=-1)
frame.Show()
app.MainLoop()

wxpython run when textctrl changes

I am making a simple text editor in wxpython. I would like it to be able to edit code such as python, and as such I would like to have it highlight the text in a similar manner to IDLE or Notepad++. I know how I would highlight it, but I would like the best way of running it. I don't know if it is possible but what I would really like is to run whenever a key is pressed, and not on a loop checking if it is pressed, so as to save on processing.
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,600))
style = wx.TE_MULTILINE|wx.BORDER_SUNKEN|wx.TE_RICH2
self.status_area = wx.TextCtrl(self, -1,
pos=(10, 270),style=style,
size=(380,150))
self.status_area.AppendText("Type in your wonderfull code here.")
fg = wx.Colour(200,80,100)
at = wx.TextAttr(fg)
self.status_area.SetStyle(3, 5, at)
self.CreateStatusBar() # A Statusbar in the bottom of the window
# Setting up the menu.
filemenu= wx.Menu()
filemenu.Append(wx.ID_ABOUT, "&About","Use to edit python code")
filemenu.AppendSeparator()
filemenu.Append(wx.ID_EXIT,"&Exit"," Terminate the program")
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content.
self.Show(True)
app = wx.App(False)
frame = MainWindow(None, "Python Coder")
app.MainLoop()
If a loop is needed what would be the best way to make it loop, with a while loop, or a
def Loop():
<code>
Loop()
My new code with the added bind:
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,600))
style = wx.TE_MULTILINE|wx.BORDER_SUNKEN|wx.TE_RICH2
self.status_area = wx.TextCtrl(self, -1,
pos=(10, 270),style=style,
size=(380,150))
#settup the syntax highlighting to run on a key press
self.Bind(wx.EVT_CHAR, self.onKeyPress, self.status_area)
self.status_area.AppendText("Type in your wonderfull code here.")
fg = wx.Colour(200,80,100)
at = wx.TextAttr(fg)
self.status_area.SetStyle(3, 5, at)
self.CreateStatusBar() # A Statusbar in the bottom of the window
# Setting up the menu.
filemenu= wx.Menu()
filemenu.Append(wx.ID_ABOUT, "&About","Use to edit python code")
filemenu.AppendSeparator()
filemenu.Append(wx.ID_EXIT,"&Exit"," Terminate the program")
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content.
self.Show(True)
def onKeyPress (self, event):
print "KEY PRESSED"
kc = event.GetKeyCode()
if kc == WXK_SPACE or kc == WXK_RETURN:
Line = self.status_area.GetValue()
print Line
app = wx.App(False)
frame = MainWindow(None, "Python Coder")
app.MainLoop()
In your MainWindow __init__ function add this
self.Bind(wx.EVT_CHAR, self.onKeyPress, self.status_area)
then define onKeyPress in MainWindow
def onKeyPress (self, event):
kc = event.GetKeyCode()
if kc == WXK_SPACE or kc == WXK_RETURN:
#Run your highlighting code here
Come to think of it, this might not be the most efficient way of doing code highlighting. Let me look this up. But in the meantime you can try this.
Edit:
Take a look at this - StyledTextCtrl . I think its more along the lines of what you need.
I solved this when I faced the same issue by creating a custom event.
First, I created a subclass of the TextCtrl, so I had a place in code to raise/post the custom event from:
import wx.lib.newevent
(OnChangeEvent, EVT_VALUE_CHANGED) = wx.lib.newevent.NewEvent()
class TextBox(wx.TextCtrl):
old_value = u''
def __init__(self,*args,**kwargs):
wx.TextCtrl.__init__(self,*args,**kwargs)
self.Bind(wx.EVT_SET_FOCUS, self.gotFocus) # used to set old value
self.Bind(wx.EVT_KILL_FOCUS, self.lostFocus) # used to get new value
def gotFocus(self, evt):
evt.Skip()
self.old_value = self.GetValue()
def lostFocus(self, evt):
evt.Skip()
if self.GetValue() != self.old_value:
evt = OnChangeEvent(oldValue=self.old_value, newValue=self.GetValue())
wx.PostEvent(self, evt)
Now, in my frame's code, here is a snippet of me Binding the event, and using it.
summ_text_ctrl = TextBox(self, -1, size=(400, -1))
summ_text_ctrl.Bind(EVT_VALUE_CHANGED, self.onTextChanged)
def OnTextChanged(self, evt):
evt.Skip()
print('old Value: %s' % evt.oldValue )
print('new Value: %s' % evt.newValue )

Resources