2017-04-11 15 views
4

私はメニューバーを備えたグラフィカルユーザーインターフェイスを持っています。私は、ユーザーがそれらをクリックしたかのようにこれらのメニューをプログラムで開くことができるようにしたいと思います。python tkinterでメニューをプログラムで開く方法は?

私の最初の推測はinvokeでしたが、目に見える効果はありません。 tk_popupを使用してメニューを開くことができますが、私は座標を把握できません。 yposition関数の戻り値が役に立ちません。 は不思議私もメニューバーの幅を取得することはできません - それは常に私がunderlineでキーイベントへのメニューボタンをバインドすることができます知っている1.

だと私はおそらくプログラム的なイベントを作成することができることが、私は本当にwouldn」それをやりたい

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)を指していると仮定しています。それは私がtearoffをTrueに設定しても機能しますが、それは私が探しているものではありません。これは別のウィンドウのメニューを開きます(私の場合、画面の左上隅にあります - ウィンドウから離れています)、ウィンドウの右上にある閉じるボタンを明示的にクリックして閉じる必要があります。

tearoff = Trueの場合でも、mb.invoke(mb.menu_file)は効果がありません( "invoke(1)"の印刷を除く)。

私はpostcascadeについていくつかの調査を行いましたが、私が探しているものとまったく同じように聞こえます。あなたがすでに指摘しているように、それは機能しません。 tcl documentationは、「pathNameがポストされていないと、現在ポストされているサブメニューのポストを取り消す以外は何の効果もありません」と言います。そして実際にm.config(menu=mb)mb.update(); mb.post(m.winfo_rootx(), m.winfo_rooty())に置き換えればうまくいきます。 (この場合はtk_popupの代わりにpostを使用しています。この場合は開いたままにしておく必要があります)キーボードでサブメニューを制御できないため、まだ完全ではありません。しかし、とにかく、私はポップアップメニューではなく、メニューバーが必要です。

メニューを「偽造する」にはどのような欠点があるのでしょうか? effbotは「このウィジェットは可能な限りネイティブコードを使用しているので、ボタンや他のTkinterウィジェットを使ってメニューを偽造しないでください」と述べているので、私はそのことを考慮しませんでした。 (他方では、この記事ではpostというメニューを開くことを提案しています。これはthis answerから学んだことがあります。tk_popupが良い方法です。)その解決策は確かに望ましい柔軟性を提供します。私が現在目にしている欠点の1つは、マウスカーソルを次のメニューに移動してもメニューが開かないということです。しかし、それを処理することが可能でなければなりません。私が考慮する必要のある詳細はありますか?

理由:私は、ユーザーがキーボードショートカットを完全にカスタマイズできるようにしたいと思います。したがって、ユーザーが選択したイベントにバインドできる関数が必要です。

別の用途では、コマンドを見つける場所をユーザーに通知するだけでなく、正しいメニューを開いてそのコマンドを選択するヘルプ機能を実装している可能性があります。ユーザーが適切なメニューを探すよりも、それを見つけるのがずっと速くなります。

答えて

2

invokeコマンドはtearoffと同等です。あなたがテアロフを許可した場合、あなたはその仕事を見るでしょう。

探しているコマンドは「ポストカスケード」です。このコマンドにはtkinterバインディングはありません。手動で呼び出すと(root.tk.eval(str(mb)+' postcascade 1')何も起こりません。おそらくバインディングが存在しません。

私は他の多くのことを試みましたが、これを動作させることができませんでした。

ただし、tkがあまりにもメニューボタンウィジェットを持っており、それが<<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() 

メニューはホットキーで開かれると、それは矢印キーでナビゲートすることができ、選択されたオプションがして実行することができますEnterキーを押すか、エスケープキーで閉じます。

+0

ありがとうございます。私の編集した質問を見てください。 – jakun

+0

私はあなたのことをまったくやり遂げることはできないと思うので、私はあなたに偽の道を与えました。 AFAIKの唯一の欠点は、それが醜いと思うということです(なぜなら、それはOSがそれを描くのではなく、あなたのものであるからです)。しかし、それはキーボードのコントロールを含め、あなたが望むことをします。 – Novel

+0

別の見方をすれば、代わりに 'ttk.Menubutton'を使うことができます。 – Novel

関連する問題