Qt Internationalization

Most real projects have a target audience in multiple countries. The most notable difference between them is the spoken language, but there are other aspects some developers may not think of. For example, dot "." and comma "," are both fairly common as the decimal separator throughout the world. Date formats are also very different and incompatible, and using a wrong format (for example, mm/dd/yyyy instead of dd/mm/yyyy) will result in a completely different date.

Qt provides the QLocale class for dealing with locale-dependent operations, including conversions between numbers in strings. In the following code, text and number may have different values, depending on the system locale:

QLocale locale = QLocale::system();
QString text = locale.toString(1.2);
double number = locale.toDouble(QStringLiteral("1,2"));

QLocale also provides methods for formatting dates and prices, and allows us to request additional information about local conventions.

Translator

ts files

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en" sourcelanguage="en">
<context>
    <name>QApplication</name>
    <message>
        <source>Hello2</source>
        <translatorcomment>there are no comment</translatorcomment>
        <translation>iam good</translation>
    </message>
</context>
<context>
    <name>QLabel</name>
    <message>
        <source>Hello</source>
        <translatorcomment>there are no comment</translatorcomment>
        <translation>iam good</translation>
    </message>
</context>
</TS>

in Qt Linguist you can release ts file to qm binary file to load by QTranslator

QTranslator translator;
if(translator.load("PNP_en_US","..\\PNP\\")){
   qDebug()<<"load translation file";
}
a.installTranslator(&translator);
qDebug()<<a.tr("Hello2");
qDebug()<<QLabel::tr("Hello");

String handling

Applications with a graphical user interface (and games surely fall into this category) are able to interact with users by displaying text and by expecting textual input from the user. We have already scratched the surface of this topic in the previous chapters using the QString class. Now, we will go into further detail.

String encodings

The C++ language does not specify encoding of strings. Thus, any char* array and any std::string object can use an arbitrary encoding. When using these types for interaction with native APIs and third-party libraries, you have to refer to their documentation to find out which encoding they use. The encoding used by native APIs of the operating system usually depends on the current locale. Third-party libraries often use the same encoding as native APIs, but some libraries may expect another encoding, for example, UTF-8.

A string literal (that is, each bare text you wrap in quotation marks) will use an implementation defined encoding. Since C++11, you have an option to specify the encoding your text will have:

u8"text" will produce a UTF-8 encoded const char[] array
u"text" will produce a UTF-16 encoded const char16_t[] array
U"text" will produce a UTF-32 encoded const char32_t[] array

Unfortunately, the encoding used for interpreting the source files is still implementation defined, so it’s not safe to put non-ASCII symbols in string literals. You should use escape sequences (such as \unnnn) to write such literals.

Text in Qt is stored using the QString class that uses Unicode internally. Unicode allows us to represent characters in almost all languages spoken in the world and is the de facto standard for native encoding of text in most modern operating systems. There are multiple Unicode-based encodings. Memory representation of the content of QString resembles UTF-16 encoding. Basically, it consists of an array of 16-bit values where each Unicode character is represented by either 1 or 2 values.

When constructing a QString from a char array or an std::string object, it’s important to use a proper conversion method that depends on the initial encoding of the text. By default, QString assumes UTF-8 encoding of the input text. UTF-8 is compatible with ASCII, so passing UTF-8 or ASCII-only text to QString(const char *str) is correct. QString provides a number of static methods to convert from other encodings such as QString::fromLatin1() or QString::fromUtf16()QString::fromLocal8Bit() method assumes the encoding corresponding to the system locale.

If you have to combine both QString and std::string in one program, QString offers you the toStdString() and fromStdString() methods to perform a conversion. These methods also assume UTF-8 encoding of std::string, so you can’t use them if your strings are in another encoding.

Default representation of string literals (for example, "text") is not UTF-16, so each time you convert it to a QString, an allocation and conversion happens. This overhead can be avoided using the QStringLiteral macro:

QString str = QStringLiteral("I'm writing my games using Qt"); 

QStringLiteral does two things:

  • It adds a u prefix to your string literal to ensure that it will be encoded in UTF-16 at compile time
  • It cheaply creates a QString and instructs it to use the literal without performing any allocation or encoding conversion

It’s a good habit to wrap all your string literals (except the ones that need to be translated) into QStringLiteral but it is not required, so don’t worry if you forget to do that.

QByteArray and QString

QString always contains UTF-16 encoded strings, but what if you have data in an unknown (yet) encoding? Also, what if the data is not even text? In these cases, Qt uses the QByteArray class. When you read data directly from a file or receive it from a network socket, Qt will return the data as a QByteArray, indicating that this is an arbitrary array of bytes without any information about the encoding:

QFile file("/path/to/file");
file.open(QFile::ReadOnly);
QByteArray array = file.readAll();

The closest equivalent of QByteArray in the standard library would be std::vector<char>. As the name implies, this is just an array of bytes with some helpful methods. In the preceding example, if you know that the file you read is in UTF-8, you can convert the data to a string, as follows:

QString text = QString::fromUtf8(array);

If you have no idea what encoding the file uses, it may be best to use the system encoding, so QString::fromLocal8Bit would be better. Similarly, when writing to a file, you need to convert the string to a byte array before passing it to the write() function:

QString text = "new file content\n";
QFile file("/path/to/file");
file.open(QFile::WriteOnly);
QByteArray array = text.toUtf8();
file.write(array);

Basic string operations

The most basic tasks that involve text strings are the ones where you add or remove characters from the string, concatenate strings, and access the string’s content. In this regard, QString offers an interface that is compatible with std::string, but it also goes beyond that, exposing many more useful methods.

Adding data at the beginning or at the end of the string can be done using the prepend() and append() methods. Inserting data in the middle of a string can be done with the insert() method that takes the position of the character where we need to start inserting as its first argument and the actual text as its second argument. All these methods have a couple of overloads that accept different objects that can hold textual data, including the classic const char* array.

Removing characters from a string is similar. The basic way to do this is to use the remove() method that accepts the position at which we need to delete characters, and the number of characters to delete is as shown:

QString str = QStringLiteral("abcdefghij");
str.remove(2, 4); // str = "abghij" 

There is also a remove() overload that accepts another string. When called, all its occurrences are removed from the original string. This overload has an optional argument that states whether comparison should be done in the default case-sensitive (Qt::CaseSensitive) or case-insensitive (Qt::CaseInsensitive) way:

QString str = QStringLiteral("Abracadabra");
str.remove(QStringLiteral("ab"), Qt::CaseInsensitive);
// str = "racadra"

To concatenate strings, you can either simply add two strings together, or you can append one string to the other:

QString str1 = QStringLiteral("abc");
QString str2 = QStringLiteral("def");
QString str1_2 = str1 + str2;
QString str2_1 = str2;
str2_1.append(str1); 

Accessing strings can be divided into two use cases. The first is when you wish to extract a part of the string. For this, you can use one of these three methods—left()right(), and mid()—that return the given number of characters from the beginning or end of the string or extract a substring of a specified length, starting from a given position in the string:

QString original = QStringLiteral("abcdefghij");
QString l = original.left(3); // "abc"
QString r = original.right(2); // "ij"
QString m = original.mid(2, 5); // "cdefg" 

The second use case is when you wish to access a single character of the string. The use of the index operator works with QString in a similar fashion as with std::string, returning a copy or non-const reference to a given character that is represented by the QChar class, as shown in the following code:

QString str = "foo";
QChar f = str[0]; // const
str[0] = 'g'; // non-const 
QChar f = str.at(0); 

The string search and lookup

The second group of functionalities is related to searching for the string. You can use methods such as startsWith()endsWith(), and contains() to search for substrings in the beginning or end or in an arbitrary place in the string. The number of occurrences of a substring in the string can be retrieved using the count() method.

If you need to know the exact position of the match, you can use indexOf() or lastIndexOf() to receive the position in the string where the match occurs. The first call works by searching forward, and the other one searches backwards. Each of these calls takes two optional parameters—the second one determines whether the search is case-sensitive (similar to how remove works). The first one is the position in the string where the search begins. It lets you find all the occurrences of a given substring:

int pos = -1;
QString str = QStringLiteral("Orangutans like bananas.");
do {
    pos = str.indexOf("an", pos + 1);
    qDebug() << "'an' found starts at position" << pos;
} while(pos != -1); 

Dissecting strings

There is one more group of useful string functionalities that makes QString different from std::string, that is, cutting strings into smaller parts and building larger strings from smaller pieces.

Very often, a string contains substrings that are glued together by a repeating separator (for example, "1,4,8,15"). While you can extract each field from the record using functions that you already know (for example, indexOf), an easier way exists. QString contains a split() method that takes the separator string as its parameter and returns a list of strings that are represented in Qt by the QStringList class. Then, dissecting the record into separate fields is as easy as calling the following code:

QString record = "1,4,8,15,16,24,42";
QStringList items = record.split(",");
for(const QString& item: items) {
    qDebug() << item;
}

The inverse of this method is the join() method present in the QStringList class, which returns all the items in the list as a single string merged with a given separator:

QStringList fields = { "1", "4", "8", "15", "16", "24", "42" };
QString record = fields.join(","); 

Converting between numbers and strings

QString also provides some methods for convenient conversion between textual and numerical values. Methods such as toInt()toDouble(), or toLongLong() make it easy to extract numerical values from strings. All such methods take an optional bool *ok parameter. If you pass a pointer to a bool variable as this parameter, the variable will be set to true or false, depending on whether the conversion was successful or not. Methods returning integers also take the second optional parameter that specifies the numerical base (for example, binary, octal, decimal, or hexadecimal) of the value:

bool ok;
int v1 = QString("42").toInt(&ok, 10);
// v1 = 42, ok = true
long long v2 = QString("0xFFFFFF").toInt(&ok, 16);
// v2 = 16777215, ok = true
double v3 = QString("not really a number").toDouble(&ok);
//v3 = 0.0, ok = false

A static method called number() performs the conversion in the other direction—it takes a numerical value and number base and returns the textual representation of the value:

QString txt = QString::number(42); // txt = "42" 

This function has some optional arguments that allow you to control the string representation of the number. For integers, you can specify the numerical base. For doubles, you can choose the scientific format 'e' or the conventional format 'f' and specify the number of digits after the decimal delimiter:

QString s1 = QString::number(42, 16); // "2a"
QString s2 = QString::number(42.0, 'f', 6); // "42.000000"
QString s3 = QString::number(42.0, 'e', 6); // "4.200000e+1"
QString str;
str.setNum(1234);       // str == "1234"

Other Useful Fuctions

QString str;
QString csv = "forename,middlename,surname,phone";
QString path = "/usr/local/bin/myapp"; // First field is empty
QString::SectionFlag flag = QString::SectionSkipEmpty;

str = csv.section(',', 2, 2);   // str == "surname"
str = path.section('/', 3, 4);  // str == "bin/myapp"
str = path.section('/', 3, 3, flag); // str == "myapp"
str = csv.section(',', -3, -2);  // str == "middlename,surname"
str = path.section('/', -1); // str == "myapp"
QString str = "  lots\t of\nwhitespace\r\n ";
str = str.simplified();
// str == "lots of whitespace";

and are there iterator functions like begin and end

QString x = "Say yes!";
QString y = "no";
x.replace(4, 3, y);
// x == "Say no!"
QString s = "Banana";
s.replace(QRegExp("a[mn]"), "ox");
// s == "Boxoxa"
QString str = "colour behaviour flavour neighbour";
str.replace(QString("ou"), QString("o"));
// str == "color behavior flavor neighbor"
QString str("LOGOUT\r\n");
str.chop(2);//remove n characters from the end 
// str == "LOGOUT"
QString str = "Vladivostok";
str.truncate(4);
// str == "Vlad"

Using arguments in strings

const int fieldWidth = 4;
qDebug() << QStringLiteral("%1 | %2").arg(5, fieldWidth).arg(6, fieldWidth);
qDebug() << QStringLiteral("%1 | %2").arg(15, fieldWidth).arg(16, fieldWidth);
// output:
// "   5 |    6"
// "  15 |   16"
QString str = tr("Copying file %1 of %2").arg(current).arg(total); 

Regular expressions

QRegularExpression regex("[1-9]\\d{0,2}\\s*(mg|g|kg)");
regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
qDebug() << regex.match("100 kg").hasMatch();       // true
qDebug() << regex.match("I don't know").hasMatch(); // false
QRegularExpression regex("[1-9]\\d{0,2}\\s*(mg|g|kg)",
     QRegularExpression::CaseInsensitiveOption);

When we need to test an input, all we have to do is call match(), passing the string we would like to check against it. In return, we get an object of the QRegularExpressionMatch type that contains all the information that is further needed—and not only to check the validity. With QRegularExpressionMatch::hasMatch(), we then can determine whether the input matches our criteria, as it returns true if the pattern could be found. Otherwise, of course, false is returned.

After we have checked that the sent guess is well formed, we have to extract the actual weight from the string. In order to be able to easily compare the different guesses, we further need to transform all values to a common reference unit. In this case, it should be a milligram, the lowest unit. So, let’s see what QRegularExpressionMatch can offer us for this task.

With capturedTexts(), we get a string list of the pattern’s captured groups. In our example, this list will contain “23kg” and “kg”. The first element is always the string that was fully matched by the pattern. The next elements are all the substrings captured by the used brackets. Since we are missing the actual number, we have to alter the pattern’s beginning to ([1-9]\d{0,2}). Now, the list’s second element is the number, and the third element is the unit. Thus, we can write the following:

int getWeight(const QString &input) {
    QRegularExpression regex("\\A([1-9]\\d{0,2})\\s*(mg|g|kg)\\z");
    regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
    QRegularExpressionMatch match = regex.match(input);
    if(match.hasMatch()) {
        const QString number = match.captured(1);
        int weight = number.toInt();
        const QString unit = match.captured(2).toLower();
        if (unit == "g") {
            weight *= 1000;
        } else if (unit == "kg") {
            weight *= 1000000 ;
        }
        return weight;
    } else {
        return -1;
    }
}

In the function’s first two lines, we set up the pattern and its option. Then, we match it against the passed argument. If QRegularExpressionMatch::hasMatch() returns true, the input is valid and we extract the number and unit. Instead of fetching the entire list of captured text with capturedTexts(), we query specific elements directly by calling QRegularExpressionMatch::captured(). The passed integer argument signifies the element’s position inside the list. So, calling captured(1) returns the matched digits as a QString.

Lastly, let’s take a final look at how to find, for example, all numbers inside a string, even those leading with zeros:

QString input = QStringLiteral("123 foo 09 1a 3");
QRegularExpression regex("\\b\\d+\\b");
QRegularExpressionMatchIterator i = regex.globalMatch(input);
while (i.hasNext()) {
    QRegularExpressionMatch match = i.next();
    qDebug() << match.captured();
}

very important point you can use regular expression with almost functions of QString like split , indexof , contains , count, replace, remove and section

List Of String

filter by sub string or regular expression

QStringList list;
    list << "Bill Murray" << "John Doe" << "Bill Clinton";

    QStringList result;
    result = list.filter("Bill");
    // result: ["Bill Murray", "Bill Clinton"]

indexOf and lastIndexOf by full string or regular expression also join and removeDuplicate and replaceInStrings

QStringList list;
    list << "alpha" << "beta" << "gamma" << "epsilon";
    list.replaceInStrings("a", "o");
    // list == ["olpho", "beto", "gommo", "epsilon"]
 QStringList list;
    list << "alpha" << "beta" << "gamma" << "epsilon";
    list.replaceInStrings(QRegularExpression("^a"), "o");
    // list == ["olpha", "beta", "gamma", "epsilon"]

and sort

void QStringList::sort(Qt::CaseSensitivity cs = Qt::CaseSensitive)

QStringRef 

The QStringRef class provides a thin wrapper around QString substrings.
and you can get it from QString by

leftRef(int n) const
midRef(int position, int n = -1)
rightRef(int n) const
splitRef(const QString &sep, Qt::SplitBehavior behavior = Qt::KeepEmptyParts, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
QStringRef(const QString *string, int position, int length)

Qt Meta Type

The QMetaType class manages named types in the meta-object system

The class is used as a helper to marshall types in QVariant and in queued signals and slots connections. It associates a type name to a type so that it can be created and destructed dynamically at run-time. Declare new types with Q_DECLARE_METATYPE() to make them available to QVariant and other template-based functions. Call qRegisterMetaType() to make types available to non-template based functions, such as the queued signal and slot connections.

Q_DECLARE_METATYPE(MyStruct)
MyStruct s;
QVariant var;
var.setValue(s); // copy s into the variant
// retrieve the value
MyStruct s2 = var.value<MyStruct>();

what can you do once you register type with meta types?

  • create object with type id
  • value as Qvariant
  • streaming data of object (save ,load ,<<,>>,…)(qRegisterMetaTypeStreamOperators<type>(type_name))
  • pass values betweens queued signals and slots connections

struct Test2
{
    int i;
};
Q_DECLARE_METATYPE(Test2);

QDataStream &operator<<(QDataStream &out, const Test2 &myObj)
{
    out<<myObj.i;
    return out;
}

QDataStream &operator>>(QDataStream &in, Test2 &myObj)
{
    in>>myObj.i;
    return in;
}

qRegisterMetaTypeStreamOperators<Test2>("Test2");

QVariant var1;
Test2 t1{.i=45};
var1.setValue(t1);
QFile f("./hello.txt");
f.open(QIODevice::WriteOnly);
QDataStream ds(&f);
ds<<var1;
f.close();

QVariant var2;
Test2 t2;
var2.setValue(t2);
f.open(QIODevice::ReadOnly);
ds>>var2;
f.close();
 qDebug()<<"value is : "<<var2.value<Test2>().i;//45
qMetaTypeId<type>()//return type id of type
QMetaType::fromType<type>()//return QMetaType 
QMetaType::type("Test2");//return type id by type name

or you can use member functions

QMetaObject* metaObject();// if it subclass of QObject return meta object
void* create(const void *copy = 0)// return pointer for copy of copy
void* construct(void *where, const void *copy = 0)// create copy of copy with determined adress (where)
destroy(void*)//
destruct(int type,void* where)// call destruct function without delete operator

QVariant

//before you can use any type as variant you sould to use Q_DECLARE_METATYPE
qvariant_cast<Type>(variant);//cast variant to type
variant.value<Type>();//cast value to type

Qt Objects

The QObject class is the base class of all Qt objects

Q_OBJECT Macro

The Q_OBJECT Macro is probably one of the weirdest things to whoever beginning to use Qt.

Qt QObject Class says:

The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt’s meta-object system.

class MyClass : public QObject
{
    Q_OBJECT

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

signals:
    void mySignal();

public slots:
    void mySlot();
};
class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Priority priority READ priority WRITE setPriority)
    Q_ENUMS(Priority)

public:
    enum Priority { High, Low, VeryHigh, VeryLow };

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

    void setPriority(Priority priority) { m_priority = priority; }
    Priority priority() const { return m_priority; }

private:
    Priority m_priority;

So, it sounds like we need it to use signal and slot, and probably for other purposes (meta-object related) as well.

Another doc related to moc explains:

The Meta-Object Compiler, moc, is the program that handles Qt’s C++ extensions.

The moc tool reads a C++ header file. If it finds one or more class declarations that contain the Q_OBJECT macro, it produces a C++ source file containing the meta-object code for those classes. Among other things, meta-object code is required for the signals and slots mechanism, the run-time type information, and the dynamic property system.

moc limitations

Multiple Inheritance Requires QObject to Be First

// correct
class SomeClass : public QObject, public OtherClass
{
    ...
};

Function Pointers Cannot Be Signal or Slot Parameters

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(void (*apply)(List *, void *), char *); // WRONG
};

// instead you can do next
typedef void (*ApplyFunction)(List *, void *);

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(ApplyFunction, char *);
};

Nested Classes Cannot Have Signals or Slots

class A
{
public:
    class B
    {
        Q_OBJECT

    public slots:   // WRONG
        void b();
    };
};

Signal/Slot return types cannot be references

Only Signals and Slots May Appear in the signals and slots Sections of a Class

QMetaObject

The QMetaObject class contains meta-information about Qt objects

The Qt Meta-Object System in Qt is responsible for the signals and slots inter-object communication mechanism, runtime type information, and the Qt property system. A single QMetaObject instance is created for each QObject subclass that is used in an application, and this instance stores all the meta-information for the QObject subclass. This object is available as QObject::metaObject().

This class is not normally required for application programming, but it is useful if you write meta-applications, such as scripting engines or GUI builders.

The functions you are most likely to find useful are these:

The index functions indexOfConstructor(), indexOfMethod(), indexOfEnumerator(), and indexOfProperty() map names of constructors, member functions, enumerators, or properties to indexes in the meta-object. For example, Qt uses indexOfMethod() internally when you connect a signal to a slot.

Classes can also have a list of namevalue pairs of additional class information, stored in QMetaClassInfo objects. The number of pairs is returned by classInfoCount(), single pairs are returned by classInfo(), and you can search for pairs with indexOfClassInfo().

Q_INVOKABLE void fun();
emmiter->metaObject()->method(emmiter->metaObject()->indexOfMethod("fun()")).invoke(emmiter,Qt::ConnectionType::DirectConnection,Q_ARG(QObject*, emmiter));

private slots:
    void MySlot(QObject* obj);

QMetaObject::invokeMethod(emmiter,"MySlot",Qt::ConnectionType::DirectConnection,Q_ARG(QObject*, emmiter));
//invoke methods defined with public slots or private slots or Q_INVOKABLE 

class Test:QObject
{
    Q_OBJECT
public:
    enum Myen{A,B,C};
    Q_ENUM(Myen);

    Test();
    Q_INVOKABLE void fun(Test::Myen en);
};
Q_DECLARE_METATYPE(Test::Myen);


bool b=QMetaObject::invokeMethod(this,"fun",Qt::ConnectionType::QueuedConnection,Q_ARG(Test::Myen,A));

//call fun function Test::A

QObject  Structure

QObjects organize themselves in object trees. When you create a QObject with another object as parent, the object will automatically add itself to the parent’s children() list. The parent takes ownership of the object; i.e., it will automatically delete its children in its destructor. You can look for an object by name and optionally type using findChild() or findChildren().

QObject(QObject *parent = nullptr)
parent() const
setParent(QObject *parent)
objectName() const
setObjectName(const QString &name)
findChild(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) 
findChildren(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively)
children() const

inherits check

QTimer *timer = new QTimer;         // QTimer inherits QObject
timer->inherits("QTimer");          // returns true
timer->inherits("QObject");         // returns true
timer->inherits("QAbstractButton"); // returns false

// QVBoxLayout inherits QObject and QLayoutItem
QVBoxLayout *layout = new QVBoxLayout;
layout->inherits("QObject");        // returns true
layout->inherits("QLayoutItem");    // returns true (even though QLayoutItem is not a QObject)

QObject *obj = new QTimer;          // QTimer inherits QObject

QTimer *timer = qobject_cast<QTimer *>(obj);
// timer == (QObject *)obj

QAbstractButton *button = qobject_cast<QAbstractButton *>(obj);
// button == nullptr

Dynamic Properties (QMetaProperty)

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 };
    Q_ENUM(Priority)//registered as QMetaEnum

    void setPriority(Priority priority)// is not diffrent if private or public
    {
        m_priority = priority;
        emit priorityChanged(priority);
    }
    Priority priority() const
    { return m_priority; }

signals:
    void priorityChanged(Priority);

private:
    Priority m_priority;
};

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

myinstance->setPriority(MyClass::VeryHigh);
object->setProperty("priority", "VeryHigh");
property(const char *name) const//call read function or get value of member
bool QObject::setProperty(const char *name, const QVariant &value)//call write function or set value to member
class Test:QObject
{
    Q_OBJECT
public:
    enum Myen{A,B,C};
    Q_ENUM(Myen);

    Test();
    Q_INVOKABLE void fun(Test::Myen en);// once you declare metataype as Test::Myen the argument should to be Test::Myen
};
Q_DECLARE_METATYPE(Test::Myen);

class A{
public:
    operator QString(){
        return QStringLiteral("A");
    }
};
Q_DECLARE_METATYPE(A);
Test::Test()
{
    QString retval;
    bool b=QMetaObject::invokeMethod(this,"fun",Qt::ConnectionType::QueuedConnection,Q_ARG(Test::Myen,A));
    qDebug()<<"end constructor";
}

void Test::fun(Test::Myen en)
{
    qDebug()<<"call fun function"<<en;
}

Signals and Slots

observer design pattern
#include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[])
{
   QApplication app(argc, argv);
   QPushButton *quitButton = new QPushButton("Quit");
   QObject::connect(quitButton, SIGNAL(clicked()),
           &app, SLOT(quit()));
   quitButton->show();
return app.exec();
}

A signal can also be connected to another signal:

class MyWidget::public QWidget
{
    Q_OBJECT

public:
    MyWidget();

signals:
    void buttonClicked();

private:
    QPushButton *myButton;
};

MyWidget::MyWidget()
{
    myButton = new QPushButton(this);
    connect(myButton, SIGNAL(clicked()),
            this, SIGNAL(buttonClicked()));
}

The signal must be a function declared as a signal in the header. The slot function can be any member function that can be connected to the signal. A slot can be connected to a given signal if the signal has at least as many arguments as the slot, and there is an implicit conversion between the types of the corresponding arguments in the signal and the slot.

QLabel *label = new QLabel;
QLineEdit *lineEdit = new QLineEdit;
QObject::connect(lineEdit, &QLineEdit::textChanged,
                 label,  &QLabel::setText);
connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection)
connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection)
connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
all connect functions return QMetaObject::Connection we can use it for disconnect later

enum Qt::ConnectionType

This enum describes the types of connection that can be used between signals and slots. In particular, it determines whether a particular signal is delivered to a slot immediately or queued for delivery at a later time.

Constant
Qt::AutoConnection
(Default) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.
Qt::DirectConnection
The slot is invoked immediately when the signal is emitted. The slot is executed in the signalling thread.
Qt::QueuedConnection
The slot is invoked when control returns to the event loop of the receiver’s thread. The slot is executed in the receiver’s thread.
Qt::BlockingQueuedConnection
Same as Qt::QueuedConnection, except that the signalling thread blocks until the slot returns. This connection must not be used if the receiver lives in the signalling thread, or else the application will deadlock.
Qt::UniqueConnection
This is a flag that can be combined with any one of the above connection types, using a bitwise OR. When Qt::UniqueConnection is set, QObject::connect() will fail if the connection already exists (i.e. if the same signal is already connected to the same slot for the same pair of objects). This flag was introduced in Qt 4.6.
#include <QtCore>

class Test : public QObject
{
    Q_OBJECT
public:
    explicit Test(QObject *parent = nullptr);

signals:
    void MySignal();
public:
    void EmitSignal();
public slots://not necesorry only if you wnat to use it as SLOT(MySlot())
    void MySlot();

};
#include "test.h"

Test::Test(QObject *parent) : QObject(parent)
{

}

void Test::EmitSignal(){
    emit this->MySignal();
    qDebug()<<"signal is emmited";
}

void Test::MySlot(){
    qDebug()<<"slot is called"<<sender();
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Test emmiter,reciver;

    QObject::connect(&emmiter,&Test::MySignal,&reciver,&Test::MySlot,Qt::ConnectionType::QueuedConnection);
    //print signal is emmited then slot is called
    
    QObject::connect(&emmiter,&Test::MySignal,&reciver,&Test::MySlot,Qt::ConnectionType::DirectConnection);
    //print slot is called then signal is emmited

    emmiter.EmitSignal();
    return a.exec();
}

there are many ways to add signal and slot to connect

QObject::connect(&emmiter,SIGNAL(MySignal()),&reciver,SLOT(MySlot()));//slot here should to be Q_INVOKABLE or public private slots:
QObject::connect(&emmiter,&Test::MySignal,&reciver,&Test::MySlot)// slot here can be any function member of object

disconnect

disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
disconnect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method)
disconnect(const QMetaObject::Connection &connection)
disconnect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method)
all of them return bool value

before we were use public static method to connect and disconnect but actually you can use public non-static members

disconnect(const char *signal = nullptr, const QObject *receiver = nullptr, const char *method = nullptr)
connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
disconnect(const QObject *receiver, const char *method = nullptr) const
void blockSignals(bool block) // non-static member to block any signal come from this object
bool signalsBlocked() const// to check if signals comming from this object is blocked or not
int	receivers(const char *signal) const //protected method number of recivers
QObject *	sender() const // protected method can use it inside any slot to get the sender object
int	senderSignalIndex() const //meta-method index of the signal that called the currently executing slot
QCoreApplication a(argc, argv);
Test *emmiter=new Test{} ,*reciver=new Test{};
QObject::connect(emmiter,&Test::destroyed,reciver,&Test::MySlot);
qDebug()<<emmiter;
delete emmiter;
return a.exec();
//===============================
void Test::MySlot(QObject* obj){
    qDebug()<<"slot is called \n"<<obj;
}

QSignalMapper

QGridLayout *gridLayout = new QGridLayout(this);
QSignalMapper *mapper = new QSignalMapper(this);
for(int row = 0; row < 3; ++row) {
    for(int column = 0; column < 3; ++column) {
        QPushButton *button = new QPushButton(" ");
        gridLayout->addWidget(button, row, column);
        m_board.append(button);
        mapper->setMapping(button, m_board.count() - 1);
        connect(button, SIGNAL(clicked()), mapper, SLOT(map()));
    }
}
connect(mapper, SIGNAL(mapped(int)),
        this,   SLOT(handleButtonClick(int)));

QObject predefined signals and slots

deleteLater()
destroyed(QObject *obj = nullptr)
objectNameChanged(const QString &objectName)

Events System

Qt creates an event object to represent it by constructing an instance of the appropriate QEvent subclass, and delivers it to a particular instance of QObject (or one of its subclasses) by calling its event() function.
This function does not handle the event itself; based on the type of event delivered, it calls an event handler for that specific type of event, and sends a response based on whether the event was accepted or ignored.

bool MyWidget::event(QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        if (ke->key() == Qt::Key_Tab) {
            // special tab handling here
            return true;
        }
    } else if (event->type() == MyCustomEventType) {
        MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
        // custom event handling here
        return true;
    }

    return QWidget::event(event);
}

Note that event() is still called for all of the cases not handled, and that the return value indicates whether an event was dealt with; a true value prevents the event from being sent on to other objects.

protected:
    bool event(QEvent *event) override{
        qDebug()<<"call event \n";
        QObject::event(event);//call timerEvent
        return false;
    }

    void timerEvent(QTimerEvent *event) override{
        qDebug()<<"call timer event \n";
    }

    bool eventFilter(QObject *watched, QEvent *event) override{
        //watched = pointer to object carch event
        qDebug()<<"call event filter \n";
        return false; // false meen can call event function
    }


    Test t;
    t.startTimer(1000);
    t.installEventFilter(&t);
call event filter
call event
call timer event

When the filter object’s eventFilter() implementation is called, it can accept or reject the event, and allow or deny further processing of the event. If all the event filters allow further processing of an event (by each returning false), the event is sent to the target object itself. If one of them stops processing (by returning true), the target and any later event filters do not get to see the event at all.

Sending Events

Many applications want to create and send their own events. You can send events in exactly the same ways as Qt’s own event loop by constructing suitable event objects and sending them with QCoreApplication::sendEvent() and QCoreApplication::postEvent().

sendEvent() processes the event immediately. When it returns, the event filters and/or the object itself have already processed the event. For many event classes there is a function called isAccepted() that tells you whether the event was accepted or rejected by the last handler that was called.

postEvent() posts the event on a queue for later dispatch. The next time Qt’s main event loop runs, it dispatches all posted events, with some optimization. For example, if there are several resize events, they are compressed into one. The same applies to paint events: QWidget::update() calls postEvent(), which eliminates flickering and increases speed by avoiding multiple repaints.

postEvent() is also used during object initialization, since the posted event will typically be dispatched very soon after the initialization of the object is complete. When implementing a widget, it is important to realize that events can be delivered very early in its lifetime so, in its constructor, be sure to initialize member variables early on, before there’s any chance that it might receive an event.

To create events of a custom type, you need to define an event number, which must be greater than QEvent::User, and you may need to subclass QEvent in order to pass specific information about your custom event. See the QEvent documentation for further details.

Q_GADGET

The Q_GADGET macro is a lighter version of the Q_OBJECT macro for classes that do not inherit from QObject but still want to use some of the reflection capabilities offered by QMetaObject. Just like the Q_OBJECT macro, it must appear in the private section of a class definition.

Q_GADGETs can have Q_ENUMQ_PROPERTY and Q_INVOKABLE, but they cannot have signals or slots.

Q_GADGET makes a class member, staticMetaObject, available. staticMetaObject is of type QMetaObject and provides access to the enums declared with Q_ENUMS.

links
https://doc.qt.io/qt-5/qobject.html
https://doc.qt.io/qt-5/qmetaobject.html