Saltar al contenido

Curso de creación de GUIs con Qt. Capítulo 05: Ventanas, principales diferencias

Vamos a seguir evolucionando hacia hacer GUIs cada vez más sofisticados. En este capítulo describo Los tipos de ventanas principales que podemos usar en nuestras aplicaciones y los rasgos principales que las diferencian.

Í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.

Hasta ahora hemos usado la clase QWidget para crear nuestras ventanas, las cuales no son muy útiles de momento… Pero tenemos varias formas de crear ventanas usando otras clases.

Normalmente, QWidget no será la clase que usaremos para crear la ventana principal de nuestra aplicación. QWidget será usada para crear subventanas que llamaremos desde la aplicación principal.

En la aplicación principal querremos usar una instancia de QMainWindow. Que no es más que un QWidget un poco más trabajado.

Hay una tercera forma de ventana que son los QDialogs que son ventanas con botones típicos de ‘Si’ o ‘No’, ‘Aceptar’ o ‘Cancelar’, ‘Cerrar’,…

Vamos a definir un poco más formalmente estas cosas usando esta respuesta que he encontrado en StackOverflow.

QWidget

Un QWidget es la clase base para todos los widgets que vamos a usar. Cualquier clase que herede de QWidget se podría mostrar como una ventana cuando no tiene un padre. Un padre será otra ventana que hará de contenedor de nuestro QWidget. Lo que hemos venido haciendo hasta ahora es usar la clase QWidget sin padre, es decir, como la ventana principal. Esto tiene sentido cuando estemos haciendo aplicaciones muy sencillas que no necesiten, por ejemplo, un menú o una barra de herramientas.

QDialog

QDialog se basa en QWidget. Dispone de funciones que hacen que funcione de forma conveniente con botones típicos como ‘Aceptar’, ‘Cancelar’, ‘Sí’, ‘No’,… Es decir, lo usaremos cuando queramos establecer un diálogo entre la aplicación y el usuario donde le daremos opciones al usuario sobre las que tendrá que responder y retroalimentar a la aplicación para que sepa cómo tiene que proceder.

QMainWindow

La clase QMainWindow ha sido diseñada para contemplar necesidades básicas que debería tener una ventana principal de una aplicación. Dispone de lugares apropiados para, por ejemplo, una barra de menús, una barra de estado o una barra de herramientas. La realidad es que esta clase hereda de QWidget y le añade la funcionalidad específica para que actúe de ventana principal.

Refactorizando

Como hicimos en el capítulo anterior vamos a reformatear nuestra aplicación para que use QMainWindow como ventana principal. Ahí va el código que luego pasaremos a explicar:

'''
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
from qtpy.QtGui import QIcon

class MiVentana(QMainWindow): ## NUEVA LÍNEA
    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.show()

if __name__ == '__main__':

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

Como véis, el código es prácticamente igual que antes pero donde antes usábamos QWidget ahora estamos usando QMainWindow (en el import y en la definición de la clase). Este código lo tenéis guardado en el fichero main00.py en la carpeta apps/05-QWidget_QMainWindow_QDialog/ del repositorio.

Si vemos todo lo disponible en nuestra instancia w de nuestra clase MiVentana vemos lo siguiente:

print(dir(w))
['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__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_create_ui', 'acceptDrops', 'accessibleDescription', 'accessibleName', 'actionEvent', 'actions', 'activateWindow', 'addAction', 'addActions', 'addDockWidget', 'addToolBar', 'addToolBarBreak', 'adjustSize', 'autoFillBackground', 'backgroundRole', 'backingStore', 'baseSize', 'blockSignals', 'centralWidget', 'changeEvent', 'childAt', 'childEvent', 'children', 'childrenRect', 'childrenRegion', 'clearFocus', 'clearMask', 'close', 'closeEvent', 'colorCount', 'connect', 'connectNotify', 'contentsMargins', 'contentsRect', 'contextMenuEvent', 'contextMenuPolicy', 'corner', 'create', 'createPopupMenu', 'createWinId', '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', 'emit', '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', 'height', 'heightForWidth', 'heightMM', 'hide', 'hideEvent', 'iconSize', 'iconSizeChanged', 'inherits', 'initPainter', 'inputMethodEvent', 'inputMethodHints', 'inputMethodQuery', 'insertAction', 'insertActions', 'insertToolBar', 'insertToolBarBreak', 'installEventFilter', 'internalWinId', 'isActiveWindow', 'isAncestorOf', 'isAnimated', 'isDockNestingEnabled', 'isEnabled', 'isEnabledTo', 'isEnabledToTLW', 'isFullScreen', 'isHidden', 'isLeftToRight', 'isMaximized', 'isMinimized', 'isModal', 'isRightToLeft', 'isSeparator', 'isSignalConnected', 'isTopLevel', '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', 'nativeParentWidget', 'nextInFocusChain', 'normalGeometry', 'objectName', 'objectNameChanged', 'overrideWindowFlags', 'overrideWindowState', 'paintEngine', 'paintEvent', 'painters', 'paintingActive', 'palette', 'parent', 'parentWidget', 'physicalDpiX', 'physicalDpiY', 'pos', 'previousInFocusChain', 'property', 'raise_', 'receivers', 'rect', 'redirected', 'registerUserData', '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', 'setToolButtonStyle', 'setToolTip', 'setToolTipDuration', 'setUnifiedTitleAndToolBarOnMac', 'setUpdatesEnabled', 'setVisible', 'setWhatsThis', 'setWindowFilePath', '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', 'tabifiedDockWidgets', 'tabifyDockWidget', 'tabletEvent', 'takeCentralWidget', 'testAttribute', 'thread', 'timerEvent', 'toolBarArea', 'toolBarBreak', 'toolButtonStyle', 'toolButtonStyleChanged', 'toolTip', 'toolTipDuration', 'topLevelWidget', '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']

¡¡¡Guauuu!!! Tenemos unas cuantas cosas para toquetear. Algunas ya las hemos estado usando, como los métodos resize, move, setWindowTitle, setWindowIcon o show. Algunas otras las iremos viendo cuando tenga sentido verlas. De momento quedaos con que es un objeto con bastante funcionalidad.

Barra de estado

Para que este capítulo no sea tan corto, antes de terminar el mismo vamos a hacer alguna cosita más con esta nueva ventana principal más potente que la que habíamos usado hasta ahora. Vamos a toquetear alguna cosa más para añadir una barra de estado para nuestra aplicación. Como antes, dejo primero el código y luego comentamos:

'''
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') ## NUEVA LÍNEA
        self.show()

if __name__ == '__main__':

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

Lo único que hemos añadido ha sido la línea:

self.statusBar().showMessage('Ready')

en la cual usamos el método showMessage del widget QStatusBar. El widget QStatusBar lo obtenemos llamando al método statusBar del QMainWindow. Como he comentado, QMainWindow no es más que un QWidget con esteroides y entre estos esteroides tenemos la posibilidad de acceder a una barra de estado que ya ha sido incluida en QMainWindow. Si os fijáis de nuevo en todo lo que dispone un QMainWindow (el print(dir(w)) que hemos hecho más arriba) veréis cosas como menuBar o centralWidget,… Pero no nos adelantemos que luego se complica. Seguimos dando pasitos cortos y sencillos.

Este código lo tenéis guardado en el fichero main01.py en la carpeta apps/05-QWidget_QMainWindow_QDialog/ del repositorio.

El resultado con la barra de estado lo podéis ver en la imagen siguiente donde abajo a la derecha veis el mensaje que estamos mostrando (Ready).

Y aquí lo dejamos hoy. En el próximo capítulo seguiremos añadiendo cosas a nuestra GUI.

P.D.: Si tienes alguna duda, por favor, usa los comentarios.

Deja una respuesta

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

27 − twenty two =

Pybonacci