Friday, 8 April 2011

Hiding the MenuBar in a PyQt application.

I've long been a fan of the firefox plugin "tiny menu" where screen real estate is preserved by compressing the application's menubar down into a single menu button.

Here's one way to achieve the same effect in pyqt4.






 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#! /usr/bin/env python
# -*- coding: utf-8 -*-
'''
A simple application with a shrinkable menu bar.
(similar functionality to firefox4)

At any point either the tiny menu or menubar should be visible.
Therefore if the toolbar is hidden, the menu will re-appear.

to experience this... either 

    click View>Tiny Menu or hit F11
'''

from PyQt4 import QtGui, QtCore

class DockableMenuBar(QtGui.QMenuBar):
    def __init__(self, parent=None):
        QtGui.QMenuBar.__init__(self, parent)
        
        self.toggleViewAction = QtGui.QAction(_("Tiny &Menu"), parent)
        self.toggleViewAction.setShortcut('f11')
        self.toggleViewAction.setCheckable(True)
        self.toggleViewAction.triggered.connect(self.toggle_visability)
        
        self.menu_view = QtGui.QMenu(_("&View"), self)
        self.menu_view.addAction(self.toggleViewAction)
        self.addMenu(self.menu_view)
        
        self._menu_button = None
        
    @property
    def menu_button(self):
        self._menu_button = QtGui.QToolButton()
        self._menu_button.setPopupMode(QtGui.QToolButton.InstantPopup)
        self._menu_button.setText(_("Menu"))
        self._menu_button.setMenu(self.mini_menu)        
        self._menu_button.setToolButtonStyle(
            QtCore.Qt.ToolButtonFollowStyle)
        return self._menu_button

    @property
    def mini_menu(self):
        self._mini_menu = QtGui.QMenu()
        for action in self.actions():
            self._mini_menu.addAction(action)
        return self._mini_menu
        
    def addViewOption(self, action):
        '''
        add an action to the 'view' category of the toolbar
        ''' 
        self.menu_view.addAction(action)
        
    def addMenu(self, *args):
        try:
            return self.insertMenu(self.actions()[0], *args)
        except IndexError:
            return QtGui.QMenuBar.addMenu(self, *args)
            
    def addAction(self, *args):
        try:
            return self.insertAction(self.actions()[0], *args)
        except IndexError:
            return QtGui.QMenuBar.addAction(self, *args)        

    def toggle_visability(self, set_visible):
        self.setVisible(not set_visible)
        if set_visible:
            self.emit(QtCore.SIGNAL("mini menu required"), self.menu_button)
        else:
            self.emit(QtCore.SIGNAL("hide mini menu"))
            
    def setNotVisible(self, menu_bar_visible):
        '''
        make sure that we don't end up with neither menu visible!
        ''' 
        if not menu_bar_visible:
            self.setVisible(True)
        
class DockAwareToolBar(QtGui.QToolBar):
    def __init__(self, parent=None):
        QtGui.QToolBar.__init__(self, parent)
        self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
        self.setObjectName("DockAwareToolbar") #for QSettings
        
        # this should happen by default IMO.
        self.toggleViewAction().setText(_("&ToolBar"))

    def add_mini_menu(self, menu_button):
        self._menu_button = menu_button
        if self.actions():
            self.insertWidget(self.actions()[0], menu_button)
        else:
            self.addWidget(menu_button)
        self.show()
    
    def clear_mini_menu(self):
        self._menu_button.hide()
        self._menu_button.setParent(None)
        self._menu_button.deleteLater()
        self._menu_button = None
              
class TestMainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        
        ## initiate instances of our classes
        
        self.toolbar = DockAwareToolBar()        
        menu_bar = DockableMenuBar(self)
        
        ## the menu bar needs this action adding
        menu_bar.addViewOption(self.toolbar.toggleViewAction())
        
        ## add them to the app
        self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolbar)
        self.setMenuBar(menu_bar)
        
        ## make them aware of one another
        self.connect(self.menuBar(), QtCore.SIGNAL("mini menu required"),
            self.toolbar.add_mini_menu)
        self.connect(self.menuBar(), QtCore.SIGNAL("hide mini menu"),
            self.toolbar.clear_mini_menu)
        self.toolbar.toggleViewAction().triggered.connect(
            self.menuBar().setNotVisible)
        
        ## some arbitrary stuff to make the app more realistic
        file_action = QtGui.QAction("&File", self)
        self.menuBar().addAction(file_action)
        
        line_edit = QtGui.QLineEdit("http://google.com")
        self.toolbar.addWidget(line_edit)
        
        go_but = QtGui.QPushButton("Go!")
        go_but.setFixedWidth(60)
        self.toolbar.addWidget(go_but)
        
        te = QtGui.QTextEdit()
        self.setCentralWidget(te)
        te.setText(__doc__)
        

if __name__ == "__main__":
    import gettext
    gettext.install("")
    
    app = QtGui.QApplication([])
    mw = TestMainWindow()
    mw.show()
    app.exec_()




No comments: