效果图:
简单讲解:
创建一个Qt Quick Application - Empty项目,编译器如下,我一般只选择msvc2017 32bit
然后就可以直接运行了,但是一个main跑qml文件,控制起来可能有些麻烦,有些需要C++操作的具体控制可能不好写,于是我将其改成了C++与qml混合编码的样子
先添加一个窗口,用来加载qml文件,qml用来布局样式(刚创建出来的main.qml的根元素是window,现在创建一个窗口加载这个资源,就得把window改为Item了,详见下方main.qml文件内容)
main.cpp
#include
#include
#include "loginwindow.h"
int main(int argc, char *argv[])
{
// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
// QGuiApplication app(argc, argv);
// QQmlApplicationEngine engine;
// engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
// if (engine.rootObjects().isEmpty())
// return -1;
QApplication app(argc, argv);
loginWindow login;
login.show();
return app.exec();
}
原来是直接加载qml文件当窗口用,现在自己创建了loginWindow窗口
loginwindow.h
#ifndef LOGINWINDOW_H
#define LOGINWINDOW_H
#include
#include
#include
#include
#include
#include
class loginWindow : public QWidget
{
Q_OBJECT
public:
explicit loginWindow(QWidget *parent = nullptr);
// 带Q_INVOKABLE宏标识,可以从qml中调用这个接口
Q_INVOKABLE int getVerificationCode();
// qml中实现无边框窗口拖动有点闪烁,所以干脆放到窗口实现中
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
signals:
// signal中的函数,可以再qml文件中用Connections响应
void sendToQml(QString text);
public slots:
private:
// 加载qml文件用的widget
QQuickWidget *m_contentView;
// 记录窗口拖动用的变量
QPoint startPoint;
bool bLeftButtonDown;
};
#endif // LOGINWINDOW_H
loginwindow.cpp
#include "loginwindow.h"
loginWindow::loginWindow(QWidget *parent) : QWidget(parent)
{
// qml布局应用到一个widget中
m_contentView = new QQuickWidget();
m_contentView->rootContext()->setContextProperty("theLoginWindow", this); //给qml设置一个可以用的变量,可以不叫theLoginWindow,可以传this以外的对象
m_contentView->setSource(QUrl("qrc:///main.qml")); //设置对应的qml文件
// qml对应的widget添加到本窗口
QVBoxLayout *layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
layout->addWidget(m_contentView);
setLayout(layout);
// 设置无边框
setWindowFlags(Qt::FramelessWindowHint);
// 发送信号,自己的signal由qml响应
emit sendToQml("巴扎嘿");
}
int loginWindow::getVerificationCode()
{
return QMessageBox::information(NULL, "haha", "呵呵呵", QMessageBox::Ok | QMessageBox::Abort);
}
void loginWindow::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::MouseButton::LeftButton)
{
startPoint = event->pos();
bLeftButtonDown = true;
}
}
void loginWindow::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::MouseButton::LeftButton)
{
bLeftButtonDown = true;
}
}
void loginWindow::mouseMoveEvent(QMouseEvent *event)
{
if(bLeftButtonDown)
{
int offset_x = event->pos().x() - startPoint.x();
int offset_y = event->pos().y() - startPoint.y();
move(frameGeometry().x() + offset_x, frameGeometry().y() + offset_y);
}
}
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
Item {
id:mainWindow
visible: true
width: 500
height: 700
property string lineColor: "#dcdcdc"
Image{
x:0
y:0
width:parent.width
height:width*388/500
source: "../../res/login_bg.png"
}
// 右上角关闭按钮,鼠标悬停离开的时候修改背景图片
Image{
id:closeButton
width:20
height:width
y: 10
x:parent.width - width - y
source: "../../res/gray_close.png"
// 支持鼠标进入离开需要设置hoverEnabled: true
MouseArea{
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
// id可以直接代表那个元素
closeButton.source = "../../res/login_close_focus.png"
}
onExited: {
closeButton.source = "../../res/gray_close.png"
}
onClicked: {
// m_contentView->rootContext()->setContextProperty("theLoginWindow", this);使得qml文件中可以使用theLoginWindow对象,也就是那个this
theLoginWindow.close()
}
}
}
// Image不支持radius,所以另外写一个qml支持圆角图片
CircleImage{
id:userProfileImage
width:120
height: 120
x:(parent.width - width)/2
y:60
img_src: "../../res/defaultUser.png"
}
// textinput的鼠标不会变成IBeamCursor,不习惯
// textField无边框要用background: Qt.transparent
TextField{
id:phoneNumber
width: 300
x:(parent.width - width)/2
y:400
inputMethodHints: Qt.ImhFormattedNumbersOnly
font.pointSize: 14
placeholderText: qsTr("请输入手机号")
background: Qt.transparent
selectByMouse: true
}
Rectangle{
width: 300
x:(parent.width - width)/2
y:phoneNumber.y + phoneNumber.height + 12
height:1
color:lineColor
}
TextField{
id:verificationCode
x:phoneNumber.x
y:phoneNumber.y + phoneNumber.height + 40
width:150
placeholderText: qsTr("请输入验证码")
font.pointSize: 14
background: Qt.transparent
}
Rectangle{
color:"#ff6321"
radius: verificationCode.height
height: verificationCode.height + 10
width: 120
x:parent.width - ((parent.width - phoneNumber.width)/2) - width
y:verificationCode.y - 5
MouseArea{
property int ret: -1
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
// 可以接收对象接口的返回值
ret = theLoginWindow.getVerificationCode()
console.log(ret)
}
}
Text {
id: getVerificationCode
text: qsTr("获取验证码")
font.pointSize: 12
color: "white"
anchors.centerIn: parent
}
}
Rectangle{
width: 300
x:(parent.width - width)/2
y:verificationCode.y + verificationCode.height + 12
height:1
color:lineColor
}
Rectangle{
x:(parent.width - width)/2
y:parent.height - height - 80
width:phoneNumber.width - 40
height: phoneNumber.height + 20
color:"#ff6321"
radius:phoneNumber.height
MouseArea{
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
Text {
id: login
text: qsTr("登录")
font.pointSize: 14
font.bold: true
color: "white"
anchors.centerIn: parent
}
}
// 响应target的signal函数,加on
Connections{
target: theLoginWindow
onSendToQml:{
console.log("onSendToQml" + text)
}
}
}
CircleImage.qml
import QtQuick 2.0
import QtGraphicalEffects 1.0
Rectangle {
property string img_src
radius: width / 2
Image {
id: _image
smooth: true
visible: false
anchors.fill: parent
source: img_src
sourceSize: Qt.size(parent.size, parent.size)
antialiasing: true
}
Rectangle {
id: _mask
color: "black"
anchors.fill: parent
radius: width / 2
visible: false
antialiasing: true
smooth: true
}
OpacityMask {
id: mask_image
anchors.fill: _image
source: _image
maskSource: _mask
visible: true
antialiasing: true
}
}
资源图片就自己添加到qrc中去即可,不添加的话,直接source:""不会生效
简单分析:
1.默认创建的Quick Application Empty直接使用qml当窗口,为了混合使用,我将qml的根元素从window替换为item,用来应用到其他窗口中,便于qml写布局,C++写控制代码
2.默认创建的pro文件中第一行,引用的QT +=不够qml与c++混合编码,所以改成了QT += core quickwidgets
3.qml控制C++
C++中
m_contentView = new QQuickWidget();
m_contentView->rootContext()->setContextProperty("theLoginWindow", this);
m_contentView->setSource(QUrl("qrc:///main.qml"));
这样main.qml中就可以使用theLoginWindow变量,访问this中的接口
4.C++控制qml
C++中
signal:
void sendToQml(QString text);
qml中
Connections{
target: theLoginWindow
onSendToQml:{
console.log("onSendToQml" + text)
}
}
然后C++中触发事件
emit sendToQml("巴扎嘿");
qml可以再onSendToQml中写入其他控制qml数据的代码
可能还有其他的互相控制的方法,但是目前掌握这么多也够用了,先不探究了
发布项目:
1.windows开始菜单打开qt的命令行工具
2.cd到项目生成目录,debug或relase
3.执行命令windeployqt testQml2.exe --qmldir D:\Qt\Qt5.12.0\5.12.0\msvc2017\qml --dir D:\Qt\testQmlPacket参数不一定
**注意** --qmldir后面的参数视情况而定,我用的是Qt5.12.0,mscv,32位编辑程序,其他设备上用的不同版本,不同编译器,不同架构要自己去Qt安装目录下找到对应qml目录才能安装,不写--qmldir参数,或者写错了,可能导致页面空白,
--dir后面的参数是打包的输出目录,所有依赖的qt库都会存访在这里,程序依赖的第三方库需要自己复制,exe文件也需要自己复制过来