Wxpython Switching frames between two python files - python

i have two py files that each have its own frame made using wxPython, MainWindow and RecWindow. MainWindow has the RecWindow python file included using the keyword "recovery".
MainWindow code:
class MainWindow(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, wx.ID_ANY,title,pos=(500,200), size = (650,500), style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.Bind(wx.EVT_CLOSE,self.OnExit)
self.SetIcon(wx.Icon('etc\icons\download.ico', wx.BITMAP_TYPE_ICO))
panel = wx.Panel(self)
RecWindow code:
class RecWindow(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, wx.ID_ANY,title,pos=(400,200), size = (700,600), style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.SetIcon(wx.Icon('etc\icons\download.ico', wx.BITMAP_TYPE_ICO))
self.count = 0
when i click on a button in MainWindow , it will hide the MainWindow create an instance of RecWindow, shown below;
def OpenRec(self,event):#this will be used to open the next frame
OR = recovery(None,-1,"RAVE")
OR.Show(True)
MainWindow.Hide()
now, what i am unsure of is how i can return to the MainWindow once i close the RecWindow. RecWindow has a cancel and finish button which both map to a self.close() function. How would i then get MainWindow to show itself again?

Use pubsub to send a message to the main window telling it to Show itself again. I actually have an example of how to do that here:
http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/
Note that this tutorial is using slightly older API that was available in wxPython 2.8. If you're using wxPython 2.9, then you'll have to use the slightly different API that I detail in this article:
http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/

When you create an instance of RecWindow keep a reference to it on main_window and bind to its close event.
In the main_window's close handler check if the window closed was the RecWindow.
If it was, clear the reference to it and show the main_window.
Elif the closed window was main_window carry out any required code.
Finally call event.Skip() so the windows get destroyed.
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, (500, 200), (650, 500),
wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
panel = wx.Panel(self)
button = wx.Button(panel, wx.ID_OPEN)
panel.sizer = wx.BoxSizer(wx.VERTICAL)
panel.sizer.Add(button, 0, wx.ALL, 7)
panel.SetSizer(panel.sizer)
button.Bind(wx.EVT_BUTTON, self.on_button)
self.Bind(wx.EVT_CLOSE, self.on_close)
self.rec_window = None
def on_button(self, event):
rec_window = RecWindow(self, 'Rec window')
rec_window.Show()
self.Hide()
rec_window.Bind(wx.EVT_CLOSE, self.on_close)
self.rec_window = rec_window
def on_close(self, event):
closed_window = event.EventObject
if closed_window == self.rec_window:
self.rec_window = None
self.Show()
elif closed_window == self:
print 'Carry out your code for when Main window closes'
event.Skip()
class RecWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, (400, 200), (700, 600),
wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
app = wx.App(False)
main_window = MainWindow(None, 'Main window')
main_window.Show()
app.MainLoop()

Related

wxpython: pause code as long as second frame is open

I'm pretty much a Python beginner and have only recently started with wxpython and now have the following question:
I'm writing an application where one window is open and by clicking a button, the user opens a second window, where they're supposed to input their name and click another button. My problem is that I don't know how to pause the 'event code' that is executed by clicking Button1 until the user is done with their actions in the second window.
Is there a way to pause the code in OnButton1 (after the line secondframe.Show() until the user has input their name in the SecondFrame and clicked on OnButton2?
Essentially my goal is that first, the name of the user is printed and only afterwards, Done is printed
class MainFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.panel = wx.Panel(self)
self.Button1 = wx.Button(self.panel, label='Button 1', pos=(20, 10), size=(90, 25))
#Set event handlers
self.Button1.Bind(wx.EVT_BUTTON, self.OnButton1)
def OnButton1(self, e):
secondframe = SecondFrame(None)
secondframe.Show()
print("Done")
class SecondFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.panel = wx.Panel(self)
self.Button2 = wx.Button(self.panel, label='Button 2', pos=(50, 20), size=(90, 25))
self.NameBox = wx.TextCtrl(self.panel, value="Type Your Name", pos=(50, 60), size=(200, -1))
#Set event handlers
self.Button2.Bind(wx.EVT_BUTTON, self.OnButton2)
def OnButton2(self, e):
Name = self.NameBox.GetValue()
print(Name)
app = wx.App(False)
frame = MainFrame(None)
frame.Show()
app.MainLoop()
I don't know of a way to actually block the code there. (Also, blocking is generally speaking never a good idea.)
But you can get the same behaviour like this:
Where you open the second window, in the first Window you call self.Disable()
When creating the second window, you also hand a reference to the first window to it (i.e. secondframe = SecondFrame(None, self)), adjust the constructor (def __init__(self, parent, main):) and store this in a variable there (self.mainFrame = main).
You add a method to MainFrame, e.g. reenable(self)
there you call self.Enable()
When you're done with Frame two, you can simply call self.mainFrame.reenable()
All in all, that would make something like:
class MainFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.panel = wx.Panel(self)
self.Button1 = wx.Button(self.panel, label='Button 1', pos=(20, 10), size=(90, 25))
#Set event handlers
self.Button1.Bind(wx.EVT_BUTTON, self.OnButton1)
def OnButton1(self, e):
secondframe = SecondFrame(None, self)
self.Disable()
secondframe.Show()
def reenable(self):
self.Enable()
print("done")
class SecondFrame(wx.Frame):
def __init__(self, parent, main):
wx.Frame.__init__(self, parent)
self.mainFrame = main
self.panel = wx.Panel(self)
self.Button2 = wx.Button(self.panel, label='Button 2', pos=(50, 20), size=(90, 25))
self.NameBox = wx.TextCtrl(self.panel, value="Type Your Name", pos=(50, 60), size=(200, -1))
#Set event handlers
self.Button2.Bind(wx.EVT_BUTTON, self.OnButton2)
def OnButton2(self, e):
Name = self.NameBox.GetValue()
print(Name)
self.mainFrame.reenable()
app = wx.App(False)
frame = MainFrame(None)
frame.Show()
app.MainLoop()

How to switch panels in python using wxPython

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!

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.

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()

Python attempting to create a simple gui, getting "AttributeError: 'MainMenu' object has no attribute 'intro_screen'"

complete newbie here to Python as I only started learning the language a few days ago (with no programming experience beforehand).
I'm basically bashing my skull against the desk here, trying to create one menu with a button that will lead you to another menu, which is supposed to replace/hide/destroy the previous menu (either works, so long as the process can be reversed).
What I've come up with so far:
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.Centre()
self.main_menu = MainMenu(self)
self.intro_screen = IntroScreen(self)
self.intro_screen.Hide()
class MainMenu(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent=parent)
self.main_menu = MainMenu
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
nextscreen = wx.Button(panel, label='Next Screen', size=(150,30))
nextscreen.Bind(wx.EVT_BUTTON, self.NextScreen)
sizer.Add(nextscreen, 0, wx.CENTER|wx.ALL, 5)
self.Show()
self.Centre()
def NextScreen(self, event):
self.main_menu.Hide(self)
self.intro_screen.Show()
class IntroScreen(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent=parent)
self.intro_screen = IntroScreen
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
gobackscreen = wx.Button(panel, label='Go Back a Screen', size=(150,30))
gobackscreen.Bind(wx.EVT_BUTTON, self.GoBackScreen)
sizer.Add(gobackscreen, 0, wx.CENTER|wx.ALL, 5)
self.Show()
self.Centre()
def GoBackScreen(self, event):
self.intro_screen.Hide()
self.main_menu.Show()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
#frame.Show()
app.MainLoop()
From what I can tell, the NextScreen button does not see the intro_screen class, and is therefore unable to show it. But I am clueless as to how to fix this.
Indeed, I have absolutely no idea if this is on the right way to do it. Any help is greatly appreciated
Using Python 2.7
intro_screen is an attribute of MainFrame instances; not of MainMenu instances.
Your MainMenu.__init__() method is passed in a MainFrame instance as parent. I am not certain if self.parent is set by the line wx.Frame.__init__(self, parent=parent), but if it is not, do add self.parent = parent in MainMenu.__init__(.
You can then refer to self.parent on MainMenu instances, and the following should work:
self.parent.intro_screen.Show()
I am not sure why you are setting the current class as an instance attribute:
self.main_menu = MainMenu
and
self.intro_screen = IntroScreen
Instead of self.main_menu.Hide(self) you can just call self.Hide(), the reference to the class is not needed.

Categories

Resources