Qt5

Features

The main feature of QtMvvm is the seperation between ui and logic. With this library, you can create a core library, containing your application logic, as well as ui controllers (called «ViewModels»), and create multiple ui projects on top of it. This way you can for example provide both, a widgets and a qt quick based application, or create different uis for different devices, without having to code anything twice.

The key features are:

  • Create ViewModels in the core application to prepare data for presentation without binding to any concret GUI
    • Supports singleton ViewModels
    • Supports automatic presentation of container ViewModels
  • Functions to show messageboxes (info, warning, error, etc.) from your core app
    • Asynchronous, with result handling
    • Supports input dialogs and native file dialogs out of the box
    • Supports color picker dialog
    • Supports progress and busy indicator dialogs
    • custom dialog types can be created
  • Methods to create Two-Way Bindings from C++ and QML
  • Macros and a ServiceRegistry to make Dependency Injection possible for Services and ViewModels
  • A generic Presenter Interface so you can create your own custom GUI implementations
  • Generic Edit-View factories to create simple edits for any kind of data from the core code
  • Supports an extensive «Settings GUI» via a simple XML format

QtMvvm Datasync

The QtMvvmDatasync modules help you to integrate QtDataSync (An easy and reliable synchronization library) into your projects. It adds ViewModels and Views to:

  • Monitor and control the connection and synchronization status
  • Manage your account and account devices
  • Easy file based import and exports, as well as a fronted for the local Network-Exchange

The Mvvm Pattern

If you don’t know the Mvvm pattern already, you can read up on the links below. It’s basically a clever seperation
of logic (the models), presentation logic (the viewmodels) and the actual GUI (the views) that is very useful when
creating applications that need to support different uis for the same data.

Good links to get started:

Detailed Description

QDesignerCustomWidgetInterface provides a custom widget with an interface. The class contains a set of functions that must be subclassed to return basic information about the widget, such as its class name and the name of its header file. Other functions must be implemented to initialize the plugin when it is loaded, and to construct instances of the custom widget for Qt Designer to use.

When implementing a custom widget you must subclass QDesignerCustomWidgetInterface to expose your widget to Qt Designer. For example, this is the declaration for the plugin used in the Custom Widget Plugin example that enables an analog clock custom widget to be used by Qt Designer:

class AnalogClockPlugin : public QObject, public QDesignerCustomWidgetInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface")
    Q_INTERFACES(QDesignerCustomWidgetInterface)
public:
    explicit AnalogClockPlugin(QObject *parent = nullptr);

    bool isContainer() const override;
    bool isInitialized() const override;
    QIcon icon() const override;
    QString domXml() const override;
    QString group() const override;
    QString includeFile() const override;
    QString name() const override;
    QString toolTip() const override;
    QString whatsThis() const override;
    QWidget *createWidget(QWidget *parent) override;
    void initialize(QDesignerFormEditorInterface *core) override;

private:
    bool initialized = false;
};

Note that the only part of the class definition that is specific to this particular custom widget is the class name. In addition, since we are implementing an interface, we must ensure that it’s made known to the meta object system using the () macro. This enables Qt Designer to use the () function to query for supported interfaces using nothing but a QObject pointer.

After Qt Designer loads a custom widget plugin, it calls the interface’s () function to enable it to set up any resources that it may need. This function is called with a QDesignerFormEditorInterface parameter that provides the plugin with a gateway to all of Qt Designer‘s API.

Qt Designer constructs instances of the custom widget by calling the plugin’s () function with a suitable parent widget. Plugins must construct and return an instance of a custom widget with the specified parent widget.

Exporting your custom widget plugin to Qt Designer using the () macro. For example, if a library called (on Unix) or (on Windows) contains a widget class called , we can export it by adding the following line to the file containing the plugin header:

Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface")

This macro ensures that Qt Designer can access and construct the custom widget. Without this macro, there is no way for Qt Designer to use it.

When implementing a custom widget plugin, you build it as a separate library. If you want to include several custom widget plugins in the same library, you must in addition subclass QDesignerCustomWidgetCollectionInterface.

Warning: If your custom widget plugin contains QVariant properties, be aware that only the following are supported:

  • QVariant::ByteArray
  • QVariant::Bool
  • QVariant::Color
  • QVariant::Cursor
  • QVariant::Date
  • QVariant::DateTime
  • QVariant::Double
  • QVariant::Int
  • QVariant::Point
  • QVariant::Rect
  • QVariant::Size
  • QVariant::SizePolicy
  • QVariant::String
  • QVariant::Time
  • QVariant::UInt

Сортировка пользовательских классов

В следующем примере мы выполним сортировку объектов пользовательского класса в QList.

Ниже представлен заголовочный файл для нашего пользовательского класса книги:

class Book {
public:
Book(QString, QString);
QString getAuthor() const;
QString getTitle() const;

private:
QString author;
QString title;
};

1
2
3
4
5
6
7
8
9
10

classBook{

public

Book(QString,QString);

QString getAuthor()const;

QString getTitle()const;

private

QString author;

QString title;

};

А здесь реализация класса Book (у нас есть два геттеры для доступа к элементам класса):

#include
#include «book.h»

Book::Book(QString auth, QString tit) {

author = auth;
title = tit;
}

QString Book::getAuthor() const {

return author;
}

QString Book::getTitle() const {

return title;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

#include
#include «book.h»
 

Book::Book(QString auth,QString tit){

author=auth;

title=tit;

}
 

QString Book::getAuthor()const{

returnauthor;

}
 

QString Book::getTitle()const{

returntitle;

}

Далее мы создадим несколько объектов класса Book и отсортируем их с помощью алгоритма std::sort:

#include
#include
#include
#include «book.h»

bool compareByTitle(const Book &b1, const Book &b2) {

return b1.getTitle() books = {
Book(«Jack London», «The Call of the Wild»),
Book(«Honoré de Balzac», «Father Goriot»),
Book(«Leo Tolstoy», «War and Peace»),
Book(«Gustave Flaubert», «Sentimental education»),
Book(«Guy de Maupassant», «Une vie»),
Book(«William Shakespeare», «Hamlet»)
};

std::sort(books.begin(), books.end(), compareByTitle);

for (Book book : books) {
out

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include
#include
#include
#include «book.h»
 

boolcompareByTitle(constBook&b1,constBook&b2){

returnb1.getTitle()b2.getTitle();

}
 

intmain(void){

QTextStream out(stdout);

QListBook>books={

Book(«Jack London»,»The Call of the Wild»),

Book(«Honoré de Balzac»,»Father Goriot»),

Book(«Leo Tolstoy»,»War and Peace»),

Book(«Gustave Flaubert»,»Sentimental education»),

Book(«Guy de Maupassant»,»Une vie»),

Book(«William Shakespeare»,»Hamlet»)

};

std::sort(books.begin(),books.end(),compareByTitle);

for(Book bookbooks){

outbook.getAuthor()»: «book.getTitle()endl;

}

}

CompareByTitle() — это функция сравнения, используемая алгоритмом сортировки:

bool compareByTitle(const Book &b1, const Book &b2) {
return b1.getTitle()

1
2
3

boolcompareByTitle(constBook&b1,constBook&b2){

returnb1.getTitle()b2.getTitle();

}

Алгоритм std:: sort сортирует книги в списке по заголовку:

std::sort(books.begin(), books.end(), compareByTitle);

1 std::sort(books.begin(),books.end(),compareByTitle);

Результат выполнения программы выше:

В этом уроке мы рассмотрели работу с контейнерами в Qt5.

Курсы «C#.NET Developer»

Курсы «Java Developer»

Курсы «Frontend Developer»

Курсы «JavaScript Developer»

Курсы «Python Developer»

Курсы «Unity/Game Developer»

Windows

There is a Q5Pas1.dll binary available.

x86: https://svn.freepascal.org/svn/lazarus/binaries/i386-win32/qt5/Qt5Pas1.dll

no 64-bit version yet.

The build is based on MinGW, thus you want to use the MinGW Qt library (i.e. qt-opensource-windows-x86-mingw492-5.6.2.exe).

If you need to build cbindings project, you need MinGW. You can use MinGW from the Qt package (qt-opensource-windows-x86-mingw492-5.6.2.exe) — it’s an optional component of Qt package installation.

Running and Deployment

The project needs to be deployed with Q5Pas1.dll.

For MinGW based package the following set of DLLs would be required to run a single FORM project.

Those DLLs come with Qt5 package (can be found at C:\Qt\Qt5.6.2\5.6\mingw49_32\bin).

Qt5Core.dll
Qt5PrintSupport.dll
Qt5Widgets.dll
Qt5Gui.dll
Qt5Network.dll
libstdc++-6.dll
libwinpthread-1.dll
libgcc_s_dw2-1.dll

Builds based on MSVC rather than MinGW might have different set of (non Qt5) dlls required.
Note that https://svn.freepascal.org/svn/lazarus/binaries/i386-win32/qt5/Qt5Pas1.dll is built against Qt5-5.6.2, but
Qt5Pas1.dll can be used with any Qt5 > 5.6.2.

Разработка масштабируемого интерфейса под Android

Как известно, устройств под

Android

большое множество и все они имеют самые разные разрешения экранов, поэтому компания

даёт следующую рекомендацию по разработке масштабируемого интерфейса:

  • Интерфейс необходимо создавать на основе dip (независимых от плотности пикселей)
  • И в общем случае применять следующую формулу для перевода dip в количество пикселей:

px = dp * (dpi / 160)

Это формула переводит соотношение плотности пикселей целевого устройства к плотности пикселей устройства с экраном

mdpi,

то есть

medium dpi

. Формула работает отлично и для 5-ти дюймовых full HD экранов.

Если говорить конкретно о

Qt,

то в данном случае необходимо заметить, что

Qt

не даёт возможности задания параметров интерфейса сразу в

dip.

Необходимо производить пересчёт плотности пикселей

dpi

в пиксели относительно dip. То есть использовать выше приведённую формулу.

При разработке

Scalable

интерфейса на

Qt Qml

можно использовать отдельный объект, который будет принимать Ваши dip пиксели и возвращать их эквивалент в физических пикселях экрана, как в программном коде, приведённом ниже.

Функция

pixelDenstity

возвращает плотность пикселей на мм, поэтому полученную величину Мы умножаем на

25.4

, чтобы перевести в дюймы. И далее функция данного объекта получая dip пиксели возвращает уже масштабированное количество физических пикселей на экране целевого устройства. При этом я добавил проверку на плотность пикселей меньше

120 dpi

. Это необходимо для обычных декстопов, как например мой ноутбук с диагональю

15,6 дюйма

и разрешением

1366 на 768

. В данном случае плотность равняется

72 dpi

. И если эту величину ещё и поделить на

160,

то тогда интерфейс получится очень маленького размера, что тоже не приемлемо для настольного ПК. Поэтому для десктопов не производится перевод

dip

в

px. dip

в данном случае приравнивается к

px.

widget.cpp

В данном файле осуществляем косметическую настройку приложения — это касается конструктора класса. И производим создание графических объектов, которые будем перетаскивать на графической сцене. При этом объекты при создании располагаются в произвольном порядке.

#include "widget.h"
#include "ui_widget.h"

/* Функция для получения рандомного числа
 * в диапазоне от минимального до максимального
 * */
static int randomBetween(int low, int high)
{
    return (qrand() % ((high + 1) - low) + low);
}

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    // Косметическая подготовка приложения
    this->resize(640,640);          // Устанавливаем размеры окна приложения
    this->setFixedSize(640,640);

    scene = new QGraphicsScene(this);   // Инициализируем графическую сцену
    scene->setItemIndexMethod(QGraphicsScene::NoIndex); // настраиваем индексацию элементов

    ui->graphicsView->resize(600,600);  // Устанавливаем размер graphicsView
    ui->graphicsView->setScene(scene);  // Устанавливаем графическую сцену в graphicsView
    ui->graphicsView->setRenderHint(QPainter::Antialiasing);    // Настраиваем рендер
    ui->graphicsView->setCacheMode(QGraphicsView::CacheBackground); // Кэш фона
    ui->graphicsView->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);

    scene->setSceneRect(0,0,500,500); // Устанавливаем размер сцены
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    MoveItem *item = new MoveItem();        // Создаём графический элемента
    item->setPos(randomBetween(30, 470),    // Устанавливаем случайную позицию элемента
                 randomBetween(30,470));
    scene->addItem(item);   // Добавляем элемент на графическую сцену
}

mainwindow.cpp

This file is contained in all the purpose of the lesson, namely QTableWidget setting and filling his records from the database.

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    this->setWindowTitle("QTableWidget Example");
    /* The first step is to create an object for the database 
     * and initialize the database connection
     * */
    db = new DataBase();
    db->connectToDataBase();

    /* Fill the database records */
    for(int i = 1; i inserIntoDeviceTable(QVariantList() createUI(QStringList() tableWidget->setColumnCount(5); 
    ui->tableWidget->setShowGrid(true); 
    ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableWidget->setHorizontalHeaderLabels(headers);
    ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
    ui->tableWidget->hideColumn(0);

    QSqlQuery query("SELECT "
                    DEVICE ".id, "
                    DEVICE "." DEVICE_CHECK_STATE ", "
                    DEVICE "." DEVICE_HOSTNAME ", "
                    DEVICE "." DEVICE_IP ", "
                    DEVICE "." DEVICE_MAC
                    " FROM " DEVICE);

    /* Perform filling QTableWidget records using a loop
     * */
    for(int i = 0; query.next(); i++){
        // Insert row
        ui->tableWidget->insertRow(i);
        /* Set the id column in the first taking it from the result of the SQL-query. 
         * This column will be hidden
         * */
        ui->tableWidget->setItem(i,0, new QTableWidgetItem(query.value(0).toString()));

        // Create an element, which will serve as a checkbox
        QTableWidgetItem *item = new QTableWidgetItem();
        item->data(Qt::CheckStateRole);
        /* Check on the status of odd if an odd device, 
         * exhibiting state of the checkbox in the Checked, Unchecked otherwise
         * */
        if(query.value(1).toInt() == 1){
            item->setCheckState(Qt::Checked);
        } else {
            item->setCheckState(Qt::Unchecked);
        }
        // Set the checkbox in the second column
        ui->tableWidget->setItem(i,1, item);
        // Next, pick up all the data from a result set in other fields
        ui->tableWidget->setItem(i,2, new QTableWidgetItem(query.value(2).toString()));
        ui->tableWidget->setItem(i,3, new QTableWidgetItem(query.value(3).toString()));
        ui->tableWidget->setItem(i,4, new QTableWidgetItem(query.value(4).toString()));
    }

    ui->tableWidget->resizeColumnsToContents();
}

Создание промежутков между виджетами

В этом разделе описывается создание макета формы.
Если вы, включив предварительный просмотр формы (Ctrl+T), измените размер окна, то увидите, что
все виджеты не изменяют размер и остаются на своих местах.
Поэтому нам нужно использовать такую возможность Qt, как «распорки» (spacers).
Распорки — это как бы пружины, отталкивающие виджеты друг от друга.

Научиться использовать распорки и управлять макетом можно только путем
проб и ошибок. Главное — помнить, что при работе с распорками их нужно расставлять сначала
горизонтально, и только потом — вертикально.

Для начала мы используем распорки для центрирования верхней надписи.
Измените размер надписи так, чтобы она занимала как можно меньше места, но при этом полностью вмещала текст.
Добавьте две распорки, по одной с каждого края. Для этого нажмите значок с нарисованной на нем пружиной
или выберите пункт меню Layout => Add Spacer. Щелкните по пустому месту слева от надписи и выберите
Horizontally. Появится синяя распорка (см. рис. 14). Повторите ту же операцию справа от надписи.
Затем добавьте распорку справа от надписи ‘Generated Signature’ и еще одну справа от кнопки ‘Create!’.

Рис. 14: Добавление распорок

Теперь, когда мы заполнили пустое место распорками, нам нужно создать
макет, который позволит виджетам изменять свой размер при изменении размеров
формы. Создание хорошего макета — важный этап разработки программы. Опять
же, попробуйте несколько раз изменить размер формы, чтобы проверить, все
ли в порядке. Мы можем использовать вертикальную или горизонтальную ориентацию,
а также привязку к сетке. Верхний ряд виджетов расположен горизонтально (распорка
+ надпись + распорка), поэтому тут нужно применить горизонтальную ориентацию.
Выделите одновременно три виджета: щелкните на первой распорке, затем, удерживая
клавишу Shift, щелкните по надписи и затем на второй распорке. Нажмите кнопку
Horizontal Layout на панели инструментов или выберите пункт меню Layout =>
Lay Out Horizontally. Вокруг трех выбранных объектов появится красная линия,
показывающая, что объекты были объединены в макет. При необходимости вы можете
изменить размер красного прямоугольника.

Теперь мы можем повторить эту операцию для трех надписей внутри
виджета GroupBox (на этот раз используя вертикальную ориентацию), и для
двух полей ввода и выпадающего списка. В данном случае лучше использовать вертикальную
ориентацию, чтобы объекты были выровнены. Если бы мы использовали горизонтальную ориентацию
для каждой надписи и поля ввода, их размер и положение изменялись бы неправильно.
Для надписи ‘Generated Signature’ и распорки, а также для двух кнопок и распорки между ними
требуется горизонтальная ориентация.

Наконец, нам нужно поместить все элементы управления в сетку (grid).
Щелкните правой кнопкой по форме и выберите «Lay Out in a Grid».
Результат изображен на рис. 15.

Рис. 15: Законченный макет формы

Добавление виджетов 

 Сигналы и слоты

widget.cpp

In this file, we provide a cosmetic adjustment application — this applies to the class constructor. And we make the creation of graphic objects that will drag on the graphic scene. Thus when creating objects arranged at random.

#include "widget.h"
#include "ui_widget.h"

/* Function to get a random number in the range from minimum to maximum
 * */
static int randomBetween(int low, int high)
{
    return (qrand() % ((high + 1) - low) + low);
}

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    this->resize(640,640);         
    this->setFixedSize(640,640);

    scene = new QGraphicsScene(this);   // Init graphics scene
    scene->setItemIndexMethod(QGraphicsScene::NoIndex); 

    ui->graphicsView->resize(600,600); 
    ui->graphicsView->setScene(scene);  
    ui->graphicsView->setRenderHint(QPainter::Antialiasing);   
    ui->graphicsView->setCacheMode(QGraphicsView::CacheBackground); 
    ui->graphicsView->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);

    scene->setSceneRect(0,0,500,500); 
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    MoveItem *item = new MoveItem();       
    item->setPos(randomBetween(30, 470),    
                 randomBetween(30,470));
    scene->addItem(item);   
}

mygraphicview.cpp

To redraw the objects in

QGraphicsScene

these same objects will need to be removed, so for the convenience of the elements of these objects will be grouped, and will write a method to remove all elements of the group. This is useful if you need to redraw the object only one of several, which consists of several elements.

#include "mygraphicview.h"

MyGraphicView::MyGraphicView(QWidget *parent)
    : QGraphicsView(parent)
{

    this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Disable scroll horizontally
    this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);   // Disable scroll vertically
    this->setAlignment(Qt::AlignCenter);                        // Make the contents of binding to the center
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);    // Stretch the widget content

    this->setMinimumHeight(100);
    this->setMinimumWidth(100);

    scene = new QGraphicsScene();   // Initialize the scene to render
    this->setScene(scene);          // Set the scene in a widget

    group_1 = new QGraphicsItemGroup(); // Initialize the first group of elements
    group_2 = new QGraphicsItemGroup(); // Initialize the elements of the second group

    scene->addItem(group_1);            // Add the first group into the scene
    scene->addItem(group_2);            // Add the second group into the scene

    timer = new QTimer();               // Initialize Timer
    timer->setSingleShot(true);

    connect(timer, SIGNAL(timeout()), this, SLOT(slotAlarmTimer()));
    timer->start(50);                   
}

MyGraphicView::~MyGraphicView()
{

}

void MyGraphicView::slotAlarmTimer()
{
    /* We remove all items from the stage if they are facing a new rendering
     * */
    this->deleteItemsFromGroup(group_1);
    this->deleteItemsFromGroup(group_2);

    int width = this->width();      
    int height = this->height();    

    /* Set the stage size to size the widget. 
     * The first coordinate - it is the top left corner, 
     * and the second - is the lower right corner
     * */
    scene->setSceneRect(0,0,width,height);

    /* Getting started drawing random pictures
     * */
    QPen penBlack(Qt::black); 
    QPen penRed(Qt::red);   

    /* Draw a black rectangle
     * */
    group_1->addToGroup(scene->addLine(20,20, width - 20, 20, penBlack));
    group_1->addToGroup(scene->addLine(width - 20, 20, width - 20, height -20, penBlack));
    group_1->addToGroup(scene->addLine(width - 20, height -20, 20, height -20, penBlack));
    group_1->addToGroup(scene->addLine(20, height -20, 20, 20, penBlack));

    /* Draw a red rectangle
     * */
    int sideOfSquare = (height > width) ? (width - 60) : (height - 60);
    int centerOfWidget_X = width/2;
    int centerOfWidget_Y = height/2;

    group_2->addToGroup(scene->addLine(centerOfWidget_X - (sideOfSquare/2),
                                       centerOfWidget_Y - (sideOfSquare/2),
                                       centerOfWidget_X + (sideOfSquare/2),
                                       centerOfWidget_Y - (sideOfSquare/2),
                                       penRed));

    group_2->addToGroup(scene->addLine(centerOfWidget_X + (sideOfSquare/2),
                                       centerOfWidget_Y - (sideOfSquare/2),
                                       centerOfWidget_X + (sideOfSquare/2),
                                       centerOfWidget_Y + (sideOfSquare/2),
                                       penRed));

    group_2->addToGroup(scene->addLine(centerOfWidget_X + (sideOfSquare/2),
                                       centerOfWidget_Y + (sideOfSquare/2),
                                       centerOfWidget_X - (sideOfSquare/2),
                                       centerOfWidget_Y + (sideOfSquare/2),
                                       penRed));

    group_2->addToGroup(scene->addLine(centerOfWidget_X - (sideOfSquare/2),
                                       centerOfWidget_Y + (sideOfSquare/2),
                                       centerOfWidget_X - (sideOfSquare/2),
                                       centerOfWidget_Y - (sideOfSquare/2),
                                       penRed));
}

/* This method catches widget resize event
 * */
void MyGraphicView::resizeEvent(QResizeEvent *event)
{
    timer->start(50);   // As soon as we start the timer event has occurred to render
    QGraphicsView::resizeEvent(event);  //Run event the parent class
}


/* Method for removing all the elements from the group
 * */
void MyGraphicView::deleteItemsFromGroup(QGraphicsItemGroup *group)
{
    /* Loop through all the elements of the scene, 
     * and if they belong to the group, passed into the method, then delete them
     * */
    foreach( QGraphicsItem *item, scene->items(group->boundingRect())) {
       if(item->group() == group ) {
          delete item;
       }
    }
}

moveitem.h

Для осуществления красивого перетаскивания графических объектов Нам понадобится использовать функции

mouseMoveEvent

,

mousePressEvent

и

mouseReleaseEvent

. В функции

mouseMoveEvent

будет производиться непосредственное перетаскивание графического объекта, а в двух других будет производиться смена внешнего вида курсора мыши, которые будет сигнализировать о том, что мы берём и отпускаем графический объект.

#ifndef MOVEITEM_H
#define MOVEITEM_H

#include 
#include 
#include 
#include 
#include 
#include 

class MoveItem : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit MoveItem(QObject *parent = 0);
    ~MoveItem();

signals:

private:
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

public slots:
};

#endif // MOVEITEM_H

mainwindow.cpp

Here in this file only the contents directly related to the analysis of the SVG file. The pressing processing slot button is selected using a file dialog box, and is broken down into graphical objects.

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "svgreader.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    scene = new QGraphicsScene();
    ui->graphicsView->setScene(scene);
    scene->setSceneRect(0,0,400,400);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_saveButton_clicked()
{
    // The code from the previous tutorial on working with SVG
}

void MainWindow::on_loadButton_clicked()
{
    QString newPath = QFileDialog::getOpenFileName(this, trUtf8("Open SVG"),
                                                   path, tr("SVG files (*.svg)"));
    if (newPath.isEmpty())
        return;

    path = newPath;
    scene->clear();

    scene->setSceneRect(SvgReader::getSizes(path)); 

    // To set a graphic scene objects, received them with a method getElements
    foreach (QGraphicsRectItem *item, SvgReader::getElements(path)) {
        QGraphicsRectItem *rect = item;
        scene->addItem(rect);
    }
}

Usage

  • CoreApp
  • ViewModel
  • ServiceRegistry
  • IPresenter

The easiest way to create a QtMvvm Project is to use the provided project template. If you did not install via a package manager or the repository, follow the steps below to add the wizard.

Create and initialize the QtMvvm Project

Follow the setup to create the project. You can select the GUI-frontends you want to use, as well as additional features. After that you get a basic project skeleton with a simple CoreApp and a ViewModel, as well as the corresponding views.

QtMvvm::QuickPresenter::getInputViewFactory(); //Workaround for QTBUG-69963

to your and it should work as expected

Create the ViewModel

  • Add a new c++ class to your core project. Let it inherit from
  • Make sure the Constructor has the following signature:
  • See for an example ViewModel

Create the View for QtWidgets

  • Create a new widget class in your widgets gui project.
  • In order to use it as widget for a viewmodel, you should name it accordingly: If your viewmodel is named , the widgets name must start with as well (e.g. , , , etc.)
  • Modify the constructor to look like this:
  • Create a member variable with your viewmodel type, e.g.
  • In the constructors implementation, cast the to your viewmodel class and assign it to
  • As final step, you need to register the widget. This can be done by adding the line to your , before calling
  • See for an example widget

Create the quick ui

  • Optional: Register the viewmodel for qml. This way autocomplete will work. Add the line to your main cpp before creating the engine.
  • Create a new qml file inside of the folder (This is required to automatically find the view).
  • In order to use it as view for a viewmodel, you should name it accordingly: If your viewmodel is named , the views name must start with as well (e.g. , , etc.)
  • Add the following imports to the qml file:
import QtQuick 2.10
import QtQuick.Controls 2.3
import de.skycoder42.QtMvvm.Core 1.1
import de.skycoder42.QtMvvm.Quick 1.1
import com.example.mvvmexample 1.0 //adjust to the module defined in your main.cpp
  • Add a property named viewmodel to the root element: (If you did not register the viewmodel, use instead of as property type)
  • Add a presenter progress element to the root element. This way lazy loading views will show a progress:
  • See for an example view
Ссылка на основную публикацию