Qt 相关资源下载地址

1,安装 Qt

目前都是采用在线安装的方式,前提是需要注册。

https://download.qt.io/official_releases/online_installers

2,QtCreator

IDE单独下载地址。

https://download.qt.io/archive/qtcreator

3,vs插件

windows下使用vs开发,可以安装这个插件。

https://download.qt.io/archive/vsaddin

4,arm环境

arm下比如ubuntu环境,可直接用apt 指令安装,但是目前仓库中的最新版本时5.9。
如下是把能装的都装了,如果空间有限,请自行裁剪。

sudo apt-get install qtbase5-dev qtbase5-dev-tools qtchooser qt5-qmake qtcreator
sudo apt-get install qtbase5-examples qtbase5-doc-html qtdeclarative5-dev qml-module-qtquick-controls2 qml-module-qtquick-extras qml-module-qt-labs-platform
sudo apt-get install qtmultimedia5-dev  libqt5multimedia5-plugins qml-module-qtquick-virtualkeyboard  libqt5svg5* qtbase5-private-dev qml-module-qt-labs-folderlistmodel
sudo apt-get install qml-module-qt-labs-folderlistmodel qml-module-qtquick* qtquickcontrols5-* qml-module-qtquick2

5,vs最新版下载
https://visualstudio.microsoft.com/zh-hans/downloads/

6,vs2019 版本
https://learn.microsoft.com/zh-cn/visualstudio/releases/2019/release-notes

发表在 其它 | 留下评论

QML 中的状态

状态描述了当前用户界面样子,QML中一个状态定义了一组属性的改变,并且会在一定条件下被触发。

假设有这么一个场景,红黄绿三个灯,用一个按钮,点击后依次切换三个灯亮起。使用QWidget的思路去实现就是在按钮click对应的槽函数中,依次获取三个button的指针,然后改变其颜色,这样也能实现,但是不够优雅。QML的思路是,全局定义一组状态,然后每个状态来控制具体的属性,使用时只要切换不同状态就可以了,后续修改的话,只需要修改这个全局状态就行,并且三个按钮集中暴露在这组状态中。

一,定义三个灯

//三个灯的按钮
Row{
anchors.centerIn: parent
spacing: 20
//红灯
Rectangle{
id:red
width: 50
height: 50
radius: 50
color: "red"
}

//黄灯
Rectangle{
id:yellow
width: 50
height: 50
radius: 50
color: "yellow"
}

//绿灯
Rectangle{
id:green
width: 50
height: 50
radius: 50
color: "green"
}
}

二,定义一组状态,每个状态控制 三个灯具体的属性

//定义一组状态 规定每个状态下 的属性
Item{
id:root
state:"red"
states: [
State {
//红灯状态 红灯亮 其它灭
name: "red"
PropertyChanges {target: red;color:"red"}
PropertyChanges {target: yellow;color:"gray"}
PropertyChanges {target: green;color:"gray"}
},
State {
name: "yellow"
PropertyChanges {target: yellow;color:"yellow"}
PropertyChanges {target: red;color:"gray"}
PropertyChanges {target: green;color:"gray"}
},
State {
name: "green"
PropertyChanges {target: green;color:"green"}
PropertyChanges {target: red;color:"gray"}
PropertyChanges {target: yellow;color:"gray"}
}
]
}

三,切换时 只需要指定 当前状态是 谁即可。

//依次切换三个状态
Button{
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("切换")
onClicked: {
if(root.state=="red")
root.state="yellow"
else if(root.state=="yellow")
root.state="green"
else
root.state="red"
}
}

四,效果

五,过渡

我们还可以在状态切换的时候,加上过渡的动画,让其看起来更丝滑。过渡规定了从状态A到状态B切换时,某个动画的变化。比如下边状态红灯 到黄灯时,红灯和黄灯颜色持续500毫秒。

六,代码

https://gitcode.com/keiler20181/QMLState.git

发表在 QQuick | 留下评论

QML 不同风格和主题的切换

Quick程序提供了方便的用于切换不同风格和主题的配置文件,如果没有设计稿,又想界面没那么丑,那么可以用这套配置,让应用看起来相对专业一点。

一,在 qrc 资源文件中添加 qtquickcontrols2.conf 文件。

二,编辑这个文件。

qt 共提供5种不同的风格。

1,Default Style:
默认风格,其外观和行为会随着操作系统和系统主题的变化而变化。它保持了跨平台的一致性,并且对大多数常见控件提供了自然的外观。

2,Material Style:
受 Google Material Design 影响的风格,主要用于移动应用程序。它强调了阴影、动画和响应式布局,具有现代感和生动感。

3,Universal Style:
致力于提供一种在不同操作系统上具有一致外观的风格。它适合那些希望应用程序在不同平台上保持相似外观的开发者。

4,Imagine Style:
一种受到 macOS 的设计风格启发的风格,它提供了类似 macOS 风格的外观和交互体验。

5,Fusion Style:
基于 Qt Widgets 的 Fusion 主题,提供了一种类似传统桌面应用程序的外观和行为。

每种风格还可以设置不同的属性。

1. accent,color类型,表示重点色,默认是Pink

2. primary,color类型,表示优选色,默认是Indigo

3. backbround,color类型,表示背景色,默认由主题指定(light或者dark)

4. elevation,int类型,表示海拔高度,值越大,阴影越深,该值与具体控件相关

5. foreground,color类型,表示前景色,默认值由主题指定(light或者dark)

6. theme,枚举类型,表示主题,默认是Light,也可修改为Dark

三,看下效果

1,fusion

2,Universal

3,Material

4,Imagine

5,默认风格

6,Dark主题

四,代码

https://gitcode.com/keiler20181/QMLStyle.git

发表在 QQuick | 留下评论

C#通过Qt使用VTK

需求:

一个项目,界面是C# 开发的,但是业务上有三维可视化的需求,VTK基于C#的绑定版本需要收费,并且资料很少。因此将VTK嵌入到Qt里,并封装成一个dll,通过接口提供给C#访问。

实现:

一,Qt程序的配置

这里用到了一第三方库(https://github.com/qtproject/qt-solutions/tree/master/qtwinmigrate),它可以将Qt的窗口给C#使用。

1,首先看pro文件,主要是dll编译配置,和第三方库引用及VTK的依赖库。

2,main.cpp

#include "widget.h"
#include <QApplication>

#include <windows.h>
#include <qmfcapp.h>
#include <qwinwidget.h>

// int main(int argc, char *argv[])
// {
// QApplication a(argc, argv);
// Widget w;
// w.show();
// return a.exec();
// }

BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpvReserved*/ )
{
static bool ownApplication = FALSE;

if ( dwReason == DLL_PROCESS_ATTACH )
ownApplication = QMfcApp::pluginInstance( hInstance );
if ( dwReason == DLL_PROCESS_DETACH && ownApplication )
delete qApp;

return TRUE;
}

QWinWidget *win=nullptr;
extern "C" __declspec(dllexport) bool initWindow( HWND parent )
{
if(parent==nullptr)
return false;
win = new QWinWidget(parent);
Widget *widget = new Widget(win);
widget->show();
win->move(0,0);
win->show();

return TRUE;
}

extern "C" __declspec(dllexport) bool destroyWindow()
{
if(win!=0){
win->close();
delete win;
}

return TRUE;
}

3,Widget 写法,可以参考这篇文章(http://qthello.com/index.php/2024/04/11/vtk/)或者看仓库上的源码。

二,C#端

1,引用dll 并调用接口

using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp
{
public partial class Form1 : Form
{
[DllImport("VTKNet.dll", EntryPoint = "initWindow", CharSet = CharSet.Ansi)]
public extern static bool initWindow(IntPtr parent);

[DllImport("VTKNet.dll", EntryPoint = "destroyWindow", CharSet = CharSet.Ansi)]
public extern static bool destroyWindow();

public Form1()
{
InitializeComponent();
//打开窗口
initWindow(this.Handle);
}
}
}

2,exe路径下 需要把自己的dll ,Qt的dll 以及VTK的dll 全部放进去。

效果:

代码:

https://gitcode.com/keiler20181/QtNetVTK.git

发表在 三维 | 留下评论

windows下利用NSIS制作安装包

经过程序员抓耳挠腮的开发,应用终于开发完毕了,那接下来无论发布给客户还是给测试,最好的方式时打一个安装包,直接给release文件夹,就显得不太专业了。

程序的打包分为两步:

1,将所以依赖文件收集全,确保其它非开发机器运行时,不会缺少库。

2,用第三方打包软件,制作安装包。

一,收集依赖文件

1,我们以一个QML程序为例,新建一个pkg/v1.0.0/bin文件夹,并将 release版本的pkgTest.exe拷贝到这个bin文件夹下。

2,找到对应版本的Qt命令行工具。

3,执行windeployqt.exe 执行,自动收集依赖文件。

  • windeployqt.exe 为打包工具
  • –qmldir 是自己的qml文件所在的目录,如果QWidget程序,这个参数 及后面的路径就不需要了。这个也告诉我们,我们的目录结构最好将所有自定义的qml文件放到一起,便于打包。
  • 自定义qml文件所在的路径
  • pkgTest.exe 我们的主程序

4,这个指令只能收集一部分程序,执行后自己双击一下主程序(pkgTest.exe),看还缺啥,手动拷贝一下。最可靠的办法是找一台非开发机器验证。

二,打包工具打包

我习惯使用NSIS+HM NIS EDIT 这两个工具进行打包,原因就是颗粒度更细,可以控制环境变量、自启动等。并且官网提供了大量的插件,设置上更加的自由。

1,安装打包工具 NSIS ,这个是一个基础的库( https://nsis.sourceforge.io/Download

2,安装编辑器,这是配套使用的一个编辑器。只用上边那个也可以,只是加上这个更方便(https://hmne.sourceforge.net/

3,使用。我们第一步可以建立一个向导,按照向导,一步一步操作。

安装语言可以自己选择,一个或者多个都可以。

选择目录这一步很关键,把自带的两个删掉,然后指定我们的主程序的父目录,也就是bin文件夹。

之后下一步,下一步,选择将脚本保存,便于后期修改。

最后就是编译脚本,生成安装包。

安装包生成到了脚本所在目录,双击可以自己验证下。

三,NSIS 指令

NSIS 官网提供了大量的插件(https://nsis.sourceforge.io/Category:Plugins),自己可以根据业务需要去下载,并且它本身也有大量的指令,可以支持更高的定制化需求。

1,静默安装。 比如安装其他依赖的exe,不想弹出来提示框,让其他在后台默默执行。

SilentInstall silent 
SilentUninstall silent
比如 下边的代码,将Video.exe 在后台执行
SilentInstall silent 
SilentUninstall silent 
Section "MainSection" SEC01
  SetOutPath "$INSTDIR"
  SetOverwrite try
  File "path_1.0.1\Video.exe"
SectionEnd

2,安装后运行指定程序

Function .onInstSuccess
ExecShell "" "$INSTDIR\Video.exe"
FunctionEnd

3,安装后 开机自启。其实就是写注册表

WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run""Video" "$INSTDIR\Video.exe" ; 开机自启

DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "Video" ;卸载开机启动

4,杀死某个进程。 可以用在安装时,确保当前程序已经被关掉。

需要下载插件:https://www.cnblogs.com/chechen/p/11125353.html

Function .onInit
KillProcDLL::KillProc "Video.exe" ;杀进程
Sleep 1000
FunctionEnd

某些进程杀不掉 用这个:

Function .onInit
ExecCmd::exec '"taskkill" /F /IM hiclass.exe /T'
FunctionEnd

5,管理员运行

WriteRegStr HKCU "Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" "$INSTDIR\Video.exe" "RUNASADMIN" ;管理员运行 

6,安装vc环境

; 安装VC环境
Function .onInstSuccess
Push $R0
ClearErrors
ReadRegStr $R0 HKLM "SOFTWARE\Classes\Installer\Dependencies\{e2803110-78b3-4664-a479-3611a381656a}" "Version"

; 检测含有vc的注册表信息是否存在
IfErrors 0 VSRedistInstalled
Exec "$INSTDIR\vcredist_msvc2015_x86.exe /q" ;若不存在,执行静默安装
StrCpy $R0 "-1"
;MessageBox MB_OK $R0

VSRedistInstalled:
;MessageBox MB_OK "已安装"
pop $R0
; Delete "$INSTDIR\vcredist_msvc2015_x86."
FunctionEnd

7,检测是否是xp系统

需要安装插件: https://blog.csdn.net/xt2zsun/article/details/79379098

#检测 Windows 7
var isWindowsXP
Section
Version::IsWindowsXP
#获取值
Pop $isWindowsXP
${if} $isWindowsXP == "1"
MessageBox MB_OK "暂不支持XP系统,请升级到Win7及以上!"
Quit
${EndIf}
SectionEnd

8,右键属性 添加信息

VIProductVersion "${PRODUCT_VERSION}" ;版本号,格式为 X.X.X.X (若使用则本条必须)
VIAddVersionKey /LANG=2052 "ProductName" "我的程序" ;产品名称
VIAddVersionKey /LANG=2052 "Comments" "这是我写的一个测试程序" ;备注
VIAddVersionKey /LANG=2052 "CompanyName" "我的公司名称" ;公司名称
VIAddVersionKey /LANG=2052 "LegalTrademarks" "Test Application is a trademark of Fake company" ;合法商标
VIAddVersionKey /LANG=2052 "LegalCopyright" "Copyright (c) 2019 我的公司版权" ;合法版权
VIAddVersionKey /LANG=2052 "FileDescription" "我的文件描述" ;文件描述(标准信息)
VIAddVersionKey /LANG=2052 "FileVersion" "${PRODUCT_VERSION}" ;文件版本
VIAddVersionKey /LANG=2052 "ProductVersion" "${PRODUCT_VERSION}" ;产品版本

9,判断文件是否存在,用于检验插件是否安装

;存在执行 第三行,不存在执行第二行  不存在+1 行
IfFileExists "C:\Program Files (x86)\WinPcap\rpcapd.exe" +3 +2
MessageBox MB_OK "已安装" ;占位 无实际作用
ExecWait '"$INSTDIR\WinPcap_4_1_3.exe" '

10,删除及创建快捷方式

Delete "$DESKTOP\快乐魔法狮.lnk"
SetOutPath "$INSTDIR"
CreateShortCut "$DESKTOP\快乐魔法狮.lnk" "$INSTDIR\hidteacher.exe"

11,创建环境变量。

EnVar::DeleteValue "vi_algorthmCall_tool_root"
EnVar::AddValue "vi_algorthmCall_tool_root" "$INSTDIR"
发表在 其它 | 留下评论

利用FCL实现更加精准的碰撞检测

一,需求

利用OSG结合FCL实现实现精准的碰撞检测。

二,效果

三,分析

我们看如下这张图,碰撞的逻辑就是,在一个三维场景中,构造一个实体,比如下边的BoxA,然后在物理引擎比如bullet中,或者专用的碰撞检测库中也构造一个对应的实体,比如BoxB。之后在BoxA位姿改变时后,将BoB的位姿也做相应的更新。之后发生碰撞时,物理引擎或者FCL就会给出信号。而这个场景,可以是VTK或者OSG。而碰撞检测可以用Bullet也可以用FCL。

之前用bullet做个尝试,基本的图形能满足需求,比如球,盒子,但是项目中涉及到点云的碰撞,而bullet中处理点云,没有找到好的处理方式。但是FCL可以将点云转变成fcl中对应的实体,因此最终选择了FCL进行碰撞检测,这里列出FCL中大概的步骤。

1,FCL中构造实体。这里构造了一个 盒子。

auto box_geometry = std::make_shared<fcl::Boxf>(w, d, h);
auto ob = new fcl::CollisionObjectf(box_geometry);

2,更新FCL实体的位姿矩阵。

void FCLManager::updateTrans(const std::string &name, const fcl::Transform3f &trans)
{
fcl::CollisionObjectf *ob=getCollisionObject(name);
if(ob){
ob->setTransform(trans);
}
}

//OSG 矩阵 需要进行转换 才能给到FCL使用
//osg 矩阵转fcl矩阵
osg::Vec3 osgTrans = mt.getTrans(); // 获取平移分量
osg::Quat osgQuat = mt.getRotate(); // 获取旋转分量

fcl::Quaternionf rotation(osgQuat.w(), osgQuat.x(), osgQuat.y(), osgQuat.z());
fcl::Vector3f translation(osgTrans.x(), osgTrans.y(), osgTrans.z());
fcl::Transform3f fclTrans=fcl::Transform3f::Identity();
fclTrans.translation() = translation;
fclTrans.linear()=rotation.toRotationMatrix();
FCLManager::getInstance()->updateTrans(this->getName(),fclTrans);

3,碰撞检测

我是检测机器人和其它障碍物的碰撞,这里把机器人关节放到一个集合中,把其它障碍物放到另一个集合中

 
bool FCLManager::detectCollision()
{
fcl::CollisionRequestf request;
fcl::CollisionResultf result;
for(auto &ob1:jointMap){
for(auto &ob2:obstacleMap){
collide(ob1.second, ob2.second, request, result);
if(result.isCollision()){
return true;
}
}
}
return false;
}

4,FCL支持三角面检测。因此我们在FCL中构造对应实体的时候,可以直接用三角面。这样不管OSG中构造的时盒子还是球,还是导入的stl,对应FCL中都是统一用三角面处理。

void FCLManager::addTriMesh(const std::string &name, osg::Node *node)
{
fcl::CollisionObjectf *obj = createNodeCollisionObject(node);
obstacleMap.emplace(name,obj);
}

fcl::CollisionObjectf *FCLManager::createNodeCollisionObject(osg::Node *node)
{
MyComputeTriMeshVisitor visitor;
node->accept( visitor );
osg::Vec3Array* vertices = visitor.getTriMesh();

typedef fcl::BVHModel<fcl::OBBRSSf> Model;
Model* model = new Model();
std::shared_ptr<fcl::CollisionGeometryf> m1_ptr(model);
model->beginModel();
osg::Vec3 p1, p2, p3;
for( size_t i = 0; i + 2 < vertices->size(); i += 3 )
{
p1 = vertices->at( i );
p2 = vertices->at( i + 1 );
p3 = vertices->at( i + 2 );

fcl::Vector3<float> pp1{p1.x(),p1.y(),p1.z()};
fcl::Vector3<float> pp2{p2.x(),p2.y(),p2.z()};
fcl::Vector3<float> pp3{p3.x(),p3.y(),p3.z()};

model->addTriangle(pp1, pp2, pp3);
}
model->endModel();
model->computeLocalAABB();

return new fcl::CollisionObjectf(m1_ptr);
}

5,点云的碰撞。点云的碰撞 使用了一种叫做八叉树的算法。首先将点云转成pcl的点云 格式,然后可以直接构造出fcl实体,这也是选用FCL的原因。

fcl::CollisionObjectf* FCLManager::createPointCloudCollisionObject(const pcl::PointCloud<pcl::PointXYZ>::Ptr pointcloud_ptr, const octomap::point3d &origin_3d)
{
// octomap octree settings
const double resolution = 0.01;
const double prob_hit = 0.9;
const double prob_miss = 0.1;
const double clamping_thres_min = 0.12;
const double clamping_thres_max = 0.98;

std::shared_ptr<octomap::OcTree> octomap_octree = std::make_shared<octomap::OcTree>(resolution);
octomap_octree->setProbHit(prob_hit);
octomap_octree->setProbMiss(prob_miss);
octomap_octree->setClampingThresMin(clamping_thres_min);
octomap_octree->setClampingThresMax(clamping_thres_max);

octomap::KeySet free_cells;
octomap::KeySet occupied_cells;

#if defined(_OPENMP)
#pragma omp parallel
#endif
{
#if defined(_OPENMP)
auto thread_id = omp_get_thread_num();
auto thread_num = omp_get_num_threads();
#else
int thread_id = 0;
int thread_num = 1;
#endif
int start_idx = static_cast<int>(pointcloud_ptr->size() / thread_num) * thread_id;
int end_idx = static_cast<int>(pointcloud_ptr->size() / thread_num) * (thread_id + 1);
if (thread_id == thread_num - 1)
{
end_idx = pointcloud_ptr->size();
}

octomap::KeySet local_free_cells;
octomap::KeySet local_occupied_cells;

for (auto i = start_idx; i < end_idx; i++)
{
octomap::point3d point((*pointcloud_ptr)[i].x, (*pointcloud_ptr)[i].y, (*pointcloud_ptr)[i].z);
octomap::KeyRay key_ray;
if (octomap_octree->computeRayKeys(origin_3d, point, key_ray))
{
local_free_cells.insert(key_ray.begin(), key_ray.end());
}

octomap::OcTreeKey tree_key;
if (octomap_octree->coordToKeyChecked(point, tree_key))
{
local_occupied_cells.insert(tree_key);
}
}

#if defined(_OPENMP)
#pragma omp critical
#endif
{
free_cells.insert(local_free_cells.begin(), local_free_cells.end());
occupied_cells.insert(local_occupied_cells.begin(), local_occupied_cells.end());
}
}

// free cells only if not occupied in this cloud
for (auto it = free_cells.begin(); it != free_cells.end(); ++it)
{
if (occupied_cells.find(*it) == occupied_cells.end())
{
octomap_octree->updateNode(*it, false);
}
}

// occupied cells
for (auto it = occupied_cells.begin(); it != occupied_cells.end(); ++it)
{
octomap_octree->updateNode(*it, true);
}

auto fcl_octree = std::make_shared<fcl::OcTree<float>>(octomap_octree);
std::shared_ptr<fcl::CollisionGeometryf> fcl_geometry = fcl_octree;
return new fcl::CollisionObjectf(fcl_geometry);
}

四,总结

OSG结合FCL实现碰撞的检测,首先将OSG中的Node 转换成FCL中的三角面。然后将点云用上述方式进行转换。最后调用result.isCollision()函数 来判断两个实体是否发生碰撞。

https://github.com/flexible-collision-library/fcl

发表在 三维 | 留下评论

QML 调用C++ 单列对象

一些对象是全局唯一的,特别适合使用单列模式,比如网络,数据库,或者跟设备相关的功能,如串口,plc等,因此在QML中访问C++的单列,就很有必要。

一,C++单例

以一个相机操作为例,这里只列出只要功能。

class ImageHandle : public QThread
{
Q_OBJECT

public:
//单例模式的声明
static ImageHandle* instance(){
static ImageHandle manager;
return &manager;
}

//给QML调用的接口
Q_INVOKABLE void startPreview();

private:
explicit ImageHandle(QObject *parent = nullptr);
~ImageHandle();

};

二,main.cpp 中注册单列,这样才能在QML中使用。

一共两步,一步定义一个函数(configureProvider),还有一步是利用qmlRegisterSingletonType注册

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QIcon>
#include "comm/imageHandle.h"

//单例的使用
static QObject *configureProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return ImageHandle::instance();
}

int main(int argc, char *argv[])
{
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QGuiApplication::setAttribute(Qt::AA_Use96Dpi);

QGuiApplication app(argc, argv);

//单例的使用
qmlRegisterSingletonType<ImageHandle>("ImageHandle",1,0,"ImageHandle",configureProvider);

QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
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中使用

import 导入后 就可以直接使用了。

发表在 QQuick | 留下评论

QML与C++交互

QML写界面,业务逻辑使用C++,既能快速的开发界面也能利用C++的强大生态,这是目前比较被认可的方式,那就涉及到QML与C++对象的交互。

我们以登录例子来说明,页面点击登录,将信息传递到c++ http对象进行密码的验证,然后返回登录结果。

一,调用C++中的函数

1,普通C++类

#ifndef HTTPHANDLER_H
#define HTTPHANDLER_H

#include <QObject>
class HTTPHandler:public QObject{
Q_OBJECT
public:
HTTPHandler(QObject* parent=0):QObject(parent){

}
//登录接口 验证用户名 和密码
Q_INVOKABLE bool login(QString name,QString pwd){
if(name=="admin"&&pwd=="123"){
return true;
}else{
return false;
}
}
};
#endif // HTTPHANDLER_H

2,注册C++ 类

main.cpp注册此 C++ 类型,这样QML中就能使用了。

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "HTTPHandler.h"

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);

//注册类型
qmlRegisterType<HTTPHandler>("HTTPHandler", 1, 0, "HTTPHandler");


QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
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();
}

3,调用C++对象中的函数

只要是C++中 通过Q_INVOKABLE关键词声明的public函数,QML中都能访问。


import QtQuick 2.15
import QtQuick.Controls 1.4
//导入C++ 对象
import HTTPHandler 1.0

Rectangle{
width: 200
height: 200

//相当于 实列化一个C++对象
HTTPHandler{
id:httpHandler
}

signal loginOk()
Row{
anchors.centerIn: parent
TextField{
id:name
}
TextField{
id:pwd
}
}
Button{
anchors.bottom: parent.bottom
text: qsTr("登录")
onClicked: {
//调用C++对象中的函数
if(httpHandler.login(name.text,pwd.text)){
loginOk()
}
}
}
}

二,响应C++中的信号

上述方式相当于同步的方式调用C++中的函数,还可以异步的响应C++中的信号,相当于QML中的槽与C++中的信号进行绑定。

1,C++类

#ifndef HTTPHANDLER_H
#define HTTPHANDLER_H

#include <QObject>
class HTTPHandler:public QObject{
Q_OBJECT
public:
HTTPHandler(QObject* parent=0):QObject(parent){

}

Q_INVOKABLE void login(QString name,QString pwd){
if(name=="admin"&&pwd=="123"){
//验证成功后 激发信号
emit loginSuccess();
}
}

signals:
//登录成功信号
void loginSuccess();
};
#endif // HTTPHANDLER_H

2,响应C++信号

import QtQuick 2.15
import QtQuick.Controls 1.4
import HTTPHandler 1.0

Rectangle{
width: 200
height: 200

//相当于 实列化一个C++对象
HTTPHandler{
id:httpHandler
//绑定C++信号
onLoginSuccess: {
loginOk()
}
}

signal loginOk()
Row{
anchors.centerIn: parent
TextField{
id:name
}
TextField{
id:pwd
}
}
Button{
anchors.bottom: parent.bottom
text: qsTr("登录")
onClicked: {
//调用C++对象中的函数
httpHandler.login(name.text,pwd.text)

}
}
}

三,绑定C++中的属性

还可以直接在C++定义属性,然后QML绑定此属性,适合实时的传递一些状态数据。

1,C++类

#ifndef HTTPHANDLER_H
#define HTTPHANDLER_H

#include <QObject>
class HTTPHandler:public QObject{
Q_OBJECT
//注册属性
Q_PROPERTY(QString status READ getStatus WRITE setStatus NOTIFY statusChanged FINAL)

public:
HTTPHandler(QObject* parent=0):QObject(parent){

}

Q_INVOKABLE bool login(QString name,QString pwd){
if(name=="admin"&&pwd=="123"){
return true;
}else{
//设置状态信息
setStatus("pwd or name error");
}
}

QString getStatus() const;
void setStatus(const QString &newStatus);

signals:
void statusChanged();

private:
//状态信息
QString status;
};

inline QString HTTPHandler::getStatus() const
{
return status;
}

inline void HTTPHandler::setStatus(const QString &newStatus)
{
if (status == newStatus)
return;
status = newStatus;
emit statusChanged();
}


#endif // HTTPHANDLER_H

2,绑定属性

import QtQuick 2.15
import QtQuick.Controls 1.4
import HTTPHandler 1.0

Rectangle{
width: 200
height: 200

//相当于 实列化一个C++对象
HTTPHandler{
id:httpHandler
}

signal loginOk()
Row{
anchors.centerIn: parent
TextField{
id:name
}
TextField{
id:pwd
}
}

//定义一个文本框 直接绑定C++的属性
Text {
anchors.top: parent.top
text: httpHandler.status
}
Button{
anchors.bottom: parent.bottom
text: qsTr("登录")
onClicked: {
//调用C++对象中的函数
if(httpHandler.login(name.text,pwd.text)){
loginOk()
}
}
}
}

3,看下效果

点击登录 ,如果密码或用户名错误会将C++的状态信息,实时的显示到左上角的QML Text控件中。

发表在 QQuick | 留下评论

QML中的页面切换方式

整理下QML中页面切换的两种方式,这里以常见的登录为例,分为两个页面。登录页和主页,软件起来首先呈现登录页,点击登录进入主页,点击退出 返回到登录页

一,隐藏的方式

1.效果

2.定义两个页面(登录页,和首页),并定义登录成功和返回信号。当点击登录时,激发登录成功信号,点击退出时 激发返回信号。

3.main.qml中对两个信号进行处理。分别隐藏和显示不同的页面。

二,动态加载的方式

用Loader占位,然后动态加载组件。类似先定义一个指针,然后根据需要指向不同的内存。

三,SwipeView方式

这种相当于把页面都放到了一个容器里,然后滑动切换。

四,总结

1与3类似。当页面构造时,如果需要同时开辟很大的内存,此时建议用1,3的方式,提前把内存开辟好,这样页面页会加载的快一些。

发表在 QQuick | 留下评论

自定义Switch

一,需求
QML中实现自定义的Switch。
二,效果


三,实现

这个要用以前的版本。

import QtQuick.Controls 1.4 as PreControl
import QtQuick.Controls.Styles 1.4
PreControl.Switch{
id:lightSwidch
height: 26
anchors.verticalCenter: parent.verticalCenter
style: SwitchStyle {
groove: Rectangle {
width: 57
height: 26
radius: 13
color: lightSwidch.checked?"#3F64EB":"#414850"
}
handle: Rectangle{
width: 26
height: 26
radius: 13
color: "#FAFCFF"
}
}
onCheckedChanged: {

}
}
发表在 QQuick | 留下评论