QtIFW学习

2023-10-29

1. 构建Qt安装程序

1.1. Qt应用程序结构

Qt打包发布文档

还有一些说明文档。如:ReadMe、License等

在 Windows 中,要解决可执行文件过大的问题,可以使用像 UPX 这样的加壳工具。在对可执行文件压缩之后,会在启动时将其解压缩到 RAM 中。

而在 Linux 中,可以通过 strip 命令来去除目标文件中的调试信息、符号信息,以减小程序的大小。但要注意一点,strip 只能用于可执行文件和动态库(.so),不能用于静态库(.a)。

1.2. 不同操作系统常用的打包工具

1.2.1. 多平台 GUI 安装程序 跨平台安装工具

  • Qt Installer Framework
    https://doc.qt.io/qtinstallerframework/
    简称 Qt IFW,由 Qt 官方提供,以前仅用于 Qt 本身,但现在已经发布了,用于创建通用的安装程序。

  • InstallBuilder
    http://installbuilder.bitrock.com/
    是一个功能强大、易于使用的跨平台安装程序创建工具。它一直都在积极维护,但仅供商业使用。

  • InstallJammer
    https://sourceforge.net/projects/installjammer/
    是一个跨平台 GUI 安装程序和生成器,虽然是开源的,但 2011 年之后就不再维护了,比较遗憾!

1.2.2. windows

  • NSIS
    https://nsis.sourceforge.io/Main_Page
    如果你喜欢开源软件,则一定知道 NSIS(全称:Nullsoft Scriptable Install System)。它是一个专业的工具,可用于创建从非常简单到非常复杂的安装程序。虽然它很小,但功能却很丰富,非常适合 Internet 分发。
    正如其名,NSIS 是基于脚本的,它能够让我们创建处理任何情况所需的复杂逻辑。幸运的是,它包含了许多插件和预定义脚本,以帮助初学者入门。

  • Inno Setup
    http://www.jrsoftware.org/isinfo.php
    是一个免费的安装制作软件,小巧、简便、精美是其最大特点,支持 pascal 脚本,能快速制作出标准 Windows 2000 风格的安装界面,足以完成一般安装任务。
    该软件用 Delphi 写成,其官网同时也提供源程序免费下载。它虽不能与 Installshield 这类“恐龙级”的安装制作软件相比,但也算是当之无愧的后起之秀。

  • Advanced Installer
    https://www.advancedinstaller.com/
    有一个免费版本,但也有其他几个版本,这些版本的价格取决于安装程序的复杂程度。如果你正在寻找更专业的东西,其中还包括一些支持选项,那么 Advanced Installer 是一个不错的选择。
    用它创建 MSI 文件包非常方便,只需添加文件、修改名称、添加按钮即可,无需任何脚本方面的知识,并且生成的安装文件保证符合 Windows 最佳操作建议。

  • InstallShield
    https://www.flexerasoftware.com/install/products/installshield.html
    是一款“恐龙级”的安装包制作工具,是 Flexera Software 的当家产品。这不仅是因为它拥有 20 多年的研发历史,而且它也是全球著名软件公司的“皇家御用”打包软件,比如 Adobe、Corel、Autodesk 等公司。
    然而这款软件过于专业,并不像 NSIS、Inno Setup 等那样容易入门,所以想学习必须下很大功夫。这也是全球领先的 Windows 安装开发解决方案,现在已经成为 Windows Installer 和 InstallScript 安装方面的行业标准。

  • WIX Toolset
    https://wixtoolset.org/
    是一个免费的打包工具,通常要与 Visual Studio(2012 或更高版本)一起使用。之所以最后提到它,是因为它需要经过大量的学习。虽然可用它创建一些非常复杂的安装程序,但要编写大量的代码并经常使用命令行。

1.2.3. Linux

对于 Linux 系统来说,要查看可执行文件依赖的库以及缺少的函数符号,可以使用 ldd -r 指令。

  • 构建源码包
    对于开源项目来说,这是最简单的方法。
    但一定要记得,在使用 tar 命令对目录树进行压缩之前,需要先运行 make distclean 来清理构建环境。

  • 创建本地分发包
    这要考虑系统的发行版,而主流的包管理系统有两个:rpm 和 deb。rpm 格式起源于 RedHat,但也被其他发行版使用,例如:CentOS、SuSE 和 Fedora。deb 格式是由 Debian 项目开发的,适用于 Debian/Ubuntu 及其衍生版。
    rpm 可参考 RPM HOWTO(http://www.tldp.org/HOWTO/RPM-HOWTO/),尤其是关于构建的部分 - Building RPMs。
    deb 可参考Debian 新维护者手册(https://www.debian.org/doc/manuals/maint-guide/)。

  • 创建独立的应用程序包
    若要将 Qt 程序作为一个独立的包部署到 Linux 中,需要将其以及所需的组件捆绑在一起,像 Qt 库、Qt 插件(尤其是 platforms 插件)。
    推荐一个 Linux 部署工具 - linuxdeployqt(https://github.com/probonopd/linuxdeployqt),它能够自动执行上述的流程,并提供 AppImage(https://appimage.org/)。

Tips:和 Windows 不同的是,Linux 下可执行文件及其依赖库放在同一目录一般是无法正常运行的。常用方式是写一个 Shell 脚本,并用 LD_LIBRARY_PATH 指定依赖库所在目录,然后通过运行这个脚本来间接地启动程序。

1.3. 静态库与动态库的区别

静态库和动态库的区别.jpg

2. 程序打包过程

@startuml
:1.构建realease版本应用程序;
:2.用打包工具添加依赖项;
:3.添加其他帮助文档到目录结构;
:4.测试程序打包是否成功;
@enduml

过程说明:
过程2中. 常用的打包工具

  • windeployqt:由 Qt 官方提供,旨在自动化创建可部署文件夹的过程,该文件夹包含了应用程序所需的 Qt 相关依赖项(库、插件和翻译等)。
  • Dependency Walker:用于查找程序所需的依赖库,类似的工具还有 Process Explore。

Tips:
windeployqt 并没有考虑程序依赖的第三方库(例如:OpenCV)。此外,如果使用了 MSVC 编译器,它也不会将相应的 C/C++ 运行时库拷贝进去。这时,需用Dependency Walker检查程序所链接的库文件。

对于特定的编译器,其依赖的库文件如下:

VC++ 14.0 (2015) MinGW
C 运行时:vccorlib140.dll libwinpthread-1.dll
vcruntime140.dll libgcc_s_dw2-1.dll
C++ 运行时:msvcp140.dll libstdc+±6.dll

3. QtIFW 安装配置

Qt Installer Framework 简称 Qt IFW,是由 Qt 官方提供的安装程序制作框架。
Qt IFW 下载页

3.1. 下载安装对应版本,选择安装路径

bin:提供了一些基本的工具,比如打包要用的 binarycreator。
doc:包含了相应的帮助文档,有助于更好的掌握 Qt IFW。
examples:有各种各样的示例,方便我们学习研究。
Licenses:许可协议。

3.2. 配置环境变量

QtIFW配置环境变量.jpg

3.3. 在QtCreatoer添加帮助文档

QtCreator添加QtIFW帮助文档.jpg
QtIFW帮助文档所在位置.jpg

3.4. 构建examples.pro可查看各个例子安装效果

构建examples.pro例子.jpg

4. QtIFW 创建安装程序

Qt IFW 创建安装程序有一定的步骤:

4.1. 创建一个新的文件夹,里面必须包含两个文件夹config/和packages/

MySoftware根目录.jpg

4.2. config 创建配置文件config.xml

congfigXml文件.jpg

Name: 被添加到页面和简介文本中的应用程序名称
Version: 程序版本号
Title: 标题栏上的安装程序名称
Publisher: 软件的发布者(如 Windows 控制面板中所示)
StartMenuDir: Windows 开始菜单中产品的默认程序组名称
TargetDir: 程序安装的目标路径

其他参数可查看:https://doc.qt.io/qtinstallerframework/ifw-globalconfig.html

4.3. 创建包的配置信息

QtIFW框架中安装程序只需要处理一个组件,提供安装程序时安装组件可选,如:com.qtobject.ifw。 在 com.qtobject.ifw/meta 中配置package.xml。

Package_xml.jpg

DisplayName: 组件名称
Description: 组件描述信息
Version: 组件版本号
ReleaseDate: 组件发布日期
Default: 如果在安装程序中预先选择了组件,则为 true。
Script: js文件名,如本例installscriqt.qs,用于在加载时执行一些安装操作。

其他参数可查看: https://doc.qt.io/qtinstallerframework/ifw-component-description.html#package-information-file-syntax

安装组件效果如下:
组件.jpg
添加到开始菜单.jpg

4.4. 打包软件生成安装程序

  • 在com.qtobject.ifw/data中放入需打包的文件(.exe、.dll 等)。

  • 要生成安装程序,需要借助 QtIFW 提供的 binarycreator.exe 参考链接

  • 打开 CMD或 PowerShell,并进入包目录 ,然后输入
    binarycreator -c config\config.xml -p packages MySoftwareInstaller.exe -v

生成安装程序.jpg

QtIFW 在生成安装程序时,会用自带的 archivegen 工具将这些文件压缩成 7zip 格式;然后在安装时,再从压缩包中将它们提取出来。
好处:保证程序的安全性,压缩程序的大小。

4.5. 测试安装

最好使用一台没有qt开发环境的电脑测试安装程序。除了在windows系统上运行,有条件的话也可以尝试在不同的系统中生成安装程序,测试运行。

5. QtIFW 创建在线安装程序

上一节简单介绍QtIFW离线安装程序,接下来介绍QtIFW如何在线安装程序。主要区别是在于存储库的创建和配置过程。

5.1. 配置存储库

  1. 在config.xml配置存储库信息。这些信息由RemoteRepositories元素指定,它可以包含若干个Repository子元素。Repository每一个都包含以下信息:
    配置存储库.jpg

Url: 存储库的地址,指向列出可用组件列表的 Updates.xml 文件。如上图:本地Url也可以,注意格式前面要加file:///
Enabled: 若为 0,则禁用存储库。若为 1,则启用存储库。
Username: 对于需要身份验证的存储库,用来表示用户名。
Password: 对于需要身份验证的存储库,用来表示密码。
DisplayName: 设置要显示的字符串,而非 Url。

Tips:密码是以纯文本形式保存的,因此不建议在这里输入。此处如果未设置身份认证信息,将会在运行时通过对话框获取,用户可以在运行时处理这些设置。

  1. 生成安装程序
    使用binarycreator -c config\config.xml -p packages MySoftwareInstaller.exe -v 命令生成安装程序

5.2. 创建在线存储库

  1. 在package.xml中修改信息,注意版本号一定要增加
    版本号从1.0.0增加到2.0.0

package.xml更新版本号.jpg

  1. 利用 repogen 工具命令repogen -p packages repository,将包转换为安装程序能在运行时获取的文件结构。此时会生成一个名叫repository 的存储库。里面包含组件包的完整副本和Updates.xml的一些额外生成的元数据(如:SHA安全校验码)
    包的完整副本和元数据.jpg

5.3. 查看在线安装程序

  1. 在安装程序的根目录选中maintenancetool.exe(Qt 中的维护工具,用于添加/更新/删除组件)可以在线更新组件,如下图可以看见组件版本号从1.0.0增加到2.0.0,选中即可更新版本。
    更新组件.jpg

  2. 在设置中的资料档案库可以看见之前配置存储库的用户和密码,选中该存储库可以用下方的按钮“条件测试”检测存储库是否可用。

资料档案库.jpg

6. QtIFW 覆盖安装

QtIFW不支持离线升级,程序新版本目录不能直接覆盖旧版本目录安装,要先卸载。QtIFW 提供覆盖安装的方法简化这一流程。

6.1. 自动卸载旧版本

  1. 编写卸载脚本。参考链接
//我们可以通过模拟用户单击与 UI 交互,来实现最终的卸载功能
// 卸载脚本:如果程序已安装,则会调用 maintenance 工具,自动进行卸载。
function Controller()
{
    gui.clickButton(buttons.NextButton);
    gui.clickButton(buttons.NextButton);

    // 连接信号槽
    installer.uninstallationFinished.connect(this, this.uninstallationFinished);
}

// 当卸载完成时,触发
Controller.prototype.uninstallationFinished = function()
{
    gui.clickButton(buttons.NextButton);
}

// 与完成页面上的部件交互
Controller.prototype.FinishedPageCallback = function()
{
    gui.clickButton(buttons.FinishButton);
}

然后用之前的安装程序测试;执行 maintenancetool.exe --script=uninstallscript.qs 命令卸载旧版本。

  1. 将卸载脚本uninstallscript.qs放入 data 目录中(例如:data/script/uninstallscript.qs),最终由 Qt IFW 打包进安装程序。当需要进行覆盖安装时,maintenancetool 工具就可以很容易的找到它。

6.2. 覆盖安装

  1. 既然是覆盖安装,必然少不了对安装位置的检测,一旦发现程序已安装,往往需要加一些友好性的提示信息(例如:显示的“检测到程序已安装,继续将会被覆盖。”)。

要完成这一步,则需要为安装程序添加自定义 UI。首先,要在 meta 目录下添加一个 targetwidget.ui 界面文件(QtDesigner可以编写ui),然后,还需要在 package.xml 文件中用 UserInterfaces元素标记它:

<?xml version="1.0" encoding="UTF-8"?>
<Package>
    <Name>MySoftware</Name>
    <Version>1.0.0</Version>
    <Title>MySoftware Installer</Title>
    <Publisher>Twy</Publisher>
    <StartMenuDir>MySoftware</StartMenuDir>
    <TargetDir>@HomeDir@/MySoftware</TargetDir>
    <UserInterfaces>
        <UserInterface>targetwidget.ui</UserInterface>
    </UserInterfaces>
</Package>
  1. 修改安装脚本

将交互部分添加到安装脚本 installscript.qs 中。注意程序各文件的路径。

var targetDirectoryPage = null;

// 构造函数
function Component() 
{
    installer.gainAdminRights();
    component.loaded.connect(this, this.installerLoaded);
}

// 实用函数,类似于 QString QDir::toNativeSeparators()
var Dir = new function () {
    this.toNativeSparator = function (path) {
        if (installer.value("os") == "win")
            return path.replace(/\//g, '\\');
        return path;
    }
};

// 添加桌面和开始菜单快捷方式
Component.prototype.createOperations = function() 
{
    component.createOperations();
    component.addOperation("CreateShortcut",
                           "@TargetDir@/bin/MySoftware.exe",
                           "@DesktopDir@/MySoftware.lnk",
                           "workingDirectory=@TargetDir@");

    component.addOperation("CreateShortcut",
                           "@TargetDir@/bin/MySoftware.exe",
                           "@StartMenuDir@/MySoftware.lnk",
                           "workingDirectory=@TargetDir@");
}

// 加载组件后立即调用
Component.prototype.installerLoaded = function()
{
    installer.setDefaultPageVisible(QInstaller.TargetDirectory, false);
    installer.addWizardPage(component, "TargetWidget", QInstaller.TargetDirectory);

    targetDirectoryPage = gui.pageWidgetByObjectName("DynamicTargetWidget");
    targetDirectoryPage.windowTitle = "选择安装目录";
    targetDirectoryPage.description.setText("请选择程序的安装位置:");
    targetDirectoryPage.targetDirectory.textChanged.connect(this, this.targetDirectoryChanged);
    targetDirectoryPage.targetDirectory.setText(Dir.toNativeSparator(installer.value("TargetDir")));
    targetDirectoryPage.targetChooser.released.connect(this, this.targetChooserClicked);

    gui.pageById(QInstaller.ComponentSelection).entered.connect(this, this.componentSelectionPageEntered);
}

// 当点击选择安装位置按钮时调用
Component.prototype.targetChooserClicked = function()
{
    var dir = QFileDialog.getExistingDirectory("", targetDirectoryPage.targetDirectory.text);
    if (dir != "") {
        targetDirectoryPage.targetDirectory.setText(Dir.toNativeSparator(dir));
    }
}

// 当安装位置发生改变时调用
Component.prototype.targetDirectoryChanged = function()
{
    var dir = targetDirectoryPage.targetDirectory.text;
    if (installer.fileExists(dir) && installer.fileExists(dir + "/bin/MySoftware.exe")) {
        targetDirectoryPage.warning.setText("<p style=\"color: red\">检测到程序已安装,继续将会被覆盖。</p>");
    } else {
        targetDirectoryPage.warning.setText("");
    }
    installer.setValue("TargetDir", dir);
}

// 当进入【选择组件】页面时调用
Component.prototype.componentSelectionPageEntered = function()
{
    var dir = installer.value("TargetDir");
    if (installer.fileExists(dir) && installer.fileExists(dir + "/maintenancetool.exe")) {
        installer.execute(dir + "/maintenancetool.exe", "--script=" + dir + "/script/uninstallscript.qs");
    }
}

7. QtIFW 实现自动升级

要实现自动更新,必须要有一个在线存储库respoitory。在 QtIFW 中,如果要实现这样的功能,可以利用 maintenancetool.exe及其两个重要选项:

--checkupdates:检测更新,并返回一个 XML。
--updater:以更新模式启动应用程序。

7.1. 检测更新

为了获取更新相关的信息,我们新建一个 bat 脚本,并通过以下命令将输出重定向到 checkUpdate.txt 文件中:

@echo off
maintenancetool --checkupdates > checkUpdate.txt

当有可用的更新时,maintenancetool 会返回一个 XML,通过bat 脚本将输出信息重定向到 checkUpdate.txt 文件中:其中包含了新版本的名称、大小、以及版本号等内容。倘若没有任何更新,将不会返回任何内容。

7.2. 以更新模式启动maintenancetool

一旦检测到有新版本存在,只需要以更新模式启动 maintenancetool 就行maintenancetool --updater,这样以来,默认就会选择【Update components】选项:剩下的具体要更新哪些组件,就交由用户选择。

7.3. 具体实现

为了启动外部程序,先简单介绍下 QProcess 类,它有两种启动方式:

一体式:start(),外部程序启动后,将随主程序的退出而退出。
分离式:startDetached(),外部程序启动后,当主程序退出时并不退出,而是继续运行。

参考 https://github.com/Skycoder42/QtAutoUpdater。自写Demo,

//在使用 --checkupdates 检测更新时,并不会运行 GUI,而是仅输出更新信息
//以下内容放入main.cpp中的一个函数中
QString program("../maintenancetool.exe");
QStringList checkArgs;
checkArgs << "--checkupdates";

// 检测更新
QProcess process;
process.start(program, checkArgs);

// 等待检测完成
if (!process.waitForFinished()) {
    qDebug() << "Error checking for updates.";
    return;
}

// 读取输出内容
QByteArray data = process.readAllStandardOutput();

// 没有输出意味着没有可用的更新
if (data.isEmpty()) {
    qDebug() << "No updates available.";
    return;
}
// 倘若需要特定的更新信息,应该解析输出的 XML。

//当检测到有可用的更新之后,以更新模式启动 maintenancetool

// 以分离式启动
QStringList updaterArgs;
updaterArgs << "--updater";
bool success = QProcess::startDetached(program, updaterArgs);
if (!success) {
    qDebug() << "Program startup failed.";
    return;
}

//需要注意的是,这里需要以分离式启动,因为程序需要关闭以进行更新。

//在启动成功之后,最后记得关闭程序:

// 关闭程序
qApp->closeAllWindows();

8. 制作一款精美的QtIFW安装程序

主要是界面的UI美化。参考样式可在github上搜索“Qt Frameless"。
在config.xml中添加QSS文件美化UI用StyleSheet等元素标记。

<WizardStyle>Classic</WizardStyle>
<StyleSheet>style.qss</StyleSheet>
<TitleColor>#b1b1b1</TitleColor>

9. 参考资料

  • github地址(含examples) https://github.com/Waleon/QtIFW.git
  • 公众号:高效程序员 教程链接
    -打包发布教程.jpg
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QtIFW学习 的相关文章

  • 如何使用 Qt DOM 通过此语法获取 xml 属性

    我正在使用 Qt DOM XML 解析器 并且遇到了如下属性定义的问题
  • Qt qDebug() 在 Windows shell 中不起作用

    我正在使用一个qDebug Qt 框架的printf屏幕上有东西 当我从 Qt Creator 运行应用程序时它工作得很好 但是当我尝试从 Windows 执行它时cmd它什么也没显示 为什么会发生这种情况 你必须添加 CONFIG con
  • QComboBox 下拉项边距

    我想设计我的风格QComboBox为下拉项目留出边距 现在是这样的 我想要这样的东西 我尝试过 QComboBox QAbstractItemView item margin 3px 但它不起作用 你能帮我解决这个问题吗 您想在项目之间设置
  • 在 QtCreator 中使用 .pro 和 .pri 正确(树)显示 Qt 项目

    是否可以使用项目包含文件 pri 显示不止一个额外级别 例如如果 pro文件包括 pri文件包含两个 pri files pro pri pri pri 在 QtCreator 项目 中显示具有误导性 pro pri pri pri 它根本
  • 如何在 OS X 上的 Qt 应用程序中设置应用程序图标,足以进行分发?

    跟进这个答案 https stackoverflow com a 20918932 368896 to 这个问题 https stackoverflow com questions 20909341 what is the fastest
  • 获取 QListView 中所有可见项目的简单方法

    我正在尝试使用 Qt Framework 开发一个图像库应用程序 应用程序从所选文件夹加载所有图像 并使用 QListView 控件显示这些图像 但现在我想通过仅加载用户可见的图像来减少内存消耗 由于没有直接函数来获取视图中的所有可见项目
  • 使用 CMake 编译时更改头文件位置会导致缺少 vtable 错误

    对于一个大型 C 项目 我需要从 qmake 过渡到 CMake 但是在处理一个玩具示例时 我遇到了一些我不理解的行为 示例代码具有单个头文件 当该头文件移动到子目录中时 我收到 MainWindow 类缺少 vtable 的错误 CMak
  • 清除pyqt中布局中的所有小部件

    有没有办法清除 删除 布局中的所有小部件 self plot layout QtGui QGridLayout self plot layout setGeometry QtCore QRect 200 200 200 200 self r
  • C++ SQL 查询构建库

    我正在寻找一个提供与 c SelectQueryBuilder 库类似功能的 c 库 http www codeproject com Articles 13419 SelectQueryBuilder Building complex a
  • 如何去除QWizard中的水平线?

    我正在研究一个样式表QWizard我想删除按钮上方的水平线 我尝试递归浏览所有小部件并将其边框设置为无 但似乎没有任何小部件具有此边框 这是我的代码 可以找到完整的可构建示例here https gist github com ardeid
  • QML 中可重用的字体属性[重复]

    这个问题在这里已经有答案了 在 QML 中 我希望能够定义一组字体属性以进行简单的语义重用 例如 代替 Text text This is a header font family Encode Sans weight Font Black
  • 将 jstring 转换为 QString

    我正在调用一个返回字符串的 Java 函数 QAndroidJniObject obj QAndroidJniObject callStaticObjectMethod
  • Qt中Q_PROPERTY的意义是什么?

    我无法理解 Q PROPERTY 的用法 Q PROPERTY 如何帮助程序具有防御性 它是干什么用的 我看过这个论坛 但确实无法应用 我已经理解了这个例子 但不明白它的用法 这是一个例子 我能从中得到什么 我知道阅读将赋予只读特权 wri
  • 针对初学者的 QT 商业许可证与非商业许可证 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 QT 许可似乎非常反学习 因为据我所知 用它开发的任何东西都只能是商业的当且仅当 its entire开发是在使用商业许可证的情况下完成的
  • 覆盖 QWebView 中的页面回复

    我试图在 Qt 的 QWebView 中拦截页面 表单请求 并在某些情况下使用替代内容进行响应 QNetworkReply ngcBrowser createRequest Operation operation const QNetwor
  • Qt 支持 Windows 蓝牙 API 吗?

    谁能告诉我 Qt 是否支持 Windows 蓝牙 API 如果是这样 您能否分享一些有关如何使用它的信息 自上次答复以来 这个问题的答案发生了一些变化 Qt 5 2 版为 Linux BlueZ 和 BlackBerry 设备实现了蓝牙 A
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • QAbstractItemModel 如何表示树?

    我仍然很难理解 QAbstractItemModel 对项目的表示 有两种返回 QModelIndex 项的方法对我来说没有任何意义 QModelIndex QAbstractItemModel index int row int colu
  • Qt 相当于 .NET 数据绑定吗?

    Qt 中是否有相当于 NET 数据绑定的功能 我想使用引用数据库中特定实体的 QString 填充一些组合框和其他小部件 但是 如果我可以将数据绑定到这些字符串 而不是基于新的组合框选择再次查询数据库 或者基于构建我自己的将使用 QStri
  • QByteArray 到整数

    正如您可能从标题中看出的那样 我在转换QByteArray为一个整数 QByteArray buffer server gt read 8192 QByteArray q size buffer mid 0 2 int size q siz

随机推荐

  • 清理水草 蓝桥杯模拟

    问题描述 小蓝有一个 n m 大小的矩形水域 小蓝将这个水域划分为 n 行 m 列 行数从 1 到 n 标号 列数从 1 到 m 标号 每行和每列的宽度都是单位 1 现在 这个水域长满了水草 小蓝要清理水草 每次 小蓝可以清理一块矩形的区域
  • VS编译错误:mt.exe : general error c101008d: Failed to write the updated manifest to the resource of file

    一 问题描述 今天在用DDK编译一个驱动时出现以下错误 1 gt 已启动生成 项目 DriverDev 配置 Driver Check Edtion Win32 1 gt 正在链接 1 gt 正在嵌入清单 1 gt mt exe gener
  • 第二节课笔记(基本知识)

    基本知识 变量 的命名和使用 变量名只能包含字母 数字和下划线 变量名可以字母或下划线打头 但不能以数字头 例如 可将变量命名为message 1 但不能将其命名为1 messageo 变量名不能包含空格 但可使用下划线来分隔其中的单词 例
  • 车辆总线-MVB通讯

    概述 MVB Multifunction vehicle bus 为多功能车辆总线 它是列车通信网 TCN Train Communication Network 的一部分 TCN 网络由 WTB Wire Train Bus MVB 构成
  • Q-learning 理解以及简单实现

    强化学习 reinforcement learning 的过程 强化学习中有状态 state 动作 action 奖赏 reward 这三个要素 智能体需要根据当前状态来采取动作 获得相应的奖赏之后 再去改进这些动作 使得下次再到相同状态时
  • MoviePy介绍

    MoivePy是一个用于视频编辑的Python库 可以 剪切 拼接 标题插入 视频合成 视频处理和创建自定义效果 它支持Windows Linux Mac 源码地址 https github com Zulko moviepy 最新发布版本
  • 前端如何调用后端接口进行数据交互(极简)

    前端调用后端接口 获得数据并渲染 一 介绍 一个完善的系统 前后端交互是必不可少的 这个过程可以分成下面几步 前端向后端发起请求 后端接口接收前端的参数后 开始层层调用方法处理数据 后端将最终数据返回给前端接口 前端请求成功后 将数据渲染至
  • ubuntu安装tomcat7

    1 上传tomcat7源码包 百度云盘链接 https pan baidu com s 1Yo4DVOcm667F iKhGwpPBw 密码 ce1h 2 先安装 JDK cd opt tar zxvf jdk 8u161 linux x6
  • 解决GitHub密码授权访问即将失效的问题

    解决GitHub密码授权访问即将失效的问题 前言 1 网络中的解决方法 2 GitHub密码授权弃用通知 3 创建个人访问令牌 4 IDEA设置Token访问GitHub 4 1 忘记密码 4 2 Token授权 5 GitHub 客户端
  • 非Unicode程序创建非本地字符集的文件路径

    开发中需要用到纯MFC的程序完成整包的文件释放 过程大概是根据整包中的文件路径记录在当前执行路径下建立子目录 创建文件 然后将整包中的内存数据写入该文件 正常英文或中文系统下路径创建及文件读写都没有遇到问题 但在路径名称或文件名称出现非本地
  • 前端常见的适配方法

    作为一个真正的前端攻城狮 我们经常会谈到web前端怎么做适配 然而平常经常做的事 一旦问起来突然好像脑子一片空白 只能想到rem flex 媒体查询那些 因此想记录一篇文章以此警醒自己 一 固定布局 pc端 静态布局 以像素作为页面的基本单
  • 谁在为网络安全制造标尺?

    我们想帮助企业往后退一步 去全局的看一下自己的安全能力建设水平如何 以及在当下的阶段最应该做的安全建设是什么 度量 对应的是更清晰的认知 而对企业安全而言 这种认知 也更在成为一把新的标尺 作者 皮爷 出品 产业家 6月开始 吕一平开始频繁
  • MySQL存储引擎:MyISAM和InnoDB区别详解

    1 MyISAM和InnoDB区别 1 1 区别 InnoDB MyISAM 构造 由 frm文件 表空间 分为独立表空间或者共享表空间 和日志文件 redo log 组成 MyISAM在磁盘上存储成三个文件 其中 frm文件存储表定义 M
  • 百度坐标(BD-09)、国测局坐标(火星坐标,GCJ-02)、和 WGS-84 坐标系之间的转换

    百度坐标 BD 09 国测局坐标 火星坐标 GCJ 02 和 WGS 84 坐标系之间的转换 Created by Wandergis on 2015 7 8 提供了百度坐标 BD 09 国测局坐标 火星坐标 GCJ 02 和 WGS 84
  • CentOS 7安装Zabbix 4.4

    我们当前部署Zabbix是在Centos7的基础上部署Zabbix4 4版本 我的服务器配置是双路四核CPU 8GBRAM 以下安装步骤仅供参考 大家也可以参考官方资料 https www zabbix com documentation
  • 142 环形链表

    142 环形链表 给定一个链表的头节点 head 返回链表开始入环的第一个节点 如果链表无环 则返回 null 重点在于如何判断是否有环 采用快慢指针的做法 快指针每次走2步 慢指针每次走1步 快指针相对慢指针每次多走一步 这样确保如果有环
  • Map Reduce和流处理

    欢迎大家前往腾讯云 社区 获取更多腾讯海量技术实践干货哦 本文由 从流域到海域翻译 发表于腾讯云 社区 map 和reduce 是在集群式设备上用来做大规模数据处理的方法 用户定义一个特定的映射 函数将使用该映射对一系列键值对进行处理 直接
  • C++日期累加

    日期累加 题目描述 设计一个程序能计算一个日期加上若干天后是什么日期 输入描述 输入第一行表示样例个数m 接下来m行每行四个整数分别表示年月日和累加的天数 输出描述 输出m行 每行按yyyy mm dd的个数输出 include
  • 深度学习(二):张量和基本运算

    张量是一个类型化的n维数组 tf Tensor 是tensorflow基本数据格式 张量的阶就是数组的维度 张量的属性 图 形状 名字 op print a graph 输出程序所在的内存地址 print a shape 输出张量a的维度
  • QtIFW学习

    1 构建Qt安装程序 1 1 Qt应用程序结构 1 2 不同操作系统常用的打包工具 1 2 1 多平台 GUI 安装程序 跨平台安装工具 1 2 2 windows 1 2 3 Linux 1 3 静态库与动态库的区别 2 程序打包过程 3