基于Qt的ribbon风格框架,简单易用,漂亮大方。这种框架适合功能复杂,并且交互上有一个主窗体的软件,类似cad或者ps。

Qt pro工程管理文件,本人认为是很好用的,语法简洁易懂,但是只能在QtCreator中使用,想用使用其它IDE比如Clion或者vs,CMakeLists是种通用的选择,另外QtCreator的调试功能跟粑粑一样。
一,思路
C++ 中编译,无外乎代码本身的头文件,源文件。三方库的头文件,库文件。Qt本身自带了UI文件和qrc资源文件。再就是宏定义或者路径的配置。
二,Qt pro文件
#需要的Qt模块
QT += core gui concurrent
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
#C++版本
CONFIG += c++17
#源文件
SOURCES += \
main.cpp \
util/DarkStyle.cpp
#头文件
HEADERS += \
3rd/snap7/include/snap7.h \
util/DarkStyle.h
#UI文件
FORMS += \
widget.ui \
widget/configwidget.ui
#翻译文件
TRANSLATIONS += \
consistency_zh_CN.ts
CONFIG += lrelease
CONFIG += embed_translations
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
#第三方库
INCLUDEPATH += $$PWD/3rd/snap7/include
LIBS += -L$$PWD/3rd/snap7/lib -lsnap7
win32:LIBS += -luser32
#资源文件
RESOURCES += \
app.qrc \
resources/darkstyle.qrc
#图标文件
RC_ICONS = app.ico
三,CMakeLists 文件
cmake_minimum_required(VERSION 3.20)
project(Consistency VERSION 1.0.0)
#C++ 版本
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
#用来编译ui文件
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
#指定输出目录
set(OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${OUTPUT_PATH})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${OUTPUT_PATH})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${OUTPUT_PATH})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${OUTPUT_PATH})
#需要加载的Qt模块
set(CMAKE_PREFIX_PATH "D:\\Qt\\5.15.2\\msvc2019_64\\lib\\cmake\\Qt5")
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets Network Concurrent)
#头文件
set(HEADERS
3rd/snap7/include/snap7.h
util/DarkStyle.h
)
#源文件
set(SOURCES
main.cpp
util/DarkStyle.cpp
)
#UI文件
set(FORMS
widget.ui
widget/configwidget.ui
)
#资源文件
set(QRC_FILES
app.qrc
resources/darkstyle.qrc
)
qt5_add_resources(QRC_HEADERS ${QRC_FILES})
#第三方库
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/3rd/snap7/include)
file(GLOB Snap7_LIB ${CMAKE_CURRENT_SOURCE_DIR}/3rd/snap7/lib/*.lib)
#生成可执行文件
add_executable(${PROJECT_NAME}
${HEADERS}
${SOURCES}
${FORMS}
${QRC_HEADERS}
logo.rc
)
#链接对应的库
target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network Qt5::Concurrent)
target_link_libraries(${PROJECT_NAME} User32.Lib)
target_link_libraries(${PROJECT_NAME} ${Snap7_LIB})
四,备注
CMake 无法像pro那样处理图标文件,需要先新建一个rc文件,rc文件中指明icon文件,然后加载rc文件。
#logo.rc 文件
IDI_ICON1 ICON DISCARDABLE "app.ico"
想做一个机械臂仿真软件,以前利用C++ 结合FCL和OSG实现了一个半成品,但是这种方案需要根据机械臂DH参数,解析出机械臂各关节位姿然后利用OSG渲染,并且需要自己实现逆解算法,过于麻烦。而使用pybullet,上述得大部分功能都有现成的API接口。
仿真软件得几个要素:
(1),渲染引擎 ,需要将模型渲染可视化出来。VTK,OSG或者原生OpenGL。
(2),动力学引擎,根据物体的物理属性计算运动、旋转和碰撞。Bullet或者ODE,或者只用于碰撞检测的FCL。
(3),模型描述,负责机器人模型的建模,描述各机器人的连杆和关节,还有质量属性,颜色等。比如常用的URDF文件和SDF文件。
pybullet 具备了上述所有的要素,动力学引擎使用bullet,渲染使用OpenGL,并可以直接加载URDF文件,而且提供了 正逆算法。
实现逻辑:
1,利用URDF文件,加载模型。
2,给出目标,也就是抓取点位置。
3,利用逆解算法,求出各关节角度。
4,设置各关节角度,并判判断否碰撞。
5,理想情况下,将此组角度过滤,选择下一组不碰撞的数据 发送真实的给机械臂。
代码:
import pybullet as p
import pybullet_data
import time
import math
from collections import namedtuple
import numpy as np
# 启动仿真引擎的GUI
p.connect(p.GUI)
# 设置重力加速度
p.setGravity(0, 0, -9.81)
# 加载URDF模型路径
p.setAdditionalSearchPath(pybullet_data.getDataPath())
# 设置视角
p.resetDebugVisualizerCamera(cameraDistance=3,
cameraYaw=120,
cameraPitch=-45,
cameraTargetPosition=[0,0,0])
# 加载平面模型作为地面
planeId = p.loadURDF("plane.urdf")
#托盘
trayId=p.loadURDF("tray/traybox.urdf",basePosition=[0.6,0,0.63])
#桌子
tableId=p.loadURDF("table/table.urdf",basePosition=[0.5,0,0])
# 加载ur5 机械臂
robotId = p.loadURDF("D:\\keiler\\sim\\urdfs\\ur5.urdf",useFixedBase=True ) #basePosition=[0.0,0,0.62]
initial_position = [0, 0, 0.62] # 设置初始位置为 [0, 0, 0.62] 米
initial_orientation = p.getQuaternionFromEuler([0, 0, 0]) # 设置初始方向为欧拉角 [0, 0, 0] 对应的四元数
p.resetBasePositionAndOrientation(robotId, initial_position, initial_orientation)
# 获取机械臂末端执行器的索引
endEffectorIndex =6
# 获取机械臂的关节数量
numJoints = p.getNumJoints(robotId)
print("关节数量:"+str(numJoints))
# 打印每个关节的信息
for joint_index in range(numJoints):
joint_info = p.getJointInfo(robotId, joint_index)
print(f"Joint {joint_index}: {joint_info}")
# 机械臂的初始位置
restingPosition = [0,3.14, -1.57, 1.57, 1.57, 1.57, -1.57, 0]
for jointNumber in range(numJoints):
p.resetJointState(robotId, jointNumber, restingPosition[jointNumber])
# 设置圆形的参数
circle_radius = 3
circle_center = [0, 0]
numPoints = 50
angles = [2 * math.pi * float(i) / numPoints for i in range(numPoints)]
#调参控件
target_x = p.addUserDebugParameter("Target X", -10, 10, 0)
target_y = p.addUserDebugParameter("Target Y", -10, 10, 0)
target_z = p.addUserDebugParameter("Target Z", -10, 10, 0)
#开始按钮
button_start = p.addUserDebugParameter("Start", 1, 0, 1)
# 初始状态变量
button_state_prev = 0
#画个球
sphere_visual = p.createVisualShape(shapeType=p.GEOM_SPHERE, radius=0.03, rgbaColor=[1, 0, 0, 1])
sphere_id = p.createMultiBody(baseMass=0, baseCollisionShapeIndex=-1, baseVisualShapeIndex=sphere_visual, basePosition=[0, 0, 1])
#多组解
def calculate_ik_multiple_solutions(robot_id, end_effector_index, target_position, target_orientation, num_solutions=5):
solutions = []
for i in range(num_solutions):
# 生成一个随机的初始关节状态
random_joint_positions = [np.random.uniform(-np.pi, np.pi) for _ in range(numJoints)]
# 计算逆运动学
ik_solution = p.calculateInverseKinematics(robot_id, end_effector_index, target_position, target_orientation, jointDamping=[0.01]*numJoints, lowerLimits=[-np.pi]*numJoints, upperLimits=[np.pi]*numJoints, jointRanges=[2*np.pi]*numJoints, restPoses=random_joint_positions)
solutions.append(ik_solution)
return solutions
try:
while True:
x = p.readUserDebugParameter(target_x)
y = p.readUserDebugParameter(target_y)
z = p.readUserDebugParameter(target_z)
# 更新球的位置
p.resetBasePositionAndOrientation(sphere_id, [x, y, z], [0, 0, 0, 1])
#判断按钮状态
button_state = p.readUserDebugParameter(button_start)
if button_state != button_state_prev:
print("Button pressed")
# 使用逆向运动学计算关节角度
jointPoses = p.calculateInverseKinematics(robotId, endEffectorIndex, [x, y, z],[0, 0, 0, 1])
# 移动机械臂
p.setJointMotorControl2(bodyIndex=robotId,jointIndex=1,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[0],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
p.setJointMotorControl2(bodyIndex=robotId,jointIndex=2,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[1],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
p.setJointMotorControl2(bodyIndex=robotId,jointIndex=3,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[2],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
p.setJointMotorControl2(bodyIndex=robotId,jointIndex=4,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[3],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
p.setJointMotorControl2(bodyIndex=robotId,jointIndex=5,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[4],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
p.setJointMotorControl2(bodyIndex=robotId,jointIndex=6,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[5],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
button_state_prev = button_state
contact_points = p.getContactPoints(bodyA=trayId,bodyB=robotId)
isColl = bool(contact_points)
if isColl:
print("撞上了!")
p.stepSimulation()
time.sleep(0.01)
except KeyboardInterrupt:
# 用户中断程序时,退出循环
print("Circle drawing interrupted by user.")
# 断开与仿真引擎的连接
p.disconnect()
问题:
1,逆解 只能返回一组关节角度,没法根据碰撞,去规划路径。
2,返回的各关节角度 貌似有偏差。
3,界面二次开发有点困难,控件太少,想封装成一个产品需要结合其它界面框架,比如Qt,有个想法是用pybullet当服务器,PyQt+VTK当客户端,因为这套东西可以无UI运行。
效果:
源码:
C++环境下,Qt+VTK是一个比较成熟的方案,那python下也可以使用同样的方式也就是PyQt+VTK。
1,VTK安装
pip install vtk
2,Qt安装
参考:
http://qthello.com/index.php/2024/05/01/pyqt/
3,代码
#!/usr/bin/env python3
import sys
import vtkmodules.vtkInteractionStyle
import vtkmodules.vtkRenderingOpenGL2
import vtk
from PyQt5 import QtCore, QtGui, QtWidgets
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from vtkmodules.vtkFiltersSources import vtkCubeSource
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderer
)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.frame = QtWidgets.QFrame()
self.vl = QtWidgets.QVBoxLayout()
self.vtkWidget = QVTKRenderWindowInteractor(self.frame)
self.vl.addWidget(self.vtkWidget)
self.ren = vtkRenderer()
self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()
# Create source
source = vtkCubeSource()
source.SetCenter(0, 0, 0)
source.SetXLength(1.0)
source.SetYLength(1.0)
source.SetZLength(1.0)
# Create a mapper
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(source.GetOutputPort())
# Create an actor
actor = vtkActor()
actor.SetMapper(mapper)
self.ren.AddActor(actor)
self.ren.ResetCamera()
self.frame.setLayout(self.vl)
self.setCentralWidget(self.frame)
self.show()
self.iren.Initialize()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
4,效果
Qt在windows平台上显示中文,简直是一门玄学,经过测试,有如下发现:
1,
环境:Qt 5.15.2 vs2019 64位 win11系统
默认用Qt 创建的文件使用utf-8编码格式,此环境下 中文没有问题
ui->textEdit->append("中文测试");
2,
某些 低于此版本的qt,上述方式会出现乱码的情况,此时需要将utf-8编码方式的文件转成utf-8 带bom 。比如使用txt进行转换,但是win7 系统的某些版本 txt 不带此格式。
3,总结
1,简单起见,在windows平台,如果只是windwos平台系统,直接将所有文件转成utf-8 带bom 格式,然后中文使用QStringLiteral(“中文”) 即可。
2,全部使用英文 如,tr(“en”),然后再去翻译文件中翻译。
4,转码工具
分享一个自己写得utf-8 和utf-8 带bom 得转码工具。
源码:
QML 原生的储存方有两种:
1,Settings
跟QWidget 中的QSettings 一样,可以简单的存储一些配置。
2,SQLite
sqlite数据库。可以存储一些复杂的数据。
一,Settings
我们以一个按钮的位置为例,进行讲解。
按钮移动时将x y 保存到Settings 中。
下次加载页面时 从Settings 读取位置。
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import Qt.labs.settings 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Settings {
id: settings
property int x: 0
property int y: 0
}
Button{
id:btn
text: "hello"
x:settings.x
y:settings.y
}
Button{
anchors.centerIn: parent
text: "move"
onClicked: {
btn.x+=10
btn.y+=10
settings.x = btn.x
settings.y = btn.y
}
}
}
main.cpp 中需要 定义 应用程序名称,公司名称,域名称。
效果:
二,SQLite
1,将数据库操作 单独封装成一个js文件。
var db;
function initDatabase() {
db = LocalStorage.openDatabaseSync("Test", "1.0", "", 100000);
try {
db.transaction( function(tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS data(name TEXT, desc TEXT, value TEXT)');
})
} catch (err) {
console.log("Error creating table in database: " + err)
};
}
function readData(name) {
var res="";
if(!db) { return; }
db.transaction( function(tx) {
var result = tx.executeSql('select value from data where name=?', [name]);
if (result.rows.length > 0) {
res = result.rows.item(0).value;
} else {
res = "Unknown";
}
})
return res
}
function insertData(name, desc,value) {
var res = "";
if(!db) { return; }
db.transaction( function(tx) {
var result = tx.executeSql('INSERT OR REPLACE INTO data VALUES (?,?,?);', [name,desc, value]);
if (result.rowsAffected > 0) {
res = "OK";
} else {
res = "Error";
}
})
return res
}
2,使用的时候 引入js 文件,之后直接调用接口就可以了。
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.LocalStorage 2.0
import "db.js" as DB
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Component.onCompleted: {
//初始化数据库
DB.initDatabase()
}
Column{
anchors.centerIn: parent
spacing: 10
Row{
spacing: 10
Button{
text: "添加"
onClicked: {
//添加数据
DB.insertData(name.text,desc.text,value.text)
}
}
TextField{
id:name
}
TextField{
id:desc
}
TextField{
id:value
}
}
Row{
spacing: 10
Button{
text: "查询"
onClicked: {
//查询数据
result.append(DB.readData(queryName.text))
}
}
TextField{
id:queryName
}
TextEdit{
id:result
}
}
}
}
3,main.cpp 中 指定下数据库的保存路径,比如当前路径。 不指定的话,会默认存到系统路径,不太好找。
4,效果
记录下PyQt程序的打包。
一,安装
pip3 install PyInstaller
二,打包
pyinstaller -w -n app app.py
根据需要选择打包参数,例如:
-F表示生成单文件模式,即只有一个可执行文件,比如windows下的exe。执行会比较慢。
-w表示生成窗口模式,即不显示控制台窗口
-i表示指定图标文件,例如-i icon.ico
-n表示指定输出文件名,例如-n app.exe
三,执行
打包后的程序位于 dist文件下。
具体程序 参考:
PyQt 入门
Python体系下GUI框架也多了去了,PyQt算是比较受欢迎的一个。如果对Qt框架熟悉,那掌握这套框架是很简单的。
一,安装
1.PyQt5
pip3 install PyQt5
2.Designer UI工具
注:这个工具对python版本有要求,貌似只支持到3.9 所以如果python高于这个版本,可以单独安装 QtDesigner工具 (pip3 install PyQt5Designer)
pip3 install PyQt5-tools
3.UI文件转py文件工具。
python下UI文件无法直接使用,需要使用这个工具转成py文件。
注:windows 下我的python版本为3.12 ,自带了这个工具,无需单独安装。
sudo apt-get install pyqt5-dev-tools
二,使用
1.创建UI文件。
使用designer工具创建ui文件。随便放几个控件,然后保存到工程目录下,取名为widget.ui。
我的designer工具位于这里:
/home/keiler/.local/lib/python3.11/site-packages/qt5_applications/Qt/bin/designer
2.UI文件转py文件。
pyuic5 -o widget.py widget.ui
3,主程序加载py文件。
import sys
from PyQt5.QtWidgets import QApplication, QWidget
from widget import Ui_Form
#Ui_Form 为 Ui文件中的类
class MyApp(Ui_Form, QWidget):
def __init__(self):
super().__init__()
self.setupUi(self)
#信号槽的连接
self.pushButton.clicked.connect(lambda:print("hello"))
self.pushButton_2.clicked.connect(self.fun)
self.show()
def fun(self):
self.textEdit.append("good")
# 应用程序入口
if __name__ == "__main__":
app = QApplication(sys.argv)
my_app = MyApp()
sys.exit(app.exec_())
4,效果
在开发用户界面时,最好的办法是保持数据与可视化的分离。比如一个通讯录,可以用list展示,也可以用grid展示,这都属于view部分,可以不同。但是数据都是相同的,都有名称,头像,手机号等元素。QML针对这种场景提供了model-view框架。其实同样的模式在传统的QWidget 框架中也有,比如QListView 和QTableView。
一,实现起来,记住三个关键点:
1,model。 数据来源,一般是通过网络请求获取。
2,view。负责可视化显示,是ListView 还是GridView。
3,delegate。代理,就是怎么展示数据。它负责将model和view连接起来,将model中的每条数据通过具体的控件,渲染到view中。
下面通过一个例子,来说明代码的写法,这个例子类似一个通讯录,数据我们写死,真实情况应该是从接口获取。利用ListView进行可视化。代理是一个Rectangle 内部一个Image展示头像,一个Text 显示文本。
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
//可视化view
ListView{
anchors.fill: parent
width: 100
delegate: addressDelegate
model: model
}
//list 模型
ListModel{
id:model
}
//代理
Component{
id:addressDelegate
Rectangle{
width: 140
height: 120
RowLayout{
anchors.fill: parent
anchors.margins: 10
//头像
Image {
source: img
Layout.preferredWidth: 100
Layout.preferredHeight: 100
}
//名称
Text {
text: name
verticalAlignment: Text.AlignVCenter
}
}
}
}
//构造模型数据
Component.onCompleted: {
model.append({"img":"https://img2.baidu.com/it/u=607192830,2624468444&fm=253&fmt=auto&app=120&f=JPEG?w=1025&h=684","name":"刘备"})
model.append({"img":"https://img2.baidu.com/it/u=1662844506,2024543724&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=1052","name":"关羽"})
model.append({"img":"https://img2.baidu.com/it/u=2512544330,2343544095&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=300","name":"张飞"})
model.append({"img":"https://img2.baidu.com/it/u=4186203074,1390758742&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=618","name":"赵云"})
}
}
二,总结
这套模型还有很多用法,比如数据量特别大的时候,可以使用Flickable元素,同时可以限制代理数量。还可以动态的增加,删除模型。添加页眉页脚等。但是底层逻辑还是那三条,model 负责加载数据,view 负责显示,delegate负责具体每条数据如何展示。
一,需求
项目要用到第三方软件,这个软件没有提供SDK,只提供了两个exe,每次开机后需要启动这两个exe,并且还要点击上边的两个按钮。这样的用户体验怎么能让人接受呢,如果查资料,发现windows提供了接口,可以自动化操作。
二,逻辑
三,实现
窗体标题 或者句柄 可以使用 Spy++ 这个工具获取。
#include <iostream>
#include <Windows.h>
//杀死窗口所在的进程
void killEXE(LPCWSTR title) {
HWND hwnd = FindWindow(NULL, title);
if (hwnd != NULL) {
DWORD processId;
GetWindowThreadProcessId(hwnd, &processId);
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, processId);
if (hProcess != NULL) {
if (TerminateProcess(hProcess, 0)) {
std::cout << "Process terminated successfully!" << std::endl;
}
else {
std::cerr << "Failed to terminate process! Error code: " << GetLastError() << std::endl;
}
CloseHandle(hProcess);
}
else {
std::cerr << "Failed to open process handle!" << std::endl;
}
}
else {
std::cerr << "Window not found!" << std::endl;
}
}
//打开可执行文件
void openEXE(LPCWSTR applicationName) {
LPWSTR commandLine = NULL;
STARTUPINFOW startupInfo;
PROCESS_INFORMATION processInfo;
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
if (CreateProcessW(applicationName, commandLine, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) {
std::cout << "Process created successfully!" << std::endl;
}
else {
std::cerr << "Failed to create process! Error code: " << GetLastError() << std::endl;
}
}
//最小化窗口
void minimizeDialog(LPCWSTR title) {
HWND hwnd = FindWindow(NULL, title);
if (hwnd != NULL) {
ShowWindow(hwnd, SW_MINIMIZE);
std::cout << "Window minimized successfully!" << std::endl;
}
else {
std::cerr << "Window not found!" << std::endl;
}
}
//点击窗口上的按钮
void clickBtn(LPCWSTR title, LPCWSTR btnName) {
HWND hwnd = FindWindow(NULL, title);
if (hwnd != NULL) {
HWND btnHandle = FindWindowEx(hwnd, NULL, L"Button", btnName);
if (btnHandle != NULL) {
SendMessage(btnHandle, BM_CLICK, 0, 0);
SendMessage(btnHandle, BM_CLICK, 0, 0);
}
else {
std::cerr << "Button not found!" << std::endl;
}
}
else {
std::cerr << "Window not found!" << std::endl;
}
}
int main()
{
//关闭进程
killEXE(L"Geomagic Control X Automation (Client)");
killEXE(L"Geomagic Control X Automation (Server)");
//打开客户端进程
openEXE(L"D:\\Program Files\\Oqton\\Geomagic Control X 2024.1\\Geomagic Control X Automation (Client).exe");
Sleep(1000);
//最小化客户端
minimizeDialog(L"Geomagic Control X Automation (Client)");
//打开服务器进程
openEXE(L"D:\\Program Files\\Oqton\\Geomagic Control X 2024.1\\Geomagic Control X Automation (Server).exe");
Sleep(1000);
//点击OK按钮
clickBtn(L"Geomagic Control X Automation (Server) - Mode Select",L"OK");
Sleep(1000);
//点击 Start Server按钮
clickBtn(L"Geomagic Control X Automation (Server)", L"Start Server");
Sleep(1000);
//最小化服务器客户端
minimizeDialog(L"Geomagic Control X Automation (Server)");
}
三,效果(需要管理员权限运行程序)
一,需求
Qt开发的GUI程序,部署到树莓派中,并设置开机自启。
二,步骤
1,制作启动脚本和停止脚本。
我们需要编写两个脚本,来启动和停止我们的应用。这里假设我们的应用名称为test。
#启动脚本
#!/usr/bin/env bash
#设置环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/test
cd /usr/test
#启动主程序
./test
#停止脚本
#!/usr/bin/env bash
ID=` ps -ef | grep test | grep -v grep | awk '{print $2}'`
for id in $ID
do
kill -9 $id
done
2,制作安装脚本
我们需要编写一个脚本用于安装qt打包后的程序,也就是整个文件夹。脚本的作用就是将文件夹拷贝到指定的位置。
比如我们的程序在test文件夹内,内部含有完整的Qt应用程序及依赖库,我们在同级目录下创建一个安装脚本 install.sh。
#!/usr/bin/env bash
Install_App() {
echo "installing test ..."
target=$1
if [ x$target == x'' ]; then
echo "please input parameter 1 for target path"
exit
fi
sudo /usr/test/stop.sh
sleep 1
if [ ! -d "$target" ]; then
sudo mkdir $target
fi
cp -a test/* $target/
#sudo chown -R root:root $target
}
if [ $UID -ne 0 ]; then
echo "Is not superuser,please use sudo "
exit
else
ROOT_PATH=/usr/test
Install_App $ROOT_PATH
echo "Install completed !"
reboot
fi
3,系统的设置
这里有两种方式。
(1),sudo vim /etc/xdg/lxsession/LXDE-pi/autostart
#@lxpanel --profile LXDE-pi
#@pcmanfm --desktop --profile LXDE-pi
#@xscreensaver -no-splash
@sudo /usr/test/start.sh
(2),
[Desktop Entry]
Name=AICounter
Comment=AICounter
Exec=sudo /usr/test/start.sh
Terminal=false
MultipulArgs=false
Type=Application
Categories=Application;Development;
StartupNotify=ture
三,树莓派的一些配置
1,屏蔽开机警告
在/boot/config.txt末尾添加语句
avoid_warnings=2
2,修改欢迎界面
替换 /usr/share/plymouth/themes/pix/splash.png
3,更换软件源
sudo nano /etc/apt/sources.list
deb https://mirrors.ustc.edu.cn/debian/ bullseye main contrib non-free
deb https://mirrors.ustc.edu.cn/debian/ bullseye-updates main contrib non-free
deb https://mirrors.ustc.edu.cn/debian/ bullseye-backports main contrib non-free
deb https://mirrors.ustc.edu.cn/debian-security/ bullseye-security main contrib non-free
sudo apt-get update
4,时间相关
1,关掉网络 时间同步服务 sudo systemctl stop systemd-timesyncd.service
2,启动网络 同步时间服务 sudo systemctl start systemd-timesyncd.service
3,时间同步服务 禁止自启 sudo systemctl disable systemd-timesyncd.service
4,开机自启 时间同步服务 sudo systemctl enable systemd-timesyncd.service
5,手动修改时间 sudo date -s "2021-10-31 20:18:00"
6,date 检查时间
7,修改时区:sudo dpkg-reconfigure tzdata 亚洲 上海
要想改时间,先要修改为正确的时区
5,pcf85063a时钟芯片
/boot/configini
dtparam=i2c_arm=on
dtparam=i2c_vc=on
dtoverlay=i2c-rtc,pcf85063a,i2c_csi_dsi
6,中文支持
sudo apt-get install scim-pinyin
QML 中风格和主题的设计可以通过配置文件选择现有几种中的一种,或者直接在控件定义时,指定其属性,如背景颜色或者字体大小。在QWidget框架中,则通过了一种叫做qss样式表的东西来进行描述,跟CSS逻辑上类似。
这个qss抽象出来就两个元素,一个叫做选择器,一个叫做属性。简单的说就是谁长什么样子。那这个谁,有好多种写法,比如所有按钮,某个按钮,按下的按钮。而这个样子又有好多,比如背景色,边框,圆角等。
一,实现
1,新建一个qss文件,并添加到资源里。然后在main函数中,进行全局的设置。
2,字体 一般是一个全局的设置,所以可以在此处设置全局字体。微软雅黑 是一个常用的字体
3,我们以如下的窗口来举例说明。
原始窗口如下:
qss 如下:
//所有QPushButton 背景色为蓝色,宽高为 180 80
QPushButton{
background-color: rgb(0, 0, 255);
width:180px;
height:80px;
}
//所有QPushButton 鼠标hover时 背景为黄色
QPushButton:hover {
background-color: rgb(255, 255, 0);
}
//所有QPushButton flat属性为true 时 背景为绿色
QPushButton[flat="true"] {
background-color: rgb(0, 255, 0);
}
//指定这两个按钮 背景为红色
#pushButton_5,#pushButton_6{
background-color: rgb(255, 0, 0);
}
效果如下:
4,还可以用自己的图片 替换子控件。
/*combobox 的下拉箭头*/
QComboBox::down-arrow {
image: url(:/img/axis_return.png);
}
二,常用属性
三,QSS支持的类型、属性、伪状态、子控件
https://doc.qt.io/qt-5/stylesheet-reference.html
四,总结
属性和控件很多,全背下来没有必要。书写时记住一个逻辑,谁 长什么样子。这个谁可以是全部控件,也可以是某个控件,或者控件的某个状态。哪些样子可以设置,参考文档就行了。
代码:
https://gitcode.com/keiler20181/WidgetStyle.git
五,qt帮助查询
1、“Qt Style Sheets Reference” Qt样式表的用法。
2、“Qt Style Sheets Examples” Qt样式表的实例
3、“The Style Sheet Syntax” Qt样式表语法
桌面虚拟天文馆软件,它使用 OpenGL 对星空进行实时渲染,在你的电脑桌面上生成一片虚拟 3D 天空,因此星空效果和你用肉眼,望远镜或者天文望远镜观察 到的星空别无二致。
1,主页地址
2,源码地址
一个QMainWindow 的dock系统。停靠方式 类似于vs 编译器。
1,源码地址