Работа с публичным репозиторием; события в TkInter

Объявления

  1. Проекту Strace нужны толковые студенты в Google Summer of Code 2021

  2. Толковые школьники могут принять участие в апробации курса по PyGame (пишите в ТГ-личку)

DVCS — не только для хранения и версионирования, но и для организации взаимодействия

Замечание: GIT не предоставляет канала обмена информацией. Это может быть email или мессенджер, движок сайта, почтовый голубь…

Удалённый репозиторий

Простая схема с одним публичным репозиторием:

git remote -v / .git/config

Проблема первоначального создания удалённого репозитория

Работа с несколькими удалёнными репозиториями:

Еще про tkinter

Tk: общие переменные

События и их обработчики:

Пример:

Ввод текста entry:

Пример (дополнительный код):

   1 class App(Application):
   2     def create_widgets(self):
   3         alpha = self.register(self.alpha)
   4         self.S = tk.StringVar()
   5         self.E = tk.Entry(self, textvariable=self.S,
   6                 validate='all', validatecommand=(alpha, '%V', '%S'))
   7         self.E.grid(columnspan=2)
   8         self.L = tk.Label(self, text="Lower letters only")
   9         self.L.grid(row=1, column=0)
  10         self.Q = tk.Button(self, text="Quit", command=self.master.quit)
  11         self.Q.grid(row=1, column=1)
  12 
  13     def alpha(self, why, txt):
  14         print(f"{why}: '{txt}'")
  15         return why != 'key' or txt.isalpha() and txt.islower()

Д/З

  1. Прочитать (про события и Entry) TODO

  2. <!> (необязательная) Задача-головоломка. Напишите класс Application(tk.Frame) таким образом, чтобы приведённый ниже код создавал приложение с интерфейсной моделью как на картинке (цвета воспроизводить не надо)

    • Код:
         1 class App(Application):
         2     def createWidgets(self):
         3         self.message = "Congratulations!\nYou've found a sercet level!"
         4         self.F1(tk.LabelFrame, "1:0", text="Frame 1")
         5         self.F1.B1(tk.Button, "0:0/NW", text="1")
         6         self.F1.B2(tk.Button, "0:1/NE", text="2")
         7         self.F1.B3(tk.Button, "1:0+1/SEW", text="3")
         8         self.F2(tk.LabelFrame, "1:1", text="Frame 2")
         9         self.F2.B1(tk.Button, "0:0/N", text="4")
        10         self.F2.B2(tk.Button, "0+1:1/SEN", text="5")
        11         self.F2.B3(tk.Button, "1:0/S", text="6")
        12         self.Q(tk.Button, "2.0:1.2/SE", text="Quit", command=self.quit)
        13         self.F1.B3.bind("<Any-Key>", lambda event: showinfo(self.message.split()[0], self.message))
        14 
        15 app = App(title="Sample application")
        16 app.mainloop()
      
    • Интерфейс
      • allinone.png

    • Особенности:
      1. Конструктором виджета является первое обращение к нему по имени, все последующие обращения по этому имени просто выдают сам объект-виджет

        • Первый параметр — тип виджета
        • Второй параметр — геометрия (см. далее)
        • Остальные параметры — именные параметры для создания виджета данного типа
      2. Виджет A.B встроен в виджет A.

        • Геометрия B указывается относительно A

        • В геометрии также указывается вес (эластичность, weight) рядов и колонок. Это значение наследуется от последнего заданного в данном ряду/колонке виджета (по умолчанию 1)

        • (в примере первые три кнопки встроены в F1, а следующие три — в F2, соответственно задана и геометрия)

      3. Обращение к уже заданному виджету работает как обычно (возвращается сам объект-виджет)

    • Синтаксис геометрии:
      • ряд.вес+высота:колонка.вес+ширина/гравитация

      • рядвиджет.grid(… row=ряд …)

      • .вес (необязательное поле) — виджет.master.rowconfigure(ряд, weight=вес)

        • по умолчанию = 1
      • +высота (необязательное поле) — grid(… rowspan=высота+1 …)

        • по умолчанию = 0 (⇒ rowspan = 1, виджет занимает один ряд)
      • для колонка.вес+ширина — соответственно

      • /гравитация (необязательное поле) — виджет.grid(… sticky=гравитация …)

        • по умолчанию "NEWS"

    • {i} Как я решал головоломку. Я сделал ешё чуть более головоломной!

      • Переписал __getattr__() (его всё равно надо переписывать) у Applicattion таким образом, чтобы обращение к отсутствующему полю возвращало конструктор

      • В конструкторе делал производный класс от типа виджета, в котором тоже переписал __getattr__(), но так, чтобы на несуществующее поле он возвращал либо поле master-объекта Application, если оно есть, либо конструктор объекта оттуда

        • Разница только в том, какой виджет является master-ом
        • Да, setattr/getattr(объект, "A.B.C") отлично работает ☺

      • В результате Tk видит иерархию виджетов, а в действительности все они — атрибуты объекта Application

        • Это сильно помогает программно найти виджет по имени

      • Возможно, я перемудрил ☺
  3. (если головоломку делать неохота; задача простая, но немного исследовательская) Реализовать класс InputLabel(tk.Label), для простейшего редактирования строк в виджете Label (такая дешёвая пластиковая имитация Entry). Должно поддерживаться

    • Требования и свойства
      • Разрешённый фокус и рамка фокуса по умолчанию
      • Ввод печатных символов
      • Текстовый курсор
      • Перемещение курсора стрелками и Home/End
      • Перемещение курсора и получение фокуса кликом мыши
      • Удаление символа перед курсором
    • Как реализовать курсор?
      • В Label, как и в любой другой виджет, можно встраивать ещё виджеты

        • Я встраивал Frame с правильным стилем и толщиной рамки

      • Для позиционирования курсора внутри Label использовать .place() (а не .grid())

      • Я не придумал, как вычислить ширину подстроки, поэтому используйте моноширинный шрифт и просто захардкодьте высоту и ширину одного знакоместа

        • у меня зашло …font="fixed", вам, возможно, придётся поупражняться с названием шрифта

      LabelEdit.gif

  4. Решать можно любую из задач (или обе, если интересно).
    • В зарегистрированном вами репозитории создать подкаталог 04_PublicRepositoryEvents (совпадает с финальной частью URL данной лекции), всё, что вы написали, положить туда

    • Решение головоломки (первой задачи) должно иметь название Simplified.py

    • Решение задачи на события (второй) должно иметь название LabelEdit.py

LecturesCMC/PythonDevelopment2021/04_PublicRepositoryEvents (последним исправлял пользователь FrBrGeorge 2021-03-14 22:14:25)