Использование глобального оператора не требуется в двух сценариях, которые приходить в голову.
- вы хотите создать код диалогового окна, которое можно импортировать для использования с основным графическим интерфейсом
- вы хотите закодировать диалоговое окно, которое можно импортировать для использования без основного графического интерфейса.
запрограммируйте диалоговое окно, которое можно импортировать для использования с основным графическим интерфейсом.
Избежать глобального оператора можно, передав словарь и ключ при создании экземпляра диалогового окна. Затем словарь и ключ можно связать с командой кнопки с помощью лямбда. Это создает анонимную функцию, которая будет выполнять вызов вашей функции (с аргументами) при нажатии кнопки.
Вы можете избежать необходимости передавать родительский элемент каждый раз, когда вы создаете экземпляр диалогового окна, привязав родительский элемент к атрибуту класса (root в этом примере).
Вы можете сохранить следующее как mbox.py
в your_python_folder\Lib\site-packages
или в той же папке, что и ваш основной файл графического интерфейса.
import tkinter
class Mbox(object):
root = None
def __init__(self, msg, dict_key=None):
"""
msg = <str> the message to be displayed
dict_key = <sequence> (dictionary, key) to associate with user input
(providing a sequence for dict_key creates an entry for user input)
"""
tki = tkinter
self.top = tki.Toplevel(Mbox.root)
frm = tki.Frame(self.top, borderwidth=4, relief='ridge')
frm.pack(fill='both', expand=True)
label = tki.Label(frm, text=msg)
label.pack(padx=4, pady=4)
caller_wants_an_entry = dict_key is not None
if caller_wants_an_entry:
self.entry = tki.Entry(frm)
self.entry.pack(pady=4)
b_submit = tki.Button(frm, text='Submit')
b_submit['command'] = lambda: self.entry_to_dict(dict_key)
b_submit.pack()
b_cancel = tki.Button(frm, text='Cancel')
b_cancel['command'] = self.top.destroy
b_cancel.pack(padx=4, pady=4)
def entry_to_dict(self, dict_key):
data = self.entry.get()
if data:
d, key = dict_key
d[key] = data
self.top.destroy()
Вы можете увидеть примеры подкласса TopLevel и tkSimpleDialog (tkinter.simpledialog в py3) в effbot.
Стоит отметить, что виджеты ttk в этом примере взаимозаменяемы с виджетами tkinter.
Чтобы точно центрировать диалоговое окно, прочтите → это.
Пример использования:
import tkinter
import mbox
root = tkinter.Tk()
Mbox = mbox.Mbox
Mbox.root = root
D = {'user':'Bob'}
b_login = tkinter.Button(root, text='Log in')
b_login['command'] = lambda: Mbox('Name?', (D, 'user'))
b_login.pack()
b_loggedin = tkinter.Button(root, text='Current User')
b_loggedin['command'] = lambda: Mbox(D['user'])
b_loggedin.pack()
root.mainloop()
закодируйте диалоговое окно, которое можно импортировать для использования без основного графического интерфейса.
Создайте модуль, содержащий класс диалогового окна (здесь MessageBox). Также включите функцию, которая создает экземпляр этого класса и, наконец, возвращает значение нажатой кнопки (или данные из виджета Entry).
Вот полный модуль, который вы можете настроить с помощью этих ссылок: NMTech и Effbot.
Сохраните следующий код как mbox.py
в your_python_folder\Lib\site-packages
import tkinter
class MessageBox(object):
def __init__(self, msg, b1, b2, frame, t, entry):
root = self.root = tkinter.Tk()
root.title('Message')
self.msg = str(msg)
# ctrl+c to copy self.msg
root.bind('<Control-c>', func=self.to_clip)
# remove the outer frame if frame=False
if not frame: root.overrideredirect(True)
# default values for the buttons to return
self.b1_return = True
self.b2_return = False
# if b1 or b2 is a tuple unpack into the button text & return value
if isinstance(b1, tuple): b1, self.b1_return = b1
if isinstance(b2, tuple): b2, self.b2_return = b2
# main frame
frm_1 = tkinter.Frame(root)
frm_1.pack(ipadx=2, ipady=2)
# the message
message = tkinter.Label(frm_1, text=self.msg)
message.pack(padx=8, pady=8)
# if entry=True create and set focus
if entry:
self.entry = tkinter.Entry(frm_1)
self.entry.pack()
self.entry.focus_set()
# button frame
frm_2 = tkinter.Frame(frm_1)
frm_2.pack(padx=4, pady=4)
# buttons
btn_1 = tkinter.Button(frm_2, width=8, text=b1)
btn_1['command'] = self.b1_action
btn_1.pack(side='left')
if not entry: btn_1.focus_set()
btn_2 = tkinter.Button(frm_2, width=8, text=b2)
btn_2['command'] = self.b2_action
btn_2.pack(side='left')
# the enter button will trigger the focused button's action
btn_1.bind('<KeyPress-Return>', func=self.b1_action)
btn_2.bind('<KeyPress-Return>', func=self.b2_action)
# roughly center the box on screen
# for accuracy see: https://stackoverflow.com/a/10018670/1217270
root.update_idletasks()
xp = (root.winfo_screenwidth() // 2) - (root.winfo_width() // 2)
yp = (root.winfo_screenheight() // 2) - (root.winfo_height() // 2)
geom = (root.winfo_width(), root.winfo_height(), xp, yp)
root.geometry('{0}x{1}+{2}+{3}'.format(*geom))
# call self.close_mod when the close button is pressed
root.protocol("WM_DELETE_WINDOW", self.close_mod)
# a trick to activate the window (on windows 7)
root.deiconify()
# if t is specified: call time_out after t seconds
if t: root.after(int(t*1000), func=self.time_out)
def b1_action(self, event=None):
try: x = self.entry.get()
except AttributeError:
self.returning = self.b1_return
self.root.quit()
else:
if x:
self.returning = x
self.root.quit()
def b2_action(self, event=None):
self.returning = self.b2_return
self.root.quit()
# remove this function and the call to protocol
# then the close button will act normally
def close_mod(self):
pass
def time_out(self):
try: x = self.entry.get()
except AttributeError: self.returning = None
else: self.returning = x
finally: self.root.quit()
def to_clip(self, event=None):
self.root.clipboard_clear()
self.root.clipboard_append(self.msg)
а также:
def mbox(msg, b1='OK', b2='Cancel', frame=True, t=False, entry=False):
"""Create an instance of MessageBox, and get data back from the user.
msg = string to be displayed
b1 = text for left button, or a tuple (<text for button>, <to return on press>)
b2 = text for right button, or a tuple (<text for button>, <to return on press>)
frame = include a standard outerframe: True or False
t = time in seconds (int or float) until the msgbox automatically closes
entry = include an entry widget that will have its contents returned: True or False
"""
msgbox = MessageBox(msg, b1, b2, frame, t, entry)
msgbox.root.mainloop()
# the function pauses here until the mainloop is quit
msgbox.root.destroy()
return msgbox.returning
После того, как mbox создает экземпляр MessageBox, он запускает основной цикл,
который фактически останавливает функцию там до тех пор, пока основной цикл не будет завершен через root.quit()
.
Затем функция mbox может получить доступ к msgbox.returning
и вернуть его значение.
Пример:
user = {}
mbox('starting in 1 second...', t=1)
user['name'] = mbox('name?', entry=True)
if user['name']:
user['sex'] = mbox('male or female?', ('male', 'm'), ('female', 'f'))
mbox(user, frame=False)
person
Honest Abe
schedule
08.04.2012