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

时间:2014-03-31 15:29:06

标签: wxpython wxwidgets

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

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

但是,如果我从TestWindow中的Add()调用中移除了wx.EXPAND标志。 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()

1 个答案:

答案 0 :(得分:2)

您的示例正如我所期望的那样使用wxPython 3.0,但您的症状很常见。通常,这意味着初始大小事件发生在sizer设置之前,因此默认的EVT_SIZE处理程序还没有用于布局的sizer。一旦窗口调整大小,然后使用sizer进行布局,所有内容都按照您的预期进行。

要解决此问题,您只需在创建框架及其内容后触发内置布局功能。 (通常在__init__的末尾)由于自动布局与大小事件相关联,因此您可以使用框架的SendSizeEvent方法让它拥有一个。或者你可以自己明确地调用Layout方法。