Evgenii Legotckoi
Evgenii LegotckoiТам. 28, 2016, 11:17 Т.Ж.

Qt/C++ - Сабақ 055. QSignalMapper VS лямбда функциялары

QSignalMapper — динамикалық түрде жасалған нысандар үшін сигналдарды және ұяшықтарды ұйымдастыруға арналған тамаша сынып. Мысалы, QStackedWidget ішіндегі динамикалық түрде жасалған түймелер немесе нысандар. Бұл әсіресе бағдарламалық жасақтаманың ескі нұсқаларында, яғни Qt 4.8 негізіндегі сигналдар мен ұяшықтар жүйесі пайдалануға негізделген болатын. макростар. Бірақ қазіргі шындықта көрсеткіштердегі жаңа синтаксис әлдеқайда ыңғайлы, сонымен қатар QSignalMapper пайдаланудан толығымен арылуға болатын lambda функцияларын қолдайды, ол жаңа жобаларда құбыжық атавизмге ұқсайды. Qt Framework және C++ тіл стандарттарының соңғы нұсқалары.

Ал егер сіз map() және mapped() шамадан тыс жүктелуін ескерсеңіз, көрсеткішті пайдаланып сигнал және ұяшық қосылымдарын пайдалансаңыз, бұл QSignalMapper бар кодты одан да қорқынышты етеді, өйткені сигналдар мен слоттар ретінде шығару керек, бірақ бұл туралы кейінірек.

Сондықтан, ресми Qt құжаттамасындағы мысалға негізделетін шағын жобаны қарастырайық. Атап айтқанда, мысал келесідей болады. Бізде QLabel, QPushButton және Тік орналасу бар. Түймені басу арқылы Тік орналасуға басқа динамикалық түймелер қосылады, оны басу арқылы түйме нөмірі бар мәтін QLabel-де келесі пішінде көрсетіледі: «2-түйме». Келесі суретте бұл қолданбаның мысалы көрсетілген, оның сыртқы түрі ерекшеленбейді, ал бағдарлама кодының бірнеше іске асырылуы болады.


1-нұсқа - QSignalMapper және макросинтаксис

Алдымен ресми құжаттамада мысал ретінде ұсынылған опцияны талдап көрейік. Сигналдар мен ұяшықтар макростар арқылы қосылған кезде Qt 4.8-мен үйлесімді нұсқа бар.

Қолданбаның сыртқы түрі графикалық дизайнерде жасалған, сондықтан ui нысанын пайдалану арқылы таң қалмаңыз.

mainwindow.h

Сонымен, қолданба жұмыс істеуі үшін бізге QSignalMapper және динамикалық түймені басу өңделетін слот қажет. Нюанс мынада, бұл түймедегі мәтін QLabel ішінде орнатылады, сондықтан біз MainWindow ұяшығымен қабылданатын QSignalMapper::mapped(const QString &) сигналын қолданамыз: :clicked(const QString & ), бұл ұяшықта виджет QPushButton, нысанына түрлендіріледі және біз одан мәтінді аламыз. Бұл түйме жасалған кезде мәтін алдын ала орнатылады. Нөмірлеу үшін құрылған түймелердің есептегіші пайдаланылады (айнымалы int есептегіші).

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSignalMapper>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();           // Слот, в котором будут создаваться кнопки
    void clicked(const QString &str);       // Слот, в котором будут обрабатываться клики кнопок

private:
    Ui::MainWindow *ui;
    int counter;
    QSignalMapper *mapper;                  // маппер, который будет обрабатываться сигналы от кнопок
};

#endif // MAINWINDOW_H

mainwindow.cpp

Мұның бәрі кодта қалай көрінетінін көрейік.

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

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

    mapper = new QSignalMapper(this);   // Инициализируем маппер
}

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

void MainWindow::on_pushButton_clicked()
{
    QPushButton *button = new QPushButton(this);            // Создаём кнопку
    button->setText("Button " + QString::number(counter));  // Устанавливаем в неё текст
    counter++;                                              // Инкрементируем счётчик
    ui->verticalLayout->addWidget(button);                  // Помещаем кнопку в vertical layout

    // подключаем сигнал клика кнопки к мапперу
    connect(button, SIGNAL(clicked(bool)), mapper, SLOT(map()));
    mapper->setMapping(button, button->text()); // по клику кнопки будем передавать текст из этой кнопки
    // передаём текст с кнопки из маппера в слот, где будет установлен текст
    connect(mapper, SIGNAL(mapped(QString)), this, SLOT(clicked(QString)));
}

void MainWindow::clicked(const QString &str)
{
    // конечно, у QLabel метод setText уже является слотом и
    // можно было бы сразу его подключить к сигналу,
    // но для обзора всех сложностей будет оптимальнее показать это отдельным слотом
    ui->label->setText(str);
}

2-нұсқа - QSignalMapper және жаңа синтаксис

Бірінші нұсқада код Qt 4.8 кодымен үйлесімді және әдетте оқуға болады, бірақ 4.8 нұсқасындағы жобаны қолдамайтын болсақ ше? Содан кейін бірінші істеу керек - бұл кодты көрсеткіш синтаксисін пайдаланып қайта жазу және лямбда функцияларының шағын қосындыларын жасау. Содан кейін мен QSignalMapper сұмдық кері қайтару сияқты көрінетінін айтқанымда не айтқым келгенін түсінесіз.

mainwindow.h

Енді тақырып файлында динамикалық түймені басуға арналған ұяшық жоқ, өйткені мұнда ламбдалар бұрыннан пайдаланылады.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSignalMapper>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();           // Слот, в котором будут создаваться кнопки

private:
    Ui::MainWindow *ui;
    int counter;
    QSignalMapper *mapper;                  // маппер, который будет обрабатываться сигналы от кнопок
};

#endif // MAINWINDOW_H

mainwindow.cpp

Енді кодтың қалай көрінетінін көрейік. Біз бір мәнді WIN алатын сияқтымыз:

  1. Lambda функциясының арқасында бір ұяшық жойылды;
  2. Біз қателерді орындау уақытында емес, компиляция сатысында қадағалау мүмкіндігіне ие болдық, бұл сигнал мен ұяшық макростарының күнәсі болып табылады;
  3. Кодты Qt5 стандартына әкелді.

Дегенмен, код өте қорқынышты көрінеді, себебі ол QSignalMapper сигналдары мен ұяшықтары үшін static_cast пайдаланады. Бұл map() ұяшығы да, mapped() сигналы да шамадан тыс жүктелгендіктен және компилятор қажет. олардың қолтаңбасын көрсету. Ал келесі конструкциялар төмендегі кодқа қысқалық пен сұлулықты қоспайды. Бірақ бұл жағдайды түзетуге болады - үшінші нұсқаны қарастырыңыз.

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

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

    mapper = new QSignalMapper(this);   // Инициализируем маппер
}

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

void MainWindow::on_pushButton_clicked()
{
    QPushButton *button = new QPushButton(this);            // Создаём кнопку
    button->setText("Button " + QString::number(counter));  // Устанавливаем в неё текст
    counter++;                                              // Инкрементируем счётчик
    ui->verticalLayout->addWidget(button);                  // Помещаем кнопку в vertical layout

    // подключаем сигнал клика кнопки к мапперу
    connect(button, &QPushButton::clicked,
            mapper, static_cast<void(QSignalMapper::*)()>(&QSignalMapper::map));
    mapper->setMapping(button, button->text()); // по клику кнопки будем передавать текст из этой кнопки
    // передаём текст с кнопки из маппера в слот, где будет установлен текст
    connect(mapper, static_cast<void(QSignalMapper::*)(const QString &)>(&QSignalMapper::mapped),
            [=](const QString str){
        ui->label->setText(str);
    });
}

3-нұсқа - QSignalMapper-тен құтылу

Енді QSignalMapper-тен арылайық. Өйткені, егер сіз lambda функцияларының барлық мүмкіндіктерін пайдалансаңыз, онда осы мысалдағыдай тапсырмаларды орындау үшін QSignalMapper мүлде қажет емес.

mainwindow.h

Көріп отырғаныңыздай, тақырып файлындағы код сәл қысқартылды.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();           // Слот, в котором будут создаваться кнопки

private:
    Ui::MainWindow *ui;
    int counter;
};

#endif // MAINWINDOW_H

mainwindow.cpp

Көріп отырғаныңыздай, lambda функциясын пайдалану кодты айтарлықтай азайтады, сонымен қатар бұл жағдайда QSignalMapper сыныбын пайдалануды толығымен жояды. Бұл класс С++ тілінің өзін дамытуға байланысты мүлдем жойылып кетуі әбден мүмкін.

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

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

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

void MainWindow::on_pushButton_clicked()
{
    QPushButton *button = new QPushButton(this);            // Создаём кнопку
    button->setText("Button " + QString::number(counter));  // Устанавливаем в неё текст
    counter++;                                              // Инкрементируем счётчик
    ui->verticalLayout->addWidget(button);                  // Помещаем кнопку в vertical layout

    // В лямбде захватываем из внешней области переменных указатель на MainWindow,
    // что позволит использовать переменные ui, а также саму кнопку button
    connect(button, &QPushButton::clicked, [this, button](){
        ui->label->setText(button->text());
    });
}

Қорытынды...

Сигналдар мен слоттар үшін жаңа синтаксисті пайдалану C++ тілінің қуатты мүмкіндіктерін қосуға мүмкіндік береді және Qt-ге қолданған кезде ескірген нұсқаларында қолданылуы даусыз болатын кейбір сыныптарды пайдаланудан толығымен құтылуға мүмкіндік береді. Qt. Сондай-ақ, ламбдаларды пайдалану бағдарлама кодын айтарлықтай азайтады және жеңілдетеді.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

АК
  • Там. 28, 2017, 8:18 Т.Ж.

Добрый день.
На

connect(ui->comboBoxProfile, &QComboBox::currentIndexChanged, [=](){ this->close();});
Ругается так
C:\Projects\Qt\BMR-1401LM-U\dialogfeatures.cpp:31: ошибка: no matching function for call to 'DialogFeatures::connect(QComboBox*&, <unresolved overloaded function type>, DialogFeatures::DialogFeatures(int, int, int, int, int, int, QWidget*)::<lambda()>)'
connect(ui->comboBoxProfile, &QComboBox::currentIndexChanged, [=](){ this->close();});
^

Не можете подсказать почему?

currentIndexChanged является сигналом с перегрузкой. Поэтому его нужно подключать через static_cast с указанием сигнатуры сигнала.

Вот такая петрушка получается:

connect(comboBox, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::currentIndexChanged), 
[=](const QString &text){ }); 

Спасибо! Знал же, что сигнал с перегрузкой. Думал : "А как компилятор поймет какую именно вызывать?",- но не знал такой прием как указать сигнатуру через static_cast :)

Этот приём в официальной документации на QComboBox указан ;-)

Лямбда удобная штука. Только вчера научился, мне нравится :)

Можешь сказать, когда лучше использовать слот и когда лучше лямбду.
По-моему, связывание сигнала с лямбдой нарушает принцип сигнал слотов.

А писать слот - это значит, что слот может быть вызван из вне. А это не всегда нужно. Я искал про private slots, думал, что private slot может быть связан только с сигналом своего класса, но я ошибался.

По сути лямбда ограничивает область видимости , но нарушает принцип qt сигнал-слот. Это так?

Был бы рад услышать твое мнение по этой теме :)

Я бы не сказал, что лямбда нарушает принципы сигналов и слотов. Это таже самая функция, просто анонимная.
На мой взгляд, принцип сигналов и слотов - это связать некоторые части кода, чтобы при определённом сигнале выполнился необходимый код. А всё остальное это уже домыслы, поскольку лямда подчиняется всем остальным особенностям работы сигналов и слотов, как последовательность подключений и т.д.

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

В примере нужно получить сигнал от кнопки, и что-то сделать с той же самой кнопкой. Если использовать слот, то придётся держать какой-то вектор объектов в заголовочном файле, либо как сделано здесь, использовать QSignalMapper, а это опять же разрастание кода и повышение избыточности кода. А так лямбда захватывает кнопку и спокойно выполняет необходимый код. Когда кнопка будет уничтожена, а память освобождена, то лямбда автоматически будет отключена от сигнала этой кнопки и так же будет уничтожена. Так что проблем здесь никаких не возникнет.

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

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

D
  • Қыр. 21, 2017, 4:29 Т.Қ.

Как вам такое


 enum {
        PROFILE_TOOLPATH_FORM,
        POCKET_TOOLPATH_FORM,
        DRILLING_TOOLPATH_FORM
    };
    QToolBar* toolpathToolBar = addToolBar(tr("Toolpath"));
    toolpathToolBar->setIconSize(QSize(24, 24));
    toolpathToolBar->setObjectName(QStringLiteral("toolpathToolBar"));

    static QVector<QAction*> ToolpathActionList;
    auto createDockWidget = [=](QWidget* dwContents, int type) {
        QDockWidget* dwCreatePath = nullptr;
        foreach (QDockWidget* dw, findChildren<QDockWidget*>()) {
            if (dw->objectName() == "dwCreatePath") {
                dwCreatePath = dw;
                break;
            }
        }
        foreach (QAction* action, ToolpathActionList) {
            action->setChecked(false);
        }
        ToolpathActionList[type]->setChecked(true);
        if (dwCreatePath == nullptr) {
            dwCreatePath = new QDockWidget(this);
            dwCreatePath->setObjectName(QStringLiteral("dwCreatePath"));
            dwCreatePath->setFloating(false);
            dwCreatePath->setWindowTitle(tr("Create Toolpath"));
            dwCreatePath->connect(dwCreatePath, &QDockWidget::visibilityChanged,
                [=](bool fl) {
                    if (!fl) {
                        dwCreatePath->deleteLater();
                        foreach (QAction* action, ToolpathActionList) {
                            action->setChecked(false);
                        };
                    }
                });
            addDockWidget(Qt::RightDockWidgetArea, dwCreatePath);
        }
        else {
            dwCreatePath->widget()->deleteLater();
        }
        dwContents->setObjectName(QStringLiteral("dwContents"));
        dwCreatePath->setWidget(dwContents);
        dwCreatePath->show();
    };

    action = toolpathToolBar->addAction(QIcon::fromTheme("object-to-path"), tr("Profile"), [=]() { createDockWidget(new ProfileToolpathForm(), PROFILE_TOOLPATH_FORM); });
    ToolpathActionList.append(action);
    //    action->setShortcut(QKeySequence::FullScreen);
    action = toolpathToolBar->addAction(QIcon::fromTheme("stroke-to-path"), tr("Pocket"), [=]() { createDockWidget(new PocketToolpathForm(), POCKET_TOOLPATH_FORM); });
    ToolpathActionList.append(action);
    //    action->setShortcuts(tr("Ctrl+0"));
    action = toolpathToolBar->addAction(QIcon::fromTheme("roll"), tr("Drilling"), [=]() { createDockWidget(new DrillingToolpathForm(), DRILLING_TOOLPATH_FORM); });
    ToolpathActionList.append(action);
    action = toolpathToolBar->addAction(QIcon::fromTheme("view-form"), tr("Tool Base"), [=]() { ToolDatabase tdb(this); tdb.exec(); });
    //    action->setShortcut(QKeySequence::ZoomIn);
    foreach (QAction* action, ToolpathActionList) {
        action->setCheckable(true);
    }
Или

QList<QString> GerberParser::Format(QString data)
{
    QList<QString> gerberLines;

    enum SATE {
        PARAM,
        MACRO,
        DATA,
    };

    SATE state = DATA;
    QString lastLine;

    auto gerberLinesAppend = [&gerberLines, &lastLine](SATE& state, const QString& val) -> void {
        switch (state) {
        case MACRO:
            lastLine.push_back(val);
            if (lastLine.endsWith('%')) {
                gerberLines << lastLine;
                state = DATA;
            }
            break;
        case PARAM:
            lastLine.push_back(val);
            if (lastLine.endsWith('%')) {
                foreach (QString tmpline, lastLine.remove('%').split('*')) {
                    if (!tmpline.isEmpty()) {
                        gerberLines << ('%' + tmpline + "*%");
                    }
                }
                state = DATA;
            }
            break;
        case DATA:
            break;
        }
    };

    auto lastLineClose = [&gerberLines](SATE state, QString& val) -> void {
        switch (state) {
        case MACRO:
            if (!val.endsWith('%'))
                val.push_back('%');
            if (!val.endsWith("*%"))
                val.insert(val.length() - 2, '*');
            gerberLines << val;
            break;
        case PARAM:
            foreach (QString tmpline, val.remove('%').split('*')) {
                if (!tmpline.isEmpty()) {
                    gerberLines << ('%' + tmpline + "*%");
                }
            }
            break;
        case DATA:
            break;
        }
        val.clear();
    };

    auto dataClose = [&gerberLines](const QString& val) -> void {
        if (val.count('*') > 1) {
            foreach (QString tmpline, val.split('*')) {
                if (!tmpline.isEmpty()) {
                    gerberLines << (tmpline + '*');
                }
            }
        }
        else {
            gerberLines << val;
        }
    };

    foreach (QString line, data.replace('\r', '\n').replace("\n\n", "\n").replace('\t', ' ').split('\n')) {
        line = line.trimmed();
        if (line.isEmpty()) {
            continue;
        }
        if (line.startsWith('%') && line.endsWith('%') && line.size() > 1) {
            lastLineClose(state, lastLine);
            if (line.startsWith("%AM")) {
                lastLineClose(MACRO, line);
            }
            else {
                lastLineClose(PARAM, line);
            }
            state = DATA;
            continue;
        }
        else if (line.startsWith("%AM")) {
            lastLineClose(state, lastLine);
            state = MACRO;
            lastLine = line;
            continue;
        }
        else if (line.startsWith('%')) {
            lastLineClose(state, lastLine);
            state = PARAM;
            lastLine = line;
            continue;
        }
        else if (line.endsWith('*') && line.length() > 1) {
            switch (state) {
            case MACRO:
            case PARAM:
                gerberLinesAppend(state, line);
                continue;
            case DATA:
                dataClose(line);
                continue;
            }
        }
        else {
            switch (state) {
            case MACRO:
            case PARAM:
                gerberLinesAppend(state, line);
                continue;
            case DATA:
                qDebug() << "Хрен его знает:" << line;
                continue;
            }
        }
    }
    return gerberLines;
}
D
  • Қыр. 21, 2017, 4:35 Т.Қ.

Может и кривовато но чёрт побери работает и класс от ненужной больше ни где фигни не разбухает.

Evgenii Legotckoi
  • Қыр. 22, 2017, 2:45 Т.Ж.

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


Есть кое-какие замечания и мысли
  • Раз уж вы используете минимум стандарт C++11 , то перепишите foreach , на range-based for цикл. foreach - это Qt-шный макрос, который является deprecated , поскольку в стандарте C++11 наконец-то ввели нормальные for циклы для контейнеров и foreach теперь не нужен.
  • Не то, чтобы замечание, но как-то непривычно наблюдать enum в теле метода, разве где-то в другом месте вы не используете подобный enum ? Возможно стоит проанализировать и немного перепроектировать класс?
  • В первом примере возможно, можно обойтись без статического пула Action`ов, если они больше нигде не используются. Вообще по возможности лучше работать без подобных статических пулов объектов настолько, насколько это возможно. Но естественно всё зависит от конкретного User Case и условий разработки.
  • Также название методов и переменных с большой буквы... Полагаю причина в том, что изначально разработка проекта велась под Visual Studio и сложился подобный код стайл в команде, но Qt использует несколько иные правила кодстайла , который немного удобнее, поскольку не приходится гадать порой, что это такое? - переменная, объявление класса, метод или конструктор в коде. Особенно, если косяк в подсветке синтаксиса кода имеется в самой IDE. Плюс при использовании Qt получаем разнобой в кодстайле от Qt и вашего проекта. Поразмышляйте в команде на досуге об этом.

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
Г

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:66ұпай,
  • Бағалау ұпайлары-1
t

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:33ұпай,
  • Бағалау ұпайлары-10
t

Qt - Тест 001. Сигналы и слоты

  • Нәтиже:52ұпай,
  • Бағалау ұпайлары-4
Соңғы пікірлер
G
GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssrАқп. 8, 2024, 6:43 Т.Қ.
Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
F
FynjyШілде 22, 2024, 4:15 Т.Ж.
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCTМаусым 25, 2024, 1 Т.Ж.
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
BlinCT
BlinCTМамыр 5, 2024, 5:46 Т.Ж.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Бізді әлеуметтік желілерде бақылаңыз