记一次开发:Qt简单电话本程序
前言
断断续续学习C++一年了,现在要做课设,觉得控制台界面实在太难看,于是用Qt做一个图形化的程序出来。
学习Qt也没有多久,只是了解了个大概,这次开发基本上是啃了2天的官方帮助文档,然后利用各种Qt提供的轮子实现的。有些地方做的确实还很不完善,不过似乎没有什么致命的bug。
代码质量底下,谨慎模仿。
Qt真的是一个很好的C++扩展库,学习完C++觉得没有用武之地的可以去学习一下Qt来开发几个图形化软件来练练手。
项目介绍
实现一个简单的电话本程序,能够实现添加、查找、修改、删除、保存到文件等基本功能
开发环境
IDE: QtCreator(我个人认为非常好用,这也是我现在在linux下编写C++的主力IDE,因为是官方的IDE所以契合度非常高)
系统:Windows(本来是想在linux下开发的,但是考虑到最后做的程序要在windows平台上展示,不想进行二次编译所以选择了windows平台,Qt是跨平台的因此这个不用过多考虑)
编程思路
其实第一反应肯定是用数据库来实现这个功能了。但是既然是C++的课设,而且考虑到数据量应该不会太大,所以就直接使用文本文档来储存和读取数据(这是一个偷懒的做法orz)
主界面,看了一下,本来是想用一个表格来展示的,调教了一天之后发现设置起来非常麻烦,于是就改用了列表控件。(其实是我上网找到了一个类似的教程我会乱说?)查询,删除,添加都可以使用列表控件自带的方法,至于保存,直接是使用文本文档输出的方式即可。
功能开发
程序主体
首先来编写程序的主窗口界面,建立一个新的Qt项目,基类选择为QMainWindow,然后编辑界面文件如下(没有美化,轻喷):
各个部件的命名如图。
接下来开始编写各个功能
添加
添加功能需要一个弹出的窗口,输入姓名和电话,然后插入到主程序的listWidget中去。因此需要再编写一个对话窗口类,向工程中添加一个设计师界面类,基类选择QDialog,命名为editDialog(这个窗口待会也可以用在修改功能中),模板选择Dialog with Buttons under(就是下面有一组按钮的对话框),然后把界面设置成这样:
两个输入框都是PlainTextEdit控件,下面那组按钮已经分别关联了这个对话框的accept()和reject()槽
然后在editDialog中添加以下几个公用函数:
QString name() const;//获取姓名
QString number() const;//获取号码
void setName(const QString &);//设置姓名
void setNumber(const QString &);//设置号码
上面两个函数用来获取编辑框中的内容,下面两个函数则用于设置编辑框中的初始内容(会在下面的修改功能中用到)
实现是这样的:
QString EditDialog::name() const
{
return ui->nameEdit->toPlainText().trimmed();
} QString EditDialog::number() const
{
return ui->numEdit->toPlainText().trimmed();
} void EditDialog::setName(const QString & name)
{
ui->nameEdit->setPlainText(name);
} void EditDialog::setNumber(const QString & num)
{
ui->numEdit->setPlainText(num);
}
然后我们来修改主窗口中对应按钮的槽(修改槽只要在控件上右键选择“转到槽”就可以快速切换到槽函数的实现了) 注意实例化EditDialog是要包含头文件的,之后新添加的类也是一样
void MainWindow::on_addButton_clicked()
{
EditDialog editDialog(this);
if(editDialog.exec()==)
{
QString line = editDialog.name()+"---"+editDialog.number();
ui->listWidget->addItem(line);
this->statusBar()->showMessage("1 item added",);//设置状态栏的提示信息
}
}
我们在这个槽里实例化一个editDialog对象,并把它的父对象设置为主窗口,利用exec()来实现模态窗口效果,通过editDialog对象内的name和number方法获取用户输入的内容并把他们组装在一起,用addItem()方法插入ListWidget就可以了。
修改
修改功能和添加功能基本是差不多的,我们直接转到对应按钮的槽里实现就可以了
void MainWindow::on_editButton_clicked()
{
if(!ui->listWidget->currentItem())
return;
QStringList parts = ui->listWidget->currentItem()->text().split("---");
EditDialog editDialog(this);
editDialog.setName(parts[]);
editDialog.setNumber(parts[]); if(editDialog.exec()==)
{
ui->listWidget->currentItem()->setText(editDialog.name()+"---"+editDialog.number());
this->statusBar()->showMessage("1 item updated",);
}
}
这里用到了QStringList类和QString的split()方法,其实并不难,看一下这个实例就很容易理解,官方的文档也写的很详细
删除
删除功能其实直接利用了QListWidget提供的返回当前选择行的currentItem()函数和delete方法,但是为了防止误操作,需要一个模态的确认对话框,再向工程中添加一个设计师界面类,仍然选择是有两个按钮的对话框,对话框的界面只需要放一个label写上“确定删除?”即可。
这个对话框类我命名为ConfirmDialog,主窗口对应按钮的槽函数如下:
void MainWindow::on_delButton_clicked()
{
if(!ui->listWidget->currentItem())
return; ConfirmDialog * confirmDialog = new ConfirmDialog(this);
if(confirmDialog->exec()==)
{
delete ui->listWidget->currentItem();
this->statusBar()->showMessage("1 item deleted",);
}
}
查找
这个是最难写的一个功能,还是先做查询窗口,这次我选择了没有按钮的对话框类,命名为QueryDialog,界面如下:
下面的两个按钮:“查询”设置为queryButton,这个槽稍后将会和主窗口里的槽关联,“退出”则直接和对话框的reject()槽关联了。
另外,为了能够把queryButton跟主窗口的槽关联起来,就必须把他设置为公有的,于是我把头文件中,ui改成了public的:
public:
Ui::QueryDialog *ui;
这其实不是一个很好的办法,破坏了面向对象的封装性,但是我懒啊。。于是就这么干了。
查找功能的思路是,用户在查找对话框中输入关键字,然后通过查询按钮发送的信号把关键字传送给主窗口里的槽,这个槽函数调用listWidget的findItem()方法,然后把结果(一个基类性为 QListWidgetItem *
的Qlist)发送给查询窗口来显示。
QueryDialog里增加一个函数来返回编辑框中的内容
QString QueryDialog::getTarget()
{
return ui->QueryEdit->toPlainText();
}
在MainWindow类里添加一个私有的QueryDialog对象qdlg(这么做是为了实现两个窗口的通信,以及槽的关联)
主窗口“查找”按钮的槽直接写成以模态窗口显示qdlg即可:
void MainWindow::on_queryButton_clicked()
{
qdlg.exec();
}
主窗口里的槽函数,写在public slots下:
void MainWindow::SendQueryResult()
{
QString target = qdlg.getTarget();
QList <QListWidgetItem * > resList = ui->listWidget->findItems(target,Qt::MatchContains);//这个函数第二个参数是匹配方法,我这里设置成包含字段就匹配
qdlg.ShowQueryResult(resList);//这个是QueryDialog里面用于处理显示结果数据的,接下来会写实现
qdlg.exec();
}
接着,在MainWindow类的构造函数中,把这个槽和它的成员对象qdlg的查询按钮的信号关联起来:
connect(qdlg.ui->queryButton,SIGNAL(clicked()),this,SLOT(SendQueryResult()));
然后是QueryDialog里面,用于接收到数据并显示的函数ShowQueryResult
:
void QueryDialog::ShowQueryResult(const QList <QListWidgetItem * > & resList)
{
if(resList.size()==)
{
ui->resLabel->setText("No results!");
return;
}
else
{
QString resNumber = QString::number(resList.size());//注意这里的字符串处理 ui->resLabel->setText(resNumber+" item(s) founded:"); for(int i=;i<resList.size();i++)
{
ui->listWidget->addItem(resList[i]->text());
}
}
}
在这里说明一下,我本来希望能够直接利用addItem函数重载的QListWidgetItem * 参数来直接读取List里的记录,但是这样做却发现不能显示内容(原因我至今不明,望高人指点),于是我用了QListWidgetItem的text()方法来获取字符串添加进去。
这样就基本实现了查询功能
保存
其实我一开始是想做成动态保存的结果的(对listWidget的操作直接影响保存文件),但是查询略麻烦,所以改成了每点击一下保存按钮就重新写入一次文档(还是偷懒23333)。
要实现保存,就需要一个外部的txt文档(我这里命名为data.txt)
在主窗口类里添加一个私有的QFile对象file,用它来读写data.txt
接下来实现,打开程序时自动加载之前保存内容。其实很简单,直接在构造函数里添加读取txt并写入listWidget就可以
file.setFileName("data.txt");
//从文件读取
if(file.open(QIODevice::ReadOnly|QIODevice::Text))//注意这里的参数
{
QTextStream readin(&file);
while(!readin.atEnd())
{
QString line = readin.readLine();//一行一行读取
ui->listWidget->addItem(line);
}
file.close();
}
接下来实现保存按钮的槽
void MainWindow::on_saveButton_clicked()
{
//功能:把当前程序中的所有记录保存到data.txt
if(file.open(QIODevice::WriteOnly))
{
QTextStream out(&file);
for(int i=;i<ui->listWidget->count();i++)
{
out<<ui->listWidget->item(i)->text()<<endl;
}
file.close();
this->statusBar()->showMessage("File saved",);
}
else
{
this->statusBar()->showMessage("Save failed!",);
return;
}
}
看一下帮助文档中QFile和QTextStream的用法,上面的程序应该不难理解。
至此我们的程序就写完了!快快发布release吧~
源码
请查看我的github:
github.com/MarkLux/Qt-PhoneBook
记一次开发:Qt简单电话本程序的更多相关文章
- 使用Go开发一个简单的服务器程序
最近有个小项目,需要一个简单的后台程序来支撑,本来想用Nodejs来做,但是由于本人js一直很菜,并且很讨厌callback,虽然我也很喜欢异步模型,但我一直都觉得JS是反人类的.后台就用了go处理, ...
- 【Flask系列】开发一个简单的Flask程序
知识点 初始化:每一个flask程序都必须创建一个程序实例,遵循WSGI(Web Server Gateway interface)协议,把请求->flask Obj; 创建实例: app = ...
- Cocos2d-x-Lua 开发一个简单的游戏(记数字步进白色块状)
Cocos2d-x-Lua 开发一个简单的游戏(记数字步进白色块状) 本篇博客来给大家介绍怎样使用Lua这门语言来开发一个简单的小游戏-记数字踩白块. 游戏的流程是这种:在界面上生成5个数1~5字并显 ...
- 简单记账本APP开发一
在对Android的一些基础的知识有了一定了解,以及对于AndroidStudio的如何使用有了 一定的熟悉后,决定做一个简单的记账本APP 开发流程 1.记账本的页面 2.可以添加新的账目 (一)页 ...
- Android-->发送短信页面实现(短信发送以及群发和从电话本中选择联系人)-----------》2
分析下怎么写 首先,我们需要一个输入框,可以手动的输入手机号码, 其次,很少有人愿意手动输入,那么我们需要提供一个按钮来给我们的用户选择自己电话本中的联系人(一次可以选择多个即群发) 然后,我们需要一 ...
- 如何开发一个简单的HTML5 Canvas 小游戏
原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...
- Objective C (iOS) for Qt C++ Developers(iOS开发,Qt开发人员需要了解什么?)
Qt/C++开发人员眼中的Obj-C 对于我们第一次自己定义iOS应用来说,对于来自Qt/C++开发人员来说,我不得不学习Objective-C相关语法与知识 为了让读者可以更easy理解这 ...
- 用SpringBoot搭建简单电商项目 01
前几节呢,我们已经简单介绍了SpringBoot框架的使用,从这一节开始,我们尝试着使用SpringBoot框架来一步一步搭建一个简单电商项目.当然了,这不是真正的电商项目,你可以看成是一个CRUD案 ...
- java模拟而一个电话本操作
哈哈.大家平时都在使用电话本.以下使用java来模拟而一个简单的电话本吧... 首先给出联系人的抽象类 package net.itaem.po; /** * * 电话人的信息 * */ public ...
随机推荐
- JavaScrip——简单练习(抓错误信息,for循环,日期)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- linux网口驱动实现(待续)
驱动位于linux/driver/net下. 1. 寄存器读写 asm/io.h #define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile un ...
- Aria2 Axel wget curl 四个下载命令
经常由于不同需求使用下载管理器从互联网下载文件,它给我和其他人提供了很多帮助.我们都想要一个超级快速的下载管理器来完成下载尽可能多的任务,以便我们可以节省时间来进一步地工作.有很多可以加速下载的下载管 ...
- cause: java.lang.IllegalStateException: Serialized class com.taotao.pojo.TbItem must implement java.io.Serializable
HTTP Status 500 - Request processing failed; nested exception is com.alibaba.dubbo.rpc.RpcException: ...
- kettle启动时候报a fatal exception has occurred
本人刚接触ETL工具 Data Integration - Kettle ,下载了kettle6.0版本,但是在window +jdk1.7(32位)下启动报错, 使用SpoonDebug.bat写的 ...
- UML---UML中的几种关系(依赖,关联,泛化,实现)
关于设计模式的总结没有落实到代码上,而且设计模式自己确实动手实现的非常少.所以在这一周里,除了看网站开发的视频,着手开始对设计模式进行实现以下.设计模式非常经典,每次看都有不同的收获,写一下自己的收获 ...
- C# 在Bitmap上绘制文字出现锯齿的问题
解决锯齿问题主要是修改Graphics的属性 修复绘制图片锯齿问题可以修改 g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiA ...
- Spring 4 官方文档学习(十一)Web MVC 框架之resolving views 解析视图
接前面的Spring 4 官方文档学习(十一)Web MVC 框架,那篇太长,故另起一篇. 针对web应用的所有的MVC框架,都会提供一种呈现views的方式.Spring提供了view resolv ...
- 使用ffmepg的lib库调试,debug版本下调试无问题,但release版本会出现跑飞的现象
如题(“使用ffmepg的lib库调试,debug版本下调试无问题,但release版本会出现跑飞的现象”). 今天使用ffmpeg进行宿放和颜色格式转换,很简单的代码,却折腾了我一天,这里说来就气啊 ...
- nodejs基础 -- EventEmitter
var events = require('events'); nodejs所有的异步I/O操作在完成时都会发送一个事件到事件队列 nodejs里面的许多对象都会分发事件,如: 一个net.Serve ...