wxPython - centering text in wx.Notebook tabs? - python-3.x

I'm writing an application using wxPython for the GUI. I'm almost there, but there are some minor aesthetic things that are bugging me. Namely, at the end of the program (the 'results' frame), I have two nested notebooks. I'd like the text for the page-select tabs to be centered in said tab -- not left-aligned as is the default. I've been through the wxPython docs and searched my best, but with no luck. Does anyone know how to do this?
I've attached some minimal code showing the behavior I don't want (namely, left-aligned text in the tabs you select the notebook pages with), as well as a screenshot from the real-live app (which has, naturally, a bit more complicated interface -- but the solution, if it exists, should be something simple, I assume)
import wx
class ResultFrame(wx.Frame):
def __init__(self, parent):
super(ResultFrame, self).__init__(parent, -1, title = "Results",
style=wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.RESIZE_BORDER)
self.targets = ['these','are','some','strings']
self.SetMinSize(wx.Size(1590,900))
self.InitUI()
def InitUI(self):
self.mainpnl = wx.Panel(self)
self.mainpnl.SetSize(wx.Size(1590,900))
self.mainsizer = wx.BoxSizer(wx.HORIZONTAL)
self.ctrlsizer = wx.BoxSizer(wx.VERTICAL)
self.restart = wx.Button(self.mainpnl, wx.ID_ANY, "Start Over",\
size=wx.Size(150,90))
self.interp = wx.Button(self.mainpnl, wx.ID_ANY, "Help Interpreting\n"
"Results", size=wx.Size(150,90))
self.nb = wx.Notebook(self.mainpnl)
for idx, tar in enumerate(self.targets):
self.nb.AddPage(wx.Panel(self.nb, -1, size=wx.Size(850,820)), tar)
self.ctrlsizer.Add(self.restart, 0, wx.ALL, 5)
self.ctrlsizer.AddStretchSpacer()
self.ctrlsizer.Add(self.interp, 0, wx.ALL, 5)
self.mainsizer.Add(self.ctrlsizer, 0, wx.ALL, 0)
self.mainsizer.Add(self.nb, 1, wx.EXPAND)
self.mainpnl.SetSizerAndFit(self.mainsizer)
def main():
app = wx.App()
frm=ResultFrame(None)
frm.Show()
app.MainLoop()
if __name__ == "__main__":
main()

In wxPython Sizers are used for laying out a window or collection of windows.
A notebook is no different, in that it too, can have its tabs laid out with a sizer or collection of sizers.
Sizers allow both positioning and alignment.
import wx
class ResultFrame(wx.Frame):
def __init__(self, parent):
super(ResultFrame, self).__init__(parent, -1, title = "Results",
style=wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.RESIZE_BORDER)
#self.targets = ['these','are','some','strings']
self.SetMinSize(wx.Size(1000,-1))
self.InitUI()
def InitUI(self):
self.mainpnl = wx.Panel(self)
self.mainpnl.SetSize(wx.Size(1590,900))
self.mainsizer = wx.BoxSizer(wx.HORIZONTAL)
self.ctrlsizer = wx.BoxSizer(wx.VERTICAL)
self.restart = wx.Button(self.mainpnl, wx.ID_ANY, "Kubla Khan",\
size=wx.Size(150,90))
self.interp = wx.Button(self.mainpnl, wx.ID_ANY, "Samuel Taylor Coleridge\n"
"Biography", size=wx.Size(150,90))
self.nb = wx.Notebook(self.mainpnl)
# for idx, tar in enumerate(self.targets):
# self.nb.AddPage(wx.Panel(self.nb, -1, size=wx.Size(850,820)), tar)
tab1 = wx.Panel(self.nb, -1)
tab2 = wx.Panel(self.nb, -1)
msg = '''In Xanadu did Kubla Khan
A stately pleasure dome decree:
Where Alph, the sacred river, ran
Through caverns measureless to man
Down to a sunless sea.
So twice five miles of fertile ground
With walls and towers were girdled round:
And there were gardens bright with sinuous rills,
Where blossomed many an incense-bearing tree;
And here were forests ancient as the hills,
Enfolding sunny spots of greenery.'''
t1_text = wx.StaticText(tab1, -1, label=msg)
t1_butt = wx.Button(tab1, -1, label="Next")
t1sizer = wx.BoxSizer(wx.VERTICAL)
t1sizer.Add(t1_text, 0, wx.ALIGN_CENTER|wx.ALL, 5)
t1sizer.Add(t1_butt, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
tab1.SetSizer(t1sizer)
self.nb.AddPage(tab1,"Stanza 1")
msg = '''But oh! that deep romantic chasm which slanted
Down the green hill athwart a cedarn cover!
A savage place! as holy and enchanted
As e'er beneath a waning moon was haunted
By woman wailing for her demon lover!
And from this chasm, with ceaseless turmoil seething,
As if this earth in fast thick pants were breathing,
A mighty fountain momently was forced:
Amid whose swift half-intermitted burst
Huge fragments vaulted like rebounding hail,
Or chaffy grain beneath the thresher's flail:
And ’mid these dancing rocks at once and ever
It flung up momently the sacred river.
Five miles meandering with a mazy motion
Through wood and dale the sacred river ran,
Then reached the caverns measureless to man,
And sank in tumult to a lifeless ocean:
And ’mid this tumult Kubla heard from far
Ancestral voices prophesying war!'''
t2_text = wx.StaticText(tab2, -1, label=msg)
t2_pbutt = wx.Button(tab2, -1, label="Prev")
t2_nbutt = wx.Button(tab2, -1, label="Next")
t2sizer = wx.BoxSizer(wx.VERTICAL)
t2sizer.Add(t2_text, 0, wx.ALIGN_CENTER|wx.ALL, 5)
t2sizer.Add(t2_pbutt, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
t2sizer.Add(t2_nbutt, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
tab2.SetSizer(t2sizer)
self.nb.AddPage(tab2,"Stanza 2")
self.ctrlsizer.Add(self.restart, 0, wx.ALL, 5)
self.ctrlsizer.AddStretchSpacer()
self.ctrlsizer.Add(self.interp, 0, wx.ALL, 5)
self.mainsizer.Add(self.ctrlsizer, 0, wx.ALL, 0)
self.mainsizer.Add(self.nb, 1, wx.EXPAND)
self.mainpnl.SetSizerAndFit(self.mainsizer)
t1_butt.Bind(wx.EVT_BUTTON, self.Next)
t2_nbutt.Bind(wx.EVT_BUTTON, self.Next)
t2_pbutt.Bind(wx.EVT_BUTTON, self.Prev)
def Next(self, event):
p = self.nb.GetSelection()
p += 1
p = min(p, self.nb.GetRowCount())
self.nb.SetSelection(p)
def Prev(self, event):
p = self.nb.GetSelection()
p -= 1
p = max(0, p)
self.nb.SetSelection(p)
def main():
app = wx.App()
frm=ResultFrame(None)
frm.Show()
app.MainLoop()
if __name__ == "__main__":
main()

Related

Inner Panel doesnt respond

So my goal is do create a settings Window. On the left side you have the different categories. Which you can switch by clicking on them. On the right side is the panel that will switch the content based on what thing you clicked on.
I implemented it by creating a class for every content panel, instantiate it from the frame and Hide all the panels except the one that was choosen.
My problem is that i cant interact with anything inside the panels that i hide and show. Here is a minimum example that you should be able to run.
# -*- coding: utf-8 -*-
"""
Created on Tue Jul 13 15:54:51 2021
#author: Odatas
"""
import wx
import wx.lib.scrolledpanel as scrolled
#First Panel
class settingsConnection(wx.ScrolledWindow):
def __init__(self,parent,size):
wx.ScrolledWindow.__init__(self,parent=parent,size=size)
self.topLevelSizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.topLevelSizer)
self.infoText = wx.StaticText(self
,label="I'm Panel settingsConnection\n\nUnder construction."
,style=wx.ALIGN_CENTRE_HORIZONTAL)
self.topLevelSizer.Add(self.infoText)
#Second Panel
class settingsGeneral(wx.ScrolledWindow):
def __init__(self,parent,size):
wx.ScrolledWindow.__init__(self,parent=parent,size=size)
self.topLevelSizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.topLevelSizer)
self.infoText = wx.StaticText(self
,label="I'm Panel settingsGeneral\n\nUnder construction."
,style=wx.ALIGN_CENTRE_HORIZONTAL)
self.topLevelSizer.Add(self.infoText)
#Third Panel
class settingsABC(scrolled.ScrolledPanel):
def __init__(self,parent,size):
scrolled.ScrolledPanel.__init__(self,parent=parent,size=size)
self.topLevelSizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.topLevelSizer)
self.firstSizer = wx.BoxSizer(wx.VERTICAL)
self.secondSizer = wx.BoxSizer(wx.VERTICAL)
self.thridSizer = wx.BoxSizer(wx.VERTICAL)
self.SetupScrolling()
self.SetAutoLayout(1)
self.autoStartCheckBox=wx.CheckBox(self, label="Button")
self.autoStartCheckBox.SetValue(True)
self.testButton = wx.Button(self,label="Test")
self.topLevelSizer.Add(self.autoStartCheckBox)
self.topLevelSizer.Add(self.testButton)
self.topLevelSizer.Layout()
self.Fit()
#The main frame that holds all the panels
class settingsWindow(wx.Frame):
FLAG_KILL_ME = False
def __init__(self,parent):
wx.Frame.__init__(self,parent=parent.frame,size=(1000,800),style= wx.CAPTION | wx.CLOSE_BOX |wx.STAY_ON_TOP|wx.FRAME_NO_TASKBAR)
#Event for Closing window
self.Bind(wx.EVT_CLOSE,self.onQuit)
#Event for changing the focus
self.Bind(wx.EVT_LIST_ITEM_FOCUSED,self.onFocusChange)
self.parent=parent
self.panel = wx.Panel(self)
self.panelSize=(800,700)
self.panel.SetBackgroundColour(wx.Colour(255,255,255))
#Contains Everything Level 0
self.topLevelSizer = wx.BoxSizer(wx.VERTICAL)
#Contains Top Part Level 01
self.windowSizer=wx.BoxSizer(wx.HORIZONTAL)
#Contains Bottom part Level 01
self.buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
#Contains Widgets for Mainpart
self.widgetSizer = wx.BoxSizer(wx.HORIZONTAL)
self.panel.SetSizer(self.topLevelSizer)
self.topLevelSizer.Add(self.windowSizer)
self.topLevelSizer.Add(self.buttonSizer)
self.settingsChooser=wx.ListCtrl(self.panel, id=wx.ID_ANY,style=wx.LC_LIST|wx.LC_SINGLE_SEL,size=(100,700))
self.settingsChooser.AppendColumn("Test")
self.windowSizer.Add(self.settingsChooser)
self.windowSizer.Add(self.widgetSizer)
self.panelList=[]
self.settingsChooser.InsertItem(0,"Allgemein")
self.generalPanel=settingsGeneral(self,self.panelSize)
self.panelList.append(self.generalPanel)
self.widgetSizer.Add(self.generalPanel)
self.settingsChooser.InsertItem(1,"Connection")
self.connectionPanel=settingsConnection(self,self.panelSize)
self.panelList.append(self.connectionPanel)
self.widgetSizer.Add(self.connectionPanel)
self.connectionPanel.Hide()
self.settingsChooser.InsertItem(2,"DLT")
self.dltPanel=settingsABC(self,self.panelSize)
self.panelList.append(self.dltPanel)
self.widgetSizer.Add(self.dltPanel)
self.dltPanel.Hide()
self.currentID=0
self.panel.Fit()
self.Centre()
self.Show()
#self.mythread = threading.Thread(target=self.downloadEndPackageMethod)
#self.mythread.start()
while self.FLAG_KILL_ME is False:
wx.GetApp().Yield()
try:
self.Destroy()
except Exception as e:
pass
#Shows the Selected Panel and hides the current shown.
def onFocusChange(self,event=None):
self.panelList[self.currentID].Hide()
self.panelList[event.GetIndex()].Show()
self.currentID=event.GetIndex()
self.panel.Fit()
self.panel.Layout()
def onQuit(self,event=None):
self.FLAG_KILL_ME = True
self.Hide()
#Main wx.Python App
class Testapp(wx.App):
def __init__(self,redirect=False,filename=None):
wx.App.__init__(self,redirect,filename)
self.frame=wx.Frame(None,title="Test")
self.panel=wx.Panel(self.frame,size=(1000,1000))
self.TopLevelSizer=wx.BoxSizer(wx.VERTICAL)
self.panel.SetSizer(self.TopLevelSizer)
self.settingsButton = wx.Button(self.panel,label="Settings")
self.TopLevelSizer.Add(self.settingsButton)
self.settingsButton.Bind(wx.EVT_BUTTON, self.openSettings)
self.frame.Show()
def openSettings(self,event=None):
settingsWindow(self)
if __name__=='__main__':
app=Testapp()
app.MainLoop()
Edit: Adjustest class "Settings Window" für use with wx.Listbook
class settingsWindow(wx.Frame):
FLAG_KILL_ME = False
def __init__(self,parent):
wx.Frame.__init__(self,parent=parent.frame,size=(1000,800),style= wx.CAPTION | wx.CLOSE_BOX |wx.STAY_ON_TOP|wx.FRAME_NO_TASKBAR)
self.Bind(wx.EVT_CLOSE,self.onQuit)
# self.Bind(wx.EVT_LIST_ITEM_FOCUSED,self.onFocusChange)
self.parent=parent
self.panel = wx.Panel(self)
self.panelSize=(800,700)
self.panel.SetBackgroundColour(wx.Colour(255,255,255))
#Contains Everything Level 0
self.topLevelSizer = wx.BoxSizer(wx.VERTICAL)
#Contains Top Part Level 01
self.windowSizer=wx.BoxSizer(wx.HORIZONTAL)
#Contains Bottom part Level 01
self.buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
#Contains Widgets for Mainpart
self.widgetSizer = wx.BoxSizer(wx.HORIZONTAL)
self.panel.SetSizer(self.topLevelSizer)
self.topLevelSizer.Add(self.windowSizer)
self.topLevelSizer.Add(self.buttonSizer)
self.windowSizer.Add(self.widgetSizer)
self.settingsBook = wx.Listbook(self.panel)
self.widgetSizer.Add(self.settingsBook)
self.settingsBook.InsertPage(0, settingsGeneral(self,self.panelSize), "Settins")
self.settingsBook.InsertPage(1, settingsConnection(self,self.panelSize), "Connections")
self.settingsBook.InsertPage(2, settingsABC(self,self.panelSize), "ABC")
self.panel.Fit()
self.Centre()
self.Show()
while self.FLAG_KILL_ME is False:
wx.GetApp().Yield()
try:
self.Destroy()
except Exception as e:
pass
def onQuit(self,event=None):
self.FLAG_KILL_ME = True
self.Hide()
Here is a standalone Listbook example taken from a working project.
I've hacked out the meat and left the potatoes. :)
Edit:
I have added a simple main frame from where you activate the configuration code. The main frame contains a counter, in an attempt to convince you, that the MainLoop is still active, as you seem to believe it gets hijacked in some way.
Hopefully, it will be of use.
import wx
import wx.lib.scrolledpanel as scrolled
#from os.path import expanduser
class Test(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "Main Window", size=(300, 200))
self.cnt = 0
panel = wx.Panel(self)
button = wx.Button(panel, label="Open Config", pos=(10, 10), size=(120, 30))
text = wx.StaticText(panel, wx.ID_ANY, "Is the main loop still active", pos=(10, 50))
self.counter = wx.TextCtrl(panel, wx.ID_ANY, "", pos=(10, 80), size=(120, 30))
self.Bind(wx.EVT_BUTTON, self.OnConfig, button)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
self.Show()
self.timer.Start(1000)
def OnConfig(self, event):
frame = Config(parent=self)
def OnTimer(self, event):
self.cnt += 1
self.counter.SetValue(str(self.cnt))
class Config(wx.Frame):
def __init__(self,parent):
wx.Frame.__init__(self,parent, wx.ID_ANY,"Configuration",size=(600,400))
#self.home_dir = expanduser("~")
#self.conf_dir = expanduser("~/fs2")
#self.config_file = self.conf_dir+"/fs2.cfg"
self.parent = parent
# Create the first tab and add it to the listbook
self.panel = wx.Panel(self)
self.lb = wx.Listbook(self.panel)
self.tab1 = scrolled.ScrolledPanel(self.lb, -1)
self.tab1.SetupScrolling()
self.tab1.SetBackgroundColour("Grey")
self.lb.AddPage(self.tab1, "Foot Pedal")
t1_1 = wx.StaticText(self.tab1, wx.ID_ANY, ("Pedal Device"))
self.fc_hidfile = wx.TextCtrl(self.tab1, wx.ID_ANY, "",size=(150, -1))
t1_2 = wx.StaticText(self.tab1, wx.ID_ANY, ("Pedal 1 *"))
self.fc_pedal_1 = wx.ComboBox(self.tab1, wx.ID_ANY, choices=[("Jump Back"), ("Play/Pause"), ("Jump Forward"),("Timestamp"),("Unclear"),("Comment Time stamp"),("Bookmark Time stamp"),("OSD Time stamp"),("Pedal De-activated")],style=wx.CB_READONLY)
t1_3 = wx.StaticText(self.tab1, wx.ID_ANY, ("Pedal 2 *"))
self.fc_pedal_2 = wx.ComboBox(self.tab1, wx.ID_ANY, choices=[("Jump Back"), ("Play/Pause"), ("Jump Forward"),("Timestamp"),("Unclear"),("Comment Time stamp"),("Bookmark Time stamp"),("OSD Time stamp"),("Pedal De-activated")],style=wx.CB_READONLY)
t1_4 = wx.StaticText(self.tab1, wx.ID_ANY, ("Pedal 3 *"))
self.fc_pedal_3 = wx.ComboBox(self.tab1, wx.ID_ANY, choices=[("Jump Back"), ("Play/Pause"), ("Jump Forward"),("Timestamp"),("Unclear"),("Comment Time stamp"),("Bookmark Time stamp"),("OSD Time stamp"),("Pedal De-activated")],style=wx.CB_READONLY)
t1_5 = wx.StaticText(self.tab1, wx.ID_ANY, ("Classic"))
self.fc_classic = wx.SpinCtrl(self.tab1, wx.ID_ANY,"0", min=0, max=2)#, ("Classic Play/Pause"))
t1sizer = wx.FlexGridSizer(10,2,5,5)
t1sizer.Add(t1_1, 0, wx.ALL, 5)
t1sizer.Add(self.fc_hidfile, 0, wx.ALL, 5)
t1sizer.Add(t1_2, 0, wx.ALL, 5)
t1sizer.Add(self.fc_pedal_1, 0, wx.ALL, 5)
t1sizer.Add(t1_3, 0, wx.ALL, 5)
t1sizer.Add(self.fc_pedal_2, 0, wx.ALL, 5)
t1sizer.Add(t1_4, 0, wx.ALL, 5)
t1sizer.Add(self.fc_pedal_3, 0, wx.ALL, 5)
t1sizer.Add(t1_5, 0, wx.ALL, 5)
t1sizer.Add(self.fc_classic, 0, wx.ALL, 5)
self.tab1.SetSizer(t1sizer)
self.tab2 = scrolled.ScrolledPanel(self.lb, -1)
self.tab2.SetupScrolling()
self.tab2.SetBackgroundColour("Grey")
self.lb.AddPage(self.tab2, "Control")
t2_1 = wx.StaticText(self.tab2, wx.ID_ANY, ("Include Hours in Time display"))
self.fc_time_disp = wx.ComboBox(self.tab2, wx.ID_ANY, choices=[("H"),("M")],style=wx.CB_READONLY)
t2_2 = wx.StaticText(self.tab2, wx.ID_ANY, ("Jump Length (secs) *"))
self.fc_jump_length = wx.SpinCtrl(self.tab2, wx.ID_ANY, "3", min=1, max=15)
t2_3 = wx.StaticText(self.tab2, wx.ID_ANY, ("Long Jump *"))
self.fc_l_jump_length = wx.SpinCtrl(self.tab2, wx.ID_ANY,value="", min=0, max=60)
t2_4 = wx.StaticText(self.tab2, wx.ID_ANY, ("Extra Long Jump *"))
self.fc_xl_jump_length = wx.SpinCtrl(self.tab2, wx.ID_ANY,value="", min=0, max=600)
t2_5 = wx.StaticText(self.tab2, wx.ID_ANY, ("Pause Jump (secs) *"))
self.fc_pause_jump = wx.SpinCtrl(self.tab2, wx.ID_ANY, "0", min=0, max=5)
t2_6 = wx.StaticText(self.tab2, wx.ID_ANY, ("Instant Loop length *"))
self.fc_ill = wx.SpinCtrl(self.tab2, wx.ID_ANY, "10", min=3, max=20)
t2sizer = wx.FlexGridSizer(10,2,5,5)
t2sizer.Add(t2_1, 0, wx.ALL, 5)
t2sizer.Add(self.fc_time_disp, 0, wx.ALL, 5)
t2sizer.Add(t2_2, 0, wx.ALL, 5)
t2sizer.Add(self.fc_jump_length, 0, wx.ALL, 5)
t2sizer.Add(t2_3, 0, wx.ALL, 5)
t2sizer.Add(self.fc_l_jump_length, 0, wx.ALL, 5)
t2sizer.Add(t2_4, 0, wx.ALL, 5)
t2sizer.Add(self.fc_xl_jump_length, 0, wx.ALL, 5)
t2sizer.Add(t2_5, 0, wx.ALL, 5)
t2sizer.Add(self.fc_pause_jump, 0, wx.ALL, 5)
t2sizer.Add(t2_6, 0, wx.ALL, 5)
t2sizer.Add(self.fc_ill, 0, wx.ALL, 5)
self.tab2.SetSizer(t2sizer)
# Create and add more tabs....
# read the config file and load in the data
# cfg = ConfigObj(infile = self.config_file)
# try:
# r_hidfile = cfg["control"]["HID_FILE_ID"]
# except:
# r_hid_file = "/dev/input/eventx"
# self.fc_hidfile.SetValue(r_hid_file)
#
# For demo purposes set fixed values
self.fc_hidfile.SetValue('/dev/input/eventx')
self.fc_time_disp.SetStringSelection('H')
self.fc_jump_length.SetValue(2)
self.fc_l_jump_length.SetValue(10)
self.fc_xl_jump_length.SetValue(15)
self.fc_pause_jump.SetValue(1)
self.fc_classic.SetValue(0)
self.fc_pedal_1.SetSelection(0)
self.fc_pedal_2.SetSelection(1)
self.fc_pedal_3.SetSelection(2)
self.fc_ill.SetValue(5)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.lb, 1, wx.ALL|wx.EXPAND, 5)
self.fc_Save = wx.Button(self.panel, wx.ID_ANY, "Save")
self.fc_Quit = wx.Button(self.panel, wx.ID_ANY, "Quit")
self.help_button = wx.Button(self.panel, wx.ID_ANY, "Help")
button_sizer = wx.BoxSizer(wx.HORIZONTAL)
button_sizer.Add(self.fc_Save)
button_sizer.Add(self.fc_Quit)
button_sizer.Add(self.help_button)
sizer.Add(button_sizer)
self.panel.SetSizer(sizer)
self.fc_Save.Bind(wx.EVT_BUTTON, self.OnSave)
self.fc_Quit.Bind(wx.EVT_BUTTON, self.OnQuit)
self.help_button.Bind(wx.EVT_BUTTON, self.OnHelp)
self.Layout()
self.Show()
def OnQuit(self,event):
self.Destroy()
self.Close()
def OnHelp(self, event):
# ShowHelp(parent=None,section='fs2_Config1.pdf')
return
def OnSave(self,event):
#cfg = ConfigObj(infile = self.config_file, create_empty=True, write_empty_values=True, list_values=False)
# write back new configuartion values here
#cfg["control"]["FOOTPEDAL_VPID_LOCK"] = footpedal_vpid_lock
#cfg.write()
self.Destroy()
if __name__ == "__main__":
app = wx.App()
frame = Test(None)
app.MainLoop()
You'll notice, that despite the Config window being active, the Main windows counter is still active.

How to use wxshaped window and png with transparency in Python 3?

Edit: Found out how to place a transparent wxPanel on my shaped frame, but now ı try to put a wxGridBagSizer on the wxPanel with some widgets in it but only the first widget will appear...Any ideas?
Original question :
Well I'm trying to make a wx.ShapedWindow with a png background that has transparency, the problem is that it looks like they changed the wx lib so to set the shape on the bitmap before we could use wx.RegionFromBitmap() but this is not working anymore, i tried to use wx.Region only but i can't figure out how to have the transparency ! I have a grey zone where i would like transparent region....
If anyone can help me !
Here's what i get now :
import wx
from os import getcwd
class ShapedFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, None, -1, "Shaped Window", style = wx.FRAME_SHAPED | wx.SIMPLE_BORDER )
self.hasShape = False
self.delta = wx.Point(0,0)
ImgDir = (getcwd()+u"\\img\\ex.png")
self.bmp = wx.Image(ImgDir, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
w,h = self.bmp.GetWidth(), self.bmp.GetHeight()
self.SetClientSize(w,h)
#self.panel = wx.Panel(self, -1,size=(100,100))
panel = Panel(self)
panel.Show()
dc = wx.ClientDC(self)
dc.DrawBitmap(self.bmp, 0,0, True)
self.SetWindowShape()
self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
self.Bind(wx.EVT_RIGHT_UP, self.OnExit)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_WINDOW_CREATE, self.SetWindowShape)
################# EDIT ONLY BUTTON 0 WILL APPEAR ########################
button0 = wx.Button(panel, -1, "hello 0")
button1 = wx.Button(panel, -1, "hello 1")
button2 = wx.Button(panel, -1, "hello 2")
button3 = wx.Button(panel, -1, "hello 3")
gbox7 = wx.GridBagSizer(0,0)
gbox7.SetEmptyCellSize((10,10))
gbox7.Add(button0,(0,0))
gbox7.Add(button1,(0,1))
gbox7.Add(button2,(1,0))
gbox7.Add(button3,(1,1))
panel.SetSizer(gbox7)
#######################################################################
def SetWindowShape(self, evt=None):
r = wx.Region(self.bmp,wx.TransparentColour)
self.hasShape = self.SetShape(r)
def OnDoubleClick(self, evt):
if self.hasShape:
self.SetShape(wx.Region())
self.hasShape = False
else:
self.SetWindowShape()
def OnPaint(self, evt):
dc = wx.PaintDC(self)
dc.DrawBitmap(self.bmp, 0,0, True)
def OnExit(self, evt):
self.Close()
def OnLeftDown(self, evt):
self.CaptureMouse()
pos = self.ClientToScreen(evt.GetPosition())
origin = self.GetPosition()
self.delta = wx.Point(pos.x - origin.x, pos.y - origin.y)
def OnMouseMove(self, evt):
if evt.Dragging() and evt.LeftIsDown():
pos = self.ClientToScreen(evt.GetPosition())
newPos = (pos.x - self.delta.x, pos.y - self.delta.y)
self.Move(newPos)
def OnLeftUp(self, evt):
if self.HasCapture():
self.ReleaseMouse()
class Panel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1, size=(200, 200,),style=wx.TRANSPARENT_WINDOW)
self.CenterOnParent()
app = wx.App()
ShapedFrame(None,-1,None).Show()
app.MainLoop()
Okay i found out a solution, looking in wx.coulor since 2.9 they added wxTransparentColour
so i replaced :
r = wx.Region(self.bmp,"grey",30)
with:
r = wx.Region(self.bmp,wx.TransparentColour)
and it works fine ! Hope it will help other people too :)

wxPython: How to make Dialog resize according to the content of the embedded StaticText

This is my small program for testing:
import wx
class Test(wx.Dialog):
def __init__(self, parent, title="", caption='', btnList=['OK']):
wx.Dialog.__init__(self, parent, -1, title=title)
self.btnList = btnList
self.parent = parent
sizer = wx.BoxSizer()
self.panel = wx.Panel(self, -1)
sizer.Add(self.panel)
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
self.panel.SetSizer(self.mainSizer)
self.caption = caption
self.setCaption()
self.setButton()
self.SetSizerAndFit(sizer)
def setCaption(self):
caplb = wx.StaticText(self.panel, -1, self.caption)
self.mainSizer.Add(caplb, 1, wx.EXPAND | wx.ALL, 10)
capFont = caplb.GetFont()
capFont.SetPointSize(10)
capFont.SetWeight(wx.FONTWEIGHT_BOLD)
caplb.SetFont(capFont)
caplb.SetForegroundColour(wx.RED)
def setButton(self):
self.btnBox = wx.BoxSizer(wx.HORIZONTAL)
self.mainSizer.Add(self.btnBox, 0, wx.EXPAND | wx.BOTTOM, 10)
self.btnBox.Add((1, 1), 1, wx.EXPAND)
self.btns = []
for i in range(len(self.btnList)):
self.btns.append(wx.Button(self.panel, -1, self.btnList[i]))
self.btnBox.Add(self.btns[i])
self.btnBox.Add((1, 1), 1, wx.EXPAND)
if __name__ == "__main__":
app = wx.App()
msg = ""
for i in range(8):
msg += "%s: ATTENTION! ATTENTION! ATTENTION! ATTENTION!\n" % i
dlg = Test(None, caption=msg, title="WARNING", btnList=['Yes', 'No'])
dlg.CenterOnParent()
val = dlg.ShowModal()
app.MainLoop()
I want the dialog to spread so that all text in the caplb can be seen. In most of the systems I has worked on this piece of code work well. But when I move to the new system, there is something with the gtk limit to show only half of the caplb. I don't know how to force it to show all of the StaticText. Can you please have a look at that and show me the hint? Thanks ahead!!!

Set a minimal window size so all sizers fit

I have tried to understand how sizer and sizes work but clearly, I am missing something vital. This is what I get:
Clearly, the inside panels (sizers) do not fit the window. What I want is this:
which was created by a manual re-size.
How can I achieve that?
Note that the real code has many more different wx.StaticBox so a generic explanation of what I am doing wrong would be more welcome than just a fix.
Here is the toy example code:
import wx
import wx.lib.agw.floatspin as FS
class GUIControl(wx.Frame):
def __init__(self, parent, id, title, *args, **kwargs):
super(GUIControl, self).__init__(parent, id, title, *args, **kwargs)
self.radio = {}
self.modes = ['Open loop', 'Current control', 'Voltage control']
panel = wx.Panel(self)
panel.SetBackgroundColour('#5968c3')
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self._ui(panel), 1, wx.EXPAND | wx.ALL, 3)
panel.SetSizer(sizer)
print(sizer.ComputeFittingWindowSize(self))
self.SetSize(sizer.ComputeFittingWindowSize(self))
self.Centre()
self.Show(True)
def _ui(self, parent):
panel = wx.Panel(parent)
vbox = wx.BoxSizer(wx.VERTICAL)
flags = wx.EXPAND | wx.SHAPED
vbox.Add(self._ui_prefix(panel), flag=wx.EXPAND)
vbox.Add(self._ui_control_mode(panel), flag=flags)
vbox.Add(self._ui_suffix(panel), flag=wx.EXPAND)
panel.SetSizer(vbox)
return panel
def _ui_prefix(self, parent):
panel = wx.Panel(parent)
panel.SetBackgroundColour('steelblue')
hbox = wx.BoxSizer(wx.HORIZONTAL)
_label = wx.StaticText(panel, label='')
_label.SetLabelMarkup("<b><i><big>Controls</big></i></b>")
hbox.Add(_label, flag=wx.ALIGN_CENTER | wx.ALL)
panel.SetSizer(hbox)
return panel
def _ui_control_mode(self, parent):
box = wx.StaticBox(parent, -1, "Control mode")
_label_modes = wx.StaticText(box, label='')
_label_modes.SetLabelMarkup("<i>Modes</i>")
for item in self.modes:
self.radio[item] = wx.RadioButton(box, -1, item)
_label_duty = wx.StaticText(box, label='')
_label_duty.SetLabelMarkup("<i>Duty cycle</i>")
self.sc1 = wx.SpinCtrl(box, wx.ID_ANY, '0', min=0, max=100)
_units_duty = wx.StaticText(box, label='%')
_label_current = wx.StaticText(box, label='')
_label_current.SetLabelMarkup("<i>Current</i>")
self.sc2 = FS.FloatSpin(box,
wx.ID_ANY,
value=0.0,
min_val=0.0,
max_val=20.0, # FIXME
increment=0.1)
self.sc2.SetFormat("%f")
self.sc2.SetDigits(1)
_units_current = wx.StaticText(box, label='A')
fgs = wx.FlexGridSizer(5, 3, 5, 7)
flags = wx.EXPAND | wx.ALL | wx.SHAPED
fgs.Add(_label_modes, 0, flags)
fgs.Add(self.radio['Open loop'], 0, flags)
fgs.AddStretchSpacer()
fgs.AddStretchSpacer()
fgs.Add(self.radio['Current control'], 0, flags)
fgs.AddStretchSpacer()
fgs.AddStretchSpacer()
fgs.Add(self.radio['Voltage control'], 0, flags)
fgs.AddStretchSpacer()
fgs.Add(_label_duty, 0, flags)
fgs.Add(self.sc1, 0, flags)
fgs.Add(_units_duty, 0, flags)
fgs.Add(_label_current, 0, flags)
fgs.Add(self.sc2, 0, flags)
fgs.Add(_units_current, 0, flags)
fgs.SetSizeHints(box)
fgs.Layout()
box.SetSizer(fgs)
return box
def _ui_suffix(self, parent):
panel = wx.Panel(parent)
panel.SetBackgroundColour('steelblue')
hbox = wx.BoxSizer(wx.HORIZONTAL)
hbox.Add(wx.BoxSizer(wx.VERTICAL),
flag=wx.RESERVE_SPACE_EVEN_IF_HIDDEN)
_quit = wx.Button(panel, -1, label=" Quit ")
_quit.SetBackgroundColour('#5968c3')
_quit.Bind(wx.EVT_BUTTON, self.OnQuit)
hbox.AddStretchSpacer()
hbox.Add(_quit, flag=wx.ALIGN_CENTER | wx.SHAPED | wx.ALL, border=3)
hbox.AddStretchSpacer()
panel.SetSizer(hbox)
return panel
def OnQuit(self, e):
self.Close()
if __name__ == '__main__':
app = wx.App()
GUIControl(None, -1, title='Control UI test')
app.MainLoop()
The easiest thing to do in this case is to also give the frame a sizer, add the main panel to it, and then call the frame's Fit method. For example, you can replace the call to self.SetSize in GUIControl.__init__ with this:
frameSizer = wx.BoxSizer()
frameSizer.Add(panel, 1, wx.EXPAND)
self.SetSizer(frameSizer)
self.Fit()

wx python image refresh on Windows

I have an application that involves displaying multiple images. This works as I would expect on linux, but on Windows there is an annoying flash as the images are painted. This is best seen as a little square in the top left-hand corner of the screen where a flash of colour appears. Am I not approaching this requirement in the right way? Or is there some fix I should be applying to overcome the Windows effect? Or is it just my version on Windows (I only have one to test it: Windows 7 Ultimate)?
I have tried Freeze and Thaw in refresh_sizer_cell but it didn't behave as I expected
import wx
class ImageSizer(wx.Frame):
BACKGROUND_COLOUR = (246, 244, 242)
def __init__(self, parent, title):
super(ImageSizer, self).__init__(parent, title=title)
self.main_sizer = wx.GridBagSizer()
self.SetSizer(self.main_sizer)
cmd_reset = wx.Button(self, label='Reset')
cmd_reset.Bind(wx.EVT_BUTTON, self.on_cmd_reset_click)
cmd_cancel = wx.Button(self, label='Cancel')
cmd_cancel.Bind(wx.EVT_BUTTON, self.on_cmd_cancel_click)
self.main_sizer.Add((400, 0), pos=(0, 0), span=(1, 2)) # dummy to position Available
self.main_sizer.Add((0, 100), pos=(1, 0), span=(1, 1)) # dummy to position Buttons
self.main_sizer.Add(cmd_reset, pos=(2, 2), flag=wx.LEFT | wx.TOP, border=10)
self.main_sizer.Add(cmd_cancel, pos=(2, 3), flag=wx.RIGHT | wx.BOTTOM | wx.TOP | wx.ALIGN_RIGHT, border=10)
self.SetBackgroundColour(self.BACKGROUND_COLOUR)
self.shape_types = {'available': 0, 'selected': 1}
self.available_shapes = []
self.selected_shapes = []
self.initialise()
self.Center()
self.Fit()
self.Show()
def initialise(self):
self.available_shapes = ['square', 'circle', 'triangle', 'cross']
self.selected_shapes = []
self.display_images()
def display_images(self):
available_sizer = ShapeSizer(self, self.available_shapes, self.shape_types['available'])
self.refresh_sizer_cell(self.main_sizer, available_sizer, (1, 2), (1, 3))
selected_sizer = ShapeSizer(self, self.selected_shapes, self.shape_types['selected'])
self.refresh_sizer_cell(self.main_sizer, selected_sizer, (1, 1), (2, 1))
self.Layout()
#staticmethod
def refresh_sizer_cell(sizer, item, pos, span, flag=wx.ALL, border=10):
old_item = sizer.FindItemAtPosition(pos)
if old_item is not None and old_item.IsWindow():
old_item.GetWindow().Hide()
sizer.Detach(old_item.GetWindow())
sizer.Add(item, pos=pos, span=span, flag=flag, border=border)
def on_available_shape_double_click(self, event):
shape = event.GetEventObject().GetName()
self.available_shapes.remove(shape)
self.selected_shapes.append(shape)
self.display_images()
def on_selected_shape_double_click(self, event):
shape = event.GetEventObject().GetName()
self.selected_shapes.remove(shape)
self.available_shapes.append(shape)
self.display_images()
def on_cmd_reset_click(self, event):
self.initialise()
def on_cmd_cancel_click(self, event):
self.Destroy()
class ShapeSizer(wx.Panel):
def __init__(self, parent, shapes, shape_type):
wx.Panel.__init__(self, parent, id = wx.ID_ANY)
if shape_type == parent.shape_types['available']:
size = 40
action = parent.on_available_shape_double_click
else:
size = 80
action = parent.on_selected_shape_double_click
panel_sizer = wx.BoxSizer(wx.HORIZONTAL)
shapes.sort()
for shape in shapes:
bitmap = wx.Bitmap(shape + '.png', wx.BITMAP_TYPE_PNG)
bitmap = self.scale_bitmap(bitmap, size, size)
img = wx.StaticBitmap(self, wx.ID_ANY, bitmap, name=shape)
img.Bind(wx.EVT_LEFT_DCLICK, action)
panel_sizer.Add(img, flag=wx.RIGHT, border=10)
self.SetSizer(panel_sizer)
#staticmethod
def scale_bitmap(bitmap, width, height):
image = wx.ImageFromBitmap(bitmap)
image = image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
result = wx.BitmapFromImage(image)
return result
if __name__ == '__main__':
app = wx.App()
ImageSizer(None, title='Image Sizer')
app.MainLoop()
Here are the images:
Every time you double click on a shape your program is creating new instances of the panels and their wx.StaticBitmap widgets, it is these new instances you are seeing as they are initially created with a small default size and then they are repositioned by the next layout. Instead you should reorganize things so you only create the set of panels once, and as the state of the shape selections changes you can have the existing panels update themselves. That will greatly reduce the flicker visible to the user.

Resources