2010-01-12 385 views
9

我正在使用QMenu作爲上下文菜單。這個菜單充滿了QActions。其中一個QActions是可檢查的,我希望能夠在不關閉上下文菜單的情況下選中/取消選中它(並且必須重新打開它才能選擇我想要的選項)。防止QMenu在其QAction觸發時關閉

我試着斷開可檢測QAction發出的信號,但沒有運氣。

任何想法?謝謝。

回答

13

將QWidgetAction和QCheckBox用於不會導致菜單關閉的「可檢查操作」。

QCheckBox *checkBox = new QCheckBox(menu); 
QWidgetAction *checkableAction = new QWidgetAction(menu); 
checkableAction->setDefaultWidget(checkBox); 
menu->addAction(checkableAction); 

在某些樣式中,這不會與可檢查操作完全相同。例如,對於Plastique風格,複選框需要縮進一點。

+0

非常感謝。 隨着plastique風格確實有一個餘地增加。所以我把複選框放在一個帶有佈局的小部件中,並設置邊距(也許有一個更簡單的方法...) 最後一件事:複選框沒有擴展到菜單的全部寬度,所以如果點擊發生在盒子標籤結束後菜單被關閉並且盒子未被檢查。設置尺寸策略不起作用。 – gregseth 2010-01-13 10:02:47

+0

這不適用於Ubuntu Unity上的'QsystemTrayIcon.contextMenu()',因爲Unity不會在'QWidgetAction'中顯示小部件 – Germar 2016-03-24 02:11:56

+0

@gregseth有沒有辦法將複選框擴展到菜單的全部寬度? – bob 2017-07-17 12:39:31

1

這裏有幾個想法我已經......在所有他們將工作壽不知道;)

1)嘗試使用QMenu的方法aboutToHide()來捕捉事件;也許你可以「取消」隱藏進程?

2)也許你可以考慮使用EventFilter?

嘗試看看:http://qt.nokia.com/doc/4.6/qobject.html#installEventFilter

3)否則,你可以重新實現QMenu添加自己的行爲,但似乎很多工作給我的......

希望這有助於有點!

0

(我開始與安迪的答案,所以謝謝安迪!)

1)aboutToHide()的作品,通過重新彈出的菜單中緩存的位置,但它也可以進入無限循環。測試在菜單之外點擊鼠標以忽略重新打開應該能夠實現。

2)我嘗試了一個事件過濾器,但它阻止了實際點擊菜單項。

3)同時使用。

這是一個骯髒的模式來證明它的工作原理。這使菜單打開,當用戶點擊時按住CTRL:

# in __init__ ... 
    self.options_button.installEventFilter(self) 
    self.options_menu.installEventFilter(self) 
    self.options_menu.aboutToHide.connect(self.onAboutToHideOptionsMenu) 

    self.__options_menu_pos_cache = None 
    self.__options_menu_open = False 

def onAboutToHideOptionsMenu(self): 
    if self.__options_menu_open:   # Option + avoid an infinite loop 
     self.__options_menu_open = False # Turn it off to "reset" 
     self.options_menu.popup(self.__options_menu_pos_cache) 

def eventFilter(self, obj, event): 
    if event.type() == QtCore.QEvent.MouseButtonRelease: 
     if obj is self.options_menu: 
      if event.modifiers() == QtCore.Qt.ControlModifier: 
       self.__options_menu_open = True 

      return False 

     self.__options_menu_pos_cache = event.globalPos() 
     self.options_menu.popup(event.globalPos()) 
     return True 

    return False 

我說這是骯髒的,因爲這裏的小部件充當兩個打開的菜單以及菜單本身按鈕事件過濾器。使用顯式的事件過濾器類將很容易添加,它會使事情更容易遵循。

布爾可能可以替換爲檢查鼠標是否在菜單上,如果沒有,請不要彈出它打開。然而,CTRL鍵仍然需要考慮到我的用例,所以它可能不是一個很好的解決方案。

當用戶按住CTRL鍵並單擊菜單時,它會翻轉一個開關,使菜單在嘗試關閉時打開自身備份。該位置被緩存,因此它在同一位置打開。有一個快速閃爍,但它感覺很好,因爲用戶知道他們正在按下一個鍵來完成這項工作。

在一天結束時(字面上),我已經有整個菜單做正確的事情。我只是想添加這個功能,而且我絕對不想改用這個小部件。出於這個原因,我現在保持這個骯髒的補丁。

1

這是我的解決方案:

// this menu don't hide, if action in actions_with_showed_menu is chosen. 
    class showed_menu : public QMenu 
    { 
     Q_OBJECT 
    public: 
     showed_menu (QWidget *parent = 0) : QMenu (parent) { is_ignore_hide = false; } 
     showed_menu (const QString &title, QWidget *parent = 0) : QMenu (title, parent) { is_ignore_hide = false; } 
     void add_action_with_showed_menu (const QAction *action) { actions_with_showed_menu.insert (action); } 

     virtual void setVisible (bool visible) 
     { 
     if (is_ignore_hide) 
      { 
      is_ignore_hide = false; 
      return; 
      } 
     QMenu::setVisible (visible); 
     } 

     virtual void mouseReleaseEvent (QMouseEvent *e) 
     { 
     const QAction *action = actionAt (e->pos()); 
     if (action) 
      if (actions_with_showed_menu.contains (action)) 
      is_ignore_hide = true; 
     QMenu::mouseReleaseEvent (e); 
     } 
    private: 
     // clicking on this actions don't close menu 
     QSet <const QAction *> actions_with_showed_menu; 
     bool is_ignore_hide; 
    }; 

    showed_menu *menu = new showed_menu(); 
    QAction *action = showed_menu->addAction (new QAction (menu)); 
    menu->add_action_with_showed_menu (action); 
7

似乎有不被阻止菜單,關閉任何優雅的方式。但是,如果該操作可以實際觸發,則該菜單纔會關閉,即該菜單已啓用。所以,我發現的最優雅的解決方案是通過在觸發時立即禁用操作來欺騙菜單。

  1. 子類QMenu
  2. 重新實現相關的事件處理程序(如mouseReleaseEvent())
  3. 在事件處理程序,禁用動作,然後調用基類的實現,然後重新啓動操作,並手動觸發它

這是重新實現mouseReleaseEvent()的一個例子:

void mouseReleaseEvent(QMouseEvent *e) 
{ 
    QAction *action = activeAction(); 
    if (action && action->isEnabled()) { 
     action->setEnabled(false); 
     QMenu::mouseReleaseEvent(e); 
     action->setEnabled(true); 
     action->trigger(); 
    } 
    else 
     QMenu::mouseReleaseEvent(e); 
} 

爲了使解決方案完美,類似應該在可能觸發該操作的所有事件處理程序中完成,例如keyPressEvent()等...

問題是,知道重新實現是否應該實際上並不總是很容易觸發該動作,或者甚至應該觸發哪個動作。最困難的可能是由助記符觸發的操作:您需要自己重新實現QMenu :: keyPressEvent()中的複雜算法。

+0

這與我剛剛提出的解決方案完全相同,並且據我所知,它運行良好。猜猜我應該在自己試驗之前閱讀所有答案。 – mooware 2015-04-09 00:56:21

+2

您可以不調用'QMenu :: mouseReleaseEvent'來代替禁用和啓用該操作。在這種情況下,它就像一種魅力。重寫'keyPressEvent'並添加空間按鈕的行爲也很好。 – bcmpinc 2015-04-11 16:52:28