为什么我的 UI 元素在用户调整窗口大小之前不可见?

问题描述 投票:0回答:1

我有一个类似于自定义进度条的视图,它基本上由两个并排的大小调整器组成:左侧的一个具有红色背景,右侧的一个具有浅灰色背景。通过调整大小调整器的相对大小,我可以指示不同的百分比。无论出于何种原因(也许是因为此窗口仅由大小调整器组成,没有“实质性”元素),当其包含的框架首次出现时,进度条不可见。当框架在任何方向上调整任意量时,进度条会突然弹出,显示正确的大小和位置以及所有内容。

下面是一个最小的代码示例。它并没有完全说明我所描述的问题:按原样运行时,进度条将正确显示。

但是,如果我从

wx.EXPAND
调用
Add()
中删除
TestWindow.__init__
标志,那么当窗口首次出现时,进度条将是错误的大小或完全不可见。 (调整窗口大小会使栏变得可见和/或采用正确的大小。)但是,当
run_on_main_thread
装饰器未应用于
update_sizes
时,栏始终会立即正确显示,无论是否
wx.EXPAND
包括旗帜!

我不确定为什么我对线程安全的努力导致了这个绘图问题。任何人都可以建议一种方法,使进度条从一开始就正确显示,同时仍然确保

update_sizes
只会在主线程上运行?例如,我可以在窗口上触发
EVT_SIZE
,以便它无需任何用户交互即可自行刷新吗?

我在 OS X 10.9.2 上使用 Python 2.7.5 和 wxPython 2.9.5.0。

代码

from functools import wraps
from math import floor
import wx

def run_on_main_thread(fn):
    """Decorator. Forces the function to run on the main thread.

    Any calls to a function that is wrapped in this decorator will return
    immediately; the return value of such a function is not available.

    """
    @wraps(fn)
    def deferred_caller(*args, **kwargs):
        wx.CallAfter(fn, *args, **kwargs)
    
    return deferred_caller


class PercentageBar(wx.Window):
    def __init__(self, parent, percentage=0.0):
        wx.Window.__init__(self, parent)

        border_color = '#000000'
        active_color = '#cc0000'
        inactive_color = '#d3d7cf'
        height = 24

        self.percentage = percentage

        self.SetBackgroundColour(border_color)

        self.active_rectangle = wx.Panel(self, size=(-1, height))
        self.active_rectangle.SetBackgroundColour(active_color)

        self.inactive_rectangle = wx.Panel(self, size=(-1, height))
        self.inactive_rectangle.SetBackgroundColour(inactive_color)

        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.SetSizer(self.sizer)
        self.update_sizes()
    
    @run_on_main_thread
    def update_sizes(self):
        self.sizer.Clear(False)

        if self.percentage != 0.0 and self.percentage != 1.0:
            active_flags = wx.EXPAND | (wx.ALL & ~wx.RIGHT)
        else:
            active_flags = wx.EXPAND | wx.ALL

        self.sizer.Add(self.active_rectangle, floor(1000 * self.percentage),
                active_flags, 1)

        self.sizer.Add(self.inactive_rectangle, floor(1000 * (1.0 - self.percentage)),
                wx.EXPAND | wx.ALL, 1)

        if self.percentage == 0.0:
            self.active_rectangle.Hide()
            self.inactive_rectangle.Show()
        elif self.percentage == 1.0:
            self.active_rectangle.Show()
            self.inactive_rectangle.Hide()
        else:
            self.active_rectangle.Show()
            self.inactive_rectangle.Show()

        self.sizer.Layout()
        self.Refresh()


class TestWindow(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, title='Test')

        vertical_sizer = wx.BoxSizer(wx.VERTICAL)
        vertical_sizer.Add((1, 1), 1)
        vertical_sizer.Add(PercentageBar(self, percentage=0.33), 2, wx.EXPAND)
        vertical_sizer.Add((1, 1), 1)

        horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
        horizontal_sizer.Add((1, 1), 1)
        horizontal_sizer.Add(vertical_sizer, 10, wx.EXPAND)
        horizontal_sizer.Add((1, 1), 1)

        self.SetSizer(horizontal_sizer)


if __name__ == '__main__':
    app = wx.App()
    window = TestWindow(None)
    window.Show()
    app.MainLoop()
wxpython wxwidgets
1个回答
2
投票

您的示例在 wxPython 3.0 上按预期工作,但您的症状是常见的。通常这意味着初始大小事件发生在设置 sizer 之前,因此默认的

EVT_SIZE
处理程序还没有可用于布局的 sizer。一旦调整窗口大小,就会使用 sizer 进行布局,一切都如您之前所期望的那样。

要解决这个问题,您只需在创建框架及其内容后触发内置布局功能即可。 (通常在

__init__
的末尾)由于自动布局与尺寸事件相关,因此您可以使用框架的
SendSizeEvent
方法让它拥有一个。或者您可以自己显式调用
Layout
方法。

© www.soinside.com 2019 - 2024. All rights reserved.