Está en la página 1de 12

PROGRAMACIÓN APLICADA A LA AUTOMATIZACIÓN

PREPRACTICA # 10 Creación de una aplicación visual


con mecanismo Signal & Slot y elementos de ventana.
CAPÍTULO DEL CURSO: Programación visual y orientada a eventos para la creación de HMI.

TEMA DE LA ACTIVIDAD: Uso de Qt Creator para desarrollar rápidamente


aplicaciones visuales con elementos de ventana que utilicen Signal & Slot. NOTA

OBJETIVOS DE APRENDIZAJE:
 Elaborar aplicaciones visuales orientadas a eventos de forma rápida que usen Signal &
Slot.
 Usar el ambiente de desarrollo integrado de Qt Creator para la edición, compilación,
ejecución y depuración de aplicaciones visuales sobre Windows.

DURACIÓN: 120 minutos

MATERIALES Y HERRAMIENTAS:
 Computadora con Qt Creator.

INTRODUCCIÓN

Uno de los fundamentos de las aplicaciones visuales interactivas es el uso de ventanas. Para la
realización de una aplicación profesional dichas ventanas incorporan menús, barras de
herramientas, barras de estado y una gran variedad de componentes o widgets. El manejo de la
interacción con muchos de estos elementos se basa en el mecanismo para el manejo de
mensajes del sistema operativo que Qt lo soporta mediante el mecanismo Signal & Slot. En esta
parte, se realizará una aplicación de blog de notas donde se utilizarán la mayoría de los
elementos de ventana, diálogos y Signal & Slot. Se debe emplear como bibliografía la conferencia
X o sesión de clase 10 y la propia bibliografía allí propuesta.

DESCRIPCIÓN DE LA PRÁCTICA: El estudiante debe realizar la prepráctica siguiendo las


instrucciones o guías planteadas a continuación. Al finalizar debe entregar el archivo del proyecto
realizado compactado para verificar su cumplimiento.

PROCEDIMIENTO:

Enunciado:

1. Desarrollar una aplicación completa que opere como un editor de texto simple.

Solución

En este caso se realizará un editor de texto simple, donde las opciones principales serán abrir o salvar un documento
y copiar o pegar texto, aspectos no muy complejos por lo que no implementaremos clases especializadas en la
solución de algún algoritmo en específico ya que no se necesita. Más bien se trabajará directamente en la interfaz
visual con las clases ya definidas en Qt.

Lo que se desea es desarrollar una aplicación que se comporte como un editor de texto bien sencillo y que luzca
como el que se muestra en la fig. 1.

Elaborado por:
MSc. Alexander Prieto León
Fig.1: Editor de texto.

Para ello comenzaremos creando un nuevo proyecto y una vez hecho esto pasamos al modo de diseño para editar la
apariencia visual de la aplicación. Empezamos definiendo los menús, que serán los que se muestran en la fig. 2.

Fig. 2: Menús de la aplicación.

La forma de hacer los menús es sencilla: basta con situarse encima de cada espacio y escribir los textos indicados en
la figura 2. A continuación introduciremos las imágenes asociadas del menú y sus teclas de acceso rápido.

Cuando en una aplicación se quiere mostrar algunas imágenes o iconos y que siempre se visualicen en cualquier
computadora es muy útil guardar estas en
un tipo de fichero destinado para ello.
Este fichero se le conoce como
resource y en Qt Creator se crea de la
siguiente manera:

1. Ir al menú File opción New File or


Project…
2. Al abrirse la ventana elegir Qt,
Qt Resource File.
3. Luego se muestra otra
ventana donde se le da
nombre al fichero. Como
recomendación
ponerle el mismo del proyecto.
Dar clic en Next.
4. Dar clic en Finish.

Al terminal dichos pasos en el modo de edición se adiciona el fichero que hemos creado, tal y como se muestra en la
fig. 3. Al dar clic sobre él, en la zona derecha se muestra su contenido que en este caso está vacío. Se va al botón
adicionar y se añade un nuevo prefix, que viene siendo como un nivel o separador de contenido dentro de la
estructura de este fichero. Por defecto sale /new/prefix pero nosotros lo dejaremos en /, como se muestra en la fi.
4.
Elaborado por:
MSc. Alexander Prieto León
Fig. 3: Creando el fichero Resource.

Fig. 4.

Luego se da adicionar nuevamente, pero esta vez será el contenido, se elige Add Files y se busca la carpeta que
contiene las imágenes que queremos se incluyan en la aplicación de manera permanente. Las imágenes con las que
trabajaremos son las que se muestran a continuación, usted puede copiar una a una hacia el Paint y salvarla con la
extensión png o jpg. Una vez tenidas todas en una carpeta adicionarlas al fichero Resource como se explicó
anteriormente.

Con el fichero Resource ya creado podemos seguir editando los menús. Para ello creamos cada una de las opciones
que se muestran en la fig. 2. A medida que se van creando se le puede añadir la imagen y el acceso rápido usando el
editor de acciones que tiene Qt Creator y que se encuentra en la pestaña inferior del área donde se muestra la
interfaz visual, fig. 5. Por defecto la pestaña activa es la correspondiente a Signal & Slots Editor.

Fig. 5: Editor de acciones.

Al dar clic sobre la acción que se quiere editar (recuerde que a cada opción del menú se le asociará una acción) se
muestra una ventana como la siguiente:

Elaborado por:
MSc. Alexander Prieto León
Fig. 6.

En esta al dar clic sobre el botón con texto … correspondiente a la sección Icon se puede cargar una imagen
cualquiera o del fichero Resource. Nosotros escogeremos este último caso por lo que se activará la ventana de la
Fig.7. En la misma se muestran todas las imágenes que contiene el resource y se elige la que se quiera mostrar en la
opción del menú. La primera vez que sale esta ventana dichas imágenes puede que no se muestren por lo que hay
que cerciorarse de dos cosas: primero que el fichero resource haya sido salvado y segundo que se recargue este,
esto se hace con la flecha verde que se encuentra en la esquina superior izquierda.

Fig. 7: seleccionando la imagen del archivo Resource.

En la misma ventana de la fig. 6 también se puede definir las teclas de acceso rápido a la opción del menú. Para ello
dar clic en la casilla en blanco correspondiente a Shortcut y tocar la combinación de teclas que se desea.
En dicha ventana también se pueden editar otras propiedades que le dejamos a usted examine como estudio
independiente.
Este proceso se repite para cada una de las opciones del menú de la aplicación. Antes de seguir adelante
deshabilitemos las acciones actionCut y actionCopy. Para ellos selecciónelas en el inspector de objetos y desmarque
la propiedad enabled (en el editor de propiedades).

Ahora añadimos el widget plainTextEdit, el cual se utiliza para escribir texto y le llamaremos textEdit. Al soltarlo
sobre la ventana principal se le aplica un Grid Layout al formulario para hacer que el plainTextEdit se redimensione
al tamaño de la ventana constantemente. En la ventana principal ponemos la propiedad WindowTitle vacía,
posteriormente se explicará con más detalles el porqué de esto.

Pasemos entonces a implementar las funcionalidades de la aplicación, donde también se explicará cómo hacer editar
las herramientas tool bar y status bar. A continuación, se muestra la definición de la clase MainWindow.

Elaborado por:
MSc. Alexander Prieto León
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QString curFile;
void createToolBars();
void createStatusBar();
void createStatusTip();
void connectActions();
void readSettings();
void writeSettings();
bool maybeSave();
void loadFile(const QString &fileName);
bool saveFile(const QString &fileName);
void setCurrentFile(const QString &fileName);
protected:
void closeEvent(QCloseEvent *event);
private slots:
void newFile();
void open();
bool save();
bool saveAs();
void about();
void documentWasModified();
};

En la sección protegida se reimplementará el método closeEvent() para detectar cuando el usuario intenta cerrar la
aplicación y alertarlo sobre los cambios que no han sido salvados. En la sección private slots se declaran los slots
correspondientes a las entradas de los menús así como el slot documentWasModified() que posteriormente se
explicará su uso. Finalmente, en la sección privada de la clase se tienen varios miembros que serán explicados a su
debido tiempo.

Veamos entonces la implementación de la clase. Comenzamos incluyendo:

#include <QtGui>
#include "mainwindow.h"

El archivo de cabecera <QtGui> contiene la definición de todas las clases de las librerías QtCore y QtGui. Esto nos
evita el problema de estar incluyendo cada una individualmente.

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
createToolBars();
createStatusBar();
createStatusTip();
readSettings();
connect(ui->textEdit->document(), SIGNAL(contentsChanged()),
this, SLOT(documentWasModified()));
connectActions();
setCurrentFile("");
}

Elaborado por:
MSc. Alexander Prieto León
En el constructor inicializamos y creamos en algunos casos las herramientas tool bar y status bar mediante las
funciones createToolBars(), createStatusBar()y createStatusTip(). Se reestablece la configuración del usuario, se
crean las conexiones de las acciones correspondiente al menú así como la del documento de QPlainTextEdit y el slot
documentWasModified(). Este slot será usado para actualizar la barra de título y mostrar cuando el documento ha
sido modificado. Al finalizar se introduce el título de la ventana usando la función setCurrentFile() la cual será
abordada posteriormente.

La función crearToolBars () se encarga de adicionar a la barra de herramientas las acciones que fueron creadas con
el editor de acciones que se encuentra en la interfaz visual. Por defecto este editor de acciones las asocia con el
Menú principal de la aplicación, por lo que si se quieren utilizar estas con otros widgets hay que hacerlo mediante
código.

void MainWindow::createToolBars()
{
ui->mainToolBar->addAction(ui->actionNew);
ui->mainToolBar->addAction(ui->actionOpen);
ui->mainToolBar->addAction(ui->actionSave);
ui->mainToolBar->addSeparator();
ui->mainToolBar->addAction(ui->actionCut);
ui->mainToolBar->addAction(ui->actionCopy);
ui->mainToolBar->addAction(ui->actionPaste);
}

La barra de estado se crea la primera vez que se llama a la función QMainWindow::statusBar() la cual devuelve un
puntero a dicha barra. En la función createStatusBars() además se muestra el texto Ready en la barra de estado
cuando esta es creada. Dicho texto se encuentra dentro de la función tr(), esta se utiliza para la traducción del texto
que se le pase como argumento. Es una buena práctica utilizar la función tr() con todas las cadenas visibles en caso
de que posteriormente se decida a traducir su aplicación a otro idioma. Para mejor entendimiento de este tema
revisar en la ayuda de Qt el tópico: Internationalization with Qt.

void MainWindow::createStatusBar()
{
statusBar()->showMessage(tr("Ready"));
}

La función createStatusTip() crea los textos que se van a mostrar en la barra de estado para cada acción. Para ello
utiliza la función QAction::setStatusTip().

void MainWindow::createStatusTip()
{
ui->actionNew->setStatusTip(tr("Create a new file"));
ui->actionOpen->setStatusTip(tr("Open an existing file"));
ui->actionSave->setStatusTip(tr("Save the document to disk"));
ui->actionSave_As->setStatusTip(tr("Save the document under a new
name"));
ui->actionExit->setStatusTip(tr("Exit the application"));
ui->actionCut->setStatusTip(tr("Cut the current selection's
contents to the clipboard"));
ui->actionCopy->setStatusTip(tr("Copy the current selection's
contents to the clipboard"));
ui->actionPaste->setStatusTip(tr("Paste the clipboard's contents
into the current selection"));
ui->actionAbout->setStatusTip(tr("Show the application's About
box"));
ui->actionAbout_Qt->setStatusTip(tr("Show the Qt library's About
box"));
}

La función connectActions() es la encargada de establecer las conexiones de las señales que emiten las acciones
con los slots de MainWindow correspondientes. La forma de hacerlos es la vista ya con anterioridad por lo que solo
se pondrán segmentos de la función.
Elaborado por:
MSc. Alexander Prieto León
void MainWindow::connectActions()
{
connect(ui->actionNew,SIGNAL(triggered()),this,SLOT(newFile()));
connect(ui->actionOpen,SIGNAL(triggered()),this,SLOT(open()));
connect(ui->actionSave,SIGNAL(triggered()),this,SLOT(save()));


connect(ui->textEdit, SIGNAL(copyAvailable(bool)),
ui->actionCut,SLOT(setEnabled(bool)));
connect(ui->textEdit, SIGNAL(copyAvailable(bool)),
ui->actionCopy, SLOT(setEnabled(bool)));

connect(ui->actionAbout,SIGNAL(triggered()),this,SLOT(about()));
connect(ui->actionAbout_Qt ,SIGNAL(triggered()),
qApp,SLOT(aboutQt()));
}

La señal triggered() es emitida por una acción cuando se da clic sobre ella. Esta es conectada al slot correspondiente
para procesarla. Esto se realiza para todas las acciones: new, open, save, save as, exit, cut, copy, paste, about y
about qt. Cuando se esté realizando la conexión de la acción exit se debe utilizar el slot QMainWindow::close().
Nóte que este no se ha declarado en la definición de la clase MainWindow pues lo trae la clase base.
En el caso de las acciones cut y copy también realizamos otra conexión para lograr que estas solo estén habilitadas
cuando hay texto seleccionado (cerciórese de que dichas acciones están deshabilitadas inicialmente en el inspector
de objetos). Para ello se conecta la señal copyAvailable() perteneciente a QPlainTextEdit con el slot setEnable() de
QAction.
Finalmente solo agregar que al establecer la conexión de la acción about qt se utiliza como slot la función aboutQt()
perteneciente a qApp, este slot muestra una ventana de dialogo con las características de la versión de Qt que se
está usando. qApp es un objeto de la clase QApplication que está presente en todo programa de Qt.

Nota: para que las acciones Cut, Copy y Paste puedan funcionar debe realizar las conexiones entre dichas acciones y
los slots del QPlainTextEdit: cut(), copy() y paste() respectivamente. También conecte la acción Save_as con su
correspondiente slot saveAs() implementado más abajo en este documento.

La función closeEvent() se reimplementa para poder cambiar el comportamiento del evento close de la ventana
principal. El código de esta función se muestra a continuación:

void MainWindow::closeEvent(QCloseEvent *event)


{
if (maybeSave())
{
writeSettings();
event->accept();
}
else
{
event->ignore();
}
}

En este caso cuando el usuario intenta cerrar la ventana se llama a la función maybeSave() para darle la posibilidad
de salvar los cambios pendientes. La función devuelve verdadero si el usuario quiere cerrar la aplicación, de otra
manera devuelve falso. En el primer caso, se salvan las preferencias del usuario hacia el disco y se acepta el evento;
en el segundo caso, se ignora el evento lo que significa que la aplicación se mantendrá como si no hubiese ocurrido
nada.

void MainWindow::newFile()
{
if (maybeSave())
{
Elaborado por:
MSc. Alexander Prieto León
ui->textEdit->clear();
setCurrentFile("");
}
}

El slot newFile() es invocado al acceder al menú File|new. En este se llama a la función maybeSave() para salvar
cualquier cambio pendiente y si el usuario acepta se limpia el texto y se llama a setCurrentFile() para actualizar el
título de la aplicación y limpiar la bandera windowModified.

void MainWindow::open()
{
if (maybeSave())
{
QString fileName = QFileDialog::getOpenFileName(this);
if (!fileName.isEmpty())
loadFile(fileName);
}
}

En el slot open() se invoca una ventana de dialogo QFileDialog permitiéndole al usuario elegir el archivo a abrir. Si el
usuario elige uno se llama a la función loadFile() para cargarlo.

bool MainWindow::save()
{
if (curFile.isEmpty())
{
return saveAs();
}
else
{
return saveFile(curFile);
}
}

El slot save() si el usuario no le ha dado nombre al fichero actual todavía, se llama a la función saveAs(), de lo
contrario se llama a la función saveFile().

bool MainWindow::saveAs()
{
QString fileName = QFileDialog::getSaveFileName(this);
if (fileName.isEmpty())
return false;
return saveFile(fileName);
}

El slot saveAs() comienza abriendo una ventana de dialogo donde el usuario podrá elegir la ubicación y el nombre
para salvar el fichero. Si el usuario elige Cancel fileName estará vacío y por tanto no se hará nada.

void MainWindow::about()
{
QMessageBox::about(this, tr("About Application"),
tr("The Application example demonstrates how to "
"write modern GUI applications using Qt, with a menu bar, "
"toolbars, and a status bar."));
}

En el slot about() se llama la función estática QMessageBox::about().

void MainWindow::documentWasModified()
{
setWindowModified(ui->textEdit->document()->isModified());
}
Elaborado por:
MSc. Alexander Prieto León
El slot documentWasModified() es invocado cada vez que el texto en el QPlainTextEdit es modificado. En este se
llama la función QWidget::setWindowModified() para hacer que la barra de título muestre que el fichero ha sido
modificado.

void MainWindow::readSettings()
{
QSettings settings("Curso Qt", "Ejemplo editor de texto");
QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
QSize size = settings.value("size", QSize(400, 400)).toSize();
resize(size);
move(pos);
}

La función readingSettings() es llamada en el constructor para cargar las preferencias del usuario. La clase QSettings
provee una interfaz para almacenar las preferencias en el disco duro. El constructor de QSettings toma argumentos
que identifican la compañía y el nombre del producto. Esto asegura que las preferencias de las aplicaciones se
mantengan separadas.

Se utiliza la función QSettings::value() para extraer el valor de la posición y el tamaño de la aplicación. El


segundo argumento de dicha función es opcional y se utiliza para indicar el valor por defecto si no
existe ninguno, este valor es usado la primera vez que corre la aplicación.

Cuando se restablece el tamaño y la posición de la ventana es importante llamar a QWidget::resize()


antes que a QWidget::move().

void MainWindow::writeSettings()
{
QSettings settings("Curso Qt", "Ejemplo editor de texto");
settings.setValue("pos", pos());
settings.setValue("size", size());
}

La función writeSettings() es llamada desde closeEvent(). Salvar las preferencias es muy similar a
leerlas.

bool MainWindow::maybeSave()
{
if (ui->textEdit->document()->isModified())
{
QMessageBox::StandardButton ret;
ret = QMessageBox::warning(this, tr("Application"),
tr("The document has been modified.\n"
"Do you want to save your changes?"),
QMessageBox::Save | QMessageBox::Discard |
QMessageBox::Cancel);
if (ret == QMessageBox::Save)
return save();
else if (ret == QMessageBox::Cancel)
return false;
}
return true;
}

Elaborado por:
MSc. Alexander Prieto León
La función maybeSave() es llamada para salvar los cambios pendientes. Si existiera alguno se levanta una ventana de
dialogo dándole al usuario la opción de salvar el documento. Esta función retorna verdadero en todos los casos
excepto cuando el usuario da clic en Cancel.

void MainWindow::loadFile(const QString &fileName)


{
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::warning(this, tr("Application"),
tr("Cannot read file %1:\n%2.")
.arg(fileName)
.arg(file.errorString()));
return;
}
QTextStream in(&file);
#ifndef QT_NO_CURSOR
QApplication::setOverrideCursor(Qt::WaitCursor);
#endif
ui->textEdit->setPlainText(in.readAll());
#ifndef QT_NO_CURSOR
QApplication::restoreOverrideCursor();
#endif
setCurrentFile(fileName);
statusBar()->showMessage(tr("File loaded"), 2000);
}

En loadFile() se usa QFile y QTextStream para leer los datos de un fichero. Un objeto de QFile provee acceso a los
bytes almacenados en un fichero. En esta función se comienza abriendo el fichero en modo de solo lectura. La
bandera QFile::Text indica que el fichero es de texto y no binario.
Si la apertura fue satisfactoria, entonces se usa un objeto de QTextStream para leer los datos. QTextStream
convierte automáticamente un dato de 8 bit a una cadena QString.
Debido a que el llamado de de la función QTextStream::readAll() puede tomar algún tiempo, se pone el cursor en
modo de espera para toda la aplicación mediante la bandera Qt::WaitCursor al llamar a la función
QApplication::setOverrideCursor.
Al finalizar se llama a la función setCurrentFile() para mostrar en la barra de título el nombre del archivo y se
muestra en la barra de estado “File loaded” durante 2 segundos.

bool MainWindow::saveFile(const QString &fileName)


{
QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text))
{
QMessageBox::warning(this, tr("Application"),
tr("Cannot write file %1:\n%2.")
.arg(fileName)
.arg(file.errorString()));
return false;
}
QTextStream out(&file);
#ifndef QT_NO_CURSOR
QApplication::setOverrideCursor(Qt::WaitCursor);
#endif
out << ui->textEdit->toPlainText();
#ifndef QT_NO_CURSOR
QApplication::restoreOverrideCursor();
#endif
setCurrentFile(fileName);
statusBar()->showMessage(tr("File saved"), 2000);
return true;
}

Elaborado por:
MSc. Alexander Prieto León
Salvar el fichero es muy similar a abrirlo por lo que no se explicará esta.

void MainWindow::setCurrentFile(const QString &fileName)


{
curFile = fileName;
ui->textEdit->document()->setModified(false);
setWindowModified(false);
QString shownName = curFile;
if (curFile.isEmpty())
shownName = "untitled.txt";
setWindowFilePath(shownName);
}

Finalmente la function setCurrentFile() es llamada para resetear el estado de algunas variables cuando un fichero es
salvado o cargado, o cuando el usuario comienza a editar un nuevo fichero. Aquí se actualiza la variable curFile, se
limpia la bandera QTextDocument::modified y la asociada QWidget:windowModified y se actualiza la barra de
título con el nombre del nuevo fichero (o untitled.txt).

Ahora solo le queda compilar y ver cómo funciona, Buena Suerte!!!!!

Estudio Independiente (opcional y no se entrega)

Cree una clase llamada Block en la cual se encapsularán los algoritmos que se le quieran añadir a este editor de
texto. La clase debe tener como mínimo:
 Un puntero a QPlainTextEdit. Este puntero debe ser inicializado inicialmente con el objeto de
QPlainTextEdit que contiene el texto con el que se está trabajando.
 Una función miembro buscarSiguiente() que reciba como argumento la cadena que se desea buscar dentro
del texto del editor.
 Otras funciones necesarias para su correcto funcionamiento, p.ej: constructor, etc.

Añada una ventana de dialogo al editor de texto que posibilite la búsqueda de una palabra cualquiera dentro del
texto. Haga uso de la clase definida anteriormente. De esta manera se está separando la interfaz visual de los
algoritmos.

Nota: Esta prepráctica no lleva informe, se entrega la carpeta del proyecto compactada.

BIBLIOGRAFIA

[1]. Zhi Eng, Lee; Rischpater, Ray. Application Development with Qt Creator: Build cross-platform
applications and GUIs using Qt 5 and C++, 3rd Edition. (2020). Packt Publishing. ISBN-10:
1789951755, ISBN-13: 978-1789951752.

[2] http://www.qtrac.eu/C++-GUI-Programming-with-Qt-4-1st-ed.zip

[3] Qt 5.14.2 Reference Documentation. Qt Creator Help.

[4] https://doc.qt.io/qtcreator/index.html

[5] https://doc.qt.io/qt-5/qtdesigner-manual.html

Elaborado por:
MSc. Alexander Prieto León
INICIAL EN DESARROLLO DESARROLLADO EXCELENTE

Firma del Profesor

Elaborado por:
MSc. Alexander Prieto León

También podría gustarte