Share This
Связаться со мной
Крути в низ
Categories
//Python, Tkinter и SQL: создаем приложение с графическим интерфейсом, кнопками и окнами. Пример GUI на Python

Python, Tkinter и SQL: создаем приложение с графическим интерфейсом, кнопками и окнами. Пример GUI на Python

Изучаем Tkinter и основные SQL-команды в ходе разработки программы WordMatch с графическим интерфейсом и CRUD-модулем для удобного создания и редактирования пользовательских словарей.

python tkinter i sql sozdaem prilozhenie s graficheskim interfejsom knopkami i oknami primer gui na python 1fd3eb7 - Python, Tkinter и SQL: создаем приложение с графическим интерфейсом, кнопками и окнами. Пример GUI на Python

Обзор проекта

Приложение WordMatch включает в себя три модуля, которые могут работать и вместе, и по отдельности:

  1. Скрипт для создания пользовательского словаря.
  2. GUI интерфейс и набор CRUD операций для добавления, редактирования и удаления записей в словаре.
  3. GUI интерфейс и скрипт для проверки правильности сопоставления иностранных слов и значений, выведенных в случайном порядке.

python tkinter i sql sozdaem prilozhenie s graficheskim interfejsom knopkami i oknami primer gui na python 9822511 - Python, Tkinter и SQL: создаем приложение с графическим интерфейсом, кнопками и окнами. Пример GUI на Python

WordMatch состоит из трех независимых скриптов

Что мы изучим

  1. Как создавать базы данных, выполнять CRUD операции и запросы на языке SQL.
  2. Как обрабатывать события в элементах Listbox.
  3. Как назначить действия основным кнопкам программы и кнопке закрытия окна.

Скрипт для создания словаря

Словарь представляет собой базу данных SQLite, которая поставляется с Python по умолчанию. Для создания новой базы не придется устанавливать никакие дополнительные модули. Однако при желании можно установить набор дополнительных инструментов для работы с SQLite и один из визуальных браузеров-редакторов:

  1. SQLiteStudio
  2. DBeaver
  3. DB Browser for SQLite

Структура таблицы словаря dictionary задается в sql_create_dictionary_table скрипта create_new_db.py:

  • id – порядковый номер записи (целое число);
  • word – иностранное слово (текстовое поле);
  • meaning – значение слова на русском языке (текстовое поле).

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

         unable to open database file Ошибка: не удалось подключиться к базе.      

Если же путь вообще не указан, файл базы данных будет создан в текущей рабочей директории – в Windows это C:UsersUser.

При подключении к несуществующей базе SQLite создает файл базы автоматически, но только при условии, что указанный путь существует. Ниже приведен полный код скрипта для создания базы данных словаря. При этом папка Dictionary в поддиректории Users была создана заранее, а файл dictionary.db в ней был сгенерирован скриптом автоматически:

create_new_db.py

         import sqlite3 from sqlite3 import Error  def create_connection(db_file):     conn = None     try:         conn = sqlite3.connect(db_file)         return conn     except Error as e:         print(e)      return conn  def create_table(conn, create_table_sql):     try:         c = conn.cursor()         c.execute(create_table_sql)     except Error as e:         print(e)  def main():     database = r"dictionary_my.db"     # описание столбцов словаря - id номер, слово и значение     sql_create_dictionary_table = """ CREATE TABLE IF NOT EXISTS dictionary (                                         id integer PRIMARY KEY,                                         word text,                                         meaning text                                     ); """       # подключение к базе     conn = create_connection(database)      # создание таблицы dictionary     if conn is not None:         create_table(conn, sql_create_dictionary_table)     else:         print("Ошибка: не удалось подключиться к базе.")   if __name__ == '__main__':     main()           

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста» Интересно, перейти к каналу

GUI интерфейс и скрипт для набора CRUD операций

Графический интерфейс программы включает стандартные элементы Tkinter и несколько виджетов модуля Ttk. Для позиционирования элементов на поверхности окна в Tkinter есть целых три метода – pack(), place() и grid(). Мы воспользуемся последним, поскольку он предусматривает максимальную точность размещения. При использовании grid() все пространство окна делится на ряды row и столбцы column. Для каждого элемента нужно указать ряд и столбец, на пересечении которых он размещается:

         (row = 2, column = 0)     

Еще можно указать ширину элемента, если нужно, чтобы он соответствовал ширине нескольких столбцов:

         columnspan = 2     

python tkinter i sql sozdaem prilozhenie s graficheskim interfejsom knopkami i oknami primer gui na python f9984ad - Python, Tkinter и SQL: создаем приложение с графическим интерфейсом, кнопками и окнами. Пример GUI на Python

Визуальный интерфейс для CRUD

SQL-запросы и команды

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

         'SELECT * FROM dictionary ORDER BY word DESC'     

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

В функции add_word() используется команда для вставки новой записи:

         'INSERT INTO dictionary VALUES(NULL, ?, ?)'     

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

         parameters = (self.word.get(), self.meaning.get())     

python tkinter i sql sozdaem prilozhenie s graficheskim interfejsom knopkami i oknami primer gui na python cfdb050 - Python, Tkinter и SQL: создаем приложение с графическим интерфейсом, кнопками и окнами. Пример GUI на Python

Сообщение об успешном добавлении нового слова

Для удаления слова необходимо выделить соответствующую строку. Слово извлекается из выделенной строки:

         word = self.tree.item(self.tree.selection())['text']     

И передается в качестве параметра с командой удаления:

         query = 'DELETE FROM dictionary WHERE word = ?' self.run_query(query, (word, ))     

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

         value = word value = old_meaning      

python tkinter i sql sozdaem prilozhenie s graficheskim interfejsom knopkami i oknami primer gui na python 6ca0b32 - Python, Tkinter и SQL: создаем приложение с графическим интерфейсом, кнопками и окнами. Пример GUI на Python

Предварительное заполнение полей в окне редактирования

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

Фактическое обновление существующей записи производится командой со следующими параметрами:

         query = 'UPDATE dictionary SET word = ?, meaning = ? WHERE word = ? AND meaning = ?' parameters = (new_word, new_meaning, word, old_meaning)     

Вот полный код для CRUD скрипта и его интерфейса:

         from tkinter import ttk from tkinter import * import sqlite3  class Dictionary:     db_name = 'dictionary.db'      def __init__(self, window):          self.wind = window         self.wind.title('Редактирование словаря')          # создание элементов для ввода слов и значений         frame = LabelFrame(self.wind, text = 'Введите новое слово')         frame.grid(row = 0, column = 0, columnspan = 3, pady = 20)         Label(frame, text = 'Слово: ').grid(row = 1, column = 0)         self.word = Entry(frame)         self.word.focus()         self.word.grid(row = 1, column = 1)         Label(frame, text = 'Значение: ').grid(row = 2, column = 0)         self.meaning = Entry(frame)         self.meaning.grid(row = 2, column = 1)         ttk.Button(frame, text = 'Сохранить', command = self.add_word).grid(row = 3, columnspan = 2, sticky = W + E)         self.message = Label(text = '', fg = 'green')         self.message.grid(row = 3, column = 0, columnspan = 2, sticky = W + E)         # таблица слов и значений         self.tree = ttk.Treeview(height = 10, columns = 2)         self.tree.grid(row = 4, column = 0, columnspan = 2)         self.tree.heading('#0', text = 'Слово', anchor = CENTER)         self.tree.heading('#1', text = 'Значение', anchor = CENTER)          # кнопки редактирования записей         ttk.Button(text = 'Удалить', command = self.delete_word).grid(row = 5, column = 0, sticky = W + E)         ttk.Button(text = 'Изменить', command = self.edit_word).grid(row = 5, column = 1, sticky = W + E)          # заполнение таблицы         self.get_words()      # подключение и запрос к базе     def run_query(self, query, parameters = ()):         with sqlite3.connect(self.db_name) as conn:             cursor = conn.cursor()             result = cursor.execute(query, parameters)             conn.commit()         return result      # заполнение таблицы словами и их значениями     def get_words(self):         records = self.tree.get_children()         for element in records:             self.tree.delete(element)         query = 'SELECT * FROM dictionary ORDER BY word DESC'         db_rows = self.run_query(query)         for row in db_rows:             self.tree.insert('', 0, text = row[1], values = row[2])      # валидация ввода     def validation(self):         return len(self.word.get()) != 0 and len(self.meaning.get()) != 0     # добавление нового слова     def add_word(self):         if self.validation():             query = 'INSERT INTO dictionary VALUES(NULL, ?, ?)'             parameters =  (self.word.get(), self.meaning.get())             self.run_query(query, parameters)             self.message['text'] = 'слово {} добавлено в словарь'.format(self.word.get())             self.word.delete(0, END)             self.meaning.delete(0, END)         else:             self.message['text'] = 'введите слово и значение'         self.get_words()     # удаление слова      def delete_word(self):         self.message['text'] = ''         try:             self.tree.item(self.tree.selection())['text'][0]         except IndexError as e:             self.message['text'] = 'Выберите слово, которое нужно удалить'             return         self.message['text'] = ''         word = self.tree.item(self.tree.selection())['text']         query = 'DELETE FROM dictionary WHERE word = ?'         self.run_query(query, (word, ))         self.message['text'] = 'Слово {} успешно удалено'.format(word)         self.get_words()     # рeдактирование слова и/или значения     def edit_word(self):         self.message['text'] = ''         try:             self.tree.item(self.tree.selection())['values'][0]         except IndexError as e:             self.message['text'] = 'Выберите слово для изменения'             return         word = self.tree.item(self.tree.selection())['text']         old_meaning = self.tree.item(self.tree.selection())['values'][0]         self.edit_wind = Toplevel()         self.edit_wind.title = 'Изменить слово'          Label(self.edit_wind, text = 'Прежнее слово:').grid(row = 0, column = 1)         Entry(self.edit_wind, textvariable = StringVar(self.edit_wind, value = word), state = 'readonly').grid(row = 0, column = 2)                  Label(self.edit_wind, text = 'Новое слово:').grid(row = 1, column = 1)         # предзаполнение поля         new_word = Entry(self.edit_wind, textvariable = StringVar(self.edit_wind, value = word))         new_word.grid(row = 1, column = 2)           Label(self.edit_wind, text = 'Прежнее значение:').grid(row = 2, column = 1)         Entry(self.edit_wind, textvariable = StringVar(self.edit_wind, value = old_meaning), state = 'readonly').grid(row = 2, column = 2)           Label(self.edit_wind, text = 'Новое значение:').grid(row = 3, column = 1)         # предзаполнение поля         new_meaning= Entry(self.edit_wind, textvariable = StringVar(self.edit_wind, value = old_meaning))         new_meaning.grid(row = 3, column = 2)          Button(self.edit_wind, text = 'Изменить', command = lambda: self.edit_records(new_word.get(), word, new_meaning.get(), old_meaning)).grid(row = 4, column = 2, sticky = W)         self.edit_wind.mainloop()     # внесение изменений в базу     def edit_records(self, new_word, word, new_meaning, old_meaning):         query = 'UPDATE dictionary SET word = ?, meaning = ? WHERE word = ? AND meaning = ?'         parameters = (new_word, new_meaning, word, old_meaning)         self.run_query(query, parameters)         self.edit_wind.destroy()         self.message['text'] = 'слово {} успешно изменено'.format(word)         self.get_words()  if __name__ == '__main__':     window = Tk()     application = Dictionary(window)     window.mainloop()       

Модуль для запоминания слов и проверки значений

Английские слова и их значения загружаются в два элемента Listbox. Для перемешивания слов и значений в случайном порядке используется метод shuffle из модуля random. Для обработки событий (кликов) по спискам Listbox мы напишем две функции – callback_left и callback_right. Чтобы связать функции с Listbox, нужно воспользоваться методом bind:

         self.right.bind("<<ListboxSelect>>", self.callback_right) self.left.bind("<<ListboxSelect>>", self.callback_left)      

python tkinter i sql sozdaem prilozhenie s graficheskim interfejsom knopkami i oknami primer gui na python 24cbb63 - Python, Tkinter и SQL: создаем приложение с графическим интерфейсом, кнопками и окнами. Пример GUI на Python

Слова и значения выводятся в случайном порядке

Функция callback_left отслеживает клики по английским словам в левом элементе Listbox. Когда пользователь кликает по слову, функция посылает запрос в базу:

         'SELECT * from dictionary WHERE word = ?'     

Результат запроса – отдельная запись:

         record = cursor.fetchone()     

Второй элемент записи record[2] является значением слова, которое передается в функцию callback_right.

Функция callback_right обрабатывает клики по значениям слов в правом списке Listbox. Когда пользователь кликает по значению, функция сравнивает его со значением, полученным из callback_left. Если они совпадают – ответ является верным, и английское слово вместе с соответствующим значением удаляются из левого и правого списков:

         if click == self.trans:     self.right.delete(ANCHOR)     self.left.delete(ANCHOR)     

В противном случае выводится сообщение о неверном ответе, а выделение с ошибочного значения снимается.

В GUI модуля для заучивания слов доступны две кнопки: Редактировать для вызова скрипта и визуального интерфейса редактирования словаря, а также Начать сначала для перезагрузки слов и значений. Чтобы вызвать скрипт редактирования словаря, достаточно написать простейшую функцию:

         def run_edit(self):     os.system('edit_dictionary.py')     

Назначение команд для кнопок выглядит так:

         ttk.Button(text="Начать сначала", command=self.restart_program).grid(row = 4, column = 1, sticky = W + E) ttk.Button(text="Редактировать", command=self.run_edit).grid(row = 4, column = 0, sticky = W + E)     

python tkinter i sql sozdaem prilozhenie s graficheskim interfejsom knopkami i oknami primer gui na python 3318fc5 - Python, Tkinter и SQL: создаем приложение с графическим интерфейсом, кнопками и окнами. Пример GUI на Python

Редактировать словарь можно прямо из модуля запоминания слов

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

python tkinter i sql sozdaem prilozhenie s graficheskim interfejsom knopkami i oknami primer gui na python 21e7e09 - Python, Tkinter и SQL: создаем приложение с графическим интерфейсом, кнопками и окнами. Пример GUI на Python

Подтверждение выхода

Для этого нужно задать новый протокол:

         self.wind.protocol("WM_DELETE_WINDOW", self.on_exit)     

И добавить функцию:

             def on_exit(self):         if messagebox.askyesno("Выйти", "Закрыть программу?"):             self.wind.destroy()     

Это полный код модуля word_match.py для запоминания и проверки значений слов:

word_match.py

         from tkinter import ttk from tkinter import * import random, os import sqlite3  class Match:     db_name = 'dictionary.db'      def __init__(self, window):          self.wind = window         self.wind.title('Учим слова')         self.eng, self.trans = str(), str()         self.message = Label(text = '', fg = 'red')         self.message.grid(row = 1, column = 0, columnspan = 2, sticky = W + E)         # правая и левая колонки         self.left = Listbox(height = 12, exportselection=False, activestyle='none')         self.left.grid(row = 2, column = 0)         self.right = Listbox(height = 12, activestyle='none')         self.right.grid(row = 2, column = 1)         self.right.bind("<<ListboxSelect>>", self.callback_right)         self.left.bind("<<ListboxSelect>>", self.callback_left)         # назначение команд кнопкам программы и х-кнопке окна         ttk.Button(text="Начать сначала", command=self.restart_program).grid(row = 4, column = 1, sticky = W + E)         ttk.Button(text="Редактировать", command=self.run_edit).grid(row = 4, column = 0, sticky = W + E)         self.wind.protocol("WM_DELETE_WINDOW", self.on_exit)         # заполняем колонки словами           self.get_words()     #  закрытие программы по клику на кнопке х     def on_exit(self):         if messagebox.askyesno("Выйти", "Закрыть программу?"):             self.wind.destroy()     #  подключение к базе и передача запроса     def run_query(self, query, parameters = ()):         with sqlite3.connect(self.db_name) as conn:             cursor = conn.cursor()             result = cursor.execute(query, parameters)             conn.commit()         return result       # запрос на извлечение всех существующих записей из базы в алфавитном порядке     def get_words(self):         query = 'SELECT * FROM dictionary ORDER BY word DESC'         db_rows = self.run_query(query)         # формирование словаря из перемешанных в случайном порядке слов и их значений         lst_left, lst_right = [], []         for row in db_rows:             lst_left.append(row[1])             lst_right.append(row[2])         random.shuffle(lst_left)         random.shuffle(lst_right)         dic = dict(zip(lst_left, lst_right))         # заполнение правой и левой колонок         for k, v in dic.items():             self.left.insert(END, k)             self.right.insert(END, v)     # обработка клика по словам в левой колонке     def callback_left(self, event):         self.message['text'] = ''         if not event.widget.curselection():             return         # извлечение из базы значения выделенного слова         w = event.widget         idx = int(w.curselection()[0])         self.eng = w.get(idx)         with sqlite3.connect(self.db_name) as conn:             cursor = conn.cursor()             sqlite_select_query = 'SELECT * from dictionary WHERE word = ?'             cursor.execute(sqlite_select_query, (self.eng,))             record = cursor.fetchone()             self.trans = record[2]         # обработка клика в правой колонке       def callback_right(self, event1):         self.message['text'] = ''         if not event1.widget.curselection():             return                  w = event1.widget         idx = int(w.curselection()[0])         click = w.get(idx)         # если выбранное слово является правильным переводом, удаляем и оригинал, и значение         if click == self.trans:             self.right.delete(ANCHOR)             self.left.delete(ANCHOR)         # сообщаем о неверном значении         else:             self.message['text'] = 'Неправильно'             self.right.selection_clear(0, END)     # загружаем окно и скрипт редактирования словаря             def run_edit(self):         os.system('edit_dictionary.py')     # перезапуск программы     def restart_program(self):         self.message['text'] = ''         self.left.delete(0, END)         self.right.delete(0, END)         self.get_words()  if __name__ == '__main__':     window = Tk()     window.geometry('250x245+350+200')     application = Match(window)     window.mainloop()       

Готовый проект доступен в репозитории.

Материалы по теме

  • Как подружить Python и базы данных SQL. Подробное руководство
  • SQL за 20 минут
  • 🐘 Руководство по SQL для начинающих. Часть 1: создание базы данных, таблиц и установка связей между таблицами

  • 38 views
  • 0 Comment

Leave a Reply

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close