选择对象不适用于上下文菜单

Selection of Object not working with context Menu

我创建了一个像电子邮件客户端一样显示消息的小 MessageBox。 就像在电子邮件客户端中一样,用户应该能够使用上下文菜单将消息标记为已读或未读。建立一个不是问题。根据选择显示消息 window 中的消息。

但是当我使用上下文菜单将消息标记为已读或未读时,它总是突出显示最后一条消息。

到目前为止我找不到解决方案。无法在 ddg 或 SO 上找到并回答或提示。

程序流程的小概述: - 消息通过 MessageObjects 显示/表示。 - 它们存储在 InBox-Object 中名为 self.__messages 的列表中 - 选择是通过左键单击消息对象完成的,上下文菜单使用右键单击打开。 - 选择是在 InBox 对象内完成的,以便只允许单选 - 上下文菜单已注册并且 运行 MessageObject.

不幸的是,我无法进一步剥离下面的示例以获得仍在工作的代码。

非常欢迎任何帮助、提示或想法,因为我无法弄清楚错误的来源。

    from sys import hexversion, modules

    if hexversion>0x03000000:
        import Tkinter
        modules['Tkinter'] = tkinter
        modules['ttk']     = tkinter.ttk

    import Tkinter as tk
    import ttk

    __DEBUG__=2

    class MessageObject(ttk.Frame):
        def __init__(self, master=None, **options):
            ttk.Frame.__init__(self, master, **options)
            self.pack(expand=True, fill=tk.BOTH)

            self.master=master
            self.takefocus=True

            self.read=tk.BooleanVar()
            self.sender=tk.StringVar()
            self.date=tk.StringVar()
            self.text=tk.StringVar()
            self.subject=tk.StringVar()
            self.mID=tk.IntVar()

            self.__loadUI()

        def __loadUI(self, event=None):
            self.grid_columnconfigure(0, weight=1)
            self.grid_columnconfigure(0, weight=1)

            self.Labels=[]

            self.Labels.append(ttk.Label(self, text="Subject:", justify=tk.LEFT))
            self.Labels.append(ttk.Label(self, text="Sender:", justify=tk.LEFT))

            self.Labels.append(ttk.Label(self, textvariable=self.subject, justify=tk.RIGHT))
            self.Labels.append(ttk.Label(self, textvariable=self.sender, justify=tk.RIGHT))

            self.Labels[0].grid(row=0, column=0, sticky=tk.N+tk.SW)
            self.Labels[1].grid(row=1, column=0, sticky=tk.N+tk.SW)

            self.Labels[2].grid(row=0, column=1, sticky=tk.N+tk.SE)
            self.Labels[3].grid(row=1, column=1, sticky=tk.N+tk.SE)

            #Context Menu
            self.__cMenu=tk.Menu(self, tearoff=0)
            self.__cMenu.add_command(label="Mark as read", command=lambda: self.Read(True))
            self.__cMenu.add_command(label="Mark as unread", command=lambda: self.Read(False))

            self.bind_all("", self.__showContextMenu)

        def __showContextMenu(self, event=None):
            self.__cMenu.post(event.x_root, event.y_root)

        def Read(self, value=False):
            self.read.set(value)
            if value:
                self.config(style='Read.TFrame')
                for Label in self.Labels:
                    Label.config(style='Read.TLabel')
            else:
                self.config(style='Unread.TFrame')
                for Label in self.Labels:
                    Label.config(style='Unread.TLabel')

        def Selected(self, value=False):
            #TODO: keep Font, only change for- and background
            if value:
                self.config(style='Selected.TFrame')
                for Label in self.Labels:
                    Label.config(style='Selected.TLabel')
            else:
                for Label in self.Labels:
                    if self.read.get():
                        Label.config(style='Read.TLabel')
                        self.config(style='Read.TFrame')
                    else:
                        Label.config(style='Unread.TLabel')
                        self.config(style='Unread.TFrame')

    class InBox(ttk.Frame):
        def __init__(self, master=None, **options):
            ttk.Frame.__init__(self, master, **options)
            self.pack(expand=True, fill=tk.BOTH)

            self.master=master

            self.grid_columnconfigure(0, weight=2)
            self.grid_columnconfigure(1, weight=0)
            self.grid_columnconfigure(2, weight=0)
            self.grid_columnconfigure(3, weight=0)

            self.grid_rowconfigure(0, weight=0)
            self.grid_rowconfigure(1, weight=1)
            self.grid_rowconfigure(2, weight=1)
            self.grid_rowconfigure(3, weight=1)

            self.__text=tk.StringVar()
            self.__currentSubject=tk.StringVar()
            self.__currentTimestamp=tk.StringVar()

            self.__itemCount=0
            self.__messages=[]

            self.__loadUI()

        def __loadUI(self):
            ttk.Label(self, text="Inbox:", anchor=tk.W).grid(row=0, column=0, sticky=tk.NW+tk.SE)
            ttk.Label(self, text="Selected Message:", anchor=tk.W).grid(row=0, column=1, sticky=tk.NW+tk.SE)

            ttk.Label(self, textvariable=self.__currentSubject).grid(row=0, column=2, sticky=tk.NW)
            ttk.Label(self, textvariable=self.__currentTimestamp).grid(row=0, column=3, sticky=tk.NW)

            self.InBoxList=ttk.Frame(self)
            self.InBoxList.grid(row=1, rowspan=3, column=0, sticky=tk.NW+tk.SE)

            self.TextDisplay=tk.Text(self)
            self.TextDisplay.grid(row=1, rowspan=3, column=1, columnspan=3, sticky=tk.NW+tk.SE)

            self.__messages=[]

        def insert(self,index=0, mID=0, sender=None, subject=None, message=None, date=None, Read=False):
            if sender!=None and subject!=None and  message!=None:
                self.InBoxList.grid_rowconfigure(self.__itemCount, weight=0)

                self.__messages.append(MessageObject(self.InBoxList))
                self.__messages[-1].mID.set(mID)
                self.__messages[-1].text.set(message)
                self.__messages[-1].sender.set(sender)
                self.__messages[-1].subject.set(subject)
                self.__messages[-1].date.set(date)
                self.__messages[-1].Read(Read)
                self.__messages[-1].grid(row=self.__itemCount, column=0, sticky=tk.NE+tk.SW)
                self.__messages[-1].bind_all("", self.__selectionChanged)

                self.__itemCount=self.__itemCount+1
                return 0
            else:
                return 1

        def Messages(self):
            return self.__messages

        def __selectionChanged(self, event):
            try:
                item=event.widget

                #Empty Message Display
                self.TextDisplay.delete(1.0, tk.END)
                self.__currentSubject.set("")
                self.__currentTimestamp.set("")

                #Deactivate all but the selected one (the one calling)
                for i in range(0,len(self.__messages)):
                    print("EventItem: %s"%item)
                    print("Message:   %s"%self.__messages[i])
                    found=False

                    if "%s"%self.__messages[i] in "%s"%item :
                        self.__messages[i].Selected(True)

                        #Refresh Message Display
                        self.TextDisplay.insert(tk.END, self.__messages[i].text.get())
                        self.__currentSubject.set(self.__messages[i].subject.get())
                        self.__currentTimestamp.set(self.__messages[i].date.get())
                    else:
                        self.__messages[i].Selected(False)

            except Exception as ex:
                #Not in clickable Area?
                if __DEBUG__>1: print("Got click but cannot link it to item - %s"%ex)
                pass

        def delete(self, index=0, messageObject=None):
            self.__logger.debug("")
            if messageObject!=None:
                for i in range(len(self.__itemCount)):
                    if self.__messages[i]==messageObject:
                        self.__messages[i].destroy()
            else:
                self.__messages[index].destroy()
            self.__itemCount=self.__itemCount-1

    if __name__=="__main__":
       # Styling
        __msg_style=ttk.Style()

        __msg_style.configure('Read.TFrame', background='#ffffff')
        __msg_style.configure('Read.TFrame', foreground='#000000')
        __msg_style.configure('Read.TFrame', font='Segue\ UI 8 normal')

        __msg_style.configure('Unread.TFrame', background='#fafafa')
        __msg_style.configure('Unread.TFrame', foreground='#0000ff')
        __msg_style.configure('Unread.TFrame', font='Segue\ UI 8 bold')

        __msg_style.configure('Selected.TFrame', background='#8888ff')
        __msg_style.configure('Selected.TFrame', foreground='#ffffff')
       #__msg_style.configure('Selected.TFrame', font='Segue\ UI 8 italic')

        __msg_style.configure('Read.TLabel', background='#ffffff')
        __msg_style.configure('Read.TLabel', foreground='#000000')
        __msg_style.configure('Read.TLabel', font='Segue\ UI 8 normal')

        __msg_style.configure('Unread.TLabel', background='#fafafa')
        __msg_style.configure('Unread.TLabel', foreground='#0000ff')
        __msg_style.configure('Unread.TLabel', font='Segue\ UI 8 bold')

        __msg_style.configure('Selected.TLabel', background='#8888ff')
        __msg_style.configure('Selected.TLabel', foreground='#ffffff')
       #__msg_style.configure('Selected.TLabel', font='Segue\ UI 8 italic')

        app=InBox()
        app.insert(sender="Hans Dampf", subject="Read Subject", message="Lorem ipsum Read", date="14/01/2013", Read=True)
        app.insert(sender="Test Name", subject="UnRead Subject", message="Lorem ipsum UnRead", date="14/01/2016", Read=False)

        app.bind_all("", app.destroy)
        app.mainloop()

我设法让它工作了。看来我没有得到正确的documentation

引发事件的小部件不是小部件MessageObject实例,而是使用了整个应用程序bind_all

But Tkinter also allows you to create bindings on the class and application level; in fact, you can create bindings on four different levels:

  • the widget instance, using bind.

  • the widget’s toplevel window (Toplevel or root), also using bind.

  • the widget class, using bind_class (this is used by Tkinter to provide standard bindings).

  • the whole application, using bind_all.

我将代码稍微调整为:

self.bind("<Button-3>", self.__showContextMenu)
for label in self.Labels:
    label.bind("<Button-3>", self.__showContextMenu)

现在小部件的实例调用处理程序,一切正常。