How to switch panels in python using wxPython - python

I'm trying to create an App which allows the user to switch the information show using buttons. The basic idea of the code is that the user sees buttons on the left side of the screen and when the user presses "button 1", the code shows Panel1. I've made 2 buttons so far and the code for 2 panels is written as well but i can't figure out how to update my MainFrame so it show a different panel when one of the buttons is pressed.
Code:
import wx
TabNumber = 1
class ButtonPanel(wx.Panel):
def __init__(self, parent):
global TabNumber
super(ButtonPanel, self).__init__(parent, -1)
self.Tab1Button = wx.Button(self, label="TAB 1")
self.Tab1Button.Bind(wx.EVT_BUTTON, self.SwitchTab(1))
self.Tab2Button = wx.Button(self, label="TAB 2")
self.Tab2Button.Bind(wx.EVT_BUTTON, self.SwitchTab(2))
self.Sizer = wx.BoxSizer(wx.VERTICAL)
self.Sizer.Add(self.Tab1Button, wx.CENTER,0)
self.Sizer.Add(self.Tab2Button, wx.CENTER, 0)
self.SetSizer(self.Sizer)
def SwitchTab(self, tab):
def OnClick(event):
print(f"Switch to tab {tab} started")
TabNumber = tab
print(TabNumber)
return OnClick
class Panel1(wx.Panel):
def __init__(self, parent):
super(Panel1, self).__init__(parent, -1)
self.panel = wx.Panel(self)
self.text = wx.StaticText(self.panel, label="1")
class Panel2(wx.Panel):
def __init__(self, parent):
super(Panel2, self).__init__(parent, -1)
self.panel = wx.Panel(self)
self.text = wx.StaticText(self.panel, label="2")
class MainFrame(wx.Frame):
def __init__(self):
super(MainFrame, self).__init__(None, -1, "Test Application")
self.Panels = {
"Panel1": Panel1(self),
"Panel2": Panel2(self)
}
self.MySizer = wx.BoxSizer(wx.HORIZONTAL)
self.tabpanel = ButtonPanel(self)
self.MySizer.Add(self.tabpanel,wx.CENTER,0)
self.InfoPanel = self.Panels["Panel"+str(TabNumber)]
self.MySizer.Add(self.InfoPanel, wx.CENTER,0)
self.SetSizer(self.MySizer)
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
I was also wondering how I can adjust the ratio for the space that is given to my ButtonPanel and my InfoPanel.

As far as I can see, you are trying to do something that works like a Wizard... On the one hand, you can use wx.adv.Wizard. On the other hand, you can look at this tutorial that does something very similar and adapt it to what you need:
WXPython: How to create a generic wizard
Good luck!

Related

wxPython append and pop message box inside panel serving like a notification center

I would like to make a wxpython program that has a notification center just like the one on windows or mac. Whenever I have a message, the message will show inside the the notification panel, and the user could close that message afterwards.
I have a sample code for illustration as follows:
import wx
import wx.lib.scrolledpanel as scrolled
class MyFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title)
topPanel = wx.Panel(self)
panel1 = wx.Panel(topPanel, -1)
button1 = wx.Button(panel1, -1, label="generate message")
self.panel2 = scrolled.ScrolledPanel(
topPanel, -1, style=wx.SIMPLE_BORDER)
self.panel2.SetAutoLayout(1)
self.panel2.SetupScrolling()
button1.Bind(wx.EVT_BUTTON, self.onAdd)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(panel1,-1,wx.EXPAND|wx.ALL,border=10)
sizer.Add(self.panel2,-1,wx.EXPAND|wx.ALL,border=10)
self.sizer2 = wx.BoxSizer(wx.VERTICAL)
topPanel.SetSizer(sizer)
self.panel2.SetSizer(self.sizer2)
def onAdd(self, event):
new_text = wx.TextCtrl(self.panel2, value="New Message")
self.sizer2.Add(new_text,0,wx.EXPAND|wx.ALL,border=1)
self.panel2.Layout()
self.panel2.SetupScrolling()
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, 'frame')
frame.Show(True)
return True
app = MyApp(0)
app.MainLoop()
In the above I code, the right panel (i.e. panel2) serves as a notification center that all the messages should shown inside it. On the left panel (i.e. panel1) I have a button to generate message just to mimic the notification behavior. Ideally the message on the right panel should be a message box that you could close (maybe a frame? Or a MessageDialog?)
Any hint or advice is much appreciated, and an example would be the best!
Thanks!
Finally figured out myself, it was easier than I initially thought.
Here is the code:
import wx
import wx.lib.scrolledpanel as scrolled
class MyFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title)
self.number_of_panels = 0
topPanel = wx.Panel(self)
panel1 = wx.Panel(topPanel, -1)
button1 = wx.Button(panel1, -1, label="generate message")
self.panel2 = scrolled.ScrolledPanel(
topPanel, -1, style=wx.SIMPLE_BORDER)
self.panel2.SetAutoLayout(1)
self.panel2.SetupScrolling()
button1.Bind(wx.EVT_BUTTON, self.onAdd)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(panel1,0,wx.EXPAND|wx.ALL,border=5)
sizer.Add(self.panel2,1,wx.EXPAND|wx.ALL,border=5)
self.sizer2 = wx.BoxSizer(wx.VERTICAL)
topPanel.SetSizer(sizer)
self.panel2.SetSizer(self.sizer2)
def onAdd(self, event):
self.number_of_panels += 1
panel_label = "Panel %s" % self.number_of_panels
panel_name = "panel%s" % self.number_of_panels
new_panel = wx.Panel(self.panel2, name=panel_name, style=wx.SIMPLE_BORDER)
self.closeButton = wx.Button(new_panel, label='Close %s' % self.number_of_panels)
self.closeButton.panel_number = self.number_of_panels
self.closeButton.Bind(wx.EVT_BUTTON, self.OnClose)
self.sizer2.Add(new_panel,0,wx.EXPAND|wx.ALL,border=1)
self.panel2.Layout()
self.panel2.SetupScrolling()
def OnClose(self, e):
if self.panel2.GetChildren():
e.GetEventObject().GetParent().Destroy()
self.number_of_panels -= 1
self.panel2.Layout() # Reset layout after destroy the panel
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, 'frame')
frame.Show(True)
return True
app = MyApp(0)
app.MainLoop()
Basically I can destroy the newly created panel. I just need to know which panel it is when I click the close button. This should work very similar to the Notification Center.

Automate Tabbing though tabs in WXnotebook

I have a WXnotebook that has different number of tabs depending on the amount of information that the program pulls. My goal is to take a screenshot of the information displayed by each tab and store those images. Im having a problem with the program going through the tabs. I was thinking maybe something like
for i in range(numOfTabs):
self.waferTab.ChangeSelection(i)
time.sleep(3)
but this only shows me the last tab in the wxnotebook. If anybody knows anyway of getting this i'd really appreciate it.
EDIT
so I tried the following like suggested below but the GUI shows up but when it shows up It looks like it already iterated through the whole loop and displays the selection is the last tab I still cant see the screen actually going through the tabs
for i in range(numOfTabs):
self.waferTab.SetSelection(i)
Refresh
wx.SafeYield()
time.sleep(10)
I don't know why you would want to do this as it seems like a confusing interface for a user to use, but here's an example using a wx.Timer:
import random
import wx
class TabPanel(wx.Panel):
def __init__(self, parent):
""""""
wx.Panel.__init__(self, parent=parent)
colors = ["red", "blue", "gray", "yellow", "green"]
self.SetBackgroundColour(random.choice(colors))
btn = wx.Button(self, label="Press Me")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(btn, 0, wx.ALL, 10)
self.SetSizer(sizer)
class DemoFrame(wx.Frame):
"""
Frame that holds all other widgets
"""
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, wx.ID_ANY,
"Notebook Tutorial",
size=(600,400)
)
panel = wx.Panel(self)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.change_tabs, self.timer)
self.timer.Start(1000)
self.notebook = wx.Notebook(panel)
tabOne = TabPanel(self.notebook)
self.notebook.AddPage(tabOne, "Tab 1")
tabTwo = TabPanel(self.notebook)
self.notebook.AddPage(tabTwo, "Tab 2")
tabThree = TabPanel(self.notebook)
self.notebook.AddPage(tabThree, 'Tab 3')
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.notebook, 1, wx.ALL|wx.EXPAND, 5)
panel.SetSizer(sizer)
self.Layout()
self.Show()
def change_tabs(self, event):
current_selection = self.notebook.GetSelection()
print(current_selection)
pages = self.notebook.GetPageCount()
if current_selection + 1 == pages:
self.notebook.ChangeSelection(0)
else:
self.notebook.ChangeSelection(current_selection + 1)
if __name__ == "__main__":
app = wx.App(True)
frame = DemoFrame()
app.MainLoop()
You could also use a Thread and use something like wx.CallAfter to update your UI, but I think a timer makes more sense in this case.

wxpython set sizer background

i am using wxpython and trying to make an background to a sizer without any success, i searched in google without any results.
i try it with this boxsizer
wx.BoxSizer(wx.HORIZONTAL)
I just use panels for this sort of thing. You can set the color of the panel several different ways: you can use a named color, a wx.Color object, a predefined wx.Color object like wx.RED or a tuple of 3 integers.
Here's a simple example:
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.SetBackgroundColour('white')
main_sizer = wx.BoxSizer(wx.VERTICAL)
for number in range(5):
btn = wx.Button(self, label='Button {}'.format(number))
main_sizer.Add(btn, 0, wx.ALL, 5)
self.SetSizer(main_sizer)
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Background colors')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
I wrote a little about this topic here:
https://www.blog.pythonlibrary.org/2009/09/03/wxpython-resetting-the-background-color/
You might also the wxPython wiki helpful:
https://wiki.wxpython.org/GettingStarted

Buttons on a gradient bar to access different panels

The following code makes a window with a grey gradient bar.
import wx
class GradientFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, lambda event: None)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Center()
self.Show()
def OnSize(self, event):
event.Skip()
self.Refresh()
def OnPaint(self, event):
dc = wx.BufferedPaintDC(self)
rect = self.GetClientRect()
dc.SetBackground(wx.Brush("white"))
dc.Clear()
rect.SetHeight(30)
dc.GradientFillLinear(rect, '#fbfbfb', '#efefef', wx.SOUTH)
rect.SetTop(30)
rect.SetHeight(2)
dc.GradientFillLinear(rect, '#dbdbdb', '#c1c1c1', wx.SOUTH)
app = wx.App(0)
frame = GradientFrame(None, 'Test')
app.MainLoop()
I would like to add toogle buttons like the following screenshot, that allows to access to different pages / panels of the GUI (each of them containing their own widgets, etc.)
What is the good framework for that : should these buttons be created manually in OnPaint (this would be very 'low-level') or somewhere else? Is there a ready-to-use way to use buttons linked to different pages ?
There is no good framework for creating custom widgets. However, there are some good recipes out there:
https://stackoverflow.com/questions/1351448/how-to-make-custom-buttons-in-wx
http://wiki.wxpython.org/CreatingCustomControls
Those two links should get you started. You can also take a look at the source for GenericButtons, AquaButton or PlateButton for additional ideas.
Alternatively, you could also just create a panel that's a specific size and put some of the custom buttons or just regular buttons on it instead.
Here's an example of how to use PlateButtons that should get you started:
import wx
import wx.lib.platebtn as platebtn
class GradientFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title)
self.panel = wx.Panel(self)
self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
self.panel.Bind(wx.EVT_ERASE_BACKGROUND, lambda event: None)
self.panel.Bind(wx.EVT_SIZE, self.OnSize)
# add plate buttons
top_sizer = wx.BoxSizer(wx.VERTICAL)
btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
labels = ["Morceaux", "Albums", "Artistes", "Genres"]
style = platebtn.PB_STYLE_GRADIENT
for label in labels:
btn = platebtn.PlateButton(self.panel, label=label, style=style)
btn.SetPressColor(wx.Colour(208,208,208))
btn_sizer.Add(btn, 0, wx.RIGHT|wx.LEFT|wx.CENTER, 5)
top_sizer.Add(btn_sizer, 0, wx.ALL|wx.CENTER, 5)
top_sizer.Add((1,1), 1, wx.EXPAND)
self.panel.SetSizer(top_sizer)
self.Center()
self.Show()
def OnSize(self, event):
event.Skip()
self.Refresh()
def OnPaint(self, event):
dc = wx.BufferedPaintDC(self.panel)
rect = self.panel.GetClientRect()
dc.SetBackground(wx.Brush("white"))
dc.Clear()
rect.SetHeight(30)
dc.GradientFillLinear(rect, '#fbfbfb', '#efefef', wx.SOUTH)
rect.SetTop(30)
rect.SetHeight(2)
dc.GradientFillLinear(rect, '#dbdbdb', '#c1c1c1', wx.SOUTH)
app = wx.App(0)
frame = GradientFrame(None, 'Test')
app.MainLoop()

Embed Separate WX GUIs into Notebook Tabs

I basically have 3 separate wxPython GUIs that I would like to combine into a single application, giving each GUI its own notebook tab. I'm not really sure how to do this though and am looking for some advice. I appreciate any comments or thoughts I can get.
My idea is that it should look something like this:
import wx
import OtherFrame
class PageOne(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
t = wx.StaticText(self, -1, "This is a PageOne object", (20,20))
panel=OtherFrame.Panel(parent)
box = wx.BoxSizer(wx.VERTICAL)
panel.SetSizer(self,box)
panel.Layout(self, parent)
class PageTwo(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
t = wx.StaticText(self, -1, "This is a PageTwo object", (40,40))
class PageThree(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
t = wx.StaticText(self, -1, "This is a PageThree object", (60,60))
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Simple Notebook Example")
p = wx.Panel(self)
nb = wx.Notebook(p)
page1 = PageOne(nb)
page2 = PageTwo(nb)
page3 = PageThree(nb)
nb.AddPage(page1, "Page 1")
nb.AddPage(page2, "Page 2")
nb.AddPage(page3, "Page 3")
sizer = wx.BoxSizer()
sizer.Add(nb, 1, wx.EXPAND)
p.SetSizer(sizer)
if __name__ == "__main__":
app = wx.App()
MainFrame().Show()
app.MainLoop()
But this gives a few errors and crashes. Without the sizers under PageOne, it just puts a gray square in the top right corner, with them, it crashes.
Thanks in advance!
I assume each of your frame's have panels in them with the code you want. Just take the code for the widgets from each of those frames and put those in each of the Page classes. You'll probably have to change the parents of the widgets to "self" instead of "panel" or whatever you call them.
Then you won't be opening a frame in each of the page code base. You don't want to open a frame there. You want the page to be the parent of the widgets, not open something else up.
Thanks to your help, I got this to work for me. Since each of my other wx App had Panel classes with all the widgets I wanted, I didn't need to create classes for each page. Here's the code:
import wx
import OtherFrame1
import OtherFrame2
import OtherFrame3
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Simple Notebook Example")
p = wx.Panel(self)
nb = wx.Notebook(p)
page1 = OtherFrame1.Panel(nb)
page2 = OtherFrame2.Panel(nb)
page3 = OtherFrame3.Panel(nb)
nb.AddPage(page1, "Page 1")
nb.AddPage(page2, "Page 2")
nb.AddPage(page3, "Page 3")
sizer = wx.BoxSizer()
sizer.Add(nb, 1, wx.EXPAND)
p.SetSizer(sizer)
if __name__ == "__main__":
app = wx.App(redirect=False)
MainFrame().Show()
app.MainLoop()
Thanks for the help!

Categories

Resources