Qt Alfabetización-Modelo Qt/Resumen de la teoría de la vista [Parte 2]

Resumen de la teoría del modelo/vista Qt [Parte 2]

1. Manejo de la selección en la vista de elementos

1. Concepto

El modelo de selección utilizado en la clase de vista de elementos proporciona una descripción general de las selecciones basadas en la arquitectura de modelo/vista. Si bien las clases estándar para manipular selecciones son suficientes para las vistas de elementos proporcionadas, el modelo de selección nos permite crear modelos de selección especializados para satisfacer las necesidades de nuestros propios modelos y vistas de elementos.

La información sobre el elemento seleccionado en la vista se almacena en una instancia de la clase QItemSelectionModel. Mantiene índices de modelo para elementos en un solo modelo, independientemente de cualquier vista. Dado que un modelo puede tener múltiples vistas, las selecciones se pueden compartir entre vistas, lo que permite que una aplicación muestre múltiples vistas de manera consistente.

Las selecciones consisten en rangos de selección. Estos métodos mantienen de manera eficiente la información sobre selecciones grandes al registrar solo los índices del modelo inicial y final de cada rango de selecciones. Cree conjuntos no contiguos de elementos de selección mediante el uso de múltiples rangos de selección para describir las selecciones;

La selección se aplica a la colección de índices de modelo que posee el modelo de selección. La aplicación seleccionada más recientemente se denomina selección actual. El efecto de esta selección se puede modificar usando ciertos tipos de comandos de selección incluso después de que se haya aplicado. Estos se discuten más adelante en esta sección.

1. Elemento actual y elemento seleccionado

En una vista, siempre hay un elemento actual y un elemento seleccionado, que son dos estados separados. Un elemento puede ser el elemento actual o el elemento seleccionado al mismo tiempo. La vista es responsable de asegurarse de que siempre haya un elemento actual, por ejemplo, la navegación con el teclado requiere un elemento actual.

La siguiente tabla destaca las diferencias entre el elemento actual y el elemento seleccionado.

elemento actual item seleccionado
Solo puede haber un elemento actual. Puede haber múltiples selecciones.
El elemento actual cambiará con la navegación con pulsaciones de teclas o con los clics del botón del mouse. Cuando un usuario interactúa con un elemento, si el estado de selección del elemento está establecido o no depende de varios modos predefinidos, por ejemplo, selección única, selección múltiple, etc.
Si se presiona la tecla de edición F2 o se hace doble clic en el elemento, se editará el elemento actual (siempre que la edición esté habilitada). El elemento actual se puede usar con anclas para especificar rangos para selección o deselección (o una combinación de ambos).
El elemento actual está representado por un rectángulo de enfoque. El elemento seleccionado se representa mediante un rectángulo de selección.

Al manipular selecciones, QItemSelectionModel generalmente se puede considerar como un registro de los estados de selección de todos los elementos en un modelo de elemento. Una vez que se establece el modelo de selección, los elementos se pueden seleccionar, deseleccionar o alternar entre sus estados seleccionados sin saber qué elementos ya están seleccionados. Los índices de todos los elementos seleccionados se pueden obtener en cualquier momento, y otros componentes pueden ser notificados del cambio del modelo de selección a través del mecanismo de señal y ranura.

2. Usa el modelo de selección

La clase de vista estándar proporciona un modelo de selección predeterminado que se puede utilizar en la mayoría de las aplicaciones. El modelo de selección que pertenece a una vista se puede obtener utilizando la función selectionModel() de la vista y se puede compartir entre varias vistas a través de setSelectionModel(), por lo que normalmente no es necesario construir un nuevo modelo de selección.
Cree una selección especificando un modelo y un par de índices de modelo QItemSelection. Esto usa índices para referirse a elementos en el modelo dado y los interpreta como elementos de arriba a la izquierda y de abajo a la derecha en el bloque de elementos seleccionados. Para aplicar una selección a un elemento en el modelo, la selección debe enviarse al modelo de selección; esto se puede lograr de varias maneras, cada una de las cuales tiene un efecto diferente en las selecciones que ya están en el modelo de selección.

1. Seleccionar elemento

Para demostrar algunas de las características principales de la selección, creamos una instancia de un modelo de tabla personalizado con un total de 32 elementos y luego abrimos una vista de tabla para ver los datos que contiene:

TableModel *model = new TableModel(8, 4, &app);

QTableView *table = new QTableView(0);
table->setModel(model);

QItemSelectionModel *selectionModel = table->selectionModel();

El modelo de selección predeterminado para la vista de tabla se recuperará para su uso posterior. En lugar de modificar los elementos del modelo, seleccionamos algunos elementos que se mostrarán en la esquina superior izquierda de la tabla. Para ello, necesitamos obtener el índice del modelo correspondiente a los elementos superior izquierdo e inferior derecho del área a seleccionar:

QModelIndex topLeft;
QModelIndex bottomRight;

topLeft = model->index(0, 0, QModelIndex());
bottomRight = model->index(5, 2, QModelIndex());

Para seleccionar estos elementos en el modelo y ver los cambios correspondientes en la vista de tabla, se debe construir un objeto de selección y luego aplicarlo al modelo de selección:

QItemSelection selection(topLeft, bottomRight);
selectionModel->select(selection, QItemSelectionModel::Select);

Aplique la selección al modelo de selección usando el comando definido por la combinación de banderas de selección.

En este caso, la bandera utilizada hace que los elementos registrados en el objeto de selección se incluyan en el modelo de selección, independientemente de su estado anterior. La selección resultante se muestra en la vista.
inserte la descripción de la imagen aquí

La selección de elementos del elemento se puede modificar a través de varias operaciones definidas por las banderas de selección. Las elecciones producidas por estas operaciones pueden tener estructuras complejas, pero el modelo de elección representa efectivamente estas elecciones. Cuando aprenda a actualizar una selección, cubriremos cómo usar las diferentes banderas de selección para manipular los elementos seleccionados.

2. Leer el estado de selección

El índice del modelo almacenado en el modelo de selección se puede leer usando la función selectedindex(). Esta función devuelve una lista desordenada de índices de modelos, que se pueden iterar siempre que sepamos para qué modelo son:

const QModelIndexList indexes = selectionModel->selectedIndexes();

for (const QModelIndex &index : indexes) {
    
    
	QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
	model->setData(index, text);
}

El código anterior usa un bucle for basado en rango para iterar y modificar el elemento en el índice devuelto por el modelo de selección.

El modelo de selección emite señales para indicar cambios en la selección. Notifican a otros componentes sobre los cambios en la selección general y el elemento enfocado actualmente en el modelo de elemento. Podemos conectar la señal selectionChanged() a una función de ranura y verificar qué elemento del modelo está marcado o desmarcado cuando cambia la selección.

La función de ranura se llama con dos objetos QItemSelection: uno que contiene una lista de índices correspondientes a los elementos recién seleccionados; el otro que contiene los índices correspondientes a los elementos recién deseleccionados.

En el código a continuación, proporcionamos una ranura que recibe la señal de selectionChanged(), llena el elemento seleccionado con una cadena y borra el contenido del elemento deseleccionado.

void MainWindow::updateSelection(const QItemSelection &selected, const QItemSelection &deselected)
{
    
    
	QModelIndexList items = selected.indexes();

	for (const QModelIndex &index : qAsConst(items)) {
    
    
		QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
		model->setData(index, text);
	}

	items = deselected.indexes();

	for (const QModelIndex &index : qAsConst(items)) {
    
    
		model->setData(index, QString());
}

Podemos realizar un seguimiento del elemento enfocado actualmente conectando la señal currentChanged() a una función de ranura llamada con dos índices de modelo. Estos corresponden al elemento enfocado anterior y al elemento actualmente enfocado, respectivamente.

En el siguiente código, proporcionamos una ranura que recibe la señal currentChanged() y utilizamos la información proporcionada para actualizar la barra de estado de QMainWindow:

void MainWindow::changeCurrent(const QModelIndex &current, const QModelIndex &previous)
{
    
    
	statusBar()->showMessage(tr("Moved from (%1,%2) to (%3,%4)")
		.arg(previous.row())
		.arg(previous.column())
		.arg(current.row()).arg(current.column()));
}

Las selecciones realizadas por el usuario se pueden monitorear directamente a través de estas señales, pero también podemos actualizar el modelo de selección directamente.

3. Actualizar la selección

Los comandos de selección son proporcionados por combinaciones de banderas de selección, definidas por QItemSelectionModel::SelectionFlag. Cada indicador de selección le dice al modelo de selección cómo actualizar el registro interno de los elementos seleccionados cuando se llama a cualquiera de las funciones select(). El indicador más utilizado es el indicador Seleccionar, que indica al modelo de selección que registre el elemento especificado como seleccionado. El indicador de alternar hace que el modelo de selección invierta el estado de los elementos especificados, es decir, seleccione los elementos no seleccionados proporcionados y anule la selección de los elementos seleccionados actualmente. El indicador Deseleccionar se utiliza para deseleccionar todos los elementos especificados.

Los elementos individuales del modelo de selección se actualizan creando una selección de elementos y aplicándola al modelo de selección. En el siguiente código, aplicamos una segunda selección de elementos al modelo de tabla que se muestra arriba, usando el comando Alternar para alternar el estado de selección de un elemento determinado.

QItemSelection toggleSelection;

topLeft = model->index(2, 1, QModelIndex());
bottomRight = model->index(7, 3, QModelIndex());
toggleSelection.select(topLeft, bottomRight);

selectionModel->select(toggleSelection, QItemSelectionModel::Toggle);

Los resultados de la operación se muestran en una vista de tabla, lo que proporciona una forma conveniente de visualización:
inserte la descripción de la imagen aquí
de forma predeterminada, los comandos de selección solo operan en un único elemento especificado por el índice del modelo. Sin embargo, las banderas utilizadas para describir los comandos de selección se pueden combinar con otras banderas para cambiar filas y columnas enteras. Por ejemplo, si llama a select() con solo un índice, pero con una combinación de select y Rows, se seleccionará toda la fila que contiene el elemento al que se hace referencia. El siguiente código demuestra cómo usar las banderas de Filas y Columnas:

QItemSelection columnSelection;

topLeft = model->index(0, 1, QModelIndex());
bottomRight = model->index(0, 2, QModelIndex());

columnSelection.select(topLeft, bottomRight);

selectionModel->select(columnSelection,
QItemSelectionModel::Select | QItemSelectionModel::Columns);

QItemSelection rowSelection;

topLeft = model->index(0, 0, QModelIndex());
bottomRight = model->index(1, 0, QModelIndex());

rowSelection.select(topLeft, bottomRight);

selectionModel->select(rowSelection,
QItemSelectionModel::Select | QItemSelectionModel::Rows);

Aunque solo se proporcionan 4 índices para el modelo de selección, el uso de las banderas de selección de fila y columna significa que se seleccionan dos columnas y dos filas. La siguiente imagen muestra el resultado de ambas selecciones:
inserte la descripción de la imagen aquí

Todos los comandos ejecutados en el modelo de ejemplo implican la acumulación de elementos seleccionados en el modelo. También puede borrar la selección o reemplazar la selección actual por una nueva.

Para reemplazar la selección actual por una nueva, los otros marcadores de selección deben fusionarse con el actual. Los comandos que usan este indicador indican al modelo de selección que reemplace el conjunto actual de índices del modelo con los índices especificados al llamar a select(). Para borrar todas las selecciones antes de comenzar a agregar nuevas selecciones, combine el indicador de otras selecciones con el indicador de borrar. Esto tiene el efecto de restablecer la colección de índices de modelo para el modelo seleccionado.

4. Seleccione todos los elementos del modelo

Para seleccionar todos los elementos del modelo, se debe crear una selección para cada capa del modelo que cubra todos los elementos de esa capa. Para hacer esto, los índices correspondientes a los elementos superior izquierdo e inferior derecho se pueden recuperar utilizando un índice principal dado:

QModelIndex topLeft = model->index(0, 0, parent);
QModelIndex bottomRight = model->index(model->rowCount(parent)-1,
model->columnCount(parent)-1, parent);

Utilice estos indicadores y modelos para la selección. A continuación, seleccione el elemento correspondiente en el modelo de selección:

QItemSelection selection(topLeft, bottomRight);
selectionModel->select(selection, QItemSelectionModel::Select);

Esto debe hacerse para todos los niveles del modelo. Para los elementos de nivel superior, definiremos el índice principal de la forma habitual:

QModelIndex parent = QModelIndex();

En un modelo jerárquico, la función hasChildren() se usa para determinar si un elemento es un elemento principal de otro elemento.

2. Crea un nuevo modelo

La separación funcional entre los componentes modelo/vista permite la creación de modelos que pueden aprovechar las vistas existentes. Este enfoque nos permite usar componentes GUI estándar como QListView, QTableView y QTreeView para presentar datos de varias fuentes.

La clase QAbstractItemModel proporciona una interfaz lo suficientemente flexible como para admitir fuentes de datos que organizan la información en una jerarquía, lo que permite que los datos se inserten, eliminen, modifiquen u ordenen de alguna manera. También admite arrastrar y soltar.

Las clases QAbstractListModel y QAbstractTableModel brindan soporte de interfaz para estructuras de datos no jerárquicas más simples y son más fáciles de usar como puntos de partida para modelos de tablas y listas simples.

En esta sección, crearemos un modelo simple de solo lectura para explorar los principios básicos de la arquitectura modelo/vista. Más adelante en esta sección, adaptaremos este modelo simple para que los usuarios puedan modificar elementos.

1. Diseña un modelo

Al crear un nuevo modelo para una estructura de datos existente, es importante considerar qué tipo de modelo se debe usar para proporcionar una interfaz a los datos. Si la estructura de datos se puede representar como una lista o tabla de elementos, se pueden crear subclases de QAbstractListModel o QAbstractTableModel, ya que estas clases proporcionan implementaciones predeterminadas adecuadas para muchas funciones.

Sin embargo, si la estructura de datos subyacente solo se puede representar mediante una estructura de árbol jerárquico, es necesario crear una subclase de QAbstractItemModel. El ejemplo del modelo de árbol simple adopta este enfoque.

En esta sección, implementaremos un modelo simple basado en una lista de cadenas, por lo que QAbstractListModel proporciona una clase base ideal para construir.

Independientemente de la forma que adopte la estructura de datos subyacente, suele ser una buena idea complementar la API QAbstractItemModel estándar con una API que permita un acceso más natural a la estructura de datos subyacente en modelos especializados. Esto hace que sea más fácil llenar el modelo con datos, pero aún permite que otros componentes genéricos de modelo/vista interactúen con él usando la API estándar. El modelo que se describe a continuación proporciona un constructor personalizado para este propósito.

2. Modelo de ejemplo de solo lectura

El modelo implementado aquí es un modelo de datos simple, no jerárquico y de solo lectura basado en la clase QStringListModel estándar. Tiene una QStringList como fuente de datos interna e implementa solo lo que se necesita para crear un modelo funcional. Para facilitar la implementación, subclasificamos QAbstractListModel porque define un comportamiento predeterminado sensible para el modelo de lista y expone una interfaz más simple que la clase QAbstractItemModel.

Al implementar el modelo, es importante recordar que QAbstractItemModel no almacena ningún dato en sí mismo, solo proporciona la interfaz que usa la vista para acceder a los datos. Para un modelo mínimo de solo lectura, solo es necesario implementar unas pocas funciones, ya que la mayoría de las interfaces tienen implementaciones predeterminadas. La declaración de clase es la siguiente:

class StringListModel : public QAbstractListModel
{
    
    
Q_OBJECT

public:
StringListModel(const QStringList &strings, QObject *parent = nullptr)
: QAbstractListModel(parent), stringList(strings) {
    
    }

int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;

private:
QStringList stringList;
};

Además del constructor del modelo, solo necesitamos implementar dos funciones: rowCount() devuelve el número de filas del modelo y data() devuelve un elemento de datos correspondiente al índice del modelo especificado.

Los modelos de buen comportamiento también implementan headerData() para proporcionar vistas de árbol y tablas con contenido para mostrar en sus encabezados.

Tenga en cuenta que este es un modelo no jerárquico, por lo que no tenemos que preocuparnos por las relaciones padre-hijo. Si nuestro modelo es jerárquico, también debemos implementar las funciones index() y parent().

La lista de cadenas se almacena internamente en la variable miembro privada stringList.

1. El tamaño del modelo.

Queremos que el número de filas en el modelo sea el mismo que el número de cadenas en la lista de cadenas. Implementamos la función rowCount() así:

int StringListModel::rowCount(const QModelIndex &parent) const
{
    
    
return stringList.count();
}

Dado que el modelo no es jerárquico, podemos ignorar con seguridad el índice del modelo correspondiente al padre. De forma predeterminada, los modelos derivados de QAbstractListModel contienen solo una columna, por lo que no es necesario volver a implementar la función columnCount().

2. encabezado y datos del modelo

Para los elementos a la vista, queremos devolver las cadenas en la lista de cadenas. La función data() es responsable de devolver el elemento de datos correspondiente al parámetro de índice:

QVariant StringListModel::data(const QModelIndex &index, int role) const
{
    
    
if (!index.isValid())
return QVariant();

if (index.row() >= stringList.size())
return QVariant();

if (role == Qt::DisplayRole)
return stringList.at(index.row());
else
return QVariant();
}

Devolvemos una QVariant válida solo si el índice del modelo proporcionado es válido, el número de fila está dentro del rango de entradas en la lista de cadenas y la función solicitada es una que admitimos.
Algunas vistas, como QTreeView y QTableView, pueden mostrar el título junto con los datos del elemento. Si nuestro modelo se muestra en una vista con un encabezado, queremos que el encabezado muestre los números de fila y columna. Podemos proporcionar información sobre los archivos de encabezado mediante la subclasificación de la función headerData():

QVariant StringListModel::headerData(int section, Qt::Orientation orientation,
 int role) const
{
    
    
if (role != Qt::DisplayRole)
return QVariant();

if (orientation == Qt::Horizontal)
return QStringLiteral("Column %1").arg(section);
else
return QStringLiteral("Row %1").arg(section);
}

Del mismo modo, devolvemos una QVariant válida solo si el carácter es uno que admitimos. Considere también la orientación del encabezado cuando decida los datos exactos a devolver.

No todas las vistas muestran encabezados con datos de elementos y las que lo hacen pueden configurarse para ocultarlos. Sin embargo, se recomienda que implementemos la función headerData() para proporcionar información relevante sobre los datos proporcionados por el modelo.

Un elemento puede tener varios roles y se proporcionarán diferentes datos de acuerdo con los roles especificados. Los elementos de nuestro modelo solo tienen una función, DisplayRole, por lo que devolvemos los datos del elemento independientemente de la función asignada. Sin embargo, podemos reutilizar los datos proporcionados para DisplayRole en otras funciones, por ejemplo, la vista puede usar ToolTipRole para mostrar la información del elemento en la información sobre herramientas.

3. Modelo editable

El modelo de solo lectura muestra cómo presentar selecciones simples al usuario, pero para muchas aplicaciones el modelo de lista editable es más útil. Podemos modificar el modelo de solo lectura para hacer que el elemento sea editable modificando la función data() que implementamos para solo lectura e implementando dos funciones adicionales: flags() y setData(). Las siguientes declaraciones de función se agregan a la definición de clase:

Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value,
 int role = Qt::EditRole) override;

1. Haz que el modelo sea editable

El delegado comprueba si un elemento es editable antes de crear un editor. El modelo debe informar al delegado que sus elementos son editables. Hacemos esto devolviendo las banderas correctas para cada elemento en el modelo; en este caso, habilitamos todos los elementos y los hacemos seleccionables y editables:

Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const
{
    
    
if (!index.isValid())
return Qt::ItemIsEnabled;

return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}

Tenga en cuenta que no necesitamos saber cómo el delegado realiza el proceso de edición real. Solo necesitamos proporcionar una forma para que el delegado establezca los datos en el modelo. Esto se logra con la función setData():

bool StringListModel::setData(const QModelIndex &index,
const QVariant &value, int role)
{
    
    
if (index.isValid() && role == Qt::EditRole) {
    
    

stringList.replace(index.row(), value.toString());
emit dataChanged(index, index, {
    
    role});
return true;
}
return false;
}

En este modelo, la entrada en la lista de cadenas correspondiente al índice del modelo se reemplaza con el valor proporcionado. Sin embargo, antes de modificar una lista de cadenas, debe asegurarse de que el índice sea válido, que los elementos sean del tipo correcto y que se admitan las funciones. Por convención, nos ceñimos al rol de EditRole, ya que eso es lo que usa el delegado de elemento estándar.

Sin embargo, para valores booleanos podemos usar Qt::CheckStateRole y configurar el indicador Qt::ItemIsUserCheckable; luego use una casilla de verificación para editar el valor. Los datos subyacentes en este modelo son los mismos para todos los actores, por lo que este detalle facilita la integración del modelo con componentes estándar.

Después de configurar los datos, el modelo debe informar a la vista que algunos datos han cambiado. Esto se hace emitiendo la señal datachchanged(). Dado que solo ha cambiado un elemento de datos, el rango de elementos especificado en la señal está limitado a un índice de modelo.

Además, la función data() debe cambiarse para agregar la prueba Qt::EditRole:

QVariant StringListModel::data(const QModelIndex &index, int role) const
{
    
    
if (!index.isValid())
return QVariant();

if (index.row() >= stringList.size())
return QVariant();

if (role == Qt::DisplayRole || role == Qt::EditRole)
return stringList.at(index.row());
else
return QVariant();
}

2. Insertar y eliminar filas

El número de filas y columnas en el modelo se puede cambiar. En el modelo de lista de cadenas, solo tiene sentido cambiar el número de filas, por lo que solo reimplementamos las funciones para insertar y eliminar filas. Estos se declaran en la definición de clase:

bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;

Dado que las filas en este modelo corresponden a cadenas en la lista, la función insertRows() inserta algunas cadenas vacías en la lista de cadenas antes de la posición especificada. El número de cadenas insertadas es igual al número especificado de filas.

El índice principal se usa normalmente para determinar en qué parte del modelo se debe agregar la fila. En este ejemplo, solo tenemos una lista de cadenas de nivel superior, por lo que solo necesitamos insertar cadenas vacías en la lista.

bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent)
{
    
    
beginInsertRows(QModelIndex(), position, position+rows-1);

for (int row = 0; row < rows; ++row) {
    
    
stringList.insert(position, "");
}

endInsertRows();
return true;
}

El modelo primero llama a la función beginInsertRows() para notificar a otros componentes la cantidad de filas que cambiarán. La función especifica los números de fila de la primera y la última fila para insertar y el índice del modelo de su padre. Después de cambiar la lista de cadenas, llama a endInsertRows() para completar la operación y notificar a otros componentes que se cambiaron las dimensiones del modelo, devolviendo verdadero para indicar el éxito.

Las funciones que eliminan filas del modelo también son fáciles de escribir. La fila que se eliminará del modelo se especifica mediante la posición dada y el número de filas. Para simplificar la implementación, ignoramos el índice principal y simplemente eliminamos el elemento correspondiente de la lista de cadenas.

 bool StringListModel::removeRows(int position, int rows, const QModelIndex &parent)
{
    
    
beginRemoveRows(QModelIndex(), position, position+rows-1);

for (int row = 0; row < rows; ++row) {
    
    
stringList.removeAt(position);
}

endRemoveRows();
return true;
}

Siempre se llama a la función beginRemoveRows() antes de que se eliminen los datos subyacentes y especifica la primera y la última fila que se eliminarán. Esto permite que otros componentes accedan a los datos antes de que no estén disponibles. Después de eliminar filas, el modelo emite endRemoveRows() para completar la operación e informar a otros componentes que las dimensiones del modelo han cambiado.

3. Próximos pasos

Podemos mostrar los elementos del modelo en una lista vertical utilizando la clase QListView, mostrando así los datos proporcionados por ese modelo o cualquier otro modelo. Para el modelo de lista de cadenas, la vista también proporciona un editor predeterminado para que se pueda operar el elemento. En la clase de vista comprobamos las posibilidades que ofrecen las clases de vista estándar.

El documento de referencia de subclases de modelos analiza los requisitos de las subclases de QAbstractItemModel con más detalle y brinda orientación sobre las funciones virtuales que deben implementarse para habilitar varias características en diferentes tipos de modelos.

3. Clase de conveniencia de vista basada en artículos

Los controles basados ​​en elementos tienen nombres que reflejan su propósito: QListWidget proporciona una lista de elementos, QTreeWidget muestra una estructura de árbol de varios niveles y QTableWidget proporciona una tabla de elementos de celda. Cada clase hereda el comportamiento de la clase QAbstractItemView, que implementa el comportamiento general de selección de elementos y gestión de títulos.

1. QList Widget

Una lista de elementos de un solo nivel generalmente se muestra usando un QListWidget y algunos QListWidgetItems. Los controles de lista se construyen de la misma manera que otros controles:

QListWidget *listWidget = new QListWidget(this);

Los elementos de la lista se pueden agregar directamente al control de la lista después de la construcción:

new QListWidgetItem(tr("Sycamore"), listWidget);
new QListWidgetItem(tr("Chestnut"), listWidget);
new QListWidgetItem(tr("Mahogany"), listWidget);

También se pueden construir sin un widget de lista principal y agregarse a la lista en un momento posterior:

QListWidgetItem *newItem = new QListWidgetItem;
newItem->setText(itemText);
listWidget->insertItem(row, newItem);

Cada elemento de la lista puede mostrar una etiqueta de texto y un icono. El color y la fuente utilizados para representar el texto se pueden cambiar para darle al elemento un aspecto personalizado. La información sobre herramientas, las indicaciones de estado y la ayuda "¿Qué es esto?" se pueden configurar fácilmente para garantizar que la lista se integre correctamente en la aplicación.

 newItem->setToolTip(toolTipText);
newItem->setStatusTip(toolTipText);
newItem->setWhatsThis(whatsThisText);

De forma predeterminada, los elementos de la lista se muestran en el orden en que se crearon. La lista de elementos se puede ordenar de acuerdo con los criterios proporcionados en Qt::SortOrder para generar una lista de elementos ordenada alfabéticamente:

listWidget->sortItems(Qt::AscendingOrder);
listWidget->sortItems(Qt::DescendingOrder);

2. Control QTreeWidget

Las clases QTreeWidget y QTreeWidgetItem proporcionan un árbol o una lista jerárquica de elementos. Cada elemento de un control de árbol puede tener sus propios elementos secundarios y puede mostrar información para muchas columnas. Cree un control de ventana de árbol como cualquier otro control de ventana:

 QTreeWidget *treeWidget = new QTreeWidget(this);

El número de columnas debe establecerse antes de agregar elementos al control de árbol. Por ejemplo, podemos definir dos columnas y crear un encabezado para proporcionar etiquetas en la parte superior de cada columna:

treeWidget->setColumnCount(2);
QStringList headers;
headers << tr("Subject") << tr("Default");
treeWidget->setHeaderLabels(headers);

La forma más sencilla de establecer etiquetas para cada sección es proporcionar una lista de cadenas. Para encabezados más complejos, podemos construir un elemento de árbol, decorarlo como deseemos y usarlo como encabezado del control de árbol.

Los elementos de nivel superior en un control de árbol se construyen con el control de árbol como elemento primario. Se pueden insertar en cualquier orden, o podemos asegurarnos de que se enumeran en un orden específico especificando el elemento anterior al construir cada elemento:

QTreeWidgetItem *cities = new QTreeWidgetItem(treeWidget);
cities->setText(0, tr("Cities"));
QTreeWidgetItem *osloItem = new QTreeWidgetItem(cities);
osloItem->setText(0, tr("Oslo"));
osloItem->setText(1, tr("Yes"));

QTreeWidgetItem *planets = new QTreeWidgetItem(treeWidget, cities);

El control de árbol maneja los elementos de nivel superior de forma ligeramente diferente a otros elementos más profundos en el árbol. Los elementos se pueden eliminar del nivel superior del árbol llamando a la función takeTopLevelItem() del control del árbol, pero los elementos se eliminan de los niveles inferiores llamando a la función takeChild() de su elemento principal. Utilice la función insertTopLevelItem() para insertar elementos en el nivel superior del árbol. En los niveles más bajos del árbol, use la función insertChild() del padre.

Mover elementos entre los niveles superior e inferior del árbol es fácil. Solo necesitamos verificar si el elemento es un elemento de nivel superior, esta información es proporcionada por la función principal () de cada elemento. Por ejemplo, podemos eliminar el elemento actual en un control de árbol independientemente de su posición:

     QTreeWidgetItem *parent = currentItem->parent();
      int index;

      if (parent) {
    
    
          index = parent->indexOfChild(treeWidget->currentItem());
          delete parent->takeChild(index);
      } else {
    
    
          index = treeWidget->indexOfTopLevelItem(treeWidget->currentItem());
          delete treeWidget->takeTopLevelItem(index);
      }

La inserción de elementos en otra parte del control de árbol sigue el mismo patrón:

      QTreeWidgetItem *parent = currentItem->parent();
      QTreeWidgetItem *newItem;
      if (parent)
          newItem = new QTreeWidgetItem(parent, treeWidget->currentItem());
      else
          newItem = new QTreeWidgetItem(treeWidget, treeWidget->currentItem());

3. QTableWidget

Las tablas de elementos similares a las que se encuentran en las aplicaciones de hojas de cálculo se construyen utilizando QTableWidget y QTableWidgetItem. Proporcionan un control de tabla de desplazamiento con encabezados y elementos para usar en él.

Las tablas se pueden crear con un cierto número de filas y columnas, o se pueden agregar a tablas de tamaño indeterminado según sea necesario.

     QTableWidget *tableWidget;
      tableWidget = new QTableWidget(12, 3, this);

Los artículos se construyen fuera de la mesa y luego se agregan a la mesa en la ubicación deseada:

   QTableWidgetItem *newItem = new QTableWidgetItem(tr("%1").arg(
          pow(row, column+1)));
      tableWidget->setItem(row, column, newItem);

Se pueden agregar encabezados horizontales y verticales a una tabla construyendo elementos fuera de la tabla y usándolos como encabezados:

      QTableWidgetItem *valuesHeaderItem = new QTableWidgetItem(tr("Values"));
      tableWidget->setHorizontalHeaderItem(0, valuesHeaderItem);

Tenga en cuenta que las filas y columnas de la tabla comienzan desde 0.

4. Características comunes

Cada clase de conveniencia tiene una serie de funciones públicas basadas en elementos disponibles a través de la misma interfaz en cada clase. Describimos estas características en las siguientes secciones y proporcionamos algunos ejemplos de los diferentes controles. Consulte la lista de clases de modelo/vista para cada control para obtener más detalles sobre cada función utilizada.

1. Elementos ocultos

A veces es útil ocultar elementos en el control de vista de elementos en lugar de eliminarlos. Los elementos de todos los controles anteriores se pueden ocultar y mostrar más tarde. Podemos determinar si un elemento está oculto llamando a la función isItemHidden() y podemos usar setItemHidden() para ocultar un elemento.
Dado que esta operación se basa en artículos, las mismas funciones están disponibles para las tres clases de conveniencia.

2. Seleccione

La forma en que se selecciona un elemento está controlada por el modo de selección del control (QAbstractItemView::SelectionMode). Esta propiedad controla si el usuario puede seleccionar uno o más elementos y, en las selecciones de varios elementos, si la selección debe ser un rango de elementos contiguos. El modo de selección funciona de la misma manera para todos los controles anteriores.

Selección única : el modo de selección única predeterminado es el más apropiado cuando el usuario necesita seleccionar un solo elemento del control. En este modo, el elemento actual y el elemento seleccionado son iguales.
inserte la descripción de la imagen aquí

Selección de elementos múltiples : en este modo, el usuario puede alternar el estado de selección de cualquier elemento en el control sin cambiar la selección existente, de la misma manera que las casillas de verificación no exclusivas se pueden alternar de forma independiente.
inserte la descripción de la imagen aquí

Selección extendida : los controles que normalmente requieren la selección de muchos elementos adyacentes, como los controles en una hoja de cálculo, deben usar el modo de selección extendida. En este modo, se puede seleccionar un rango contiguo de elementos en el control con el mouse y el teclado. Si se utilizan teclas modificadoras, también se pueden crear selecciones complejas, incluidos muchos elementos que no son adyacentes a otros elementos seleccionados en el control.
inserte la descripción de la imagen aquí

Si el usuario selecciona un elemento sin usar una tecla modificadora, borra la selección existente.

Utilice la función selectedItems() para leer los elementos seleccionados en el control y proporcionar una lista iterable de elementos relacionados. Por ejemplo, podemos encontrar la suma de todos los valores en la lista de elementos seleccionados con el siguiente código:

      const QList<QTableWidgetItem *> selected = tableWidget->selectedItems();
      int number = 0;
      double total = 0;

      for (QTableWidgetItem *item : selected) {
    
    
          bool ok;
          double value = item->text().toDouble(&ok);

          if (ok && !item->text().isEmpty()) {
    
    
              total += value;
              number++;
          }
      }

Tenga en cuenta que para el modo de selección única, el elemento actual estará en la selección. En los modos de selección múltiple y selección extendida, es posible que el elemento actual no esté en la selección, dependiendo de cómo el usuario forme la selección.

3. Buscar

Suele ser útil poder buscar elementos en un control de vista de elementos, ya sea como desarrollador o como un servicio proporcionado a los usuarios. Las tres clases de conveniencia de vista de elementos proporcionan una función común findItems() para que sea lo más consistente y simple posible.

Busca el texto contenido en las entradas según los criterios especificados por un conjunto de valores en Qt::MatchFlags. Podemos obtener una lista de elementos coincidentes usando la función findItems():

      const QList<QTreeWidgetItem *> found = treeWidget->findItems(
          itemText, Qt::MatchWildcard);

      for (QTreeWidgetItem *item : found) {
    
    
          item->setSelected(true);
          // Show the item->text(0) for each item.
      }

El código anterior hace que se seleccione el elemento en el control de árbol cuando contiene el texto proporcionado en la cadena de búsqueda. Este modo también está disponible para controles de lista y tabla.

4. Use la vista de elemento de arrastrar y soltar

La infraestructura de arrastrar y soltar de Qt es totalmente compatible con el marco modelo/vista. Los elementos de listas, tablas y árboles se pueden arrastrar en la vista, y los datos se pueden importar y exportar como datos codificados en MIME.

Las vistas estándar admiten automáticamente arrastrar y soltar internamente, lo que permite mover elementos para cambiar el orden en que se muestran. De forma predeterminada, estas vistas no admiten arrastrar y soltar, ya que están configuradas para el uso más simple y común. Para permitir el arrastre de un elemento, ciertas propiedades de la vista deben estar habilitadas y el elemento mismo también debe permitir el arrastre.
Un modelo que solo permite exportar elementos desde la vista y no permite que se coloquen datos en él tiene menos necesidades que un modelo de arrastrar y soltar totalmente habilitado.

1. Vista fácil de usar

De manera predeterminada, cada tipo de elemento utilizado por QListWidget, QTableWidget y QTreeWidget está configurado para usar un conjunto diferente de indicadores. Por ejemplo, cada QListWidgetItem o QTreeWidgetItem está inicialmente habilitado, inspeccionable, seleccionable y se puede usar como fuente para operaciones de arrastrar y soltar; cada QTableWidgetItem también se puede editar y usar como destino para operaciones de arrastrar y soltar.

Si bien todos los elementos estándar establecen una bandera o dos para arrastrar y soltar, normalmente necesitará establecer varias propiedades en la vista para aprovechar la compatibilidad integrada con arrastrar y soltar:

  • Para habilitar el arrastre de elementos, establezca la propiedad dragEnabled de la vista en verdadero.
  • Para permitir que los usuarios suelten elementos internos o externos en una vista, establezca la propiedad acceptDrops del viewport() de la vista en verdadero.
  • Para mostrar al usuario dónde estaba el elemento soltado actualmente cuando se soltó, establezca la propiedad showDropIndicator de la vista. Esto proporciona al usuario información continuamente actualizada sobre la posición del elemento en la vista.

Por ejemplo, podemos habilitar la funcionalidad de arrastrar y soltar en un control de lista con la siguiente línea de código:

  QListWidget *listWidget = new QListWidget(this);
  listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
  listWidget->setDragEnabled(true);
  listWidget->viewport()->setAcceptDrops(true);
  listWidget->setDropIndicatorShown(true);

El resultado es un control de lista que permite copiar elementos dentro de las vistas e incluso permite a los usuarios arrastrar elementos entre vistas que contienen el mismo tipo de datos. En ambos casos, los elementos se copian en lugar de moverse.

Para que el usuario pueda mover elementos en la vista, debemos configurar el modo arrastrarDropMode del control de lista:

listWidget->setDragDropMode(QAbstractItemView::InternalMove);

1. Usar clases de modelo/vista

Las vistas configuradas para arrastrar y soltar siguen el mismo patrón que las vistas de conveniencia. Por ejemplo, un QListView se puede configurar de la misma manera que un QListWidget:

  QListView *listView = new QListView(this);
  listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
  listView->setDragEnabled(true);
  listView->setAcceptDrops(true);
  listView->setDropIndicatorShown(true);

Dado que el modelo controla el acceso a los datos que muestra la vista, el modelo utilizado también debe admitir operaciones de arrastrar y soltar. Las operaciones admitidas por el modelo se pueden especificar volviendo a implementar la función QAbstractItemModel::supporteddroptions(). Por ejemplo, use el siguiente código para habilitar las operaciones de copiar y mover:

  Qt::DropActions DragDropListModel::supportedDropActions() const
  {
    
    
      return Qt::CopyAction | Qt::MoveAction;
  }

Aunque se puede dar cualquier combinación de valores en Qt::droptions, el modelo debe escribirse para admitirlos. Por ejemplo, para permitir que Qt::MoveAction se use correctamente con un modelo de lista, el modelo debe proporcionar una implementación de QAbstractItemModel::removeRows(), ya sea directamente o heredada de una clase base.

2. Admite arrastrar y soltar para el artículo

Al volver a implementar la función QAbstractItemModel::flags() para proporcionar las banderas adecuadas, el modelo indica a la vista qué elementos se pueden arrastrar y cuáles se aceptan.

Por ejemplo, un modelo que proporciona una lista simple basada en QAbstractListModel puede permitir operaciones de arrastrar y soltar en cada elemento asegurándose de que los indicadores devueltos contengan los valores Qt::ItemIsDragEnabled y Qt::ItemIsDropEnabled:

  Qt::ItemFlags DragDropListModel::flags(const QModelIndex &index) const
  {
    
    
      Qt::ItemFlags defaultFlags = QStringListModel::flags(index);

      if (index.isValid())
          return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
      else
          return Qt::ItemIsDropEnabled | defaultFlags;
  }

Tenga en cuenta que los elementos se pueden arrastrar y soltar sobre el modelo, pero solo se pueden arrastrar los elementos válidos.

En el código anterior, dado que el modelo se deriva de QStringListModel, obtenemos un conjunto de indicadores predeterminados llamando a la implementación de la función flags().

3. Codifique los datos exportados

Cuando los elementos de datos se exportan desde el modelo mediante operaciones de arrastrar y soltar, se codifican en el formato apropiado para uno o más tipos de MIME. Los modelos declaran los tipos MIME que pueden usar para proporcionar elementos mediante la reimplementación de la función QAbstractItemModel::mimeTypes(), que devuelve una lista de tipos MIME estándar.
Por ejemplo, un modelo que solo proporciona texto sin formato proporcionaría la siguiente implementación:

  QStringList DragDropListModel::mimeTypes() const
  {
    
    
      QStringList types;
      types << "application/vnd.text.list";
      return types;
  }

El modelo también debe proporcionar código, codificando los datos en el formato de anuncio. Esto se logra al volver a implementar la función QAbstractItemModel::mimeData() para proporcionar un objeto QMimeData, como en cualquier otra operación de arrastrar y soltar.
El siguiente código muestra cómo codificar cada elemento de datos correspondiente a una lista de índice determinada en texto sin formato y almacenarlo en un objeto QMimeData.

  QMimeData *DragDropListModel::mimeData(const QModelIndexList &indexes) const
  {
    
    
      QMimeData *mimeData = new QMimeData;
      QByteArray encodedData;

      QDataStream stream(&encodedData, QIODevice::WriteOnly);

      for (const QModelIndex &index : indexes) {
    
    
          if (index.isValid()) {
    
    
              QString text = data(index, Qt::DisplayRole).toString();
              stream << text;
          }
      }

      mimeData->setData("application/vnd.text.list", encodedData);
      return mimeData;
  }

Dado que se proporciona una lista de índices de modelo a la función, este método es lo suficientemente general para trabajar con modelos jerárquicos y no jerárquicos.
Tenga en cuenta que los tipos de datos personalizados deben declararse como metaobjetos y deben implementarse operadores de flujo para ellos. Consulte la descripción de la clase QMetaObject para obtener más detalles.

4. Inserte los datos eliminados en el modelo

La forma en que cualquier modelo maneja los datos que faltan depende de su tipo (lista, tabla o árbol) y la forma en que su contenido puede presentarse al usuario. En general, el método utilizado para acomodar los datos faltantes debe ser el método más adecuado para el almacenamiento de datos subyacente del modelo.
Los diferentes tipos de modelos tienden a manejar los datos faltantes de diferentes maneras. Los modelos de lista y tabla simplemente proporcionan una estructura plana para almacenar elementos de datos. Por lo tanto, pueden insertar nuevas filas (y columnas) cuando se eliminan datos en elementos existentes en la vista, o pueden sobrescribir el contenido del elemento en el modelo con algunos datos proporcionados. Un modelo de árbol a menudo puede agregar elementos secundarios que contienen datos nuevos a su almacén de datos subyacente, por lo que su comportamiento es más predecible en lo que respecta al usuario.
Los datos descartados son manejados por la reimplementación de un modelo de QAbstractItemModel::dropMimeData(). Por ejemplo, un modelo que maneja listas simples de cadenas podría proporcionar una implementación que maneje los datos colocados en elementos existentes por separado de los datos colocados encima del modelo (es decir, los datos colocados en elementos no válidos).
Al volver a implementar QAbstractItemModel::canDropMimeData(), el modelo puede prohibir la eliminación de ciertos elementos o depender de los datos que se eliminen.
El modelo primero debe asegurarse de que se debe realizar la operación, que los datos proporcionados están en un formato consumible y que su destino en el modelo es válido:

  bool DragDropListModel::canDropMimeData(const QMimeData *data,
      Qt::DropAction action, int row, int column, const QModelIndex &parent)
  {
    
    
      Q_UNUSED(action);
      Q_UNUSED(row);
      Q_UNUSED(parent);

      if (!data->hasFormat("application/vnd.text.list"))
          return false;

      if (column > 0)
          return false;

      return true;
  }
  bool DragDropListModel::dropMimeData(const QMimeData *data,
      Qt::DropAction action, int row, int column, const QModelIndex &parent)
  {
    
    
      if (!canDropMimeData(data, action, row, column, parent))
          return false;

      if (action == Qt::IgnoreAction)
          return true;

Un modelo de lista de cadena de una sola columna simple puede indicar un error si los datos proporcionados no son texto sin formato o si se especifica un número de columna no válido para su eliminación.
Los datos que se van a insertar en el modelo se manejan de manera diferente dependiendo de si se colocan en un elemento existente. En este ejemplo simple, queremos permitir colocar elementos entre elementos existentes, antes del primer elemento de la lista y después del último elemento.

Cuando se produce una eliminación, el índice del modelo correspondiente al elemento principal es válido, lo que indica que la eliminación se produjo en un elemento, o no es válido, lo que indica que la eliminación se produjo en algún lugar de la vista correspondiente al nivel superior del modelo.

   int beginRow;

      if (row != -1)
          beginRow = row;

Primero verificamos el número de fila proporcionado para ver si se puede usar para insertar un elemento en el modelo, independientemente de si el índice principal es válido.

else if (parent.isValid())
          beginRow = parent.row();

La eliminación se produce en los elementos si el índice del modelo principal es válido. En este modelo de lista simple, encontramos el número de fila del elemento y usamos ese valor para insertar el elemento eliminado en el nivel superior del modelo.

     else
          beginRow = rowCount(QModelIndex());

Cuando se arrastra y suelta en otra parte de la vista y los números de línea no están disponibles, agregamos el elemento al nivel superior del modelo.
En un modelo jerárquico, cuando se arrastra y suelta un elemento, es mejor insertar el nuevo elemento en el modelo como elemento secundario de ese elemento. En el ejemplo simple que se muestra aquí, el modelo tiene solo un nivel, por lo que este enfoque no es adecuado.

5. Decodificar los datos importados

Cada implementación de dropMimeData() también debe decodificar los datos e insertarlos en la estructura de datos subyacente del modelo.
Para un modelo de lista de cadenas simple, los elementos codificados se pueden descodificar y transmitir a una QStringList:

      QByteArray encodedData = data->data("application/vnd.text.list");
      QDataStream stream(&encodedData, QIODevice::ReadOnly);
      QStringList newItems;
      int rows = 0;

      while (!stream.atEnd()) {
    
    
          QString text;
          stream >> text;
          newItems << text;
          ++rows;
      }

Luego, la cadena se puede insertar en el almacén de datos subyacente. Para mantener la coherencia, esto se puede hacer a través de la propia interfaz del modelo:

      insertRows(beginRow, rows, QModelIndex());
      for (const QString &text : qAsConst(newItems)) {
    
    
          QModelIndex idx = index(beginRow, 0, QModelIndex());
          setData(idx, text);
          beginRow++;
      }

      return true;
  }

Tenga en cuenta que, por lo general, el modelo necesita proporcionar implementaciones de las funciones QAbstractItemModel::insertRows() y QAbstractItemModel::setData().

5. Modelo de representación

En el marco modelo/vista, los elementos de datos proporcionados por un solo modelo pueden ser compartidos por cualquier cantidad de vistas, y cada vista puede representar la misma información de una manera completamente diferente. Las vistas personalizadas y los delegados son una forma efectiva de proporcionar representaciones completamente diferentes de los mismos datos. Sin embargo, las aplicaciones a menudo necesitan proporcionar vistas regulares para versiones procesadas de los mismos datos, como vistas que ordenan una lista de elementos de manera diferente.

Si bien puede parecer apropiado realizar las operaciones de clasificación y filtrado como funciones internas de la vista, este enfoque no permite que múltiples vistas compartan los resultados de tales operaciones potencialmente costosas. Otro enfoque (que implica la ordenación en el propio modelo) conduce a problemas similares, ya que cada vista debe mostrar elementos de datos organizados de acuerdo con las operaciones de procesamiento más recientes.

Para resolver este problema, el marco modelo/vista utiliza un modelo proxy para administrar la información proporcionada entre cada modelo y vista. Desde la perspectiva de la vista, un modelo proxy es un componente que se comporta como un modelo normal y accede a los datos del modelo de origen en nombre de la vista. Las señales y las ranuras utilizadas por el marco de modelo/vista garantizan que cada vista se actualice adecuadamente, sin importar cuántos modelos proxy se coloquen entre ella y el modelo de origen.

1. Usa el modelo proxy

Se puede insertar un modelo proxy entre un modelo existente y cualquier número de vistas. Qt proporciona un modelo de proxy estándar, QSortFilterProxyModel, que generalmente se crea una instancia y se usa directamente, pero también se puede subclasificar para proporcionar un comportamiento de clasificación y filtrado personalizado. La clase QSortFilterProxyModel se puede utilizar de las siguientes formas:

      QSortFilterProxyModel *filterModel = new QSortFilterProxyModel(parent);
      filterModel->setSourceModel(stringListModel);

      QListView *filteredView = new QListView;
      filteredView->setModel(filterModel);

Dado que los modelos proxy se heredan de QAbstractItemModel, se pueden conectar a cualquier tipo de vista y se pueden compartir entre vistas. También se pueden usar para procesar información obtenida de otros modelos de agentes en el arreglo de canalización.
La clase QSortFilterProxyModel está diseñada para ser instanciada y utilizada directamente en la aplicación. Se pueden crear modelos proxy más especializados subclasificando esta clase e implementando las operaciones de comparación requeridas.

2. Modelo de proxy personalizado

Por lo general, el tipo de procesamiento utilizado en un modelo proxy implica mapear cada elemento de datos desde su ubicación original en el modelo de origen a una ubicación diferente en el modelo proxy. En algunos modelos, algunos elementos pueden no tener una posición correspondiente en el modelo proxy; estos modelos son modelos proxy de filtrado. La vista accede a los elementos utilizando índices de modelo proporcionados por el modelo proxy, que no contienen información sobre el modelo de origen o la posición del elemento original dentro de ese modelo.

QSortFilterProxyModel permite el filtrado de datos en el modelo de origen antes de proporcionarlo a la vista y también permite que el contenido del modelo de origen se proporcione a la vista como datos preordenados.

1. Modelo de filtro personalizado

La clase QSortFilterProxyModel proporciona un modelo de filtrado bastante general que se puede usar en una variedad de situaciones comunes. Para usuarios avanzados, QSortFilterProxyModel se puede subclasificar para proporcionar un mecanismo que permita implementar filtros personalizados.
Las subclases de QSortFilterProxyModel pueden volver a implementar dos funciones virtuales que se llaman cuando se solicitan desde el modelo proxy o cuando se usa el índice del modelo:

  • filterAcceptsColumn() se usa para filtrar columnas específicas de parte del modelo de origen.
  • filterAcceptsRow() se usa para filtrar filas específicas de parte del modelo de origen.

Las implementaciones predeterminadas de las funciones anteriores en QSortFilterProxyModel devuelven verdadero para garantizar que todos los elementos se pasen a la vista; las reimplementaciones de estas funciones deben devolver falso para filtrar filas y columnas individuales.

2. Modelo de clasificación personalizado

Una instancia de QSortFilterProxyModel usa la función std::stable_sort() para configurar una asignación entre los elementos del modelo de origen y los elementos del modelo de proxy, lo que permite exponer una jerarquía de elementos ordenados a la vista sin modificar la estructura del modelo de origen. Para proporcionar un comportamiento de clasificación personalizado, la función lessThan() debe volver a implementarse para realizar comparaciones personalizadas.

3. Modelo de referencia de subclases

Las subclases de modelo son necesarias para proporcionar implementaciones de muchas de las funciones virtuales definidas en la clase base QAbstractItemModel. El número de estas funciones que deben implementarse depende del tipo de modelo, ya sea que proporcione una lista simple, una tabla o una jerarquía de elementos compleja para la vista. Los modelos que heredan de QAbstractListModel y QAbstractTableModel pueden aprovechar las implementaciones predeterminadas de las funciones proporcionadas por estas clases. Un modelo que expone elementos de datos en una estructura de árbol debe proporcionar implementaciones para muchas funciones virtuales en qab

6. Modelo de referencia de subclases

Las subclases de modelo son necesarias para proporcionar implementaciones de muchas de las funciones virtuales definidas en la clase base QAbstractItemModel. El número de estas funciones que deben implementarse depende del tipo de modelo, ya sea que proporcione una lista simple, una tabla o una jerarquía de elementos compleja para la vista. Los modelos que heredan de QAbstractListModel y QAbstractTableModel pueden aprovechar las implementaciones predeterminadas de las funciones proporcionadas por estas clases. Un modelo que expone elementos de datos en una estructura de árbol debe proporcionar implementaciones para muchas de las funciones virtuales en QAbstractItemModel.

Las funciones que deben implementarse en las subclases del modelo se pueden dividir en tres grupos:

  • **procesamiento de datos de elementos**: todos los modelos deben implementar una funcionalidad que permita a las vistas y delegados consultar las dimensiones del modelo, inspeccionar elementos y recuperar datos.
  • Navegación y creación de índices : los modelos jerárquicos deben proporcionar funciones que las vistas puedan llamar para navegar por la estructura de árbol que exponen y obtener el índice del modelo para un elemento.
  • Compatibilidad con arrastrar y soltar y manejo de tipos MIME : el modelo hereda funciones que controlan cómo se realizan las operaciones internas y externas de arrastrar y soltar. Estas funciones permiten que los elementos de datos se describan con tipos MIME que otros componentes y aplicaciones pueden comprender.

1. Procesamiento de datos del artículo

Los modelos pueden proporcionar diferentes niveles de acceso a los datos que proporcionan: pueden ser componentes simples de solo lectura, algunos modelos pueden admitir operaciones de cambio de tamaño, mientras que otros pueden permitir la edición de elementos.

2. Acceso de solo lectura

Para proporcionar acceso de solo lectura a los datos proporcionados por un modelo, se deben implementar las siguientes funciones en las subclases de modelo:

Nombre de la función función
banderas() Utilizado por otros componentes para obtener información sobre cada elemento proporcionado por el modelo. En muchos modelos, la combinación de indicadores debe incluir Qt::ItemIsEnabled y Qt::ItemIsSelectable.
datos() Se utiliza para proporcionar datos de elementos a vistas y delegados. En general, el modelo solo necesita proporcionar datos para Qt::DisplayRole y cualquier rol de usuario específico de la aplicación, pero también es una buena práctica proporcionar datos para Qt::ToolTipRole, Qt::AccessibleTextRole y Qt::AccessibleDescriptionRole. Consulte la documentación de la enumeración Qt::ItemDataRole para obtener información sobre los tipos asociados con cada rol.
datos de encabezado () Proporciona a la vista información para mostrar en su título. Solo las vistas que pueden mostrar información de encabezado pueden recuperar esta información.
número de filas () Proporciona el número de filas de datos expuestos por el modelo.

Las cuatro funciones anteriores deben implementarse en todos los tipos de modelos, incluidos los modelos de lista (subclases de QAbstractListModel) y los modelos de tabla (subclases de QAbstractTableModel).

Además, las siguientes funciones deben implementarse en subclases directas de QAbstractTableModel y QAbstractItemModel:

Nombre de la función función
recuento de columnas () Proporciona el número de columnas de datos expuestas por el modelo. El modelo de lista no proporciona esta función porque ya está implementada en QAbstractListModel.

1. Elementos editables

Un modelo editable permite la modificación de elementos de datos y también puede proporcionar funciones que permiten la inserción y eliminación de filas y columnas. Para habilitar la funcionalidad de edición, las siguientes funciones deben implementarse correctamente:

Nombre de la función función
banderas() Se debe devolver la combinación apropiada de banderas para cada artículo. Específicamente, el valor devuelto por esta función debe incluir Qt::ItemIsEditable además del valor que se aplica a los elementos en el modelo de solo lectura.
establecer datos () Se utiliza para modificar el elemento de datos asociado con el índice del modelo especificado. Para poder aceptar la entrada del usuario proporcionada por los elementos de la interfaz de usuario, esta función debe manejar datos relacionados con Qt::EditRole. Las implementaciones también pueden aceptar datos asociados con muchos tipos diferentes de roles especificados por Qt::ItemDataRole. Después de cambiar un elemento de datos, el modelo debe emitir la señal datachchanged() para notificar a otros componentes del cambio.
establecer datos de encabezado () Se utiliza para modificar la información del encabezado horizontal y vertical. Después de cambiar un elemento de datos, el modelo debe emitir la señal headerdatachchanged() para notificar a otros componentes del cambio.

2. Modelo redimensionable

Todos los tipos de modelos pueden admitir la inserción y eliminación de filas. Los modelos de tablas y los modelos jerárquicos también pueden admitir la inserción y eliminación de columnas. Es importante notificar a otros componentes antes y después de que cambie la dimensión del modelo. Por lo tanto, se pueden implementar las siguientes funciones para permitir que se cambie el tamaño del modelo, pero las implementaciones deben garantizar que se llame a las funciones adecuadas para notificar las vistas y los delegados adjuntos:

Nombre de la función función
insertRows() Se utiliza para agregar nuevas filas de datos y elementos a todo tipo de modelos. Las implementaciones deben llamar a beginInsertRows() antes de insertar nuevas filas en cualquier estructura de datos subyacente y endInsertRows() inmediatamente después.
eliminar filas () Se utiliza para eliminar filas y los elementos de datos que contienen de todos los tipos de modelos. Las implementaciones deben llamar a beginRemoveRows() antes de eliminar filas de cualquier estructura de datos subyacente y llamar a endRemoveRows() inmediatamente después.
insertarColumnas() Se utiliza para agregar nuevas columnas y elementos de datos al modelo de tabla y al modelo jerárquico. Las implementaciones deben llamar a beginInsertColumns() antes de insertar nuevas columnas en cualquier estructura de datos subyacente y endInsertColumns() inmediatamente después.
eliminarColumnas() Se utiliza para eliminar columnas y los elementos de datos que contienen del modelo de tabla y el modelo jerárquico. Las implementaciones deben llamar a beginRemoveColumns() antes de eliminar columnas de cualquier estructura de datos subyacente y llamar a endRemoveColumns() inmediatamente después.

Por lo general, estas funciones deberían devolver verdadero si la operación se realizó correctamente. En algunos casos, sin embargo, la operación puede tener un éxito parcial, por ejemplo, si se pueden insertar menos filas del número especificado. En este caso, el modelo debería devolver falso para indicar que no se puede habilitar ningún complemento para manejar este caso.

La señal emitida por la función llamada en la implementación de la API de cambio de tamaño le da al componente adjunto la oportunidad de tomar medidas antes de que los datos dejen de estar disponibles. Encapsular las operaciones de inserción y eliminación con funciones de inicio y fin también permite que el modelo administre correctamente los índices persistentes del modelo.

Por lo general, las funciones de inicio y finalización pueden notificar a otros componentes sobre cambios en la estructura subyacente del modelo. Para cambios más complejos en la estructura del modelo, que pueden implicar reorganización interna, ordenamiento de datos o cualquier otro cambio estructural, es necesario realizar la siguiente secuencia:

  • Diseño de la señalAboutToBeChanged()
  • Actualiza los datos internos que representan la estructura del modelo.
  • Use changePersistentIndexList() para actualizar índices persistentes
  • Emite la señal layoutChanged().

Esta secuencia se puede utilizar para cualquier actualización estructural en lugar de métodos de conservación más avanzados y convenientes. Por ejemplo, si un modelo con 2 millones de filas necesita quitar todas las filas impares, eso es 1 millón de rangos inconexos, cada uno con 1 elemento. Es posible usar beginRemoveRows y endRemoveRows 1 millón de veces, pero obviamente es ineficiente. En cambio, esto se puede expresar como un único cambio de diseño que actualiza todos los índices persistentes necesarios a la vez.

3. Retraso en el llenado de los datos del modelo

La población diferida de los datos del modelo permite que las solicitudes de información del modelo se retrasen hasta que la vista realmente lo necesite.

Algunos modelos necesitan obtener datos de fuentes remotas o deben realizar operaciones que consumen mucho tiempo para obtener información sobre cómo se organizan los datos. Dado que las vistas suelen solicitar la mayor cantidad de información posible para mostrar con precisión los datos del modelo, es útil limitar la cantidad de información que se devuelve a la vista para reducir las solicitudes de datos posteriores innecesarias.

En un modelo jerárquico, encontrar el número de elementos secundarios de un elemento dado es una operación costosa, por lo que es útil asegurarse de que la implementación de rowCount() del modelo solo se llame cuando sea necesario. En este caso, la función hasChildren() se puede volver a implementar para proporcionar vistas con una forma económica de verificar la presencia de niños y, en el caso de QTreeViews, dibujar decoraciones apropiadas para sus padres.

Independientemente de si la reimplementación de hasChildren() devuelve verdadero o falso, es posible que la vista no necesite llamar a rowCount() para determinar cuántos elementos secundarios existen. Por ejemplo, un QTreeView no necesita saber cuántos hijos tiene si el padre no se expande para mostrarlos.

A veces, volver a implementar hasChildren() para que devuelva true incondicionalmente es un enfoque útil si sabe que muchos elementos tendrán elementos secundarios. Esto asegura que mientras inicialmente se completan los datos del modelo lo más rápido posible, cada elemento se puede verificar posteriormente para los niños. El único inconveniente es que los elementos sin hijos pueden mostrarse incorrectamente en algunas vistas hasta que el usuario intente ver los hijos que no existen.

3. Navegación y creación de índices de modelos.

Los modelos jerárquicos son necesarios para proporcionar funciones a las que las vistas pueden llamar para navegar por la estructura de árbol que exponen y para obtener el índice del modelo de un elemento.

1. Padres e hijos

Dado que la estructura expuesta a la vista está determinada por la estructura de datos subyacente, cada subclase de modelo puede crear su propio índice de modelo proporcionando una implementación de las siguientes funciones:

Nombre de la función función
índice () Dado el índice del modelo de un elemento principal, esta función permite que las vistas y los delegados accedan a los elementos secundarios del elemento. Si no se encuentra un elemento secundario válido, correspondiente a la fila, la columna y el índice de modelo principal especificados, la función debe devolver QModelIndex(), que es un índice de modelo no válido.
padre() Proporciona el índice del modelo correspondiente al padre de cualquier hijo dado. Si el índice del modelo especificado corresponde a un elemento de nivel superior en el modelo, o si no hay un padre válido en el modelo, la función debe devolver un índice de modelo no válido creado con un constructor QModelIndex() vacío.

Las dos funciones anteriores usan la función de fábrica createIndex() para generar índices para que los usen otros componentes. El modelo generalmente proporciona algún identificador único a esta función para garantizar que el índice del modelo pueda volver a asociarse más tarde con su elemento correspondiente.

4. Soporte de arrastrar y soltar y manejo de tipos MIME

Las clases de modelo/vista admiten arrastrar y soltar, lo que proporciona un comportamiento predeterminado suficiente para muchas aplicaciones. Sin embargo, durante una operación de arrastrar y soltar, también es posible personalizar cómo se codifican los elementos, si se copian o mueven de forma predeterminada y cómo se insertan en un modelo existente.
Además, el comportamiento especializado que implementa la clase de vista de conveniencia debe coincidir con lo que esperan los desarrolladores existentes. La sección de vista de conveniencia proporciona una descripción general de este comportamiento.

1. Datos MIME

De forma predeterminada, los modelos y las vistas integrados usan un tipo MIME interno (application/x-qabstractitemmodeldatalist) para pasar información sobre el índice del modelo. Esto especifica datos para la lista de elementos, que contienen los números de fila y columna para cada elemento e información sobre las funciones que admite cada elemento.
Los datos codificados con este tipo MIME se pueden obtener llamando a QAbstractItemModel::mimeData() con un QModelIndexList que contiene los elementos que se serializarán.
Al implementar la compatibilidad con arrastrar y soltar en un modelo personalizado, es posible exportar elementos de datos en un formato especial al volver a implementar las siguientes funciones:

  • La función mimeData()
    se puede volver a implementar para devolver datos en otros formatos que no sean el tipo MIME interno predeterminado application/x-qabstractitemmodeldatalist.
    Las subclases pueden obtener el objeto QMimeData predeterminado de la clase base y agregarle datos en otros formatos.

Para muchos modelos, es útil proporcionar el contenido del elemento en un formato común representado por tipos MIME, como texto/sin formato e imagen/png. Tenga en cuenta que las imágenes, los colores y los documentos HTML se pueden agregar fácilmente a un objeto QMimeData mediante las funciones QMimeData::setImageData(), QMimeData::setColorData() y QMimeData::setHtml().

2. Aceptar datos descartados

Cuando se realiza una operación de arrastrar y soltar en una vista, se consulta el modelo subyacente para determinar qué tipos de operaciones admite y qué tipos MIME puede aceptar. Esta información la proporcionan las funciones QAbstractItemModel::supporteddroptions() y QAbstractItemModel::mimeTypes(). Los modelos que no anulan la implementación proporcionada por QAbstractItemModel admiten la operación de copia y el tipo MIME interno predeterminado del elemento.

Cuando los datos de elementos serializados se colocan en la vista, los datos se insertarán en el modelo actual mediante la implementación de QAbstractItemModel::dropMimeData(). La implementación predeterminada de esta función nunca sobrescribe ningún dato en el modelo; en su lugar, intenta insertar elementos de datos como hermanos de un elemento o elementos secundarios de ese elemento.

Para aprovechar la implementación predeterminada de tipos MIME incorporados de QAbstractItemModel, los nuevos modelos deben proporcionar reimplementaciones de las siguientes funciones:

Nombre de la función función
insertar fila () insertar columnas () Estas funciones permiten que el modelo inserte automáticamente nuevos datos utilizando la implementación existente proporcionada por QAbstractItemModel::dropMimeData().
establecer datos () Permite llenar nuevas filas y columnas con artículos.
setItemData() Esta función proporciona un soporte más eficiente para completar nuevos elementos.

Para aceptar otras formas de datos, se deben volver a implementar estas funciones:

Nombre de la función función
apoyoDropActions () Combinación utilizada para devolver operaciones de arrastrar y soltar, indicando el tipo de operaciones de arrastrar y soltar aceptadas por el modelo.
tipo de Mimica () 用于返回可由 model 解码和处理的MIME类型列表。通常,支持输入 model 的MIME类型与为外部组件使用的数据编码时可以使用的MIME类型相同。
dropMimeData () 对通过拖放操作传输的数据执行实际解码,确定将在 model 中设置数据的位置,并在必要的地方插入新的行和列。如何在子类中实现此函数取决于每个 model 公开的数据的需求。

如果dropMimeData()函数的实现通过插入或删除行或列来更改 model 的维度,或者如果修改了数据项,则必须注意确保发出所有相关信号。简单地调用子类中其他函数的重新实现,例如setData()、insertRows()和insertColumns(),可以确保 model 的行为一致。
为了确保拖动操作正常工作,重新实现以下从 model 中删除数据的函数是很重要的:
removeRows ()
removeRow ()
removeColumns ()
removeColumn ()
有关使用 item view 拖放的详细信息,请参阅使用 item view 拖放。

3. 方便的 view

方便的 view (QListWidget、QTableWidget和QTreeWidget)覆盖了默认的拖放功能,提供了不那么灵活但更自然的行为,适合于许多应用程序。例如,由于更常见的是将数据放入QTableWidget中的单元格中,用正在传输的数据替换现有的内容,底层 model 将设置目标项的数据,而不是向 model 中插入新的行和列。有关在方便 view 中拖放的详细信息,请参见在项 view 中使用拖放。

5. 针对大量数据的性能优化

canFetchMore()函数检查父节点是否有更多可用数据,并相应地返回true或false。fetchMore()函数根据指定的父节点获取数据。这两个函数可以组合在一起,例如,在涉及增量数据的数据库查询中填充QAbstractItemModel。我们重新实现canFetchMore()来指示是否有更多的数据需要获取,并根据需要将fetchMore()填充到 model 中。
另一个例子是动态填充的树 model ,当树 model 中的分支被扩展时,我们重新实现fetchMore()。
如果fetchMore()的重新实现将行添加到 model 中,则需要调用beginInsertRows()和endInsertRows()。此外,canFetchMore()和fetchMore()都必须重新实现,因为它们的默认实现返回false并且不做任何事情。

七、 model / view 类

这些类使用 model / view 设计模式,在该模式中,底层数据(在 model 中)与用户(在 view 中)呈现和操作数据的方式保持分离。

类名 功能作用
QAbstractItemDelegate 用于显示和编辑 model 中的数据项
QAbstractItemModel item model 类的抽象接口
QAbstractItemView 项 view 类的基本功能
QAbstractListModel 抽象 model ,可以子类化为一维列表 model
QAbstractProxyModel 可以执行排序、过滤或其他数据处理任务的代理项 model 的基类
QAbstractTableModel 抽象 model ,可以被子类化为创建表 model
QColumnView 列 view 的 model / view 实现
QConcatenateTablesProxyModel 代理多个源 model ,连接它们的行
QDataWidgetMapper 数据 model 的一部分到控件之间的映射
QFileSystemModel 本地文件系统的数据 model
QHeaderView 项 view 的标题行或标题列
QIdentityProxyModel 代理未修改的源 model
QItemDelegate 显示和编辑来自 model 的数据项的工具
QItemEditorCreator 使创建 item 编辑器创建基而不子类化QItemEditorCreatorBase成为可能
QItemEditorCreatorBase 在实现新的项编辑器创建器时必须子类化的抽象基类
QItemEditorFactory 用于编辑 view 和委托中的项数据的控件
QItemSelection 管理关于 model 中选定项的信息
QItemSelectionModel 跟踪 view 的选定项
QItemSelectionRange 管理关于 model 中选定项范围的信息
QListView 列表或图标 view 到 model 上
QListWidget 基于 item 的列表控件
QListWidgetItem 项与QListWidget项 view 类一起使用
QModelIndex 用于定位数据 model 中的数据
QPersistentModelIndex 用于定位数据 model 中的数据
QSortFilterProxyModel 持排序和过滤在另一个 model 和 view 之间传递的数据
QStandardItem 项与QStandardItemModel类一起使用
QStandardItemEditorCreator 注册控件的可能性,而不必子类化QItemEditorCreatorBase
QStandardItemModel 用于存储自定义数据的通用 model
QStringListModel 为 view 提供字符串的 model
QStyledItemDelegate 显示和编辑来自 model 的数据项的工具
QTableView 表 view 的默认 model / view 实现
QTableWidget 带有默认 model 的基于项的表 view
QTableWidgetItem Los elementos se utilizan en la clase QTableWidget
QTableWidgetSelectionRango Métodos para interactuar con selecciones en un modelo sin usar el índice del modelo y el modelo de selección
QTreeView Implementación de modelo/vista predeterminada para la vista de árbol
QTree Widget Una vista de árbol usando un modelo de árbol predefinido
QTreeWidgetItem elemento para usar con la clase de conveniencia QTreeWidget
QTreeWidgetItemIterator Método para iterar elementos en una instancia de QTreeWidget

Supongo que te gusta

Origin blog.csdn.net/qq_43680827/article/details/132225747
Recomendado
Clasificación