Qt 5.15.0 demo
Qt QML与C++类的交互方式
方式1:向QML注册C++类,并在.qml文件中使用
1
2
3
// add image provider
QQmlContext* context = engine.rootContext();
context->setContextProperty("deviceQrImgLoader",&qrImageLoader);
之后可以使用Connections
进行信号的处理
1
2
3
4
5
6
Connections{
target: deviceQrImgLoader
on...:{
console.log("on... triggered")
}
}
若信号为initCompleted
,则qml
中为onInitCompleted
方式2:向QQmlContext上下文中注册类
1
2
qmlRegisterType<FileService>("cn.MetaNetworks.fileservice",1,0,"FileService");
qmlRegisterType<DeviceQrImageLoader>("cn.MetaNetworks.deviceqrimgloader",1,0,"DeviceQrImgLoader");
之后可以在QML中定义使用,通过id
进行定义。
1
2
3
4
FileService {
id: service
...
}
方式3:直接向QQmlContext中注册QObject
1
context->setContextObject(QObject* object)
Qt中使用libqrencode生成、展示二维码
1. 生成二维码
- 保存在代码第一行的
image
中
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
QImage image(300,300, QImage::Format_RGB32);
image.fill(QColor("black"));
QPainter painter(&image);
if(!painter.isActive()){
}else{
_data = QJsonDocument(device_arr.at(index).toObject()).toJson(QJsonDocument::Compact);
QRcode *qrCode = QRcode_encodeString(this->_data.toUtf8().data(), 1, QR_ECLEVEL_H, QR_MODE_8, true);
qDebug()<<_data;
if(!qrCode){
QColor error("white");
painter.setBrush(error);
painter.setPen(Qt::NoPen);
painter.drawRect(0, 0, image.width(), image.height());
painter.end();
}else{
QColor colorForPoint("black");
QColor colorForBackgroud("white");
//先画背景
painter.setBrush(colorForBackgroud);
painter.setPen(Qt::NoPen);
painter.drawRect(0, 0, image.width(), image.height());
//再画二维码图案
painter.setBrush(colorForPoint);
const double &&s = ( qrCode->width > 0 ) ? ( qrCode->width ) : ( 1 );
const double &&aspect = image.width() / image.height();
const double &&scale = ( ( aspect > 1.0 ) ? image.height() : image.width() ) / s;
for ( int y = 0; y < s; ++y )
{
const int &&yy = static_cast< int >( y * s );
for( int x = 0; x < s; ++x )
{
const int &&xx = yy + x;
const unsigned char &b = qrCode->data[xx];
if( b & 0x01 )
{
const double rx1 = x * scale, ry1 = y * scale;
QRectF r( rx1, ry1, scale, scale );
painter.drawRects( &r,1 );
}
}
}
QRcode_free( qrCode );
painter.end();
}
}
2. 将二维码图片传入QML界面
此处使用ImageProvider
实现.
1
2
3
4
5
6
7
8
9
10
11
12
13
class ImageProvider : public QQuickImageProvider
{
public:
ImageProvider(): QQuickImageProvider(QQuickImageProvider::Pixmap){
}
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);
QImage img;
};
1
2
3
4
5
6
7
8
9
10
11
#include "imageprovider.h"
QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
return this->img;
}
QPixmap ImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
{
return QPixmap::fromImage(this->img);
}
- 在QmlApplicationEngine中注册
1
engine.addImageProvider("DeviceQRImg", qrImageLoader.provider);
注册后,可以使用"image://DeviceQRImg"
访问到图片,若在上述实现方法中用到了id
,则将id
追加在DeviceQRImg
之后即可
- QML代码
- 如果图片需要切换,则需要关闭QML Image的默认
cache
- 如果图片需要切换,则需要关闭QML Image的默认
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Image{
Layout.alignment: Qt.AlignCenter
id: img
width: 250
height: 250
source: "image://DeviceQRImg"
cache: false //关闭缓存
Connections{
target: deviceQrImgLoader
onRefreshImg:{
console.log("qml refresh");
img.source = "";
img.source = "image://DeviceQRImg";
var info = deviceQrImgLoader.getNetworkDevicesInfo()[deviceQrImgLoader.index];
current_device.text = "当前:"+info["card"];
network_card_ip.text = info["ip"];
network_card_mac.text = info["mac"];
network_card_name.text = info["hostname"];
network_card_type.text = info["type"];
}
}
}
Qt中针对不同平台的代码实现
- macOS:
Q_OS_MACOS
- Linux:
Q_OS_LINUX
-
Windows:
Q_OS_WINDOWS
- …
可以针对不同的系统,实现函数
1
2
3
4
5
#ifdef Q_OS_MACOS
void class::function(){
}
#endif
Qt中QSSLSocket的使用
此处记录:创建一个自己的类,继承QTcpServer
流程:incomingConnection -> newConnection -> nextPendingConnection -> readyRead
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
30
31
32
33
34
35
36
37
38
class FileService : public QTcpServer
{
Q_OBJECT
Q_PROPERTY(bool status READ getStatus WRITE changeStatus NOTIFY statusChanged);
public:
FileService(QObject *parent=0);
// 端口监听
Q_INVOKABLE int startListen();
Q_INVOKABLE int stopListen();
void changeStatus(bool running){this->status = running;};
bool getStatus(){return this->status;};
public slots:
void init();
private slots:
void sslErrors(const QList<QSslError> &errors);
void link();
void rx();
void disconnected();
protected:
void incomingConnection(qintptr socketDescriptor); //重载函数
signals:
void initComplete();
void statusChanged(bool running);
private:
// 初始化SSL
int initSSL();
QSslKey _key;
QSslCertificate _cert;
bool status;
QProcess _process;
};
#endif // FILESERVICE_H
- 初始化SSL证书
1
2
3
4
5
6
7
8
9
10
// 初始化文件HTTP
QFile keyFile(":/cert/red_local.key");
keyFile.open(QIODevice::ReadOnly);
_key = QSslKey(keyFile.readAll(), QSsl::Rsa);
keyFile.close();
QFile certFile(":/cert/red_local.pem");
certFile.open(QIODevice::ReadOnly);
_cert = QSslCertificate(certFile.readAll());
certFile.close();
-
开启监听
-
有新用户连接时,会先调用incomingConnection(这个函数不重载的话为普通TCP连接,自己实现可以加入SSL功能等)
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14
void FileService::incomingConnection(qintptr socketDescriptor) { qDebug("incoming"); QSslSocket *sslSocket = new QSslSocket(this); connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>))); sslSocket->setSocketDescriptor(socketDescriptor); sslSocket->setPrivateKey(_key); sslSocket->setLocalCertificate(_cert); sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone); sslSocket->startServerEncryption(); addPendingConnection(sslSocket); }
-
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int FileService::startListen()
{
if (!listen(QHostAddress("0.0.0.0"), 2090)) {
qCritical() << "2090端口被占用,请检查其他程序是否占用端口";
QMessageBox box;
box.setStandardButtons(QMessageBox::Ok);
box.setText("2090端口被占用,请检查其他程序是否占用端口");
box.setDefaultButton(QMessageBox::Ok);
box.exec();
exit(0);
}
connect(this,&FileService::newConnection,this,&FileService::link);
status = true;
emit statusChanged(status);
return OK;
}
- 实现link函数
- 此处绑定读取数据、断开连接二信号,分别对应我们实现的rx、disconnected函数(相当于callback)
1
2
3
4
5
6
7
8
9
void FileService::link()
{
qDebug()<<"recv 1 client";
QTcpSocket *clientSocket;
clientSocket = nextPendingConnection();
connect(clientSocket, &QTcpSocket::readyRead, this, &FileService::rx);
connect(clientSocket, &QTcpSocket::disconnected, this, &FileService::disconnected);
}
-
可以在
rx
中使用sender()
获取QTcpSocket*
指针进行数据交互-
1 2
QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>(sender()); QByteArray recvData = clientSocket->readAll();
-
-
可以在
disconnected
中做一些用户断开的操作-
1 2 3
qDebug("Client Disconnected"); QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>(sender()); clientSocket->deleteLater();
-
QML中Row、RowLayout、Column、ColumnLayout的区别
Row和Column是QtQuick 库提供的,RowLayout以及ColumnLayout是QtQuick.Layouts提供的。 顾名思义,含有Layout的表示一种布局方法,而Row和Column表示一种排列方法。
-
Row、Column都直接为QML类型
-
Column is a type that positions its child items along a single column. It can be used as a convenient way to vertically position a series of items without using anchors.
-
-
RowLayout、ColumnLayout都继承自Item,Item为QML类型
- 子item可以使用Layout.xxx方法进行居中等显示
…这个后面再整理吧