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

I. Resumen

Qt incluye un conjunto de clases de vista de elementos que utilizan la arquitectura modelo/vista para administrar la relación entre los datos y la forma en que se presentan al usuario. Las funciones introducidas por la arquitectura brindan a los desarrolladores una mayor flexibilidad para personalizar la presentación, y el marco también proporciona una interfaz de modelo estándar para permitir el uso de una amplia gama de fuentes de datos con las vistas de elementos existentes.

En este artículo, presentamos brevemente los conceptos de programación de modelo/vista, describimos los conceptos involucrados y describimos la arquitectura del sistema de vista de elementos. Se explica cada componente de la arquitectura y se dan ejemplos de cómo utilizar las clases proporcionadas. La información a la que se hace referencia aquí es la información oficial de Qt.

1.modelo/arquitectura de vista

Model-View-Controller, (Model-View-Controller, MVC) es un patrón de diseño derivado de Smalltalk, a menudo utilizado para construir interfaces de usuario.

En el libro "Patrones de diseño", Gamma et al escriben:
MVC consta de tres tipos de objetos. El modelo es el objeto de la aplicación, la Vista es su visualización en pantalla y el Controlador define cómo reacciona la interfaz de usuario a la entrada del usuario.

Antes de MVC, el diseño de la interfaz de usuario tendía a mantener estos objetos juntos. MVC los desacopla para brindar flexibilidad y reutilización.

Si se combinan los objetos Vista y Controlador, el resultado es una arquitectura modelo/vista. Esto aún separa cómo se almacenan los datos de cómo se presentan al usuario, pero proporciona un marco más simple basado en los mismos principios.

Esta separación hace posible mostrar los mismos datos en múltiples vistas diferentes e implementar nuevos tipos de vista sin cambiar las estructuras de datos subyacentes .

De hecho, es decir, si nuestro negocio se puede separar de la interfaz, es mejor separarlo en lugar de mezclarlo.Al igual que Linux no depende de la interfaz para ejecutarse, sino que el sistema de Windows en realidad combina el sistema y La interfaz.

Para manejar la entrada del usuario de manera flexible, presentamos el concepto de delegado.

El beneficio de usar un delegado en este marco es que permite personalizar cómo se procesan y editan los elementos de datos.
inserte la descripción de la imagen aquí

El modelo se comunica con las fuentes de datos y proporciona interfaces a otros componentes de la arquitectura. La naturaleza de la comunicación depende del tipo de fuente de datos y de cómo se implementa el modelo.

View obtiene índices del modelo del modelo; estas son referencias a elementos de datos . La vista puede recuperar elementos de datos de la fuente de datos proporcionando un índice de modelo a modelo.

En una vista estándar, el delegado representa el elemento de datos. Cuando se edita un elemento, el delegado se comunica directamente con el modelo mediante el índice del modelo.

De las palabras anteriores se puede ver que si el modelo se comunica con el delegado y la vista, es el índice del modelo usado por el delegado y la vista.

En general, las clases de modelo/vista se pueden dividir en los tres grupos descritos anteriormente: modelo, vista y delegado.

Cada componente está definido por una clase abstracta que proporciona una interfaz común y, en algunos casos, implementaciones predeterminadas de funcionalidad.

Las clases abstractas están destinadas a ser subclasificadas para proporcionar el conjunto completo de funcionalidades esperadas por otros componentes, al mismo tiempo que permiten la escritura de componentes especializados.

El modelo, la vista y el delegado se comunican entre sí mediante señales y espacios:

  • Las señales del modelo informan a la Vista sobre los cambios en los datos contenidos en la fuente de datos.
  • Las señales de la vista proporcionan información sobre la interacción del usuario con el elemento que se muestra.
  • Las señales del delegado se utilizan durante la edición para informar al modelo y a View sobre el estado del editor.

2. modelo

Todos los modelos de elementos se basan en la clase QAbstractItemModel. Esta clase define una interfaz que Ver y delegar usan para acceder a los datos. Los datos en sí no tienen que almacenarse en el modelo; se pueden mantener en estructuras de datos o repositorios proporcionados por clases, archivos, bases de datos u otros componentes de la aplicación separados. Esto es lo que separa los datos. El propósito del modelo es un poco como hacer una capa intermedia para conectarse con la interfaz, que debe entenderse.

La sección de clase de modelo introducirá el concepto básico de modelo.

QAbstractItemModel proporciona una interfaz de datos que es lo suficientemente flexible para manejar vistas que representan datos en forma de tablas, listas y árboles.

Sin embargo, las clases QAbstractListModel y QAbstractTableModel son mejores puntos de partida cuando se implementan nuevos modelos para estructuras de datos tipo lista y tabla , ya que proporcionan implementaciones predeterminadas apropiadas de funciones comunes. Todas estas clases se pueden dividir en subclases para proporcionar modelos que admitan tipos específicos de listas y tablas. , uso mucho QAbstractTableModel,

Qt proporciona algunos modelos listos para manejar elementos de datos:

  • QStringListModel se usa para almacenar una lista simple de elementos QString.
  • QStandardItemModel administra una estructura de árbol de elementos más compleja, cada elemento puede contener datos arbitrarios.
  • QFileSystemModel proporciona información sobre archivos y directorios en el sistema de archivos local.
  • QSqlQueryModel, qsqlltablemodel y QSqlRelationalTableModel acceden a la base de datos por convención de modelo/vista (a menudo utilizada).

Si estos modelos estándar no pueden satisfacer las necesidades reales, debemos subclasificar QAbstractListModel, QAbstractListModel o QAbstractTableModel para crear nuestro propio modelo personalizado.

3. Ver

Qt proporciona una vista completa de diferentes tipos:

  • QListView muestra la lista de elementos
  • QTableView muestra datos del modelo en la tabla
  • QTreeView muestra los elementos del modelo de datos en una lista jerárquica.

Estas clases se basan en la clase base abstracta QAbstractItemView. Si bien estas clases son implementaciones listas para usar, también se pueden dividir en subclases para proporcionar vistas personalizadas.
Las vistas disponibles se describen en la sección Clase de vista.

4. Delegado

QAbstractItemDelegate es la clase base abstracta para delegados en el marco de modelo/vista.

La implementación del delegado predeterminado la proporciona QStyledItemDelegate, que la vista estándar de Qt usa como delegado predeterminado.

Sin embargo, QStyledItemDelegate y QItemDelegate son alternativas independientes para dibujar y proporcionan editores para elementos en View . (Para este editor, podemos usar nuestros propios controles personalizados o controles Qt como QLineEdit, QSpinBox, etc.)

La diferencia entre ellos es que QStyledItemDelegate usa el estilo actual para pintar sus elementos.

Por lo tanto, al implementar un delegado personalizado o usar hojas de estilo Qt, recomendamos usar QStyledItemDelegate como clase base.

5. Ordenar

Hay dos métodos de clasificación en la arquitectura modelo/vista; cuál elegir depende de su modelo subyacente.

Si su modelo se puede ordenar, es decir, si vuelve a implementar la función QAbstractItemModel::sort(), tanto QTableView como QTreeView proporcionan una API que le permite ordenar los datos del modelo mediante programación.

Además, también podemos habilitar la clasificación interactiva (es decir, permitir que el usuario clasifique los datos haciendo clic en el encabezado de la Vista), conectando la señal QHeaderView::sortIndicatorChanged() a la función de ranura QTableView::sortByColumn() o QTreeView: :sortByColumn respectivamente () función de ranura.

Alternativamente, si su modelo no tiene la interfaz requerida, o si desea usar una Vista de lista para mostrar datos, use un modelo proxy para transformar la estructura del modelo antes de mostrar los datos en la Vista. Esto se cubre en detalle en la sección sobre modelos proxy.

6. Atajos

Para beneficiar a las aplicaciones que se basan en la vista de elementos basada en elementos de Qt y las clases de tabla, se derivan varias clases de conveniencia de la clase de vista estándar.

No están destinados a ser subclasificados. Su propósito es ser utilizado.

Los ejemplos de estas clases incluyen QListWidget, QTreeWidget y QTableWidget. Estas clases no son tan flexibles como las clases View y no se pueden usar con modelos arbitrarios.

Qt recomienda que usemos el método modelo/vista para procesar los datos en itemView, a menos que realmente necesite un conjunto de clases basadas en elementos. De hecho, utilicé el método de programación de vista modelo, dos palabras para describir: ¡realmente fragante!

Si desea aprovechar las funciones proporcionadas por el enfoque de modelo/vista mientras sigue usando la interfaz basada en elementos, considere usar clases de vista como QListView, QTableView y QTreeView con QStandardItemModel. Esto significa que nuestra vista aún puede usar controles basados ​​en modo/vista, pero usamos QStandardItemModel para representar los datos de cada elemento en la vista. Este tipo de flexibilidad no es tan alta, pero Qt todavía la proporciona.

2. Usar modelo/vista

Las próximas secciones explican cómo usar el modelo/patrón de vista en Qt. Cada sección contiene un ejemplo, seguido de una sección que muestra cómo crear nuevos componentes.

1. Qt contiene dos modelos

Los dos modelos estándar proporcionados por Qt son QStandardItemModel y QFileSystemModel. QStandardItemModel Un modelo multipropósito que se puede usar para representar varias estructuras de datos requeridas por vistas de lista, tabla y árbol. Este modelo también contiene el elemento de datos. QFileSystemModel es un modelo que mantiene la información del contenido del directorio. Por lo tanto, no contiene elementos de datos en sí mismo, sino que simplemente representa archivos y directorios en el sistema de archivos local.

QFileSystemModel proporciona un modelo listo para la experimentación que se puede configurar fácilmente para usar los datos existentes. Con este modelo, podemos mostrar cómo configurar el modelo para una vista lista para usar y explorar cómo usar el índice del modelo para manipular datos.

2. Usar la vista en el modelo existente

Las clases QListView y QTreeView son las vistas más adecuadas para usar con QFileSystemModel. El ejemplo que se proporciona a continuación muestra el contenido de un directorio en una vista de árbol, junto a la misma información en una vista de lista. Las dos vistas comparten la selección del usuario, por lo que los elementos seleccionados se resaltan en ambas vistas.

Es un modelo compartido.
inserte la descripción de la imagen aquí

Configuramos un QFileSystemModel para que pueda usarse y crear algunas vistas para mostrar el contenido del directorio. Esto muestra la forma más sencilla de utilizar el modelo. La construcción y uso del modelo se realiza en una función main():

int main(int argc, char *argv[])
{
    
    
	QApplication app(argc, argv);
	QSplitter *splitter = new QSplitter;

	QFileSystemModel *model = new QFileSystemModel;
	model->setRootPath(QDir::currentPath());

El modelo está configurado para usar datos de un sistema de archivos. Llamar a setRootPath() le dice al modelo qué ruta de unidad en el sistema de archivos debe exponer a la vista.

Cree dos vistas para inspeccionar elementos en el modelo de dos maneras diferentes:

QTreeView *tree = new QTreeView(splitter);
tree->setModel(model);
tree->setRootIndex(model->index(QDir::currentPath()));

QListView *list = new QListView(splitter);
list->setModel(model);
list->setRootIndex(model->index(QDir::currentPath()));

view se construye de la misma manera que otros widgets.
Para mostrar los elementos del modelo en la vista, simplemente llame a su función setModel() con el modelo de catálogo como parámetro .

Filtramos los datos proporcionados por el modelo llamando a la función setRootIndex() en cada vista, pasando un índice de modelo apropiado para el directorio actual del modelo de sistema de archivos.

La función index() utilizada aquí es exclusiva de QFileSystemModel. Le damos de comer un directorio y devuelve un índice de modelo. El índice del modelo se analiza en la clase del modelo.

El resto de la función solo muestra las vistas en el widget divisor y ejecuta el ciclo de eventos de la aplicación:

	splitter->setWindowTitle("Two views onto the same file system model");
	splitter->show();
	return app.exec();
}

En los ejemplos anteriores, ignoramos cómo manejar la selección de elementos. La sección Manejo de selecciones en la vista Elemento cubre este tema con más detalle.

3. Clase modelo

1. Conceptos básicos

En una arquitectura modelo/vista, el modelo proporciona una interfaz estándar que la vista y el delegado usan para acceder a los datos.

En Qt, la interfaz estándar está definida por la clase QAbstractItemModel. Independientemente de cómo se almacenen los elementos de datos en cualquier estructura de datos subyacente, todas las subclases de QAbstractItemModel representan los datos como una estructura jerárquica que contiene una tabla de elementos.

Las vistas usan esta convención para acceder a elementos de datos en el modelo, pero no están restringidas en la forma en que muestran información al usuario.
inserte la descripción de la imagen aquí
El modelo también notifica cualquier vista adjunta sobre cambios de datos a través del mecanismo de señales y ranuras.

Esta sección describe algunos conceptos básicos que son fundamentales para la forma en que otros componentes acceden a los elementos de datos a través de clases de modelo. Las secciones posteriores discuten conceptos más avanzados.

1. índice del modelo

Para asegurar que la representación de los datos esté separada de la forma de acceder a los datos, se introduce el concepto de índice del modelo. Cada pieza de información disponible a través de un modelo está representada por un índice de modelo. Las vistas y los delegados usan estos índices para solicitar elementos de datos para mostrar.

Por lo tanto, solo el modelo necesita saber cómo obtener los datos, y es bastante genérico definir los tipos de datos que maneja el modelo. (No importa el formato del modelo que obtiene datos desde el exterior, pero el contenido modificado se limita al modelo y no afectará la vista. La próxima vez que lo expandamos es realmente muy agradable y los cambios serán menores .)

El índice del modelo contiene un puntero al modelo que los creó, lo que evita confusiones cuando se usan varios modelos.

QAbstractItemModel *model = index.model();

Un índice de modelo proporciona una referencia temporal a una parte de la información y se puede utilizar para recuperar o modificar datos a través del modelo. Dado que el modelo puede reorganizar su estructura interna de vez en cuando, el índice del modelo puede volverse inválido y no debe almacenarse . Si se requiere una referencia a largo plazo a una parte de la información, se debe crear un índice de modelo persistente. Esto proporciona una referencia a la información que el modelo mantiene actualizada.

El índice del modelo temporal lo proporciona la clase QModelIndex y el índice del modelo persistente lo proporciona la clase QPersistentModelIndex.

Para obtener el índice del modelo correspondiente al elemento de datos, se deben especificar tres atributos para el modelo: número de fila, número de columna y el índice del modelo del elemento principal.

Las siguientes secciones describen y explican estas propiedades en detalle.

2. Filas y columnas

En su forma más básica, se puede acceder a un modelo como una tabla simple, donde los elementos se ubican por sus números de fila y columna.

Esto no implica que los bloques de datos subyacentes estén almacenados en una estructura de matriz , el uso de números de fila y columna es simplemente una convención para permitir que los componentes se comuniquen entre sí .

Podemos recuperar información sobre el propósito de cualquier artículo dado y obtener un índice que represente el propósito de ese artículo especificando los números de fila y columna del propósito del artículo en el modelo:

QModelIndex index = model->index(row, column, ...);

Los modelos que proporcionan una interfaz para estructuras de datos simples de un solo nivel, como listas y tablas, no necesitan proporcionar ninguna información adicional; sin embargo, como muestra el código anterior, debemos proporcionar más información al obtener el índice del modelo.

inserte la descripción de la imagen aquí

La figura anterior muestra una representación de un modelo de tabla básico donde cada elemento se ubica mediante un par de números de fila y columna. Obtenemos un índice de modelo que hace referencia a un elemento de datos pasando los números de fila y columna relevantes al modelo.

QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

Siempre se hace referencia a los elementos de nivel superior en el modelo especificando QModelIndex() como su elemento principal. Esto se discutirá en la siguiente sección.

2. El artículo principal del artículo.

La interfaz similar a una tabla proporcionada por el modelo es ideal cuando se trabaja con datos en una tabla o vista de lista; el sistema de numeración de filas y columnas se corresponde exactamente con la forma en que la vista muestra los elementos. Sin embargo, las estructuras como las vistas de árbol requieren que el modelo exponga una interfaz más flexible para los elementos que contiene. Por lo tanto, cada elemento también puede ser el padre de otro elemento, al igual que un elemento de nivel superior en una vista de árbol puede contener otra lista de elementos.

Al solicitar un índice para un artículo modelo, debemos proporcionar cierta información sobre el artículo principal del artículo. Fuera del modelo, la única forma de hacer referencia a un objeto de elemento es a través del índice del modelo, por lo que también se debe proporcionar el índice del modelo principal:

QModelIndex index = model->index(row, column, parent);

inserte la descripción de la imagen aquí
La figura anterior muestra una representación de un modelo de árbol en el que cada elemento está referenciado por un elemento principal, un número de fila y un número de columna.
Los artículos "A" y "C" se representan como hermanos de nivel superior en el modelo:

QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

Un elemento tiene varios elementos secundarios. El índice del modelo del ítem "B" se obtiene mediante el siguiente código:

QModelIndex indexB = model->index(1, 0, indexA);

3. Funciones de los elementos

Los elementos del modelo pueden desempeñar diferentes funciones para otros componentes, lo que permite proporcionar diferentes tipos de datos para diferentes situaciones.

Por ejemplo, Qt::DisplayRole se usa para acceder a cadenas que se pueden mostrar como texto en una vista. Por lo general, un elemento contiene datos en muchos roles diferentes; el rol estándar lo define Qt::ItemDataRole.

Podemos solicitar datos del propósito del artículo del modelo pasando el índice del modelo correspondiente al propósito del artículo al modelo y especificando un rol para obtener el tipo de datos que queremos:

 QVariant value = model->data(index, role);
inserte la descripción de la imagen aquí El rol indica al modelo el tipo de datos a los que se hace referencia. La vista puede mostrar roles de diferentes maneras, por lo que es importante proporcionar la información adecuada para cada rol. Algunos usos específicos de roles se tratan con más detalle en la sección Creación de un nuevo modelo.

Los usos más comunes de los datos de elementos están cubiertos por los roles estándar definidos en Qt::ItemDataRole. Al proporcionar datos de elementos apropiados para cada función, el modelo puede proporcionar sugerencias a la vista y delegar cómo se debe presentar el elemento al usuario. Diferentes tipos de vistas son libres de interpretar o ignorar esta información según sea necesario. También se pueden definir roles adicionales para fines específicos de la aplicación.

4. Resumen

  • El índice del modelo proporciona al modelo información de vista y delegado sobre el destino del elemento de una manera independiente de cualquier estructura de datos subyacente.
  • Los elementos se referencian por sus números de fila y columna y el índice del modelo de su elemento principal.
  • El índice del modelo lo construye el modelo a pedido de otros componentes, como la vista y el delegado.
  • Si se especifica un índice de modelo válido para un elemento principal cuando se solicita un índice con index(), el índice devuelto se referirá al elemento debajo de ese elemento principal en el modelo. El índice obtenido apunta al elemento secundario de este elemento.
  • Si se especifica un índice de modelo no válido para el elemento principal al solicitar un índice mediante index(), el índice devuelto apuntará al elemento de nivel superior en el modelo.
  • El rol distingue los diferentes tipos de datos asociados con el elemento.

2. Usa el Índice del modelo

Para demostrar cómo recuperar datos de un modelo utilizando el índice del modelo, configuramos un QFileSystemModel sin una vista y mostramos los nombres de archivos y directorios en un widget. Si bien esta no es la forma normal de usar modelos, demuestra las convenciones que usa el modelo cuando se trata de índices de modelos.
La carga de QFileSystemModel es asíncrona para minimizar el uso de recursos del sistema. Tenemos que tener esto en cuenta cuando se trata de este modelo.
Modelamos el sistema de archivos de la siguiente manera:

QFileSystemModel *model = new QFileSystemModel;
connect(model, &QFileSystemModel::directoryLoaded, [model](const QString &directory) {
    
    
QModelIndex parentIndex = model->index(directory);
int numRows = model->rowCount(parentIndex);
});
model->setRootPath(QDir::currentPath);

En este caso, primero establecemos un QFileSystemModel predeterminado. Lo conectamos a una lambda que usa la implementación específica de index() proporcionada por el modelo para obtener el índice principal. En la expresión lambda, usamos la función rowCount() para contar el número de filas en el modelo. Finalmente, establecemos la ruta raíz de QFileSystemModel, dejamos que comience a cargar datos y active la expresión lambda.
Para simplificar, solo nos interesan los elementos de la primera columna del modelo. Examinamos cada fila a su vez, obtenemos el primer índice del modelo de objeto de elemento en cada fila y leemos los datos del objeto de elemento almacenados en el modelo.

for (int row = 0; row < numRows; ++row) {
    
    
QModelIndex index = model->index(row, 0, parentIndex);

Para obtener el índice del modelo, especificamos el número de fila, el número de columna (la primera columna es 0) y el índice del modelo correspondiente al elemento principal de todos los elementos que queremos. El texto almacenado en cada elemento se puede obtener a través de la función data() del modelo. Especificamos el índice del modelo y DisplayRole para obtener los datos del propósito del elemento en forma de cadena.

 QString text = model->data(index, Qt::DisplayRole).toString();
// Display the text in a widget.

}

El ejemplo anterior demuestra los principios básicos de la recuperación de datos de un modelo:

  • Use rowCount() y columnCount() para obtener las dimensiones del modelo. Estas funciones generalmente requieren especificar un índice de modelo principal.
    -El índice del modelo se usa para acceder al elemento en el modelo. Especificar elemento requiere índices de fila, columna y modelo principal.
  • Para acceder a los elementos de nivel superior del modelo, utilice QModelIndex() para especificar un índice de modelo vacío como índice principal.
  • El elemento elemento contiene datos de diferentes roles. Para obtener datos para un rol específico, el índice del modelo y el rol deben proporcionarse al modelo.

4. Ver clase

1. Concepto

En la arquitectura modelo/vista, la vista obtiene elementos de datos del modelo y los presenta al usuario.

La representación de los datos no necesita ser similar a la representación de datos proporcionada por el modelo y puede ser bastante diferente de la estructura de datos subyacente utilizada para almacenar el elemento de datos.

Al usar la interfaz de modelo estándar proporcionada por QAbstractItemModel y la interfaz de vista estándar proporcionada por QAbstractItemView, y al usar el índice del modelo para representar el elemento de datos de una manera común, se logra la separación de contenido y presentación.

La vista generalmente administra el diseño general de los datos obtenidos del modelo. Pueden representar elementos de datos individuales por sí mismos o usar un delegado para manejar las funciones de representación y edición.

Además de mostrar datos, las vistas también manejan la navegación entre elementos, así como algunos aspectos de la selección de elementos.

Estas vistas también implementan la funcionalidad básica de la interfaz de usuario, como menús contextuales y arrastrar y soltar. view puede proporcionar una función de edición predeterminada para el elemento y también puede proporcionar un editor personalizado junto con el delegado.

Una vista se puede construir sin un modelo, pero se debe proporcionar un modelo para mostrar información útil. Las vistas realizan un seguimiento de los elementos seleccionados por el usuario mediante el uso de elementos de selección que se pueden mantener individualmente para cada vista o compartir entre varias vistas.

Algunas vistas, como QTableView y QTreeView, muestran títulos y elementos. Estos también se implementan mediante una clase de vista QHeaderView.

Los encabezados generalmente acceden al mismo modelo que la vista que los contiene. Obtienen datos del modelo mediante la función QAbstractItemModel::headerData() y, por lo general, muestran la información del encabezado en forma de etiquetas. Se pueden subclasificar nuevos encabezados de la clase QHeaderView para proporcionar etiquetas más especializadas para las vistas.

2. Usa la vista proporcionada por Qt

Qt proporciona tres clases de vista disponibles, que presentan los datos en el modelo de una manera con la que la mayoría de los usuarios están familiarizados. QListView puede mostrar los elementos del modelo como una lista simple o como una vista de icono clásica. QTreeView muestra los elementos del modelo como una jerarquía de listas, lo que permite que las estructuras profundamente anidadas se representen de manera compacta. QTableView presenta los elementos del modelo en forma de tabla, como el diseño de una aplicación de hoja de cálculo.
inserte la descripción de la imagen aquí

El comportamiento predeterminado de las vistas estándar que se muestran arriba debería ser suficiente para la mayoría de las aplicaciones. Proporcionan una funcionalidad de edición básica y se pueden personalizar para satisfacer las necesidades de una interfaz de usuario más profesional.

1. Usa modelos

Crearemos un modelo de lista de cadenas como modelo de ejemplo, estableceremos algunos datos en él y crearemos una vista para mostrar el contenido del modelo. Todo esto se puede hacer en una función:

int main(int argc, char *argv[])
{
    
    
QApplication app(argc, argv);

// Unindented for quoting purposes:
QStringList numbers;
numbers << "One" << "Two" << "Three" << "Four" << "Five";

QAbstractItemModel *model = new StringListModel(numbers);

Tenga en cuenta que StringListModel se declara como QAbstractItemModel. Esto significa que podemos usar la interfaz abstracta del modelo y estar seguros de que el código seguirá funcionando incluso si reemplazamos el modelo de lista de cadenas con un modelo diferente. Pensé que se había utilizado la interfaz QAbstractItemModel, ¡esta es la genialidad del polimorfismo!

La vista de lista proporcionada por QListView es suficiente para mostrar los elementos en el modelo de lista de cadenas. Usamos el siguiente código para construir la vista y construir el modelo:

QListView *view = new QListView;
view->setModel(model);

La vista se muestra de la manera normal:

	view->show();
	return app.exec();
}

La vista representa el contenido del modelo y accede a los datos a través de la interfaz del modelo. Cuando el usuario intenta editar el elemento, la vista usa el delegado predeterminado para proporcionar el widget del editor.
inserte la descripción de la imagen aquí

La figura anterior muestra cómo QListView representa los datos en un modelo de lista de cadenas. Dado que el modelo es editable, la vista permite editar automáticamente cada elemento de la lista mediante el delegado predeterminado.

2. Usando múltiples vistas del modelo

Para proporcionar múltiples vistas para el mismo modelo, simplemente configure el mismo modelo para cada vista. En el siguiente código, creamos dos vistas de tabla, cada una usando el mismo modelo de tabla simple que creamos para este ejemplo:

QTableView *firstTableView = new QTableView;
QTableView *secondTableView = new QTableView;

firstTableView->setModel(model);
secondTableView->setModel(model);

El uso de señales y ranuras en una arquitectura de modelo/vista significa que los cambios en el modelo se pueden propagar a todas las vistas adjuntas, lo que garantiza que siempre tengamos acceso a los mismos datos, sin importar qué vista se utilice.
inserte la descripción de la imagen aquí

La imagen de arriba muestra dos vistas diferentes del mismo modelo, cada una con algunos elementos seleccionados. Aunque los datos del modelo se muestran de forma coherente en toda la vista, cada vista mantiene su propio modelo de selección interno. Esto puede ser útil en algunos casos, pero para muchas aplicaciones es deseable un modelo de selección compartida.

3. Selección de elementos de procesamiento

La clase QItemSelectionModel proporciona el mecanismo para manejar la selección de elementos en una vista. Todas las vistas estándar construyen sus propios modelos de selección por defecto e interactúan con ellos de forma normal.

El modelo de selección utilizado por la vista se puede obtener a través de la función selectionModel() y el modelo de selección alternativo se puede especificar a través de setSelectionModel(). La capacidad de controlar el modelo de selección utilizado por una vista es útil cuando queremos proporcionar varias vistas coherentes para los mismos datos del modelo.

En general, a menos que sea una subclase de modelo o vista, no hay necesidad de manipular directamente el contenido seleccionado. Sin embargo, también se puede acceder a la interfaz del modelo de selección si lo desea, y discutiremos cómo manejar las selecciones en la vista Elemento en la Sección 12.4.3.

1. Compartir selección entre vistas

Aunque es conveniente que la clase de vista proporcione su propio modelo de selección por defecto, cuando usamos múltiples vistas en el mismo modelo, generalmente queremos que los datos del modelo y la selección del usuario sean consistentes en todas las vistas. Dado que las clases de vista permiten reemplazar sus modelos de selección internos, podemos lograr una selección uniforme en todas las vistas con el siguiente código:

 secondTableView->setSelectionModel(firstTableView->selectionModel());

La segunda vista es el modelo de selección de la primera vista. Ambas vistas ahora operan en el mismo modelo de selección, manteniendo sincronizados los datos y el elemento seleccionado.
inserte la descripción de la imagen aquí

En el ejemplo anterior, se utilizan dos vistas del mismo tipo para mostrar datos del mismo modelo. Sin embargo, si se utilizan dos tipos diferentes de vistas, los elementos del elemento seleccionado pueden aparecer de manera muy diferente en cada vista; por ejemplo, una selección continua en una vista de tabla puede representarse como un conjunto de fragmentos de elementos resaltados en una vista de árbol.

5. Clase de delegado

1. Concepto

A diferencia del patrón modelo-vista-controlador, el diseño modelo/vista no incluye un componente completamente independiente que gestione la interacción con el usuario. Por lo general, la vista es responsable de presentar los datos del modelo al usuario y manejar la entrada del usuario. Para permitir cierta flexibilidad en la forma en que se obtiene la entrada, el delegado realiza la interacción. Estos componentes brindan funcionalidad de entrada y también son responsables de representar elementos individuales en ciertas vistas. La interfaz estándar para controlar el delegado se define en la clase QAbstractItemDelegate.

Los delegados quieren poder representar su propio contenido implementando las funciones paint() y sizeHint(). Sin embargo, los delegados simples basados ​​en widgets pueden heredar de QStyledItemDelegate en lugar de QAbstractItemDelegate y aprovechar la implementación predeterminada de estas funciones.

El editor delegado puede administrar el proceso de edición mediante el uso de widgets o mediante el manejo de eventos directamente. El primer método se describe más adelante en esta sección y también se muestra en el ejemplo de delegado de Spin Box.

El ejemplo de Pixelator muestra cómo crear un delegado personalizado para realizar una representación especial para la vista de tabla.

2. Usa un delegado existente

Las vistas estándar proporcionadas por Qt usan instancias de QStyledItemDelegate para proporcionar funcionalidad de edición. La implementación predeterminada de la interfaz de delegado representa los elementos en el estilo habitual de las vistas estándar (QListView, QTableView y QTreeView).

Todos los roles estándar son manejados por el delegado predeterminado que usan las vistas estándar. La forma en que se interpretan se describe en la documentación de QStyledItemDelegate.

El delegado utilizado por la vista lo devuelve la función itemDelegate(). La función setItemDelegate() le permite instalar un delegado personalizado para una vista estándar. Esta función debe usarse al configurar un delegado para una vista personalizada.

3. Un simple delegado

El delegado implementado aquí usa QSpinBox para proporcionar funciones de edición, principalmente para mostrar modelos enteros. Aunque configuramos un modelo de tabla personalizado basado en enteros para esto, podemos usar fácilmente QStandardItemModel porque el delegado personalizado controla la entrada de datos. Creamos una vista de tabla para mostrar el contenido del modelo, que se editará con un delegado personalizado.
inserte la descripción de la imagen aquí

Heredamos el delegado de QStyledItemDelegate porque no queremos escribir funciones de visualización personalizadas. Sin embargo, todavía tenemos que proporcionar funciones para administrar los widgets del editor:

class SpinBoxDelegate : public QStyledItemDelegate
{
    
    
	Q_OBJECT
	
public:
	SpinBoxDelegate(QObject *parent = nullptr);
	
	QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
	const QModelIndex &index) const override;
	
	void setEditorData(QWidget *editor, const QModelIndex &index) const override;
	void setModelData(QWidget *editor, QAbstractItemModel *model,
	const QModelIndex &index) const override;
	
	void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
	const QModelIndex &index) const override;
};

Tenga en cuenta que el widget del editor no está configurado cuando se construye el delegado. Solo construimos widgets de editor cuando es necesario.

1. Proporcionar un editor

En este ejemplo, cuando la vista de tabla necesita proporcionar un editor, le pide al delegado que proporcione un widget de editor apropiado para el elemento que se está modificando. La función createEditor() proporciona todo lo que el delegado necesita para configurar el widget adecuado:

QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
 const QStyleOptionViewItem &/* option */,
 const QModelIndex &/* index */) const
{
    
    
	QSpinBox *editor = new QSpinBox(parent);
	editor->setFrame(false);
	editor->setMinimum(0);
	editor->setMaximum(100);

	return editor;
}

Tenga en cuenta que no necesitamos mantener un puntero en el widget del editor, ya que la vista se encargará de destruirlo cuando ya no sea necesario.

Instalamos el filtro de eventos predeterminado del delegado en el editor para garantizar que proporcionara los accesos directos de edición estándar que los usuarios esperan.

Se pueden agregar accesos directos adicionales al editor para permitir un comportamiento más complejo; estos se analizan en la sección Sugerencias de edición.

La vista asegura que la información de datos y geometría del editor esté configurada correctamente llamando a funciones que definimos más adelante para estos propósitos. Podemos crear diferentes editores basados ​​en el índice del modelo proporcionado por la vista. Por ejemplo, si tenemos una columna de enteros y una columna de cadenas, podemos devolver un QSpinBox o un QLineEdit, según la columna que se esté editando.

El delegado debe proporcionar una función para copiar los datos del modelo en el editor. En este ejemplo, leemos los datos almacenados en el rol de visualización y establecemos el valor en el cuadro de número correspondiente.

void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    
    
	int value = index.model()->data(index, Qt::EditRole).toInt();
	
	QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
	spinBox->setValue(value);
}

En este ejemplo, sabemos que el widget del editor es un cuadro giratorio, pero podemos tener diferentes editores para diferentes tipos de datos en el modelo, en cuyo caso debemos configurar el widget en el tipo apropiado.

2. Enviar datos al modelo

Cuando el usuario termine de editar el valor en el control giratorio, la vista llamará a la función setModelData() y le pedirá al delegado que almacene el valor editado en el modelo.

void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, 
	 const QModelIndex &index) const
{
    
    
	QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
	spinBox->interpretText();
	int value = spinBox->value();
	
	model->setData(index, value, Qt::EditRole);
}

Dado que la vista administra el widget del editor para el delegado, solo necesitamos actualizar el modelo con el contenido del editor proporcionado. En este ejemplo, nos aseguramos de que la ruleta esté actualizada y actualicemos el modelo con el valor que contiene utilizando el índice especificado.

La clase estándar QStyledItemDelegate notifica a la vista cuando ha terminado de editar emitiendo la señal closeEditor(). view asegura que el widget del editor se cierre y se destruya. En este ejemplo, solo proporcionamos una funcionalidad de edición simple, por lo que nunca necesitamos emitir esta señal.

Todas las operaciones sobre los datos se realizan a través de la interfaz proporcionada por QAbstractItemModel. Esto hace que el delegado sea en gran medida independiente del tipo de datos con los que opera, pero para usar ciertos tipos de widgets de editor se deben hacer algunas suposiciones. En este ejemplo, asumimos que el modelo siempre contiene valores enteros, pero aún podemos usar este delegado para diferentes tipos de modelos, ya que QVariant proporciona valores predeterminados razonables para datos inesperados.

3. Actualizar la geometría del editor

La responsabilidad del delegado es administrar la geometría del editor. La geometría debe establecerse cuando se crea el editor y cuando cambia el tamaño del objeto del elemento o la posición en la vista. Afortunadamente, la vista proporciona toda la información geométrica necesaria en el objeto del elemento de vista.

void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,
 		const QStyleOptionViewItem &option,
		const QModelIndex &/* index */) const
{
    
    
	editor->setGeometry(option.rect);
}

En este ejemplo, solo usamos la información de geometría proporcionada por el elemento de vista en el rectángulo del objeto del elemento. El delegado que representa un elemento con varios elementos no usa el rectángulo del elemento directamente. Posicionará el editor en relación con otros elementos en el elemento del elemento.

4. Consejos de edición

Después de editar, el delegado debe brindar sugerencias a otros componentes sobre el resultado del proceso de edición y proporcionar sugerencias que faciliten cualquier operación de edición posterior. Esto se logra enviando la señal closeEditor() con el aviso apropiado.

Esto lo maneja el filtro de eventos predeterminado QStyledItemDelegate, que instalamos al crear el cuadro de número.

El comportamiento del cuadro de número se puede ajustar para que sea más fácil de usar. En el filtro de eventos predeterminado proporcionado por QStyledItemDelegate, si el usuario presiona Retorno para confirmar su selección en el control giratorio, el delegado envía el valor al modelo y cierra el control giratorio.

Podemos cambiar este comportamiento instalando nuestro propio filtro de eventos en el control giratorio y proporcionar sugerencias de edición según sea necesario; por ejemplo, podemos enviar closeEditor() con una sugerencia EditNextItem para comenzar a editar automáticamente el siguiente elemento en la vista.

Otro enfoque que no requiere el uso de filtros de eventos es proporcionar nuestro propio widget de editor, posiblemente subclasificando QSpinBox por conveniencia. Esta alternativa nos dará más control sobre el comportamiento del widget del editor, a costa de escribir código extra. Si necesita personalizar el comportamiento de los widgets Qt Editor estándar, generalmente es más fácil instalar filtros de eventos en el delegado.

Los delegados no tienen que emitir estos avisos, pero aquellos que no lo hacen están menos integrados con la aplicación y son menos utilizables que los que emiten avisos para admitir operaciones de edición comunes.

Supongo que te gusta

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