wxPython:居中BoxSizer中控件显隐时如何避免其他控件移位
问题描述
我设置了一个用于复制TextCtrl中文本的按钮,希望点击复制后在按钮旁展示提示信息,确认文本已复制到剪贴板,因此使用了点击按钮时可切换显隐状态的StaticText控件实现该提示。
我将复制按钮和StaticText提示控件放置在水平居中的BoxSizer中,但出现了问题:当StaticText显示时,由于BoxSizer内的子控件为居中对齐,原有按钮的位置会发生偏移。
原有实现代码如下:
def __init__(self, *args, **kw) -> None: super(Window, self).__init__(*args, **kw) # 框架内创建面板 pnl = wx.Panel(self) # 面板内创建控件 self.text_area1 = wx.TextCtrl(pnl, style=wx.TE_MULTILINE | wx.TE_NOHIDESEL) self.text_area2 = wx.TextCtrl(pnl, style = wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_NOHIDESEL | wx.TE_RICH2) btn_correct = wx.Button(pnl, label="Correct >>>") btn_correct.Bind(wx.EVT_BUTTON, self.on_press_correct) self.btn_copy = wx.Button(pnl, label="Copy") self.btn_copy.Bind(wx.EVT_BUTTON, self.on_press_copy) self.label = wx.StaticText(pnl, label="Text copied") self.label.Hide() self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.on_timer, self.timer) # 创建sizer管理子控件布局 sizer = wx.BoxSizer(wx.VERTICAL) sub_sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.text_area1, 1, wx.ALL | wx.EXPAND, 5) sizer.Add(btn_correct, 0, wx.ALL | wx.CENTER, 5) sizer.Add(self.text_area2, 1, wx.ALL | wx.EXPAND, 5) sub_sizer.Add(self.btn_copy, 0, wx.ALL | wx.CENTER, 5) sub_sizer.Add(self.label, 0, wx.ALL | wx.CENTER, 5) sizer.Add(self.sub_sizer, 0, wx.BOTTOM | wx.CENTER, 5) pnl.SetSizer(sizer) def on_press_copy(self, event): data_obj = wx.TextDataObject() data_obj.SetText(self.text_area2.GetValue()) if wx.TheClipboard.Open(): wx.TheClipboard.SetData(data_obj) wx.TheClipboard.Close() self.btn_copy.Disable() self.label.Show() self.label.GetParent().GetSizer().Layout() self.timer.StartOnce(3000) else: wx.MessageBox("Can't open clipboard", "Error") def on_timer(self, event): self.btn_copy.Enable() self.label.Show(show=False) self.label.GetParent().GetSizer().Layout()
需求:实现StaticText显隐切换时,复制按钮位置保持固定不移动的效果。
解决方案
偏移的根本原因是StaticText隐藏时不会占据sizer空间,显示时会增加水平子sizer的总宽度,居中对齐规则下整个sizer的位置会重新计算,连带按钮一起移位。只要提前给提示文本预留固定的显示空间,让sizer总宽度不随提示显隐变化即可解决,有两种可直接复用的实现方式:
- 给提示控件设置固定最小尺寸:提前计算提示文本的实际渲染宽度作为预留值,即使label隐藏,sizer也会保留这部分空间,不会压缩总宽度。
- 不切换控件显隐状态,只切换文本内容:label始终占位显示在sizer中,初始状态文本为空,需要展示提示时写入
Text copied,提示到期后清空文本,控件本身一直存在,不会触发布局宽度变化。
预留固定尺寸的实现仅需修改添加label到sizer的代码:
# 计算提示文本的实际渲染宽度作为预留宽度 tip_width = self.label.GetTextExtent("Text copied").width # 替换原有sub_sizer.Add(self.label, 0, wx.ALL | wx.CENTER, 5) sub_sizer.Add(self.label, 0, wx.ALL | wx.CENTER, 5).SetMinSize((tip_width, -1))
切换文本的实现改动也很小:
- 初始化label时不调用
Hide(),设置初始文本为空:self.label = wx.StaticText(pnl, label="") - 复制成功触发提示时,直接设置文本:
self.label.SetLabelText("Text copied"),无需调用Show() - 计时器触发隐藏提示时,清空文本即可:
self.label.SetLabelText(""),无需调用Show(show=False)
两种方式都不需要修改其他布局逻辑,就能保证复制按钮位置完全固定,不会随提示显隐发生偏移。
内容的提问来源于stack exchange,提问作者Nicryc
相关产品推荐
相关产品推荐

