nonocast / me

记录和分享技术的博客
http://nonocast.cn
MIT License
20 stars 0 forks source link

学习 C++ (Part 5: Qt and QML) #233

Open nonocast opened 2 years ago

nonocast commented 2 years ago

Quick

因为"讨厌"(最早MFC写烦了), 所以这么多年就躲着C++,自然也就放弃了Qt,看到OBS用一套Qt代码就搞定了跨平台,还是让我非常惊讶。速度重新看了一下Qt,只能说好强啊,C/C++ yyds !

Qt的几个概念:

import QtQuick

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
}

所以我们简单看下Qt Widget后就直接进入QML/Quick.

QtWidget

通过qtCreator可以直接生成template,我们看下shell下的hello world,

app.cxx

#include <QApplication>
#include <QLabel>

int main(int argc, char* argv[]) {
  QApplication app(argc, argv);
  QLabel *label = new QLabel("<h2><i>hello</i> <font color=red>world</font></h2>");
  label->show();
  return app.exec();
}

qmake -project 生成项目文件foo.project

# 这3句为生成后自行添加,否则需要#include <QtWidgets/QApplication>
QT       += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17

TEMPLATE = app
TARGET = foo
INCLUDEPATH += .

SOURCES += app.cxx

然后make foo.pro生成Makefile,然后make就可以得到一个foo.app, 通过open foo.app就能看到你的hello world窗口。

多提一句,macOS下QApplication是在QtWidgets.framework中,所以在c_cpp_properties中配置如下:

"includePath": [
  "/Users/nonocast/qt/6.3.0/macos/lib/QtWidgets.framework/Headers",
  "${default}"
]

看看Button Click的实现:

#include <QApplication>
#include <QPushButton>

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

Auto Layout

Qt Widgets通过Layouts和Spacers来实现自动布局,逻辑和Android比较类似,其中Layouts主要就是Vertical Layout和Horizontal Layout,Spaces就是Horizontal Spacer和Vertical Spacer。

所以通过不断的嵌套VLayout和HLayout来实现总体布局的自适应。

Next example

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QStyle>

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
  ui->setupUi(this);
  ui->button1->setProperty("pagematches", true);
}

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

void MainWindow::on_tabWidget_currentChanged(int index) {
  ui->button1->setProperty("pagematches", false);
  ui->button2->setProperty("pagematches", false);
  ui->button3->setProperty("pagematches", false);

  if (index == 0) {
    ui->button1->setProperty("pagematches", true);
  } else if (index == 1) {
    ui->button2->setProperty("pagematches", true);
  } else {
    ui->button3->setProperty("pagematches", true);
  }

  ui->button1->style()->polish(ui->button1);
  ui->button2->style()->polish(ui->button2);
  ui->button3->style()->polish(ui->button3);
}

void MainWindow::on_button1_clicked() {
  ui->tabWidget->setCurrentIndex(0);
}

void MainWindow::on_button2_clicked() {
  ui->tabWidget->setCurrentIndex(1);
}

void MainWindow::on_button3_clicked() {
  ui->tabWidget->setCurrentIndex(2);
}

QML

Qt Meta Language or Qt Modeling Language (QML) is a Javascript-inspired user interface mark-up language used by Qt for designing user interfaces. Qt provides you with Qt Quick components (widgets powered by the QML technology) to easily design touch-friendly UI without C++ programming. We will learn more about how to use QML and Qt Quick components to design our program's UI by following the steps given in the following section.

放一张图片也折腾了我好久,要注意几点:

然后QML更新了自动布局的方式,之前通过V/HLayout才能做布局,现在改用了Google的Adnroid方式,

full

Image {
  id: image
  anchors.fill: parent
  source: "qrc:/images/qt.png"
  fillMode: Image.PreserveAspectFit
}

right corner

Image {
  id: image
  anchors.right: parent.right
  anchors.rightMargin: 0
  source: "qrc:/images/qt.png"
  fillMode: Image.PreserveAspectFit
}

QML Click

hello

Button {
  id: button
  x: 58
  y: 38
  text: qsTr("Button")
  onClicked: {
    console.log("button clicked")
  }
}

通过QML实现Button click关联Dialog

Button {
  id: button
  x: 58
  y: 38
  text: qsTr("Button")
  onClicked: msg.visible = true
}

MessageDialog {
  id: msg
  title: "Title"
  text: "Button pressed"
  onAccepted: visible = false
}

C++ Invoke

注入一个对象(instance)的基本步骤:

  1. 新建一个Service Class

service.h

#ifndef SERVICE_H
#define SERVICE_H

#include <QObject>

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

  Q_INVOKABLE void requestBackendData();
};

#endif // SERVICE_H

service.cpp

#include "service.h"
#include <QDebug>

Service::Service(QObject *parent) : QObject{parent} {
}

void Service::requestBackendData() {
  qDebug() << "request backend data...";
}

在engine注册对象,

main.cpp

#include "service.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

Service service;

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

  QQmlApplicationEngine engine;

  engine.rootContext()->setContextProperty("service", &service);

  const QUrl url(u"qrc:/wee-qml/main.qml"_qs);
  QObject::connect(
      &engine, &QQmlApplicationEngine::objectCreated, &app,
      [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
          QCoreApplication::exit(-1);
      },
      Qt::QueuedConnection);
  engine.load(url);

  return app.exec();
}

然后在QML通过Connections关联后就可以直接调用对象,

import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs

Window {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Connections {
        target: service
    }

    Button {
        id: button
        x: 58
        y: 38
        text: qsTr("Button")
        onClicked: {
            service.requestBackendData()
        }
    }
}

WebView

Qt与Web混合开发(一)--简单使用 - 知乎

hello.pro QT += core gui webenginewidgets

main.cpp

#include "mainwindow.h"

#include <QApplication>
#include <QWebEngineView>

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

  QWebEngineView view;
  view.load(QUrl("https://google.com"));
  view.show();
  return a.exec();
}

注:

Widget or QML?

2021: 关于Qt选择qml还是widget的深度思考 - 知乎

评论中这句话还是比较务实: "我个人觉得如果这个应用是用手指头操作,那么用 QML 更合适,如果是用鼠标操作,那么用 Widget 更合适。"

参考阅读