wxPython: disable multiple buttons - python

I have a wxPython GUI which looks like this:
As you can see there are three "columns", each enclosed in a wx.StaticBox. I want to disable all the buttons, text box, and radio buttons within a column. I tried using .Disable on the static box but it had no effect. Is there an easy way to disable everything within a static box?

A wx.StaticBox is not actually a container, it's just:
a rectangle drawn around other panel items to denote a logical grouping of items.
So, the only way you can do this with StaticBox is to keep track of the widgets you've logically grouped together, and call Disable on all of them.
Or, alternatively, you can put the widgets into any of the actual container widgets (like windows and sizers) instead, and then just Disable the container.

Make the StaticBoxSizer into a class attribute if you haven't already (i.e. self.mySizer instead of just mySizer). Then you can use its GetChildren() method to return the widgets. Next you just loop through the widgets and Disable them. Something like this should do it:
children = self.mySizer.GetChildren()
for child in children:
child.Disable()
You might have to add a check in the loop to make sure it's a button or a text control. I recommend using Python's isinstance for that.

Here is a working example:
"""
Demonstration on how to disable / enable all objects within a sizer
Reinhard Daemon / Austria
08.10.2019
"""
import wx
class MainWindow(wx.Frame):
def __init__(self, parent=None):
wx.Frame.__init__(self, parent, -1,
title='Disable / Enable all Widgets within a Sizer',size=(500,200))
self.Move((50,50))
panel = wx.Panel(self)
# layout (sizers, boxes,...):
top_sizer = wx.BoxSizer(wx.VERTICAL)
widget_box = wx.StaticBox(panel, id=-1,
label='Widgets, which are controlled')
widget_box.SetBackgroundColour("yellow")
control_box = wx.StaticBox(panel, -1,
label='Widgets, which controll')
control_box.SetBackgroundColour("yellow")
self.widget_sizer = wx.StaticBoxSizer(widget_box, wx.HORIZONTAL)
control_sizer = wx.StaticBoxSizer(control_box, wx.HORIZONTAL)
# create the widgets:
widget_1 = wx.TextCtrl(panel, value='Text 1')
widget_2 = wx.RadioButton(panel, label='Radio 1')
widget_3 = wx.RadioButton(panel, label='Radio 2')
widget_4 = wx.Button(panel, label='Button 1')
widget_disable = wx.Button(panel, label='DISABLE')
self.widget_enable = wx.Button(panel, label='ENABLE', pos = (100,50))
# add the widgets to the layout:
self.widget_sizer.Add(widget_1)
self.widget_sizer.Add(widget_2)
self.widget_sizer.Add(widget_3)
self.widget_sizer.Add(widget_4)
control_sizer.Add(widget_disable)
control_sizer.Add(self.widget_enable)
# finalize the layout:
top_sizer.Add(sizer=self.widget_sizer, flag=wx.CENTER | wx.EXPAND)
top_sizer.AddSpacer(30)
top_sizer.Add(control_sizer, 0, wx.CENTER | wx.EXPAND)
panel.SetSizer(top_sizer)
panel.Fit()
# bindings:
widget_disable.Bind(wx.EVT_BUTTON, self.on_button_disable)
self.widget_enable.Bind(wx.EVT_BUTTON, self.on_button_enable)
def on_button_disable(self, evt):
children = self.widget_sizer.GetChildren()
for child in children:
print(child.GetWindow(),end='')
try:
child.GetWindow().Enable(False)
print(' DISABLED')
except:
print(' ERROR')
def on_button_enable(self, evt):
children = self.widget_sizer.GetChildren()
for child in children:
print(child.GetWindow(),end='')
try:
child.GetWindow().Enable(True)
print(' ENABLED')
except:
print(' ERROR')
if __name__ == "__main__":
app = wx.App()
view1 = MainWindow()
view1.Show()
app.MainLoop()

Related

How to create an info icon with wxPython

I so far failed to create what is colloquially called an "info icon" with wxPython. An icon with some sort of 'i' image that shows a large tooltip on hover.
I can add a wx.StaticBitmap for the image but it ignores all SetToolTipString or SetToolTip(wx.ToolTip()) calls. OR I can add a large tool tip to a wx.StaticText as shown below.
Ignore that the icon doesn't have the correct size yet.
Needless to say that eventually the tooltip needs a background color that is different from the panel background color (not the focus here). I can't use wx.adv.RichToolTip because I'm on wxPython 3.0.2.0 osx-cocoa.
What is a good way to solve this?
If you create a button with an ID of wx.ID_HELP then you'll get the stock help button for the platform, if it has one. Then you can do whatever you want with it like any button. Assign a tooltip, do something in the EVT_BUTTON event, etc. See the StockButtons sample in the demo. If the stock image or label doesn't meet your needs then you can probably just use a wx.BitmapButton to show the image you want and still have the standard tooltip support.
Something else you may want to look into is the ContextHelp sample in the demo. It shows how to use a wx.ContextHelpButton which, when clicked, puts the application into context-help mode. A popup tip window will then be shown for whatever widget is clicked on next. Not quite what you are asking for, but it might be a good fit.
wxArtProvider may be able to help http://docs.wxwidgets.org/trunk/classwx_art_provider.html
import wx
class Test(wx.Frame):
def __init__(self,parent,msg,title):
wx.Frame.__init__(self, None)
self.panel = wx.Panel(self, size=(300,400))
mainSizer = wx.BoxSizer(wx.HORIZONTAL)
staticIcon = wx.BitmapButton(self.panel, bitmap=wx.ArtProvider.GetBitmap(wx.ART_WARNING), size=(32,32))
mainSizer.Add(staticIcon, flag=wx.ALL, border=10)
ttip = "xxxxxxxxxxxxxxx\n"
ttip += "xxxxxxxxxxxxxxxxxxxxxxxxxx\n"
ttip += "xxxxxxxxxxxxxxxxxxxxxxxxxxx\n"
ttip += "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
staticIcon.SetToolTipString(ttip)
buttonText = wx.StaticText(self.panel, -1, msg, wx.DefaultPosition, wx.DefaultSize, 0)
mainSizer.Add(buttonText, flag=wx.ALL, border=10)
staticIcon.Bind(wx.EVT_BUTTON, self.OnButton)
self.SetSizer(mainSizer)
self.Show()
def OnButton(self, evt):
print "The button was pressed - display some help"
if __name__ == '__main__':
app = wx.App()
Test(None, "Dummy Exercise", "Test 123")
app.MainLoop()
If all you want to do is show a tooltip when the image is moused over, then you need to bind your instance of the wx.StaticBitmap to EVT_MOTION:
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
bmp = wx.ArtProvider.GetBitmap(wx.ART_WARNING)
self.image = wx.StaticBitmap(self, bitmap=bmp)
self.image.Bind(wx.EVT_MOTION, self.on_mouse_over)
def on_mouse_over(self, event):
self.image.SetToolTipString('BLAH BLAH BLAH')
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Icon Mouser')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
When I run this code, I get something like this:

wxPython : How to change alignment StaticText after SetLabelText

If you create a frame through wxpython and change the contents of the staticText, the alignment will be initialized.
How can I solve this problem?
I want to align it again.
This problem is caused by the autoresize of the text control so to prevent that just add wx.ST_NO_AUTORESIZE to the style and it will be solved.
txtCtrl = wx.StaticText(parent, -1, "some label", style = wx.ALIGN_CENTER| wx.ST_NO_AUTORESIZE)
The static text does not have a method called SetStyle()
To dynamically set window alignment flags you have to
Get the wx.Sizer
Find the wx.SizerItem
Set the wx.SizerItem flags via SetFlag
call Sizer.Layout()
Here's a simple example:
import wx
import traceback
def run():
app = wx.App()
# create the test frame
frame = wx.Frame(None, title="test frame", size=(500, 500))
# create a simple boxsizer
sizer = wx.BoxSizer()
# create the object that we'll be dynamically adjusting
st = wx.StaticText(frame, label="Click me")
st.SetFont(wx.Font(30, 74, 90, wx.FONTWEIGHT_BOLD, True, "Arial Rounded"))
# align the text to the middle initially
sizer.Add(st, 0, wx.ALIGN_CENTER_VERTICAL)
frame.SetSizer(sizer)
# bind to an arbitrary event
st.Bind(wx.EVT_LEFT_DOWN, on_click)
# do the initial layout and show the frame
frame.Layout()
frame.Show()
app.MainLoop()
def on_click(event):
event.Skip()
# retrieving the static text object
st = event.GetEventObject() # type: wx.StaticText
# get the sizer that contains this object
sizer = st.GetContainingSizer() # type: wx.BoxSizer
# find the sizer item
# the sizer item holds the alignment information and tells
# the sizer how to display this object
sizer_item = sizer.GetItem(st) # type: wx.SizerItem
# alternate between aligning at the top & bottom
if sizer_item.GetFlag() & wx.ALIGN_BOTTOM:
print("Setting alignment to top")
sizer_item.SetFlag(wx.ALIGN_TOP)
else:
print("Setting alignment to bottom")
sizer_item.SetFlag(wx.ALIGN_BOTTOM)
# call Layout to recalculate the object positions
sizer.Layout()
if __name__ == "__main__":
try:
run()
except:
traceback.print_exc()
input()

wxPython : Issues on windows using multiple sizers, but works well on linux

I am writing a small app that works very well on linux, but I have some trouble on windows. Here is the code sample:
import wx
#####################################################################
class Main(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="StackOverflow", pos=wx.DefaultPosition, size=(800,600))
self.SetMinSize( self.GetSize() )
p = wx.Panel(self)
nb = wx.Notebook(p)
page1 = AddToCollection(nb)
page2 = CollectionStatistics(nb)
nb.AddPage(page1, "Page 1")
nb.AddPage(page2, "Page 2")
# finally, put the notebook in a sizer for the panel to manage
# the layout
sizer = wx.BoxSizer()
sizer.Add(nb, 1, wx.EXPAND)
p.SetSizer(sizer)
#########################################################################
class CollectionStatistics(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
#########################################################################
class AddToCollection(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.v1_qty_list = [str(x) for x in range(9)]
self.v2_qty_list = [str(x) for x in range(9)]
self.sizername = wx.GridBagSizer(5, 5)
self.sizername.AddGrowableCol(0,0)
self.name_txt = wx.StaticText(self, label="Enter Name :")
self.sizername.Add(self.name_txt,(2,0),(1,1),wx.EXPAND)
self.name = wx.TextCtrl(self,style=wx.TE_PROCESS_ENTER,value=u"")
self.sizername.Add(self.name,(3,0),(1,1),wx.EXPAND)
self.Bind(wx.EVT_TEXT_ENTER, self.OnPressEnter, self.name)
self.SetSizerAndFit(self.sizername)
self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y )
##########################################################################
def OnPressEnter(self,event):
self.selected_name = self.name.GetValue()
self.AddToCol()
##########################################################################
def AddToCol(self):
self.sizerAdd = wx.GridBagSizer(5, 5)
self.sizerAdd.AddGrowableCol(0, 0)
self.name.Enable(False)
### Expansion
self.expansion = wx.Choice(self, -1, choices=['test 1', 'test 2'])
self.expansion.SetSelection(0)
self.sizerAdd.Add(self.expansion,(5,0),(1,6),wx.EXPAND)
### Quantities txt
self.v1_txt = wx.StaticText(self, label="V1 Quantity :")
self.sizerAdd.Add(self.v1_txt,(7,0),(1,1),wx.EXPAND)
self.v2_txt = wx.StaticText(self, label="V2 Quantity :")
self.sizerAdd.Add(self.v2_txt,(8,0),(1,1),wx.EXPAND)
### Quantities choices
self.v1_qty = wx.Choice(self, -1, choices=self.v1_qty_list)
self.v1_qty.SetSelection(0)
self.sizerAdd.Add(self.v1_qty,(7,5),(1,1),wx.EXPAND)
self.v2_qty = wx.Choice(self, -1, choices=self.v1_qty_list)
self.v2_qty.SetSelection(0)
self.sizerAdd.Add(self.v2_qty,(8,5),(1,1),wx.EXPAND)
### Ok Button
self.Add_btn = wx.Button(self, -1, "Add")
self.Add_btn.Bind(wx.EVT_BUTTON, self.OnAdd)
self.sizerAdd.Add(self.Add_btn,(9,5),(1,1),wx.EXPAND)
### Reset Button
self.Reset_btn = wx.Button(self, -1, "Reset")
self.Reset_btn.Bind(wx.EVT_BUTTON, self.OnResetPanel)
self.sizerAdd.Add(self.Reset_btn,(9,4),(1,1),wx.EXPAND)
self.SetSizerAndFit(self.sizerAdd)
self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y )
######################################################################
def OnResetPanel(self,event):
### Kill all children
self.expansion.Destroy()
self.v1_txt.Destroy()
self.v1_qty.Destroy()
self.v2_txt.Destroy()
self.v2_qty.Destroy()
self.Add_btn.Destroy()
self.Reset_btn.Destroy()
### Reinitialise sizer
self.name.Enable(True)
self.name.SetValue("")
######################################################################
def OnAdd(self,event):
print 'Add'
self.OnResetPanel(self)
######################################################################
######################################################################
if __name__ == "__main__":
app = wx.App()
Main().Show()
app.MainLoop()
Basically, I have a TextCtrl in a first sizer which is waiting for an entry. Once the user hits enter, several objects appear in a second sizer.
The issue on windows seems to come from the use of the two gridbagsizers (sizername and sizerAdd). After pressing enter (waited event in the __init__), the objects defined within the sizerAdd do not appear. When I extend the window where the script is running, these objects appear magically !
Any idea ?
EDIT : The code is now runnable
I think the problem in your code is these two lines at the end of your AddToCol method:
self.SetSizerAndFit(self.sizerAdd)
self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y )
At this point, you're changing the sizer of the AddToCollection panel from self.sizername to self.sizerAdd. The Enter Name: label and the textbox however are still within the self.sizername sizer. However, this sizer isn't the sizer for any window, nor has it been added to any other sizer.
Generally, in wxPython, every sizer should be set as the sizer for a window, or be added to another sizer. This other sizer would then be the sizer for a window, or be contained within another sizer, and so on. In your case, your self.sizername sizer ends up being neither, and in this situation I would expect unpredictable behaviour. If your code works on Linux then I would say that it happens to work by accident.
I can think of a few things you could do here:
Add self.sizerAdd as a child of self.sizername. This can be done by replacing the two lines above with
self.sizername.Add(self.sizerAdd,(4,0),(1,1),wx.EXPAND)
self.sizername.Layout()
In AddToCol, add the widgets directly to the self.sizername sizer instead of adding them to self.sizerAdd.
Create a wx.BoxSizer() with vertical orientation, set that to be the sizer for the AddToCollection panel, and add the self.sizername and self.sizerAdd sizers to your BoxSizer.
In all three cases, after creating the new widgets you will need to call the Layout() method on the top-level sizer, be it either self.sizername or the top-level BoxSizer. The code snippet under option 1 includes this line already.
Additionally, you may need to modify your OnResetPanel() method. If you chose options 1 or 3, you will need to remove the self.sizerAdd sizer from whichever sizer you added it to. For example, in option 1, you would add the line
self.sizername.Remove(self.sizerAdd)
Another approach would be for your AddToCol method to create all the widgets within a Panel and add that to the main panel at the end. Your AddToCol method would then need to create a child panel, add the extra controls as children of this panel instead of the main panel (self), set the sizer of the child panel to self.sizerAdd and finally add this panel to the self.sizername sizer.
def AddToCol(self):
self.sizerAdd = wx.GridBagSizer(5, 5)
self.sizerAdd.AddGrowableCol(0, 0)
self.name.Enable(False)
self.child_panel = wx.Panel(self)
### Expansion
self.expansion = wx.Choice(self.child_panel, -1, choices=['test 1', 'test 2'])
self.expansion.SetSelection(0)
self.sizerAdd.Add(self.expansion,(5,0),(1,6),wx.EXPAND)
# Create other widgets as before but with the parent set to self.child_panel
# instead of self.
self.child_panel.SetSizer(self.sizerAdd)
self.sizername.Add(self.child_panel,(4,0),(1,1),wx.EXPAND)
self.sizername.Layout()
You would then also need to replace the line
self.sizername.Remove(self.sizerAdd)
in OnResetPanel() with the two lines:
self.sizername.Remove(self.child_panel)
self.child_panel.Destroy()
One thing which bugged me about my approach 1 above was that I saw the widgets briefly appear in the top-left corner before appearing in the correct place. This adaptation fixes this problem and so makes the GUI behave itself a bit better. I couldn't reproduce your black area issue you mention in your comment, but hopefully this approach fixes your problem as well.

wxPython best way to pass back and forth a set of parameters from frame to dialog

I'm new to wxPython programming and what I would ideally do is have parameters that can be set open in custom dialog (ParameterDialog) with text boxes that have the default values already filled in to the default parameter values set in ImageFrame. Then passing back the changed values or all values in the ParameterDialog Dialog frame by pressing OK or closing/exiting the dialog frame. What is the best way to go about this? Or is there a better solution to this than using a dialog pop-up frame.
Also I have read that modeless windows open using Show() instead of ShowModal(). Whenever I use Show(), instead of ShowModal() nothing happens. I've cut most the code out below but should give a mostly minimal example of what I want and have been able to piece together.
import os
import pprint
import random
import wx
import numpy as np
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar
class ImageFrame(wx.Frame):
""" The main frame of the application
"""
def __init__(self):
wx.Frame.__init__(self, None, -1, 'title')
self.param1 = 10
self.param2 = 0.2
self.panel = wx.Panel(self)
self.button_set_parameters = wx.Button(self.panel, -1, "Set Parameters")
self.Bind(wx.EVT_BUTTON, self.on_set_parameters, self.button_set_parameters)
#
# Layout with box sizers
#
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(self.button_set_parameters, 0, border=3, flag=flags)
self.panel.SetSizer(self.vbox)
self.vbox.Fit(self)
def on_set_parameters(self, event):
dia = ParameterDialog()
res = dia.ShowModal() # Use Show() here to allow edit window to remain open while using rest of application
# Ideally the code below would run on ok or on closing the dialog?? Is this possible or better way to do this? Or is Checking everytime a change is made to a textbox possible and a better way?
if res == wx.ID_CLOSE or res == wx.ID_EXIT or res == wx.ID_OK:
self.param1 = dia.param1.GetValue()
self.param2 = dia.param2.GetValue()
dia.Destroy()
return True
def on_exit(self, event):
self.Destroy()
class ParameterDialog(wx.Dialog):
"""
Used to set the parameters for running.
"""
def __init__(self):
wx.Dialog.__init__(self, None, title="Parameters")
self.static_text_param1 = wx.StaticText(self, label="Param1:")
# Defualt value of 10 displayed in the textbox here as param1 but would need passed in from the ImageFrame class.
self.param1 = wx.TextCtrl(self, size=(100, -1))
self.static_param2 = wx.StaticText(self, label="Param2:")
self.param2 = wx.TextCtrl(self, size=(100, -1))
# Setup up Sizer
flags = wx.ALIGN_LEFT | wx.ALL | wx.ALIGN_CENTER_VERTICAL
sizer_vert = wx.BoxSizer(wx.VERTICAL)
sizer_horz = wx.BoxSizer(wx.HORIZONTAL)
sizer_horz.Add(self.static_text_param1, 0, border=3, flag=flags)
sizer_horz.Add(self.param1, 0, border=3, flag=flags)
sizer_vert.Add(sizer_horz, 0, flag = wx.ALIGN_LEFT | wx.TOP)
sizer_horz = wx.BoxSizer(wx.HORIZONTAL)
sizer_horz.Add(self.static_param2, 0, border=3, flag=flags)
sizer_horz.Add(self.param2, 0, border=3, flag=flags)
sizer_vert.Add(sizer_horz, 0, flag = wx.ALIGN_LEFT | wx.BOTTOM)
self.SetSizer(sizer_vert)
sizer_vert.Fit(self)
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = ImageFrame()
app.frame.Show()
app.MainLoop()
You should be able to get the values using what you have already:
self.param1 = dia.param1.GetValue()
However, this only works when you are showing the dialog modally. When you show a dialog modally, it blocks the main loop of your application and creates a new main loop for the dialog. Then when the dialog exits, you can grab the values using the code above before you Destroy the dialog.
If you don't want to use a modal dialog or just want to try doing it a slightly different way, I would recommend giving pubsub a try. It uses the Publish/Subscribe model where you set up one or more listeners and then publish messages to said listeners. Here a link to a simple tutorial: http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/

wxPython: "Super" wx.SpinCtrl with float values, layout inside sizer

wx.SpinCtrl is limited to spinning across integers, and not floats. Therefore, I am building a wx.TextCtrl + wx.SpinButton combo class which enables me to spin across floats. I am able to size and layout both of them programmatically so that the combo looks exactly the same as an ordinary wx.SpinCtrl.
I am subclassing this combo from the wx.TextCtrl because I want its parent panel to catch wx.EVT_TEXT events. I would appreciate if you can improve on this argument of mine.
The wx.EVT_SPIN_UP and wx.EVT_SPIN_DOWN events from the wx.SpinButton are both internal implementations and the parent frame doesn't care about these events.
Now, I just hit a brick wall. My combo class doesn't work well with sizers. After .Add()ing the combo class to a wx.GridBagSizer, only the wx.TextCtrl is laid out within the wx.GridBagSizer. The wx.SpinButton is left on its own by itself. The wx.EVT_SPIN* bindings work very well, though.
My problem is the layout. How should I write the class if I want the wx.GridBagSizer to treat it as one widget?
Here is my combo class code:
class SpinnerSuper(wx.TextCtrl):
def __init__(self, parent, max):
wx.TextCtrl.__init__(self, parent=parent, size=(48, -1))
spin = wx.SpinButton(parent=parent, style=wx.SP_VERTICAL, size=(-1, 21))
self.OnInit()
self.layout(spin)
self.internalBindings(spin)
self.SizerFlag = wx.ALIGN_CENTER
self.min = 0
self.max = max
def OnInit(self):
self.SetValue(u"0.000")
def layout(self, spin):
pos = self.GetPosition()
size = self.GetSize()
RightEdge = pos[0] + size[0]
TopEdge = pos[1] - (spin.GetSize()[1]/2 - size[1]/2)
spin.SetPosition((RightEdge, TopEdge))
def internalBindings(self, spin):
spin.Bind(wx.EVT_SPIN_UP, self.handlerSpinUp(self), spin)
spin.Bind(wx.EVT_SPIN_DOWN, self.handlerSpinDown(self), spin)
def handlerSpinUp(CallerObject, *args):
def handler(CallerObject, *data):
text = data[0]
prev = text.GetValue()
next = float(prev) + 0.008
text.SetValue("{0:0.3f}".format(next))
return lambda event: handler(CallerObject, *args)
def handlerSpinDown(CallerObject, *args):
def handler(CallerObject, *data):
text = data[0]
prev = text.GetValue()
next = float(prev) - 0.008
text.SetValue("{0:0.3f}".format(next))
return lambda event: handler(CallerObject, *args)
You need to override DoGetBestSize() if you want your control to be
correctly managed by sizers. Have a look at CreatingCustomControls.
You could also have a look at FloatSpin that ships with wxPython
(in wx.lib.agw) from version 2.8.9.2 upwards.
In response to your comments:
Implementing DoGetBestSize() does not require drawing bitmaps directly. You just need to find a way, how you can determine the best size of your new widget. Typically you'd just use
the sizes of the two widgets it is composed of (text + spinner) as basis.
To let sizers treat two widgets as one, you can place them in another sizer.
The recommended way to implement a custom widget with wxPython is to derive your new widget from wx.PyControl, add a sizer to it and add the two widgets you want to combine to that sizer.
As mentionned in Kit's comments, FloatSpin is now the way to go.
It has been integrated in recent versions.
Here is a simple example of usage:
import wx
from wx.lib.agw.floatspin import FloatSpin
class Example_FloatSpin(wx.Frame):
def __init__(self, parent, title):
super(Example_FloatSpin, self).__init__(parent, title=title, size=(480, 250))
panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
spin = FloatSpin(panel, value=0.0, min_val=0.0, max_val=8.0, increment=0.5, digits=2, size=(100,-1))
vbox.Add(spin, proportion=0, flag=wx.CENTER, border=15)
panel.SetSizer(vbox)
self.Centre()
self.Show()
if __name__ == '__main__':
app = wx.App()
Example_FloatSpin(None, title='Check FloatSpin')
app.MainLoop()

Categories

Resources