Dynamically change the choices in a wx.ComboBox() - python

I didn't find a better way to change the different choices in a wx.ComboBox() than swap the old ComboBox with a new one. Is there a better way?
Oerjan Pettersen
#!/usr/bin/python
#20_combobox.py
import wx
import wx.lib.inspection
class MyFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.p1 = wx.Panel(self)
lst = ['1','2','3']
self.st = wx.ComboBox(self.p1, -1, choices = lst, style=wx.TE_PROCESS_ENTER)
self.st.Bind(wx.EVT_COMBOBOX, self.text_return)
def text_return(self, event):
lst = ['3','4']
self.st = wx.ComboBox(self.p1, -1, choices = lst, style=wx.TE_PROCESS_ENTER)
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, '20_combobox.py')
frame.Show()
self.SetTopWindow(frame)
return 1
if __name__ == "__main__":
app = MyApp(0)
# wx.lib.inspection.InspectionTool().Show()
app.MainLoop()

wx.ComboBox derives from wx.ItemContainer, which has methods for Appending, Clearing, Inserting and Deleting items, all of these methods are available on wx.ComboBox.
One way to do what you want would be to define the text_return() method as follows:
def text_return(self, event):
self.st.Clear()
self.st.Append('3')
self.st.Append('4')

Related

WxPython Cut, Copy, Paste functions

I am making a small application and I have trouble defining efficient "Edit" menu functions.
I have tried this:
from pyautogui import hotkey
.
.
.
def OnCopy ( self, event ):
hotkey ( 'ctrl, 'c' )
However, the above doesn't always work and even breaks sometimes. Is there a better method?
wxPython has its own Clipboard object. Its implementation depends on the use of a wxDataObject because, of course, you can copy and paste many types of data onto the clipboard
import wx
class MainFrame(wx.Frame):
"""Create MainFrame class."""
def __init__(self, *args, **kwargs):
super().__init__(None, *args, **kwargs)
self.size = (400, 1000)
self.panel = MainPanel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.panel)
self.SetSizer(sizer)
self.Center()
self.Show()
def on_copy(self, event):
if wx.TheClipboard.Open():
# Put some text onto the clipboard
text = self.panel.txt_input.GetValue()
data_object = wx.TextDataObject(text)
wx.TheClipboard.SetData(data_object)
# Now retrieve it from the clipboard and print the value
text_data = wx.TextDataObject()
success = wx.TheClipboard.GetData(text_data)
wx.TheClipboard.Close()
if success:
print(f'This data is on the clipboard: {text_data.GetText()}')
class MainPanel(wx.Panel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.parent = parent
self.txt_input = wx.TextCtrl(self)
cmd_copy = wx.Button(self, wx.ID_COPY)
cmd_copy.Bind(wx.EVT_BUTTON, parent.on_copy)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.txt_input)
sizer.Add(cmd_copy)
self.SetSizer(sizer)
if __name__ == '__main__':
wx_app = wx.App()
MainFrame()
wx_app.MainLoop()

Elements between panels are not aligned within a sizer

I have two panels to which I am both adding to a single sizer at the top level, however the elements within those panels are not aligned with each other.
Here is a simple example to demonstrate what I am trying to achieve.
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
super(MyPanel, self).__init__(parent=parent)
mygridsizer = wx.GridBagSizer()
sizer = wx.BoxSizer(orient=wx.HORIZONTAL)
sizer.Add(wx.StaticText(self,label="Hello world"))
sizer.Add(wx.Button(self, label="hello"))
mygridsizer.Add(sizer, pos=(0,0))
mygridsizer.Add(wx.ComboBox(self), pos=(0,1))
self.SetSizer(mygridsizer)
class MyPanel2(wx.Panel):
def __init__(self, parent):
super(MyPanel2, self).__init__(parent=parent)
sizer = wx.BoxSizer(orient=wx.HORIZONTAL)
sizer.Add(wx.Button(self, label="non-aligned button"))
self.SetSizer(sizer)
class MainFrame(wx.Frame):
def __init__(self, parent):
super(MainFrame, self).__init__(None)
sizer = wx.GridSizer(3, 1)
panel1 = MyPanel(parent=self)
panel2 = MyPanel2(parent=self)
sizer.Add(panel1)
sizer.Add(panel2)
self.SetSizer(sizer)
if __name__ == '__main__':
app = wx.App()
frame = MainFrame(None)
frame.Show()
app.MainLoop()
With the example above, what can I do to align the buttons of both the panels?
The problem is the sizer is aligning the Panels, if you want the buttons aligned you should make them part of the same sizer (not part of two Panels with their own sizers). You could also do something like this for a quick hack (essentially adding a spacer the same size as the text):
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
super(MyPanel, self).__init__(parent=parent)
mygridsizer = wx.GridBagSizer()
sizer = wx.BoxSizer(orient=wx.HORIZONTAL)
sizer.Add(wx.StaticText(self,label="Hello world"))
sizer.Add(wx.Button(self, label="hello"))
mygridsizer.Add(sizer, pos=(0,0))
mygridsizer.Add(wx.ComboBox(self), pos=(0,1))
self.SetSizer(mygridsizer)
class MyPanel2(wx.Panel):
def __init__(self, parent):
super(MyPanel2, self).__init__(parent=parent)
sizer = wx.BoxSizer(orient=wx.HORIZONTAL)
t = wx.StaticText(self,label="Hello world")
t.Hide()
t.GetSize()
sizer.Add(t.GetSize())
sizer.Add(wx.Button(self, label="non-aligned button"))
self.SetSizer(sizer)
class MainFrame(wx.Frame):
def __init__(self, parent):
super(MainFrame, self).__init__(None)
sizer = wx.GridSizer(3, 1)
panel1 = MyPanel(parent=self)
panel2 = MyPanel2(parent=self)
sizer.Add(panel1)
sizer.Add(panel2)
self.SetSizer(sizer)
if __name__ == '__main__':
app = wx.App()
frame = MainFrame(None)
frame.Show()
app.MainLoop()

creating statictext dynamically and then setting label with python

I'm creating table with static text with a while loop, after that I want to set labels. I'm having problem with it, because it only works with the last one. Here is my code:
import wx
class Mainframe(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.panel = wx.Panel(self)
def test(self,n):
while n <=5:
a = wx.StaticText(self.panel, label='bad', id=n, pos=(20,30*n))
n = n+1
return a
test(self,0)
if test(self,0).GetId()==1:
test(self,0).SetLabel('good')
if test(self,0).GetId()==5:
test(self,0).SetLabel('exelent')
if __name__=='__main__':
app = wx.App(False)
frame = Mainframe(None)
frame.Show()
app.MainLoop()
When you are returning a it is only the last control created because each time you loop it is overwritten.
Append the controls to a list and then you can access them all.
Also note that you are calling test 5 times, so you will have 5 lots of your set of statictexts created on top of each other.
import wx
class Mainframe(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.panel = wx.Panel(self)
ctrls = []
for n in range(6):
ctrls.append(wx.StaticText(self.panel, label='bad',
pos=(20, 30 * n)))
ctrls[1].SetLabel('good')
ctrls[5].SetLabel('excellent')
if __name__ == '__main__':
app = wx.App(False)
frame = Mainframe(None)
frame.Show()
app.MainLoop()

wxPython CollapsiblePane strange clipping issue

This is a follow up from allocating more size in sizer to wx.CollapsiblePane when expanded.
EDIT: The answer to that question solved my original problem, which was that nothing moved when I expanded or collapsed a pane, but now I've encountered another bug. While things do move properly, the buttons seem to smear out over each other as shown in the image below. Mousing over a button seems to force it to redraw properly over the rest of the mess, but it leaves a bunch of random button pieces drawn behind and around it.
I've done my absolute best to replicate this bug in a sample app, but I can't. I'm just looking for leads here. Does anyone have any idea what might cause the kind of issue shown below? It only happens after expanding and collapsing some of the upper panes.
For what it's worth, code below is for a sample app that looks very similar, but for some reason doesn't cause the same problems. EDIT Also for what it's worth, here's a link to the full source code for the GUI of my project which generated the screenshot below. http://code.google.com/p/dicom-sr-qi/source/browse/gui/main.py?r=8a876f7b4a034df9747a2c1f2791258f671e44b1
import wx
class SampleSubPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.StaticText(self, 1, "A label"))
sizer.Add(wx.SpinCtrl(self))
self.SetSizer(sizer)
class SampleCollapsiblePane(wx.CollapsiblePane):
def __init__(self, *args, **kwargs):
wx.CollapsiblePane.__init__(self,*args,**kwargs)
sizer = wx.BoxSizer(wx.VERTICAL)
for x in range(2):
sizer.Add(wx.CheckBox(self.GetPane(), label = str(x)))
sizer.Add(SampleSubPanel(self.GetPane()))
self.GetPane().SetSizer(sizer)
self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.on_change)
def on_change(self, event):
self.GetParent().Layout()
class SampleSubPanel2(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
sizer = wx.BoxSizer(wx.VERTICAL)
for x in range(2):
sizer.Add(SampleCollapsiblePane(self, label = str(x)), 0)
self.SetSizer(sizer)
class Main_Frame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.main_panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(wx.Panel(self.main_panel, style=wx.RAISED_BORDER),1, wx.EXPAND)
sizer.Add(SampleSubPanel2(self.main_panel, style = wx.RAISED_BORDER),2, wx.EXPAND )
sizer.Add(wx.Button(self.main_panel,label= "a button"),0,wx.ALIGN_CENTER)
self.main_panel.SetSizer(sizer)
class SampleApp(wx.App):
def OnInit(self):
frame = Main_Frame(None, title = "Sample App")
frame.Show(True)
frame.Centre()
return True
def main():
app = SampleApp(0)
app.MainLoop()
if __name__ == "__main__":
main()
If I am getting you right, you want the code here - allocating more size in sizer to wx.CollapsiblePane when expanded to work properly. The reason it did not work was you forgot to bind the wx.EVT_COLLAPSIBLEPANE_CHANGED. Here is a code which worked for me -
import wx
class SampleCollapsiblePane(wx.CollapsiblePane):
def __init__(self, *args, **kwargs):
wx.CollapsiblePane.__init__(self,*args,**kwargs)
sizer = wx.BoxSizer(wx.VERTICAL)
for x in range(5):
sizer.Add(wx.Button(self.GetPane(), label = str(x)))
self.GetPane().SetSizer(sizer)
self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.on_change)
def on_change(self, event):
self.GetParent().Layout()
class Main_Frame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.main_panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
for x in range(5):
sizer.Add(SampleCollapsiblePane(self.main_panel, label = str(x)), 0)
self.main_panel.SetSizer(sizer)
class SampleApp(wx.App):
def OnInit(self):
frame = Main_Frame(None, title = "Sample App")
frame.Show(True)
frame.Centre()
return True
def main():
app = SampleApp(0)
app.MainLoop()
if __name__ == "__main__":
main()
EDIT: Looks like it may be a bug in wxPython running on windows. Below is screen shot of the the exact same code that has problems on Windows running on Ubuntu with no problems.

SpinCtrl with step increment value

I'm trying to create a custom SpinCtrl with a step increment. It seems like such a simple thing so I was surprised the native SpinCtrl doesn't appear to have this functionality, and Google proves uncommonly useless in this matter as well.
When I try to make a custom one, however, I run into problems. Here's some quick and dirty code
class SpinStepCtrl( wx.SpinCtrl ):
def __init__( self, *args, **kwargs ):
super( SpinStepCtrl, self ).__init__( *args, **kwargs )
self.step = 99
self.Bind( wx.EVT_SPINCTRL, self.OnSpin )
#self.Bind( wx.EVT_SPIN_UP, self.OnUp )
#self.Bind( wx.EVT_SPIN_DOWN, self.OnDown )
def OnSpin( self, event ):
print 'X'
self.SetValue( self.GetValue() + self.step )
The print is just there so I can see what, if anything, happens. The EVT_SPIN_UP and EVT_SPIN_DOWN events don't seem to work at all, at least the callbacks are never called which is why I took them out.
When using EVT_SPINCTRL, the callback is called, but end up in an infinite loop because SetValue apparently causes a new such event to be called. It doesn't help much either way, because I can't find a way of telling whether it was a spin up or spin down event, so I can't change the value appropriately.
How do I get this to work?
wx.lib.agw has floatspin widget which has more options. I use it with SetDigits(0) for integer input as well.
import wx
import wx.lib.agw.floatspin as FS
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "FloatSpin Demo")
panel = wx.Panel(self)
floatspin = FS.FloatSpin(panel, -1, pos=(50, 50), min_val=0, max_val=1000,
increment=99, value=0, agwStyle=FS.FS_LEFT)
floatspin.SetDigits(0)
# our normal wxApp-derived class, as usual
app = wx.App(0)
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
OK, not the best, but works on Ubuntu:
#!/usr/bin/python
import wx
class SpinStepCtrl(wx.SpinCtrl):
def __init__(self, *args, **kwargs):
wx.SpinCtrl.__init__(self, *args, **kwargs)
self.step = 99
self.last_value = 0
self.Bind(wx.EVT_SPINCTRL, self.OnSpin)
def OnSpin(self, event):
delta = self.GetValue() - self.last_value
if delta == 0:
return
elif delta > 0:
self.last_value = self.GetValue() + self.step
else:
self.last_value = self.GetValue() - self.step
self.SetValue(self.last_value)
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.spin = SpinStepCtrl(self.panel, min=0, max=1000)
self.Show()
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
Still, I would consider "self.SetValue" generating "wx.EVT_SPINCTRL" a bug.
This works for me:
import wx
class SpinStepCtrl(wx.SpinCtrl):
def __init__(self, *args, **kwargs):
wx.SpinCtrl.__init__(self, *args, **kwargs)
self.step = 99
self.Bind(wx.EVT_SPIN_UP, self.OnUp)
self.Bind(wx.EVT_SPIN_DOWN, self.OnDown)
def OnUp(self, event):
self.SetValue(self.GetValue() + self.step)
def OnDown(self, event):
self.SetValue(self.GetValue() - self.step)
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.spin = SpinStepCtrl(self.panel, min=0, max=1000)
self.Show()
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
Here is my solution for wxSpinCtrl, which takes values from a list to spin.
It is a pity that the events EVT_SPIN_UP and EVT_SPIN_DOWN from wx.SpinButton do not work under all platforms (not working here with wx 4.0.6 and Windows 10 / 64 Bit)
import wx
class SpinCtrlList(wx.SpinCtrl):
"""A SpinCtrl that spins through a given list. This list must be in
ascending order and the values of the list must be integers,
whereat negative numbers are allowed
"""
def __init__(self, *args, **kwargs):
wx.SpinCtrl.__init__(self, *args, **kwargs)
self.Bind(wx.EVT_SPINCTRL, self._on_spin)
self.thelist = ([0,1])
self.SetList([0, 1])
self.SetValue(self.thelist[0])
def SetList(self, thelist):
self.thelist = thelist
self.SetRange(self.thelist[0], self.thelist[len(self.thelist)-1])
def GetList(self):
return self.thelist
def SetValue(self, val):
self.sp_old_value = val
super(SpinCtrlList, self).SetValue(val)
def _next_greater_ele(self, in_list, in_val):
for x, val in enumerate(in_list):
if val > in_val:
break
return val
def _next_smaller_ele(self, in_list, in_val):
for x, val in enumerate(reversed(in_list)):
if val < in_val:
break
return val
def _on_spin(self, event):
self.sp_new_value = self.GetValue()
#print(self.sp_old_value, self.sp_new_value)
if self.sp_new_value > self.sp_old_value:
set_val = self._next_greater_ele(self.thelist, self.sp_new_value)
self.SetValue(set_val)
self.sp_old_value = set_val
elif self.sp_new_value < self.sp_old_value:
set_val = self._next_smaller_ele(self.thelist, self.sp_new_value)
self.SetValue(set_val)
self.sp_old_value = set_val
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.spin = SpinCtrlList(self.panel)
ctrl_list =[100, 200, 500, 1000, 2000, 5000, 10000, 12000, 15000]
#ctrl_list = [-300, -100, 0]
self.spin.SetList(ctrl_list)
self.spin.SetValue(ctrl_list[-2])
self.Show()
def main():
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
if __name__ == '__main__':
main()

Categories

Resources