added sqlite db and commands for gui, help and about windows

This commit is contained in:
vadzik 2023-12-15 15:31:49 +03:00
parent ecdd7c33ee
commit b934c0f3bd
8 changed files with 265 additions and 84 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"python.analysis.autoImportCompletions": true
}

BIN
AmDB.db Normal file

Binary file not shown.

View File

@ -1,2 +1,2 @@
[DEFAULT] [DEFAULT]
db_path = db.json db_path = AmDB.db

View File

@ -1 +0,0 @@
["{\"id\":\"96235c7e-19b1-4510-bb09-06013d1344a6\",\"name\":\"\u0410\u0437\u043e\u0432\u0441\u043a\u043e\u0435 \u043c\u043e\u0440\u0435\",\"description\":\"\u043f\u043e\u043b\u0443\u0437\u0430\u043c\u043a\u043d\u0443\u0442\u043e\u0435 \u043c\u043e\u0440\u0435 \u0410\u0442\u043b\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u043a\u0435\u0430\u043d\u0430 \u043d\u0430 \u0432\u043e\u0441\u0442\u043e\u043a\u0435 \u0415\u0432\u0440\u043e\u043f\u044b, \u043e\u043c\u044b\u0432\u0430\u044e\u0449\u0435\u0435 \u043f\u043e\u0431\u0435\u0440\u0435\u0436\u044c\u0435 \u0420\u043e\u0441\u0441\u0438\u0438 \u0438 \u0423\u043a\u0440\u0430\u0438\u043d\u044b. \u0421\u0430\u043c\u043e\u0435 \u043c\u0435\u043b\u043a\u043e\u0435 \u043c\u043e\u0440\u0435 \u0432 \u043c\u0438\u0440\u0435: \u0433\u043b\u0443\u0431\u0438\u043d\u0430 \u043d\u0435 \u043f\u0440\u0435\u0432\u044b\u0448\u0430\u0435\u0442 13,5 \u043c\u0435\u0442\u0440\u043e\u0432[2], \u0441\u0440\u0435\u0434\u043d\u044f\u044f \u0433\u043b\u0443\u0431\u0438\u043d\u0430 \u043e\u043a\u043e\u043b\u043e 7,4 \u043c (\u043f\u043e \u0440\u0430\u0437\u043d\u044b\u043c \u043e\u0446\u0435\u043d\u043a\u0430\u043c \u2014 \u043e\u0442 6,8 \u0434\u043e 8 \u043c)[3]. \\n \u0421\u043e\u0435\u0434\u0438\u043d\u044f\u0435\u0442\u0441\u044f \u0441 \u0410\u0442\u043b\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043e\u043a\u0435\u0430\u043d\u043e\u043c \u0434\u043b\u0438\u043d\u043d\u043e\u0439 \u0446\u0435\u043f\u043e\u0447\u043a\u043e\u0439 \u043f\u0440\u043e\u043b\u0438\u0432\u043e\u0432 \u0438 \u043c\u043e\u0440\u0435\u0439: \u041a\u0435\u0440\u0447\u0435\u043d\u0441\u043a\u0438\u0439 \u043f\u0440\u043e\u043b\u0438\u0432 \u2014 \u0427\u0451\u0440\u043d\u043e\u0435 \u043c\u043e\u0440\u0435 \u2014 \u043f\u0440\u043e\u043b\u0438\u0432 \u0411\u043e\u0441\u0444\u043e\u0440 \u2014 \u041c\u0440\u0430\u043c\u043e\u0440\u043d\u043e\u0435 \u043c\u043e\u0440\u0435 \u2014 \u043f\u0440\u043e\u043b\u0438\u0432 \u0414\u0430\u0440\u0434\u0430\u043d\u0435\u043b\u043b\u044b \u2014 \u042d\u0433\u0435\u0439\u0441\u043a\u043e\u0435 \u043c\u043e\u0440\u0435 \u2014 \u0421\u0440\u0435\u0434\u0438\u0437\u0435\u043c\u043d\u043e\u0435 \u043c\u043e\u0440\u0435 \u2014 \u0413\u0438\u0431\u0440\u0430\u043b\u0442\u0430\u0440\u0441\u043a\u0438\u0439 \u043f\u0440\u043e\u043b\u0438\u0432. \u041f\u043e \u043e\u0442\u0434\u0430\u043b\u0451\u043d\u043d\u043e\u0441\u0442\u0438 \u043e\u0442 \u043e\u043a\u0435\u0430\u043d\u0430 \u0410\u0437\u043e\u0432\u0441\u043a\u043e\u0435 \u043c\u043e\u0440\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0430\u043c\u044b\u043c \u043a\u043e\u043d\u0442\u0438\u043d\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u043c\u043e\u0440\u0435\u043c \u043f\u043b\u0430\u043d\u0435\u0442\u044b. \u041e\u0431\u044a\u0451\u043c \u0432\u043e\u0434\u044b \u2014 290 \u043a\u043c\u00b3[4]. \\n \u0412 \u0434\u0440\u0435\u0432\u043d\u043e\u0441\u0442\u0438 \u0410\u0437\u043e\u0432\u0441\u043a\u043e\u0433\u043e \u043c\u043e\u0440\u044f \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u043e \u0438 \u0414\u043e\u043d \u0432\u043f\u0430\u0434\u0430\u043b \u0432 \u0427\u0451\u0440\u043d\u043e\u0435 \u043c\u043e\u0440\u0435 \u0432 \u0440\u0430\u0439\u043e\u043d\u0435 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u041a\u0435\u0440\u0447\u0435\u043d\u0441\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u043b\u0438\u0432\u0430. \u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0430\u043a\u0432\u0430\u0442\u043e\u0440\u0438\u0438 \u0410\u0437\u043e\u0432\u0441\u043a\u043e\u0433\u043e \u043c\u043e\u0440\u044f \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u043e \u043e\u043a\u043e\u043b\u043e 5600 \u0433\u043e\u0434\u0430 \u0434\u043e \u043d. \u044d.\",\"img_path\":\"img/azovskoe.jpg\"}"]

View File

@ -1,51 +1,81 @@
from typing import List from typing import LiteralString
from pydantic import BaseModel, Field, TypeAdapter import sqlite3
from uuid import UUID, uuid4
import json import json
class Record(BaseModel): class Record:
id: UUID = Field(default_factory=uuid4) id: int | None = None
name: str | None = '' name: str | None = ""
description:str | None = '' description: str | None = ""
img_path: str | None = '' img_path: str | None = ""
def __init__(self, id, name, img_path, description):
self.id = id
self.name = name
self.img_path = img_path
self.description = description
class DB: class DB:
con = None
records: List[Record] = [] path = ""
path = '' def __init__(self, path="AmDB.db"):
if path != "":
def __init__(self, path=''): self.con = sqlite3.connect(path)
if path != '': self.cur = self.con.cursor()
with open(path, 'r') as file: self.cur.execute(
js_data = json.load(file) """CREATE TABLE IF NOT EXISTS records (
tmp = [] id INTEGER PRIMARY KEY AUTOINCREMENT,
for item in js_data: name TEXT NOT NULL,
tmp.append(json.loads(item)) path TEXT NOT NULL,
js_data = tmp description BLOB
self.records = [Record(**model_data) for model_data in js_data] )""")
self.path = path self.path = path
def add_record(self, name="", description="", img_path=""):
self.cur.execute(
'''INSERT INTO records (name, path, description) VALUES (?, ?, ?)''', (name, img_path, description))
self.con.commit()
def add_record(self, name='', description='', img_path=''): def edit_record(self, old_name, name="", img_path="", description=""):
new_rec = Record(name=name, description=description, img_path=img_path) record = self.get_record_by_name(old_name)
self.records.append(new_rec) if record.name != name:
with open(self.path, 'w') as file: self.cur.execute(
model_list_json = [record.model_dump_json() for record in self.records] 'UPDATE records SET name = ? WHERE name = ?', (name, old_name))
json.dump(model_list_json, file) if record.description != description:
self.cur.execute(
'UPDATE records SET description = ? WHERE name = ?', (description, old_name))
if record.img_path != img_path:
self.cur.execute(
'UPDATE records SET path = ? WHERE name = ?', (img_path, old_name))
self.con.commit()
def edit_record(self, id): def get_record_by_name(self, name):
pass self.cur.execute('SELECT * FROM records WHERE name=?', (name,))
record = self.cur.fetchone()
if record == None:
return None
return Record(record[0], record[1], record[2], record[3])
def load_records(self):
self.cur.execute('''SELECT * FROM records''')
records = []
for row in self.cur.fetchall():
new_rec = Record(row[0], row[1], row[2], row[3])
records.append(new_rec)
return records
def delete_record(self, id): def delete_record(self, name):
for i in range(len(self.records)): self.cur.execute("DELETE FROM records WHERE name = ?", (name,))
if self.records[i].id == id: self.con.commit()
self.records.pop(i)
def close(self):
self.con.close()
# db_t = DB('db.json') # db_t = DB("AmDB.db")
# db_t.add_record(name='Азовское море',description='полузамкнутое море Атлантического океана на востоке Европы, омывающее побережье России и Украины. Самое мелкое море в мире: глубина не превышает 13,5 метров[2], средняя глубина около 7,4 м (по разным оценкам — от 6,8 до 8 м)[3]. \n Соединяется с Атлантическим океаном длинной цепочкой проливов и морей: Керченский пролив — Чёрное море — пролив Босфор — Мраморное море — пролив Дарданеллы — Эгейское море — Средиземное море — Гибралтарский пролив. По отдалённости от океана Азовское море является самым континентальным морем планеты. Объём воды — 290 км³[4]. \n В древности Азовского моря не существовало и Дон впадал в Чёрное море в районе современного Керченского пролива. Предполагается, что заполнение акватории Азовского моря произошло около 5600 года до н. э.', img_path='img/azovskoe.jpg') # db_t.add_record(name='Азовское море',description='\tПолузамкнутое море Атлантического океана на востоке Европы, омывающее побережье России и Украины. Самое мелкое море в мире: глубина не превышает 13,5 метров[2], средняя глубина около 7,4 м (по разным оценкам — от 6,8 до 8 м)[3]. \n\tСоединяется с Атлантическим океаном длинной цепочкой проливов и морей: Керченский пролив — Чёрное море — пролив Босфор — Мраморное море — пролив Дарданеллы — Эгейское море — Средиземное море — Гибралтарский пролив. По отдалённости от океана Азовское море является самым континентальным морем планеты. Объём воды — 290 км³[4]. \n\tВ древности Азовского моря не существовало и Дон впадал в Чёрное море в районе современного Керченского пролива. Предполагается, что заполнение акватории Азовского моря произошло около 5600 года до н. э.', img_path='img/azovskoe.jpg')
# db_t.close()

BIN
img/BlackSea.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

BIN
img/Black_sea_RUS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

211
main.py
View File

@ -1,5 +1,6 @@
from ctypes import alignment
from tkinter import * from tkinter import *
from tkinter import ttk from tkinter import ttk, filedialog, messagebox
import configparser import configparser
import db import db
from PIL import ImageTk, Image from PIL import ImageTk, Image
@ -11,12 +12,13 @@ listbox = None
image_label = None image_label = None
desc_label = None desc_label = None
img = None img = None
fViewHelp = False
def load_config(): def load_config():
global config global config
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read('cfg.ini') config.read('cfg.ini')
print(config['DEFAULT']['db_path'])
def load_database(): def load_database():
@ -41,36 +43,186 @@ def fond_menu():
Label(newWindow, Label(newWindow,
text="This is a new window").pack() text="This is a new window").pack()
def close_prog(event):
def close_prog(event=None):
global database
database.close()
exit() exit()
def key_pressed(event): def key_pressed(event):
print(event.keysym) print(event.keysym)
if event.keysym == 'F1': if event.keysym == 'F1':
print("Клавиша 'F1' была нажата") open_help_window()
if event.keysym == 'F2': if event.keysym == 'F2':
print("Клавиша 'F2' была нажата") add_record_window()
if event.keysym == 'F3': if event.keysym == 'F3':
print("Клавиша 'F3' была нажата") confirm_delete()
if event.keysym == 'F4': if event.keysym == 'F4':
print("Клавиша 'F4' была нажата") update_record_window()
def selected(event): def selected(event):
global listbox, image_label, desc_label, img global listbox, image_label, desc_label, img
print(event)
# получаем индексы выделенных элементов
selected_indices = listbox.curselection() selected_indices = listbox.curselection()
# получаем сами выделенные элементы
selected = ''.join([listbox.get(i) for i in selected_indices]) selected = ''.join([listbox.get(i) for i in selected_indices])
for record in database.records: record = database.get_record_by_name(selected)
if record.name == selected:
print(record.img_path)
img = ImageTk.PhotoImage(Image.open(record.img_path)) img = ImageTk.PhotoImage(Image.open(record.img_path))
image_label.config(image=img) image_label.config(image=img)
# image_label.pack()
desc_label.insert(1.0, record.description) desc_label.insert(1.0, record.description)
def add_record_window(event=""):
global database, listbox
def browse_image():
filename = filedialog.askopenfilename(parent=add_window)
image_path_entry.delete(0, "end")
image_path_entry.insert(0, filename)
def add_button():
database.add_record(name=name_entry.get(), description=description_entry.get(
1.0, "end-1c"), img_path=image_path_entry.get())
load_listbox()
add_window.destroy()
# Создание окна
add_window = Toplevel()
add_window.title("Добавить запись")
# Создание и размещение виджетов
name_label = Label(add_window, text="Имя:")
name_label.grid(row=0, column=0)
name_entry = Entry(add_window, width=50)
name_entry.grid(row=0, column=1)
description_label = Label(add_window, text="Описание:")
description_label.grid(row=1, column=0)
description_entry = Text(add_window, width=50, height=10)
description_entry.grid(row=1, column=1)
image_path_label = Label(add_window, text="Путь до изображения:")
image_path_label.grid(row=2, column=0)
image_path_entry = Entry(add_window, width=50)
image_path_entry.grid(row=2, column=1)
browse_button = Button(add_window, text="Обзор", command=browse_image)
browse_button.grid(row=2, column=2)
update_button = Button(add_window, text="Добавить", command=add_button)
update_button.grid(row=3, column=0, columnspan=2)
add_window.mainloop()
def update_record_window(event=""):
global database, listbox
def browse_image():
filename = filedialog.askopenfilename(parent=update_window)
image_path_entry.delete(0, "end")
image_path_entry.insert(0, filename)
def edit_button():
database.edit_record(selected, name=name_entry.get(), description=description_entry.get(
1.0, "end-1c"), img_path=image_path_entry.get())
load_listbox()
update_window.destroy()
selected_indices = listbox.curselection()
selected = ''.join([listbox.get(i) for i in selected_indices])
record = database.get_record_by_name(selected)
if record == None:
return
# Создание окна
update_window = Toplevel()
update_window.title("Изменить запись")
update_window.lift()
# Создание и размещение виджетов
name_label = Label(update_window, text="Имя:")
name_label.grid(row=0, column=0)
name_entry = Entry(update_window, width=50)
name_entry.insert(0, record.name)
name_entry.grid(row=0, column=1)
description_label = Label(update_window, text="Описание:")
description_label.grid(row=1, column=0)
description_entry = Text(update_window, width=50, height=10)
description_entry.insert("1.0", record.description)
description_entry.grid(row=1, column=1)
image_path_label = Label(update_window, text="Путь до изображения:")
image_path_label.grid(row=2, column=0)
image_path_entry = Entry(update_window, width=50)
image_path_entry.insert(0, record.img_path)
image_path_entry.grid(row=2, column=1)
browse_button = Button(update_window, text="Обзор", command=browse_image)
browse_button.grid(row=2, column=2)
update_button = Button(update_window, text="Изменить", command=edit_button)
update_button.grid(row=3, column=0, columnspan=2)
update_window.mainloop()
def load_listbox():
global listbox, database
listbox.delete(0, "end")
for i, record in enumerate(database.load_records(), 0):
listbox.insert(i, record.name)
def open_help_window():
global fViewHelp
print(fViewHelp)
def close_help():
global fViewHelp
fViewHelp = False
top.destroy()
if not fViewHelp:
top = Toplevel()
top.title("Справка")
top.geometry("400x200")
top.maxsize(400, 200)
top.minsize(400, 200)
top.focus_set()
top.protocol("WM_DELETE_WINDOW", close_help)
label = Label(top, text="База данных 'Известные моря России'\nПозволяет добавлять / изменять / удалять \n информацию.\nКлавиши программы:\nF1-вызов справки о программе,\nF2-добавить в базу данных,\nF3-удалить из базы данных,\nF4-изменить запись в базе данных,\nF10-меню программы", justify='left', anchor='w')
label.place(x=0, y=0, relwidth=0.95)
close_button = Button(top, text="Закрыть", command=close_help)
close_button.place(x=300, y=150)
fViewHelp = True
def open_about_window():
aboutwindow = Toplevel()
aboutwindow.title("О программе")
aboutwindow.geometry("300x100")
aboutwindow.maxsize(300, 100)
aboutwindow.minsize(300, 100)
aboutwindow.grab_set()
label = Label(aboutwindow, text="База данных 'Известные моря России'\n(c) Romanko M.I., Russia, 2023",
anchor=CENTER, justify='center')
label.place(relx=0.5, rely=0.5, anchor='center')
def confirm_delete():
global database, listbox
selected_indices = listbox.curselection()
selected = ''.join([listbox.get(i) for i in selected_indices])
result = messagebox.askquestion(
"Подтверждение", "Вы уверены, что хотите удалить запись?")
if result == 'yes':
database.delete_record(selected)
messagebox.showinfo("Успех", "Запись успешно удалена")
load_listbox()
else:
messagebox.showinfo("Отмена", "Удаление отменено")
def main(): def main():
global root, listbox, database, image_label, desc_label global root, listbox, database, image_label, desc_label
@ -80,57 +232,54 @@ def main():
root = Tk() root = Tk()
root.title("amDB") root.title("amDB")
root.geometry('600x400+50+50') root.geometry('700x500+50+50')
root.minsize(700, 500) root.minsize(700, 500)
root.maxsize(700, 500)
root.bind('<KeyPress-F1>', key_pressed) root.bind('<KeyPress-F1>', key_pressed)
root.bind('<KeyPress-F2>', key_pressed) root.bind('<KeyPress-F2>', key_pressed)
root.bind('<KeyPress-F3>', key_pressed) root.bind('<KeyPress-F3>', key_pressed)
root.bind('<KeyPress-F4>', key_pressed) root.bind('<KeyPress-F4>', key_pressed)
root.bind('<KeyPress-F10>', key_pressed) root.bind('<KeyPress-F10>', key_pressed)
root.bind('<Control-x>', close_prog) root.bind('<Control-x>', close_prog)
for c in range(3): root.columnconfigure(index=c, weight=1)
for r in range(2): root.rowconfigure(index=r, weight=1)
main_menu = Menu() main_menu = Menu()
fond_menu = Menu(tearoff=0) fond_menu = Menu(tearoff=0)
fond_menu.add_command(label="Найти...") fond_menu.add_command(label="Найти...")
fond_menu.add_separator() fond_menu.add_separator()
fond_menu.add_command(label="Добавить") fond_menu.add_command(label="Добавить F2", command=add_record_window)
fond_menu.add_command(label="Удалить") fond_menu.add_command(label="Удалить F3", command=confirm_delete)
fond_menu.add_command(label="Изменить") fond_menu.add_command(label="Изменить F4", command=update_record_window)
fond_menu.add_separator() fond_menu.add_separator()
fond_menu.add_command(label="Выход") fond_menu.add_command(label="Выход Ctrl+X", command=close_prog)
help_menu = Menu(tearoff=0) help_menu = Menu(tearoff=0)
help_menu.add_command(label="Содержание") help_menu.add_command(label="Содержание", command=open_help_window)
help_menu.add_separator() help_menu.add_separator()
help_menu.add_command(label="О программе") help_menu.add_command(label="О программе", command=open_about_window)
main_menu.add_cascade(label="Фонд", menu=fond_menu) main_menu.add_cascade(label="Фонд", menu=fond_menu)
main_menu.add_cascade(label="Справка", menu=help_menu) main_menu.add_cascade(label="Справка", menu=help_menu)
label_bottom = Label(root, text="F1-справка F2-добавить F3-удалить F4-изменить F10-меню", bd=1, relief=SUNKEN, anchor=W, height=1) label_bottom = Label(root, text="F1-справка F2-добавить F3-удалить F4-изменить F10-меню",
bd=1, relief=SUNKEN, anchor=W, height=1)
label_bottom.place(relx=0.0, rely=0.95, relwidth=1) label_bottom.place(relx=0.0, rely=0.95, relwidth=1)
listbox = Listbox(root, justify="left", width=30) listbox = Listbox(root, justify="left", width=30)
listbox.bind("<<ListboxSelect>>", selected) listbox.bind("<<ListboxSelect>>", selected)
listbox.place(x=0, y=0, relheight=0.95) listbox.place(x=0, y=0, relheight=0.95)
for i in range(len(database.records)): load_listbox()
listbox.insert(i, database.records[i].name)
image_frame = ttk.Frame(root) image_frame = ttk.Frame(root)
image_label = Label(image_frame, text="Hello Tkinter", borderwidth=2) image_label = Label(image_frame)
image_label.place(relx=0.0, rely=0.0, relheight=1.0, relwidth=1.0) image_label.place(relx=0.0, rely=0.0, relheight=1.0, relwidth=1.0)
image_frame.place(relx=0.35, rely=0.0,relheight=0.95, relwidth=0.3) image_frame.place(x=200, rely=0.0, relheight=0.95, width=300)
desc_frame = ttk.Frame(root) desc_frame = ttk.Frame(root)
desc_label = Text(desc_frame, wrap="word") desc_label = Text(desc_frame, wrap="word")
desc_label.insert(1.0, "test")
desc_label.place(relx=0.0, rely=0.0, relheight=1.0, relwidth=1.0) desc_label.place(relx=0.0, rely=0.0, relheight=1.0, relwidth=1.0)
desc_frame.place(relx=0.7, rely=0.0, relwidth=0.3, relheight=0.95) desc_frame.place(x=500, rely=0.0, relwidth=0.3, relheight=0.95)
root.config(menu=main_menu) root.config(menu=main_menu)
root.mainloop() root.mainloop()