DBus menu: Implement visibility and automatically hide the menu when the window is blocked by a modal dialog

This commit is contained in:
Kovid Goyal 2014-10-30 14:01:43 +05:30
parent 75a8c36305
commit ce69866801
3 changed files with 64 additions and 4 deletions

View File

@ -10,7 +10,7 @@ import time
from PyQt5.Qt import ( from PyQt5.Qt import (
QApplication, QMainWindow, QVBoxLayout, Qt, QKeySequence, QAction, QApplication, QMainWindow, QVBoxLayout, Qt, QKeySequence, QAction,
QActionGroup, QMenu, QPushButton, QWidget, QTimer) QActionGroup, QMenu, QPushButton, QWidget, QTimer, QMessageBox, pyqtSignal)
from calibre.gui2.dbus_export.utils import setup_for_cli_run from calibre.gui2.dbus_export.utils import setup_for_cli_run
from calibre.gui2.dbus_export.widgets import factory from calibre.gui2.dbus_export.widgets import factory
@ -22,6 +22,9 @@ def make_checkable(ac, checked=True):
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
window_blocked = pyqtSignal()
window_unblocked = pyqtSignal()
def __init__(self): def __init__(self):
QMainWindow.__init__(self) QMainWindow.__init__(self)
f = factory() f = factory()
@ -84,6 +87,8 @@ class MainWindow(QMainWindow):
b.clicked.connect(self.add_menu), l.addWidget(b) b.clicked.connect(self.add_menu), l.addWidget(b)
self.rb = b = QPushButton('Remove a created menu') self.rb = b = QPushButton('Remove a created menu')
b.clicked.connect(self.remove_menu), l.addWidget(b) b.clicked.connect(self.remove_menu), l.addWidget(b)
self.sd = b = QPushButton('Show modal dialog')
b.clicked.connect(self.show_dialog), l.addWidget(b)
print ('DBUS connection unique name:', f.bus.get_unique_name()) print ('DBUS connection unique name:', f.bus.get_unique_name())
def update_tooltip(self): def update_tooltip(self):
@ -143,6 +148,17 @@ class MainWindow(QMainWindow):
def about_to_show_two(self): def about_to_show_two(self):
self.menu_two.addAction('Action added by about to show') self.menu_two.addAction('Action added by about to show')
def show_dialog(self):
QMessageBox.information(self, 'A test dialog', 'While this dialog is shown, the global menu should be hidden')
def event(self, ev):
if ev.type() in (ev.WindowBlocked, ev.WindowUnblocked):
if ev.type() == ev.WindowBlocked:
self.window_blocked.emit()
else:
self.window_unblocked.emit()
return QMainWindow.event(self, ev)
app=QApplication([]) app=QApplication([])
app.setAttribute(Qt.AA_DontUseNativeMenuBar, False) app.setAttribute(Qt.AA_DontUseNativeMenuBar, False)
app.setApplicationName('com.calibre-ebook.DBusExportDemo') app.setApplicationName('com.calibre-ebook.DBusExportDemo')

View File

@ -34,7 +34,7 @@ def create_properties_for_action(ac, previous=None):
ans['label'] = swap_mnemonic_char(text) ans['label'] = swap_mnemonic_char(text)
if not ac.isEnabled(): if not ac.isEnabled():
ans['enabled'] = False ans['enabled'] = False
if not ac.isVisible(): if not ac.isVisible() or ac.property('blocked') is True:
ans['visible'] = False ans['visible'] = False
if ac.menu() is not None: if ac.menu() is not None:
ans['children-display'] = 'submenu' ans['children-display'] = 'submenu'
@ -124,6 +124,22 @@ class DBusMenu(QObject):
self.add_action(ac) self.add_action(ac)
self.dbus_api.LayoutUpdated(self.dbus_api.revision, 0) self.dbus_api.LayoutUpdated(self.dbus_api.revision, 0)
def set_visible(self, visible):
ac = self.id_to_action(0)
if ac is not None and self.qmenu is not None:
changed = False
blocked = not visible
for ac in ac.menu().actions():
ac_id = self.action_to_id(ac)
if ac_id is not None:
old = ac.property('blocked')
if old is not blocked:
ac.setProperty('blocked', blocked)
self.action_changes.add(ac_id)
changed = True
if changed:
self.action_changed_timer.start()
def add_action(self, ac): def add_action(self, ac):
ac_id = 0 if ac.menu() is self.qmenu else self.next_id ac_id = 0 if ac.menu() is self.qmenu else self.next_id
self._id_to_action[ac_id] = ac self._id_to_action[ac_id] = ac

View File

@ -37,6 +37,8 @@ class ExportedMenuBar(QMenuBar):
global menu_counter global menu_counter
if not parent.isWindow(): if not parent.isWindow():
raise ValueError('You must supply a top level window widget as the parent for an exported menu bar') raise ValueError('You must supply a top level window widget as the parent for an exported menu bar')
self._blocked = False
self.is_visible = True
QMenuBar.__init__(self, parent) QMenuBar.__init__(self, parent)
QMenuBar.setVisible(self, False) QMenuBar.setVisible(self, False)
self.menu_action = MenuBarAction(self) self.menu_action = MenuBarAction(self)
@ -51,6 +53,10 @@ class ExportedMenuBar(QMenuBar):
self.dbus_menu.publish_new_menu(self) self.dbus_menu.publish_new_menu(self)
self.register() self.register()
parent.installEventFilter(self) parent.installEventFilter(self)
# See https://bugreports.qt-project.org/browse/QTBUG-42281
if hasattr(parent, 'window_blocked'):
parent.window_blocked.connect(self._block)
parent.window_unblocked.connect(self._unblock)
def register(self): def register(self):
wid = self.parent().effectiveWinId() wid = self.parent().effectiveWinId()
@ -66,16 +72,38 @@ class ExportedMenuBar(QMenuBar):
self.bus.call_blocking(*args) self.bus.call_blocking(*args)
def setVisible(self, visible): def setVisible(self, visible):
pass # no-op self.is_visible = visible
self.dbus_menu.set_visible(self.is_visible and not self._blocked)
def isVisible(self): def isVisible(self):
return True return self.is_visible
def show(self):
self.setVisible(True)
def hide(self):
self.setVisible(False)
def menuAction(self): def menuAction(self):
return self.menu_action return self.menu_action
def _block(self):
self._blocked = True
self.setVisible(self.is_visible)
def _unblock(self):
self._blocked = False
self.setVisible(self.is_visible)
def eventFilter(self, obj, ev): def eventFilter(self, obj, ev):
etype = ev.type() etype = ev.type()
# WindowBlocked and WindowUnblocked aren't delivered to event filters,
# so we have to rely on co-operation from the mainwindow class
# See https://bugreports.qt-project.org/browse/QTBUG-42281
# if etype == QEvent.WindowBlocked:
# self._block()
# elif etype == QEvent.WindowUnblocked:
# self._unblock()
if etype == QEvent.WinIdChange: if etype == QEvent.WinIdChange:
self.unregister() self.unregister()
self.register() self.register()