ultimatelistctrl: list index out of range - python

I am using the UltimateListCtrl in wxPython. I can get the demo code on Mouse vs Python to work, but when i try to adapt it. Or, even copy and paste it into my program it fails with:
Traceback (most recent call last):
File "C:\Users\dgunter\Documents\GitHub\TimeJobTracker\timejob.py", line 346, in ArchiveJobs
jobwin = ArchiveJobWindow(self)
File "C:\Users\dgunter\Documents\GitHub\TimeJobTracker\timejob.py", line 87, in __init__
mylist.SetStringItem(0, 1, "Go")
File "C:\Python27\lib\site-packages\wx-3.0-msw\wx\lib\agw\ultimatelistctrl.py", line 11366, in SetStringItem
self._mainWin.SetItem(info)
File "C:\Python27\lib\site-packages\wx-3.0-msw\wx\lib\agw\ultimatelistctrl.py", line 8802, in SetItem
line.SetItem(item._col, item)
File "C:\Python27\lib\site-packages\wx-3.0-msw\wx\lib\agw\ultimatelistctrl.py", line 4095, in SetItem
item = self._items[index]
IndexError: list index out of range
Here is the code I am working with, you will notice the list items are copied from the link I gave. My actual data is loaded form a database I just left that part out.
import wx
from wx.lib.agw import ultimatelistctrl as ULC
class ArchiveJobWindow(wx.Frame):
def __init__(self, parent):
super(ArchiveJobWindow, self).__init__(parent, title='Archive Unused Jobs', style=wx.CAPTION, size=(650,350))
self.parent = parent
self.panel = wx.Panel(self, wx.ID_ANY)
topSizer = wx.BoxSizer(wx.VERTICAL)
buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldfont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldfont.SetWeight(wx.BOLD)
boldfont.SetPointSize(12)
mylist = ULC.UltimateListCtrl(self.panel, agwStyle=wx.LC_REPORT|wx.LC_VRULES|wx.LC_HRULES|wx.LC_SINGLE_SEL|0x0010, size=(200, 100))
info = ULC.UltimateListItem()
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT | ULC.ULC_MASK_CHECK
info._image = []
info._format = 0
info._kind = 1
info._text = "Artist Name"
mylist.InsertColumnInfo(0, info)
info = ULC.UltimateListItem()
info._format = wx.LIST_FORMAT_RIGHT
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT | ULC.ULC_MASK_FONT
info._image = []
info._text = "Title"
info._font = boldfont
mylist.InsertColumnInfo(1, info)
info = ULC.UltimateListItem()
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
info._format = 0
info._text = "Genre"
info._font = font
info._image = []
mylist.InsertColumnInfo(2, info)
mylist.InsertStringItem(0, "Newsboys")
mylist.SetStringItem(0, 1, "Go")
mylist.SetStringItem(0, 2, "Rock")
mylist.InsertStringItem(1, "Puffy")
mylist.SetStringItem(1, 1, "Bring It!")
mylist.SetStringItem(1, 2, "Pop")
mylist.InsertStringItem(2, "Family Force 5")
mylist.SetStringItem(2, 1, "III")
mylist.SetStringItem(2, 2, "Crunk")
mylist.SetColumnWidth(0, 150)
mylist.SetColumnWidth(1, 200)
mylist.SetColumnWidth(2, 100)
cancelbutton = wx.Button(self.panel, id=wx.ID_CANCEL)
Okbutton = wx.Button(self.panel, id=wx.ID_OK)
# panelSizer.Add(self.panel, 1, wx.EXPAND)
buttonSizer.AddStretchSpacer(6)
buttonSizer.Add(cancelbutton, 1, wx.ALIGN_RIGHT, 0)
buttonSizer.Add((20,0))
buttonSizer.Add(Okbutton, 1, wx.ALIGN_RIGHT, 0)
buttonSizer.AddStretchSpacer(1)
topSizer.AddStretchSpacer(2)
topSizer.Add(mylist)
topSizer.AddStretchSpacer(2)
topSizer.Add(buttonSizer, 1, wx.ALIGN_RIGHT, 0)
topSizer.Add((0,20))
# panelSizer.Add(topSizer, 1, wx.EXPAND)
self.panel.SetSizer(topSizer)
app = wx.App(False)
window = ArchiveJobWindow(None)
window.Show()
app.MainLoop()
Any advice is welcome.
Thanks

I do not understand why, but it makes it work if you change this line:
mylist = ULC.UltimateListCtrl(self.panel, agwStyle=wx.LC_REPORT|wx.LC_VRULES|wx.LC_HRULES|wx.LC_SINGLE_SEL|0x0010, size=(200, 100))
to this:
mylist = ULC.UltimateListCtrl(self.panel, agwStyle=ULC.ULC_REPORT, size=(200, 100))
It makes since because in order to get the example to even run I had to add the 0x0010
I think the examples must be outdated.

Related

How do I make my wxPython scrolledPanel scroll to the bottom of the window?

Problem: I want to be able to force the scrollbar to the bottom when I call createNewRow(). I can see that the self.Scroll(0, self.scrollRange) is occurring in OnKeyTyped() and the scrollbar moves to the bottom of the window but then the scrollbar moves to the top of the window again. I tried to stop this by binding wx.EVT_SCROLLWIN to OnScroll which calls event.Skip() but it appears that this has not worked. I am out of ideas on how to proceed as I don't know enough about eventHandlers and scroll events in wxPython. Any help on how to proceed would be much appreciated. Full code below.
import os
import wx
import datetime as dt
import wx.lib.scrolledpanel as scrolled
class MyFrame(wx.Frame):
width = 1000
height = 600
today = dt.date.today()
today_date = f"{today:%A - %d %B %Y}"
filename = f"Worklog {today_date}"
wxTHICK_LINE_BORDER = 3
def __init__(self, parent=None, title=filename, size=(width,height - 1)):
wx.Frame.__init__(self, parent=parent, title=title, size=size)
self.parent = parent
self.title = title
self.size = size
self.BuildMenuBar()
def BuildMenuBar(self):
# Menu bar
self.menuBar = wx.MenuBar()
self.fileMenu = wx.Menu()
self.NewOpt = wx.MenuItem(self.fileMenu, wx.ID_NEW, '&New\tCtrl+N')
self.OpenOpt = wx.MenuItem(self.fileMenu, wx.ID_OPEN, '&Open\tCtrl+O')
self.SaveOpt = wx.MenuItem(self.fileMenu, wx.ID_SAVE, '&Save\tCtrl+S')
self.QuitOpt = wx.MenuItem(self.fileMenu, wx.ID_EXIT, '&Quit\tCtrl+Q')
self.fileMenu.Append(self.NewOpt)
self.fileMenu.Append(self.OpenOpt)
self.fileMenu.Append(self.SaveOpt)
self.fileMenu.Append(self.QuitOpt)
self.Bind(wx.EVT_MENU, self.OnQuit, self.QuitOpt)
self.menuBar.Append(self.fileMenu, '&File')
self.SetMenuBar(self.menuBar)
def OnQuit(self, e):
self.Close()
class MyPanel(wx.Panel):
def __init__(self,parent):
wx.Panel.__init__(self, parent=parent)
self.parent = parent
self.size = parent.size
panel_colour = wx.Colour(240, 240, 240, 255)
self.SetBackgroundColour(panel_colour)
self.Refresh()
class MyScrolledPanel(scrolled.ScrolledPanel):
def __init__(self, parent):
scrolled.ScrolledPanel.__init__(self, parent=parent, style = wx.TAB_TRAVERSAL | wx.TB_BOTTOM)
self.parent = parent
# self.size = parent.size
self.width = parent.size[0]
self.height = parent.size[1]
scrollpanel_colour = wx.Colour(255, 255, 255, 255)
self.SetBackgroundColour(scrollpanel_colour)
# Call a refresh to update the UI
self.Refresh()
self.SetAutoLayout(True)
self.SetupScrolling()
self.InitUI()
self.Bind(wx.EVT_SCROLLWIN, self.OnScroll, self)
self.Bind(wx.EVT_SIZE, self.OnSize, self)
def OnScroll(self, e):
e.Skip()
def InitUI(self):
vgap = 0
hgap = 0
self.rowList = []
self.n = 0
self.scrollSizer = wx.GridBagSizer(vgap + 10, hgap + 10)
self.row = self.CreateNewRow(self.n)
self.rowList.append(self.row)
print(f"Row List: {self.rowList[-1]}")
self.scrollSizer.Add(self.row[0], pos = (self.i, 0), flag = wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL, border = 10)
self.scrollSizer.Add(self.row[1], pos = (self.i, 1), flag = wx.EXPAND | wx.TOP | wx.RIGHT | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL , border = 10)
self.scrollSizer.AddGrowableCol(1)
self.SetSizer(self.scrollSizer)
self.panelSizer = wx.GridBagSizer(vgap, hgap)
self.panelSizer.AddGrowableRow(0)
self.panelSizer.AddGrowableCol(0)
self.panelSizer.Add(self, pos = (0, 0), flag = wx.EXPAND, border = 0) # Add wx.Window not wx.Sizer
self.parent.SetSizer(self.panelSizer)
def CreateNewRow(self, number):
self.i = number
self.txtStr = "%02d" % (self.i+1) + ". "
self.staticText = wx.StaticText(self, wx.ID_ANY, self.txtStr)
#pos = (x, y)
#self.staticText.SetForegroundColour(wx.Colour(0,0,0))
self.control = wx.TextCtrl(self, self.i)
self.control.SetMaxLength(256)
self.text_history_length = 0
self.control.Bind(wx.EVT_TEXT, self.OnKeyTyped, id = self.i)
#self.control = wx.TextCtrl(self, -1, pos = (x + w + 5,y) )
#style = wx.TE_MULTILINE
elems = [self.staticText, self.control]
return elems
def OnSize(self, e):
self.width, self.height = e.GetSize()
self.SetSize((self.width, self.height))
self.OnSizeChange()
self.Refresh()
def OnSizeChange(self):
# Fit child elements
self.scrollSizer.FitInside(self)
# Resize layout
self.Layout()
# Resize scrolling
self.SetupScrolling()
def OnKeyTyped(self, e):
self.text_length = len(e.GetString())
if (self.text_history_length == 1 and self.text_length == 0):
print(f"History length: {self.text_history_length}")
print(f"Text length: {self.text_length}")
self.text_history_length = self.text_length
pass
elif (self.text_history_length == 0 and self.text_length == 1):
print(f"History length: {self.text_history_length}")
print(f"Text length: {self.text_length}")
self.n += 1
self.row = self.CreateNewRow(self.n)
print(f"Action: {self.row}")
print(f"Row List: {self.rowList[-1]}")
self.rowList.append(self.row)
self.scrollSizer.Add(self.row[0], pos = (self.n, 0), flag = wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL, border = 10)
self.scrollSizer.Add(self.row[1], pos = (self.n, 1), flag = wx.EXPAND | wx.TOP | wx.RIGHT | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL , border = 10)
self.SetupScrolling()
self.text_history_length = self.text_length
self.rowList[self.n-1][1].Bind(wx.EVT_TEXT, None, id = self.n-1)
self.text_history_length = 0
else:
print(f"History length: {self.text_history_length}")
print(f"Text length: {self.text_length}")
self.text_history_length = self.text_length
self.rowList[-1][1].SetFocus()
self.scrolledPanelChild = self.GetChildren()[-1] # [ scrollPanel ]
self.ScrollChildIntoView(self.scrolledPanelChild)
self.OnSizeChange()
self.scrollRange = self.GetScrollRange(wx.VERTICAL)
print(f"ScrollRange: {self.scrollRange}")
#self.scrollUnits = self.GetScrollPixelsPerUnit()
#print(f"ScrollUnit: {self.scrollUnits}")
#self.scrollThumb = self.GetScrollThumb(wx.VERTICAL)
#print(f"ScrollThumb: {self.scrollThumb}")
self.Scroll(0, self.scrollRange)
def main():
app = wx.App(False)
app.locale = wx.Locale(wx.Locale.GetSystemLanguage())
frame = MyFrame()
panel = MyPanel(frame)
scrolledPanel = MyScrolledPanel(panel)
frame.Show(True)
app.MainLoop()
if __name__ == "__main__":
main()
In :
def OnSizeChange(self):
# Fit child elements
self.scrollSizer.FitInside(self)
# Resize layout
self.Layout()
# Resize scrolling
self.SetupScrolling()
the SetupScrolling() event causes the scrollbar to reset to the top of the page. So if you comment this the Scroll(0, self.scrollRange) will scroll the scrollbar to the bottom of the page. Improvement would be to get this call to happen after SetupScrolling so that it happens anyway. Maybe the call to SetupScrolling is not necessary in anycase and Layout() is enough.
Old question but still. This can be solved using:
wx.CallAfter(self._scrolled_panel.ScrollChildIntoView, new_text) Or calling any of the other scroll methods from the CallAfter.
or
SetupScrolling(scrollIntoView=True, scrollToTop=False)

WxPython. ListCtrl. Insert button - Python

1) It is possible to insert somehow the button in each line of the certain column of object wx.ListCtrl?
2) Why is it possible to edit the value of a string only in the first column? Initializing wx.ListCtrl:
self.m_listCtrl_number = wx.ListCtrl(self.panel, wx.ID_ANY, wx.DefaultPosition, (450, 80),
wx.LC_REPORT | wx.BORDER_SUNKEN | wx.LC_EDIT_LABELS | wx.LC_SINGLE_SEL)
self.bsizer_textCtrl.Add(self.m_listCtrl_number, 0, wx.ALL, 5)
self.m_listCtrl_number.InsertColumn(0, 'Min', format=wx.LIST_FORMAT_CENTER, width=wx.LIST_AUTOSIZE)
self.m_listCtrl_number.InsertColumn(1, 'Max', format=wx.LIST_FORMAT_CENTER, width=wx.LIST_AUTOSIZE)
self.m_listCtrl_number.InsertColumn(2, 'Fill', format=wx.LIST_FORMAT_CENTER, width=wx.LIST_AUTOSIZE_USEHEADER)
Adding an item:
self.m_listCtrl_number.InsertItem(self.index_numer, '0')
self.m_listCtrl_number.SetItem(self.index_numer, 1, '1')
self.m_listCtrl_number.SetItem(self.index_numer, 2, 'qwerty') # fixme Button!!!
self.index_numer += 1
You will have to use ultimatelistctrl.
Here is a hacked version of one of the demonstration programs:
import wx
from wx.lib.agw import ultimatelistctrl as ULC
class TestPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldfont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldfont.SetWeight(wx.BOLD)
boldfont.SetPointSize(12)
self.ulc = ULC.UltimateListCtrl(self, agwStyle = wx.LC_REPORT
| wx.LC_VRULES
| wx.LC_HRULES
| ULC.ULC_HAS_VARIABLE_ROW_HEIGHT)
info = ULC.UltimateListItem()
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT | ULC.ULC_MASK_CHECK
info._image = []
info._format = 0
info._kind = 1
info._text = "Artist Name"
self.ulc.InsertColumnInfo(0, info)
info = ULC.UltimateListItem()
info._format = wx.LIST_FORMAT_RIGHT
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT | ULC.ULC_MASK_FONT
info._image = []
info._text = "Title"
info._font = boldfont
self.ulc.InsertColumnInfo(1, info)
info = ULC.UltimateListItem()
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
info._format = 0
info._text = "Genre"
info._font = font
info._image = []
self.ulc.InsertColumnInfo(2, info)
self.button1 = wx.Button(self.ulc, -1, "Button1")
self.button2 = wx.Button(self.ulc, -1, "Button2")
self.button3 = wx.Button(self.ulc, -1, "Button3")
index = self.ulc.InsertStringItem(3, " ")
self.ulc.SetItemWindow(index, 0, self.button1)
self.ulc.SetItemWindow(index, 1, self.button2)
self.ulc.SetItemWindow(index, 2, self.button3)
self.Bind(wx.EVT_BUTTON, self.OnButton)
self.ulc.InsertStringItem(0, "Newsboys")
self.ulc.SetStringItem(0, 1, "Go")
self.ulc.SetStringItem(0, 2, "Rock")
self.ulc.InsertStringItem(1, "Puffy")
self.ulc.SetStringItem(1, 1, "Bring It!")
self.ulc.SetStringItem(1, 2, "Pop")
self.ulc.InsertStringItem(2, "Family Force 5")
self.ulc.SetStringItem(2, 1, "III")
self.ulc.SetStringItem(2, 2, "Crunk")
self.ulc.SetColumnWidth(0, 150)
self.ulc.SetColumnWidth(1, 200)
self.ulc.SetColumnWidth(2, 100)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.ulc, 1, wx.EXPAND)
self.SetSizer(sizer)
def OnButton(self,event):
b= event.GetEventObject()
print(b.GetLabel(),"pressed")
########################################################################
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="MvP UltimateListCtrl Demo")
panel = TestPanel(self)
self.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App()
frame = TestFrame()
app.MainLoop()

wxPython : Soundboard panel [NOW PLAYING] just like in winamp

I am almost done my wxPython soundboard and want to implement one quick feature.
How does one add another panel to a wxPython window, and how do you implement the text, [ NOW PLAYING ] within that panel.
Here is my code so far:
import wx
import os
import pygame
pygame.init()
##SOUNDS##
##SOUNDS##
class windowClass(wx.Frame):
__goliathwav = pygame.mixer.Sound("goliath.wav")
__channelopen = pygame.mixer.Sound("channelopen.wav")
def __init__(self, *args, **kwargs):
super(windowClass,self).__init__(*args,**kwargs)
self.__basicGUI()
def __basicGUI(self):
panel = wx.Panel(self)
menuBar = wx.MenuBar()
fileButton = wx.Menu()
aboutButton = wx.Menu()
exitItem = fileButton.Append(wx.ID_EXIT, 'Exit','status msg...')
aboutItem = aboutButton.Append(wx.ID_ABOUT, "About")
menuBar.Append(fileButton, 'File')
menuBar.Append(aboutButton, 'About this program')
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.__quit, exitItem)
self.Bind(wx.EVT_MENU, self.__onmenuhelpabout, aboutItem)
self.__sound_dict = { "Goliath" : self.__goliathwav,
"Goliath2" : self.__channelopen
}
self.__sound_list = sorted(self.__sound_dict.keys())
self.__list = wx.ListBox(panel,pos=(20,20), size=(250,150))
for i in self.__sound_list:
self.__list.Append(i)
self.__list.Bind(wx.EVT_LISTBOX,self.__on_click)
textarea = wx.TextCtrl(self, -1,
style=wx.TE_MULTILINE|wx.BORDER_SUNKEN|wx.TE_READONLY|
wx.TE_RICH2, size=(250,150))
self.usertext = textarea
#self.__list2 = wx.ListBox(panel,pos=(19.5,180), size=(251,21)) #second panel
#for j in self.__sound_list:
# self.__list2.Append(i)
#self.__list2.Bind(wx.EVT_LISTBOX,self.__on_click)
#wx.TextCtrl(panel,pos=(10,10), size=(250,150))
self.SetTitle("Soundboard")
self.Show(True)
def __onmenuhelpabout(self,event):
dialog = wx.Dialog(self, -1, "[Soundboard]") # ,
#style=wx.DIALOG_MODAL | wx.STAY_ON_TOP)
dialog.SetBackgroundColour(wx.WHITE)
panel = wx.Panel(dialog, -1)
panel.SetBackgroundColour(wx.WHITE)
panelSizer = wx.BoxSizer(wx.VERTICAL)
boldFont = wx.Font(panel.GetFont().GetPointSize(),
panel.GetFont().GetFamily(),
wx.NORMAL,wx.BOLD)
lab1 = wx.StaticText(panel, -1, " SOUNDBOARD ")
lab1.SetFont(wx.Font(36,boldFont.GetFamily(), wx.ITALIC, wx.BOLD))
lab1.SetSize(lab1.GetBestSize())
imageSizer = wx.BoxSizer(wx.HORIZONTAL)
imageSizer.Add(lab1, 0, wx.ALL | wx.ALIGN_CENTRE_VERTICAL, 5)
lab2 = wx.StaticText(panel, -1, "Created by youonlylegoonce(cyrex)(Kommander000) for the glory " + \
"of the republic.")
panelSizer.Add(imageSizer, 0, wx.ALIGN_CENTRE)
panelSizer.Add((10, 10)) # Spacer.
panelSizer.Add(lab2, 0, wx.ALIGN_CENTRE)
panel.SetAutoLayout(True)
panel.SetSizer(panelSizer)
panelSizer.Fit(panel)
topSizer = wx.BoxSizer(wx.HORIZONTAL)
topSizer.Add(panel, 0, wx.ALL, 10)
dialog.SetAutoLayout(True)
dialog.SetSizer(topSizer)
topSizer.Fit(dialog)
dialog.Centre()
btn = dialog.ShowModal()
dialog.Destroy()
def __on_click(self,event):
event.Skip()
name = self.__sound_list[self.__list.GetSelection()]
sound = self.__sound_dict[name]
print("[ NOW PLAYING ] ... %s" % name)
pygame.mixer.Sound.play(sound)
def __quit(self, e):
self.Close()
def main():
app = wx.App()
windowClass(None, -1, style=wx.MAXIMIZE_BOX | wx.CAPTION | wx.CENTRE)
app.MainLoop()
main()
Before:
print("[ NOW PLAYING ] ... %s" % name)
input:
self.usertext.SetValue("[ NOW PLAYING ] ... %s" % name)
P.S. Your indentation is a mess

wxPython multiple windows scope issue

I'm trying to hone my Python skills by creating a simple app to organize an itinerary. The basis of the app is that you enter the task to add, then another window pops out to ask for the due date and priority of the task. The second window has a submit button that is supposed to add all the gathered information to a list box with multiple columns.
My issue is where to create an array to hold the information so that it can be added. I can't add it to the add function itself, because it needs to be referenced by other functions.
I've tried adding it to the __init__ of my MainWindow, but that's where I get stuck. I don't know how to reference the array once it's there. Simply referencing the name (toAdd) give me exception
Traceback (most recent call last):
File "Making GUI - First Shot (Development).py", line 60, in AddItem
toAdd.append(appendItem)
NameError: global name 'toAdd' is not defined
Here's my code:
import wx
class mainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(650, 500))
panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
hbox1 = wx.BoxSizer(wx.HORIZONTAL)
hbox2 = wx.BoxSizer(wx.HORIZONTAL)
menubar = wx.MenuBar()
topFont = wx.Font(20, wx.SWISS, wx.NORMAL, wx.NORMAL)
self.topLabel = wx.StaticText(panel, size = (-1, -1), label="Itinerary")
font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)
self.topLabel.SetFont(topFont)
vbox.Add(self.topLabel, 0, wx.ALL, 2)
vbox.Add((-1, 10))
self.listBox = wx.ListCtrl(panel, style = wx.LC_REPORT)
self.listBox.InsertColumn(0, "Priority", width = 150)
self.listBox.InsertColumn(0, "Due Date (if applicable)", width = 250)
self.listBox.InsertColumn(0, "Task", width=250)
self.listBox.SetFont(font)
vbox.Add(self.listBox, 1, wx.EXPAND | wx.ALL, 2)
hbox1.Add((5, -1))
self.newItemInput = wx.TextCtrl(panel, size = (400, -1))
hbox2.Add(self.newItemInput, 1, wx.ALIGN_LEFT | wx.ALL, 1)
self.submitButton = wx.Button(panel, size = (100, -1))
self.submitButton.SetLabel("Hello")
self.Bind(wx.EVT_BUTTON, self.AddItem, self.submitButton)
hbox2.Add(self.submitButton, 1, wx.ALIGN_RIGHT | wx.ALL, 2)
vbox.Add(hbox1, 0, wx.ALL, 2)
vbox.Add(hbox2, 0, wx.EXPAND | wx.ALL, 2)
vbox.Add((-1, 35))
panel.SetSizer(vbox)
self.Show(True)
toAdd = []
def AddItem(self, event):
appendItem = self.newItemInput.GetValue()
toAdd.append(appendItem)
confirmBox = wx.Frame(frame, title = "Task Details", size = (300, 150))
confirmVert = wx.BoxSizer(wx.VERTICAL)
confirmHoriz1 = wx.BoxSizer(wx.HORIZONTAL)
confirmHoriz2 = wx.BoxSizer(wx.HORIZONTAL)
confirmHoriz3 = wx.BoxSizer(wx.HORIZONTAL)
confirmHoriz4 = wx.BoxSizer(wx.HORIZONTAL)
confirmPrompt = wx.StaticText(confirmBox, size = (25, -1), label = "Due Date for " + toAdd[0])
confirmHoriz1.Add(confirmPrompt, 1, wx.EXPAND | wx.ALIGN_CENTER)
addingDate = wx.TextCtrl(confirmBox, size = (25, -1))
confirmHoriz2.Add(addingDate, 1, wx.EXPAND | wx.ALIGN_CENTER)
confirmPriority = wx.StaticText(confirmBox, size = (25, -1), label = "Priority")
confirmHoriz3.Add(confirmPriority, 1, wx.EXPAND | wx.ALIGN_CENTER)
addingPriority = wx.TextCtrl(confirmBox, size = (25, -1))
confirmHoriz4.Add(addingPriority, 1, wx.EXPAND | wx.ALIGN_CENTER)
addListButton = wx.Button(confirmBox, size = (-1, 25), label = "Submit")
confirmVert.Add(confirmHoriz1, 0, wx.EXPAND | wx.ALIGN_CENTER)
confirmVert.Add(confirmHoriz2, 0, wx.EXPAND | wx.ALIGN_CENTER)
confirmVert.Add(confirmHoriz3, 0, wx.EXPAND | wx.ALIGN_CENTER)
confirmVert.Add(confirmHoriz4, 0, wx.EXPAND | wx.ALIGN_CENTER)
confirmVert.Add(addListButton, 0, wx.EXPAND | wx.ALIGN_CENTER)
def itemSubmit(self):
date = addingDate.GetValue()
priority = addingPriority.GetValue()
toAdd.append(date)
toAdd.append(priority)
panel.listBox.Append(toAdd)
confirmBox.Bind(wx.EVT_BUTTON, itemSubmit, addListButton)
confirmBox.SetSizer(confirmVert)
confirmBox.Show()
app = wx.App(False)
frame = mainWindow(None, "Itinerary Manager")
app.MainLoop()
How would I go about making AddItem append the information to toAdd so that I can then append toAdd to the ListCtrl on the main window?
Utilise the self reference for your data items, so declare
toAdd = []
as
self.toAdd[]
this should apply to your other variables as well.
The first argument of every class method, including init, is always a reference to the current instance of the class. By convention, this argument is always named self. It isn't a rule just a convention.

Updating the labels of the panels in a GUI using threading in wxPython

Level: Beginner
I am using python v2.7 and wxPython v3.0 and the OS is windows 7.
My GUI app: Well in my GUI app I am reading some values from a server and then based upon number of these values I create panels in my GUI. Then each of these panels will represent the values in form of a staticText. For eg: If I receive from the server 1,2,3 values, then I create 3 panels each displaying 1, 2 and 3 respectively. This works fine till here.
Problem: I want to check the server every 5 seconds to fetch the values and accordingly update my GUI ie. I have to update the staticText on the panels to display the updated values. I don't want to add new panels I just want to update the values in the old panels.
For eg: If I check the server and if the server returns 1, 2, 3 as values then I want to create 3 panels displaying 1, 2, 3 values respectively. Then after 5 seconds when I check the server again then if server gives 4, 5, 6 as values then I just want to update these values on the old panels. Which means now the panels will display 4, 5, 6 instead of 1, 2, 3 respectively. I read some tutorials & posts on SC regarding using threads, I understood some basic facts too. Unfortunately I don't understand how to apply this concept to my problem.
It would be really great if I could get a working example for my this particular problem so that I could apply the same to my rest of the application.
Code: I have created a short sample code for this particular problem. The getLabels() in the class labelsA and the getLabels() in the class labelsB mimics the server by just generating some random values and returning them in a list. Then the list of values returned by getLabels() of the class labelA and the list of values returned by getLabels() of the class labelsB are used by createPanels()A and createPanelsB() respectively to create panels and to display these values. The panel in white background is panelA and the panel with yellow background is the panelB. It would be really great if some one can teach me how to use threading to update the values of these two panels without freezing/blocking my GUI.
Download: The sample code is provided below and can be downloaded from here too to avoid identation problems.
#!/usr/bin/env python
from random import randrange
import wx
import wx.lib.scrolledpanel
class GUI(wx.Frame):
def __init__(self, parent, id, title):
screenWidth = 800
screenHeight = 450
screenSize = (screenWidth, screenHeight)
wx.Frame.__init__(self, None, id, title, size=screenSize)
self.locationFont = locationFont = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
mainSizer = wx.BoxSizer(wx.HORIZONTAL)
panelSizer = wx.BoxSizer(wx.HORIZONTAL)
self.sizerA = sizerA = wx.BoxSizer(wx.VERTICAL)
self.panelA = panelA = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
panelA.SetupScrolling()
panelA.SetBackgroundColour('#FFFFFF')
self.sizerB = sizerB = wx.BoxSizer(wx.VERTICAL)
self.panelB = panelB = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
panelB.SetupScrolling()
panelB.SetBackgroundColour('#FFF000')
panelA.SetSizer(sizerA)
panelB.SetSizer(sizerB)
mainSizer.Add(panelA, 15, wx.EXPAND|wx.ALL)
mainSizer.Add(panelB, 15, wx.EXPAND|wx.ALL)
self.SetSizer(mainSizer)
self.createPanelsA()
self.createPanelsB()
def createPanelsA(self):
k = 0
labelObj = labelsA()
locations = labelObj.getLabel()
print locations
for i in locations:
sPanels = 'sPanel'+str(k)
sPanels = wx.Panel(self.panelA)
label = str(k+1)
text = wx.StaticText(sPanels, -1, label)
text.SetFont(self.locationFont)
text.SetForegroundColour('#0101DF')
self.sizerA.Add(sPanels, 0, wx.ALL, 5)
self.sizerA.Add(wx.StaticLine(self.panelA), 0, wx.ALL|wx.EXPAND, 0)
k += 1
def createPanelsB(self):
k = 0
labelObj = labelsB()
locations = labelObj.getLabel()
print locations
for i in locations:
sPanels = 'sPanel'+str(k)
sPanels = wx.Panel(self.panelB)
label = str(k+1)
text = wx.StaticText(sPanels, -1, label)
text.SetFont(self.locationFont)
text.SetForegroundColour('#0101DF')
self.sizerB.Add(sPanels, 0, wx.ALL, 5)
self.sizerB.Add(wx.StaticLine(self.panelB), 0, wx.ALL|wx.EXPAND, 0)
k += 1
################################################
class labelsA():
def getLabel(self):
mylist =[]
i = randrange(10)
for k in range(1,i+1):
mylist.append(k)
return mylist
###############################################
class labelsB():
def getLabel(self):
mylist =[]
i = randrange(10)
for k in range(1,i+1):
mylist.append(k)
return mylist
###############################################
if __name__=='__main__':
app = wx.App()
frame = GUI(parent=None, id=-1, title="Test")
frame.Show()
app.MainLoop()
Thank you very much for your time. Any help will be appreciated.
Here is a demonstration of creating a thread to get info from somewhere every 5 seconds, and also showing how to create StaticTexts as you need them, and how to update them.
#!/usr/bin/env python
from random import randrange
import wx
import wx.lib.scrolledpanel
import time
import threading
from wx.lib.pubsub import setupkwargs
from wx.lib.pubsub import pub
class GUI(wx.Frame):
def __init__(self, parent, id, title):
screenWidth = 800
screenHeight = 450
screenSize = (screenWidth, screenHeight)
wx.Frame.__init__(self, None, id, title, size=screenSize)
self.locationFont = locationFont = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.sizer = sizer = wx.BoxSizer(wx.VERTICAL)
self.panel = panel = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
panel.SetupScrolling()
panel.SetBackgroundColour('#FFFFFF')
panel.SetSizer(sizer)
mainSizer.Add(panel, 15, wx.EXPAND|wx.ALL)
self.SetSizer(mainSizer)
self.text_labels = [] # Stores the labels where server data is displayed
pub.subscribe(self.OnNewLabels, "NEW_LABELS")
def OnNewLabels(self, labels):
locations = labels
print locations
if len(self.text_labels) < len(labels):
new_labels_needed = len(labels) - len(self.text_labels)
label = "(no data)"
for i in range(new_labels_needed):
sPanels = wx.Panel(self.panel)
text = wx.StaticText(sPanels, -1, label)
text.SetFont(self.locationFont)
text.SetForegroundColour('#0101DF')
self.sizer.Add(sPanels, 0, wx.ALL, 5)
self.sizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 0)
self.text_labels.append(text)
self.sizer.Layout()
k = 0
for label in locations:
self.text_labels[k].SetLabel(str(label))
k=k+1
###############################
#
#
def InterfaceThread():
while True:
# get the info from the server
mylist =[]
i = randrange(10)
for k in range(1,i+1):
mylist.append(randrange(10))
# Tell the GUI about them
wx.CallAfter(pub.sendMessage, "NEW_LABELS", labels = mylist)
time.sleep(5)
class ServerInterface():
def __init__(self):
interface_thread = threading.Thread(target = InterfaceThread, args = ())
interface_thread.start()
#############
#
if __name__=='__main__':
app = wx.App()
frame = GUI(parent=None, id=-1, title="Test")
frame.Show()
server_interface = ServerInterface()
app.MainLoop()
For the record, I know this question was answered a long time ago, the same result can be achieved with a simple wx.Timer.
Using the code supplied above:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import wx
import wx.lib.scrolledpanel
from random import randrange
class GUI(wx.Frame):
def __init__(self, parent, id, title):
screenWidth = 800
screenHeight = 450
screenSize = (screenWidth, screenHeight)
wx.Frame.__init__(self, None, id, title, size=screenSize)
self.locationFont = locationFont = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.sizer = sizer = wx.BoxSizer(wx.VERTICAL)
self.panel = panel = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
panel.SetupScrolling()
panel.SetBackgroundColour('#FFFFFF')
panel.SetSizer(sizer)
mainSizer.Add(panel, 15, wx.EXPAND|wx.ALL)
self.SetSizer(mainSizer)
self.text_labels = [] # Stores the labels where server data is displayed
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
self.timer.Start(5000)
def OnNewLabels(self, labels):
locations = labels
print locations
if len(self.text_labels) < len(labels):
new_labels_needed = len(labels) - len(self.text_labels)
label = "(no data)"
for i in range(new_labels_needed):
sPanels = wx.Panel(self.panel)
text = wx.StaticText(sPanels, -1, label)
text.SetFont(self.locationFont)
text.SetForegroundColour('#0101DF')
self.sizer.Add(sPanels, 0, wx.ALL, 5)
self.sizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 0)
self.text_labels.append(text)
self.sizer.Layout()
k = 0
for label in locations:
self.text_labels[k].SetLabel(str(label))
k=k+1
if len(self.text_labels) > len(labels):
labels_not_needed = len(self.text_labels) - len(labels)
for i in range(labels_not_needed):
self.text_labels[k].SetLabel("-")
k+=1
def OnTimer(self, evt):
# get the info from the server
mylist =[]
i = randrange(10)
for k in range(1,i+1):
mylist.append(randrange(10))
self.OnNewLabels(mylist)
if __name__=='__main__':
app = wx.App()
frame = GUI(parent=None, id=-1, title="Test")
frame.Show()
app.MainLoop()

Categories

Resources