Mecanismo QT Core 2: Sistema de atributos

escrito en frente

Este artículo es básicamente una comprensión de la traducción de algunos capítulos de la documentación oficial de Qt. La razón para traducir estos capítulos es que creo que estos son los elementos centrales de Qt. El proceso de traducción es el proceso de obligarme a leerlos detenidamente. "Copiaré el texto original palabra por palabra, pero traduciré los puntos clave según mi propio entendimiento. Después de todo, mi propósito es entenderlos y organizarlos de una manera que pueda usar con flexibilidad, en lugar de transformarlos mecánicamente de uno". al otro El idioma cambia a otro idioma. El contenido original de los documentos oficiales involucrados incluye principalmente los siguientes capítulos:

  1. El sistema de metaobjetos del sistema de metaobjetos
  2. El sistema de propiedad sistema de propiedad
  3. Señales y tragamonedas Señales y tragamonedas

Se acuerda aquí que la traducción del texto original utiliza fuentes normales y la comprensión personal utiliza fuentes en cursiva.

Se divide en tres artículos en total, este artículo es la traducción del sistema de propiedad The Property System .

Enlaces a las tres traducciones:

Mecanismo QT Core 1: Blog de Metasystem_lczdk-CSDN Blog

Mecanismo central 2 de QT: atributo system_lczdk's blog-CSDN blog

QT Core Mechanism 3: Señales y ranuras - Se busca programador

sistema de atributos

Al igual que los compiladores proporcionados por muchos proveedores de compiladores, Qt también proporciona un sistema de propiedades sofisticado. Sin embargo, como biblioteca independiente del compilador y de la arquitectura, Qt no depende de las características del compilador no estándar, como __property o [property]. Este conjunto de características del sistema de propiedades de Qt se puede usar para cualquier compilador y arquitectura compatibles con Qt. Se basa en el sistema de metaobjetos (Meta-Object System), que también proporciona un mecanismo de señal y ranura para la comunicación entre objetos.

El siguiente extracto explica brevemente la diferencia entre propiedades y variables miembro:

  • La variable miembro es un concepto "interno", que refleja la estructura de la clase. El atributo es un concepto "externo" que refleja el significado lógico de la clase.
  • Las variables miembro no tienen control de acceso de lectura y escritura, mientras que las propiedades se pueden designar como de solo lectura o de solo escritura, o ambas.
  • Las variables miembro no realizan ningún procesamiento posterior para la lectura y no realizan ningún procesamiento previo para la escritura, pero las propiedades sí.
  • Una variable miembro pública se puede considerar como un atributo de lectura y escritura sin ningún procesamiento previo o posterior. Sin embargo, la variable miembro privada no puede considerarse como un atributo porque es invisible para el exterior y no coincide con las características del atributo "exterior".
  • Aunque en la mayoría de los casos, los atributos estarán representados por una o algunas variables miembro, pero no existe una correspondencia necesaria entre los atributos y las variables miembro, por ejemplo, no existe la llamada variable miembro outputcorrespondiente $outputal atributo de la puerta NAND.

La diferencia y la conexión entre los atributos y las variables miembro y la comprensión de los modificadores de atributos

Requisitos de declaración de atributos

Las propiedades se pueden declarar utilizando la definición de macro Q_PROPERTY en una clase que hereda la clase QObject. El formato de uso de esta definición de macro se ilustra a continuación:

Q_PROPERTY(type name
            (READ getFunction [WRITE setFunction] |
             MEMBER memberName [(READ getFunction | WRITE setFunction)])
            [RESET resetFunction]
            [NOTIFY notifySignal]
            [REVISION int]
            [DESIGNABLE bool]
            [SCRIPTABLE bool]
            [STORED bool]
            [USER bool]
            [CONSTANT]
            [FINAL])

el tipo, el nombre y el contenido entre corchetes "()" deben existir, y las partes separadas por "|" entre corchetes son elementos opcionales. Se debe usar al menos uno de ellos, y las opciones entre "[]" son un objeto opcional.

Los siguientes son algunos ejemplos de declaraciones de propiedad en la clase QWidget. Cabe señalar aquí que no es necesario escribir ";" después de la macro Q_PROPERTY, porque el final de la macro ya se ha escrito y, por supuesto, no es nada. para escribir

// 声明一个布尔类型的,名为focus的属性,可以通过hasFocus方法作为访问器进行读取
Q_PROPERTY(bool focus READ hasFocus)
    
// 声明一个布尔类型的,名为enabled的属性,可以通过isEnabled方法读取,通过setEnabled方法写入
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)

// 声明一个QCursor类型的,名为cursor的属性,可以通过setCursor方法读取,通过unsetCursor方法写入
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)

El siguiente es un ejemplo de cómo usar la palabra clave MEMBER para exportar variables miembro como propiedades. Al usar la palabra clave MEMBER para exportar propiedades, debemos tener en cuenta que debemos usar la palabra clave NOTIFY para especificar una señal para que QML pueda realizar vinculación de propiedad.

Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)
     Q_PROPERTY(qreal spacing MEMBER m_spacing NOTIFY spacingChanged)
     Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)
     ...
 signals:
     void colorChanged();
     void spacingChanged();
     void textChanged(const QString &newText);

 private:
     QColor  m_color;
     qreal   m_spacing;
     QString m_text;

Una de las funciones de estos dos métodos de creación de propiedades debería servir para el lenguaje QML de Qt. Al cooperar con el enlace de propiedades, QML puede actualizarse automáticamente a través del acceso especificado por la palabra clave WRITE cuando cambia la propiedad del objeto de control enlazado. El valor del enlace o cuando la propiedad cambia y se envía la señal especificada por SIGNAL, el valor de la propiedad se actualiza automáticamente en el objeto de control enlazado a través del descriptor de acceso READ. Se recomienda consultar el siguiente artículo: https://zhuanlan.zhihu.com/p/152477333 . Actualmente, solo conozco el concepto de QML y nunca lo he usado.

Las propiedades se comportan como miembros de datos de una clase, pero tienen algunas características adicionales cuando se accede a través del sistema de metaobjetos. Los siguientes son algunos requisitos para declarar atributos y sus características* (el énfasis está en negrita, solo se enumeran las palabras clave que deben entenderse en este momento)*:

  • Si no se especifica ninguna variable MEMBER, se requiere el método de acceso READ. Se utiliza para leer valores de propiedad. Idealmente, se puede usar una función const (se garantiza que las funciones const no modifican los miembros de datos en la clase) para este propósito, y debe devolver el tipo de propiedad o una referencia constante a ese tipo. Por ejemplo, QWidget::focus es una propiedad de solo lectura con el método READ QWidget::hasFocus().
  • La función de acceso WRITE es opcional. Se utiliza para establecer valores de propiedad. Debe devolver void y debe aceptar un argumento, ya sea el tipo de propiedad o un puntero o referencia a ese tipo. Por ejemplo, QWidget::enabled tiene una función de ESCRITURA QWidget::setEnabled(). Las propiedades de solo lectura no requieren una función ESCRIBIR. Por ejemplo, QWidget::focus no tiene función de ESCRITURA.
  • Si no se especifica ninguna función de acceso READ, se debe asociar la variable MEMBER. Esto hace que una variable miembro dada sea legible y escribible sin crear funciones de acceso de LECTURA y ESCRITURA. Si necesita controlar el acceso a las variables, puede usar las funciones de acceso READ o WRITE (pero no ambas) además de la asociación de variables MEMBER. Se puede entender aquí que al usar la palabra clave MEMBER para asociar una variable como atributo, Qt generará automáticamente los métodos READ y WRITE predeterminados.
  • La función NOTIFY es opcional. Si se define, debe especificar una señal existente en la clase que debe emitirse siempre que cambie el valor de la propiedad (normalmente cuando se llama a un descriptor de acceso WRITE para actualizar la propiedad). La señal NOTIFY de la variable MEMBER debe aceptar parámetros 0 o 1, que almacenan el valor modificado y son del mismo tipo que la variable MEMBER. La señal NOTIFY solo debe emitirse cuando la propiedad realmente cambia, para evitar volver a calcular innecesariamente los enlaces en QML. Qt emite esta señal automáticamente cuando la propiedad MEMBER no tiene un descriptor de acceso WRITE explícito.

Los métodos READ, WRITE, RESET se pueden heredar. También pueden ser virtuales. Cuando son heredados por una clase que usa herencia múltiple, deben provenir de la primera clase heredada.

El tipo de atributo puede ser cualquier tipo admitido por QVariant (la clase QVariant puede considerarse como la unión de los tipos de datos Qt más comunes) o puede ser un tipo definido por el usuario. En este ejemplo, la clase QDate se considera como un tipo definido por el usuario.

Q_PROPERTY(QDate date READ getDate WRITE setDate)

Dado que QDate está definido por el usuario, debe incluir el encabezado "QDate" en la declaración de propiedad.

Propiedades de lectura y escritura utilizando el sistema de metaobjetos

Utilice los métodos generales QObject::property() y QObject::setProperty() para leer y escribir propiedades sin conocer solo el nombre de la propiedad. En el fragmento de código a continuación, la llamada a QAbstractButton::setDown() y la llamada a QObject::setProperty() establecen la propiedad "abajo".

 //以下例子中,当我们使用object对象指针访问对象时,我们只知道这个对象继承了QObject,但是只要知道属性名字,依然可以正常访问属性。
 QPushButton *button = new QPushButton;
 QObject *object = button;

 button->setDown(true);
 object->setProperty("down", true);

Es más recomendable acceder a las propiedades a través de su accesor WRITE, ya que es más rápido y admite diagnósticos en el momento de la compilación, pero configurar las propiedades de esta manera requiere que conozca la clase en el momento de la compilación. Acceder a las propiedades por nombre le da acceso a clases que no conoce en tiempo de compilación. Puede consultar las propiedades de una clase en tiempo de ejecución consultando el QObject, QMetaObject y QMetaProperties de la clase.

//以下例子中object指针指向了某个继承自QObject类的对象,之后通过访问元对象系统的相关对象查询属性的总数,并遍历这些属性获取属性名称与属性值。 
QObject *object = ...
 const QMetaObject *metaobject = object->metaObject();
 int count = metaobject->propertyCount();
 for (int i=0; i<count; ++i) {
    
    
     QMetaProperty metaproperty = metaobject->property(i);
     const char *name = metaproperty.name();
     QVariant value = object->property(name);
     ...
 }

En el fragmento de código anterior, QMetaObject::property() se usa para obtener metadatos sobre cada propiedad definida en alguna clase desconocida. El nombre de la propiedad se obtiene de los metadatos y se pasa a QObject::property() para obtener el valor de la propiedad en el objeto actual.

Un ejemplo completo de declaración de propiedades y propiedades de lectura y escritura

Supongamos que tenemos una clase MyClass que se deriva de QObject y usa la macro Q_OBJECT en su sección privada. Queremos declarar una propiedad en MyClass para realizar un seguimiento del valor de prioridad. El nombre de la propiedad será prioridad y su tipo será un tipo de enumeración llamado prioridad que se define en MyClass.

Declaramos esta propiedad usando la macro Q_PROPERTY() en la parte privada de la clase. La función de LECTURA requerida se llama prioridad, y también incluimos una función de ESCRITURA llamada establecerPrioridad. Los tipos enumerados deben registrarse con el sistema de metaobjetos utilizando la macro Q_ENUM(). Registre el tipo de enumeración para que el nombre del enumerador se pueda usar al llamar a QObject::setProperty() * (al configurar el tipo de propiedad, ingrese directamente el nombre de enumeración en formato de cadena para asignar el valor de enumeración correspondiente a la propiedad, luego Veré ejemplos de uso en el fragmento de código que llama a la demostración)*. También tenemos que proporcionar nuestras propias declaraciones para las funciones READ y WRITE. Entonces la declaración de MyClass podría verse así:

 class MyClass : public QObject
 {
    
    
     Q_OBJECT
     Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)

 public:
     MyClass(QObject *parent = 0);
     ~MyClass();

     enum Priority {
    
     High, Low, VeryHigh, VeryLow };
     
     //向元对象系统注册枚举,后面使用setPriority时就可以直接使用枚举名称来映射枚举值
     Q_ENUM(Priority) 

     void setPriority(Priority priority)
     {
    
    
         m_priority = priority;
         emit priorityChanged(priority);
     }
     
     //const函数,声明方法为const方法表明此方法不会改动类中的数据成员变量
     Priority priority() const
     {
    
     return m_priority; }

 signals:
     void priorityChanged(Priority);

 private:
     Priority m_priority;
 };

El método de acceso READ es un método estático que se utiliza para devolver la prioridad actual. El método de acceso WRITE devuelve void y solo tiene un parámetro para pasar la nueva prioridad. Esto es para cumplir con los requisitos del sistema de metaobjetos.

Dado un puntero a una instancia de MyClass o un puntero a un QObject que es una instancia de MyClass, tenemos dos formas de establecer su propiedad de prioridad:

 MyClass *myinstance = new MyClass;
 QObject *object = myinstance;

 myinstance->setPriority(MyClass::VeryHigh);

//前面使用了Q_ENUM宏定义注册了枚举名字,这样我们就可以直接传入字符串格式的枚举名字,元对象系统会自动给属性赋值名字对应的枚举值
 object->setProperty("priority", "VeryHigh");

En este ejemplo, el tipo enumerado, el tipo de propiedad, se declara en MyClass y se registra con el sistema de metaobjetos usando la macro Q_ENUM(). Esto hace que el valor de enumeración esté disponible como una cadena al llamar a setProperty(). Si el tipo de enumeración se declara en otra clase, se requiere su nombre completamente calificado (es decir, OtherClass::Priority). Si se usa el mismo nombre de enumeración en varias clases, entonces debemos especificar su nombre completamente calificado al usarlo. Nombre ( por ejemplo: OtherClass::Priority), y otras clases también necesitan heredar QObject y usar la definición de macro Q_ENUM() para registrar el tipo de enumeración en ella.

También se puede utilizar una macro similar Q_FLAG(). Al igual que Q_ENUM(), registra un tipo de enumeración, pero marca el tipo como un conjunto de banderas, es decir, valores que se pueden combinar mediante ORing bit a bit. Una clase de E/S puede tener valores de enumeración de Lectura y Escritura. Si desea que QObject::setProperty() acepte el uso de Lectura | Escritura, debe usar Q_FLAG() para registrar este tipo de enumeración.

propiedades dinámicas

QObject::setProperty() también se puede usar para agregar nuevas propiedades a las instancias de una clase en tiempo de ejecución. Cuando se llama con un nombre y un valor, si existe una propiedad con el mismo nombre en el QObject y el valor dado es compatible con el tipo de propiedad, el valor se almacena en la propiedad existente y se devuelve verdadero. Si el valor no es compatible con el tipo de propiedad, la propiedad no cambia y se devuelve falso. Sin embargo, si una propiedad con el nombre especificado no existe en el QObject (por ejemplo, si no se declaró con Q_PROPERTY()), se agregará automáticamente una nueva propiedad con el nombre y el valor proporcionados al QObject, pero seguirá siendo falso. devuelto Esto significa que devolver falso no se puede usar para determinar si una propiedad en particular está realmente establecida, a menos que sepa de antemano que la propiedad ya existe en el QObject.

Tenga en cuenta que las propiedades dinámicas se agregan por instancia, es decir, se agregan al QObject, no al QMetaObject. Se puede eliminar una propiedad de una instancia pasando el nombre de la propiedad y un valor QVariant no válido a QObject::setProperty(). El constructor predeterminado de QVariant construye un QVariant no válido.

Las propiedades dinámicas se pueden consultar usando QObject::property(), al igual que las propiedades declaradas en tiempo de compilación usando Q_PROPERTY().

Crear propiedades con tipos personalizados

En C++, podemos crear tipos personalizados a través de typedef, union, enum, struct, etc.

Los tipos personalizados utilizados por las propiedades deben registrarse con la macro Q_DECLARE_METATYPE() para que sus valores puedan almacenarse en los objetos QVariant. De esta manera, pueden usarse para propiedades estáticas creadas con Q_PROPERTY o propiedades dinámicas creadas en tiempo de ejecución.

El siguiente ejemplo muestra cómo registrar un tipo personalizado:

 // 注册一个结构体类型
 struct MyStruct
 {
     int i;
     ...
 };

 Q_DECLARE_METATYPE(MyStruct)
 // 如果存在命名空间,Q_DECLARE_METATYPE宏必须位于命名空间外,并且注册时指明类型所处的命名空间 
 namespace MyNamespace
 {
    
    
     ...
 }

 Q_DECLARE_METATYPE(MyNamespace::MyStruct)

Añadir información extra a la clase

El sistema de propiedades viene con una macro llamada Q_CLASSINFO() que se puede usar para adjuntar pares de nombre-valor adicionales al metaobjeto de la clase:

Q_CLASSINFO("Version", "3.0.0")

Se puede acceder a la información de clase en tiempo de ejecución como otros metadatos (metadatos) usando QMetaObject::classInfo().

Supongo que te gusta

Origin blog.csdn.net/lczdk/article/details/125026257
Recomendado
Clasificación