wxPython minimal size of Frame with a Panel - python

wxpython 2.8.11.0, python 2.7
If i put some Sizer with some controls directly into a Frame like
import wx
app=wx.App()
frm = wx.Frame(None, title='title')
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.SpinCtrl(frm))
sizer.Add(wx.SpinCtrl(frm))
frm.SetSizerAndFit(sizer)
frm.Show()
app.MainLoop()
the Frame will automagically have a correct minimum size to contain the Sizer and it is not possible to make it smaller.
If there is a Panel in between (as needed for tabbing between controls) this does not work, the window can be made too small.
import wx
app=wx.App()
frm = wx.Frame(None, title='title')
pan = wx.Panel(frm)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.SpinCtrl(pan))
sizer.Add(wx.SpinCtrl(pan))
pan.SetSizerAndFit(sizer)
frm.Show()
app.MainLoop()
Additionally frm.Fit() and frm.SetMinSize(frm.GetEffectiveMinSize()) are required to get the same behavior. Full Code:
import wx
app=wx.App()
frm = wx.Frame(None, title='title')
pan = wx.Panel(frm)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.SpinCtrl(pan))
sizer.Add(wx.SpinCtrl(pan))
pan.SetSizerAndFit(sizer)
frm.Fit()
frm.SetMinSize(frm.GetEffectiveMinSize())
frm.Show()
app.MainLoop()
I am okay with frm.Fit(), but i dislike frm.SetMinSize(frm.GetEffectiveMinSize()). Isn't there a better solution so that the Frame automatically considers the minimum size of the Panel just like with the Sizer before? I'm considered with what happens if the EffectiveMinSize changes after another control is added to the sizer.
Edit:
Apparently
panel.SetSizerAndFit(sizer)
frm.Fit()
frm.SetMinSize(frm.GetEffectiveMinSize())
should be replaced by
panel.SetSizer(sizer)
sizer.SetSizeHints(frm)
which looks somewhat cleaner. So in total this looks like
import wx
app=wx.App()
frm = wx.Frame(None, title='title')
pan = wx.Panel(frm)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.SpinCtrl(pan))
sizer.Add(wx.SpinCtrl(pan))
pan.SetSizer(sizer)
sizer.SetSizeHints(frm)
frm.Show()
app.MainLoop()
Is this the preferred way to do this?
If widgets are added later on even the first approach with the Sizer directly on the Frame doesn't get it right without intervention, so i think that is a totally different question.

The most elegant way (IMHO) is given in phineas' answer. It is slightly inefficient to have an extra otherwise unneeded sizer but I don't think it can really be noticeable.
Some people do call SetSizeHints() manually, as in your last example, and this works as well and might be more clear (whenever I use an extra sizer I feel the need to leave a comment explaining why it shouldn't be removed).
Unfortunately there is no better way and I am not sure if we can even add one because you need to do something with all of the panel, the sizer and the frame and this means that you can't do it with a single method call without passing unrelated parameters to it. I.e. we could have something like
panel.SetSizerAndFitParent(sizer, frame);
but it's not really clear whether this would be better.

What about introducing a sizer that manages the frame content (e.g. the panel)? Since you didn't include a minimal example, I haven't tested it yet.
frameSizer = wx.BoxSizer(wx.VERTICAL)
frameSizer.Add(panel)
frm.SetSizerAndFit(frameSizer)

Related

wxPython how to move items to new row if they become hidden?

I have a collection of buttons arranged in a row.
Here is a simplified example of my code:
import wx
app = wx.App(False)
frame = wx.Frame(None)
panel = wx.Panel(frame, wx.ID_ANY)
box = wx.BoxSizer(wx.HORIZONTAL)
for i in range(10):
btn = wx.Button(panel, wx.ID_ANY, "Test")
box.Add(btn, 1, wx.FIXED_MINSIZE)
panel.SetSizer(box)
frame.Show()
app.MainLoop()
Making the window smaller leads to the buttons being hidden, as would be expected. However, I want the buttons to retain their dimensions and be moved to a second row instead of shrinking and finally disappearing. I have tried using different settings and different types of sizers but with no avail. How can I go about doing this?
Any help would be greatly appreciated.
I just found out that wxPython has a sizer that does this. For some reason I didn't notice it on the docs before. For anyone stumbling on this in the future, you'll want to use wx.WrapSizer instead of wx.BoxSizer.

wxPython - change the formatting and appearance of a grid object

I would like to know if it is possible to change the formatting of a grid object in wxPython.
Specifically I would like to know how to change the background color, font type, font color, and border color of the header labels and the row labels.
Here is a simple grid that I would like to change the formatting of:
import wx
import wx.grid as gridlib
class MyForm(wx.Frame):
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, parent=None, title="A Simple Grid")
panel = wx.Panel(self)
myGrid = gridlib.Grid(panel)
myGrid.CreateGrid(12, 8)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(myGrid, 1, wx.EXPAND)
panel.SetSizer(sizer)
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm().Show()
app.MainLoop()
Thanks in advance!
First off, wx.PySimpleApp has been deprecated for a very long time and you really shouldn't use it any more. Use wx.App instead.
As for your question you would need to use wx.lib.mixins.gridlabelrenderer. The demo has an example in it called GridLabelRenderer that you can use. Basically you subclass GridLabelRenderer and modify its Draw method. Then you call each column's SetColLabelRenderer method and set it to use your custom renderer.

wxPython - Button size being ignored on OSX

I'm taking the first steps to move from .NET to Python but I'm already having a few headaches regarding the GUI design.
For some reason, passing the size attribute to a wx.Button seems to be kind of ignored. And I say "kind of" because the actual space seems to change but the actual button keeps occupying the same space:
import wx
class Example(wx.Frame):
def __init__(self, *args, **kwargs):
super(Example, self).__init__(*args, **kwargs)
self.InitUI()
def InitUI(self):
self.SetSize((800, 600))
self.SetTitle('Main Menu')
self.Centre()
self.Show(True)
''' Fill the form '''
self.lblUsername = wx.StaticText(self, size=(80, -1), pos=(20,20), label="Username:" )
self.txtUsername = wx.TextCtrl(self, size=(140, -1), pos=(100,20), style=wx.TE_PROCESS_ENTER)
self.lblPassword = wx.StaticText(self, size=(80, -1), pos=(20,50), label="Password:" )
self.txtPassword = wx.TextCtrl(self, size=(140, -1), pos=(100,50), style=wx.TE_PROCESS_ENTER)
self.btnOK = wx.Button( self, label="OK", pos=(260, 16), size=(50,50))
self.btnOK.Bind(wx.EVT_BUTTON, self.onClickOK)
self.statusbar = self.CreateStatusBar()
self.statusbar.SetStatusText('Ready')
def onClickOK(self, e):
print "Button triggered"
def main():
ex = wx.App()
Example(None)
ex.MainLoop()
if __name__ == '__main__':
main()
No matter what size I set, the Button won't stretch (it will be centered as if all the space was actually being used, but will still be small).
Can anyone spot what am I doing wrong?
This is a limit imposed by OSX. The way the native button widget is drawn only allows it to be stretched horizontally, and the vertical size is fixed. Or rather, as you've discovered, the widget itself can be larger than normal vertically, but it will only draw itself at a fixed height within that space. It seems less neccessary with modern versions of OSX, but if you look at buttons in OSX from a few years ago you can probably see why this is so. The esthetic graphical effect of the "tic-tack" or "capsule" buttons would be totally ruined if they were a non-standard vertical size, causing the images used to draw the buttons to be stretched. wxWidgets follows the native plaform look and feel standards where possible, in this case it happens that Apple's standard is imposed upon us and wx can't offer the same level of flexibility that it usually does.
You do have some options however if you really want taller than normal buttons. The native widgets have a few different standard sizes, which you can select using the SetWindowVariant method, although I don't think the variants would get as tall as you want. Or you could use a generic button widget instead of a native one, such as wx.lib.buttons.ThemedGenButton.
Same problem in my little Software EventSoundControl.
Just a workaround: Use a multiline label and sizes of wxButton will work as desired!
If you want the button to stretch when you resize the frame, then you cannot use static sizes and positioning. You will need to put your widgets in a sizer. Then the sizer will manage the position / size of the widget(s) as you change the size of the frame. There are many examples on the wxPython wiki that demonstrate how to use sizers. You might also find the following tutorial helpful:
http://zetcode.com/wxpython/layout/

wxPython set selected control upon application start

I have a simple wxPython application. It basically has a image and a text-entry field (a wx.TextCtrl).
I want to be able to immediately be able to start entering text as soon as the window opens. Right now, I have to first click in the text control, and then I can start entering text.
Here is a minimal app that demonstrates the issue:
import wx
class MyFrame(wx.Frame):
""" We simply derive a new class of Frame. """
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(200, 100))
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
mainSizer.Add(self.control, 1, wx.EXPAND)
self.SetSizer(mainSizer)
self.Show(True)
app = wx.App(False)
frame = MyFrame(None, 'Small editor')
app.MainLoop()
I've poked around with wx.SetInsertionPoint, but that does not seem to have any effect.
Ah, derp. I had to look further up the inheritance chain.
You can simply call SetFocus() on the control (in this case, self.control.SetFocus()).
SetFocus() is a member function of wxWindow. I was only looking at the docs for wxTextCtrl.
Of course, I didn't think to look up the inheritance chain until I had already asked the question.
I'm leaving this here, as this is a pretty hard to google issue. Hopefully this will help someone else.

How to add widgets inside a StaticBox, wxPython?

it is possible to add things into a StaticBox, in wxPython?
I know this is a rather simple question however I cannot seem to find anything on Google.
I would like to actually be able to add things into the StaticBox, such as buttons.
Looking at the code from one of my old projects, it looks like I used a StaticBoxSizer, and added the elements to this sizer.
Quick test:
app = wx.App(redirect=False)
frame = wx.Frame(None)
static_box = wx.StaticBox(frame, label='Label')
sizer = wx.StaticBoxSizer(static_box, wx.VERTICAL)
for i in range(5):
sizer.Add(wx.Button(frame, label='Button ' + str(i)))
frame.Sizer = sizer
frame.Sizer.Fit(frame)
frame.Show()
app.MainLoop()

Categories

Resources