wxPython - Getting attribute from another class? - python

I want to update the self.CreateStatusBar() in MainWindow from MainPanel. And update the self.textOutput in MainPanel from MainWindow.
Been reading alot, but still cant grasp it. Please help me. =)
import wx
ID_EXIT = 110
class MainPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.buttonRun = wx.Button(self, label="Run")
self.buttonRun.Bind(wx.EVT_BUTTON, self.OnRun )
self.buttonExit = wx.Button(self, label="Exit")
self.buttonExit.Bind(wx.EVT_BUTTON, self.OnExit)
self.labelChooseRoot = wx.StaticText(self, label ="Root catalog: ")
self.labelScratchWrk = wx.StaticText(self, label ="Scratch workspace: ")
self.labelMergeFile = wx.StaticText(self, label ="Merge file: ")
self.textChooseRoot = wx.TextCtrl(self, size=(210, -1))
self.textChooseRoot.Bind(wx.EVT_LEFT_UP, self.OnChooseRoot)
self.textScratchWrk = wx.TextCtrl(self, size=(210, -1))
self.textMergeFile = wx.TextCtrl(self, size=(210, -1))
self.textOutput = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY)
self.sizerF = wx.FlexGridSizer(3, 2, 5, 5)
self.sizerF.Add(self.labelChooseRoot) #row 1, col 1
self.sizerF.Add(self.textChooseRoot) #row 1, col 2
self.sizerF.Add(self.labelScratchWrk) #row 2, col 1
self.sizerF.Add(self.textScratchWrk) #row 2, col 2
self.sizerF.Add(self.labelMergeFile) #row 3, col 1
self.sizerF.Add(self.textMergeFile) #row 3, col 2
self.sizerB = wx.BoxSizer(wx.VERTICAL)
self.sizerB.Add(self.buttonRun, 1, wx.ALIGN_RIGHT|wx.ALL, 5)
self.sizerB.Add(self.buttonExit, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
self.sizer1 = wx.BoxSizer()
self.sizer1.Add(self.sizerF, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL, 10)
self.sizer1.Add(self.sizerB, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL)
self.sizer2 = wx.BoxSizer()
self.sizer2.Add(self.textOutput, 1, wx.EXPAND | wx.ALL, 5)
self.sizerFinal = wx.BoxSizer(wx.VERTICAL)
self.sizerFinal.Add(self.sizer1, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL)
self.sizerFinal.Add(self.sizer2, 1, wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL)
self.SetSizerAndFit(self.sizerFinal)
def OnChooseRoot(self, event):
dlg = wx.DirDialog(self, "Choose a directory:", style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
root_path = dlg.GetPath()
self.textChooseRoot.SetValue(root_path)
dlg.Destroy()
def OnRun(self, event):
#First check if any of the boxes is empty
pass
def OnExit(self, event):
self.GetParent().Close()
class MainWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="IndexGenerator", size=(430, 330),
style=((wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE |
wx.STAY_ON_TOP) ^ wx.RESIZE_BORDER))
self.CreateStatusBar()
self.fileMenu = wx.Menu()
self.fileMenu.Append(ID_EXIT, "E&xit", "Exit the program")
self.menuBar = wx.MenuBar()
self.menuBar.Append(self.fileMenu, "&File")
self.SetMenuBar(self.menuBar)
wx.EVT_MENU(self, ID_EXIT, self.OnExit)
self.Panel = MainPanel(self)
self.CentreOnScreen()
self.Show()
def OnExit(self, event):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = MainWindow()
app.MainLoop()

Generally speaking, UI elements shouldn't directly modify one another. In an event-driven application, UI elements can listen for events, and then perform some action following the event. Example: if the user does something to element B, element A can be notified of the event and then take an action.
Read more about events in wxPython here:
http://wiki.wxpython.org/AnotherTutorial#Events

I agree with AJ. You shouldn't modify GUI elements from each other directly. That ties things together pretty tightly. Instead, you should use something like pubsub or maybe wx.PostEvent to communicate between classes. Here's a simple pubsub example: http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/

In MainWindow:
self.Panel.textOutput.SetValue("...")
In MainPanel:
wx.Panel.__init__(self, parent)
self.window = parent
self.window.SetStatusText("...")

Related

wxPython - How to hide and show items in a centred BoxSizer without moving everything else?

I have a button to copy text from a TextCtrl and I want to display a message next to it to confirm that the text has been copied to the clipboard. So I'm using a StaticText that hides and shows when the button is clicked.
I have put the button and the StaticText in a horizontal centred BoxSizer. The thing is when the StaticText shows, as the BoxSizer's children are centred it moves them. Here is my code:
def __init__(self, *args, **kw) -> None:
super(Window, self).__init__(*args, **kw)
# create a panel in the frame
pnl = wx.Panel(self)
# create some widgets in the panel
self.text_area1 = wx.TextCtrl(pnl, style=wx.TE_MULTILINE | wx.TE_NOHIDESEL)
self.text_area2 = wx.TextCtrl(pnl, style = wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_NOHIDESEL | wx.TE_RICH2)
btn_correct = wx.Button(pnl, label="Correct >>>")
btn_correct.Bind(wx.EVT_BUTTON, self.on_press_correct)
self.btn_copy = wx.Button(pnl, label="Copy")
self.btn_copy.Bind(wx.EVT_BUTTON, self.on_press_copy)
self.label = wx.StaticText(pnl, label="Text copied")
self.label.Hide()
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
# create a sizer to manage the layout of child widgets
sizer = wx.BoxSizer(wx.VERTICAL)
sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.text_area1, 1, wx.ALL | wx.EXPAND, 5)
sizer.Add(btn_correct, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(self.text_area2, 1, wx.ALL | wx.EXPAND, 5)
sub_sizer.Add(self.btn_copy, 0, wx.ALL | wx.CENTER, 5)
sub_sizer.Add(self.label, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(self.sub_sizer, 0, wx.BOTTOM | wx.CENTER, 5)
pnl.SetSizer(sizer)
def on_press_copy(self, event):
data_obj = wx.TextDataObject()
data_obj.SetText(self.text_area2.GetValue())
if wx.TheClipboard.Open():
wx.TheClipboard.SetData(data_obj)
wx.TheClipboard.Close()
self.btn_copy.Disable()
self.label.Show()
self.label.GetParent().GetSizer().Layout()
self.timer.StartOnce(3000)
else:
wx.MessageBox("Can't open clipboard", "Error")
def on_timer(self, event):
self.btn_copy.Enable()
self.label.Show(show=False)
self.label.GetParent().GetSizer().Layout()
How can I achieve that without moving the button when the StaticText shows?
I have added a dummy element (filler - size >= length of label text) and used that to the left of the button and created a sizer (label_sizer which contains the filler element) to the right. That means the button does not move when the text is shown
import wx
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
super().__init__(None, *args, **kwargs)
self.Title = 'Wx App'
self.panel = MainPanel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.panel)
self.SetSizer(sizer)
self.Center()
self.Show()
class MainPanel(wx.Panel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
# create some widgets in the panel
self.text_area1 = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_NOHIDESEL)
self.text_area2 = wx.TextCtrl(self, style = wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_NOHIDESEL | wx.TE_RICH2)
btn_correct = wx.Button(self, label="Correct >>>")
btn_correct.Bind(wx.EVT_BUTTON, self.on_press_correct)
self.btn_copy = wx.Button(self, label="Copy")
self.btn_copy.Bind(wx.EVT_BUTTON, self.on_press_copy)
self.label_text = "Text copied"
filler = (100, 0)
self.label = wx.StaticText(self, label=self.label_text)
self.label.Hide()
self.label_dummy = filler
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
# create a sizer to manage the layout of child widgets
sizer = wx.BoxSizer(wx.VERTICAL)
sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
label_sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.text_area1, 1, wx.ALL | wx.EXPAND, 5)
sizer.Add(btn_correct, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(400,0)
label_sizer.Add(filler)
label_sizer.Add(self.label)
sizer.Add(self.text_area2, 1, wx.ALL | wx.EXPAND, 5)
sub_sizer.Add(self.label_dummy, 0, wx.ALL | wx.CENTER, 5)
sub_sizer.Add(self.btn_copy, 0, wx.ALL | wx.CENTER, 5)
sub_sizer.Add(label_sizer, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(sub_sizer, 0, wx.BOTTOM | wx.CENTER, 5)
self.SetSizer(sizer)
def on_press_copy(self, event):
data_obj = wx.TextDataObject()
data_obj.SetText(self.text_area2.GetValue())
if wx.TheClipboard.Open():
wx.TheClipboard.SetData(data_obj)
wx.TheClipboard.Close()
self.btn_copy.Disable()
self.label.Show()
self.label.GetParent().GetSizer().Layout()
self.timer.StartOnce(3000)
else:
wx.MessageBox("Can't open clipboard", "Error")
def on_press_correct(self, event):
print('Correct')
def on_timer(self, event):
self.btn_copy.Enable()
self.label.Show(show=False)
self.label.GetParent().GetSizer().Layout()
if __name__ == '__main__':
wx_app = wx.App()
MainFrame()
wx_app.MainLoop()
For reference, a partial code snippet, with errors, won't help get your question attention.
Obviously, centring 1 item versus 2 items, is never going to work. There will always be a difference.
You'll need to find another way.
You could output a messagebox or update the status line or use a different sizer, which probably gives you an approximation, of what you're after.
e.g.
Here I use a GridBagSizer and simply update the label text.
import wx
import time
class Application(wx.Frame):
def __init__(self, *args, **kw) -> None:
super(Application, self).__init__(*args, **kw)
# create a panel in the frame
pnl = wx.Panel(self)
# create some widgets in the panel
self.text_area1 = wx.TextCtrl(pnl, style=wx.TE_MULTILINE | wx.TE_NOHIDESEL)
self.text_area2 = wx.TextCtrl(pnl, style = wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_NOHIDESEL | wx.TE_RICH2)
btn_correct = wx.Button(pnl, label="Correct >>>")
btn_correct.Bind(wx.EVT_BUTTON, self.on_press_correct)
self.btn_copy = wx.Button(pnl, label="Copy")
self.btn_copy.Bind(wx.EVT_BUTTON, self.on_press_copy)
self.label = wx.StaticText(pnl, label=" ")
#self.label.Hide()
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
# create a sizer to manage the layout of child widgets
sizer = wx.BoxSizer(wx.VERTICAL)
sub_sizer = wx.GridBagSizer()
sizer.Add(self.text_area1, 1, wx.ALL | wx.EXPAND, 5)
sizer.Add(btn_correct, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(self.text_area2, 1, wx.ALL | wx.EXPAND, 5)
sub_sizer.Add((-1, -1), pos=(0, 0), span=(0, 3), flag=wx.EXPAND, border=5)
sub_sizer.Add(self.btn_copy, pos=(0, 3), flag=wx.ALL | wx.EXPAND, border=5)
sub_sizer.Add(self.label, pos=(0, 4), flag=wx.ALIGN_CENTER, border=5)
sizer.Add(sub_sizer, 0, wx.BOTTOM | wx.CENTER, 5)
pnl.SetSizer(sizer)
self.Show()
def on_press_correct(self, event):
return
def on_press_copy(self, event):
data_obj = wx.TextDataObject()
data_obj.SetText(self.text_area2.GetValue())
if wx.TheClipboard.Open():
wx.TheClipboard.SetData(data_obj)
wx.TheClipboard.Close()
self.btn_copy.Disable()
self.label.SetLabel("Text copied")
#elf.label.GetParent().GetSizer().Layout()
self.timer.StartOnce(3000)
else:
wx.MessageBox("Can't open clipboard", "Error")
def on_timer(self, event):
self.btn_copy.Enable()
#self.label.Show(show=False)
#self.label.GetParent().GetSizer().Layout()
self.label.SetLabel(" ")
app = wx.App(False)
frame = Application(None)
app.MainLoop()
It isn't perfect but it's close.
I thought for stuff like that the status bar was first choice

pass information among panels in wxpython

I have a pretty complicated wxpython app that has nested notebooks and panels. Basically the structure is that in my main frame I have a notebook (let's say notebook 1, in one page of the notebook I have two panels (upper and lower). In the lower panel I have another notebook (lets say notebook 2). So the question is how do I pass information between these two notebooks pages, and to the upper panels.
I know that there are basically three ways of passing information: by event id, by publisher, by .parent and .child. However I am really confused with the difference of these three methods and when to use them.
I have attached my code as below.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import datetime, time
import wx, sys, wx.grid
import xlrd
import pandas as pd
import numpy as np
import wx.lib.scrolledpanel as scrolled
EVEN_ROW_COLOUR = '#CCE6FF'
GRID_LINE_COLOUR = '#ccc'
import pandas as pd
import numpy as np
class ResultTable(wx.grid.PyGridTableBase):
def __init__(self, data=None):
wx.grid.PyGridTableBase.__init__(self)
self.data = data
self.odd=wx.grid.GridCellAttr()
self.odd.SetBackgroundColour("sky blue")
self.odd.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD))
self.even=wx.grid.GridCellAttr()
self.even.SetBackgroundColour("sea green")
self.even.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD))
def GetNumberRows(self):
return self.data.shape[0]
def GetNumberCols(self):
return self.data.shape[1]
def GetValue(self, row, col):
return self.data.loc[row][col]
def GetColLabelValue(self, row):
return list(self.data)[row]
def SetValue(self, row, col, value):
pass
def GetAttr(self, row, col, prop):
attr = wx.grid.GridCellAttr()
if row % 2 == 1:
attr.SetBackgroundColour(EVEN_ROW_COLOUR)
return attr
class ResultTablePanel(wx.Panel):
def __init__(self, parent, id):
wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)
grid = wx.grid.Grid(self)
result = pd.DataFrame({'a' : np.random.randn(100), 'b' : np.random.randn(100), 'c' : np.random.randn(100)})
table = ResultTable(result)
grid.SetTable(table)
grid.AutoSize()
grid.AutoSizeColumns(True)
grid.SetGridLineColour(GRID_LINE_COLOUR)
grid.EnableDragGridSize( False )
grid.SetRowLabelSize( 50 )
grid.SetDefaultCellAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(grid, 1, wx.ALL|wx.EXPAND)
self.SetSizer(sizer)
btn_ID = parent.GetParent().GetParent().topPanel.analysisButton.GetId()
self.Bind(wx.EVT_BUTTON, self.getResult, id = btn_ID)
def getResult(self,e):
"""
This function should get the variables passed by the analysisOrder function,
once the button on the SearchConditionPanel is clicked.
"""
fileName, start_date, end_date = parent.GetParent().GetParent().topPanel.analysisOrder
print "result get as follows:"
print fileName, start_date, end_date
class SearchConditionPanel(wx.Panel):
def __init__(self, parent, id):
wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)
# Creat input box for searching time period
nameTitleLable = wx.StaticText(self,-1, label=u"File Name:")
self.fileNameInput = wx.TextCtrl(self,-1, "20170310221612")
dateTitleLable = wx.StaticText(self,-1, label=u"Date range:")
yearLable1 = wx.StaticText(self, label=u"Year:")
monthLable1 = wx.StaticText(self,-1, label=u"Month:")
dayLable1 = wx.StaticText(self,-1, label=u"Day:")
yearLable2 = wx.StaticText(self,-1, label=u"Year:")
monthLable2 = wx.StaticText(self,-1, label=u"Month:")
dayLable2 = wx.StaticText(self,-1, label=u"Day:")
startLable = wx.StaticText(self,-1, label=u"Start Date:")
endLable = wx.StaticText(self,-1, label=u"End Date:")
self.startYearInput = wx.TextCtrl(self,1, "2016")
self.startMonthInput = wx.TextCtrl(self,-1, "10")
self.startDayInput = wx.TextCtrl(self,-1, "30")
self.endYearInput = wx.TextCtrl(self,-1, "2017")
self.endMonthInput = wx.TextCtrl(self,-1, "11")
self.endDayInput = wx.TextCtrl(self,-1, "22")
self.analysisButton = wx.Button(self, -1, label = u'Start')
exportButton = wx.Button(self, -1, label = u'Export')
exportButton.Disable()
## Set up overall layout for the panel
hbox = wx.BoxSizer(wx.HORIZONTAL)
vbox_File_button = wx.BoxSizer(wx.VERTICAL)
hbox_file = wx.BoxSizer(wx.HORIZONTAL)
vbox_date = wx.BoxSizer(wx.VERTICAL)
hbox_button = wx.BoxSizer(wx.HORIZONTAL)
hbox_startDate = wx.BoxSizer(wx.HORIZONTAL)
hbox_endDate = wx.BoxSizer(wx.HORIZONTAL)
hbox.Add(vbox_date, 0, wx.ALIGN_LEFT | wx.ALL, 5)
hbox.Add(wx.StaticLine(self, style=wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND, 5)
hbox.Add(vbox_File_button, 0, wx.ALIGN_LEFT | wx.ALL, 5)
## Setup the layout for the right side
vbox_File_button.Add(hbox_file, 0, wx.ALIGN_LEFT | wx.ALL, 5)
vbox_File_button.Add(wx.StaticLine(self), 0, wx.ALL | wx.EXPAND, 5)
vbox_File_button.Add(hbox_button, 0, wx.ALIGN_LEFT | wx.ALL, 5)
hbox_file.Add(nameTitleLable, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 10)
hbox_file.Add(self.fileNameInput, 0, wx.ALL | wx.EXPAND, 5)
hbox_button.Add(self.analysisButton, 1, wx.TOP | wx.LEFT, 10)
hbox_button.Add(exportButton, 1, wx.TOP | wx.LEFT, 10)
## Setup the layout for the left side
vbox_date.Add(dateTitleLable, 0, wx.ALL, 5)
vbox_date.Add(hbox_startDate, 0, wx.ALL | wx.EXPAND, 5)
vbox_date.Add(hbox_endDate, 0, wx.ALL | wx.EXPAND, 5)
hbox_startDate.Add(startLable, 0, wx.ALL, 5)
hbox_startDate.Add(yearLable1, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 5)
hbox_startDate.Add(self.startYearInput, 0, wx.ALL, 5)
hbox_startDate.Add(monthLable1, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 5)
hbox_startDate.Add(self.startMonthInput, 0, wx.ALL|wx.EXPAND, 5)
hbox_startDate.Add(dayLable1, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 5)
hbox_startDate.Add(self.startDayInput, 0, wx.ALL|wx.EXPAND, 5)
hbox_endDate.Add(endLable, 0, wx.LEFT | wx.RIGHT, 5)
hbox_endDate.Add(yearLable2, 0, wx.LEFT, 5)
hbox_endDate.Add(self.endYearInput, 0, wx.LEFT | wx.RIGHT, 5)
hbox_endDate.Add(monthLable2, 0, wx.LEFT, 5)
hbox_endDate.Add(self.endMonthInput, 0, wx.LEFT | wx.RIGHT, 5)
hbox_endDate.Add(dayLable2, 0, wx.LEFT, 5)
hbox_endDate.Add(self.endDayInput, 0, wx.LEFT, 5)
self.SetSizer(hbox)
self.Bind(wx.EVT_BUTTON, self.analysisOrder, id=self.analysisButton.GetId())
def analysisOrder(self,e):
fileName_temp = self.fileNameInput.GetValue().strip()
fileName = fileName_temp + '.xls'
start_date = self.startYearInput.GetValue().strip() + '-' + self.startMonthInput.GetValue().strip() + '-' + self.startDayInput.GetValue().strip()
end_date = self.endYearInput.GetValue().strip() + '-' + self.endMonthInput.GetValue().strip() + '-' + self.endDayInput.GetValue().strip()
print "analysis order button called"
return fileName, start_date, end_date
class ResultNotebook(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# Creat notebooks for window layout
result_notebook = wx.Notebook(self)
data_page = ResultTablePanel(result_notebook, -1)
result_notebook.AddPage(data_page, u"Display Data")
# Set up a boxsizer for the tabs
result_notebook_sizer = wx.BoxSizer(wx.HORIZONTAL)
result_notebook_sizer.Add(result_notebook, 1, wx.EXPAND)
self.SetSizer(result_notebook_sizer)
class ModePage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# Localize all the panels
self.topPanel = SearchConditionPanel(self, -1)
self.botPanel = ResultNotebook(self)
#Set up the panels, align and position them in the right place
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.topPanel, 0, wx.EXPAND | wx.ALL, 5)
vbox.Add(self.botPanel, -1, wx.EXPAND | wx.ALL, 5)
self.SetSizer(vbox)
self.Centre()
self.Show(True)
class FreqPage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# t = wx.StaticText(self, -1, "This is a Frequency Page object", (20,20))
# Localize all the panels
topPanel = SearchConditionPanel(self, -1)
t = wx.StaticText(self, -1, "This is a Frequency Page object")
#Set up the panels, align and position them in the right place
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(topPanel, 0, wx.EXPAND | wx.ALL, 5)
vbox.Add(t, -1, wx.EXPAND | wx.ALL, 5)
self.SetSizer(vbox)
self.Centre()
self.Show(True)
class OrderPage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# Localize all the panels
self.topPanel = SearchConditionPanel(self, -1)
self.botPanel = ResultNotebook(self)
#Set up the panels, align and position them in the right place
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.topPanel, 0, wx.EXPAND | wx.ALL, 5)
vbox.Add(self.botPanel, -1, wx.EXPAND | wx.ALL, 5)
self.SetSizer(vbox)
self.Centre()
self.Show(True)
class ShopPage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# Creat notebooks for window layout
shop_notebook = wx.Notebook(self)
mode_page = ModePage(shop_notebook)
freq_page = FreqPage(shop_notebook)
shop_notebook.AddPage(mode_page, u"Mode Analysis")
shop_notebook.AddPage(freq_page, u"Frequency Analysis")
# Set up a boxsizer for the tabs
shop_page_sizer = wx.BoxSizer(wx.HORIZONTAL)
shop_page_sizer.Add(shop_notebook, 1, wx.EXPAND)
self.SetSizer(shop_page_sizer)
##**----------------------------------------------------------------------------------**##
##**-------------- Create a windows to display the entire system ---------------------**##
class OrderAnalysis(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(1024, 576))
# self.Maximize(True) # Default display to be maximized
panel = wx.Panel(self, -1)
# Creat notebooks for window layout
main_notebook = wx.Notebook(panel, style=wx.NB_LEFT)
main_notebook.SetFont(wx.Font(10, wx.FONTFAMILY_DEFAULT,
wx.FONTWEIGHT_NORMAL,
wx.FONTSTYLE_NORMAL))
# create the page windows as children of the notebook
order_page = OrderPage(main_notebook)
shop_page = ShopPage(main_notebook)
# add the pages to the notebook with the label to show on the tab
main_notebook.AddPage(order_page, u"Order Analysis")
main_notebook.AddPage(shop_page, u"Shop Analysis")
# finally, put the notebook in a sizer for the panel to manage
# the layout
sizer = wx.BoxSizer()
sizer.Add(main_notebook, 1, wx.EXPAND)
panel.SetSizer(sizer)
self.Centre()
self.Show(True)
if __name__ == "__main__":
app = wx.App()
OrderAnalysis(None, -1, 'Order Info Analyser')
app.MainLoop()
As you can see, I have a search condition panel and a result panel that are used in different pages. For each specific page, the search condition and result could be different and only for that page. So my question is, what is the best way to pass the search condition (i.e. data and file name) to the corresponding page to display the result (i.e. the "getResult" function inside "ResultTablePanel" doesn't work).
For example, inside the "order page", the search condition panel has user input start and end dates, and the file name. Below the search condition panel is the result panel showing the computed result based on the search condition. Once the "Start" button is clicked, the "start date", "end date" and "file name" variables are passed to the result panel ("ResultTablePanel") within the "order page". Similarly, since the "Mode page" also calls both "ResultTablePanel" and "SearchConditionPanel", it should have its own "start date", "end date" and "file name" variables (note that they can be same or different from the ones inside "order page"), and its own result displayed in "ResultTablePanel".
Thank you in advance for taking time reviewing and answering my question.
================================================================================
Thank you guys for the help, I have figured it out using PubSub and .parent methods combined. I have attached my solution code as follows:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import datetime, time
import wx, sys, wx.grid
import xlrd
import pandas as pd
import numpy as np
import wx.lib.scrolledpanel as scrolled
EVEN_ROW_COLOUR = '#CCE6FF'
GRID_LINE_COLOUR = '#ccc'
import pandas as pd
import numpy as np
from wx.lib.pubsub import pub
class ResultTable(wx.grid.PyGridTableBase):
def __init__(self, data=None):
wx.grid.PyGridTableBase.__init__(self)
self.data = data
self.odd=wx.grid.GridCellAttr()
self.odd.SetBackgroundColour("sky blue")
self.odd.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD))
self.even=wx.grid.GridCellAttr()
self.even.SetBackgroundColour("sea green")
self.even.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD))
def GetNumberRows(self):
if self.data.empty:
return 5
else:
return self.data.shape[0]
def GetNumberCols(self):
if self.data.empty:
return 5
else:
return self.data.shape[1]
def GetValue(self, row, col):
if self.data.empty:
return None
else:
return self.data.loc[row][col]
def GetColLabelValue(self, row):
if self.data.empty:
return None
else:
return list(self.data)[row]
def SetValue(self, row, col, value):
pass
def GetAttr(self, row, col, prop):
attr = wx.grid.GridCellAttr()
if row % 2 == 1:
attr.SetBackgroundColour(EVEN_ROW_COLOUR)
return attr
def ResetView(self):
"""Trim/extend the control's rows and update all values"""
self.getGrid().BeginBatch()
for current, new, delmsg, addmsg in [
(self.currentRows, self.GetNumberRows(), wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED),
(self.currentColumns, self.GetNumberCols(), wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED),
]:
if new < current:
msg = wx.grid.GridTableMessage(
self,
delmsg,
new, # position
current-new,
)
self.getGrid().ProcessTableMessage(msg)
elif new > current:
msg = wx.grid.GridTableMessage(
self,
addmsg,
new-current
)
self.getGrid().ProcessTableMessage(msg)
self.UpdateValues()
self.getGrid().EndBatch()
# The scroll bars aren't resized (at least on windows)
# Jiggling the size of the window rescales the scrollbars
h,w = grid.GetSize()
grid.SetSize((h+1, w))
grid.SetSize((h, w))
grid.ForceRefresh()
def UpdateValues( self ):
"""Update all displayed values"""
msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
self.getGrid().ProcessTableMessage(msg)
class ResultTablePanel(wx.Panel):
def __init__(self, parent, id, page):
wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)
self.grid = wx.grid.Grid(self)
self.parent = parent
result = pd.DataFrame({'0' : np.random.randn(100), 'b' : np.random.randn(100), 'c' : np.random.randn(100)})
result = pd.DataFrame()
table = ResultTable(result)
self.grid.SetTable(table)
self.grid.AutoSize()
self.grid.AutoSizeColumns(True)
self.grid.SetGridLineColour(GRID_LINE_COLOUR)
self.grid.EnableDragGridSize( False )
self.grid.SetRowLabelSize( 50 )
self.grid.SetDefaultCellAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.grid, 1, wx.ALL|wx.EXPAND)
self.SetSizer(sizer)
pub.subscribe(self.getResult, "panelListener")
def getResult(self, message, page):
"""
This function should get the variables passed by the analysisOrder function,
once the button on the SearchConditionPanel is clicked.
"""
result = pd.DataFrame()
if page == "Order" and self.parent.GetParent().GetParent().page_identifier == "Order":
# For test purpose only
result = pd.DataFrame({'0' : np.random.randn(100), 'b' : np.random.randn(100), 'c' : np.random.randn(100)}) ## For test only
print "Update Order page"
table = ResultTable(result)
self.grid.SetTable(table)
self.Refresh()
if page == "Mode" and self.parent.GetParent().GetParent().page_identifier == "Mode":
result = pd.DataFrame({'0' : np.random.randn(1000), 'b' : np.random.randn(1000)}) ## For test only
print "Update Mode page"
table = ResultTable(result)
self.grid.SetTable(table)
self.Refresh()
else:
pass
class SearchConditionPanel(wx.Panel):
def __init__(self, parent, id, pageID):
wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)
# Creat input box for searching time period
nameTitleLable = wx.StaticText(self,-1, label=u"File Name:")
self.fileNameInput = wx.TextCtrl(self,-1, "20170310221612")
dateTitleLable = wx.StaticText(self,-1, label=u"Date range:")
yearLable1 = wx.StaticText(self, label=u"Year:")
monthLable1 = wx.StaticText(self,-1, label=u"Month:")
dayLable1 = wx.StaticText(self,-1, label=u"Day:")
yearLable2 = wx.StaticText(self,-1, label=u"Year:")
monthLable2 = wx.StaticText(self,-1, label=u"Month:")
dayLable2 = wx.StaticText(self,-1, label=u"Day:")
startLable = wx.StaticText(self,-1, label=u"Start Date:")
endLable = wx.StaticText(self,-1, label=u"End Date:")
self.startYearInput = wx.TextCtrl(self,1, "2016")
self.startMonthInput = wx.TextCtrl(self,-1, "10")
self.startDayInput = wx.TextCtrl(self,-1, "30")
self.endYearInput = wx.TextCtrl(self,-1, "2017")
self.endMonthInput = wx.TextCtrl(self,-1, "11")
self.endDayInput = wx.TextCtrl(self,-1, "22")
self.analysisButton = wx.Button(self, -1, label = u'Start')
exportButton = wx.Button(self, -1, label = u'Export')
exportButton.Disable()
## Set up overall layout for the panel
hbox = wx.BoxSizer(wx.HORIZONTAL)
vbox_File_button = wx.BoxSizer(wx.VERTICAL)
hbox_file = wx.BoxSizer(wx.HORIZONTAL)
vbox_date = wx.BoxSizer(wx.VERTICAL)
hbox_button = wx.BoxSizer(wx.HORIZONTAL)
hbox_startDate = wx.BoxSizer(wx.HORIZONTAL)
hbox_endDate = wx.BoxSizer(wx.HORIZONTAL)
hbox.Add(vbox_date, 0, wx.ALIGN_LEFT | wx.ALL, 5)
hbox.Add(wx.StaticLine(self, style=wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND, 5)
hbox.Add(vbox_File_button, 0, wx.ALIGN_LEFT | wx.ALL, 5)
## Setup the layout for the right side
vbox_File_button.Add(hbox_file, 0, wx.ALIGN_LEFT | wx.ALL, 5)
vbox_File_button.Add(wx.StaticLine(self), 0, wx.ALL | wx.EXPAND, 5)
vbox_File_button.Add(hbox_button, 0, wx.ALIGN_LEFT | wx.ALL, 5)
hbox_file.Add(nameTitleLable, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 10)
hbox_file.Add(self.fileNameInput, 0, wx.ALL | wx.EXPAND, 5)
hbox_button.Add(self.analysisButton, 1, wx.TOP | wx.LEFT, 10)
hbox_button.Add(exportButton, 1, wx.TOP | wx.LEFT, 10)
## Setup the layout for the left side
vbox_date.Add(dateTitleLable, 0, wx.ALL, 5)
vbox_date.Add(hbox_startDate, 0, wx.ALL | wx.EXPAND, 5)
vbox_date.Add(hbox_endDate, 0, wx.ALL | wx.EXPAND, 5)
hbox_startDate.Add(startLable, 0, wx.ALL, 5)
hbox_startDate.Add(yearLable1, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 5)
hbox_startDate.Add(self.startYearInput, 0, wx.ALL, 5)
hbox_startDate.Add(monthLable1, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 5)
hbox_startDate.Add(self.startMonthInput, 0, wx.ALL|wx.EXPAND, 5)
hbox_startDate.Add(dayLable1, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 5)
hbox_startDate.Add(self.startDayInput, 0, wx.ALL|wx.EXPAND, 5)
hbox_endDate.Add(endLable, 0, wx.LEFT | wx.RIGHT, 5)
hbox_endDate.Add(yearLable2, 0, wx.LEFT, 5)
hbox_endDate.Add(self.endYearInput, 0, wx.LEFT | wx.RIGHT, 5)
hbox_endDate.Add(monthLable2, 0, wx.LEFT, 5)
hbox_endDate.Add(self.endMonthInput, 0, wx.LEFT | wx.RIGHT, 5)
hbox_endDate.Add(dayLable2, 0, wx.LEFT, 5)
hbox_endDate.Add(self.endDayInput, 0, wx.LEFT, 5)
self.SetSizer(hbox)
self.Bind(wx.EVT_BUTTON, self.analysisOrder, id=self.analysisButton.GetId())
self.page_ID = pageID
def analysisOrder(self,e):
fileName_temp = self.fileNameInput.GetValue().strip()
fileName = fileName_temp + '.xls'
start_date = self.startYearInput.GetValue().strip() + '-' + self.startMonthInput.GetValue().strip() + '-' + self.startDayInput.GetValue().strip()
end_date = self.endYearInput.GetValue().strip() + '-' + self.endMonthInput.GetValue().strip() + '-' + self.endDayInput.GetValue().strip()
print "analysis order button called"
pub.sendMessage("panelListener", message=fileName, page = self.page_ID)
return fileName, start_date, end_date
class ResultNotebook(wx.Panel):
def __init__(self, parent, pageID):
wx.Panel.__init__(self, parent)
# Creat notebooks for window layout
result_notebook = wx.Notebook(self)
self.data_page = ResultTablePanel(result_notebook, -1, page = pageID)
result_notebook.AddPage(self.data_page, u"Display Data")
# Set up a boxsizer for the tabs
result_notebook_sizer = wx.BoxSizer(wx.HORIZONTAL)
result_notebook_sizer.Add(result_notebook, 1, wx.EXPAND)
self.SetSizer(result_notebook_sizer)
class ModePage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.page_identifier = "Mode"
# Localize all the panels
self.topPanel = SearchConditionPanel(self, -1, pageID = "Mode")
self.botPanel = ResultNotebook(self, pageID = "Mode")
#Set up the panels, align and position them in the right place
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.topPanel, 0, wx.EXPAND | wx.ALL, 5)
vbox.Add(self.botPanel, -1, wx.EXPAND | wx.ALL, 5)
self.SetSizer(vbox)
self.Centre()
self.Show(True)
class FreqPage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# t = wx.StaticText(self, -1, "This is a Frequency Page object", (20,20))
# Localize all the panels
topPanel = SearchConditionPanel(self, -1, pageID = "Frequency")
t = wx.StaticText(self, -1, "This is a Frequency Page object")
#Set up the panels, align and position them in the right place
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(topPanel, 0, wx.EXPAND | wx.ALL, 5)
vbox.Add(t, -1, wx.EXPAND | wx.ALL, 5)
self.SetSizer(vbox)
self.Centre()
self.Show(True)
class OrderPage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.page_identifier = "Order"
# Localize all the panels
self.topPanel = SearchConditionPanel(self, -1, pageID = "Order")
self.botPanel = ResultNotebook(self, pageID = "Order")
#Set up the panels, align and position them in the right place
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.topPanel, 0, wx.EXPAND | wx.ALL, 5)
vbox.Add(self.botPanel, -1, wx.EXPAND | wx.ALL, 5)
self.SetSizer(vbox)
self.Centre()
self.Show(True)
class ShopPage(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# Creat notebooks for window layout
shop_notebook = wx.Notebook(self)
mode_page = ModePage(shop_notebook)
freq_page = FreqPage(shop_notebook)
shop_notebook.AddPage(mode_page, u"Mode Analysis")
shop_notebook.AddPage(freq_page, u"Frequency Analysis")
# Set up a boxsizer for the tabs
shop_page_sizer = wx.BoxSizer(wx.HORIZONTAL)
shop_page_sizer.Add(shop_notebook, 1, wx.EXPAND)
self.SetSizer(shop_page_sizer)
##**----------------------------------------------------------------------------------**##
##**-------------- Create a windows to display the entire system ---------------------**##
class OrderAnalysis(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(1024, 576))
# self.Maximize(True) # Default display to be maximized
panel = wx.Panel(self, -1)
# Creat notebooks for window layout
main_notebook = wx.Notebook(panel, style=wx.NB_LEFT)
main_notebook.SetFont(wx.Font(10, wx.FONTFAMILY_DEFAULT,
wx.FONTWEIGHT_NORMAL,
wx.FONTSTYLE_NORMAL))
# create the page windows as children of the notebook
order_page = OrderPage(main_notebook)
shop_page = ShopPage(main_notebook)
# add the pages to the notebook with the label to show on the tab
main_notebook.AddPage(order_page, u"Order Analysis")
main_notebook.AddPage(shop_page, u"Shop Analysis")
# finally, put the notebook in a sizer for the panel to manage
# the layout
sizer = wx.BoxSizer()
sizer.Add(main_notebook, 1, wx.EXPAND)
panel.SetSizer(sizer)
self.Centre()
self.Show(True)
if __name__ == "__main__":
app = wx.App()
OrderAnalysis(None, -1, 'Order Info Analyser')
app.MainLoop()
Thank you everyone again! Much appreciated.
I usually recommend using Pubsub for this sort of thing. It's very easy to implement and makes it easy to pass a message to one or more recipients (panels).
Here's a simple example:
import wx
from wx.lib.pubsub import pub
class OtherFrame(wx.Frame):
""""""
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, wx.ID_ANY, "Secondary Frame")
panel = wx.Panel(self)
msg = "Enter a Message to send to the main frame"
instructions = wx.StaticText(panel, label=msg)
self.msgTxt = wx.TextCtrl(panel, value="")
closeBtn = wx.Button(panel, label="Send and Close")
closeBtn.Bind(wx.EVT_BUTTON, self.onSendAndClose)
sizer = wx.BoxSizer(wx.VERTICAL)
flags = wx.ALL|wx.CENTER
sizer.Add(instructions, 0, flags, 5)
sizer.Add(self.msgTxt, 0, flags, 5)
sizer.Add(closeBtn, 0, flags, 5)
panel.SetSizer(sizer)
def onSendAndClose(self, event):
"""
Send a message and close frame
"""
msg = self.msgTxt.GetValue()
pub.sendMessage("panelListener", message=msg)
pub.sendMessage("panelListener", message="test2", arg2="2nd argument!")
self.Close()
class MyPanel(wx.Panel):
""""""
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
pub.subscribe(self.myListener, "panelListener")
btn = wx.Button(self, label="Open Frame")
btn.Bind(wx.EVT_BUTTON, self.onOpenFrame)
def myListener(self, message, arg2=None):
"""
Listener function
"""
print "Received the following message: " + message
if arg2:
print "Received another arguments: " + str(arg2)
def onOpenFrame(self, event):
"""
Opens secondary frame
"""
frame = OtherFrame()
frame.Show()
class MyFrame(wx.Frame):
""""""
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="PubSub Tutorial")
panel = MyPanel(self)
self.Show()
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
And here is some additional information:
https://wxpython.org/Phoenix/docs/html/wx.lib.pubsub.html
https://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/
You mention three ways of passing information but I have to confess I don't understand what you are talking about (and I've written many wxWidget applications). One way of handling the situation you describe is for the event handler to call a function in the parent class, which can then relay the information to other functions/classes. Schematically:
class A:
def __init__(self):
B(self)
self.c = C(self)
def a_function(self, *data):
# do something with the data
# you can pass the information to class C here
class B:
def __init__(self, parent):
self.parent = parent
def an_event_handler(self):
data = something
self.parent.a_function(data)
# or use wx.CallAfter for later execution
class C:
pass
Another solution is the MVC concept, where your event handler passes its information to a model class which stores it or processes it further. This model class can raise a custom wx Event, which components of the UI can then handle as necessary to redraw any affected widgets.

wxPython How to make a dynamic scrolled panel

I have a problem: I want to make a password manager for myself .
I have the logic for the en/decoding done and also I have the dynamic adding of passwords done, but if I add too many passwords to my manager then I can't see all of the passwords because my screen is too small.
I wanted to add a scroll bar but the scroll bar didn't work.
Even after several hours of researching the code didn't want to work. This is the code for the PanelTwo:
class PanelTwo(wx.Panel):
#staticmethod
def scale_bitmap(bitmap, width, height):
image = wx.ImageFromBitmap(bitmap)
image = image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
result = wx.BitmapFromImage(image)
return result
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
self.refr = wx.Button(self,wx.ID_ANY,u"Refresh")
self.refr.Bind(wx.EVT_BUTTON, self.refresh)
self.co = wx.Button(self, wx.ID_ANY, u"Close")
self.co.Bind(wx.EVT_BUTTON, self.close)
self.number_of_pwds = 0
self.frame = parent
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
controlSizer = wx.BoxSizer(wx.HORIZONTAL)
self.widgetSizer = wx.BoxSizer(wx.VERTICAL)
#controlSizer.Add(self.Passest, 0, wx.CENTER | wx.ALL, 5)
controlSizer.Add(self.refr, 0, wx.CENTER | wx.ALL, 5)
controlSizer.Add(self.co, 0, wx.CENTER | wx.ALL, 5)
self.addButton = wx.Button(self, label="Add")
self.addButton.Bind(wx.EVT_BUTTON, self.onAddWidget)
controlSizer.Add(self.addButton, 0, wx.CENTER | wx.ALL, 5)
self.removeButton = wx.Button(self, label="Remove")
self.removeButton.Bind(wx.EVT_BUTTON, self.onRemoveWidget)
controlSizer.Add(self.removeButton, 0, wx.CENTER | wx.ALL, 5)
self.mainSizer.Add(controlSizer, 0, wx.CENTER)
self.mainSizer.Add(self.widgetSizer, 0, wx.CENTER | wx.ALL, 10)
self.SetSizer(self.mainSizer)
The adding and removing of the Pwds is here :
def onAddWidget(self, event):
self.number_of_pwds += 1
label = "Pwd %s" % self.number_of_pwds
name = "Pwd%s" % self.number_of_pwds
new_Text = wx.StaticText(self, label=label, name=name)
self.widgetSizer.Add(new_Text, 0, wx.ALL, 5)
self.frame.fSizer.Layout()
self.frame.Fit()
def onRemoveWidget(self, event):
if self.widgetSizer.GetChildren():
self.widgetSizer.Hide(self.number_of_pwds - 1)
self.widgetSizer.Remove(self.number_of_pwds - 1)
self.number_of_pwds -= 1
self.frame.fSizer.Layout()
self.frame.Fit()
my main Form is here :
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY,
"Passwort Manager",
size=(300,130))
self.panel_one = PanelOne(self)
self.panel_two = PanelTwo(self)
self.panel_three = PanelThree(self)
self.panel_two.Hide()
self.panel_three.Hide()
self.fSizer = wx.BoxSizer(wx.VERTICAL)
self.fSizer.Add(self.panel_one, 1, wx.EXPAND)
self.fSizer.Add(self.panel_two, 1, wx.EXPAND)
self.fSizer.Add(self.panel_three,1,wx.EXPAND)
self.SetSizer(self.fSizer)
self.SetBackgroundColour(Color.White)
def onSwitchPanels(self, event):
if self.panel_one.IsShown():
self.SetTitle("Passwort Manager")
self.SetBackgroundColour(Color.Random())
self.panel_one.Hide()
self.panel_two.Show()
elif self.panel_two.IsShown():
self.SetTitle("Passwort Manager")
self.SetBackgroundColour(Color.Random())
self.panel_three.Show()
self.panel_two.Hide()
else:
self.SetTitle("Passwort Manager")
self.SetBackgroundColour(Color.Random())
self.panel_one.Show()
self.panel_three.Hide()
self.Layout()
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
So how can I add a dynamic scrollbar that resizes automatically and that works with my code? I am using Python3.6.
Thanks for helping me out.
Have you tried using a scrolled panel?
import wx
import wx.lib.scrolledpanel as scrolled
class TestPanel(scrolled.ScrolledPanel):
def __init__(self, parent):
scrolled.ScrolledPanel.__init__(self, parent, -1)
"""
Write your code here...
"""
self.SetupScrolling()

Global variable between frames in wxPython

I wanted to use a variable defined in a child frame. I can't figure out how to pull back the username after a successful login event in the child frame back to the main frame. I wanted to update the static label to show the username.
import wx
import MySQLdb
import sys
username = ""
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, title = 'Application Title',style = wx.SYSTEM_MENU)
panel = wx.Panel(self)
menubar = wx.MenuBar()
panel.SetBackgroundColour((200,200,200))
vbox = wx.BoxSizer(wx.HORIZONTAL)
midPan = wx.Panel(panel)
midPan.SetBackgroundColour((100,0,0))
pan2 = wx.Panel(panel)
pan2.SetBackgroundColour((0,100,0))
vbox.Add(pan2, 1, wx.EXPAND | wx.ALL)
vbox.Add(midPan, 5, wx.EXPAND | wx.ALL)
hbox = wx.BoxSizer(wx.VERTICAL)
hbox1=wx.BoxSizer(wx.VERTICAL)
h1=wx.Panel(midPan)
h1.SetBackgroundColour((200,0,100))
h2=wx.Panel(midPan)
h2.SetBackgroundColour((0,100,200))
#######################################################################
text1 = wx.StaticText(h1, label="Welcome ") #insert the global variable in this label after the user has successfully logged in
text2 = wx.StaticText(h1, label="Label2")
hbox.Add(h1,3,wx.EXPAND|wx.ALL,20)
hbox.Add(h2,1,wx.EXPAND|wx.ALL,20)
hbox1.Add(text1,1,wx.ALIGN_LEFT)
hbox1.Add(text2,1,wx.ALIGN_RIGHT)
panel.SetSizer(vbox)
midPan.SetSizer(hbox)
h1.SetSizer(hbox1)
self.statusbar = self.CreateStatusBar()
self.SetMenuBar(menubar)
self.Centre()
self.Maximize()
self.Show(True)
frame = LoginFrame(self)
frame.Show(True)
frame.MakeModal(True)
def OnExit(self, e):
self.Close()
class LoginFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent,style=wx.DEFAULT_FRAME_STYLE & ~(wx.RESIZE_BORDER | wx.MAXIMIZE_BOX | wx.CLOSE_BOX | wx.MINIMIZE_BOX ), title = "System Login")
self.panel = wx.Panel(self)
self.userlabel = wx.StaticText(self.panel, label="Username:")
self.passlabel = wx.StaticText(self.panel, label="Password:")
self.userbox = wx.TextCtrl(self.panel, size=(140, -1))
self.passbox = wx.TextCtrl(self.panel, size=(140, -1), style=wx.TE_PASSWORD)
self.login = wx.Button(self.panel, label="Login")
self.exit = wx.Button(self.panel, label="Exit")
self.Centre()
self.windowSizer = wx.BoxSizer()
self.windowSizer.Add(self.panel, 1, wx.ALL | wx.EXPAND)
self.sizer = wx.GridBagSizer(5, 5)
self.sizer.Add(self.userlabel, (0, 0))
self.sizer.Add(self.userbox, (0, 1))
self.sizer.Add(self.passlabel, (1, 0))
self.sizer.Add(self.passbox, (1, 1))
self.sizer.Add(self.login, (2, 0))
self.sizer.Add(self.exit, (2, 1))
self.border = wx.BoxSizer()
self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 5)
self.panel.SetSizerAndFit(self.border)
self.SetSizerAndFit(self.windowSizer)
self.login.Bind(wx.EVT_BUTTON, self.OnLogin)
self.exit.Bind(wx.EVT_BUTTON, self.OnExit)
def OnLogin(self, e):
db = MySQLdb.connect("localhost","root","","test" )
cursor = db.cursor()
username = self.userbox.GetValue()
password = self.passbox.GetValue()
try:
cursor.execute("SELECT * FROM useraccounts WHERE username=%s" ,username)
data = cursor.fetchall()
for row in data:
dbpass = row[2]
if dbpass == password:
self.MakeModal(False)
e.Skip()
self.Close()
wx.MessageBox('You are now logged in', 'Info', wx.OK | wx.ICON_INFORMATION)
else:
wx.MessageBox('Username or Password is incorrect', 'Info', wx.OK | wx.ICON_INFORMATION)
except:
wx.MessageBox('Username or Password is incorrect', 'Info', wx.OK | wx.ICON_INFORMATION)
def OnExit(self,e):
self.MakeModal(False)
e.Skip()
self.Close()
sys.exit(0)
if __name__ == '__main__':
app = wx.App()
MainFrame()
app.MainLoop()
I think the easiest and cleanest way to accomplish this is to use pubsub. The API for pubsub is different depending on which version of wxPython you use. I have written up tutorials for both though. So for wxPython 2.8, you'd want to look at this one:
http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/
For wxPython 2.9+, you'd want to take a look at this one:
http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/
There is a trick to make wxPython 2.8 behave in the new manner, which I explain in the second article.
Anyway, enough with the intro, let's actually try the 2.9 method in your code:
import wx
import sys
username = ""
from wx.lib.pubsub import pub
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, title = 'Application Title',style = wx.SYSTEM_MENU)
pub.subscribe(self.myListener, "update_frame")
panel = wx.Panel(self)
menubar = wx.MenuBar()
panel.SetBackgroundColour((200,200,200))
vbox = wx.BoxSizer(wx.HORIZONTAL)
midPan = wx.Panel(panel)
midPan.SetBackgroundColour((100,0,0))
pan2 = wx.Panel(panel)
pan2.SetBackgroundColour((0,100,0))
vbox.Add(pan2, 1, wx.EXPAND | wx.ALL)
vbox.Add(midPan, 5, wx.EXPAND | wx.ALL)
hbox = wx.BoxSizer(wx.VERTICAL)
hbox1=wx.BoxSizer(wx.VERTICAL)
h1=wx.Panel(midPan)
h1.SetBackgroundColour((200,0,100))
h2=wx.Panel(midPan)
h2.SetBackgroundColour((0,100,200))
#######################################################################
self.text1 = wx.StaticText(h1, label="Welcome ") #insert the global variable in this label after the user has successfully logged in
text2 = wx.StaticText(h1, label="Label2")
hbox.Add(h1,3,wx.EXPAND|wx.ALL,20)
hbox.Add(h2,1,wx.EXPAND|wx.ALL,20)
hbox1.Add(self.text1,1,wx.ALIGN_LEFT)
hbox1.Add(text2,1,wx.ALIGN_RIGHT)
panel.SetSizer(vbox)
midPan.SetSizer(hbox)
h1.SetSizer(hbox1)
self.statusbar = self.CreateStatusBar()
self.SetMenuBar(menubar)
self.Centre()
self.Maximize()
self.Show(True)
frame = LoginFrame(self)
frame.Show(True)
frame.MakeModal(True)
#----------------------------------------------------------------------
def myListener(self, msg):
""""""
self.text1.SetLabel("Welcome %s" % msg)
def OnExit(self, e):
self.Close()
class LoginFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent,style=wx.DEFAULT_FRAME_STYLE & ~(wx.RESIZE_BORDER | wx.MAXIMIZE_BOX | wx.CLOSE_BOX | wx.MINIMIZE_BOX ), title = "System Login")
self.panel = wx.Panel(self)
self.userlabel = wx.StaticText(self.panel, label="Username:")
self.passlabel = wx.StaticText(self.panel, label="Password:")
self.userbox = wx.TextCtrl(self.panel, size=(140, -1))
self.passbox = wx.TextCtrl(self.panel, size=(140, -1), style=wx.TE_PASSWORD)
self.login = wx.Button(self.panel, label="Login")
self.exit = wx.Button(self.panel, label="Exit")
self.Centre()
self.windowSizer = wx.BoxSizer()
self.windowSizer.Add(self.panel, 1, wx.ALL | wx.EXPAND)
self.sizer = wx.GridBagSizer(5, 5)
self.sizer.Add(self.userlabel, (0, 0))
self.sizer.Add(self.userbox, (0, 1))
self.sizer.Add(self.passlabel, (1, 0))
self.sizer.Add(self.passbox, (1, 1))
self.sizer.Add(self.login, (2, 0))
self.sizer.Add(self.exit, (2, 1))
self.border = wx.BoxSizer()
self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 5)
self.panel.SetSizerAndFit(self.border)
self.SetSizerAndFit(self.windowSizer)
self.login.Bind(wx.EVT_BUTTON, self.OnLogin)
self.exit.Bind(wx.EVT_BUTTON, self.onClose)
self.Bind(wx.EVT_CLOSE, self.onClose)
def OnLogin(self, e):
#db = MySQLdb.connect("localhost","root","","test" )
#cursor = db.cursor()
username = self.userbox.GetValue()
password = self.passbox.GetValue()
#self.MakeModal(False)
self.Close()
#try:
#cursor.execute("SELECT * FROM useraccounts WHERE username=%s" ,username)
#data = cursor.fetchall()
#for row in data:
#dbpass = row[2]
#if dbpass == password:
#
#e.Skip()
#self.Close()
#wx.MessageBox('You are now logged in', 'Info', wx.OK | wx.ICON_INFORMATION)
#else:
#wx.MessageBox('Username or Password is incorrect', 'Info', wx.OK | wx.ICON_INFORMATION)
#except:
#wx.MessageBox('Username or Password is incorrect', 'Info', wx.OK | wx.ICON_INFORMATION)
def onClose(self,e):
username = self.userbox.GetValue()
pub.sendMessage("update_frame", msg=username)
self.Destroy()
if __name__ == '__main__':
app = wx.App()
MainFrame()
app.MainLoop()
This code worked for me on Windows 7 with wxPython 2.9 and Python 2.7. Note that I commented out the SQL stuff to make it a little simpler to run. I also changed the event bindings slightly as OnExit wasn't firing for me.

wxpython layout with sizers

I'm having difficulty getting my sizers to work properly in wxpython. I am trying to do a simple one horizontal bar at top (with text in it) and two vertical boxes below (with gridsizers * the left one should only be 2 columns!! * inside each). I want the everything in the image to stretch and fit my panel as well (with the ability to add padding to sides and top/bottom).
I have two main issues:
1. I cant get the text in the horizontal bar to be in the middle (it goes to the left)
2. I would like to space the two vertical boxes to span AND fit the page appropriately (also would like the grids to span better too).
Here is my code (with some parts omitted):
self.LeagueInfoU = wx.Panel(self.LeagueInfo,-1, style=wx.BORDER_NONE)
self.LeagueInfoL = wx.Panel(self.LeagueInfo,-1, style=wx.BORDER_NONE)
self.LeagueInfoR = wx.Panel(self.LeagueInfo,-1, style=wx.BORDER_NONE)
vbox = wx.BoxSizer(wx.VERTICAL)
hbox1 = wx.BoxSizer(wx.HORIZONTAL)
hbox2 = wx.BoxSizer(wx.HORIZONTAL)
vbox2a = wx.GridSizer(12,2,0,0)
vbox3a = wx.GridSizer(10,3,0,0)
hbox1a = wx.BoxSizer(wx.VERTICAL)
vbox2 = wx.BoxSizer(wx.VERTICAL)
vbox3 = wx.BoxSizer(wx.VERTICAL)
hbox1.Add(self.LeagueInfoU, 1, wx.EXPAND | wx.ALL, 3)
vbox2.Add(self.LeagueInfoL, 1, wx.EXPAND | wx.ALL, 3)
vbox3.Add(self.LeagueInfoR, 1, wx.EXPAND | wx.ALL, 3)
vbox2a.AddMany([this is all correct])
self.LeagueInfoL.SetSizer(vbox2a)
vbox3a.AddMany([this is all correct])
self.LeagueInfoR.SetSizer(vbox3a)
font = wx.Font(20, wx.DEFAULT, wx.NORMAL, wx.BOLD)
self.Big_Header = wx.StaticText(self.LeagueInfoU, -1, 'Testing This')
self.Big_Header.SetFont(font)
hbox1a.Add(self.Big_Header, 0, wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL)
self.LeagueInfoU.SetSizer(hbox1a)
hbox2.Add(vbox2, 0, wx.EXPAND)
hbox2.Add(vbox3, 0, wx.EXPAND)
vbox.Add(hbox1, 0, wx.EXPAND)
vbox.Add(hbox2, 1, wx.EXPAND)
self.LeagueInfo.SetSizer(vbox)
Is this what you're after?
import wx
class Frame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.panel = wx.Panel(self)
main_sizer = wx.BoxSizer(wx.VERTICAL)
# Title
self.centred_text = wx.StaticText(self.panel, label="Title")
main_sizer.Add(self.centred_text, 0, wx.ALIGN_CENTRE | wx.ALL, 3)
# Grids
content_sizer = wx.BoxSizer(wx.HORIZONTAL)
grid_1 = wx.GridSizer(12, 2, 0, 0)
grid_1.AddMany(wx.StaticText(self.panel, label=str(i)) for i in xrange(24))
content_sizer.Add(grid_1, 1, wx.EXPAND | wx.ALL, 3)
grid_2 = wx.GridSizer(10, 3, 0, 0)
grid_2.AddMany(wx.StaticText(self.panel, label=str(i)) for i in xrange(30))
content_sizer.Add(grid_2, 1, wx.EXPAND | wx.ALL, 3)
main_sizer.Add(content_sizer, 1, wx.EXPAND)
self.panel.SetSizer(main_sizer)
self.Show()
if __name__ == "__main__":
app = wx.App(False)
Frame(None)
app.MainLoop()
something like this??
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,-1,"Test Stretching!!")
p1 = wx.Panel(self,-1,size=(500,100))
p1.SetMinSize((500,100))
p1.SetBackgroundColour(wx.GREEN)
hsz = wx.BoxSizer(wx.HORIZONTAL)
p2 = wx.Panel(self,-1,size=(200,400))
p2.SetMinSize((200,400))
p2.SetBackgroundColour(wx.RED)
p3 = wx.Panel(self,-1,size=(300,400))
p3.SetMinSize((300,400))
p3.SetBackgroundColour(wx.BLUE)
hsz.Add(p2,1,wx.EXPAND)
hsz.Add(p3,1,wx.EXPAND)
sz = wx.BoxSizer(wx.VERTICAL)
sz.Add(p1,0,wx.EXPAND)
sz.Add(hsz,1,wx.EXPAND)
self.SetSizer(sz)
self.Layout()
self.Fit()
a = wx.App(redirect=False)
f = MyFrame()
f.Show()
a.MainLoop()
Here's one way to do it:
import wx
########################################################################
class MyPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
mainSizer = wx.BoxSizer(wx.VERTICAL)
hSizer = wx.BoxSizer(wx.HORIZONTAL)
leftGridSizer = wx.GridSizer(rows=10, cols=12, vgap=5, hgap=5)
rightGridSizer = wx.GridSizer(rows=10, cols=3, vgap=5, hgap=5)
title = wx.StaticText(self, label="Main title")
mainSizer.Add(wx.StaticText(self), 0, wx.EXPAND) # add a "spacer"
mainSizer.Add(title, 0, wx.CENTER, wx.ALL, 10)
for row in range(1, 11):
for col in range(1, 13):
lbl = "Row%s Col%s" % (row, col)
leftGridSizer.Add(wx.StaticText(self, label=lbl))
hSizer.Add(leftGridSizer, 0, wx.ALL, 20)
for row in range(1, 11):
for col in range(1, 4):
lbl = "Row%s Col%s" % (row, col)
rightGridSizer.Add(wx.StaticText(self, label=lbl))
hSizer.Add(rightGridSizer, 0, wx.ALL, 20)
mainSizer.Add(hSizer)
self.SetSizer(mainSizer)
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Sizers", size=(1600,600))
panel = MyPanel(self)
self.Show()
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
To learn about spanning rows, I recommend looking at the wxPython demo. I think that may only be supported in wx.GridBagSizer and the FlexGridSizer though. You can try the span parameter though. Also, it should be noted that wx.GROW and wx.EXPAND are one and the same. You might also want to check out the wiki for more information: http://wiki.wxpython.org/GridBagSizerTutorial

Categories