Home > PyQt5, python > PyQt5: QMenuBar, QMenu

PyQt5: QMenuBar, QMenu

28 Maggio 2019

Torna all’indice degli appunti

QMenuBar

La classe QMenuBar crea una barra orizzontale dove è possibile sistemare elementi detti pull-down Menu.
Il costruttore della menubar è QMenuBar:

>>> from PyQt5.QtWidgets import QApplication, QMenuBar
>>> app = QApplication([])
>>> menubar = QMenuBar()

Per aggiungere i menu sulla menubar appena creata, si utilizza il metodo addMenu(menu), dove menu è un oggetto QMenu, ovvero un widget che contiene una lista di elementi detti actions.

Vediamo nello specifico l’oggetto QMenu.

QMENU

Questa classe QMenu crea il widget che viene comunemente utilizzato nelle menubar, menu contestuali (es. bottone destro del mouse) e popup menu.
Come già anticipato, un oggetto QMenu, consiste in una lista di elementi “Action”.
Un elemento Action può avere una label, una icona ed una shortcut (scorciatoia da tastiera).
Il costruttore di un oggetto QMenu è appunto QMenu(text), dove text una stringa che rappresenta il nome del menu.
Per settare un’icona di menu, si utilizza il metodo setIcon(icon), dove icon è ovviamente un oggetto QIcon.
Aggiungiamo quindi alla menubar un menu “File” con tanto di Icona:

>>> from PyQt5.QtWidgets import QMenu
>>> from PyQt5.QtGui import QIcon
>>> menufile = QMenu("File")
>>> menufile.setIcon(QIcon("file.png"))
>>> menubar.addMenu(menufile)
<PyQt5.QtWidgets.QAction object at 0x0373D3A0>

Il metodo addMenu, ritorna l’oggetto QAction relativo al menu appena inserito.
L’oggetto qaction contiene tutte le informazioni relative al menu e può contenere appunto una icona, un testo, un tip.
E’ possibile ottenere le actions collegate ad una menubar, con il metodo actions()

>>> menubar.actions()
[<PyQt5.QtWidgets.QAction object at 0x0373D210>]
>>> fileaction = menubar.actions()[0]
>>> fileaction.icon()
<PyQt5.QtGui.QIcon object at 0x0373D3F0>
>>> fileaction.text()
'File' 

Ora che abbiamo creato un menu, dobbiamo creare le voci di menu.
Per fare questo, utilizziamo il metodo addAction(text), dove text è il nome della voce di menu.
E’ possibile anche inserire un’icona in fase di aggiunta della action, passandola al metodo precedente come primo parametro, ma è anche possibile farlo successivamente, utilizzando il metodo setIcon(icon).
In realtà, dal momento che l’utilizzo del metodo addAction(text), ritorna l’oggetto QAction, è possibile aggiungere tutti gli elementi in seguito, con i metodi:
setIcon(icon), setToolTip(tooltip):

>>> newtxt_action = menufile.addAction("New &Txt file")
>>> newtxt_action
<PyQt5.QtWidgets.QAction object at 0x0373D530>
>>> newtxt_action.setIcon(QIcon("txt.png"))
>>> menufile.addAction(QIcon("pdf.png"), "New &Pdf file")
<PyQt5.QtWidgets.QAction object at 0x0373D5D0>
>>> menufile.addAction(QIcon("doc.png"), "New &Doc file")
<PyQt5.QtWidgets.QAction object at 0x0373D670>
>>> menufile.actions()
[<PyQt5.QtWidgets.QAction object at 0x0373D530>, <PyQt5.QtWidgets.QAction object at 0x0373D5D0>, <PyQt5.QtWidgets.QAction object at 0x0373D670>]
>>> for action in menufile.actions():
...     print(action.text(), action.icon().isNull())
...     
New &Txt file False
New &Pdf file False
New &Doc file False

SEGNALI

I due segnali da tenere in considerazione sono

hovered: segnale emesso quando la voce di menu viene evidenziata al passaggio del mouse;
triggered: segnale emesso quando clicchiamo sulla voce di menu;

Entrambi i segnali rendono disponibile action, ovvero l’oggetto QAction che ha causato l’emissione del segnale.

Vediamo un esempio pratico:

from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QApplication, QMenuBar, QMenu, QAction, QWidget
from PyQt5.QtGui import QIcon


import sys


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowTitle("QMenu Example")
        menubar = QMenuBar(parent=self)
        menufile = QMenu("File", parent=menubar)
        for text, ext, shortcut in [("New &Txt file", "txt", "CTRL+T"),
                                     ("New &Pdf file", "pdf", "CTRL+P"),
                                     ("New &Doc file", "doc", "CTRL+D")]:
            action = QAction(text, parent=menufile)
            action.setShortcut(shortcut)
            action.setToolTip("Create new %s file" % ext)
            action.setIcon(QIcon("%s.png" % ext))
            menufile.addAction(action)
        menufile.setToolTipsVisible(True)
        menubar.addMenu(menufile)
        self.setMenuBar(menubar)
        self.resize(250, 300)
        self.central_widget = FormWidget(self) 
        self.setCentralWidget(self.central_widget) 

        menufile.triggered.connect(self.on_menu)

    def on_menu(self, action):
        print("[SIG] %s menu clicked!" % action.text())


class FormWidget(QWidget):
    def __init__(self, parent):
        super(FormWidget, self).__init__(parent)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow(parent=None)
    main_window.show()
    sys.exit(app.exec_())

Nota:

Per far sì che i tooltips siano visibili, è fondamentale chiamare il metodo dell’oggetto QMenu, setToolTipsVisible(True).

TEAR-OFF

Quando un menu viene usato intensivamente, è possibile abilitarne la modalità TEAROFF, ovvero è possibile “staccare” una copia del menù e appoggiarla in una parte delle schermo a noi congeniale. Questa copia, rimane fissa e non si disattiva ad ogni click, rendendo più comode le operazioni ripetitive.
Per abilitare tale funzione si deve utilizzare il metodo setTearOffEnabled(True) dell’oggetto QMenu.
Basterà cliccare sul menu e sulla linea tratteggiata dello stesso, apparirà così un menu, in una finestra propria, da muovere a proprio piacimento.

ICON SIZE

L’oggetto QMenu non ha un metodo che consenta di modificare la dimensione delle icone nelle voci di menu.
Per avere delle icone maggiorate, bisogna appoggiarsi alla classe QProxyStyle.
La classe suddetta, permette il wrapping della classe QStyle, che gestisce lo stile di default del sistema, semplificando di fatto l’override degli elementi della classe Qstyle.
Il trucco sta nel creare una classe che erediti da QStyle ed eseguire l’override dell’elemento che ci interessa.

class CustomProxyStyle(QProxyStyle):
    def pixelMetric(self, QStyle_PixelMetric, option=None, widget=None):
        if QStyle_PixelMetric == QStyle.PM_SmallIconSize and \
                isinstance(widget, QMenu):
            return 36
        else:
            return QProxyStyle.pixelMetric(self, QStyle_PixelMetric, option,
                                           widget)

L’override avviene proprio sul metodo pixelMetric di QStyle, che restituisce il pixel Metric relativo al QStyle enum passato come argomento e al widget.
Se il valore del QStyle enum equivale a PM_SmallIconSize e il widget di riferimento è un QMenu, allora il metodo ritorna il valore di pixel personalizzato, altrimenti ritorna quello di default.
Non resta che associare il nostro stile custom, alla nostra applicazione, con il metodo setStyle(style):
Per verificare che l’impostazione sia giusta, mettiamo nello slot dedicato al click su menu, due print che controllino i valori ritornati dal metodo pixeMetric del
nostro proxy style:

...
    def on_menu(self, action):
        menufile = self.sender()
        style = self.style()
        print("[SIG] %s menu clicked!" % action.text())
        print(style)
        print("QMenu: %s" % style.pixelMetric(QStyle.PM_SmallIconSize,
                                              widget=menufile))
        print("widgets: %s" % style.pixelMetric(QStyle.PM_SmallIconSize))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    custom_style = CustomProxyStyle('bancaldo')
    main_window = MainWindow(parent=None)
    main_window.show()
    app.setStyle(custom_style)
    sys.exit(app.exec_())

[SIG] New &Txt file menu clicked!
<__main__.CustomProxyStyle object at 0x010F3850>
QMenu: 36
widgets: 16

Torna all’indice degli appunti

Categorie:PyQt5, python Tag: ,
I commenti sono chiusi.