选择不与上下文菜单一起使用的对象

时间:2015-10-11 09:10:02

标签: python python-2.7 python-3.x tkinter ttk

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

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

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

程序流程的小概述: - 通过MessageObject显示/表示消息。 - 它们存储在self.__messages - 对象内的名为InBox的列表中 - 通过左键单击消息对象来完成选择,使用右键单击打开上下文菜单。 - 选择在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()

1 个答案:

答案 0 :(得分:1)

我设法让它发挥作用。好像我没有正确地得到documentation

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

  

但是Tkinter还允许您在类和应用程序级别创建绑定;实际上,您可以在四个不同的级别上创建绑定:

     
      
  • 小部件实例,使用bind。

  •   
  • 小部件的顶层窗口(Toplevel或root),也使用bind。

  •   
  • widget类,使用bind_class(Tkinter使用它来提供标准绑定)。

  •   
  • 整个应用程序,使用bind_all。

  •   

我稍微修改了代码:

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

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