Свой виджет на питоне

Создание собственного виджета в PyQt5

Python 3 логотип

PyQt5 имеет богатый набор готовых виджетов. Тем не менее, нет в мире инструментария, способного предоставить все виджеты, в которых программисты могут нуждаться для своих приложений. Библиотеки обычно предоставляют только самые распространенные виджеты, такие как кнопки, текстовые виджеты или ползунки. Если есть необходимость в более специализированных виджетах, мы должны создать их сами.

Собственные виджеты создаются путём использования инструментов рисования, предоставляемых PyQt5. Существует две основных возможности: программист может изменить или дополнить существующий виджет, или он может создать собственный виджет с нуля.

Виджет записи дисков

Это виджет, который мы можем увидеть в Nero, K3B и в других программах записи CD/DVD.

 В нашем примере, у нас есть QSlider и собственный виджет. Ползунок контролирует наш виджет. Этот виджет показывает графически общий объём носителя и свободное место, доступное для нас. Минимальное значение нашего виджета – 1, максимальное – 750. Если мы достигаем значения 700, мы начинаем рисование красным цветом. Это обычно означает переполнение. Виджет записи размещается внизу окна путём использования одного QHBoxLayout и одного QVBoxLayout.
Виджет записи основывается на виджете QWidget.
Мы меняем минимальный размер (высоту) виджета. Значение по умолчанию немного маловато для нас.
Мы используем более мелкий шрифт, чем по умолчанию. Это лучше подходит для наших нужд.
Мы рисуем виджет динамически. Чем больше окно, тем больше виджет записи, и наоборот. Вот почему мы должны вычислять размер виджета.

Параметр till — общий размер. Это значение известно нам из виджета ползунка. Это доля от полного пространства.

Параметр full определяет точку, где мы начинаем рисовать красным цветом.

Фактически рисование содержит три шага. Мы рисуем жёлтый или красный и жёлтый прямоугольник. Затем мы рисуем вертикальные линии, которые делят виджет на несколько частей. Наконец, мы рисуем числа, которые показывают ёмкость носителя.

Мы используем метрики шрифтов, чтобы рисовать текст. Мы должны знать ширину текста, для того чтобы центрировать его рядом с вертикальной линией.
Когда мы двигаем ползунок, вызывается метод changeValue(). Внутри метода, мы отправляем пользовательский сигнал updateBW с параметром. Параметр – это текущее значение ползунка. Значение позднее используется, чтобы вычислить ёмкость рисуемого виджета записи. Затем, наш виджет перерисовывается.

В этой части руководства PyQt5, мы создали свой виджет.

Для вставки кода на Python в комментарий заключайте его в теги

Источник

Создание собственных виджетов и Notebook / tkinter 23

Класс ttk.Notebook — еще один новый виджет из модуля ttk . Он позволяет добавлять разные виды отображения приложения в одном окне, предлагая после этого выбрать желаемый с помощью клика по соответствующей вкладке.

Панели с вкладками — это удобный вариант повторного использования графического интерфейса для тех ситуаций, когда содержимое нескольких областей не должно отображаться одновременно.

Следующее приложение показывает список дел, разбитый по категориям. В этом примере данные доступны только для чтения (для упрощения):

Отображение панелей с вкладками с помощью Notebook

Создаем ttk.Notebook с фиксированными размерами, и затем проходимся по словарю с заранее определенными данными. Он выступит источником вкладок и названий для каждой области:

 
import tkinter as tk
import tkinter.ttk as ttk

class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("Ttk Notebook")

todos = "Дом": ["Постирать", "Сходить за продуктами"],
"Работа": ["Установить Python", "Учить Tkinter", "Разобрать почту"],
"Отпуск": ["Отдых!"]
>

self.notebook = ttk.Notebook(self, width=250, height=100, padding=10)
for key, value in todos.items():
frame = ttk.Frame(self.notebook)
self.notebook.add(frame, text=key, underline=0,
sticky=tk.NE + tk.SW)
for text in value:
ttk.Label(frame, text=text).pack(anchor=tk.W)
self.label = ttk.Label(self)

self.notebook.pack()
self.label.pack(anchor=tk.W)
self.notebook.enable_traversal()
self.notebook.bind(">", self.select_tab)

def select_tab(self, event):
tab_id = self.notebook.select()
tab_name = self.notebook.tab(tab_id, "text")
text = "Ваш текущий выбор: <>".format(tab_name)
self.label.config(text=text)

if __name__ == "__main__":
app = App()
app.mainloop()

При клике по вкладке метка в нижней части экрана обновляет содержимое, показывая название текущей вкладки.

Как работает этот виджет

Виджет ttk.Notebook создается с фиксированными шириной, высотой и внешними отступами.

Каждый ключ из словаря todos используется в качестве названия вкладки, а список значений добавляется в виде меток в ttk.Frame , который представляет собой область окна:

 
self.notebook = ttk.Notebook(self, width=250, height=100, padding=10)
for key, value in todos.items():
frame = ttk.Frame(self.notebook)
self.notebook.add(frame, text=key,
underline=0, sticky=tk.NE+tk.SW)
for text in value:
ttk.Label(frame, text=text).pack(anchor=tk.W)

После этого у виджета ttk.Notebook вызывается метод enable_traversal() . Это позволяет пользователям переключаться между вкладками с помощью Ctrl + Shift + Tab и Ctrl + Tab соответственно.

Благодаря этому также можно переключиться на определенную вкладку, зажав Alt и подчеркнутый символ: Alt + H для вкладки Home, Alt + W — для Work, а Alt + V для Vacation.

Виртуальное событие " >" генерируется автоматически при изменении выбора. Оно связывается с методом select_tab() . Стоит отметить, что это событие автоматически срабатывает при добавлении вкладки в ttk.Notebook :

 
self.notebook.pack()
self.label.pack(anchor=tk.W)
self.notebook.enable_traversal()
self.notebook.bind(">", self.select_tab)

При упаковке элементов необязательно размещать дочерние элементы ttk.Notebook , поскольку это делается автоматически с помощью вызова geometry manager:

 
def select_tab(self, event):
tab_id = self.notebook.select()
tab_name = self.notebook.tab(tab_id, "text")
self.label.config(text=f"Ваш текущий выбор: ")

Если нужно получить текущий дочерний элемент ttk.Notebook , то для этого не нужно использовать дополнительные структуры данных для маппинга индекса вкладки и окна виджета.

Метод nametowidget() доступен для всех классов виджетов, так что с его помощью можно легко получить объект виджета, соответствующий определенному имени:

 
def select_tab(self, event):
tab_id = self.notebook.select()
frame = self.nametowidget(tab_id)
# .

Применение стилей Ttk

У тематических виджетов есть отдельный API для изменения внешнего вида. Прямо задавать параметры нельзя, потому что они определены в классе ttk.Style .

В этом разделе разберем, как изменять виджеты и добавлять им стили.

Для добавления дополнительных настроек нужен объект ttk.Style , который предоставляет следующие методы:

  • configure(style, opts) — меняет внешний вид opts для style виджета. Именно здесь задаются такие параметры, как фон, отступы и анимации.
  • map(style, query) — меняет динамический вид style виджета. Аргумент query — аргумент-ключевое слово, где каждый ключ отвечает за параметр стиля, а значение — список кортежей в виде (state, value) . Это значит, что значение каждого параметра определяется его текущим состоянием.

Например, отметим следующие примеры для двух ситуаций:

 
import tkinter as tk
import tkinter.ttk as tk

class App(tk.Tk):
def __init__(self):
super().__init__()

self.title("Tk themed widgets")
style = ttk.Style(self)
style.configure("TLabel", padding=10)
style.map("TButton",
foreground=[("pressed", "grey"), ("active", "white")],
background=[("pressed", "white"), ("active", "grey")]
)
# .

Теперь каждый ttk.Label отображается с внутренним отступом 10, а у ttk.Button динамические стили: серая заливка с белым фоном, когда состояние кнопки — pressed и белая заливка с серым фоном — когда active.

Как работают стили

Создавать ttk.Style довольно просто. Нужно лишь создать экземпляр с родительским виджетом в качестве первого параметра.

После этого можно задать настройки стиля для виджетов с помощью символа T в верхнем регистре и названия виджета: Tbutton для ttk.Button , Tlabel для ttk.Label и так далее. Однако есть и исключения, поэтому рекомендуется сверяться с помощью интерпретатора Python, вызывая winfo_class() для экземпляра виджета.

Также можно добавить префикс, чтобы указать, что этот стиль должен быть не по умолчанию, а явно задаваться для определенных виджетов:

 
style.configure("My.TLabel", padding=10)
# .
label = ttk.Label(master, text="Какой-то текст", style="My.TLabel")

Создание виджета выбора даты

Если нужно позволить пользователям выбирать дату в приложении, то можно попробовать оставить текстовую подсказку, которая бы побудила их написать строку в формате даты. Еще одно решение — добавить несколько числовых полей для ввода дня, месяца и года, но в этом случае понадобятся несколько правил валидации.

В отличие от других фреймворков для создания графических интерфейсов, в Tkinter нет класса для этих целей, но можно воспользоваться знаниями тематических виджетов для создания виджета календаря.

В этом материале пошагово разберем процесс создания виджета выбора даты с помощью виджетов Ttk:

Создание виджета выбора даты

Помимо модулей tkinter также нужны модули calendar и datetime из стандартной библиотеки. Это поможет моделировать данные виджета и взаимодействовать с ними.

Заголовок виджета показывает стрелки для перемещения между месяцами. Их внешний вид зависит от текущих выбранных стилей Tk. Тело виджета состоит из таблицы ttk.Treeview с экземпляром Canvas , который подсвечивает ячейку выбранной даты:

 
import calendar
import datetime
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkfont
from itertools import zip_longest

class TtkCalendar(ttk.Frame):
def __init__(self, master=None, **kw):
now = datetime.datetime.now()
fwday = kw.pop('firstweekday', calendar.MONDAY)
year = kw.pop('year', now.year)
month = kw.pop('month', now.month)
sel_bg = kw.pop('selectbackground', '#ecffc4')
sel_fg = kw.pop('selectforeground', '#05640e')

super().__init__(master, **kw)

self.selected = None
self.date = datetime.date(year, month, 1)
self.cal = calendar.TextCalendar(fwday)
self.font = tkfont.Font(self)
self.header = self.create_header()
self.table = self.create_table()
self.canvas = self.create_canvas(sel_bg, sel_fg)
self.build_calendar()

# .

def main():
root = tk.Tk()
root.title('Календарь Tkinter')
ttkcal = TtkCalendar(firstweekday=calendar.SUNDAY)
ttkcal.pack(expand=True, fill=tk.BOTH)
root.mainloop()

if __name__ == '__main__':
main()

Полный код в файле lesson_23/creating_widget.py на Gitlab.

Как создается виджет

Этот класс TtkCalendar можно кастомизировать, передавая параметры в виде аргументов-ключевых слов. Их можно получать при инициализации, указав кое-какие значения по умолчанию, например, текущие месяц и год:

Источник

Читайте также:  Динамическое создание массивов java
Оцените статью