Saltar al contenido

Curso de creación de GUIs con Qt. Capítulo 07: Menu

Una ventana de una aplicación compleja dispone de muchas cosas y entre estas cosas suele haber un menú para poder acceder a la mayor parte de la funcionalidad de la misma de forma ordenada y organizada. En este capítulo describo cómo escribir menús y submenus de la ventana principal y algunas peculiaridades más de Qt.

Índice:

[Los materiales para este capítulo los podéis descargar de aquí]

[INSTALACIÓN] Si todavía no has pasado por el inicio del curso, donde explico cómo poner a punto todo, ahora es un buen momento para hacerlo y después podrás seguir con esta nueva receta.

Partimos del último código que dejamos en el capítulo 5 (de momento aparcamos el Splash Screen):

'''
Curso de creación de GUIs con Qt5 y Python

Author: Kiko Correoso
Website: pybonacci.org 
Licencia: MIT
'''

import os
os.environ['QT_API'] = 'pyside2'
import sys
from pathlib import Path

from qtpy.QtWidgets import QApplication, QMainWindow
from qtpy.QtGui import QIcon

class MiVentana(QMainWindow):
    def __init__(self):
        super().__init__()
        self._create_ui()

    def _create_ui(self):
        self.resize(500, 300)
        self.move(0, 0)
        self.setWindowTitle('Hola, QMainWindow')
        ruta_icono = Path('.', 'imgs', 'pybofractal.png')
        self.setWindowIcon(QIcon(str(ruta_icono)))
        self.statusBar().showMessage('Ready')
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    w = MiVentana()
    sys.exit(app.exec_())

Barra de menú y menús

Al código anterior le quiero añadir un menu. Si inspeccionamos w, la instancia de MiVentana, vemos que tiene lo siguiente:

print(dir(w))

Nos devuelve lo siguiente:

['AllowNestedDocks', 'AllowTabbedDocks', 'AnimatedDocks', 'DockOption', 'DockOptions', 'DrawChildren', 'DrawWindowBackground', 'ForceTabbedDocks', 'GroupedDragging', 'IgnoreMask', 'PaintDeviceMetric', 'PdmDepth', 'PdmDevicePixelRatio', 'PdmDevicePixelRatioScaled', 'PdmDpiX', 'PdmDpiY', 'PdmHeight', 'PdmHeightMM', 'PdmNumColors', 'PdmPhysicalDpiX', 'PdmPhysicalDpiY', 'PdmWidth', 'PdmWidthMM', 'RenderFlag', 'RenderFlags', 'VerticalTabs', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_create_menu', '_create_ui', 'acceptDrops', 'accessibleDescription', 'accessibleName', 'actionEvent', 'actions', 'activateWindow', 'addAction', 'addActions', 'addDockWidget', 'addToolBar', 'addToolBarBreak', 'adjustSize', 'autoFillBackground', 'backgroundRole', 'baseSize', 'blockSignals', 'centralWidget', 'changeEvent', 'childAt', 'childEvent', 'children', 'childrenRect', 'childrenRegion', 'clearFocus', 'clearMask', 'close', 'closeEvent', 'colorCount', 'connectNotify', 'contentsMargins', 'contentsRect', 'contextMenuEvent', 'contextMenuPolicy', 'corner', 'create', 'createPopupMenu', 'createWindowContainer', 'cursor', 'customContextMenuRequested', 'customEvent', 'deleteLater', 'depth', 'destroy', 'destroyed', 'devType', 'devicePixelRatio', 'devicePixelRatioF', 'devicePixelRatioFScale', 'disconnect', 'disconnectNotify', 'dockOptions', 'dockWidgetArea', 'documentMode', 'dragEnterEvent', 'dragLeaveEvent', 'dragMoveEvent', 'dropEvent', 'dumpObjectInfo', 'dumpObjectTree', 'dynamicPropertyNames', 'effectiveWinId', 'ensurePolished', 'enterEvent', 'event', 'eventFilter', 'find', 'findChild', 'findChildren', 'focusInEvent', 'focusNextChild', 'focusNextPrevChild', 'focusOutEvent', 'focusPolicy', 'focusPreviousChild', 'focusProxy', 'focusWidget', 'font', 'fontInfo', 'fontMetrics', 'foregroundRole', 'frameGeometry', 'frameSize', 'geometry', 'getContentsMargins', 'grab', 'grabGesture', 'grabKeyboard', 'grabMouse', 'grabShortcut', 'graphicsEffect', 'graphicsProxyWidget', 'hasFocus', 'hasHeightForWidth', 'hasMouseTracking', 'hasTabletTracking', 'height', 'heightForWidth', 'heightMM', 'hide', 'hideEvent', 'iconSize', 'iconSizeChanged', 'inherits', 'initPainter', 'inputMethodEvent', 'inputMethodHints', 'inputMethodQuery', 'insertAction', 'insertActions', 'insertToolBar', 'insertToolBarBreak', 'installEventFilter', 'isActiveWindow', 'isAncestorOf', 'isAnimated', 'isDockNestingEnabled', 'isEnabled', 'isEnabledTo', 'isFullScreen', 'isHidden', 'isLeftToRight', 'isMaximized', 'isMinimized', 'isModal', 'isRightToLeft', 'isSeparator', 'isSignalConnected', 'isVisible', 'isVisibleTo', 'isWidgetType', 'isWindow', 'isWindowModified', 'isWindowType', 'keyPressEvent', 'keyReleaseEvent', 'keyboardGrabber', 'killTimer', 'layout', 'layoutDirection', 'leaveEvent', 'locale', 'logicalDpiX', 'logicalDpiY', 'lower', 'mapFrom', 'mapFromGlobal', 'mapFromParent', 'mapTo', 'mapToGlobal', 'mapToParent', 'mask', 'maximumHeight', 'maximumSize', 'maximumWidth', 'menuBar', 'menuWidget', 'metaObject', 'metric', 'minimumHeight', 'minimumSize', 'minimumSizeHint', 'minimumWidth', 'mouseDoubleClickEvent', 'mouseGrabber', 'mouseMoveEvent', 'mousePressEvent', 'mouseReleaseEvent', 'move', 'moveEvent', 'moveToThread', 'nativeEvent', 'nativeParentWidget', 'nextInFocusChain', 'normalGeometry', 'objectName', 'objectNameChanged', 'overrideWindowFlags', 'overrideWindowState', 'paintEngine', 'paintEvent', 'paintingActive', 'palette', 'parent', 'parentWidget', 'physicalDpiX', 'physicalDpiY', 'pos', 'previousInFocusChain', 'property', 'pyqtConfigure', 'raise_', 'receivers', 'rect', 'releaseKeyboard', 'releaseMouse', 'releaseShortcut', 'removeAction', 'removeDockWidget', 'removeEventFilter', 'removeToolBar', 'removeToolBarBreak', 'render', 'repaint', 'resize', 'resizeDocks', 'resizeEvent', 'restoreDockWidget', 'restoreGeometry', 'restoreState', 'saveGeometry', 'saveState', 'scroll', 'sender', 'senderSignalIndex', 'setAcceptDrops', 'setAccessibleDescription', 'setAccessibleName', 'setAnimated', 'setAttribute', 'setAutoFillBackground', 'setBackgroundRole', 'setBaseSize', 'setCentralWidget', 'setContentsMargins', 'setContextMenuPolicy', 'setCorner', 'setCursor', 'setDisabled', 'setDockNestingEnabled', 'setDockOptions', 'setDocumentMode', 'setEnabled', 'setFixedHeight', 'setFixedSize', 'setFixedWidth', 'setFocus', 'setFocusPolicy', 'setFocusProxy', 'setFont', 'setForegroundRole', 'setGeometry', 'setGraphicsEffect', 'setHidden', 'setIconSize', 'setInputMethodHints', 'setLayout', 'setLayoutDirection', 'setLocale', 'setMask', 'setMaximumHeight', 'setMaximumSize', 'setMaximumWidth', 'setMenuBar', 'setMenuWidget', 'setMinimumHeight', 'setMinimumSize', 'setMinimumWidth', 'setMouseTracking', 'setObjectName', 'setPalette', 'setParent', 'setProperty', 'setShortcutAutoRepeat', 'setShortcutEnabled', 'setSizeIncrement', 'setSizePolicy', 'setStatusBar', 'setStatusTip', 'setStyle', 'setStyleSheet', 'setTabOrder', 'setTabPosition', 'setTabShape', 'setTabletTracking', 'setToolButtonStyle', 'setToolTip', 'setToolTipDuration', 'setUnifiedTitleAndToolBarOnMac', 'setUpdatesEnabled', 'setVisible', 'setWhatsThis', 'setWindowFilePath', 'setWindowFlag', 'setWindowFlags', 'setWindowIcon', 'setWindowIconText', 'setWindowModality', 'setWindowModified', 'setWindowOpacity', 'setWindowRole', 'setWindowState', 'setWindowTitle', 'sharedPainter', 'show', 'showEvent', 'showFullScreen', 'showMaximized', 'showMinimized', 'showNormal', 'signalsBlocked', 'size', 'sizeHint', 'sizeIncrement', 'sizePolicy', 'splitDockWidget', 'stackUnder', 'startTimer', 'staticMetaObject', 'statusBar', 'statusTip', 'style', 'styleSheet', 'tabPosition', 'tabShape', 'tabifiedDockWidgetActivated', 'tabifiedDockWidgets', 'tabifyDockWidget', 'tabletEvent', 'takeCentralWidget', 'testAttribute', 'thread', 'timerEvent', 'toolBarArea', 'toolBarBreak', 'toolButtonStyle', 'toolButtonStyleChanged', 'toolTip', 'toolTipDuration', 'tr', 'underMouse', 'ungrabGesture', 'unifiedTitleAndToolBarOnMac', 'unsetCursor', 'unsetLayoutDirection', 'unsetLocale', 'update', 'updateGeometry', 'updateMicroFocus', 'updatesEnabled', 'visibleRegion', 'whatsThis', 'wheelEvent', 'width', 'widthMM', 'winId', 'window', 'windowFilePath', 'windowFlags', 'windowHandle', 'windowIcon', 'windowIconChanged', 'windowIconText', 'windowIconTextChanged', 'windowModality', 'windowOpacity', 'windowRole', 'windowState', 'windowTitle', 'windowTitleChanged', 'windowType', 'x', 'y']

Los observadores puede que se hayan dado cuenta de la existencia de menuBar… Como ya comentamos en el capítulo 5 un QMainWindow no es más que un QWidget con algunas cosas más. Entre estas cosas ya vimos (en el capítulo 5) que tenía un statusBar y, vemos ahora, también tiene un menuBar. Vamos a hacer uso del mismo. Escribo un poco de código nuevo. Cada nueva línea de código se puede identificar porque lleva el comentario ## NUEVA LÍNEA al final, como hemos venido haciendo hasta ahora:

'''
Curso de creación de GUIs con Qt5 y Python

Author: Kiko Correoso
Website: pybonacci.org 
Licencia: MIT
'''

import os
os.environ['QT_API'] = 'pyside2'
import sys
from pathlib import Path

from qtpy.QtWidgets import QApplication, QMainWindow
from qtpy.QtGui import QIcon

class MiVentana(QMainWindow):
    def __init__(self):
        super().__init__()
        self._create_ui()

    def _create_ui(self):
        self.resize(500, 300)
        self.move(0, 0)
        self.setWindowTitle('Hola, QMainWindow')
        ruta_icono = Path('.', 'imgs', 'pybofractal.png')
        self.setWindowIcon(QIcon(str(ruta_icono)))
        self.statusBar().showMessage('Ready')
        self._create_menu() ## NUEVA LÍNEA
        self.show()

    def _create_menu(self): ## NUEVA LÍNEA
        menubar = self.menuBar() ## NUEVA LÍNEA
        file_menu = menubar.addMenu('&File') ## NUEVA LÍNEA
        help_menu = menubar.addMenu('&Help') ## NUEVA LÍNEA

if __name__ == '__main__':

    app = QApplication(sys.argv)
    w = MiVentana()
    sys.exit(app.exec_())

Podéis guardar el anterior código en un fichero que se llame main00.py o, si habéis descargado el repositorio, podéis usar el que se encuentra en la carpeta apps/07-MenuPrincipal. Lo podéis ejecutar con el terminal en el mismo sitio donde está el programa haciendo (si no os sale el icono es porque no lo tenéis metido en la carpeta apps/07-MenuPrincipal/imgs:

python main00.py

Si todo funciona correctamente deberíais ver una ventana como la siguiente:

En esa ventana se ve que tenemos dos menús, uno llamado File y otro llamado Help. Si pulsáis sobre ellos no pasará nada porque nos falta meter más cosas. Pero antes vamos a comentar el código nuevo.


        self._create_menu()

Esta es fácil… Dentro del método _create_ui llamamos al método _create_menu. En el segundo es dónde me he dedicado a gestionar la creación del menú.


    def _create_menu(self)
        menubar = self.menuBar()
        file_menu = menubar.addMenu('&File')
        help_menu = menubar.addMenu('&Help')

En este método, primero creamos una instancia de QMenuBar usando el método menuBar del QMainWindow y la guardamos en la variable menubar. En la instancia de QMenuBar tenemos el método addMenu que nos permite añadir menús a la barra de menús. El método addMenu recibe un string. En el string estoy usando un símbolo & y luego lo que quiero que aparezca en el manú (e.g., '&File' para que se vea File en el GUI. ¿Para qué sirve el ampersand, “&”? Es una forma rápida y sencilla de navegación. Cuando esté el GUI activo, si pulso sobre la tecla “Alt” hay varias letras del GUI que se verán subrrayadas, en el caso de nuestro GUI serán la F y la H. Si después de pulsar “Alt” pulsamos “F” será una forma de usar el teclado para navegar por nuestro GUI. Este sería un buen momento para experimentar y ejecutar el código cambiando la siguiente linea:

        file_menu = menubar.addMenu('&File')

Con

        file_menu = menubar.addMenu('File')

o

        file_menu = menubar.addMenu('Fil&e')

Si quitamos el ampersand, “&”, perdemos la navegación rápida con teclado para el menú File. Si movemos el ampersand, “&”, a otra letra podremos usar esa otra letra para nuestra navegación rápida con teclado.

Muy bien, ya tenemos un menú pero lo normal es que el menú se despliegue y nos muestre opciones.

Pero antes vamos a hacer un inciso para hablar de qtawesome.

qtawesome

Todas las aplicaciones quedan más bonitas y usables si usamos iconos bonitos y que nos informen de forma rápida de posibles interacciones que podemos hacer con el GUI. Una forma sencilla de integrar este tipo de iconos es usando la biblioteca qtawesome desarrollada por el equipo de gente detrás de spyder-ide. Esta biblioteca nos permite integrar de forma sencilla iconos de varios paquetes como FontAwesome, ElusiveIcons o MaterialDesignIcons.

Por tanto, para poder usar qtawesome antes lo tendréis que instalar en el entorno pyboqt que venimos usando. Para ello, hacemos lo siguiente en la terminal (o Anaconda Prompt si estás en Windows):

conda activate pyboqt
conda install qtawesome

Para usar un icono de, por ejemplo, FontAwesome podemos hacer lo siguiente:

import qtawesome as qta

icono = qta.icon('fa5.times-circle')

Y en icono ya tendriamos una instancia de QIcon con el icono times-circle de FontAwesome listo para usarlo en el GUI.

Esto ha sido un vistazo muy rápido a qtawesome. Para saber más le puedes echar un ojo a la documentación. Para lo que vamos a hacer hoy con lo que he explicado nos vale.

Elementos del menú

Volvemos a los menús…

Para ver cómo añadir elementos a un menú vamos a volver a escribir nuevo código y lo vamos explicando. Y, como siempre, las nuevas líneas vendrán destacadas con el comentario ## NUEVA LÍNEA:

'''
Curso de creación de GUIs con Qt5 y Python

Author: Kiko Correoso
Website: pybonacci.org 
Licencia: MIT
'''

import os
os.environ['QT_API'] = 'pyside2'
import sys
from pathlib import Path

from qtpy.QtWidgets import QApplication, QMainWindow, QAction ## NUEVA LÍNEA
from qtpy.QtGui import QIcon
import qtawesome as qta ## NUEVA LÍNEA


class MiVentana(QMainWindow):
    def __init__(self):
        super().__init__()
        self._create_ui()

    def _create_ui(self):
        self.resize(500, 300)
        self.move(0, 0)
        self.setWindowTitle('Hola, QMainWindow')
        ruta_icono = Path('.', 'imgs', 'pybofractal.png')
        self.setWindowIcon(QIcon(str(ruta_icono)))
        self.statusBar().showMessage('Ready')
        self._create_menu()
        self.show()

    def _create_menu(self):        
        menubar = self.menuBar()
        # File menu and its QAction's
        file_menu = menubar.addMenu('&File')
        exit_action = QAction(qta.icon('fa5.times-circle'),  ## NUEVA LÍNEA
                              '&Exit',  ## NUEVA LÍNEA
                              self) ## NUEVA LÍNEA
        exit_action.setShortcut('Ctrl+Q') ## NUEVA LÍNEA
        exit_action.setStatusTip('Exit application') ## NUEVA LÍNEA
        file_menu.addAction(exit_action) ## NUEVA LÍNEA
        # Help menu and its QAction's
        help_menu = menubar.addMenu('&Help')
        about_action = QAction(qta.icon('fa5s.info-circle'), ## NUEVA LÍNEA 
                               '&Exit',  ## NUEVA LÍNEA
                               self) ## NUEVA LÍNEA
        about_action.setShortcut('Ctrl+I') ## NUEVA LÍNEA
        about_action.setStatusTip('About...') ## NUEVA LÍNEA
        help_menu.addAction(about_action) ## NUEVA LÍNEA

if __name__ == '__main__':

    app = QApplication(sys.argv)
    w = MiVentana()
    sys.exit(app.exec_())

Empezamos, de nuevo, a comentar las nuevas líneas:


from qtpy.QtWidgets import QApplication, QMainWindow, QAction ## NUEVA LÍNEA

Añadimos el widget QAction en el import. Luego lo explico más en detalle.


import qtawesome as qta ## NUEVA LÍNEA

Esto lo hemos explicado en la sección anterior. importamos la biblioteca qtawesome para poder integrar fácilmente iconos en nuestra aplicación.


        exit_action = QAction(qta.icon('fa5.times-circle'), '&Exit', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.setStatusTip('Exit application')
        file_menu.addAction(exit_action)

En el método _create_menu estábamos creando la barra de menús y habíamos metido dos menús vacios (File y Help). En lás lineas destacadas aquí encima lo que estamos haciendo es crear una “acción” y la metemos en uno de los menús.

Pero, ¿Qué es una acción? En los GUIs hay acciones comunes que se pueden realizar de varias formas. Por ejemplo, para cerrar la aplicación podemos ir al menú “Archivo” y luego pulsar sobre el elemento “Cerrar” (“File” -> “Close” en inglés). Otra forma típica de cerrar es usar “Ctrl+Q”, que suele ser un atajo de teclado para cerrar la aplicación. Una tercera forma sería pulsando sobre el icono cerrar de la barra de herramientas (que todavía no hemos visto). Como todas esas acciones deben tener el mismo resultado se puede usar la clase QAction para tener una abstracción de ese comportamiento que luego podremos usar en las diferentes formas de cerrar la aplicación.

  • exit_action = QAction(qta.icon('fa5.times-circle'), '&Exit', self): En la primera línea de código es donde se usa la clase QAction. Para crear una instancia podemos pasar un icono, texto y clase padre o solo texto y la clase padre. En este caso uso la primera forma que incluye un icono. En el icono, en lugar de usar QIcon y una figura que tengamos en el disco duro hacemos lo mismo pero usando qtawesome como he explicado antes. Luego, el texto que queremos que muestre el elemento del menú y, por último, la clase padre será el propio QMainWindow (como self en este caso).
  • exit_action.setShortcut('Ctrl+Q'): Usamos el método setShortcut de QAction para añadir un atajo de teclado a la aplicación. En este caso usamos Ctrl+Q como atajo de teclado para que la aplicación se cierre (pero de momento no hará nada).
  • exit_action.setStatusTip('Exit application'): Con el método setStatusTip añadimos en mensaje que aparecerá en la barra de estado cuando el puntero del ratón esté sobre el elemento del menú.
  • file_menu.addAction(exit_action): Por último, anadimos el elemento del menú a su menú usando el método addAction de la instancia de QMenu llamada file_menu y que hemos creado mediante el método addMenu de QMenuBar.

Como podéis ver en la última línea, esto es un poco como un Lego donde tenemos un montón de piezas y las vamos uniendo de alguna forma para obtener, al final, nuestra figura final.

El resto de código del método _create_menu es repetir lo que acabo de explicar pero para el segundo menú, Help.

Si guardáis el anterior código completo en un fichero que se llame main01.py y lo ejecutáis, al igual que hemos hecho antes, deberéis ver algo parecido a lo siguiente:

En la anterior imagen podemos ver que al pulsar sobre File se despliega su menú. En ese menú podemos ver el icono (el circulito con la X), el texto que le hemos indicado y su atajo de teclado. Al poner el puntero del ratón sobre el elemento Exit vemos que aparece en la barra de estado (status_bar) el mensaje que habíamos definido, “Exit application”.

Si pulsáis con el botón izquierdo del ratón sobre la opción Exit o si usáis el atajo de teclado “Ctrl+Q” el GUI no hace absolutamente nada. Eso es algo que veremos en el próximo capítulo…

En este capítulo vamos a seguir trabajando nuestro menú,

Añadiendo un submenú

En un menú podemos añadir acciones o, también, otros menús. Vamos a ver como. Nuevamente, añadimos primero el código completo resaltando las líneas nuevas con el comentario ## NUEVA LÍNEA.

'''
Curso de creación de GUIs con Qt5 y Python

Author: Kiko Correoso
Website: pybonacci.org 
Licencia: MIT
'''

import os
os.environ['QT_API'] = 'pyside2'
import sys
from pathlib import Path

from qtpy.QtWidgets import (QApplication, QMainWindow,  ## NUEVA LÍNEA
                            QAction, QMenu) ## NUEVA LÍNEA
from qtpy.QtGui import QIcon
import qtawesome as qta


class MiVentana(QMainWindow):
    def __init__(self):
        super().__init__()
        self._create_ui()

    def _create_ui(self):
        self.resize(500, 300)
        self.move(0, 0)
        self.setWindowTitle('Hola, QMainWindow')
        ruta_icono = Path('.', 'imgs', 'pybofractal.png')
        self.setWindowIcon(QIcon(str(ruta_icono)))
        self.statusBar().showMessage('Ready')
        self._create_menu()
        self.show()

    def _create_menu(self):        
        menubar = self.menuBar()
        # File menu and its QAction's
        file_menu = menubar.addMenu('&File')
        exit_action = QAction(qta.icon('fa5.times-circle'),
                              '&Exit',
                              self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.setStatusTip('Exit application')
        file_menu.addAction(exit_action)
        # Help menu and its QAction's
        help_menu = menubar.addMenu('&Help')
        about_action = QAction(qta.icon('fa5s.info-circle'),
                               '&Exit',
                               self)
        about_action.setShortcut('Ctrl+I')
        about_action.setStatusTip('About...')
        help_menu.addAction(about_action)
        # NewMenu menu with a SubMenu and its QAction's
        new_menu = menubar.addMenu('NewMenu') ## NUEVA LÍNEA
        new_submenu = QMenu('SubMenu', self) ## NUEVA LÍNEA
        first_action = QAction('SubMenuAction1', self) ## NUEVA LÍNEA
        second_action = QAction('SubMenuAction2', self) ## NUEVA LÍNEA
        new_submenu.addAction(first_action) ## NUEVA LÍNEA
        new_submenu.addAction(second_action) ## NUEVA LÍNEA
        new_menu.addMenu(new_submenu) ## NUEVA LÍNEA

if __name__ == '__main__':

    app = QApplication(sys.argv)
    w = MiVentana()
    sys.exit(app.exec_())

Podéis guardar el anterior código en un fichero que se llame main02.py o, si habéis descargado el repositorio, podéis usar el que se encuentra en la carpeta apps/07-MenuPrincipal. Lo podéis ejecutar con el terminal en el mismo sitio donde está el programa haciendo (si no os sale el icono es porque no lo tenéis metido en la carpeta apps/07-MenuPrincipal/imgs:

python main02.py

Vemos el resultado:

Y comentamos el nuevo código:


from qtpy.QtWidgets import (QApplication, QMainWindow,  ## NUEVA LÍNEA
                            QAction, QMenu) ## NUEVA LÍNEA

Añadimos QMenu a los widgets que importamos.


        new_menu = menubar.addMenu('NewMenu')
        new_submenu = QMenu('SubMenu', self)
        first_action = QAction('SubMenuAction1', self)
        second_action = QAction('SubMenuAction2', self)
        new_submenu.addAction(first_action)
        new_submenu.addAction(second_action)
        new_menu.addMenu(new_submenu)

De nuevo, dentro del método _create_menu metemos algo más de código para meter un menú dentro de otros menú (submenú). Paso a comentar el código:

  • new_menu = menubar.addMenu('NewMenu'): Esto ya lo hemos visto antes. Añadimos un nuevo menú a la barra de menús.
  • new_submenu = QMenu('SubMenu', self): Ahora creamos un menú nuevo pero en lugar de usar el método addMenu del objeto QMenuBar usamos directamente la clase QMenu.
  • Las líneas first_action = QAction('SubMenuAction1', self) y second_action = QAction('SubMenuAction2', self): Son similares a lo visto antes. Simplemente ahora no añadimos atajo de teclado ni mensaje para la barra de estado.
  • Las líneas new_submenu.addAction(first_action)' y 'new_submenu.addAction(second_action): Esto también lo hemos visto antes. Añadimos ambas acciones al submenú.
  • Por último, new_menu.addMenu(new_submenu): añade el submenú al menú new_menu.

Lo dicho anteriormente, pequeñas piezas de Lego que al montarlas correctamente van adquieriendo forma.

Y, por hoy, creo que ya es suficiente.

Como resumen. Hemos creado menús, submenús, añadido atajos de teclado,… Pero, de momento, no sabemos como interactuar con el GUI, es decir, no nos podemos comunicar con el mismo para que haga cosas. Eso será el objeto del próximo capítulo.

2 comentarios en «Curso de creación de GUIs con Qt. Capítulo 07: Menu»

  1. Hola, buenas tardes! Soy nuevo en este mundo de la programacion y estoy con un proyecto de escritorio. Estoy queriendo hacer un sistema, que contiene una ventana princpial con su respectivo menu. Al hacer click en una opcion de la barra de menu, llamo a una ventana (qwidget), hasta ahi todo bien. Pero luego, desde esa ventana abierta, quiero llamar a otra ventana (qwidget), lo cual no estoy logrando hacer funcionar, me podrias comentar si es posible esta operacion y como seria? Muchas gracias.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

+ three = 6

Pybonacci