geekdoc-python-zh/docs/pythonlibrary/wxpython-creating-a-dark-mo...

10 KiB
Raw Permalink Blame History

wxPython:创建“黑暗模式”

原文:https://www.blog.pythonlibrary.org/2011/11/05/wxpython-creating-a-dark-mode/

上个月的一天,有人告诉我,我的一个程序有一个功能需求。当他们在晚上使用我的应用程序时,他们想要一个“黑暗模式”,因为正常的颜色有点刺眼。我的程序在警车的笔记本电脑上使用,所以我能理解他们的沮丧。我花了一些时间研究这个问题,并得到了一个基本可行的脚本,我将与我的读者分享。当然,如果您是一个长期读者,您可能知道我在谈论一个 wxPython 程序。我几乎用 wxPython 编写了所有的 GUI。不管怎样我们继续讲故事吧

陷入黑暗

让小部件在 wxPython 中改变颜色非常容易。你只需要两个方法 SetBackgroundColourSetForegroundColour 。我在这样做时遇到的唯一主要问题是让我的 ListCtrl / ObjectListView 小部件适当地改变颜色。您需要循环遍历每个 ListItem 并分别更改它们的颜色。我改变行的颜色,使事情变得更有趣。另一个问题是恢复 ListCtrl 的背景色。通常你可以设置一个小部件的背景颜色为 wx。NullColour(或 wx。NullColor ),它将恢复到默认颜色。然而,有些小部件不是这样工作的,你必须实际指定一种颜色。还需要注意的是,有些小部件似乎根本不关注 SetBackgroundColour。我发现的一个这样的小部件是 wx.ToggleButton。

现在你知道我所知道的,所以让我们看看我想出的解决我的问题的代码:


import wx
try:
    from ObjectListView import ObjectListView
except:
    ObjectListView = False

#----------------------------------------------------------------------
def getWidgets(parent):
    """
    Return a list of all the child widgets
    """
    items = [parent]
    for item in parent.GetChildren():
        items.append(item)
        if hasattr(item, "GetChildren"):
            for child in item.GetChildren():
                items.append(child)
    return items

#----------------------------------------------------------------------
def darkRowFormatter(listctrl, dark=False):
    """
    Toggles the rows in a ListCtrl or ObjectListView widget. 
    Based loosely on the following documentation:
    http://objectlistview.sourceforge.net/python/recipes.html#recipe-formatter
    and http://objectlistview.sourceforge.net/python/cellEditing.html
    """

    listItems = [listctrl.GetItem(i) for i in range(listctrl.GetItemCount())]
    for index, item in enumerate(listItems):
        if dark:
            if index % 2:
                item.SetBackgroundColour("Dark Grey")
            else:
                item.SetBackgroundColour("Light Grey")
        else:
            if index % 2:
                item.SetBackgroundColour("Light Blue")
            else:
                item.SetBackgroundColour("Yellow")
        listctrl.SetItem(item)

#----------------------------------------------------------------------
def darkMode(self, normalPanelColor):
    """
    Toggles dark mode
    """
    widgets = getWidgets(self)
    panel = widgets[0]
    if normalPanelColor == panel.GetBackgroundColour():
        dark_mode = True
    else:
        dark_mode = False
    for widget in widgets:
        if dark_mode:
            if isinstance(widget, ObjectListView) or isinstance(widget, wx.ListCtrl):
                darkRowFormatter(widget, dark=True)
            widget.SetBackgroundColour("Dark Grey")
            widget.SetForegroundColour("White")
        else:
            if isinstance(widget, ObjectListView) or isinstance(widget, wx.ListCtrl):
                darkRowFormatter(widget)
                widget.SetBackgroundColour("White")
                widget.SetForegroundColour("Black")
                continue
            widget.SetBackgroundColour(wx.NullColor)
            widget.SetForegroundColour("Black")
    self.Refresh()
    return dark_mode

这段代码有点复杂,但是它完成了任务。让我们把它分解一下,看看它是如何工作的。首先,我们尝试导入 ObjectListView这是一个很酷的包装 wx 的第三方小部件。ListCtrl 并使它更容易使用。然而,它现在不是 wxPython 的一部分,所以您需要测试它是否存在。如果它不存在,我就把它设置为 False。

GetWidgets 函数接受一个父参数,通常是一个 wx。框架或 wx。面板并遍历它的所有子面板来创建一个小部件列表然后返回给调用函数。主要功能是黑暗模式。它还带有两个参数一个是名不副实的“self”指的是父窗口小部件另一个是默认的面板颜色。它调用 GetWidgets然后使用一个条件语句来决定是否应该启用黑暗模式。接下来它遍历小部件并相应地改变颜色。完成后它将刷新传入的父节点并返回一个 bool 让您知道黑暗模式是开还是关。

还有一个名为 darkRowFormatter 的函数,仅用于设置 wx 中列表项的颜色。ListCtrl 或 ObjectListView 小工具。这里我们使用列表理解来创建一个 wx 列表。然后我们迭代列表项,改变它们的颜色。为了实际应用颜色变化,我们需要调用 SetItem 并向它传递一个 wx。ListItem 对象实例。

尝试黑暗模式

所以现在你可能想知道如何实际使用上面的脚本。这一节将向您展示如何做到这一点。这是一个简单的程序,里面有一个列表控件和一个切换按钮!


import wx
import darkMode

########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        self.defaultColor = self.GetBackgroundColour()

        rows = [("Ford", "Taurus", "1996", "Blue"),
                ("Nissan", "370Z", "2010", "Green"),
                ("Porche", "911", "2009", "Red")
                ]
        self.list_ctrl = wx.ListCtrl(self, style=wx.LC_REPORT)

        self.list_ctrl.InsertColumn(0, "Make")
        self.list_ctrl.InsertColumn(1, "Model")
        self.list_ctrl.InsertColumn(2, "Year")
        self.list_ctrl.InsertColumn(3, "Color")

        index = 0
        for row in rows:
            self.list_ctrl.InsertStringItem(index, row[0])
            self.list_ctrl.SetStringItem(index, 1, row[1])
            self.list_ctrl.SetStringItem(index, 2, row[2])
            self.list_ctrl.SetStringItem(index, 3, row[3])
            if index % 2:
                self.list_ctrl.SetItemBackgroundColour(index, "white")
            else:
                self.list_ctrl.SetItemBackgroundColour(index, "yellow")
            index += 1

        btn = wx.ToggleButton(self, label="Toggle Dark")
        btn.Bind(wx.EVT_TOGGLEBUTTON, self.onToggleDark)
        normalBtn = wx.Button(self, label="Test")

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
        sizer.Add(btn, 0, wx.ALL, 5)
        sizer.Add(normalBtn, 0, wx.ALL, 5)
        self.SetSizer(sizer)

    #----------------------------------------------------------------------
    def onToggleDark(self, event):
        """"""
        darkMode.darkMode(self, self.defaultColor)

########################################################################
class MyFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, wx.ID_ANY,
                          "MvP ListCtrl Dark Mode Demo")
        panel = MyPanel(self)
        self.Show()

#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

如果您运行上面的程序,您应该会看到类似这样的内容:

如果您单击 toggle 按钮,您应该会看到类似这样的内容:

注意切换按钮是如何不受 SetBackgroundColour 方法影响的。还要注意列表控件的列标题也没有改变颜色。不幸的是wxPython 没有公开对列标题的访问,所以没有办法控制它们的颜色。

无论如何,让我们花一点时间来看看黑暗模式代码是如何使用的。首先我们需要导入它。在这种情况下,该模块被称为黑暗模式。为了实际调用它,我们需要查看 ToggleButton 的事件处理程序:


darkMode.darkMode(self, self.defaultColor)

正如你所看到的,我们所做的只是用面板对象(“self ”)和我们在 wx 开头设置的 defaultColor 调用 darkMode.darkMode。面板的 init 方法。这也是我们必须做的。我们可能应该用一个变量来设置它,以捕捉返回值,但是对于这个例子,我们并不关心。

包扎

现在我们完成了,你也可以为你的应用程序创建一个“黑暗模式”。在某种程度上,我想把它更一般化一些,做成一个颜色改变器脚本,我可以把我想要的任何颜色传递给它。真正酷的是把它做成 mixin。但那是未来的事。现在享受吧

进一步阅读

源代码