QT上位机开发(会员管理软件)

news2024/12/23 18:20:29

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        前面我们学习了ini文件的解析办法,通过QSettings类就可以很轻松地访问ini文件里面的数据。除了ini文件之外,另外一种经常出现的文件格式其实是json格式。一般来说,如果读写的数据不是很多,那么完全可以用json文件替换成数据库,实现数据的保存和加载工作。今天,我们通过编写一个会员管理软件的办法,正好学习下qt下面如何进行json数据的处理。当然,还可以借助这个小项目,多了解一下qt下面不同控件的用法和写法。

1、设计界面

        如果界面上的控件比较少,可以直接用c++语言编写,没有问题。但是如果控件比较多的话,那么建议还是用designer来进行设计。本次编写的会员管理软件,控件的数量稍微有点多,正好可以借这个机会把designer练一练。

        练习的过程当中,我们也发现,部分控件存在着排列层次的关系。比如左侧的operation,如果是后面加上去的,没有把它放到单选框、标签、输入框的最下面,那么生成窗口之后,其实不管是radioButton、还是textBox,都是没有办法进行输入的。这一点可能需要稍微注意下。另外,整个界面是删除菜单栏、工具栏和状态栏的。

2、QtWidgetsApplication.h头文件

        头文件中需要注意的部分,主要就是各个控件的回调函数。这里面有radioButton的回调函数、按钮的回调函数。其中radioButton虽然是4个,但是可以看成是一组,这样可以少写3个回调函数,编写上面比较方便一点。当然,除了界面之外,关联的业务数据也要根据实际情况及时添加上。

#pragma once

#include <string>
#include <vector>
using namespace std;

#include <QtWidgets/QMainWindow>
#include "ui_QtWidgetsApplication.h"

class QtWidgetsApplication : public QMainWindow
{
    Q_OBJECT

public:
    QtWidgetsApplication(QWidget *parent = nullptr);
    ~QtWidgetsApplication();

private:
    Ui::QtWidgetsApplicationClass ui;
	int mode_;
	int max_number_;
	vector<int> id_array_;
	vector<string> name_array_;

private:
	int findDataById(int id);
	void updateData();
	void loadFile();

private:
	void onRadioButtonToggled(bool checked);
	void onOkClicked();
	void onCancelClicked();
	void onSaveClicked();

};

3、QtWidgetsApplication.cpp文件设计

        到目前为止,这个cpp文件算得上是目前qt项目代码行数最多的文件,主要也是因为功能要求比较多。首先,它包含了基本的构造函数和析构函数。构造函数里面最主要的部分,就是把控件和它的回调函数关联在一起。其次,代码中涉及到json数据的加载和保存。和c# wpf不同,qt本身有相关的类来处理这些数据。最后,就是业务逻辑。业务逻辑一般比较复杂、麻烦一点,编写之前最好想清楚,比如插入数据的时候是不是需要检查一下是不是有同名id,删除的时候是不是考虑存在找不到的情况。crud的处理方式虽然比较简单,但是涉及到业务层面,还是要想清楚、搞明白,中间出错都没有关系,但是可以通过这个crud来提高自己的业务分析能力,也是不错的一种方式。

        另外,因为测试的时候涉及到了data.json文件,这部分大家可以先参考这个模板。将来使用的话,可以在这个模板之上进一步去拓展和延申,

{
    "count": 6,
    "items": [
        {
            "ID": 1,
            "NAME": "abcde"
        },
        {
            "ID": 2,
            "NAME": "bbb"
        },
        {
            "ID": 3,
            "NAME": "ccc"
        },
        {
            "ID": 5,
            "NAME": "ddd"
        },
        {
            "ID": 6,
            "NAME": "eee"
        },
        {
            "ID": 4,
            "NAME": "fff"
        }
    ]
}

        和ini文件一样,这个json文件也需要保存在h文件、cpp文件目录下面。最后,还是给出完整的cpp代码,虽然内容多了一点,但还是比较有借鉴意义的,可以耐心地去看一看、分析下。

#include <QDebug>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
#include <QMessageBox>
#include "QtWidgetsApplication.h"

QtWidgetsApplication::QtWidgetsApplication(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);

	mode_ = 1; // add
	max_number_ = 100; // maximum number is 100
	ui.radioButton1->setChecked(true); // first radio button is checked right now

	loadFile(); // load data from json file

	connect(ui.radioButton1, &QRadioButton::toggled, this, &QtWidgetsApplication::onRadioButtonToggled);
	connect(ui.radioButton2, &QRadioButton::toggled, this, &QtWidgetsApplication::onRadioButtonToggled);
	connect(ui.radioButton3, &QRadioButton::toggled, this, &QtWidgetsApplication::onRadioButtonToggled);
	connect(ui.radioButton4, &QRadioButton::toggled, this, &QtWidgetsApplication::onRadioButtonToggled);

	connect(ui.pushButton1, &QPushButton::clicked, this, &QtWidgetsApplication::onOkClicked);
	connect(ui.pushButton2, &QPushButton::clicked, this, &QtWidgetsApplication::onCancelClicked);
	connect(ui.pushButton3, &QPushButton::clicked, this, &QtWidgetsApplication::onSaveClicked);

	updateData();
}


QtWidgetsApplication::~QtWidgetsApplication()
{}

void QtWidgetsApplication::onRadioButtonToggled(bool checked) 
{
	if (checked) 
	{
		if (sender() == ui.radioButton1)
		{
			mode_ = 1;
			ui.lineEdit2->setEnabled(true);
		}
		else if (sender() == ui.radioButton2)
		{
			mode_ = 2;
			ui.lineEdit2->setEnabled(false);
		}
		else if (sender() == ui.radioButton3)
		{
			mode_ = 3;
			ui.lineEdit2->setEnabled(true);
		}
		else if (sender() == ui.radioButton4)
		{
			mode_ = 4;
			ui.lineEdit2->setEnabled(false);
		}
		else
		{
			qDebug() << "Unknown option was selected";
		}
	}
}

void QtWidgetsApplication::onOkClicked()
{
	int id;
	string name;
	int pos;
	bool conversionOK;

	switch (mode_)
	{
	case 1: //add
		if (ui.lineEdit1->text() == "")
		{
			QMessageBox::information(nullptr, "Tips", "Id is empty!");
			return;
		}

		if (ui.lineEdit2->text() == "")
		{
			QMessageBox::information(nullptr, "Tips", "Name is empty!");
			return;
		}

		if (id_array_.size() >= max_number_)
		{
			QMessageBox::information(nullptr, "Tips", "Buffer is full!");
			return;
		}

		id = ui.lineEdit1->text().toInt(&conversionOK);
		if (findDataById(id) != -1)
		{
			QMessageBox::information(nullptr, "Tips", "Id already existed!");
			return;
		}
		name = ui.lineEdit2->text().toStdString();
		
		id_array_.push_back(id);
		name_array_.push_back(name);

		QMessageBox::information(nullptr, "Tips", "Successfully add data!");
		updateData();

		ui.lineEdit1->setText("");
		ui.lineEdit2->setText("");
		break;

	case 2://del
		if (ui.lineEdit1->text() == "")
		{
			QMessageBox::information(nullptr, "Tips", "Id is empty!");
			return;
		}

		id = ui.lineEdit1->text().toInt(&conversionOK);
		pos = findDataById(id);
		if(pos == -1)
		{
			QMessageBox::information(nullptr, "Tips", "Specified Id does not existed!");
			return;
		}

		id_array_.erase(id_array_.begin() + pos);
		name_array_.erase(name_array_.begin() + pos);

		QMessageBox::information(nullptr, "Tips", "Successfully del data!");
		updateData();

		ui.lineEdit1->setText("");
		ui.lineEdit2->setText("");
		break;

	case 3:// update
		if (ui.lineEdit1->text() == "")
		{
			QMessageBox::information(nullptr, "Tips", "Id is empty!");
			return;
		}

		if (ui.lineEdit2->text() == "")
		{
			QMessageBox::information(nullptr, "Tips", "Name is empty!");
			return;
		}

		id = ui.lineEdit1->text().toInt(&conversionOK);
	    pos = findDataById(id);
		if(pos == -1)
		{
			QMessageBox::information(nullptr, "Tips", "Specified Id does not existed!");
			return;
		}

		name = ui.lineEdit2->text().toStdString();
		name_array_[pos] = name;

		QMessageBox::information(nullptr, "Tips", "Successfully update data!");
		updateData();

		ui.lineEdit1->setText("");
		ui.lineEdit2->setText("");
		break;

	case 4: // search
		if (ui.lineEdit1->text() == "")
		{
			QMessageBox::information(nullptr, "Tips", "Id is empty!");
			return;
		}

		id = ui.lineEdit1->text().toInt(&conversionOK);
		pos = findDataById(id);
		if (pos == -1)
		{
			QMessageBox::information(nullptr, "Tips", "Specified Id does not existed!");
			return;
		}

		name = name_array_[pos];
		ui.lineEdit1->setText("");
		ui.lineEdit2->setText("");

		QMessageBox::information(nullptr, "Tips", QString::fromStdString(string("Name is ") + name + string("!")));
		break;

	default:
		break;
	}
}


void QtWidgetsApplication::onCancelClicked()
{
	this->close();
}

void QtWidgetsApplication::onSaveClicked()
{
	QJsonArray itemsArray;

	// save data to itemsArray
	for (int i = 0; i < id_array_.size(); i++) {
		QJsonObject itemObject;
		itemObject["ID"] = id_array_[i];
		itemObject["NAME"] = QString::fromStdString(name_array_[i]);
		itemsArray.append(itemObject);
	}

	// create jsonObject
	QJsonObject jsonObject;
	jsonObject["count"] = itemsArray.size();
	jsonObject["items"] = itemsArray;

	// transfer to jsonDocuent
	QJsonDocument jsonDocument(jsonObject);

	// save to json file
	QFile file("data.json");
	if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
		file.write(jsonDocument.toJson());
		file.close();
		QMessageBox::information(nullptr, "Tips", "Successfully save the json file!");
	}
	else {
		qDebug() << "Failed to save JSON file";
	}
}


void QtWidgetsApplication::loadFile()
{
	QFile file("data.json");
	if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
		qDebug() << "Failed to open JSON file";
		return;
	}

	QByteArray jsonData = file.readAll();
	file.close();

	QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
	if (jsonDocument.isNull()) {
		qDebug() << "Failed to create JSON document";
		return;
	}

	QJsonObject jsonObject = jsonDocument.object();
	int num = jsonObject["count"].toInt();

	// get items
	QJsonArray itemsArray = jsonObject["items"].toArray();

	// read data from items
	for (int i = 0; i < itemsArray.size(); ++i) {
		QJsonValue itemValue = itemsArray.at(i);
		if (itemValue.isObject()) {
			QJsonObject itemObject = itemValue.toObject();

			// read data
			int id = itemObject["ID"].toInt();
			QString name = itemObject["NAME"].toString();

			id_array_.push_back(id);
			name_array_.push_back(name.toStdString());
		}
	}
}

int QtWidgetsApplication::findDataById(int id)
{
	int i;

	for (i = 0; i < id_array_.size(); i++)
	{
		if (id_array_[i] == id)
		{
			return i;
		}
	}

	return -1;
}

void QtWidgetsApplication::updateData()
{
	string s = "";
	int i;

	for (i = 0; i < id_array_.size(); i++)
	{
		s += std::to_string(id_array_[i]);
		s += " ";
		s += name_array_[i];
		s += "\n";
	}

	ui.textEdit1->setPlainText("");
	ui.textEdit1->setPlainText(QString::fromStdString(s));

}

4、测试和验证

        相比较而言,测试和验证就容易得多。首先,加载的时候,看看json数据有没有全部加载到界界面里面。其次,看下增删改查的功能是否正常。如果一切都没有问题,那就基本ok了。有问题的话,单步去调试即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1350742.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安装 Node.js、npm

安装 nodejs 安装Node.js的最简单的方法是通过软件包管理器。 Node.js官网&#xff1a;https://nodejs.org/en/download/ cd /usr/local/src/wget -c https://nodejs.org/dist/v18.16.0/node-v18.16.0-linux-x64.tar.xz xz -d node-v18.16.0-linux-x64.tar.xz tar -xf node…

探究Chrome仿真模拟设备时Click区域不准确问题

一、开发环境 windows版本&#xff1a; windows 10 Chrome 版本&#xff1a; 116.0.5845.141 二、问题描述 在Chrome DevTools中开启仿真设备&#xff08;微信开发者工具也有类似问题&#xff09;&#xff0c;如果页面元素有绑定click时&#xff0c;实际点击事件响应区域会…

如何使用Docker部署Swagger Editor结合内网穿透实现远程编辑API文档

文章目录 Swagger Editor本地接口文档公网远程访问1. 部署Swagger Editor2. Linux安装Cpolar3. 配置Swagger Editor公网地址4. 远程访问Swagger Editor5. 固定Swagger Editor公网地址 Swagger Editor本地接口文档公网远程访问 Swagger Editor是一个用于编写OpenAPI规范的开源编…

【DevOps】搭建 项目管理软件 禅道

文章目录 1、简介2、环境要求3、搭建部署环境3.1. 安装Apache服务3.2. 安装PHP环境&#xff08;以php7.0为例 &#xff09;3.3. 安装MySQL服务 4、搭建禅道4.1、下载解压4.2、 配置4.2.1、 启动4.2.2、自启动4.2.3、确认是否开机启动 5、成功安装 1、简介 禅道是国产开源项目管…

一篇文章掌握系统架构的演变和常见微服务框架

目录 前言 一、系统架构的演变 1、单体应用架构 优点&#xff1a; 缺点&#xff1a; 2、垂直应用架构 优点&#xff1a; 缺点&#xff1a; 3、分布式SOA架构 3.1 什么是SOA 3.2 SOA架构 优点&#xff1a; 缺点&#xff1a; 4、微服务架构 优点&#xff1a; 缺点…

os-ten-ta-tion

在线词源词典 这是一张展现英语发展轨迹的地图。词源不是定义&#xff0c;但词源解释了我们现在所使用的单词&#xff0c;在过去的 600 年甚至 2000 年前的意思和发音是怎样的。 正文中的日期表示该单词最早留下文献记录的年份(除非另有说明&#xff0c;一般指英文文献)。但这…

【解决】电脑上的WIFI图标不见了咋整?

相信不少同学都遇到过这种情况&#xff1a;电脑上的wifi图标莫名不见了&#xff0c;甚至有时候还是在使用的中途突然断网消失的。 遇到这种情况一般有两种解决方案&#xff1a; 1. 在开机状态下长按电源键30秒以上 这种办法应该是给主板放电&#xff0c;一般应用在wifi6上面。…

Vue3-32-路由-重定向路由

什么是重定向 路由的重定向 &#xff1a;将匹配到的路由 【替换】 为另一个路由。 redirect : 重定向的关键字。 重定向的特点 1、重定向是路由的直接替换,路由的地址是直接改变的&#xff1b; 2、在没有子路由配置的情况下&#xff0c;重定向的路由可以省略 component 属性的配…

HackTheBox - Medium - Linux - Investigation

Investigation Investigation 是一款 Linux 机器&#xff0c;难度为中等&#xff0c;它具有一个 Web 应用程序&#xff0c;可为图像文件的数字取证分析提供服务。服务器利用 ExifTool 实用程序来分析图像&#xff0c;但是&#xff0c;正在使用的版本存在命令注入漏洞&#xff…

带大家做一个,易上手的家常糖醋白菜

准备 如果是大白菜就一个 小白菜就要两个 因为白菜炒完之后会变少 将白菜叶剥开每叶分成三个小块 整个剥完之后 放入盆中清洗干净 调一个糖醋汁 一勺料酒 两勺生抽 三勺白砂糖 四勺香醋 起锅烧油 放两个干辣椒 辣椒炒一下 然后倒入白菜 翻炒直到油全部融入白菜 然后倒入…

python打开文件的方式比较

open(addr,w) 打开之后文件无论以前有什么&#xff0c;打开后都要清空 /// open(addr,r) 文件打开后&#xff0c;不删除以前内容

fineBI web组件传参

1、fineBI web组件传参 1.1、 Web组件- FineBI帮助文档 FineBI帮助文档1. 概述1.1 版本FineBI 版本HTML5移动端展现功能变动6.0--V11.0.83web组件适配移动端效果优化6.0.13-web组件支持传递参数 ${过滤组件https://help.fanruan.com/finebi/doc-view-143.html 1.2、自己做的例…

51单片机之LED灯

51单片机之LED灯 &#x1f334;前言&#xff1a;&#x1f3ee;点亮LED灯的原理&#x1f498;点亮你的第一个LED灯&#x1f498;点亮你的八个LED灯 &#x1f4cc;让LED灯闪烁的原理&#x1f3bd; LED灯的闪烁&#x1f3d3;错误示范1&#x1f3d3;正确的LED闪烁代码应该是这样&am…

太阳系三体模拟器

介绍 《三体》是刘慈欣创作的长篇科幻小说&#xff0c;文中提到的三体问题比较复杂和无解。 该项目代码就是利用 Python 来模拟三体的运行&#xff0c;此项目代码完全共享&#xff0c;欢迎下载。 我们可以自己通过调整天体的初始坐标、质量和矢量速度等等参数来自定义各种场景…

2024年01月数据库流行度最新排名

点击查看最新数据库流行度最新排名&#xff08;每月更新&#xff09; 2024年01月数据库流行度最新排名 TOP DB顶级数据库索引是通过分析在谷歌上搜索数据库名称的频率来创建的 一个数据库被搜索的次数越多&#xff0c;这个数据库就被认为越受欢迎。这是一个领先指标。原始数…

【算法每日一练]-数论 (保姆级教程 篇2 )#行列式 #甜甜花研究 #约数个数 #模数 #数树 #盒子与球

目录 今日知识点&#xff1a; 辗转相减法化下三角求行列式 组合数动态规划打表 约数个数等于质因数的次方1的乘积 求一个模数 将n个不同的球放入r个不同的盒子&#xff1a;f[i][j]f[i-1][j-1]f[i-1][j]*j 行列式 甜甜花的研究 约数个数 模数 数树 盒子与球 行列…

【刷题日志】深度理解除(/)与取模(%)附水仙花数以及变种水仙花数题解

文章目录 &#x1f680;前言&#x1f680;除与取模&#x1f680;水仙花数&#x1f680;变种水仙花数 &#x1f680;前言 本专栏文章都直奔刷题主题&#xff0c;阿辉都不会在废话了&#xff0c;加油&#xff0c;少年&#xff01;&#xff01;&#xff01; &#x1f680;除与取…

《MySQL系列-InnoDB引擎03》MySQL文件相关介绍

文章目录 第三章 文件1 参数文件1.1 什么是参数&#xff1f;1.2 参数类型 2 日志文件2.1 错误日志2.2 慢查询日志2.2x 慢查询日志拓展-如何开启MySQL慢查询日志&#xff1f;2.2XX 慢查询日志拓展-mysqldumpslow日志分析工具2.3 查询日志2.4 二进制日志 3 套接字文件4 PID文件5 …

使用ros2 launch启动gazebo时,可以设置的sdf文件名称

事实证明&#xff1a;这个文件夹里面的名称都可以设置/usr/share/gz/gz-sim8/worlds 并且不用指定文件夹路径&#xff0c;可以直接输入文件名。当然也可以完全指定路径&#xff1a;

梳理Langchain-Chatchat-UI接口文档

在 Langchain-Chatchat v0.1.17 版本及以前是有前后端分离的 Vue 项目的&#xff0c;但是 v0.2.0 后就没有了。所以本文使用的是 Langchain-Chatchat v0.1.17 版本中的 Vue 项目。经过一番折腾终于将 Langchain-Chatchat v0.1.17 版本前端 Vue 接口和 Langchain-Chatchat v0.2.…