На моей прошлой работе, мне как-то сказали, что мы должны будем добавить ещё одну возможность в нашем приложение. Заказчик хотел, чтобы появился «тёмный режим», так как во время использования приложения ночью, яркий свет буквально выжигал им глаза.
Нашу программу использовали на ноутбуках в силовых структурах, поэтому я мог понять эмоции, которые они испытывали, когда у них в машине всю ночь ярко горело приложение. Я потратил немного времени для того, чтобы вникнуть в суть проблемы и нашёл решение, которое подойдёт большинству виджетов.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Во тьму
Получить виджет для смены цвета в wxPython довольно просто. Вам понадобятся всего два метода: SetBackgroundColour и SetForegroundColour. Единственная крупная проблема, с которой я столкнулся, это получение виджета ListCtrl / ObjectListView, необходимого для нормальной смены цветов в wxPython. Вам нужно будет выбрать каждый ListItem и сменить его цвет. Я чередовал цвет каждой строки, что бы всё выглядело поинтереснее.
Ещё одной проблемой, с которой я столкнулся, было восстановление фонового цвета ListCtrl. Обычно вы можете задать цветом фона виджета wx.NullColour, благодаря чему он вернётся к своему изначальному цвету. Как бы то ни было, с некоторыми виджетами сделать этого не получится, и вам придётся задавать цвет вручную. Также учтите, что некоторые виджеты вообще не реагируют на SetBackgroundColour. Одним из виджетов, которые не работают как нужно является wx.ToggleButton.
Теперь, когда я объяснил вам суть проблемы, давайте посмотрим на решение. Сохраните код ниже как файл, под названием «dark_mode.py».
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# dark_mode.py 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): """ Переключает строки в виджетах ListCtrl или ObjectListView """ 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): """ Переключается на «тёмный режим» """ 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.NullColour) widget.SetForegroundColour("Black") self.Refresh() return dark_mode |
Код выглядит немного запутанным, но он делает своё дело. Давайте немного передохнём и разберёмся с тем, как он работает. Для начала, мы пытаемся импортировать ObjectListView, изящный сторонний виджет, который сворачивает wx.ListCtrl и делает его использование НАМНОГО удобнее. Как бы то ни было, пока что он не является частью wxPython, так что вам нужно проверить установлен ли он у вас. Если его вдруг нет, то сработает опция False.
Функция GetWidgets берёт пэрент параметр, которым обычно является wx.Frame и wx.Panel, и собирает все его компоненты воедино, чтобы создать список виджетов, а затем снова превращается в вызывающую функцию. Главная функция это darkMode в wxPython. Она также состоит из двух параметров: self, который связан с пэрент виджетом, и стандартный цвет панели. Она вызывает GetWidgets, после чего использует условное утверждение для того, чтобы можно было выбрать применять «тёмный режим» или же нет. А затем она проходит по всем виджетам и изменяет цвета согласно принятому решению. Когда переключение состоялось, она обновит все пройденные этапы в пэренте и сообщит вам о том, включён «тёмный режим» или нет.
Ещё есть одна функция, которая называется darkRowFormatter, которая подходит только для установки цветов ListItems в wx.ListCtrl и виджете ObjectListView. Здесь мы используем сжатие списка чтобы создать список wx.ListItems, который мы потом задействуем для смены цветов. Для принятия смены цветов, нам нужно вызвать SetItem и пропустить его через инстанцию объекта wx.ListItem.
Тестируем «тёмный режим»
Сейчас вы, наверное, пытаетесь понять, как использовать скрипт, написанный выше. Это раздел ответит на все ваши вопросы. Вот простая программа с контролем списков и кнопкой переключения!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
import wx import dark_mode 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): """""" dark_mode.darkMode(self, self.defaultColor) class MyFrame(wx.Frame): """""" def __init__(self): """Constructor""" wx.Frame.__init__(self, None, title="MvP ListCtrl Dark Mode Demo", size=(400, 400)) panel = MyPanel(self) self.Show() if __name__ == "__main__": app = wx.App(False) frame = MyFrame() app.MainLoop() |
Если вы запустите программу, код которой написан выше, вы должны увидеть что-то вроде:
Если вы кликните по кнопке ToggleButton, вы должны будете увидеть что-то вроде:
Заметьте, что на кнопку переключения не повлиял метод SetBackgroundColour. Также обратите внимание на то, что заголовки колонок в контроле списка также не изменили цвет. К сожалению, wxPython не даёт контроля над заголовками колонок, так что способа управлять их цветом просто не существует.
Как бы то ни было, давайте уделим минутку, чтобы увидеть, как работает «тёмный режим». Сначала, нам нужно его импортировать. В данном случае это модуль, который называется dark_mode. Для того, чтобы его вызвать, нам нужно взглянуть на хендлер ToggleButton:
1 |
darkMode.darkMode(self, self.defaultColor) |
Как вы видите, мы просто вызвали darkMode.darkMode с помощью объекта панели и defaultColor, который мы задали в начале init-метода wx.Panel. Это всё, что нам нужно было сделать. Нам также, скорее всего, стоило задать переменную, чтобы получить обратное значение, но в данном примере это не имеет значения.
Обратите внимание: в wxPython Phoenix методы InsertStringItem и SetStringItem уже считаются устаревшими. Вместо них нужно использовать методы InsertItem и SetItem соответственно.
Итоги
Теперь, когда мы закончили, вы можете создать «тёмный режим» для вашего приложения. В каком-то смысле, я хотел бы объединить немного больше методов и создать скрипт для смены цвета, который позволит мне переключатся между всеми цветами, какими я только захочу. Вот сделать его примесью было бы действительно круто. Но это уже так, планы на будущее. А пока, наслаждайтесь!
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»