2017-04-11 48 views
4

我有一個菜單欄的圖形用戶界面。我希望能夠以編程方式打開這些菜單,就好像用戶點擊了它們一樣。如何在python tkinter中以編程方式打開菜單?

我的第一個猜測是invoke但沒有明顯的效果。 我知道我可以使用tk_popup打開菜單,但我無法弄清楚座標。 yposition函數的返回值看起來沒有幫助。 奇怪的是我甚至無法獲得菜單欄的寬度 - 它總是1.

我知道我可以一個菜單按鈕的按鍵事件綁定underline和我大概可以編程創建這樣的事件,但我真的止跌」不想這樣做。

import Tkinter as tk 

class MenuBar(tk.Menu): 
    def __init__(self, root): 
     tk.Menu.__init__(self, root) 
     self.root = root 
     self.menu_file = tk.Menu(m, tearoff=False) 
     self.menu_file.label = 'File' 
     self.menu_file.add_command(label='save') 
     self.menu_file.add_command(label='open') 

     self.menu_edit = tk.Menu(m, tearoff=False) 
     self.menu_edit.label = 'Edit' 
     self.menu_edit.add_command(label='add') 
     self.menu_edit.add_command(label='remove') 

     self.menus = (
      self.menu_file, 
      self.menu_edit, 
     ) 
     for menu in self.menus: 
      self.add_cascade(label=menu.label, menu=menu, underline=0) 

    def invoke(self, menu): 
     if menu in self.menus: 
      index = self.index(menu.label) 
     else: 
      index = menu 
     print("invoke({!r})".format(index)) 
     tk.Menu.invoke(self, index) 

    def open_menu(self, menu): 
     x = self.root.winfo_rootx() 
     y = self.root.winfo_rooty() 
     print("yposition: {}".format(self.yposition(self.index(menu.label)))) 
     print("mb.width : {}".format(self.winfo_width())) 
     print("mb.geometry: {}".format(self.winfo_geometry())) 
     print("tk_popup({x},{y})".format(x=x, y=y)) 
     menu.tk_popup(x,y) 
     pass 

m = tk.Tk() 
mb = MenuBar(m) 
m.config(menu=mb) 
m.update() 
m.bind('f', lambda e: mb.invoke(mb.menu_file)) 
m.bind('e', lambda e: mb.invoke(mb.menu_edit)) 
m.bind('<Control-f>', lambda e: mb.open_menu(mb.menu_file)) 
m.bind('<Control-e>', lambda e: mb.open_menu(mb.menu_edit)) 
m.mainloop() 

在此先感謝。

編輯: 我假設你,喬納森,指的是mb.menu_file.invoke(0)。如果我設置撕裂爲真,那是有效的,但是這不是我正在尋找的。因爲這會在某個地方的某個窗口中打開菜單(在我的情況下,在屏幕的左上角 - 遠離窗口),並且必須在窗口右上角的關閉按鈕上單擊鼠標右鍵才能關閉菜單。

即使可撕下=真mb.invoke(mb.menu_file)具有仍然沒有影響(打印以外「調用(1)」)。

我已經做postcascade一些研究,這聽起來完全像我所期待的。除此之外 - 正如你已經指出的那樣 - 它不起作用。 tcl documentation對此表示:「如果未張貼pathName,則該命令除了取消任何當前張貼的子菜單之外沒有任何作用」。事實上,如果我用mb.update(); mb.post(m.winfo_rootx(), m.winfo_rooty())代替m.config(menu=mb)它就行。 (我在這裏使用的是post而不是tk_popup,因爲在這種情況下,它應該保持打開。)它仍然不完美,因爲我無法用鍵盤控制子菜單;但是,無論如何,我想要一個菜單​​欄,而不是一個菜單。

你知道它有什麼缺點來「僞造」菜單嗎?我不考慮這樣做,因爲effbot指出「由於這個小部件儘可能使用本機代碼,因此不應該嘗試使用按鈕和其他Tkinter小部件來僞造菜單。」 (在另一方面這篇文章還建議與post打開菜單,這是我從this answer學會了不是最好的辦法 - tk_popup是更好的。)這種解決方案肯定會提供所需的靈活性。我目前看到的一個缺點是,當將鼠標光標移動到菜單未打開的下一個菜單時。但應該可以處理這個問題。我需要考慮更多細節嗎?

至於爲什麼:我希望用戶能夠完全自定義的快捷鍵。因此我需要一個可以綁定到用戶選擇的事件的函數。

另一個用例可能被實施的幫助功能,它不僅告訴用戶在哪裏可以找到一個命令,但打開正確的菜單,並選擇命令。這會讓用戶更快地找到它,而不是自己搜索正確的菜單。

回答

2

invoke命令相當於撕下。如果你允許撕下,你會看到這項工作。

您正在查找的命令是'postcascade'。沒有tkinter綁定到這個命令,如果你手動調用它(root.tk.eval(str(mb)+' postcascade 1')沒有任何反應,這可能是爲什麼沒有綁定。

我嘗試了許多其他的東西,但我無法得到這個工作。

但是,tk也有一個Menubutton小部件,它對<<Invoke>>事件作出響應。所以,(如果你真的很想要這個功能),你可以使自己的菜單欄:

import Tkinter as tk 
import ttk 

def log(command): 
    print 'running {} command'.format(command) 

class MenuBar(tk.Frame): 
    def __init__(self, master=None): 
     tk.Frame.__init__(self, master, bd=1, relief=tk.RAISED) 

     file_btn = tk.Menubutton(self, text='File') 
     menu_file = tk.Menu(file_btn, tearoff=False) 
     menu_file.add_command(label='save', command=lambda: log('save')) 
     menu_file.add_command(label='open', command=lambda: log('open')) 
     file_btn.config(menu=menu_file) 
     file_btn.pack(side=tk.LEFT) 
     master.bind('f', lambda e: file_btn.event_generate('<<Invoke>>')) 

     edit_btn = tk.Menubutton(self, text='Edit') 
     menu_edit = tk.Menu(edit_btn, tearoff=False) 
     menu_edit.add_command(label='add', command=lambda: log('add')) 
     menu_edit.add_command(label='remove', command=lambda: log('remove')) 
     edit_btn.config(menu=menu_edit) 
     edit_btn.pack(side=tk.LEFT) 
     master.bind('e', lambda e: edit_btn.event_generate('<<Invoke>>')) 

m = tk.Tk() 
m.geometry('300x300') 
mb = MenuBar(m) 
mb.pack(side=tk.TOP, fill=tk.X) 
m.mainloop() 

一旦菜單打開與熱鍵,就可以用箭頭鍵進行導航,所選擇的選項可以與運行輸入密鑰,或使用退出鍵關閉。

+0

謝謝。請看我編輯的問題。 – jakun

+0

我不認爲有可能做到完全一樣,這就是爲什麼我給你假的方式。 AFAIK唯一的缺點是它看起來更醜陋(因爲它不是OS繪製它),你必須自己處理所有東西。但它能做到你想要的,包括鍵盤控制。 – Novel

+0

對於不同的外觀,您可以使用'ttk.Menubutton'代替。 – Novel

相關問題