wxPython How to make a dynamic scrolled panel - python

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

Related

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

Update/Refresh Dynamically–Created WxPython Widgets

New python programmer here and trying to learn how to dynamically update widgets. To start, I have the following code. What I would like to do is change my variable "self.dynamiclength" to any integer, and have WxPython update the number of widgets appropriately. I have tried putting self.Refresh() and self.Update() in my TestFrame after updating self.dynamiclength to no avail.
I have done as much reading as possible on this before resorting to asking for help, but I am just too new at Wx to solve this one on my own. Thank you much!
import wx
import wx.lib.scrolledpanel as scrolled
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, size=(1000, 550))
panel = wx.Panel(self)
mainSizer = wx.BoxSizer(wx.VERTICAL)
pbox0 = wx.BoxSizer(wx.VERTICAL)
controlback0 = wx.Button(panel, label="Back0")
controlforward0 = wx.Button(panel, label="Forward0")
pbox0.Add(controlback0, 0, wx.ALL)
pbox0.Add(controlforward0, 0, wx.ALL)
mainSizer.Add(pbox0)
self.scrolling_window = scrolled.ScrolledPanel( panel )
self.scrolling_window.SetAutoLayout(1)
self.scrolling_window.SetupScrolling()
self.sizer = wx.BoxSizer( wx.VERTICAL )
self.child_windows = []
##############################################
#this is the variable that I want to change,
#and I don't know how to get the 'for loop'
#below to update as well.
self.eedictionary = {}
self.dynamiclength = 5
for i in range(0,self.dynamiclength):
wind = self.addBox(i)
self.sizer.Add(wind, 0, wx.CENTER|wx.ALL, 5)
###############################################
#the following code binds all appropriate buttons to a pedigree variable updater
button_binding_list = ['controlback','controlforward']
for j in button_binding_list:
eid = self.eedictionary[str(i)+j]
self.scrolling_window.Bind(wx.EVT_BUTTON, lambda evt: self.onclick(evt, id), id=eid)
self.scrolling_window.SetSizer(self.sizer)
mainSizer.Add(self.scrolling_window, 1, wx.EXPAND)
panel.SetSizer(mainSizer)
def addBox(self, i):
pbox = wx.BoxSizer(wx.VERTICAL)
controlback = wx.Button(self.scrolling_window, label="Back")
controlforward = wx.Button(self.scrolling_window, label="Forward")
pbox.AddMany([(controlback, 0, wx.ALL), (controlforward, 0, wx.ALL)])
#for each object created in the addBox module, its id is added to the dictionary
self.eedictionary[str(i)+'controlback'] = controlback.GetId()
self.eedictionary[str(i)+'controlforward'] = controlforward.GetId()
return pbox
def onclick(self, event):
self.dynamiclength +=1
print 'added one to self.dynamiclength', self.dynamiclength
if __name__=='__main__':
app = wx.App(False)
f = TestFrame()
f.Show()
app.MainLoop()
I have similar test code which I have written some time ago. Maybe you will find it useful.
import wx
#===================================================================================================
class UpperPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.combo = wx.ComboBox(self, choices=["0", "1", "2", "3", "4"], size=(200, -1))
self.combo.Bind(wx.EVT_COMBOBOX, self.GetParent().middlePanel.Change)
self.logo = wx.Button(self, size=(300, 100))
self.sizer = wx.BoxSizer()
self.sizer.Add(self.combo, 0, wx.EXPAND)
self.sizer.Add(self.logo, 0, wx.EXPAND)
self.SetSizerAndFit(self.sizer)
#===================================================================================================
class MiddlePanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.subs = []
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizerAndFit(self.sizer)
def Change(self, e):
self.sizer = wx.BoxSizer(wx.VERTICAL)
for a in self.subs:
a.Destroy()
self.subs = []
for a in range(int(e.GetString())):
b = wx.Button(self, size=(-1, 50))
self.subs.append(b)
self.sizer.Add(b, 1, wx.EXPAND)
self.SetSizerAndFit(self.sizer)
self.GetParent().Fit()
#===================================================================================================
class MainWin(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.middlePanel = MiddlePanel(self)
self.upperPanel = UpperPanel(self)
self.textArea = wx.TextCtrl(self, size=(-1, 300), style=wx.TE_MULTILINE)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.upperPanel, 0, wx.EXPAND)
self.sizer.Add(self.middlePanel, 0, wx.EXPAND)
self.sizer.Add(self.textArea, 1, wx.EXPAND)
self.SetSizerAndFit(self.sizer)
#===================================================================================================
if __name__ == '__main__':
app = wx.PySimpleApp()
main_win = MainWin()
main_win.Show()
app.MainLoop()
If you need to update the number of widgets AFTER you've already created and shown the application, the you'll need to do it in a method, NOT in the init. The init only runs the first time the application is instantiated. Whenever you add or remove widgets after the frame is shown, you'll need to call Layout() on the parent widget or its sizer. See also
http://wxpython-users.1045709.n5.nabble.com/dynamically-adding-amp-removing-widgets-td2342432.html
https://groups.google.com/forum/?fromgroups#!topic/wxPython-users/eQjlYlsw4qs
Adding a widget with a button - wxPython

Destroy() wxpython simple error?

i am having an interesting problem,
This program is a simple image viewer and it can contain different images in a listbox. The listbox contains the names of the images. You can load an image to an item in the listbox. You can click any item on the listbox to see its image. For some reason Destroy() is not functioning properly. Please run the following code if you are unable to understand me,
IMAGE_NAME=[]
IMAGE_DATA=[]
IMAGE_LISTSEL=[]
import sys
import wx
def deletepic(self,parent):
try:
bitmap1.Destroy()
bmp1.Destroy()
except:
print sys.exc_info()
def sendnewpic(self,parent):
global scroll_img
deletepic(self,parent)
print IMAGE_DATA[IMAGE_LISTSEL[0]]
if IMAGE_DATA[IMAGE_LISTSEL[0]]!='':
try:
print IMAGE_DATA[IMAGE_LISTSEL[0]]
bmp1 = wx.Image(IMAGE_DATA[IMAGE_LISTSEL[0]], wx.BITMAP_TYPE_ANY).ConvertToBitmap()
bitmap1 = wx.StaticBitmap(scroll_img, -1, bmp1, (0, 0))
except:
pass
def areachange(self,pg):
print pg
try:
if IMAGE_DATA[IMAGE_LISTSEL[0]]=='':
deletepic(self,parent)
except:
pass
if pg=="Images":
self.images_area.Show()
else:
self.images_area.Hide()
class imageMax(wx.Panel):
pass
class imageTab(imageMax):
def imagesel(self,parent):
IMAGE_LISTSEL[:] = []
IMAGE_LISTSEL.append(self.listBox.GetSelection())
sendnewpic(self,parent)
def newAddImage(self,parent):
IMAGE_NAME.append('hi');
IMAGE_DATA.append('');
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(len(IMAGE_NAME)-1)
self.imagesel(self) #making it a selected image, globally
def reName(self,parent):
sel = self.listBox.GetSelection()
text = self.listBox.GetString(sel)
renamed = wx.GetTextFromUser('Rename item', 'Rename dialog', text)
if renamed != '':
IMAGE_NAME.pop(sel)
IMAGE_NAME.insert(sel,renamed)
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(sel)
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.listBox = wx.ListBox(self, size=(200, -1), choices=IMAGE_NAME, style=wx.LB_SINGLE)
self.sizer = wx.BoxSizer(wx.VERTICAL)
btnSizer = wx.BoxSizer(wx.VERTICAL) #change to horizontal for side by side
self.sizerMain = wx.BoxSizer()
self.listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.reName)
self.listBox.Bind(wx.EVT_LISTBOX, self.imagesel)
btn = wx.Button(self, label="Create New",size=(200, 40))
btnTwo = wx.Button(self, label="Test 2",size=(200, 40))
btn.Bind(wx.EVT_BUTTON, self.newAddImage)
self.sizer.Add(self.listBox, proportion=1, flag=wx.TOP | wx.EXPAND | wx.LEFT, border=5)
btnSizer.Add(btn, 0, wx.ALL, 5)
btnSizer.Add(btnTwo, 0, wx.ALL, 5)
self.sizer.Add(btnSizer)
self.sizerMain.Add(self.sizer, proportion=0, flag=wx.BOTTOM | wx.EXPAND, border=0)
self.SetSizer(self.sizerMain)
class MyNotebook(wx.Notebook):
def __init__(self, *args, **kwargs):
wx.Notebook.__init__(self, *args, **kwargs)
class MyPanel(imageTab):
def OnClickTop(self, event):
scroll_img.Scroll(600, 400)
def OnClickBottom(self, event):
scroll_img.Scroll(1, 1)
def OnPageChanged(self, event):
new = event.GetSelection()
areachange(self,self.notebook.GetPageText(new))
event.Skip()
def OnPageChanging(self, event):
event.Skip()
def onOpenFile(self,parent):
""" Open a file"""
filename = wx.FileSelector()
if (filename!=''):
global bitmap1,bmp1,scroll_img
if IMAGE_DATA[IMAGE_LISTSEL[0]]!='':
deletepic(self,parent)
bmp1 = wx.Image(filename, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
bitmap1 = wx.StaticBitmap(scroll_img, -1, bmp1, (0, 0))
scroll_img.SetScrollbars(1, 1, bmp1.GetWidth(), bmp1.GetHeight())
IMAGE_DATA[IMAGE_LISTSEL[0]]=filename
print IMAGE_DATA
def __init__(self, *args, **kwargs):
global bitmap1,bmp1,scroll_img
wx.Panel.__init__(self, *args, **kwargs)
self.notebook = MyNotebook(self, size=(225, -1))
# self.button = wx.Button(self, label="Something else here? Maybe!")
tab_images = imageTab(self.notebook)
# add the pages to the notebook with the label to show on the tab
self.notebook.AddPage(tab_images, "Pics",select=True)
scroll_img = wx.ScrolledWindow(self, -1)
scroll_img.SetScrollbars(1, 1, 600, 400)
#self.button = wx.Button(scroll_img, -1, "Scroll Me", pos=(50, 20))
#self.Bind(wx.EVT_BUTTON, self.OnClickTop, self.button)
#self.button2 = wx.Button(scroll_img, -1, "Scroll Back", pos=(500, 350))
#self.Bind(wx.EVT_BUTTON, self.OnClickBottom, self.button2)
self.images_area=wx.StaticBox(self, -1, '')
self.sizerBox = wx.StaticBoxSizer(self.images_area,wx.HORIZONTAL)
#self.load_file=wx.Button(self, label='Load File')
#self.sizerBox.Add(self.load_file,0,wx.ALL,5)
self.sizerBox2 = wx.BoxSizer()
self.sizerBox.Add(scroll_img, 1, wx.EXPAND|wx.ALL, 10)
self.sizerBox2.Add(self.sizerBox, 1, wx.EXPAND|wx.ALL, 10)
self.sizer = wx.BoxSizer()
self.sizer.Add(self.notebook, proportion=0, flag=wx.EXPAND)
# self.sizer.Add(self.button, proportion=0)
btnSizer = wx.BoxSizer() #change to horizontal for side by side
btnTwo = wx.Button(self, label="Load File",size=(200, 40))
btnTwo.Bind(wx.EVT_BUTTON,self.onOpenFile)
bmp1 = None
bitmap1 = None
btnSizer.Add(btnTwo, 0, wx.TOP, 15)
self.sizerBox2.Add(btnSizer)
#self.sizerBox.Add(self.bitmap1)
self.sizer.Add(self.sizerBox2, proportion=1, flag=wx.EXPAND)
self.SetSizer(self.sizer)
self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
areachange(self,self.notebook.GetPageText(0))
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = MyPanel(self)
self.Show()
app = wx.App(False)
win = MainWindow(None, size=(600, 400))
app.MainLoop()
Try this to see what the error is,
1.you press create new (any amount of times)
2.press, load file
3. click any item on the listbox (except for the one that you are in)
4. Then go back to the orginal item that you were in,
5. Then click any item, other than the one you are currently in
There will be some sort of problem, the image does not destroy itself and returns an error like the following:
(, PyDeadObjectError('The C++ part of the StaticBitmap object has been deleted, attribute access no longer allowed.',), )
I am still able to load images but the previous images do not delete.
It is hard to word this problem, if anyone can help me with this situation, it would be greatly appreciated. If you need further explanation please comment. I thank you greatly for viewing.
Here you have your code fixed to clear your current image when loading another one.
This is done basically using self.parent.bitmap.Destroy().
I modified some few things without changing the structure of your code, in order for you to recognize changes. I eliminated globals calls. Look how I also eliminated the global IMAGE_LISTSEL variable and converted it in a class attribute. That is what Robin and Fenikso were telling you. Try to do the same with IMAGE_NAME and IMAGE_DATA.
Although the code is working, it is still far from being acceptable wxpython code. You can get many examples of correctly written wxpython code in the web. If you can afford it I recommend to you wxPython in Action from Noel Rappin and Robin Dunn.
IMAGE_NAME = []
IMAGE_DATA = []
import sys
import wx
def deletepic(self):
try:
self.parent.bitmap.Destroy()
except:
print sys.exc_info()
def sendnewpic(self):
if self.parent.bitmap: deletepic(self)
if IMAGE_DATA[self.image_listsel] != '':
try:
print IMAGE_DATA[self.image_listsel]
bmp = wx.Image(IMAGE_DATA[self.image_listsel], wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.parent.scroll_img.SetScrollbars(1, 1, bmp.GetWidth(), bmp.GetHeight())
self.parent.bitmap = wx.StaticBitmap(self.parent.scroll_img, -1, bmp, (0, 0))
self.parent.Refresh()
except:
pass
def areachange(self, pg):
print pg
try:
if IMAGE_DATA[self.image_listsel] == '':
deletepic(self)
except:
pass
if pg == "Images":
self.images_area.Show()
else:
self.images_area.Hide()
class imageTab(wx.Panel):
def __init__(self, parent, grandparent):
wx.Panel.__init__(self, parent)
self.parent = grandparent
self.image_listsel = 0
self.listBox = wx.ListBox(self, size=(200, -1), choices=IMAGE_NAME, style=wx.LB_SINGLE)
self.sizer = wx.BoxSizer(wx.VERTICAL)
btnSizer = wx.BoxSizer(wx.VERTICAL) #change to horizontal for side by side
self.sizerMain = wx.BoxSizer()
self.listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.reName)
self.listBox.Bind(wx.EVT_LISTBOX, self.imagesel)
btn = wx.Button(self, label="Create New",size=(200, 40))
btnTwo = wx.Button(self, label="Test 2",size=(200, 40))
btn.Bind(wx.EVT_BUTTON, self.newAddImage)
self.sizer.Add(self.listBox, proportion=1, flag=wx.TOP | wx.EXPAND | wx.LEFT, border=5)
btnSizer.Add(btn, 0, wx.ALL, 5)
btnSizer.Add(btnTwo, 0, wx.ALL, 5)
self.sizer.Add(btnSizer)
self.sizerMain.Add(self.sizer, proportion=0, flag=wx.BOTTOM | wx.EXPAND, border=0)
self.SetSizer(self.sizerMain)
def imagesel(self, evt):
self.image_listsel = self.listBox.GetSelection()
sendnewpic(self)
def newAddImage(self, evt):
IMAGE_NAME.append('hi')
IMAGE_DATA.append('')
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(len(IMAGE_NAME)-1)
self.imagesel(None) #making it a selected image, globally
def reName(self,parent):
sel = self.listBox.GetSelection()
text = self.listBox.GetString(sel)
renamed = wx.GetTextFromUser('Rename item', 'Rename dialog', text)
if renamed != '':
IMAGE_NAME.pop(sel)
IMAGE_NAME.insert(sel,renamed)
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(sel)
class MyPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.notebook = wx.Notebook(self, size=(225, -1))
#
self.tab_images = imageTab(self.notebook, self)
# add the pages to the notebook with the label to show on the tab
self.notebook.AddPage(self.tab_images, "Pics", select=True)
self.scroll_img = wx.ScrolledWindow(self, -1)
self.scroll_img.SetScrollbars(1, 1, 600, 400)
self.images_area = wx.StaticBox(self, -1, '')
self.sizerBox = wx.StaticBoxSizer(self.images_area, wx.HORIZONTAL)
self.sizerBox2 = wx.BoxSizer()
self.sizerBox.Add(self.scroll_img, 1, wx.EXPAND|wx.ALL, 10)
self.sizerBox2.Add(self.sizerBox, 1, wx.EXPAND|wx.ALL, 10)
self.sizer = wx.BoxSizer()
self.sizer.Add(self.notebook, proportion=0, flag=wx.EXPAND)
#
btnSizer = wx.BoxSizer() #change to horizontal for side by side
btnTwo = wx.Button(self, label="Load File", size=(200, 40))
btnTwo.Bind(wx.EVT_BUTTON, self.onOpenFile)
self.bmp = None
self.bitmap = None
btnSizer.Add(btnTwo, 0, wx.TOP, 15)
self.sizerBox2.Add(btnSizer)
#
self.sizer.Add(self.sizerBox2, proportion=1, flag=wx.EXPAND)
self.SetSizer(self.sizer)
self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
areachange(self, self.notebook.GetPageText(0))
def OnClickTop(self, event):
self.scroll_img.Scroll(600, 400)
def OnClickBottom(self, event):
self.scroll_img.Scroll(1, 1)
def OnPageChanged(self, event):
new = event.GetSelection()
areachange(self, self.notebook.GetPageText(new))
event.Skip()
def OnPageChanging(self, event):
event.Skip()
def onOpenFile(self, evt):
""" Open a file"""
filename = wx.FileSelector()
if filename != '':
IMAGE_DATA[ self.tab_images.image_listsel] = filename
self.tab_images.imagesel(None)
print IMAGE_DATA
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = MyPanel(self)
self.Show()
app = wx.App(False)
win = MainWindow(None, size=(600, 400))
app.MainLoop()
Sometimes you are using bmp1 and bitmap1 as local variables and sometimes as globals. Since you are making multiple instances of them without saving the prior references anywhere then you are losing your references to the already existing objects. When you Destroy() them then you are only destroying the most recently created instances.
Try adding them to some sort of collection (like a list) instead and then you can access any of the items from the list when you need them later. Also try to avoid using global variables. Store your variables in the object instances that they belong to.

wxPython - Getting attribute from another class?

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

Categories