Что такое mainloop в python
Перейти к содержимому

Что такое mainloop в python

  • автор:

Как в python запустить паралельно две функции mainloop и socket

мне надо запустить одновременно две функции window.mainloop (модуль tkinter) и функцию с сервером socket. Как это сделать?

Отслеживать
задан 8 ноя 2022 в 17:33
359 2 2 серебряных знака 14 14 бронзовых знаков

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

У Питона с коробки есть модуль threading . Он позволяет выполнять функции паралелно.

from threading import * t1 = Thread(target='функция 1') t2 = Thread(target='функция 2') t1.start() t2.start() 

Повторюсь, они запускаются паралельно, то есть вторая не ждет окончае первой

Что такое Tkinter

Tkinter – это пакет для Python, предназначенный для работы с библиотекой Tk. Библиотека Tk содержит компоненты графического интерфейса пользователя (graphical user interface – GUI). Эта библиотека написана на языке программирования Tcl.

Под графическим интерфейсом пользователя (GUI) подразумеваются все те окна, кнопки, текстовые поля для ввода, скроллеры, списки, радиокнопки, флажки и другие элементы, которые вы видите на экране, открывая то или иное приложение. Через них вы взаимодействуете с программой и управляете ею. Все эти элементы интерфейса будем называть виджетами (widgets – штуковины).

В настоящее время почти все приложения, которые создаются для конечного пользователя, имеют GUI. Редкие программы, подразумевающие взаимодействие с человеком, остаются консольными. В предыдущих двух курсах мы писали только консольные программы.

Существует множество библиотек GUI, среди которых Tk не самый популярный инструмент, хотя с его помощью написано немало проектов. Он был выбран для Python по-умолчанию. Установочный файл интерпретатора Питона обычно уже включает пакет tkinter в составе стандартной библиотеки.

Tkinter можно представить как переводчик с языка Python на язык Tcl. Вы пишете программу на Python, а код модуля tkinter переводит ваши инструкции на язык Tcl, который понимает библиотека Tk.

Программы с графическим интерфейсом пользователя событийно-ориентированные. Вы уже должны иметь представление о структурном и желательно объектно-ориентированном программировании. Событийно-ориентированное ориентировано на события. То есть та или иная часть программного кода начинает выполняться лишь тогда, когда случается то или иное событие.

Событийно-ориентированное программирование базируется на объектно-ориентированном. Даже если мы не будем разрабатывать собственные классы, мы обязательно будем пользоваться теми, что есть в tkinter . Все виджеты – это объекты-экземпляры, создаваемые от встроенных классов.

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

Tkinter импортируется стандартно для модуля Python любым из способов:

  • import tkinter
  • from tkinter import *
  • import tkinter as tk

Можно импортировать отдельные классы, что делается редко. В данном курсе мы будем использовать выражение from tkinter import * .

Если необходимо, узнать установленную версию Tk можно через константу TkVersion:

>>> from tkinter import * >>> TkVersion 8.6

Чтобы написать GUI-программу, надо выполнить приблизительно следующее:

  1. Создать главное окно.
  2. Создать виджеты и выполнить конфигурацию их свойств (опций).
  3. Определить события, то есть то, на что будет реагировать программа.
  4. Описать обработчики событий, то есть то, как будет реагировать программа.
  5. Расположить виджеты в главном окне.
  6. Запустить цикл обработки событий.

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

В современных операционных системах любое пользовательское приложение заключено в окно, которое можно назвать главным, так как в нем располагаются все остальные виджеты. Объект окна верхнего уровня создается от класса Tk модуля tkinter . Переменную, связываемую с объектом, часто называют root (корень):

root = Tk()

Пусть в окне приложения располагаются текстовое поле, метка и кнопка. Данные объекты создаются от классов Entry , Label и Button модуля tkinter . Сразу выполним конфигурацию некоторые их свойства с помощью передачи аргументов конструкторам этих классов:

ent = Entry(root, width=20) but = Button(root, text="Преобразовать") lab = Label(root, width=20, bg='black', fg='white')

Устанавливать свойства объектов не обязательно при их создании. Существуют еще пара способов, с помощью которых это можно сделать после.

Первым аргументом в конструктор виджета передается виджет-хозяин, то есть тот, на котором будет располагаться создаваемый. В случае, когда элементы GUI помещаются непосредственно на главное окно, родителя можно не указывать. То есть в нашем примере мы можем убрать root :

ent = Entry(width=20) but = Button(text="Преобразовать") lab = Label(width=20, bg='black', fg='white')

Однако виджеты не обязательно располагаются на root ‘е. Они могут находиться на других виджетах, и тогда указывать «мастера» необходимо.

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

def str_to_sort_list(event): s = ent.get() s = s.split() s.sort() lab['text'] = ' '.join(s)

У функций, которые вызываются при наступлении события с помощью метода bind , должен быть один параметр. Обычно его называют event , то есть «событие».

В нашей функции с помощью метода get из поля забирается текст, представляющий собой строку. Она преобразуется в список слов с помощью метода split . Потом список сортируется. В конце изменяется свойство text метки. Ему присваивается строка, полученная из списка с помощью строкового метода join .

Теперь необходимо связать вызов функции с событием:

but.bind('', str_to_sort_list)

В данном случае это делается с помощью метода bind . Ему передается событие и функция-обработчик. Событие будет передано в функцию и присвоено параметру event . Здесь событием является щелчок левой кнопкой мыши, что обозначается строкой » .

В любом приложении виджеты не разбросаны по окну как попало, а организованы, интерфейс продуман до мелочей и обычно подчинен определенным стандартам. Пока расположим элементы друг под другом с помощью наиболее простого менеджера геометрии tkinter – метода pack :

ent.pack() but.pack() lab.pack()

Метод mainloop экземпляра Tk запускает главный цикл обработки событий, что в том числе приводит к отображению главного окна со всеми «упакованными» на нем виджетами:

root.mainloop()

Полный код программы:

from tkinter import * def str_to_sort_list(event): s = ent.get() s = s.split() s.sort() lab['text'] = ' '.join(s) root = Tk() ent = Entry(width=20) but = Button(text="Преобразовать") lab = Label(width=20, bg='black', fg='white') but.bind('', str_to_sort_list) ent.pack() but.pack() lab.pack() root.mainloop() 

В результате выполнения данного скрипта появляется окно, в текстовое поле которого можно ввести список слов, нажать кнопку и получить его отсортированный вариант:

Пример программы на Python, написанной с помощью tkinter

Попробуем теперь реализовать в нашей программе объектно-ориентированный подход. Это необязательно, но позволяет связать/объединить вместе (вспомните про инкапсуляцию в ООП) виджет и функции, которые обрабатывают связанные с ним события, что концептуально более верно. В этом случае функции становятся методами.

Пусть группа из метки, кнопки и поля представляет собой один объект, порождаемый от класса Block . Тогда в основной ветке программы будет главное окно, объект типа Block и запуск окна. Поскольку блок должен быть привязан к главному окну, то неплохо бы передать в конструктор класса окно-родитель:

from tkinter import * root = Tk() first_block = Block(root) root.mainloop()

Теперь напишем сам класс Block :

class Block: def __init__(self, master): self.ent = Entry(master, width=20) self.but = Button(master, text="Преобразовать") self.lab = Label(master, width=20, bg='black', fg='white') self.but['command'] = self.str_to_sort self.ent.pack() self.but.pack() self.lab.pack() def str_to_sort(self): s = self.ent.get().split() s.sort() self.lab['text'] = ' '.join(s)

Здесь виджеты являются значениями полей объекта типа Block , функция-обработчик события нажатия на кнопку устанавливается не с помощью метода bind , а с помощью свойства кнопки command . В этом случае в вызываемой функции не требуется параметр event . В метод мы передаем только сам объект.

Больший смысл в определении собственного класса появляется, когда требуется несколько или множество похожих объектов-блоков. Допустим, нам нужно несколько блоков, состоящих из метки, кнопки, поля. Причем у кнопки каждой группы будет своя функция-обработчик клика.

Тогда можно передавать значения для свойства command в конструктор. Значение будет представлять собой привязываемую к кнопке функцию-обработчик события. Полный код программы:

from tkinter import * class Block: def __init__(self, master, func): self.ent = Entry(master, width=20) self.but = Button(master, text="Преобразовать") self.lab = Label(master, width=20, bg='black', fg='white') # self.but['command'] = eval('self.' + func) self.but['command'] = getattr(self, func) self.ent.pack() self.but.pack() self.lab.pack() def str_to_sort(self): s = self.ent.get().split() s.sort() self.lab['text'] = ' '.join(s) def str_reverse(self): s = self.ent.get().split() s.reverse() self.lab['text'] = ' '.join(s) root = Tk() first_block = Block(root, 'str_to_sort') second_block = Block(root, 'str_reverse') root.mainloop() 

Выражение getattr(self, func) , где вместо func подставляется строка ‘str_to_sort’ или ‘str_reverse’, преобразуется в выражение self.str_to_sort или self.str_reverse .

При выполнения этого кода в окне будут выведены два однотипных блока, кнопки которых выполняют разные действия.

Окно с двумя блоками кнопки, метки и текстового поля

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

Практическая работа

Напишите простейший калькулятор, состоящий из двух текстовых полей, куда пользователь вводит числа, и четырех кнопок «+», «-«, «*», «/». Результат вычисления должен отображаться в метке. Если арифметическое действие выполнить невозможно (например, если были введены буквы, а не числа), то в метке должно появляться слово «ошибка».

Курс с примерами решений практических работ: pdf-версия

X Скрыть Наверх

Tkinter. Программирование GUI на Python

Окна в tkinter

В этом уроке рассмотрим основные настройки окон, в которых располагаются виджеты. Обычные окна в Tkinter порождаются не только от класса Tk , но и Toplevel . От Tk принято создавать главное окно. Если создается многооконное приложение, то остальные окна создаются от Toplevel. Методы обоих классов схожи.

Размер и положение окна

По умолчанию окно приложения появляется в верхнем левом углу экрана. Его размер (ширина и высота) определяется совокупностью размеров расположенных в нем виджетов. В случае если окно пустое, то tkinter устанавливает его размер в 200 на 200 пикселей.

С помощью метода geometry можно изменить как размер окна, так и его положение. Метод принимает строку определенного формата.

from tkinter import * root = Tk() root.geometry('600x400+200+100') root.mainloop()

Первые два числа в строке-аргументе geometry задают ширину и высоту окна. Вторая пара чисел обозначает смещение на экране по осям x и y . В примере окно размерностью 600 на 400 будет смещено от верхней левой точки экрана на 200 пикселей вправо и на 100 пикселей вниз.

Если перед обоими смещениями вместо плюса указывается минус, то расчет происходит от нижних правых углов экрана и окна. Так выражение root.geometry(‘600×400-0-0’) заставит окно появиться в нижнем правом углу.

В аргументе метода geometry можно не указывать либо размер, либо смещение. Например, чтобы сместить окно, но не менять его размер, следует написать root.geometry(‘+200+100’) .

Бывает удобно, чтобы окно появлялось в центре экрана. Методы winfo_screenwidth и winfo_screenheight возвращают количество пикселей экрана, на котором появляется окно. Рассмотрим, как поместить окно в центр, если размер окна известен:

from tkinter import * root = Tk() w = root.winfo_screenwidth() h = root.winfo_screenheight() w = w // 2 # середина экрана h = h // 2 w = w - 200 # смещение от середины h = h - 200 root.geometry(f'400x400++') root.mainloop()

Здесь мы вычитаем половину ширины и высоты окна (по 200 пикселей). Иначе в центре экрана окажется верхний левый угол окна, а не его середина.

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

from tkinter import * root = Tk() Button(text="Button", width=20).pack() Label(text="Label", width=20, height=3).pack() Button(text="Button", width=20).pack() root.update_idletasks() s = root.geometry() s = s.split('+') s = s[0].split('x') width_root = int(s[0]) height_root = int(s[1]) w = root.winfo_screenwidth() h = root.winfo_screenheight() w = w // 2 h = h // 2 w = w - width_root // 2 h = h - height_root // 2 root.geometry('+<>+<>'.format(w, h)) root.mainloop()

Метод update_idletasks позволяет перезагрузить данные об окне после размещения на нем виджетов. Иначе geometry вернет строку, где ширина и высота равняются по одному пикселю. Видимо таковы параметры на момент запуска приложения.

По умолчанию пользователь может разворачивать окно на весь экран, а также изменять его размер, раздвигая границы. Эти возможности можно отключить с помощью метода resizable . Так root.resizable(False, False) запретит изменение размеров главного окна как по горизонтали, так и вертикали. Развернуть на весь экран его также будет невозможно, при этом соответствующая кнопка разворота исчезает.

resizable.png

Заголовок окна

По умолчанию в заголовке окна находится надпись «tk». Для установки собственного названия используется метод title .

… root.title("Главное окно")

root_title.png

Если необходимо, заголовок окна можно вообще убрать. В программе ниже второе окно ( Toplevel ) открывается при клике на кнопку, оно не имеет заголовка, так как к нему был применен метод overrideredirect с аргументом True . Через пять секунд данное окно закрывается методом destroy .

from tkinter import * def about(): a = Toplevel() a.geometry('200x150') a['bg'] = 'grey' a.overrideredirect(True) Label(a, text="About this").pack(expand=1) a.after(5000, lambda: a.destroy()) root = Tk() root.title("Главное окно") Button(text="Button", width=20).pack() Label(text="Label", width=20, height=3).pack() Button(text="About", width=20, command=about).pack() root.mainloop()

overrideredirect

Практическая работа

Напишите программу, в которой на главном окне находятся холст и кнопка «Добавить фигуру». Кнопка открывает второе окно, включающее четыре поля для ввода координат и две радиокнопки для выбора, рисовать ли на холсте прямоугольник или овал. Здесь же находится кнопка «Нарисовать», при клике на которую соответствующая фигура добавляется на холст, а второе окно закрывается. Проверку корректности ввода в поля можно опустить.

Программа на tkinter

Курс с примерами решений практических работ: pdf-версия

X Скрыть Наверх

Tkinter. Программирование GUI на Python

Введение в Tkinter

image

Tkinter – это кроссплатформенная библиотека для разработки графического интерфейса на языке Python (начиная с Python 3.0 переименована в tkinter). Tkinter расшифровывается как Tk interface, и является интерфейсом к Tcl/Tk.
Tkinter входит в стандартный дистрибутив Python.

Весь код в этой статье написан для Python 2.x.
Чтобы убедиться, что Tkinter установлен и работает, воспользуемся стандартной функцией Tkinter _test():

import Tkinter Tkinter._test()

После выполнения данного кода должно появиться следующее окно:

Отлично, теперь можно приступать к написанию нескольких простых программ для демонстрации основных принципов Tkinter.

Hello world

Конечно, куда же без него. Первым делом нам нужно создать главное окно, написав

from Tkinter import * root = Tk()

Да-да, всего одна строка, это вам не WinAPI (=. Теперь создадим кнопку, при нажатии на которую будет выводиться текст в консоль:

def Hello(event): print "Yet another hello world" btn = Button(root, #родительское окно text="Click me", #надпись на кнопке width=30,height=5, #ширина и высота bg="white",fg="black") #цвет фона и надписи btn.bind("", Hello) #при нажатии ЛКМ на кнопку вызывается функция Hello btn.pack() #расположить кнопку на главном окне root.mainloop()

Всё просто, не так ли? Создаём экземпляр класса Button, указываем родителя и при желании список параметров. Есть еще немало параметров, таких как шрифт, толщина рамки и т.д.
Затем привязываем к нажатию на кнопку событие (можно привязать несколько разных событий в зависимости, например, от того, какой кнопкой мыши был нажат наш btn.
mainloop() запускает цикл обработки событий; пока мы не вызовем эту функцию, наше окно не будет реагировать на внешние раздражители.

Упаковщики

Функция pack() — это так называемый упаковщик, или менеджер расположения. Он отвечает за то, как виджеты будут располагаться на главном окне. Для каждого виджета нужно вызвать метод упаковщика, иначе он не будет отображён. Всего упаковщиков три:

pack(). Автоматически размещает виджеты в родительском окне. Имеет параметры side, fill, expand. Пример:

from Tkinter import * root = Tk() Button(root, text = '1').pack(side = 'left') Button(root, text = '2').pack(side = 'top') Button(root, text = '3').pack(side = 'right') Button(root, text = '4').pack(side = 'bottom') Button(root, text = '5').pack(fill = 'both') root.mainloop()

grid(). Размещает виджеты на сетке. Основные параметры: row/column – строка/столбец в сетке, rowspan/columnspan – сколько строк/столбцов занимает виджет. Пример:

from Tkinter import * root = Tk() Button(root, text = '1').grid(row = 1, column = 1) Button(root, text = '2').grid(row = 1, column = 2) Button(root, text = '__3__').grid(row = 2, column = 1, columnspan = 2) root.mainloop()

place(). Позволяет размещать виджеты в указанных координатах с указанными размерами.
Основные параметры: x, y, width, height. Пример:

from Tkinter import * root = Tk() Button(root, text = '1').place(x = 10, y = 10, width = 30) Button(root, text = '2').place(x = 45, y = 20, height = 15) Button(root, text = '__3__').place(x = 20, y = 40) root.mainloop()

Теперь для демонстрации других возможностей Tkinter, напишем простейший

Текстовый редактор

Без лишних слов приведу код:

from Tkinter import * import tkFileDialog def Quit(ev): global root root.destroy() def LoadFile(ev): fn = tkFileDialog.Open(root, filetypes = [('*.txt files', '.txt')]).show() if fn == '': return textbox.delete('1.0', 'end') textbox.insert('1.0', open(fn, 'rt').read()) def SaveFile(ev): fn = tkFileDialog.SaveAs(root, filetypes = [('*.txt files', '.txt')]).show() if fn == '': return if not fn.endswith(".txt"): fn+=".txt" open(fn, 'wt').write(textbox.get('1.0', 'end')) root = Tk() panelFrame = Frame(root, height = 60, bg = 'gray') textFrame = Frame(root, height = 340, width = 600) panelFrame.pack(side = 'top', fill = 'x') textFrame.pack(side = 'bottom', fill = 'both', expand = 1) textbox = Text(textFrame, font='Arial 14', wrap='word') scrollbar = Scrollbar(textFrame) scrollbar['command'] = textbox.yview textbox['yscrollcommand'] = scrollbar.set textbox.pack(side = 'left', fill = 'both', expand = 1) scrollbar.pack(side = 'right', fill = 'y') loadBtn = Button(panelFrame, text = 'Load') saveBtn = Button(panelFrame, text = 'Save') quitBtn = Button(panelFrame, text = 'Quit') loadBtn.bind("", LoadFile) saveBtn.bind("", SaveFile) quitBtn.bind("", Quit) loadBtn.place(x = 10, y = 10, width = 40, height = 40) saveBtn.place(x = 60, y = 10, width = 40, height = 40) quitBtn.place(x = 110, y = 10, width = 40, height = 40) root.mainloop()

Здесь есть несколько новых моментов.

Во-первых, мы подключили модуль tkFileDialog для диалогов открытия/закрытия файла. Использовать их просто: нужно создать объект типа Open или SaveAs, при желании задав параметр filetypes, и вызвать его метод show(). Метод вернёт строку с именем файла или пустую строку, если пользователь просто закрыл диалог.

Во-вторых, мы создали два фрейма. Фрейм предназначен для группировки других виджетов. Один содержит управляющие кнопки, а другой — поле для ввода текста и полосу прокрутки.
Это сделано, чтобы textbox не налезал на кнопки и всегда был максимального размера.

В-третьих, появился виджет Text. Мы его создали с параметром wrap=’word’, чтобы текст переносился по словам. Основные методы Text: get, insert, delete. Get и delete принимают начальный и конечный индексы. Индекс — это строка вида ‘x.y’, где x — номер символа в строке, а y — номер строки, причём символы нумеруются с 1, а строки — с 0. То есть на самое начала текста указывает индекс ‘1.0’. Для обозначения конца текста есть индекс ‘end’. Также допустимы конструкции вида ‘1.end’.

B в-четвёртых, мы создали полосу прокрутки (Scrollbar). После создания её нужно связать с нужным виджетом, в данном случае, с textbox. Связывание двустороннее:

scrollbar['command'] = textbox.yview textbox['yscrollcommand'] = scrollbar.set

Вот и всё. Tkinter – это, безусловно, мощная и удобная библиотека. Мы осветили не все её возможности, остальные — тема дальнейших статей.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *