Related
I'm developing a calendar application
The top level window is a frame containing a panel that displays the calendar grid and a panel that contains a "Close" button.
I'm unable to obtain the size of the calendar grid panel.
When I add code to get the panel size, the result is (20,20), which cannot be correct
The screen size is (1920,1080) so I'm expecting something like (1920, 1000)
When I add the wx.lib.inspection module, I see the correct size being displayed. It is (1920, 968)
Can anyone shed some light how to get the correct size of the panel?
This is the code I have so far
import wx
class DrawFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent=None, title='Agenda', style= wx.CAPTION | wx.CLOSE_BOX)
self.drawpanel = DrawPanel(self)
self.buttonpanel = ButtonPanel(self)
self.framesizer = wx.BoxSizer(wx.VERTICAL)
self.framesizer.Add(self.drawpanel,1, flag=wx.EXPAND)
# Add an empty space 10 pixels high above and below the button panel
self.framesizer.Add((0,10),0)
self.framesizer.Add(self.buttonpanel,0, flag=wx.EXPAND)
self.framesizer.Add((0,10),0)
self.SetSizer(self.framesizer)
self.SetInitialSize()
self.Maximize()
self.Show()
def GetPanelSize(self):
return self.drawpanel.GetSize()
def OnClose(self, event):
self.Close()
class DrawPanel(wx.Panel):
# This panel's parent is DrawFrame. DrawFrame is the top level window.
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
self.parent = parent
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.x1, self.y1, self.x2, self.y2 = wx.GetClientDisplayRect()
b = self.x1, self.y1, self.x2, self.y2
print b
self.width, self.height = wx.GetDisplaySize()
c = self.width, self.height
print c
def OnPaint(self, event=None):
dc = wx.PaintDC(self)
dc.Clear()
dc.SetPen(wx.Pen(wx.BLACK, 2))
dc.SetBrush(wx.Brush('WHITE'))
"""
DrawRectangle (self, x, y, width, height)
Draw a rectangle with the given corner coordinate and size.
x and y specify the top left corner coordinates and both width and height are positive.
"""
dc.DrawRectangle(self.x1 + 5, self.y1, self.x2 - 10, self.y2 - 60)
dc.DrawLine(40, 100, 600, 100)
class ButtonPanel(wx.Panel):
# This panel's parent is DrawFrame. DrawFrame is the top level window.
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
self.parent=parent
self.buttonpanelsizer = wx.BoxSizer(wx.HORIZONTAL)
self.closebutton = wx.Button(self, label = 'Close')
self.Bind(wx.EVT_BUTTON, self.OnClose, self.closebutton)
self.buttonpanelsizer.AddStretchSpacer(prop=1)
self.buttonpanelsizer.Add(self.closebutton, 0, wx.ALIGN_CENTER)
self.SetSizer(self.buttonpanelsizer)
def OnClose(self, event):
self.parent.OnClose(event)
app = wx.App(False)
frame = DrawFrame()
print frame.GetPanelSize()
app.MainLoop()
Much appreciated,
Thanks
You are calling the GetPanelSize too early. Keep in mind that wxPython (and pretty much any GUI framework) is event based. That means that for it to work it must keep processing events, which in case of wxPython means that app.MainLoop() must run. So do not call GetPanelSize before calling app.MainLoop(). Instead, call it when you need it. Do you need it when you paint something? Just use dc.GetSize(). Do you need it elsewhere? Process the wx.EVT_SIZE event and store the current size. Possibly you will have to trigger some action in the EVT_SIZE handler.
In this code below, I made a simple route guidance by opening defined windows each time I press a specific button e.g.) Mainmenu -> Classroom Floor -> Classroom No.
I'm trying to make a 'Home' button that will close all open windows except for the first Main Menu window with WELCOME written.
For example, after I press btn7, btn702, then I have 4 windows open. I would like to add a 'Home' button that will close 3 newly open windows and leave the first window alive. How might I make this kind of button?
from os import system
from tkinter import *
from PIL import ImageTk, Image
mainmenu = Tk()
mainmenu.title("CAU 310 GUIDE MAP")
mainmenu.geometry("1280x800+0+0")
canvas = Canvas(mainmenu, width = 1280, height = 800)
canvas.pack(fill='both', expand = True)
canvas.create_text(640, 250, text = 'WELCOME', font=times 45)
btnclassroom = Button(mainmenu, padx=5, pady=5,text="Classroom", font="times 30", command=selectfloor)
btnclassroom.place(x=140, y=570)
def selectfloor():
mainmenu = Tk()
mainmenu.title("DESTINATION")
mainmenu.geometry("1280x800+0+0")
mainmenu.config(bg='white')
canvas = Canvas(mainmenu, width = 1280, height = 800)
canvas.pack(fill='both', expand = True)
canvas.create_text(640, 150, text = 'Select floor of classroom', font='Arial 40')
btn7=Button(mainmenu, padx=4, pady=4, text="7F", font="Arial 42 bold", command=floor7)
btn7.place(x=160-5, y=490)
def floor7():
mainmenu = Tk()
mainmenu.title("FLOOR 7")
mainmenu.geometry("1280x800+0+0")
canvas = Canvas(mainmenu, width = 1280, height = 800)
canvas.pack(fill='both', expand = True)
canvas.create_text(640, 150, text = 'Select classroom No.', font='Arial 40')
btn702=Button(mainmenu, padx=3, pady=3, text="No.702", font="Arial 38 bold", command=room702)
btn702.place(x=100+220*1, y=240)
mainmenu.mainloop()
def room702():
mainmenu = Tk()
mainmenu.title("Elevator 1")
mainmenu.geometry("1280x800+0+0")
lobby = ImageTk.PhotoImage(Image.open("1F_elevator1.jpg"), master=mainmenu)
canvas = Canvas(mainmenu, width = 1280, height = 800)
canvas.pack(fill='both', expand = True)
canvas.create_image(0, 0, image=lobby,anchor = "nw")
mainmenu.mainloop()
mainmenu.mainloop()
**I've been googling around for this kind of matter, and I think I've got a hint of making the 'selectfloor', 'floor7', 'room702' window as a children widget. But I'm still not sure how to make it happen.
You can store the windows in a list, and then iterate over the list to destroy them.
Here's a simplified example. This example uses Toplevel windows rather than instances of Tk. It's not clear why you're using multiple instances of Tk, but in general you should only ever have one. This same technique works with any sort of widgets, however.
import tkinter as tk
windows = []
def delete_all_but_first():
for window in windows[1:]:
window.destroy()
def new_window():
window = tk.Toplevel(root)
windows.append(window)
window.title(f"Window #{len(windows)}")
root = tk.Tk()
windows.append(root)
del_all = tk.Button(root, text="Delete all", command=delete_all_but_first)
new = tk.Button(root, text="New window", command=new_window)
new.pack(side="top", padx=20, pady=20)
del_all.pack(side="top", padx=20, pady=20)
root.mainloop()
There's an optimization you can make if you use Toplevel instead of Tk, and if you always make the windows direct children of the root window. In that case you can just iterate over all children of root, and delete any window that is a top-level.
In this case, you don't need to maintain the windows list.
def delete_toplevels():
for child in root.winfo_children():
if child.winfo_class() == "Toplevel":
child.destroy()
def new_window():
window = tk.Toplevel(root)
window.title(f"Window #{len(windows)}")
...
del_all = tk.Button(root, text="Delete all", command=delete_toplevels)
Ok, so I've been tasked with creating a VERY simple GUI at work (I'm an intern). The task is to eventually connect to a machine and process real data, but right now I'm working on randomly generated sine data with noise. I've chose to work in Python 3.0, and use wxpython to create my GUI components.
As I want everything to appear on the same window, I'm using panels (hence wx.lib.plot.PlotCanvas rather than something like matplotlib.pyplot)
The problem that I have is that over time, the plot seems to 'expand' off of the panel. This is temporarily solved when I manually resize the window, but resumes again immediately after (you need to run the code to see what I mean).
Expansion over time in panel
Another problem (that has bugged me since I have started writing the code) is that sometimes when I resize the window (manually) or minimize it and then maximize it again, the timer randomly starts and stops.
I have tried all sorts of things (changing padding in sizers, extra arguments, changing time between refreshes GetBestSize()) but I believe that I simply don't understand wxpython well enough to identify where the problem is
I would really appreciate any help you can shed on either of these problems (I don't know, they might even be linked to each other).
FYI: I am not an experienced coder, and my code is not finished (I have more functions to code, but I feel like I should resolve this first). I have constructed this code by looking at different techniques from various tutorials and websites like stackoverflow, so I know it's not formatted well and could definitely be made more efficient. Also, I have removed some parts just to be safe about confidentiality - nothing important, just strings in messages.
PS: If you do have an easier way to do the whole plot/update thing that doesn't have this problem (preferably still in wx) I would be thrilled to hear that as well
And here's my code:
EDIT: Solved the expanding problem by using self.p2.SetSize((W+0,L+0)) instead of (self.p2.GetBestSize())
EDIT: Made transitions much smoother by just regenerating data and redrawing it on existing canvas in the evt_timer function (instead of recreating the whole canvas, which gave a blink-y appearance if you know what I mean)
import wx
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import leastsq
import wx.lib.plot as plot
import time
import os
wildcard = "Text File (*.txt)|*.txt|"\
"Picture (*.png)|*.png|"\
"All files (*.*)|*.*"#This wildcard shows the options for file endings in the "SAVE" tab - see OnSave(self,event)
wildcard2 = "Picture (*.png)|*.png|"\
"Text File (*.txt)|*.txt|"\
"All files (*.*)|*.*"
class PlotCanvas(plot.PlotCanvas):
def __init__(self,parent,id,size,accepted):
"""
This randomly generates sine data (with noise) and plots it to a panel.
Incorporated as a separate class instead of instatiating it as a plot.PlotCanvas object
to overcome an issue of the size of the plot in the panel.
"""
plot.PlotCanvas.__init__(self,parent,id,style=wx.BORDER_SUNKEN,size = size)
N = 100 # number of data points
self.t = np.linspace(0, 4*np.pi, N)
f = 1.15247 # Optional!! Advised not to use
self.data = 3.0*np.sin(f*self.t+0.001) + 0.5 + np.random.randn(N) # create artificial data with noise
guess_mean = np.mean(self.data)
guess_phase = 0
guess_freq = 1
guess_amp = 1
optimize_func = lambda x: x[0]*np.sin(x[1]*self.t+x[2]) + x[3] - self.data
est_amp, est_freq, est_phase, est_mean = leastsq(optimize_func, [guess_amp, guess_freq, guess_phase, guess_mean])[0]
fine_t = np.arange(0,max(self.t),0.1)
data_fit=est_amp*np.sin(est_freq*fine_t+est_phase)+est_mean
multiplier = 1
dataset1 = [(x,[d for d in self.data][[td for td in self.t].index(x)])for x in [td for td in self.t]]
fitdata1 = [(x,[df for df in data_fit][[tf for tf in fine_t].index(x)]) for x in [tf for tf in fine_t]]
dataset =[(x,y*multiplier) for (x,y) in dataset1]
fitdata = [(x,y*multiplier) for (x,y) in fitdata1]
self.data = dataset
self.data2 = fitdata
line = plot.PolyLine(self.data,legend = 'random',colour = 'light blue', width =2)
line2 = plot.PolyLine(self.data2,legend = 'sineline',colour ='black',width =2)
a = []
if "D" in accepted:
a.append(line)
if "S" in accepted:
a.append(line2)
if "G" in accepted:
pass
if "L" in accepted:
pass
gc = plot.PlotGraphics(a,'Line Graph','X','Y')
xmin = self.t[0]-0.01*(self.t[-1]-self.t[0])
xmax = self.t[-1]+0.01*(self.t[-1]-self.t[0])
self.Draw(gc,xAxis=(xmin,xmax),yAxis=(min([x[1] for x in dataset])-0.01*(max([x[1] for x in dataset])-min([x[1] for x in dataset])),
max([x[1] for x in dataset])+0.01*(max([x[1] for x in dataset])-min([x[1] for x in dataset]))))
#self.showLegend = True
#self.enableZoom = True
def Dialog(self, parent, message, c):# Will be used to notify the user of errors/processes
if c == "W":
caption = "Warning!"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
elif c == "I":
caption = "Information"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()#Destroys dialog on close
class Frame(wx.Frame):
"""
This is the main class. In it, we declare the separate panels, canvas, menubar, buttons and sizers.
"""
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition)
self.CurrentDirectory = os.getcwd()
self.timer=wx.Timer(self)#Instantiating the timer
self.count=0
self.Bind(wx.EVT_TIMER,self.evt_timer)#Binding it to itself so that it is always triggered
self.Bind(wx.EVT_PAINT,self.paint)
menubar = wx.MenuBar()
fileMenu = wx.Menu() #Creating the Menubar at the top
#Creating 3 menus: fileMenu,fit,and help
save = wx.Menu()
z = wx.MenuItem(save,wx.ID_ANY,'Save Raw Data\tCtrl+D')
self.Bind(wx.EVT_MENU,self.OnSave,z)
save.Append(z)
z= wx.MenuItem(save,wx.ID_ANY,'Save Image\tCtrl+I')
self.Bind(wx.EVT_MENU,self.OnSaveImage,z)
save.Append(z)
fileMenu.AppendSubMenu(save,'&Save')
fileMenu.AppendSeparator()
z = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit\tCtrl+W')
self.Bind(wx.EVT_MENU, self.OnQuit, z)
fileMenu.Append(z)
fit = wx.Menu()#Making a check menu
self.gaussian = fit.Append(wx.ID_ANY,'Gaussian',kind = wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleGaussian,self.gaussian)
fit.Check(self.gaussian.GetId(),False)
self.sine = fit.Append(wx.ID_ANY,'Sine',kind = wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleSine,self.sine)
fit.Check(self.sine.GetId(),False)
self.linear = fit.Append(wx.ID_ANY,'Linear',kind=wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleLinear,self.linear)
fit.Check(self.linear.GetId(),False)
help = wx.Menu()
z = wx.MenuItem(help,wx.ID_ANY,'&About\tCtrl+H')
self.Bind(wx.EVT_MENU,self.OnHelp,z)
help.Append(z)
menubar.Append(fileMenu, '&File')
menubar.Append(fit, '&Fit')
menubar.Append(help, '&Help')#adding menus to menubar
self.SetMenuBar(menubar)#formatting the frame with menubar
self.sp = wx.SplitterWindow(self)#Splitting the window into 2 panels
self.p1 = wx.Panel(self.sp,style = wx.SUNKEN_BORDER)#For buttons and user events
self.p2 = wx.Panel(self.sp,style = wx.SUNKEN_BORDER)#For display of the plot
self.sp.SplitVertically(self.p1,self.p2,300)
sizer = wx.GridBagSizer(3, 3)#Versatile sizer for layout of first panel self.p1
bitmappath = self.CurrentDirectory + "\\BITMAPS"
bmp = wx.Bitmap(bitmappath+"\\SAVE.BMP",wx.BITMAP_TYPE_BMP)
self.saveBtn = wx.BitmapButton(self.p1,wx.ID_ANY,bitmap = bmp,size =(bmp.GetWidth()+10,bmp.GetHeight()+10))
self.Bind(wx.EVT_BUTTON,self.OnSave,self.saveBtn)
sizer.Add(self.saveBtn, (0, 0), wx.DefaultSpan, wx.ALL,5)
bmp = wx.Bitmap(bitmappath +"\\START.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn = wx.BitmapButton(self.p1,-1,bitmap = bmp,size =(bmp.GetWidth()+10,bmp.GetHeight()+10))# A button that starts and stops the plotting
self.startBtn.startval = "START"
self.Bind(wx.EVT_BUTTON,self.paint,self.startBtn)
sizer.Add(self.startBtn, (0, 1), wx.DefaultSpan,wx.ALL,5)
sizer1 = wx.BoxSizer(wx.VERTICAL)
W,L = self.p2.GetSize()
self.p2.canvas = PlotCanvas(self.p2,wx.ID_ANY,(W,L),["D"])
sizer1.Add(self.p2.canvas,1,wx.ALL,0,0)
self.p2.SetSizerAndFit(sizer1)
self.p1.SetSizerAndFit(sizer)
self.p2.SetSizerAndFit(sizer1)
self.p2.SetSize(W,L)
self.Maximize(True)
self.Centre()
self.Show()
############### event methods ###########
def paint(self,event):
"""
Updates the canvas based on the value of the startbtn(not the image). Bound to self.timer.
"""
bitmappath = self.CurrentDirectory + "\\BITMAPS"
if self.startBtn.startval == "START":
self.timer.Start(1)# increase the value for more time
bmp = wx.Bitmap(bitmappath + "\\STOP.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn.SetBitmap(bmp)
self.startBtn.startval = "STOP"
elif self.startBtn.startval == "STOP":
self.timer.Stop()
bmp = wx.Bitmap(bitmappath+ "\\START.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn.SetBitmap(bmp)
self.startBtn.startval = "START"
def evt_timer(self,event):
self.count +=1
if self.count== 10:# By increasing count (or the number in self.timer.Start()) you can increase the interval between updates
#self.p2.canvas.Clear()
sizer1 = wx.BoxSizer(wx.VERTICAL)
W,L = self.p2.GetSize()
a = ["D"]
if self.sine.IsChecked():
a.append("S")
elif self.linear.IsChecked():
a.append("L")
elif self.gaussian.IsChecked():
a.append("G")
self.p2.canvas = PlotCanvas(self.p2,wx.ID_ANY,(W,L),a)
sizer1.Add(self.p2.canvas,1,wx.ALL,0,0)
self.p2.SetSizerAndFit(sizer1)
self.p2.SetSize(self.p2.GetBestSize())
self.count=0 # reset the count
def Dialog(self, parent, message, c):# Will be used to notify the user of errors/processes
if c == "W":
caption = "Warning!"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
elif c == "I":
caption = "Information"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()#Destroys dialog on close
def OnSave(self,event):#Triggered by menubar and button
try:
rawdata = self.p2.canvas.data
raw_X =[x[0] for x in rawdata]
raw_Y =[x[1] for x in rawdata]
dlg = wx.FileDialog(#Code for this from http://www.blog.pythonlibrary.org
self, message="Save file as ...",
defaultDir=self.CurrentDirectory,
defaultFile=str(time.ctime()), wildcard=wildcard, style=wx.FD_SAVE
)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
f = open(path+".txt","w+")
for i in range(len(raw_X)):
f.write(str(raw_X[i])+"\t"+str(raw_Y[i])+"\n")
f.close()
self.Dialog(None,"File successfully saved","I")
except UnboundLocalError:#Catch error when user closes save window without selecting any directory or filename
pass
def OnSaveImage(self,event):
try:
rawdata = self.p2.canvas.data
raw_X = [x[0] for x in rawdata]
raw_Y = [x[1] for x in rawdata]
dlg = wx.FileDialog(
self, message="Save file as ...",
defaultDir=self.CurrentDirectory,
defaultFile=str(time.ctime()), wildcard=wildcard2, style=wx.FD_SAVE
)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
fig1 = plt.figure()
plt.plot(raw_X,raw_Y)
plt.title("Raw Data")
fig1.savefig(path+".png")
self.Dialog(None,"File successfully saved","I")
except UnboundLocalError:
pass
def OnMultiply(self,e):
try:
factor = self.x.GetValue()
factor = float(factor)
self.IntegrationTime = factor
except ValueError as e:
self.Dialog(None,str(e),"W")
def OnQuit(self, e):
self.Close()
def OnHelp(self,e):
self.Dialog(None,"N/A","I")
def ToggleSine(self,e):
pass
def ToggleLinear(self,e):
self.Dialog(None,"Not added yet","W")
def ToggleGaussian(self,e):
self.Dialog(None,"Not added yet","W")
if __name__ =="__main__":
app=wx.App()
Frame(None,-1,"N/A")
app.MainLoop()
I'm trying to teach myself the tkinter module by programming minesweeper. I have created a grid with buttons and a method to set an image flag to cells. It works, in that when you press the right mouse button the image of the button changes as desired, but when you right click on the next button the image just moves to the next button, rather than creating a second flag. I want to be able to place a new flag image on each cell that I right click, rather than just shuffle the image around. Here's my code:
import tkinter as Tk
def main():
root = Tk.Tk()
root.geometry('{}x{}'.format(700, 700))
instance = Minesweeper(root, 10, 10)
root.mainloop()
class Minesweeper:
def __init__(self, parent, height, width):
self.top_frame = Tk.Frame(parent)
self.top_frame.place(anchor=Tk.CENTER, relx=0.5, rely=0.5)
self.frames = []
self.buttons = []
index = 0
for x in range(height):
for y in range(width):
self.frames.append(Tk.Frame(self.top_frame, height=50, width=50))
self.buttons.append(Tk.Button(self.frames[index], bg="white"))
self.frames[index].grid_propagate(False)
self.frames[index].columnconfigure(0, weight=1)
self.frames[index].rowconfigure(0, weight=1)
self.frames[index].grid(row=x, column=y)
self.buttons[index].grid(sticky="wens")
self.buttons[index].bind('<Button-3>', self.flag)
index += 1
def flag(self, event):
self.flag = Tk.PhotoImage(file="flag.png")
event.widget.configure(image=self.flag)
if __name__ == "__main__":
main()
Seems that the below fixed it:
def flag(self, event):
self.flag = Tk.PhotoImage(file="flag.png")
event.widget.image = self.flag # <---- this seemed to fix it
event.widget.configure(image=self.flag)
This code worked fine on wxPython 2.8, following an upgrade today to 2.9 however the toolbar doesn't
display at all. If I remove the self.SetToolBar() call the icon does show up but not as a button, and the toolbar formatting doesn't stretch when the screen is re-sized. Any ideas?
import wx
class MyApp(wx.App):
def OnInit(self):
self.frame = Example(None, title="Word Bag", size=(400,100))
self.SetTopWindow(self.frame)
self.frame.Show()
return True
class MyToolbar(wx.ToolBar):
"""Toolbars are attached to frames, so need TBar = Toolbar(self) in frame init"""
def __init__(self, parent):
wx.ToolBar.__init__(self, parent)
# set my preferred default size for icons
self.SetToolBitmapSize((32,32))
# the main bit where icons are formatted, added, and bound to handlers
self.initialiseIcons()
# Need to call realise before exiting
self.Realize()
def initialiseIcons(self):
"""Iterate over icons and add them to toolbar"""
for each in self.toolbarData():
self.createSimpleTool(*each)
def createSimpleTool(self, label, filename, statbar, handler):
"""Adds icons to bar using AddSimpleTool"""
if not label:
self.AddSeparator()
return
bmp = wx.Image(filename, wx.BITMAP_TYPE_PNG).ConvertToBitmap()
tool = self.AddSimpleTool(-1, bmp, label, statbar)
self.Bind(wx.EVT_MENU, handler, tool)
def toolbarData(self):
"""Put your icon data here in the following format...
[0] = tooltip label, [1] = bitmap path, [2] = status bar label, [3] = bound function"""
return [["Add new word","/Users/paulpatterson/Desktop/add.png","Add a new word to the dictionary",self.OnAddWord]]
# toolbar icon handlers here...
def OnAddWord(self, event):
pass
def OnRemoveWord(self, event):
pass
def OnSearchWord(self, event):
pass
class Example(wx.Frame):
def __init__(self, parent, title, size):
super(Example, self).__init__(parent, title=title, size=size)
# Create and set the toolbar
tBar = MyToolbar(self)
self.SetToolBar(tBar)
self.frameSizer = wx.BoxSizer(wx.VERTICAL)
self.panelOne = MyPanel(self)
self.frameSizer.Add(self.panelOne, 1, wx.EXPAND)
self.SetSizer(self.frameSizer)
#self.frameSizer.Fit(self)
self.Centre()
self.Show()
class MyPanel(wx.Panel):
def __init__(self, parent):
super(MyPanel, self).__init__(parent)
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
### widgets here
# set optimum layout for mainsizer...
self.SetSizer(self.mainSizer)
# ...then fit main sizer to the panel.
self.mainSizer.Fit(self)
if __name__ == '__main__':
app = MyApp(False)
app.MainLoop()
I had the same problem just hours ago. I couldn't get a custom created toolbar to work, but using Frame.CreateToolBar() works as expected. Example:
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(
self, parent=None, id=-1, title='Test window',
size=wx.Size(800, 600)
)
self.setup_toolbar()
def setup_toolbar(self):
# First create the toolbar.
self.toolbar = self.CreateToolBar(wx.TB_FLAT | wx.TB_TEXT)
self.Bind(wx.EVT_TOOL, self.on_toolbar)
# Add a 'Clear all' button.
self.toolbar.AddLabelTool(
wx.ID_NEW, 'Clear all', get_toolbar_art('new_big'),
shortHelp='Remove all the contents from the text inputs.'
)
# Add an 'Open' button.
self.toolbar.AddLabelTool(
wx.ID_OPEN, 'From file...', get_toolbar_art('open_big'),
shortHelp='Fill the input box with the ' +
'contents of a Linjekort text file.'
)
# self.toolbar.AddSeparator() # A separator.
# Add a 'Save all' button.
self.toolbar.AddLabelTool(
wx.ID_SAVE, 'Save results to...', get_toolbar_art('save_big'),
shortHelp='Save all the Ozi files to a directory.'
)
self.toolbar.Realize()
def get_toolbar_art(name):
return wx.Bitmap('icons/{}.png'.format(name))
But this doesn't answer how to get a custom toolbar subclass to work. Have you tried to just add the toolbar to your layout using a sizer, not using the SetToolBar function? That's the only way I know of to avoid ending up with the OSX native Frame toolbar. Here's an example of that done:
def create_output_panel(self, parent, slug, label):
panel = wx.Panel(parent, style=wx.BORDER_THEME)
panel.SetBackgroundColour(
wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW)
)
# Toolbar.
toolbar = wx.ToolBar(panel, -1, style=wx.BORDER_RAISED)
toolbar.AddSimpleTool(
wx.ID_SAVE, get_toolbar_art('save'),
shortHelpString='Save to file...'
)
toolbar.Realize()
toolbar.Bind(wx.EVT_TOOL, lambda evt: print('Success'))
# Text control.
textctrl = wx.TextCtrl(
panel, -1, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.BORDER_NONE
)
# Organize controls.
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(
wx.StaticText(panel, -1, label), proportion=0, border=5, flag=wx.ALL
)
sizer.Add(toolbar, proportion=0, flag=wx.EXPAND)
sizer.Add(textctrl, proportion=1, flag=wx.EXPAND)
panel.SetSizer(sizer)
return panel
And a screen shot:
I hope this helps!