在多线程环境下,例如存在线程A与线程B,且A,B线程都需要调用数据库,线程A为主线程,表的创建以及数据库的移除均出现在线程A中。
所需要的头文件
//数据库
#include<QSqlDatabase>
#include<QSqlError>
#include<QSqlQuery>
//线程锁
#include<QMutex>
所有线程中,所有关于数据库变量的设置,都在cpp源文件的初始化中进行 。假设线程A的初始化先于线程B
线程A
连接数据库:
在线程A,线程A.cpp的构造函数中,进行数据库相关内容的设置:
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL","a");
db.setHostName("localhost");//数据库服务器IP
db.setUserName("root");
db.setPassword("123456789"); //这里输入你的密码
db.setDatabaseName("test");//这里输入你的数据库名
bool flagdb=db.open();
if(flagdb==false)
{
QMessageBox::critical(NULL, QObject::tr("无法打开数据库"),"无法创建数据库连接! ", QMessageBox::Cancel);
return;
}
创建表:
需要在线程A.h头文件中,定义QSQLQuery的对象query,因为线程A.cpp中多个自定义函数需要调用数据库,保证query的作用域为整个A.cpp。
线程A的.h中
QSqlQuery query;
QString insert="insert into acmmdatabase "
"(id,enfrist,ensecond,enthree,enfour,enfive,ensix) "
"values(:id,:enfrist,:ensecond,:enthree,:enfour,:enfive,:ensix);";
之后紧接着线程A.cpp的初始化程序,创建表
query = QSqlQuery ("",db);
bool flagquery=query.exec("create table acmmdatabase(id int primary key,enfrist double,ensecond double,enthree double,enfour double,enfive double,ensix double)");
if(flagquery==false)// 一旦路径错误,就会出错,提示工作人员去修改路径
{
// qDebug()<<"数据库的表有问题!";
bool clearfalg = query.exec("DROP table acmmdatabase");//删除是使用DROP
if(clearfalg==false)
{
qDebug()<< query.lastError();
}
else
{
qDebug() << "delete table!";
}
bool flagqueryagain=query.exec("create table acmmdatabase(id int primary key,enfrist double,ensecond double,enthree double,enfour double,enfive double,ensix double)");
if(flagqueryagain==false)
{
int chose = QMessageBox::warning(NULL,"warning","数据库创建表失败,请联系工作人员!",QMessageBox::Yes);
if(chose==QMessageBox::Yes)
return;
}
}
之后在线程A.cpp的其他自定义函数中,调用数据库,并进行操作即可,比如插入,清空,删除一项等。
因为此前query是全局变量,所以在整个线程A.cpp均可调用query进行数据库的相关操作
数据插入:
- 数据插入(示例,最后会放上整个程序段)
void Widget::tableoperation()
{
/*************数据库调用*************/
mutex.lock();//保持原子性
int oi=0;
bool insertfalg =query.prepare(insert);//插入操作
if(insertfalg == false)//判断操作是否成功
{
qDebug()<<query.lastError();
}
else
{
qDebug()<<"insert succeed!";
}
query.bindValue(":id",num+1);
query.bindValue(":enfrist",dcom->oriangle[oi]);
query.bindValue(":ensecond",dcom->oriangle[oi+1]);
query.bindValue(":enthree",dcom->oriangle[oi+2]);
query.bindValue(":enfour",dcom->oriangle[oi+3]);
query.bindValue(":enfive",dcom->oriangle[oi+4]);
query.bindValue(":ensix",dcom->oriangle[oi+5]);
if(query.exec()==false)//判断赋值是否成功
{
qDebug()<<query.lastError();
}
else
{
qDebug()<<"insert data succeed!";
}
num++;
mutex.unlock();//保持原子性
}
线程B:
连接数据库 :
在线程B.cpp中,连接数据库
QSqlDatabase db2 = QSqlDatabase::addDatabase("QMYSQL","b");
db2.setHostName("localhost");//数据库服务器IP
db2.setUserName("root");
db2.setPassword("123456789"); //这里输入你的密码
db2.setDatabaseName("test");//这里输入你的数据库名
bool flagdb = db2.open();
if(flagdb == false)
{
QMessageBox::critical(NULL, QObject::tr("无法打开数据库"),"无法创建数据库连接! ", QMessageBox::Cancel);
return;
}
imquery =QSqlQuery("",db2);
同理,需要在线程B.h中,定义QSQLQuery的对象imquery,因为线程B.cpp中多个自定义函数需要调用数据库,保证query的作用域为整个B.cpp。
QSqlQuery imquery;
查询(检测A线程中插入的数据是否存在):
之后在线程B.cpp进行调用数据库的操作,如查询。
bool fflag = imquery.exec("select enfrist from acmmdatabase");//只进行enfrist的查询
if(fflag==false)
{
qDebug() <<"Error: Fail to enfrist create table." << imquery.lastError();
}
else
{
while(imquery.next())
{
//数据处理的示例,仅供参考
x=52*sin( imquery.value("enfrist").toDouble()*pi )+65;
y=52*cos(imquery.value("enfrist").toDouble()*pi )+85;
p.drawLine(65,85,x,y);
}
}
注意:使用select查询语句时,切记要注意,imquery.next必须在imquery.value之前,否则没有数据。
移除数据库:
最后,当我们需要关闭程序时,要对数据库的表进行删除,并对数据库进行removeDatabase
如前文所说,线程A.cpp才是主线程,所以表的删除及removeDatabase需要在线程A中完成。
void Widget::closeEvent(QCloseEvent *event)
{
qDebug()<<"进入退出处理程序";
int ret = QMessageBox::question(this,"question","是否需要关闭窗口",QMessageBox::Yes,QMessageBox::No);
if(ret == QMessageBox::Yes)
{
event->accept();
stopimage();//关闭线程B
QSqlDatabase::removeDatabase("b");//移除数据库b,及线程B中使用的数据库
QSqlDatabase::removeDatabase("a");//移除数据库a,及线程B中使用的数据库
/*MySQL*/
qDebug()<<"数据库已经完成关闭和移除";
/*MySQL*/
}
else if(ret == QMessageBox::No)
{
event->ignore();
}
}
附录(完整代码):
- 补充:整个程序段的展示(部分数据处理的内容未展示)
- 线程A.h
//数据库
#include<QSqlDatabase>
#include<QSqlError>
#include<QSqlQuery>
//线程锁
#include<QMutex>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
protected:
void closeEvent(QCloseEvent *event);
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void progress();
int prnumb;//进度条读书
//数据库
QSqlQuery query;
QString insert="insert into acmmdatabase "
"(id,enfrist,ensecond,enthree,enfour,enfive,ensix) "
"values(:id,:enfrist,:ensecond,:enthree,:enfour,:enfive,:ensix);";
private slots:
void on_save_clicked();
void on_deletedata_clicked();
void on_clear_clicked();
private:
Ui::Widget *ui;
imagethread *imT;
QThread *imgthread;
QProgressDialog *progressindex;
};
#endif // WIDGET_H
- 线程A.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
/*********************************线程B*********************************************/
imT = new imagethread;//分配空间
imgthread = new QThread(this);//指定父对象,子线程
imT->moveToThread(imgthread);//自定义线程加入子线程
//启动image子线程
imgthread->start();
//启动线程
/*********************************线程B*********************************************/
/***********************************数据库Mysql*****************************************************/
mutex.lock();
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL","a");
db.setHostName("localhost");//数据库服务器IP
db.setUserName("root");
db.setPassword("2112414cjm"); //这里输入你的密码
db.setDatabaseName("test");//这里输入你的数据库名
bool flagdb=db.open();
if (flagdb==false)
{
QMessageBox::critical(NULL, QObject::tr("无法打开数据库"),"无法创建数据库连接! ", QMessageBox::Cancel);
return;
}
mutex.unlock();
query = QSqlQuery ("",db);//如果是全局变量,需要制定
bool flagquery=query.exec("create table acmmdatabase(id int primary key,enfrist double,ensecond double,enthree double,enfour double,enfive double,ensix double)");
if(flagquery==false)// 一旦路径错误,就会出错,提示工作人员去修改路径
{
// qDebug()<<"数据库的表有问题!";
bool clearfalg = query.exec("DROP table acmmdatabase");//删除是使用DROP
if(clearfalg==false)
{
qDebug()<< query.lastError();
}
else
{
qDebug() << "delete table!";
}
bool flagqueryagain=query.exec("create table acmmdatabase(id int primary key,enfrist double,ensecond double,enthree double,enfour double,enfive double,ensix double)");
if(flagqueryagain==false)
{
int chose = QMessageBox::warning(NULL,"warning","数据库创建表失败,请联系工作人员!",QMessageBox::Yes);
if(chose==QMessageBox::Yes)
return;
}
}
/***********************************数据库Mysql*****************************************************/
}
Widget::~Widget()
{
delete ui;
}
//前界面表格的具体内容
void Widget::tableoperation()
{
/*******
数据处理....
********/
/*************数据库调用*************/
mutex.lock();
//摁下之后会进行操作
int oi=0;
bool insertfalg =query.prepare(insert);//插入操作
if(insertfalg == false)
{
qDebug()<<query.lastError();
}
else
{
qDebug()<<"insert succeed!";
}
query.bindValue(":id",num+1);
query.bindValue(":enfrist",dcom->oriangle[oi]);
query.bindValue(":ensecond",dcom->oriangle[oi+1]);
query.bindValue(":enthree",dcom->oriangle[oi+2]);
query.bindValue(":enfour",dcom->oriangle[oi+3]);
query.bindValue(":enfive",dcom->oriangle[oi+4]);
query.bindValue(":ensix",dcom->oriangle[oi+5]);
// qDebug()<<num+1;
//更改之后,可以连续赋值
if( query.exec()==false)//判断赋值是否成功
{
qDebug()<<query.lastError();
}
else
{
qDebug()<<"insert data succeed!";
}
mutex.unlock();
/*******
数据处理....
********/
/**************数据库调用************/
}
/*******
数据处理....
********/
}
//删除某行
void Widget::on_deletedata_clicked()
{
/*******
数据处理....
********/
bool onefalg = query.exec("DELETE FROM acmmdatabase WHERE id = ?");//ok
if(onefalg==false)
{
qDebug()<<"delete one`s 指令错误"<<query.lastError();
}
else
{
qDebug()<<"deleted,one!指令正确";
}
query.addBindValue(numcow+1);
if(query.exec()==false)
{
qDebug()<<"delete one`s error"<<query.lastError();
}
else
{
qDebug()<<"deleted,one!";
}
/*******
数据处理....
********/
}
//表格全部清空
void Widget::on_clear_clicked()
{
/*******
数据处理....
********/
qDebug()<<"准备清理数据库";
mutex.lock();
bool clearfalg = query.exec("DELETE FROM acmmdatabase");//
if(clearfalg==false)
{
qDebug()<< query.lastError();
}
else
{
qDebug() << "delete,all!";
}
mutex.unlock();
/*******
数据处理....
********/
}
void Widget::progress()//进度条
{
prnumb=0;//进度条初始化
// qDebug()<<"进度条";
progressindex = new QProgressDialog(this);
progressindex->setMinimum(0);//最小值
progressindex->setMaximum(table->rowCount());//最大值
progressindex->reset();//让进度条重新回到开始
progressindex->setWindowModality(Qt::WindowModal);
progressindex->setMinimumDuration(5);
progressindex->setWindowTitle(tr("plese waiting"));
progressindex->setLabelText("Copying...");
progressindex->setCancelButtonText(tr("Cancel"));
progressindex->setRange(0,table->rowCount());
progressindex->autoReset();//自动关闭
}
void Widget::progressValue()//进度条赋值
{
prnumb++;
progressindex->setValue(prnumb);
}
//关闭窗口
void Widget::closeEvent(QCloseEvent *event)
{
qDebug()<<"进入退出处理程序";
int ret = QMessageBox::question(this,"question","是否需要关闭窗口",QMessageBox::Yes,QMessageBox::No);
//摁键一定要设置,否则会报错
if(ret == QMessageBox::Yes)
{
event->accept();
stopimage();//关闭线程image
QSqlDatabase::removeDatabase("b");
QSqlDatabase::removeDatabase("a");
/*MySQL*/
qDebug()<<"数据库已经完成关闭和移除";
/*MySQL*/
}
else if(ret == QMessageBox::No)
{
event->ignore();
}
}
//关闭线程B
void Widget::stopimage()//退出崩溃就是你
{
// qDebug()<<"进入线程关闭函数";
if(imgthread->isRunning() == true)
{
imgthread->quit();
imgthread->wait();
}
if(imT!=NULL)//imT不为空,删除,让他变成空的
{
delete imT;
}
}
void Widget::on_save_clicked()
{
filepath= QFileDialog::getSaveFileName(this, "保存","../","Excel 文件(*.xls)");
for(int i=0;i<7;i++)
{
hHeaderItem[i]=table->horizontalHeaderItem(i)->text(); //读取表头
}
throwcount = table->rowCount();//行数
thcolcount = table->columnCount();
//
if (filepath!="")
{
QAxObject *excel ;
excel = new QAxObject;//建立excel操作对象
if (excel->setControl("Excel.Application")) //连接Excel控件
{
qDebug()<<"进入程序";
excel->dynamicCall("SetVisible (bool Visible)","false");//不显示窗体
excel->setProperty("DisplayAlerts", false);
//不显示任何警告信息。如果为true那么在关闭是会出现类似“文件已修改,是否保存”的提示
QAxObject *workbooks = excel->querySubObject("WorkBooks");//获取工作簿集合
workbooks->dynamicCall("Add");//新建一个工作簿
QAxObject *workbook = excel->querySubObject("ActiveWorkBook");//获取当前工作簿
QAxObject *worksheet = workbook->querySubObject("Worksheets(int)", 1);
//访问excel中的工作表中第一个单元格
//保存到execl,总的来说就是操作单元格,赋值的赋值,改格式大的改格式,遵循一点,修改颜色,行高,字体这些属性,务必是要设定一个修改范围再动手
int i,j,colcount,rowcount;//表格的列数
colcount=thcolcount,rowcount=throwcount;
QAxObject *cell;
QAxObject *range;//= worksheet->querySubObject("Range(const QString&)", cellTitle);
for(i=0;i<colcount;i++)
{
//读取
QString columnName;
//修改内容
cell=worksheet->querySubObject("Cells(int,int)", 1, i+1);//确定操作单元格,将数据保存到哪儿,行的表头,所以要从第二列开始
columnName=hHeaderItem[i];//获取此处的文本内容,i是列号,就是第几列中的文本内容
cell->dynamicCall("SetValue(const QString&)", columnName);//打印到excel
//前半句是获取单元格字体,后半句是字体加粗
cell->querySubObject("Font")->setProperty("Bold", true);
cell->querySubObject("Interior")->setProperty("Color",QColor(191, 191, 191));
cell->setProperty("HorizontalAlignment", -4108);//xlCenter
cell->setProperty("VerticalAlignment", -4108);//xlCenter
}
//数据区
//qDebug()<<"行数"<<rowcount<<"列数"<<colcount;//列数不对
progress();
QString rowdata[7];
if(rowcount!=0)
{
// mutex.lock();
for(int datai=0;datai<rowcount;datai++)
{
mutex.lock();
bool chaxun = query.exec(tr("select * from acmmdatabase where id = %1 ").arg(datai+1));//是从1开始的
if(chaxun==false)
{
qDebug()<<query.lastError();
}
else
qDebug()<<"succeed";
if(query.next())//必须要有next,输出,必须是string
{
// (dcom->oriangle[numj]/180)*3.14159265359
double test[7];
test[0]=query.value("id").toDouble();
test[1]=((query.value("enfrist").toDouble())/anglepi)*piw;
test[2]=((query.value("ensecond").toDouble())/anglepi)*piw;
test[3]=((query.value("enthree").toDouble())/anglepi)*piw;
test[4]=((query.value("enfour").toDouble())/anglepi)*piw;
test[5]=((query.value("enfive").toDouble())/anglepi)*piw;
test[6]=((query.value("ensix").toDouble())/anglepi)*piw;
rowdata[0]=QString::number(test[0],10,6);
rowdata[1]=QString::number(test[1],10,6);
rowdata[2]=QString::number(test[2],10,6);
rowdata[3]=QString::number(test[3],10,6);
rowdata[4]=QString::number(test[4],10,6);
rowdata[5]=QString::number(test[5],10,6);
rowdata[6]=QString::number(test[6],10,6);
}
for (int dataj=0;dataj<colcount;dataj++)
{
//在此处,直接从数据库里面读取数据,不要从前界面读取内容了,实在是太麻烦了,而且容易崩溃,毕竟你的链表没有分陪空间
// rowdata[dataj]=printdata[datai][dataj];
worksheet->querySubObject("Cells(int,int)", datai+2, dataj+1)->dynamicCall("SetValue(const QString&)",rowdata[dataj]);
}
mutex.unlock();
//进度条
progressValue();
}
//画框线
QString lrange;
lrange.append("A2:");//起始行列位置(原点),修改后,开始的位置会变化,A2的从A2那一行开始,A3就是第三行
lrange.append(colcount-1 + 'A');//终止列,-1,就是正常状态,改变colcount后面的
lrange.append(QString::number(rowcount +1));//终止行
//这个起始的位置,相当于原点,最终给一个行列的终止位置就行
range = worksheet->querySubObject("Range(const QString&)", lrange);
//querySubObject("Borders")是对边框的设置,必须有
range->querySubObject("Borders")->setProperty("LineStyle", QString::number(1));
range->querySubObject("Borders")->setProperty("Color", QColor(0, 0, 0));//颜色的设置
//调整数据区行高
QString rowsName;
rowsName.append("A2:");//起始行列位置
rowsName.append(colcount-1 + 'A');//终止列
//上面两句也可以变成
//rowsName.append("2:");//起始列
rowsName.append(QString::number(rowcount + 1));//终止行
range = worksheet->querySubObject("Range(const QString&)", rowsName);
range->setProperty("RowHeight", 20);//设置行高
range->setProperty("ColumnWidth", 20); //设置单元格列宽
}
workbook->dynamicCall("SaveAs(const QString&)",QDir::toNativeSeparators(filepath));
//qDebug()<<"chenggong";
workbook->dynamicCall("Close()");//关闭工作簿
excel->dynamicCall("Quit()");//关闭excel
delete excel;
QMessageBox::warning(this,tr("save"),tr("导出成功"),QMessageBox::Yes);
}
else
{
QMessageBox::warning(this,"错误","未能创建 Excel 对象,请安装 Microsoft Excel。",QMessageBox::Apply);
}
}
qDebug()<<"线程一次完成";
}
- 线程B.h
#ifndef IMAGETHREAD_H
#define IMAGETHREAD_H
//数据库
#include<QSqlDatabase>
#include<QSqlError>
#include<QSqlQuery>
#include<QMessageBox>
//线程锁
#include<QMutex>
class imagethread : public QObject
{
Q_OBJECT
public:
explicit imagethread(QObject *parent = nullptr);
QSqlDatabase db2;//此处为全局变量,也可为局部变量,根据工程需求而定
QSqlQuery imquery;
};
#endif // IMAGETHREAD_H
- 线程B
#include "imagethread.h"
imagethread::imagethread(QObject *parent) :
QObject(parent)
{
/*********
数据处理
*********/
}
void imagethread::drawImage()
{
if(newflag==true)
{
db2 = QSqlDatabase::addDatabase("QMYSQL","b");
db2.setHostName("localhost");//数据库服务器IP
db2.setUserName("root");
db2.setPassword("2112414cjm"); //这里输入你的密码
db2.setDatabaseName("test");//这里输入你的数据库名
bool flagdb=db2.open();
if (flagdb==false)
{
QMessageBox::critical(NULL, QObject::tr("无法打开数据库"),"无法创建数据库连接! ", QMessageBox::Cancel);
return;
}
imquery =QSqlQuery("",db2);
newflag=false;
}
/*********************************MySql****************************************/
if(competeleflag==true)
{
competeleflag=false;
mutex.lock();
bool fflag = imquery.exec("select enfrist from acmmdatabase");//只进行enfrist的查询
if(fflag==false)
{
qDebug() <<"Error: Fail to enfrist create table." << imquery.lastError();
}
else
{
while(imquery.next())
{
x=52*sin( imquery.value("enfrist").toDouble()*pi )+65;
y=52*cos(imquery.value("enfrist").toDouble()*pi )+85;
p.drawLine(65,85,x,y);
}
}
/*********
数据处理
*********/
mutex.unlock();
}
}
写程序时务必不要忘记,保证程序段的原子性。