[英]Python Tk : How to create a custom message box
Please give me advice.请给我建议。
Custom message box requirements include:自定义消息框要求包括:
Be modal (Realized)模态(已实现)
While the message box is displayed, the message box is on the front and other TK objects cannot be operated.消息框显示时,消息框在前面,不能操作其他TK对象。
Wait for the return value of the message box等待消息框的返回值
I want to wait for the message box input before moving on to the next action (display / hide widget etc.).我想在继续下一个操作(显示/隐藏小部件等)之前等待消息框输入。
I want to continue processing the application(Realized)我想继续处理申请(已实现)
The original Tk object wants to continue regular processing with the after method.原来的 Tk 对象想用 after 方法继续常规处理。
I want to adopt my own design(Realized)我想采用我自己的设计(实现)
In order to unify the design.为了统一设计。
-Execution result- -执行结果-
If you close the message box, an error is displayed.如果关闭消息框,则会显示错误。 It will not proceed to the second line in the Btn_Messagebox_clicked method until self.MainWindow_obj is closed.
在 self.MainWindow_obj 关闭之前,它不会进入 Btn_Messagebox_clicked 方法中的第二行。 I don't know why I get an error.
我不知道为什么我收到错误。 Also, if you close the message box, you won't know why it won't come back.
此外,如果您关闭消息框,您将不知道为什么它不会回来。
invalid command name "2291801753672dialog_mouse_release"
while executing
"2291801753672dialog_mouse_release 105 1 ?? ?? ?? 264 103442140 ?? 22 6 ?? 0 ?? ?? .!frame3.!button2 5 438 422 ??"
invoked from within
"if {"[2291801753672dialog_mouse_release 105 1 ?? ?? ?? 264 103442140 ?? 22 6 ?? 0 ?? ?? .!frame3.!button2 5 438 422 ??]" == "break"} break"
(command bound to event)
-code- -代码-
import tkinter as tk
from tkinter import ttk
from PIL import Image,ImageTk,ImageDraw,ImageFont
class CustomDialog(object):
def __init__(self):
self.title_bar_color = '#8FAADC'
self.item_ground_color = 'whitesmoke'
self.background_color = '#D9D9D9'
self.select_bar_color = '#BDD7EE'
self.isDrag_DlgMotion = False
self.drag_dx = 0
self.drag_dy = 0
def dialog_left_click(self,event):
dialog_x=self.dev_dialog.winfo_rootx()
dialog_y=self.dev_dialog.winfo_rooty()
point_x=self.dev_dialog.winfo_pointerx()
point_y=self.dev_dialog.winfo_pointery()
dx = point_x - dialog_x
dy = point_y - dialog_y
if (dx >= 0 and dx <= self.title_bar_width) and (dy >= 0 and dy <= self.title_bar_height):
self.drag_dx = dx
self.drag_dy = dy
self.isDrag_DlgMotion = True
return
def dialog_mouse_move_on(self,event):
if self.isDrag_DlgMotion:
X = event.x_root - self.drag_dx
Y = event.y_root - self.drag_dy
self.dev_dialog.geometry('+{0}+{1}'.format(X, Y))
pass
return
def dialog_mouse_release(self,event):
if self.isDrag_DlgMotion:
self.isDrag_DlgMotion = False
return
class CommonMessageBoxDialog(CustomDialog):
def __init__(self,title,message,state,parent = None):
self.return_state = None
if not isinstance(title,str) or not isinstance(message,str) or not isinstance(state,int):
return
if state < 1 or state > 3 :
return
root = ttk.tkinter.Tk()
#root = tk.Toplevel(parent)
#root.overrideredirect(True)
super().__init__()
self.box_state = state
self.box_message = message
self.box_title = title
W = 0
H = 1
self.dlg_size = [400,200]
self.title_bar_width = self.dlg_size[W]
self.title_bar_height = 40
self.btn_bar_height = 42
self.btn_32x32_size = 42
self.row_height = 28
self.btn_row_height = 32
self.frm_space = 10
self.parent = parent
self.CreateDialog(root)
root.wait_window(root)
#root.mainloop()
def CreateDialog(self,root):
W = 0
H = 1
if self.parent != None:
self.parent.update_idletasks()
ww=self.parent.winfo_screenwidth()
wh=self.parent.winfo_screenheight()
x=self.parent.winfo_rootx()
y=self.parent.winfo_rooty()
parent_w = self.parent.winfo_width()
parent_h = self.parent.winfo_height()
parent_x = self.parent.winfo_x()
parent_y = self.parent.winfo_y()
else:
root.update_idletasks()
ww=root.winfo_screenwidth()
wh=root.winfo_screenheight()
x=root.winfo_rootx()
y=root.winfo_rooty()
parent_w = root.winfo_width()
parent_h = root.winfo_height()
parent_x = root.winfo_x()
parent_y = root.winfo_y()
self.dev_dialog = root
dialog = self.dev_dialog
dialog.overrideredirect(True)
dlg_x = int((parent_x+parent_w) - (self.dlg_size[W]/2))
dlg_y = int((parent_y+parent_h) - (self.dlg_size[H]/2))
if dlg_x < 0 : dlg_x = 0
if dlg_y < 0 : dlg_y = 0
dialog.geometry('{}x{}+{}+{}'.format(self.dlg_size[W],self.dlg_size[H],dlg_x,dlg_y))
self.Title_Bar = tk.Frame(
dialog,
relief='flat',
bg = self.title_bar_color ,
)
self.Title_Label = tk.Label(
self.Title_Bar,
bg = self.title_bar_color ,
text = self.box_title,
)
dialog.bind('<Button-1>', self.dialog_left_click)
dialog.bind('<B1-Motion>', self.dialog_mouse_move_on)
dialog.bind('<ButtonRelease-1>',self.dialog_mouse_release)
self.MsgArea_frame = tk.Frame(
dialog,
relief='flat',
bg = self.select_bar_color,
)
self.message_frame = tk.Frame(
self.MsgArea_frame,
relief='flat',
bg = self.item_ground_color ,
)
self.label_message = tk.Label(
self.message_frame,
bg = self.item_ground_color ,
text = self.box_message,
)
self.BtnArea_frame = tk.Frame(
dialog,
relief='flat',
bg = self.item_ground_color,
)
self.btn_ok = tk.Button(
self.BtnArea_frame,
bg = self.item_ground_color,
text = 'OK',
command = lambda:self.btn_msgbox_clicked(1),
)
self.btn_yes = tk.Button(
self.BtnArea_frame,
bg = self.item_ground_color,
text = 'YES',
command = lambda:self.btn_msgbox_clicked(1),
)
self.btn_no = tk.Button(
self.BtnArea_frame,
bg = self.item_ground_color,
text = 'NO',
command = lambda:self.btn_msgbox_clicked(2),
)
self.btn_cancel = tk.Button(
self.BtnArea_frame,
bg = self.item_ground_color,
text = 'CANCEL',
command = lambda:self.btn_msgbox_clicked(3),
)
frm_space = self.frm_space
msg_frm_w = 4
btn_fram_h = 36
message_area_h = self.dlg_size[H] - self.title_bar_height - frm_space *2 - btn_fram_h
# Frame
self.Title_Bar.place(
x = 0, y = 0,
width = self.title_bar_width, height = self.title_bar_height
)
self.MsgArea_frame.place(
x = frm_space, y = self.title_bar_height + frm_space,
width = self.title_bar_width - frm_space*2, height = message_area_h
)
self.BtnArea_frame.place(
x = 0, y = self.title_bar_height + frm_space + message_area_h,
width = self.title_bar_width, height = btn_fram_h
)
self.Title_Label.grid(row = 0, column = 1,sticky = tk.W+tk.N+tk.S)
self.Title_Bar.columnconfigure(0,minsize = self.frm_space)
self.Title_Bar.rowconfigure(0,minsize = self.title_bar_height)
self.MsgArea_frame.columnconfigure(0,minsize = self.frm_space)
self.MsgArea_frame.rowconfigure(0,minsize = message_area_h)
self.BtnArea_frame.rowconfigure(0,minsize = btn_fram_h)
self.message_frame.place(
x = msg_frm_w, y = msg_frm_w,
width = self.title_bar_width - frm_space*2 - msg_frm_w*2, height = message_area_h - msg_frm_w*2,
)
# self.message_frame
self.label_message.grid(row = 0, column = 1,sticky = tk.W+tk.N+tk.S)
if self.box_state == 1:
self.btn_ok.place(
x = (self.title_bar_width/2) - 80/2 , y = btn_fram_h/2 - 24/2,
)
if self.box_state == 2:
self.btn_yes.place(
x = (self.title_bar_width/2) - (80 + frm_space) , y = btn_fram_h/2 - 24/2,
)
self.btn_no.place(
x = (self.title_bar_width/2) + frm_space , y = btn_fram_h/2 - 24/2,
)
if self.box_state == 3:
self.btn_yes.place(
x = (self.title_bar_width/2) - (80*1.5 + frm_space*2) , y = btn_fram_h/2 - 24/2,
)
self.btn_no.place(
x = (self.title_bar_width/2) - 80/2 , y = btn_fram_h/2 - 24/2,
)
self.btn_cancel.place(
x = (self.title_bar_width/2) + 80/2 + frm_space*2 , y = btn_fram_h/2 - 24/2,
)
#dialog.grab_set()
dialog.grab_set_global()
def btn_msgbox_clicked(self,state):
self.return_state = state
self.dev_dialog.grab_release()
self.dev_dialog.destroy()
def get_return_state(self):
return self.return_state
class CreateScreen(object):
def __init__(self):
self.cnt = 0
W = 0
H = 1
self.dlg_size = [400,200]
geo_string = '{}x{}'.format(self.dlg_size[W],self.dlg_size[H])
self.MainWindow_obj = ttk.tkinter.Tk()
self.MainWindow_obj.geometry(geo_string)
self.CntSting = tk.StringVar()
self.CntSting.set('...')
Label_Conter_text = tk.Label(
self.MainWindow_obj,
textvariable = self.CntSting,
)
self.MsgSting = tk.StringVar()
self.MsgSting.set(str(self.cnt))
Label_Message_text = tk.Label(
self.MainWindow_obj,
textvariable = self.MsgSting,
)
Btn_Messagebox = tk.Button(
self.MainWindow_obj,
text = 'Push',
command = self.Btn_Messagebox_clicked
)
Label_Conter_text.pack()
Label_Message_text.pack()
Btn_Messagebox.pack()
self.MainWindow_obj.after(1000,self.loop_msg)
self.MainWindow_obj.mainloop()
def Btn_Messagebox_clicked(self):
self.dlg = CommonMessageBoxDialog(title='Test',message='Do you remember ?',state=3,parent =self.MainWindow_obj)
ret = self.dlg.get_return_state()
if ret == 1:
self.MsgSting.set('Yes')
if ret == 2:
self.MsgSting.set('No')
if ret == 3:
self.MsgSting.set('Cancel')
return
def loop_msg(self):
self.cnt += 1
self.MsgSting.set(str(self.cnt))
self.MainWindow_obj.after(1000,self.loop_msg)
if __name__ == '__main__':
screen_obj = CreateScreen()
I solved it myself.我自己解决了。
I'm binding mouse events for custom dialog navigation.我正在为自定义对话框导航绑定鼠标事件。 Therefore, after Destroy was called, the dialog with the mouse release event discarded was notified and an error occurred.
因此,在调用Destroy 后,会通知丢弃鼠标释放事件的对话框并发生错误。
To solve this, I made self.isDestroy, and in the btn_msgbox_clicked method, changed self.isDestroy to True and connected the dialog_mouse_release method to the destroy method.为了解决这个问题,我制作了self.isDestroy,在btn_msgbox_clicked方法中,将self.isDestroy改为True,将dialog_mouse_release方法连接到destroy方法。
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from PIL import Image,ImageTk,ImageDraw,ImageFont
import datetime
class CustomDialog(object):
def __init__(self):
self.title_bar_color = '#8FAADC'
self.item_ground_color = 'whitesmoke'
self.background_color = '#D9D9D9'
self.select_bar_color = '#BDD7EE'
self.isDrag_DlgMotion = False
self.drag_dx = 0
self.drag_dy = 0
def dialog_left_click(self,event):
dialog_x=self.dev_dialog.winfo_rootx()
dialog_y=self.dev_dialog.winfo_rooty()
point_x=self.dev_dialog.winfo_pointerx()
point_y=self.dev_dialog.winfo_pointery()
dx = point_x - dialog_x
dy = point_y - dialog_y
if (dx >= 0 and dx <= self.title_bar_width) and (dy >= 0 and dy <= self.title_bar_height):
self.drag_dx = dx
self.drag_dy = dy
self.isDrag_DlgMotion = True
return
def dialog_mouse_move_on(self,event):
if self.isDrag_DlgMotion:
X = event.x_root - self.drag_dx
Y = event.y_root - self.drag_dy
self.dev_dialog.geometry('+{0}+{1}'.format(X, Y))
pass
return
def dialog_mouse_release(self,event):
if self.isDrag_DlgMotion:
self.isDrag_DlgMotion = False
return
class CommonMessageBoxDialog(CustomDialog):
def __init__(self,title,message,state,parent = None):
self.return_state = None
self.isDestroy = False
if not isinstance(title,str) or not isinstance(message,str) or not isinstance(state,int):
return
if state < 1 or state > 3 :
return
root = ttk.tkinter.Tk()
#root = tk.Toplevel(parent)
#root.overrideredirect(True)
super().__init__()
self.box_state = state
self.box_message = message
self.box_title = title
W = 0
H = 1
self.dlg_size = [400,200]
self.title_bar_width = self.dlg_size[W]
self.title_bar_height = 40
self.btn_bar_height = 42
self.btn_32x32_size = 42
self.row_height = 28
self.btn_row_height = 32
self.frm_space = 10
self.parent = parent
self.CreateDialog(root)
root.wait_window(root)
#root.mainloop()
def CreateDialog(self,root):
W = 0
H = 1
if self.parent != None:
self.parent.update_idletasks()
ww=self.parent.winfo_screenwidth()
wh=self.parent.winfo_screenheight()
x=self.parent.winfo_rootx()
y=self.parent.winfo_rooty()
parent_w = self.parent.winfo_width()
parent_h = self.parent.winfo_height()
parent_x = self.parent.winfo_x()
parent_y = self.parent.winfo_y()
else:
root.update_idletasks()
ww=root.winfo_screenwidth()
wh=root.winfo_screenheight()
x=root.winfo_rootx()
y=root.winfo_rooty()
parent_w = root.winfo_width()
parent_h = root.winfo_height()
parent_x = root.winfo_x()
parent_y = root.winfo_y()
self.dev_dialog = root
dialog = self.dev_dialog
dialog.overrideredirect(True)
dlg_x = int((parent_x+parent_w) - (self.dlg_size[W]/2))
dlg_y = int((parent_y+parent_h) - (self.dlg_size[H]/2))
if dlg_x < 0 : dlg_x = 0
if dlg_y < 0 : dlg_y = 0
dialog.geometry('{}x{}+{}+{}'.format(self.dlg_size[W],self.dlg_size[H],dlg_x,dlg_y))
self.Title_Bar = tk.Frame(
dialog,
relief='flat',
bg = self.title_bar_color ,
)
self.Title_Label = tk.Label(
self.Title_Bar,
bg = self.title_bar_color ,
text = self.box_title,
)
dialog.bind('<Button-1>', self.dialog_left_click)
dialog.bind('<B1-Motion>', self.dialog_mouse_move_on)
dialog.bind('<ButtonRelease-1>',self.dialog_mouse_release)
self.MsgArea_frame = tk.Frame(
dialog,
relief='flat',
bg = self.select_bar_color,
)
self.message_frame = tk.Frame(
self.MsgArea_frame,
relief='flat',
bg = self.item_ground_color ,
)
self.label_message = tk.Label(
self.message_frame,
bg = self.item_ground_color ,
text = self.box_message,
)
self.BtnArea_frame = tk.Frame(
dialog,
relief='flat',
bg = self.item_ground_color,
)
self.btn_ok = tk.Button(
self.BtnArea_frame,
bg = self.item_ground_color,
text = 'OK',
command = lambda:self.btn_msgbox_clicked(1),
)
self.btn_yes = tk.Button(
self.BtnArea_frame,
bg = self.item_ground_color,
text = 'YES',
command = lambda:self.btn_msgbox_clicked(1),
)
self.btn_no = tk.Button(
self.BtnArea_frame,
bg = self.item_ground_color,
text = 'NO',
command = lambda:self.btn_msgbox_clicked(2),
)
self.btn_cancel = tk.Button(
self.BtnArea_frame,
bg = self.item_ground_color,
text = 'CANCEL',
command = lambda:self.btn_msgbox_clicked(3),
)
frm_space = self.frm_space
msg_frm_w = 4
btn_fram_h = 36
message_area_h = self.dlg_size[H] - self.title_bar_height - frm_space *2 - btn_fram_h
# Frame
self.Title_Bar.place(
x = 0, y = 0,
width = self.title_bar_width, height = self.title_bar_height
)
self.MsgArea_frame.place(
x = frm_space, y = self.title_bar_height + frm_space,
width = self.title_bar_width - frm_space*2, height = message_area_h
)
self.BtnArea_frame.place(
x = 0, y = self.title_bar_height + frm_space + message_area_h,
width = self.title_bar_width, height = btn_fram_h
)
self.Title_Label.grid(row = 0, column = 1,sticky = tk.W+tk.N+tk.S)
self.Title_Bar.columnconfigure(0,minsize = self.frm_space)
self.Title_Bar.rowconfigure(0,minsize = self.title_bar_height)
self.MsgArea_frame.columnconfigure(0,minsize = self.frm_space)
self.MsgArea_frame.rowconfigure(0,minsize = message_area_h)
self.BtnArea_frame.rowconfigure(0,minsize = btn_fram_h)
self.message_frame.place(
x = msg_frm_w, y = msg_frm_w,
width = self.title_bar_width - frm_space*2 - msg_frm_w*2, height = message_area_h - msg_frm_w*2,
)
# self.message_frame
self.label_message.grid(row = 0, column = 1,sticky = tk.W+tk.N+tk.S)
if self.box_state == 1:
self.btn_ok.place(
x = (self.title_bar_width/2) - 80/2 , y = btn_fram_h/2 - 24/2,
)
if self.box_state == 2:
self.btn_yes.place(
x = (self.title_bar_width/2) - (80 + frm_space) , y = btn_fram_h/2 - 24/2,
)
self.btn_no.place(
x = (self.title_bar_width/2) + frm_space , y = btn_fram_h/2 - 24/2,
)
if self.box_state == 3:
self.btn_yes.place(
x = (self.title_bar_width/2) - (80*1.5 + frm_space*2) , y = btn_fram_h/2 - 24/2,
)
self.btn_no.place(
x = (self.title_bar_width/2) - 80/2 , y = btn_fram_h/2 - 24/2,
)
self.btn_cancel.place(
x = (self.title_bar_width/2) + 80/2 + frm_space*2 , y = btn_fram_h/2 - 24/2,
)
#dialog.grab_set()
dialog.grab_set_global()
def btn_msgbox_clicked(self,state):
self.return_state = state
self.isDestroy = True
def get_return_state(self):
return self.return_state
def dialog_mouse_release(self,event):
if self.isDrag_DlgMotion:
self.isDrag_DlgMotion = False
if self.isDestroy:
self._quit()
return
def _quit(self):
self.dev_dialog.grab_release()
self.dev_dialog.destroy()
class CreateScreen(object):
def __init__(self):
self.cnt = 0
W = 0
H = 1
self.dlg_size = [400,200]
geo_string = '{}x{}'.format(self.dlg_size[W],self.dlg_size[H])
self.MainWindow_obj = ttk.tkinter.Tk()
self.MainWindow_obj.geometry(geo_string)
self.CntSting = tk.StringVar()
self.CntSting.set(str(self.cnt))
Label_Conter_text = tk.Label(
self.MainWindow_obj,
textvariable = self.CntSting,
)
self.MsgSting = tk.StringVar()
self.MsgSting.set('...')
Label_Message_text = tk.Label(
self.MainWindow_obj,
textvariable = self.MsgSting,
)
Btn_Messagebox = tk.Button(
self.MainWindow_obj,
text = 'Push',
command = self.Btn_Messagebox_clicked
)
Label_Conter_text.pack()
Label_Message_text.pack()
Btn_Messagebox.pack()
self.MainWindow_obj.after(1000,self.loop_msg)
self.MainWindow_obj.mainloop()
def Btn_Messagebox_clicked(self):
self.dlg = CommonMessageBoxDialog(title='Test',message='Do you remember ?',state=3,parent =self.MainWindow_obj)
ret = self.dlg.get_return_state()
if ret == 1:
self.MsgSting.set('Yes')
if ret == 2:
self.MsgSting.set('No')
if ret == 3:
self.MsgSting.set('Cancel')
return
def loop_msg(self):
self.cnt += 1
self.CntSting.set(str(self.cnt))
self.MainWindow_obj.after(1000,self.loop_msg)
if __name__ == '__main__':
screen_obj = CreateScreen()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.