L'utilizzo dello global statement non è necessario nei due scenari che vengono in mente.
- si desidera codificare una finestra di dialogo che può essere importato da usare con una GUI principale
- si desidera codificare una finestra di dialogo che può essere importato da usare senza una GUI principale
codice di una finestra di dialogo che può essere importato da usare con una GUI principale
012.351.
È possibile evitare l'istruzione globale passando una chiave del dizionario & quando si crea un'istanza di una finestra di dialogo. Il tasto dizionario & può quindi essere associato al comando del pulsante, utilizzando lambda. Ciò crea una funzione anonima che eseguirà la chiamata di funzione (con argomenti) quando si preme il pulsante.
È possibile evitare la necessità di passare il padre ogni volta che si crea un'istanza della finestra di dialogo vincolando il genitore a un attributo di classe (root in questo esempio).
È possibile salvare quanto segue come mbox.py
in your_python_folder\Lib\site-packages
o nella stessa cartella del file della GUI principale.
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()
Si può vedere esempi che sottoclasse TopLevel e tkSimpleDialog (tkinter.simpledialog in PY3) a effbot.
Vale la pena notare che ttk widgets sono intercambiabili con i widget tkinter in questo esempio.
Per centrare accuratamente la finestra di dialogo, leggere → this.
Esempio di utilizzo:
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()
codice di una finestra di dialogo che può essere importato da usare senza una GUI principale
Creare un modulo contenente una classe di finestra di dialogo (MessageBox qui). Inoltre, includi una funzione che crea un'istanza di quella classe e infine restituisce il valore del pulsante premuto (o i dati da un widget di voce).
Ecco un modulo completo che è possibile personalizzare con l'aiuto di questi riferimenti: NMTech & Effbot.
Salvare il seguente codice come mbox.py
in 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)
e:
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
Dopo Mbox crea un'istanza di MessageBox avvia il ciclo principale,
che si ferma in modo efficace la funzione di lì fino all'uscita dal mainloop tramite root.quit()
.
La funzione mbox può quindi accedere a msgbox.returning
e restituirne il valore.
Esempio:
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)
Questo è bello. Collega. – Marcin
Come posso utilizzare il secondo codice in modo che l'interfaccia utente principale non sia selezionabile mentre viene chiamata la mbox? – DRTauli
@DRTauli Personalmente avrei nascosto la finestra se non volevo che le persone interagissero con essa; perché renderlo insensibile potrebbe far pensare all'utente che il programma si bloccasse. Tuttavia, è possibile disabilitare temporaneamente la maggior parte dei widget. Consiglio di porre questa domanda come una nuova domanda generalizzata; i commenti sono per chiarimenti e suggerimenti. –