хранилище находится на том же компьютере, где выполняется сценарий в виде одного или более локальных файлов;
from tkinter import *
from tkinter.messagebox import showerror
import shelve
shelvename = ‘class-shelve’
fieldnames = (‘name’, ‘age’, ‘job’, ‘pay’)
def makeWidgets(): global entries window = Tk()
window.title(‘People Shelve’) form = Frame(window) form.pack() entries = {}
for (ix, label) in enumerate((‘key’,) + fieldnames): lab = Label(form, text=label) ent = Entry(form) lab.grid(row=ix, column=0) ent.grid(row=ix, column=1) entries[label] = ent
Button(window, text=”Fetch”, command=fetchRecord).pack(side=LEFT) Button(window, text=”Update”, command=updateRecord).pack(side=LEFT) Button(window, text=”Quit”, command=window.quit).pack(side=RIGHT) return window
def fetchRecord():
key = entries[‘key’].get() try:
record = db[key] # извлечь запись по ключу, отобразить в форме
except:
showerror(title=’Error’, message=’No such key!’) else:
for field in fieldnames:
entries[field].delete(0, END)
entries[field].insert(0, repr(getattr(record, field)))
def updateRecord():
key = entries[‘key’].get() if key in db:
record = db[key] # изменяется существующая запись
else:
from person import Person # создать/сохранить новую запись
record = Person(name=’?’, age=’?’) # eval: строки должны
# заключаться в кавычки
for field in fieldnames:
setattr(record, field, eval(entries[field].get())) db[key] = record
db = shelve.open(shelvename) window = makeWidgets() window.mainloop()
db.close() # в эту точку программа попадает при щелчке на кнопке Quit # или при закрытии окна
Для размещения надписей и полей ввода в этом сценарии вместо метода pack используется метод grid. Как мы увидим далее, этот метод располагает виджеты по столбцам и строкам сетки, что обеспечивает более естественное для форм выравнивание надписей и полей ввода по горизонтали. Далее мы также увидим, что неплохого размещения виджетов на форме можно добиться и с помощью метода pack, добавив вложенные контейнеры для размещения виджетов по горизонтали и надписи фиксированной длины. Пока графический интерфейс никак не обрабатывает изменение размеров окна (для этого необходимы параметры настройки, которые мы будем исследовать позже), тем не менее объем программного кода реализации такой возможности при использовании любого из методов, grid или pack, будет примерно один и тот же.
Обратите внимание, что в конце сценария сначала открывается хранилище, как глобальная переменная, а затем запускается графический интерфейс - хранилище остается открытым на протяжении всего времени работы графического интерфейса (функция mainloop возвращает управление только после закрытия главного окна). Как будет показано в следующем разделе, такое удержание хранилища в открытом состоянии существенно отличает графический интерфейс от веб-интерфейса, где каждая операция обычно является автономной программой. Обратите также внимание, что использование глобальных переменных делает программный код более простым, но непригодным для использования вне контекста нашей базы данных; подробнее об этом мы поговорим ниже.
Пользование графическим интерфейсом
Построенный нами графический интерфейс достаточно прост, но он позволяет просматривать и изменять содержимое файла хранилища без ввода программного кода. Чтобы извлечь запись из хранилища и отобразить ее в графическом интерфейсе, необходимо ввести ключ в поле key (ключ) и щелкнуть на кнопке Fetch (Извлечь). Чтобы изменить запись, необходимо изменить содержимое полей записи после ее извлечения из хранилища и щелкнуть на кнопке Update (Изменить) - значения из полей ввода будут сохранены в базе данных. А чтобы добавить новую запись, необходимо заполнить все поля ввода новыми значениями и щелкнуть на кнопке Update (Изменить) - в хранилище будет добавлена новая запись с указанным ключом и значениями полей.
Другими словами, поля ввода служат одновременно и для отображения, и для ввода. На рис. 1.8 изображена форма после добавления новой записи (щелчком на кнопке Update (Изменить)), а на рис. 1.9 - диалог с сообщением об ошибке, когда пользователь попытался извлечь запись с ключом, отсутствующим в хранилище.
Обратите внимание, что для отображения значений полей, извлеченных из хранилища, здесь также используется функция repr, а для преобразования значений полей в объекты Python, перед тем как они будут записаны в хранилище, - функция eval. Как уже упоминалось выше, это потенциально опасно, так как открывает возможность ввести в поля ввода злонамеренный программный код, но мы пока не будем касаться этой проблемы.