第12章 输入与输出
QIODevice 基类
QFile
QTemporaryFile 临时文件
QBuffer 从QByteArray中读取或写入数据
QProcess 运行外部程序并处理进程间通信
QTcpSocket
QUdpSocket
QSslSocket 利用SSL/TLS在网络上传输加密数据流
后4个为顺序存储设备
前3个为随机存储设备 seek()
QDataStream 读写二进制数据
QTextStream 读写文本数据
这2个类考虑了字节顺序和文本编码等问题
比标准c++更加方便,标准c++将这些问题留给了程序员来处理。
QFile QDir QFileInfo
通过QFile打开文件
然后通过QDataStream对象存取
// 写数据
QImage image("philip.png");
QMap<QString, QColor> map;
// ...
QFile file("facts.dat");
if (!file.open(QIODevice::WriteOnly)) // file.errorString()
return;
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_3);
out << quint32(0x12345678) << image << map;
// 读数据
quint32 n;
QImage image;
QMap<QString, QColor> map;
QFile file("facts.dat");
if (!file.open(QIODevice::ReadOnly))
return;
QDataStream in(&file);
in.setVersion(QDataStream::Qt_4_3);
in >> n >> image >> map;
// in.status() 错误检查
qRegisterMetaTypeStreamOperators<T>()
// 保存时
out << quint16(out.version());
// 读取时
quint16 streamVersion;
in >> streamVersion;
if (streamVersion > in.version)
return false;
QIODevice
QFile sourceFile(source);
QFile destFile(dest);
sourceFile.open(QIODevice::ReadOnly);
dest.open(QIODevice::WriteOnly);
destFile.write(source.readAll());
压缩解压缩
qCompress()
qUncompress()
QtIOCompressor()
QTextStream
处理了字符集编码转换、不同的行尾符的转换
还支持C++基本数字类型,处理数字与字符串之间的转换
QTextStream out(&file);
out << 123 << endl;
stream.setCodec("UTF-8");
可以应用在QString上
QString str;
QTextStream(&str);
QDir 提供与平台无关的遍历目录并获得有关文件信息的方法
// 计算目录下所有图片的大小
qlonglong imageSpace(const QString& path)
{
QDir dir(path);
qlonglong size = 0;
QStringList filters;
foreach (QByteArray format, QImageReader::supportedImageFormats())
filter += "*." + format;
foreach (QString file, dir.entryList(filters, QDir::Files))
size += QFileInfo(dir, file).size();
foreach (QString subDir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
size += imageSpace(path + QDir::separator() + subDir);
return size;
}
QDir::convertSeparators() 将斜线转为针对具体平台的正确的分隔符
QDir::currentPath();
QDir::homePath();
QCoreApplication app(argc, argv);
QStringList args = QCoreApplication::arguments();
dir.entryInfoList();
dir.rename();
dir.exists();
dir.mkdir();
dir.rmdir();
QFile::remove();
QFile::exists();
QFileSystemWatcher可以通过发送directoryChanged()、fileChanged()信号,在目录或者文件发生任何改变时通知我们。
QFile可以使用嵌入资源 e.g. :/datafiles/file.dat
进程间通信
QProcess允许我们执行外部程序并且和它们进行交互,这个类时异步工作的,且它在后台完成的工作。当外部进程得到数据或者已经完成时,QProcess会发出信号通知我们。
QDir::toNativeSeparators(fileName);
QProcess process;
process.start("convert", args);
process.readAllStandardError();
QProcess信号
readyReadStandardError()
finished()
error()
QTemporaryFile临时文件
QProcss::execute()静态函数运行外部程序并等待外部进程完成。
process.waitForStarted()
process.waitForFinished()
Windows下的ActiveQt扩展程序
如果想启动用户喜欢的网页浏览器或电子邮件客户端程序,只需要:
QDesktopServices::openUrl()
第13章 数据库
1. QSqlQuery 提供了一种直接执行任意SQL语句并处理其结果的方式
2. QSqlTableModel QSqlRelationalTableModel
数据库驱动
QMYSQL
QOCI 甲骨文公司
QODBC
QSQLITE SQLite3
QSQLITE2
bool createConnection()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("mozart.konkordia.edu");
db.setDatabaseName("musicdb");
db.setUserName("gbatstone");
db.setPassword("T17aV44");
if (!db.open())
return false;
return true;
}
// 如果结果集为空或查询失败,next()第一调用将返回false
QSqlQuery query; // QSqlQuery query("...");
query.exec("select title, year from cd where year >= 1998");
while (query.next())
{
QString title = query.value(0).toString();
int year = query.value(1).toInt();
}
// 对于某些数据库,以下函数可能会比next()更慢或更耗内存
query.first();
query.last();
query.previous();
query.seek();
// 在操作大数据集时,为了便于优化调用以下函数然后只使用next()
QSqlQuery::setForwardOnly(true);
query.numRowsAffected()
Oracle风格语法
QSqlQuery query;
query.prepare("insert into cd (id, artistid, title, year)"
"values (:id, :artistid, :title, :year)");
query.bindValue(":id", 203);
// ...
query.exec();
ODBC风格语法
QSqlQuery query;
query.prepare("insert into cd (id, artistid, title, year)"
"values (?, ?, ?, ?)");
query.addBindValue(203);
// ...
query.exec();
// 事务处理
// 对于不支持事务处理的数据库,事务处理函数什么都不做
QSqlDatabase::database().transaction();
QSqlQuery query;
query.exec("...");
QSqlDatabase::database().commit(); // rollback()
// 测试数据库特征
QSqlDriver* driver = QSqlDatabase::database().driver();
driver->hasFeature(QSqlDriver::Transactions);
// 使用多个数据库连接时
// 第二个参数命名
QSqlDatabase db = QSqlDatabase::addDatabase("QMySql", "mysql");
// 查询时指定数据库
QSqlQuery query(QSqlDatabase::database("mysql"));
高级界面接口 QSqlTableModel
QSqlTableModel model;
model.setTable("cd");
model.setFilter("year >= 1998");
model.select();
for (int i=0; i<model.rowCount(); ++i)
{
QSqlRecord record = model.record(i);
QString title = record.value("title").toString();
// record.indexOf("title");
// record.value(titleIndex).toString();
}
// 插入记录
int row = 0;
model.insertRows(row, 1);
model.setData(model.index(row, 0), 113);
model.setData(model.index(row, 1), "Shanghai My Heart");
// ...
model.submitAll(); // 写入数据库,插入失败返回false
// 更新记录
record.setValue("title", "Melody A.M.");
// ...
model.setRecord(0, record);
model.submitAll();
// or
model.setData(model.index(0, 1), "Melody A.M.");
// ...
model.submitAll();
// 删除记录
model.removeRows(0, 1); // 第二个参数,记录的个数
model.submitAll();
.pro文件
QT += sql
QDataWidgetMapper
第14章 多线程
子类化QThread并且重新实现run()函数
QThread成员函数
terminate() 终止线程执行
isRunning()
wait()
线程同步
QMutex 每次只能有一个线程可以访问同一个变量
QReadWriteLock 允许执行多个读取访问而不会影响性能
QSemaphore 用于保护(guard)一定数量的相同资源
QWaitCondition 允许一个线程在满足一定的条件下触发其他多个线程
QMutex mutex;
mutex.lock();
mutex.unlock();
mutex.tryLock();
QMutexLocker 构造函数锁定,析构函数解锁
QMutexLocker locker(&mutex);
QReadWriteLock lock;
lock.lockForRead();
lock.lockForWrite();
QReadLocker
QWriteLocker
// 等价关系
QSemaphore semaphore(1); // QMutex mutex;
semaphore.acquire(); // mutex.lock();
semaphore.release(); // mutex.unlock();
semaphore信号量的一个典型应用场景是:
当两个线程间传递一定量的数据时,这两个线程会使用某一特定大小的共享唤醒缓冲器。
QWaitCondition w;
w.wait();
w.wakeAll();
线程局部存储
QThreadStorage<T>
.hasLocalData()
.setLocalData()
.localData()
与主线程通信
主线程是唯一允许创建QApplication或QCoreApplication对象,并且可以对创建的对象调用exec()的线程。
以上同步技术没有一个可以用来与主线程进行通信,因为它们会锁住事件循环并且会冻结用户界面。
使用信号和槽与主线程通信
例如:
在一个图像处理软件中,图像保存在线程对象中,在界面上选择对图像应用的变换时,主线程将包含该事务的事务对象添加到线程对象的事务列表中,线程对象后台对图像进行变换,当变换完成后通知主线程
connect(&thread, SIGNAL(transactionStarted(const QString&)),
startusBar(), SLOT(showMessage(const QString&)));
connect(&thread, SIGNAL(allTransactionsDone()),this, SLOT(allTransactionsDone()));
如果需要删除一个存在于不同线程中的QObject对象,则必须调用线程安全的QObject::deleteLater()函数,它可以置入一个延期删除事件。
事件循环
QThread::exec()
QProcess::waitForFinished()
QAbstractSocket::waitForDisconnected()
可重入类:
类的多个实例可以安全地在多个线程中访问。
QWidget和它的子类都是不可重入的,这样造成的后果之一就是我们不能在一个来自次线程的窗口部件上直接调用函数。
可以通过发送信号或者:
QMetaObject::invokeMethod(label, SLOT(setText(const QString&)),
Q_ARG(QString, "Hello"));