Consider this example (tried on python2.7, Ubuntu 11.04):
import wx
import wx.lib.agw.knobctrl as KC
# started from: http://wxpython.org/Phoenix/docs/html/lib.agw.knobctrl.html
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "KnobCtrl Demo")
self.panel = wx.Panel(self)
self.knob1 = KC.KnobCtrl(self, -1, size=(100, 100))
self.knob1.SetTags(range(0, 151, 10))
self.knob1.SetAngularRange(-45, 225)
self.knob1.SetValue(45)
# explicit sizes here - cannot figure out the expands ATM
self.text_ctrl_1 = wx.TextCtrl(self, -1, "0", size=(50, -1))
self.slider_1 = wx.Slider(self, -1, 0, -12, 12, style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS|wx.SL_INVERSE, size=(150, -1))
self.text_ctrl_2 = wx.TextCtrl(self, -1, "0", size=(50, -1))
main_sizer = wx.BoxSizer(wx.VERTICAL)
main_sizer.Add(self.knob1, 0, wx.EXPAND | wx.ALL, 20)
main_sizer.Add(self.text_ctrl_1, 0, wx.EXPAND, 20)
main_sizer.Add(self.slider_1, 0, wx.EXPAND , 20)
main_sizer.Add(self.text_ctrl_2, 0, wx.EXPAND, 20)
self.panel.SetSizer(main_sizer)
main_sizer.Layout()
self.knob1.Bind(KC.EVT_KC_ANGLE_CHANGED, self.OnAngleChanged)
self.slider_1.Bind(wx.EVT_SCROLL, self.OnSliderScroll)
def OnAngleChanged(self, e):
theknob = e.EventObject
x = theknob._mousePosition.x
y = theknob._mousePosition.y
ang = theknob.GetAngleFromCoord(x, y)
self.text_ctrl_1.SetValue("%.2f" % (ang))
self.text_ctrl_1.Refresh() # no dice
def OnSliderScroll(self, e):
obj = e.GetEventObject()
val = obj.GetValue()
self.text_ctrl_2.SetValue(str(val))
# our normal wxApp-derived class, as usual
app = wx.App(0)
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
It results with something like this:
The thing is: if you move the slider very fast, you will notice the bottommost text box updates also rather fast; but if you move the rotary knob very fast, it seems its text box (below it) changes with much reduced frequency ?!
Why is this; and would it be possible to get the response speed of the rotary knob's text box to be as fast as slider's one?
Ok, I think I got something working, but I'm not 100% sure, so a more erudite answer would be appreciated. First, note that:
The wx.Slider widget is (I think) implemented in C; so the only time Python "knows" anything about its execution is when the widget broadcasts an event
The wx.lib.agw.knobctrl.KnobCtrl is implemented in Python; thus the Python interpreter "knows" (as it has to run it) each line of code of the widget's execution.
So, what I did, is I tried to trace the execution, with:
python -m trace --trace --timing test.py > test.pylog
What I could notice is that: OnSliderScroll appeared in the log apparently without being triggered by a mouse event, while multiple OnMouseEvents would appear from knobctrl.py (the mouseovers), and only some would trigger SetTrackPosition() which eventually calls OnAngleChanged(). But even more importantly, there was a ton of _gdi_.DC_DrawLine in the log! Then, looking at the knobctrl.py source, I realised that the gradient is actually painted in a Python for loop, line-by-line:
def DrawDiagonalGradient(self, dc, size):
...
for ii in xrange(0, maxsize, 2):
currCol = (r1 + rf, g1 + gf, b1 + bf)
dc.SetPen(wx.Pen(currCol, 2))
dc.DrawLine(0, ii+2, ii+2, 0)
...
... so I thought, this must be hogging the time! So, what is done in the code below, is that a subclass is derived from KnobCtrl, where DrawDiagonalGradient() so it uses a plain fill instead of a gradient, which works much faster.
So, the code below compares the original and the derived variant, using the same event handler and the same textfield; you can check out the video at https://vid.me/kM8V, which looks something like this:
You'll notice the textctrl barely changes when the original knob is turned (even if, notably, the printouts are emitted with the expected speed) ; but updates pretty decently when the derived knob with "plain" background is turned (nearly as fast as the slider). I think the "plain" goes even faster when there is no draw of any kind in the overloaded method, but then the previous positions of the knob dot are not erased. My guess is, the draws of the original knob's gradient take up so much time in the allocated drawing timeframe, that Python needs to drop other queued updates for that frame, here notably the update of the text control.
Note that the knob emits both KC.EVT_KC_ANGLE_CHANGING (which will not refresh the draw unless e.Skip() is present in the handler), and KC.EVT_KC_ANGLE_CHANGED; however, as far as I can see, they always follow each other, so below *CHANGED is used for both.
Of course, if I've misunderstood the problem and the solution, I'd love to know...
import wx
import wx.lib.agw.knobctrl as KC
# started from: http://wxpython.org/Phoenix/docs/html/lib.agw.knobctrl.html
class KnobCtrlPlain(KC.KnobCtrl):
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
size=wx.DefaultSize,
agwStyle=KC.KC_BUFFERED_DC):
super(KnobCtrlPlain, self).__init__(parent, id, pos, size, agwStyle)
def DrawDiagonalGradient(self, dc, size):
col1 = self._startcolour
r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
sizex, sizey = size
# must have a filled draw here, to erase previous draws:
dc.SetPen(wx.TRANSPARENT_PEN)
dc.SetBrush(wx.Brush(col1, wx.SOLID))
#~ dc.DrawCircle(self.Width/2, self.Height/2, sizex)
dc.DrawRectangle(0, 0, sizex, sizey) # same effect as circle; prob. faster?
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "KnobCtrl DemoB")
self.panel = wx.Panel(self)
self.knob1 = KC.KnobCtrl(self.panel, -1, size=(100, 100))
self.knob1.SetTags(range(0, 151, 10))
self.knob1.SetAngularRange(-45, 225)
self.knob1.SetValue(45)
self.knob2 = KnobCtrlPlain(self.panel, -1, size=(100, 100))
self.knob2.SetTags(range(0, 151, 10))
self.knob2.SetAngularRange(-45, 225)
self.knob2.SetValue(45)
self.text_ctrl_1 = wx.TextCtrl(self.panel, -1, "0", size=(50, -1))
self.slider_1 = wx.Slider(self.panel, -1, 0, -12, 12, style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS|wx.SL_INVERSE, size=(150, -1))
self.text_ctrl_2 = wx.TextCtrl(self.panel, -1, "0", size=(50, -1))
main_sizer = wx.BoxSizer(wx.VERTICAL)
main_sizer.Add(self.knob1, 0, wx.EXPAND | wx.ALL, 20)
main_sizer.Add(self.text_ctrl_1, 0, wx.EXPAND, 20)
main_sizer.Add(self.knob2, 0, wx.EXPAND | wx.ALL, 20)
main_sizer.Add(self.slider_1, 0, wx.EXPAND , 20)
main_sizer.Add(self.text_ctrl_2, 0, wx.EXPAND, 20)
self.panel.SetSizer(main_sizer)
main_sizer.Layout()
self.knob1.Bind(KC.EVT_KC_ANGLE_CHANGED, self.OnAngleChanged)
self.knob2.Bind(KC.EVT_KC_ANGLE_CHANGED, self.OnAngleChanged)
self.slider_1.Bind(wx.EVT_SCROLL, self.OnSliderScroll)
def OnAngleChanged(self, e):
theknob = e.EventObject
x = theknob._mousePosition.x
y = theknob._mousePosition.y
ang = theknob.GetAngleFromCoord(x, y)
strval = str("%.2f" % (ang))
print("ac: " + strval)
self.text_ctrl_1.SetValue(strval)
def OnSliderScroll(self, e):
obj = e.GetEventObject()
val = obj.GetValue()
strval = str(val)
print("ss: " + strval)
self.text_ctrl_2.SetValue(strval)
# our normal wxApp-derived class, as usual
app = wx.App(0)
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
Related
I want to change the color for my whole pythonwx application. I found out that the currently used colors are noted down in wx.Frame.DefaultAttributes.colBg respectively.colFg. I checked with paint that these are really the used colors.
Now there is a wx.Frame.GetDefaultAttributes() but not wx.Frame.SetDefaultAttributes() method. But I still need to change the color and I do not think that setting every control manually is a desired solution.
I tried:
frame.DefaultProperties = customProperties
and
frame.DefaultProperties.colBg = customColor
but both throw a AttributeError ("can't set attribute"). Any help is appreciated.
The default properties are probably defined within whatever theme you have set for the desktop. I do not believe that there is a way to redefine these from within wxpython.
The easiest way that I have found to set a default colour scheme, is to set the colours for each of the children in an object, such as a panel.
In the following code, keep pressing the Encrypt button to see the results.
import wx
from random import randrange
class CipherTexter(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(1000, 600))
self.panel = wx.Panel(self)
cipherText = wx.StaticText(self.panel, label="Cipher Texter ", pos=(20, 30))
encryptorText = wx.StaticText(self.panel, label="Encryptor ", pos=(20, 70))
decryptorText = wx.StaticText(self.panel, label="Decryptor ", pos=(20, 100))
self.cipher = wx.TextCtrl(self.panel, -1, style=wx.TE_MULTILINE, size=(400,400), pos=(400, 30))
self.encryptor = wx.TextCtrl(self.panel, -1, size=(100,30), pos=(200, 70))
self.decryptor = wx.TextCtrl(self.panel, -1, size=(100,30), pos=(200, 100))
self.encrypt = wx.Button(self.panel, -1, "Encrypt", pos=(20, 140))
self.decrypt = wx.Button(self.panel, -1, "Decrypt", pos=(20, 180))
self.panel.SetBackgroundColour('white')
self.encrypt.Bind(wx.EVT_BUTTON, self.encryptNow)
self.decrypt.Bind(wx.EVT_BUTTON, self.decryptNow)
self.Show()
def AColour(self):
red = randrange(0,255)
green = randrange(0,255)
blue = randrange(0,255)
x = wx.Colour(red,green,blue)
return x
def encryptNow(self, event):
cfg_colour = self.AColour()
txt_colour = self.AColour()
children = self.panel.GetChildren()
for child in children:
child.SetBackgroundColour(cfg_colour)
child.SetForegroundColour(txt_colour)
print(cfg_colour)
def decryptNow(self, event):
pass
app = wx.App(False)
frame = CipherTexter(None, "The SS Cipher")
app.MainLoop()
I have created a pop up window, but the TextCtrl is not fully expanded to fill up the window. It works great if I use StaticText instead, (but if content too large then I would need the scroll bar, that is why I am using TextCtrl now). Please provide some guidance.
self.description = WindowPopup(self, wx.SIMPLE_BORDER, content)
btn = event.GetEventObject()
dw = wx.DisplaySize()[0]
width = self.description.GetSize()[0]
y = btn.ClientToScreen((0,0))[1]
height = btn.GetSize()[1]
x = dw - width - 20 - 10
self.description.Position((x, y), (0, height))
self.description.Show(True)
class WindowPopup(wx.PopupWindow):
""" Pops up a window to provide description for the selection """
def __init__(self, parent, style, content):
wx.PopupWindow.__init__(self, parent, style)
self.SetSize((700, 287))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
st = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE | wx.TE_READONLY)
st.SetValue(content)
sizer.Add(st, 0, wx.EXPAND)
panel.SetSizer(sizer)
I suspect your problem is that the panel is not as big as the popupwindow ... so even though the textfield is expanding to fill its sizer area it is not filling the popup its self.
try using something like
def __init__(...):
...
self.SetMinSize((700,287))
sizer2 = wx.BoxSizer()
sizer2.Add(panel)
self.SetSizer(sizer2)
also make sure that you are calling layout on it at some point (note this is totally untested... so it may need some tweeks, or even worse just be wrong...)
The actual answer is:
sizer = wx.BoxSizer(wx.VERTICAL)
st = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE | wx.TE_READONLY, size = (500, 174))
st.SetValue(content)
self.SetSize((500, 174))
sizer.Add(st, 0, wx.EXPAND)
self.SetSizer(sizer)
self.Layout()
self.Show(True)
Credits to Joran for noticing Layout().
PopupWindow does not require an additional panel, because the window itself can have sizer set to it. This has been realized by using the wxPython Widget Inspection Tool.
Make sure TextCtrl and PopupWindow have the same size.
MMGP Answerd but wont let me credit him right ;-) So I will at least mention him here.
(And I did finally get to credit him... 8-)
His linked discussion on Double Buffering provided a base code that worked with the following modifications:
Insert this beginning at line 106 (overwritting existing code until you get to the last line shown here):
# Here's the actual drawing code.
cliWidth, cliHeight = self.GetClientSize()
bmp=wx.Bitmap("Logo16x9.png")
bmpWide = bmp.GetWidth()
bmpHeight = bmp.GetHeight()
img = bmp.ConvertToImage()
scaleFactor = cliWidth/bmpWide
bmp = wx.BitmapFromImage(img.Scale(int(bmpWide * scaleFactor), int(bmpHeight * scaleFactor)))
bmpWide = bmp.GetWidth()
bmpHeight = bmp.GetHeight()
xPos = (cliWidth - (bmpWide))/2
yPos = (cliHeight - (bmpHeight))/2
# altered by me
dc.DrawBitmap(bmp, xPos, yPos)
class TestFrame(wx.Frame):
I've been beating my head against this all day.
I'm new to drawing graphics with the wxPython modules, and when I needed to draw a background image on a frame I found this code which works well if the image is the full size of the window.
However, I need to place a company logo as the background, and have it remain centered through resizes. In it's current form the resize causes a graphic artifact the size of a small nation to appear on the screen with any resize event.
The logo image file (used on line 43 of the code) is a 400x300 (WxH) image.
I am looking for a way to either: resize my image on the fly to match wx.GetClientSize(),
or a way to avoid/remove the artifact. Preferably without involving PIL or ImageMagick. App has to function on a local level only, and be system agnostic (Win, Lin and Mac), none of this involves network activities or mapped drives.
Python 2.7 and wxPython 2.8
The code I am using (with my modification annotated) is as follows:
import wx
########################################################################
class MainPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.frame = parent
sizer = wx.BoxSizer(wx.VERTICAL)
hSizer = wx.BoxSizer(wx.HORIZONTAL)
for num in range(4):
label = "Button %s" % num
btn = wx.Button(self, label=label)
sizer.Add(btn, 0, wx.ALL, 5)
hSizer.Add((1,1), 1, wx.EXPAND)
hSizer.Add(sizer, 0, wx.TOP, 100)
hSizer.Add((1,1), 0, wx.ALL, 75)
self.SetSizer(hSizer)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
#----------------------------------------------------------------------
def OnEraseBackground(self, evt):
"""
Add a picture to the background
"""
# yanked from ColourDB.py
dc = evt.GetDC()
# Added by me
cliWidth, cliHeight = self.GetClientSize()
if not dc:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
dc.SetClippingRect(rect)
dc.Clear()
# use a 400x300 image
bmp = wx.Bitmap("Logo4x3.png")
# added by me
xPos = (cliWidth - 400)/2
yPos = (cliHeight - 300)/2
# altered by me
dc.DrawBitmap(bmp, xPos, yPos)
#dc.DrawBitmap(bmp, 0, 0)
########################################################################
class MainFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, size=(600,450))
panel = MainPanel(self)
self.Center()
########################################################################
class Main(wx.App):
""""""
#----------------------------------------------------------------------
def __init__(self, redirect=False, filename=None):
"""Constructor"""
wx.App.__init__(self, redirect, filename)
dlg = MainFrame()
dlg.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = Main()
app.MainLoop()
Update: Latest Failure - Modified lines 37 to 52
if not dc:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
dc.SetClippingRect(rect)
dc.Clear()
# use a 400x300 image
bmp = wx.Bitmap("Logo4x3.png")
img = bmp.ConvertToImage()
scaleFactor = cliWidth/400
bmp = wx.BitmapFromImage(img.Scale(int(400*scaleFactor),int(300*scaleFactor)))
# added by me
#xPos = (cliWidth - 400)/2
#yPos = (cliHeight - 300)/2
# altered by me
#dc.DrawBitmap(bmp, xPos, yPos)
dc.DrawBitmap(bmp, 0, 0)
Another attempt and another fail. No difference in the output to screen. Additionally, the referenced document on double buffering does not address this issue, but does suffer from the same result. This code modifies lines 36 through 57 of the original.
brsh = wx.Brush('#000000')
if not dc:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
dc.SetClippingRect(rect)
dc.SetBackground(brsh)
dc.SetDeviceOrigin(0,0)
dc.DestroyClippingRegion()
dc.Clear()
# use a 400x300 image
bmp = wx.Bitmap("Logo4x3.png")
img = bmp.ConvertToImage()
scaleFactor = cliWidth/400
bmp = wx.BitmapFromImage(img.Scale(int(400*scaleFactor),int(300*scaleFactor)))
# added by me
#xPos = (cliWidth - 400)/2
#yPos = (cliHeight - 300)/2
# altered by me
#dc.DrawBitmap(bmp, xPos, yPos)
dc.DrawBitmap(bmp, 0, 0)
From the comments I suggested using double buffered drawing, but I didn't see that in the edited post. Also, I had several drawing issues when self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) was used. But this line might be helpful in other systems beyond mine, so I wanted to keep it. So, in order to handle the situation, here is a updated code that uses double buffered drawing and works fine here:
import wx
class MainPanel(wx.Panel):
def __init__(self, parent, bg_img='Logo4x3.png'):
wx.Panel.__init__(self, parent=parent)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.bg = wx.Bitmap(bg_img)
self._width, self._height = self.bg.GetSize()
sizer = wx.BoxSizer(wx.VERTICAL)
hSizer = wx.BoxSizer(wx.HORIZONTAL)
for num in range(4):
btn = wx.Button(self, label="Button %s" % num)
sizer.Add(btn, 0, wx.ALL, 5)
hSizer.Add((1,1), 1, wx.EXPAND)
hSizer.Add(sizer, 0, wx.TOP, 100)
hSizer.Add((1,1), 0, wx.ALL, 75)
self.SetSizer(hSizer)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
def OnSize(self, size):
self.Layout()
self.Refresh()
def OnEraseBackground(self, evt):
pass
def OnPaint(self, evt):
dc = wx.BufferedPaintDC(self)
self.Draw(dc)
def Draw(self, dc):
cliWidth, cliHeight = self.GetClientSize()
if not cliWidth or not cliHeight:
return
dc.Clear()
xPos = (cliWidth - self._width)/2
yPos = (cliHeight - self._height)/2
dc.DrawBitmap(self.bg, xPos, yPos)
app = wx.App()
frame = wx.Frame(None, size=(400,300))
panel = MainPanel(frame)
frame.Show()
app.MainLoop()
The method OnEraseBackground is intentionally empty.
How can I display the full content of a HtmlWindow? I have some HtmlWindows in a scrollable panel and I want to see the full text in these windows. I have tried setting the proportion to 1 and the style to wx.EXPAND, but that doesn't work.
Currently it looks like this:
But I want to see in the windows the full text:
some long text
with multiple lines
and another line
Sample code:
import wx
from wx import html
from wx.lib.scrolledpanel import ScrolledPanel
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.notebook_1 = wx.Notebook(self, -1, style=0)
self.notebook_1_pane_1 = ScrolledPanel(self.notebook_1, -1)
sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
sizer_2 = wx.BoxSizer(wx.VERTICAL)
for _ in xrange(10):
self.html = html.HtmlWindow(self.notebook_1_pane_1)
self.html.SetPage('some long text<br />with multiple lines<br />' \
'and another line')
self.html.SetBorders(0)
self.sizer_3_staticbox = wx.StaticBox(self.notebook_1_pane_1, -1,
'a')
sizer_3 = wx.StaticBoxSizer(self.sizer_3_staticbox, wx.VERTICAL)
sizer_3.Add(self.html, 1, wx.EXPAND, 0)
sizer_2.Add(sizer_3, 0, wx.EXPAND, 0)
self.notebook_1_pane_1.SetSizer(sizer_2)
self.notebook_1.AddPage(self.notebook_1_pane_1, "tab1")
sizer_1.Add(self.notebook_1, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
self.notebook_1_pane_1.SetScrollRate(20, 20)
if __name__ == "__main__":
app = wx.PySimpleApp()
frame_1 = MyFrame(None, -1, size=(400, 300))
app.SetTopWindow(frame_1)
frame_1.Show()
app.MainLoop()
You need to give sizer_2 a proportion of 1 or more as well.
sizer_2.Add(sizer_3, 1, wx.EXPAND, 0)
This makes the sizer_3 element stretch appropriately too. Otherwise, it only stretches in one direction. I would reduce the number of HTMLWindows you're putting in too unless you're using a high resolution monitor. Expanding this out so that all the text was visible in all the Windows is difficult on these low res wide screens.
I'm using wxPython to build a GUI and I'm trying to align some text but it's not working at all.
I'm trying align three different static text items in three places (right aligned, center aligned, and left aligned) in three seperate panels. The result that I'm getting though is that all three static text controls are aligned in the top left corners of their respective panels.
This is code I have:
self.lastText=wx.StaticText(self.textDisplayPanel1, label=self.lastWords, style=wx.ALIGN_RIGHT)
self.currentText=wx.StaticText(self.textDisplayPanel2, label=self.currentWords, style=wx.ALIGN_CENTRE)
self.nextText=wx.StaticText(self.textDisplayPanel3, label=self.nextWords, style=wx.ALIGN_LEFT)
Any ideas on how I fix this?
Thank you!
I'm running mac OSX 10.7.2 with Python 2.7.1 and wxPython 2.8.12.1
Edit: Although everything commented below works on windows, the first option would not work on, for example, Ubuntu due to maybe a bug. A previous post given in the comments indicate that the same problem is found in OSX.
In any case, the second option using vertical sizers works both in Ubuntu and windows so you could try it on OSX.
Your text has the instruction to align in the way you want with wx.ALIGN... and, in fact, it is aligned. However the size of the StaticText is not that of the panel but just the size of the text. Having restricted its position to its own size, you can not see the diference between the alignment modes.
You have two options to solve the problem:
Option 1. Expand the StaticText widget size and position your text on it
You could expand the size of your StaticText widget using its size parameter. This is a bad solution except for fixed size parents or frames you are not going to change size or reuse in other applications. If the size of the widget containing the text changes then the relative position of the text will be also modified because its size stays fixed. So it is always better to organize your widgets by means of sizers.
The proportion of the available space the widget occupies in the sizer slot is given by the second parameter in sizer.Add() (0 is minimal size, 1 is full occupation):
sizer_2.Add(self.label_1, 0, 0, 0)
To see the text aligned in the panel as you want you have to tell the StaticText to expand to all the space available:
sizer_2.Add(self.label_1, 1, 0, 0)
Here you have the relevant code:
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.panel_1 = wx.Panel(self, -1)
self.label_1 = wx.StaticText(self.panel_1, -1, "label_1", style=wx.ALIGN_RIGHT)
self.panel_2 = wx.Panel(self, -1)
self.label_2 = wx.StaticText(self.panel_2, -1, "label_2", style=wx.ALIGN_CENTRE)
self.panel_3 = wx.Panel(self, -1)
self.label_3 = wx.StaticText(self.panel_3, -1, "label_3")
self.panel_1.SetBackgroundColour(wx.Colour(0, 255, 0))
self.panel_2.SetBackgroundColour(wx.Colour(0, 255, 255))
self.panel_3.SetBackgroundColour(wx.Colour(219, 112, 147))
sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
sizer_4 = wx.BoxSizer(wx.HORIZONTAL)
sizer_2.Add(self.label_1, 1, 0, 0)
sizer_3.Add(self.label_2, 1, 0, 0)
sizer_4.Add(self.label_3, 1, 0, 0)
self.panel_1.SetSizer(sizer_2)
self.panel_2.SetSizer(sizer_3)
self.panel_3.SetSizer(sizer_4)
sizer_1.Add(self.panel_1, 1, wx.EXPAND, 0)
sizer_1.Add(self.panel_2, 1, wx.EXPAND, 0)
sizer_1.Add(self.panel_3, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.Layout()
Note the code is longer than needed in order to mimick your example with three panels. You obtain the same frame view using one only panel. In fact it could be simplified further not using panels and setting the StaticText directly on the sizer:
class MyFrame2(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.label_1 = wx.StaticText(self, -1, "label_1", style=wx.ALIGN_RIGHT)
self.label_2 = wx.StaticText(self, -1, "label_2", style=wx.ALIGN_CENTRE)
self.label_3 = wx.StaticText(self, -1, "label_3")
self.label_1.SetBackgroundColour(wx.Colour(127, 255, 0))
self.label_2.SetBackgroundColour(wx.Colour(0, 255, 255))
self.label_3.SetBackgroundColour(wx.Colour(219, 112, 147))
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.label_1, 1, wx.EXPAND, 0)
sizer.Add(self.label_2, 1, wx.EXPAND, 0)
sizer.Add(self.label_3, 1, wx.EXPAND, 0)
self.SetSizer(sizer)
sizer.Fit(self)
self.Layout()
Option 2. Locate the widget itself in the desired position at the available space of the sizer.
You could use the position parameter of StaticText for that purpose. But this would have the same problems indicated above for the use of size. So again you want to control the geometry of your views with sizers.
You position the widget in the sizer using one of:
sizer_6.Add(self.label_5, 0, wx.ALIGN_RIGHT, 0)
or
sizer_7.Add(self.label_6, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
For some reason, for this to work you need a vertical BoxSizer (and in the same way, if you would like to use wx.ALIGN_CENTER_VERTICAL you will need an horizontal BoxSizer:
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.panel_4 = wx.Panel(self, -1)
self.label_5 = wx.StaticText(self.panel_4, -1, "label_5")
self.panel_5 = wx.Panel(self, -1)
self.label_6 = wx.StaticText(self.panel_5, -1, "label_6")
self.panel_6 = wx.Panel(self, -1)
self.label_7 = wx.StaticText(self.panel_6, -1, "label_7")
self.panel_4.SetBackgroundColour(wx.Colour(0, 255, 255))
self.panel_5.SetBackgroundColour(wx.Colour(127, 255, 0))
self.panel_6.SetBackgroundColour(wx.Colour(219, 112, 219))
sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
sizer_8 = wx.BoxSizer(wx.VERTICAL)
sizer_7 = wx.BoxSizer(wx.VERTICAL)
sizer_6 = wx.BoxSizer(wx.VERTICAL)
sizer_6.Add(self.label_5, 0, wx.ALIGN_RIGHT, 0)
sizer_7.Add(self.label_6, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
sizer_8.Add(self.label_7, 0, 0, 0)
self.panel_4.SetSizer(sizer_6)
self.panel_5.SetSizer(sizer_7)
self.panel_6.SetSizer(sizer_8)
sizer_1.Add(self.panel_4, 1, wx.EXPAND, 0)
sizer_1.Add(self.panel_5, 1, wx.EXPAND, 0)
sizer_1.Add(self.panel_6, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.Layout()
This option implies a combination of panels and sizers that produces a code that is more difficult to simplify than that shown for the first option.
I think that text is not aligned because the size is not set. Try:
t1=wx.StaticText(smth, label="foo", pos=(0,0), size=(200,20), style=wx.ALIGN_RIGHT)
t2=wx.StaticText(smth, label="bar", pos=(0,0), size=(200,20), style=wx.ALIGN_CENTRE)
And it will be aligned inside a 200*20 box. By default the size is "auto" i.e. (-1,-1) i.e. just enough for the text to be visible, and aligning in that box will not give visible effect.
However, this does not always work.
I also had the same problem yesterday, I use MAC OS X, wxpython 4.0.6.
At last I find out this script works for me.
And I realise that if u don't SetSizer at last, the statictext will not align at center.
My code