[PYTHON] 사용자 정의 팝업 tkinter 대화 상자를 구현하는 올바른 방법
PYTHON사용자 정의 팝업 tkinter 대화 상자를 구현하는 올바른 방법
방금 사용자 지정 팝업 대화 상자를 만드는 방법을 배우기 시작했습니다. 실제로 tkinter messagebox는 사용하기가 쉽지만 너무 많이하지는 않습니다. 다음은 입력을 받아서 사용자 이름에 저장하는 대화 상자를 만드는 나의 시도입니다.
내 질문은 이것을 구현하는 데 권장되는 스타일은 무엇입니까? Bryan Oakley가이 의견에서 제안했듯이.
어쩌면 전역 변수를 사용하여 내 문자열을 반환하는 것이 가장 좋은 방법은 아니지만 그 이유는 무엇입니까? 그리고 제안 된 방법은 무엇입니까? 창문이 파손되면 어떻게 끈을 묶는지를 모르기 때문에 혼란스러워집니다. 그리고 실제 위젯을 파괴하는 것에 대해, 그가 TopLevel을 참조하는지 확신 할 수 없습니다.
내가 물어 보는 이유는 제출 버튼을 누른 후에 팝업 상자가 파괴되기를 원하기 때문입니다. 결국 주 프로그램으로 다시 돌아가서 무언가를 업데이트하기를 원하기 때문입니다.이 경우 버튼 메서드가 무엇을 보내야합니까? 이 특정 예제의 아이디어는 사용자가 원하는 경우 반복해서 수행하도록 허용하기위한 것입니다.
import tkinter as tk
class MyDialog:
def __init__(self, parent):
top = self.top = tk.Toplevel(parent)
self.myLabel = tk.Label(top, text='Enter your username below')
self.myLabel.pack()
self.myEntryBox = tk.Entry(top)
self.myEntryBox.pack()
self.mySubmitButton = tk.Button(top, text='Submit', command=self.send)
self.mySubmitButton.pack()
def send(self):
global username
username = self.myEntryBox.get()
self.top.destroy()
def onClick():
inputDialog = MyDialog(root)
root.wait_window(inputDialog.top)
print('Username: ', username)
username = 'Empty'
root = tk.Tk()
mainLabel = tk.Label(root, text='Example for pop up input box')
mainLabel.pack()
mainButton = tk.Button(root, text='Click me', command=onClick)
mainButton.pack()
root.mainloop()
해결법
-
==============================
1.마음에 오는 두 가지 시나리오에서 global 문을 사용하는 것은 불필요합니다.
마음에 오는 두 가지 시나리오에서 global 문을 사용하는 것은 불필요합니다.
대화 상자의 인스턴스를 만들 때 사전과 키를 전달하면 전역 명령문을 피할 수 있습니다. 그런 다음 람다를 사용하여 사전 및 키를 단추의 명령과 연결할 수 있습니다. 이렇게하면 버튼을 눌렀을 때 (args로) 함수 호출을 실행할 익명의 함수가 생성됩니다.
부모를 클래스 속성 (이 예제에서는 root)에 바인딩하여 대화 상자의 인스턴스를 만들 때마다 부모를 전달할 필요가 없습니다.
your_python_folder \ Lib \ site-packages 또는 메인 GUI의 파일과 같은 폴더에 mbox.py로 다음을 저장할 수 있습니다.
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()
effbot에서 TopLevel 및 tkSimpleDialog (py3의 tkinter.simpledialog)를 서브 클래 싱하는 예제를 볼 수 있습니다.
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의 도움으로 사용자 정의 할 수있는 완벽한 모듈입니다. 다음 코드를 your_python_folder \ Lib \ site-packages에 mbox.py로 저장하십시오.
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)
-
==============================
2.inputDialog 객체가 파괴되지 않았으므로 객체 속성에 액세스 할 수있었습니다. 반환 문자열을 특성으로 추가했습니다.
inputDialog 객체가 파괴되지 않았으므로 객체 속성에 액세스 할 수있었습니다. 반환 문자열을 특성으로 추가했습니다.
import tkinter as tk class MyDialog: def __init__(self, parent): top = self.top = tk.Toplevel(parent) self.myLabel = tk.Label(top, text='Enter your username below') self.myLabel.pack() self.myEntryBox = tk.Entry(top) self.myEntryBox.pack() self.mySubmitButton = tk.Button(top, text='Submit', command=self.send) self.mySubmitButton.pack() def send(self): self.username = self.myEntryBox.get() self.top.destroy() def onClick(): inputDialog = MyDialog(root) root.wait_window(inputDialog.top) print('Username: ', inputDialog.username) root = tk.Tk() mainLabel = tk.Label(root, text='Example for pop up input box') mainLabel.pack() mainButton = tk.Button(root, text='Click me', command=onClick) mainButton.pack() root.mainloop()
-
==============================
3.나는 Honest Abe의 두 번째 코드 부분을 사용했다 :
나는 Honest Abe의 두 번째 코드 부분을 사용했다 :
템플릿으로 사용하고 수정했습니다. 나는 엔트리 대신 콤보 박스가 필요 했으므로 구현했습니다. 다른 것을 필요로한다면 수정하는 것이 아주 쉽습니다.
다음은 변경 사항입니다.
제거됨
다음을 mbox.py로 your_python_folder \ Lib \ site-packages 또는 메인 GUI 파일과 같은 폴더에 저장하십시오.
import tkinter import tkinter.ttk as ttk class MessageBox(object): def __init__(self, msg, b1, b2, parent, cbo, cboList): root = self.root = tkinter.Toplevel(parent) root.title('Choose') root.geometry('100x100') root.resizable(False, False) root.grab_set() # modal self.msg = str(msg) 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) if cbo: message.pack(padx=8, pady=8) else: message.pack(padx=8, pady=20) # if entry=True create and set focus if cbo: self.cbo = ttk.Combobox(frm_1, state="readonly", justify="center", values= cboList) self.cbo.pack() self.cbo.focus_set() self.cbo.current(0) # 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 if cbo: btn_1.pack(side='left', padx=5) else: btn_1.pack(side='left', padx=10) if not cbo: btn_1.focus_set() btn_2 = tkinter.Button(frm_2, width=8, text=b2) btn_2['command'] = self.b2_action if cbo: btn_2.pack(side='left', padx=5) else: btn_2.pack(side='left', padx=10) # 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() root.geometry("210x110+%d+%d" % (parent.winfo_rootx()+7, parent.winfo_rooty()+70)) root.protocol("WM_DELETE_WINDOW", self.close_mod) # a trick to activate the window (on windows 7) root.deiconify() def b1_action(self, event=None): try: x = self.cbo.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() def close_mod(self): # top right corner cross click: return value ;`x`; # we need to send it a value, otherwise there will be an exception when closing parent window self.returning = ";`x`;" self.root.quit()
빠르고 쉽게 사용해야합니다. 다음은 그 예입니다.
from mbox import MessageBox from tkinter import * root = Tk() def mbox(msg, b1, b2, parent, cbo=False, cboList=[]): msgbox = MessageBox(msg, b1, b2, parent, cbo, cboList) msgbox.root.mainloop() msgbox.root.destroy() return msgbox.returning prompt = {} # it will only show 2 buttons & 1 label if (cbo and cboList) aren't provided # click on 'x' will return ;`x`; prompt['answer'] = mbox('Do you want to go?', ('Go', 'go'), ('Cancel', 'cancel'), root) ans = prompt['answer'] print(ans) if ans == 'go': # do stuff pass else: # do stuff pass allowedItems = ['phone','laptop','battery'] prompt['answer'] = mbox('Select product to take', ('Take', 'take'), ('Cancel', 'cancel'), root, cbo=True, cboList=allowedItems) ans = prompt['answer'] print(ans) if (ans == 'phone'): # do stuff pass elif (ans == 'laptop'): # do stuff pass else: # do stuff pass
from https://stackoverflow.com/questions/10057672/correct-way-to-implement-a-custom-popup-tkinter-dialog-box by cc-by-sa and MIT license
'PYTHON' 카테고리의 다른 글
[PYTHON] 파이썬에서 문자열로부터 모듈로드하기 (0) | 2018.10.04 |
---|---|
[PYTHON] 도커에 최소 플라스크 앱 배포 - 서버 연결 문제 (0) | 2018.10.04 |
[PYTHON] for 중첩 된 루프 방지하기 (0) | 2018.10.04 |
[PYTHON] 메소드에 인스턴스 멤버의 기본 인수 값을 전달하는 방법은 무엇입니까? (0) | 2018.10.04 |
[PYTHON] __eq__는 파이썬에서 어떤 순서로 처리됩니까? (0) | 2018.10.04 |