-----------------------------------
QString初始化:
-----------------------------------
QString s1 = "汉语";
QString s2("漢語");
QString s3 = tr("中文")
QString s4 = QStringLiteral("中文");//只要字符串不需要翻译,请关注这个
QString s5 = QString::fromWCharArray(L"中文");
QString s6 = u8"中文";//C++11
QString s7 = tr(u8"中文")
QStringLiteral("dbzhang800");
QLatin1String("dbzhang800");
前者占用的常量区比较多,程序体积会稍微大一点。
QLatin1String:避免操作符 "==" 中隐含的 malloc
QStringRef:没有 malloc 的字符串操作 => QString::midRef()、QString::leftRef()与QString::rightRef()
QString::reserve 与 QString::squeeze
=> 提前调用 QString::reserve 来分配额外的内存,这样每次调用 QString::append() 不会导致一个 malloc。
额外的内存可以使用 QString::squeeze 来回收。
QStringBuilder:字符串的快速连接
(1)直接方式就是包含QStringBuilder ,当你在任何地方用到它的时候。然后当连接字符串的时候,用'%'代替'+'。
#include <QStringBuilder>
QString hello("hello");
QStringRef el(&hello, 2, 3);
QLatin1String world("world");
QString message = hello % el % world % QChar('!');
(2)全局的方式,定义一个宏定义。然后用用'%'代替'+'
#define QT_USE_FAST_CONCATENATION
(3)最方便,但是并不是所有代码都会兼容。就是包含两个宏定义即可
#define QT_USE_FAST_CONCATENATION
#define QT_USE_FAST_OPERATOR_PLUS
然后所有的'+'就会被当做QStringBuilder 的'%'执行
(!)5.0,这个宏已被新的QT_USE_QSTRINGBUILDER宏所替代
-----------------------------------
QString输出,格式化及格式化输出:
-----------------------------------
(1)QTextStream
#include <QTextStream>
QTextStream cin(stdin, QIODevice::ReadOnly);
QTextStream cout(stdout, QIODevice::WriteOnly);
QTextStream cerr(stderr, QIODevice::WriteOnly);
QString str="test %1,%2";
str=str.arg("This is arg0,测试中文").arg("This is arg1");
cout<<str<<endl;
(2)qPrintable ==>相当于QString.toLocal8Bit().constData
printf("%s\n",qPrintable(str));
进一步说明:
QByteArray::constData可转为char*
QString str(" fa fkajfj ");
str=str.trimmed();==>去前后空格
-----------------------------------
QStringMatcher:字符串快速匹配
-----------------------------------
==>纯文本查找,不能含正则表达式
QStringMatcher matcher("1234");
int pos=matcher.indexIn(QLatin1String("4781234"));
-----------------------------------
File读写:
-----------------------------------
(1)文本文件:
QFile inputFile(QStringLiteral("f:/opencv/training/sample/pos-data.txt"));
inputFile.open(QIODevice::ReadOnly);
QTextStream in(&inputFile);
QString line = in.readAll();
inputFile.close();
ui->textEdit->setPlainText(line);
QTextCursor cursor = ui->textEdit->textCursor();
cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor, 1);
ui->textEdit->find(searchString, QTextDocument::FindWholeWords);
(2)二进制文件:
//read from file
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {...}
QDataStream in(&file);
in >> pairs;
if (pairs.isEmpty()) {...}
//write to file
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {...}
QDataStream out(&file);
out << pairs;
file.close();
(3)文件对话框
QString fileName = QFileDialog::getSaveFileName(this);
if (!fileName.isEmpty()){....}
--------------------
信号:
--------------------
Q_OBJECT
signals:
void finishedAnalysis();
private slots:
void handleReply(QNetworkReply*);
emit finishedAnalysis();
QObject::connect(m_network, SIGNAL(finished(QNetworkReply*)),this, SLOT(handleReply(QNetworkReply*)));//注意形参一致
--------------------
Q_PROPERTY
--------------------
Q_PROPERTY(type name READ getFunction [WRITE setFunction][RESET resetFunction][DESIGNABLE bool][SCRIPTABLE bool][STORED bool])
class Test : public QObject
{
Q_OBJECT
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
public:
Test(QObject *parent = 0) : QObject(parent) {}
virtual ~Test(){}
void setEnabled(bool e) { enabled = e; }
bool isEnabled() const { return enabled; }
private:
bool enabled;
};
int main(){
Test *test = new Test;
test->setProperty("enabled", true);//===>可用QObject::setProperty();
//test->setEnabled(true); //ok also work
if(test->property("enabled").toBool())
}
--------------------
Q_UNUSED :
--------------------
Q_UNUSED(d) ==>避免编译器报warning
--------------------
QT_BEGIN_NAMESPACE /QT_END_NAMESPACE :
--------------------
可以降低各个文件编译时的关联度,不会在改动了一下部分类的时候,引 发其他大量文件的重新编译,
在做小工程的时候没什么区别,但是做大了,编译一次需要好几个小时的时候,这样做的优势就显现出来了
.h文件:
QT_BEGIN_NAMESPACE
class QNetworkAccessManager;
class QNetworkReply;
class QNetworkDiskCache;
QT_END_NAMESPACE
.cpp文件:
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QNetworkDiskCache>
带泛型的在.h文件里也要用include,如:
QFutureWatcher<QRgb> * m_watcher;
--------------------
QList:
--------------------
QList<QImage> m_imageQueue = QList<QImage>();
m_imageQueue << img;
foreach(const QRgb & triplet, m_watcher->future().results()){} //QList遍历
m_imageQueue << img
m_imageQueue.pop_back();
//容器类的内存管理,当容器内存放的是指针时...
qDeleteAll(childItems); ==>childItems里存放的是指针,一次全部释放动态内存
--------------------
QRgb:
--------------------
rTot += qRed(triplet);
bTot += qBlue(triplet);
gTot += qGreen(triplet);
--------------------
QtConcurrent/Qt多线程编程
--------------------
#include <QtConcurrentMap>
m_watcher->setFuture(QtConcurrent::mapped(m_imageQueue, averageRGB));
QT通过三种形式提供了对线程的支持。
它们分别是,一、平台无关的线程类,二、线程安全的事件投递,三、跨线程的信号-槽连接。
(1)常用线程类:
QThread 提供了开始一个新线程的方法
QThreadStorage 提供逐线程数据存储
QMutex 提供相互排斥的锁,或互斥量
QMutexLocker 是一个便利类,它可以自动对QMutex加锁与解锁
QReadWriterLock 提供了一个可以同时读操作的锁
QReadLocker与QWriteLocker 是便利类,它自动对QReadWriteLock加锁与解锁
QSemaphore 提供了一个整型信号量,是互斥量的泛化
QWaitCondition 提供了一种方法,使得线程可以在被另外线程唤醒之前一直休眠。
Qt 高级线程类:
QtConcurrent 开启线程事务
QFutureWatcher 观测线程状态
QFuture 线程启动类
QThread创建线程
Asynchronous Run<QtConcurrentRun>
提供一个方式来运行一个 函数在一个独立的线程
Concurrent Filter and Filter-Reduce<QtConcurrentFilter>
提供同时发生的过滤器 和filter-reduce
Concurrent Map and Map-Reduce<QtConcurrentMap>
提供同时发生的Map和MapReduceQAtomicInt平台独立的原子操作在整数。
QAtomicPointer模板类,提供 平台独立的原子操作在指针上。QFuture代表一个同步的计算的结果。
QFutureSynchronizer方便类,简单化QFuture同步。
(2)线程类定义:
class MyThread : public QThread
{
Q_OBJECT
protected:
void run();
};
void MyThread::run(){...}
(3)QtConcurrent创建线程:
QtConcurrent 创建线程的方法比较多, 而且QtConcurrent 本身比较特殊,若系统有空闲线程时,它会调度空闲线程,无空闲线程时将会创建一个线程。
(注意:QtConcurrent 创建线程归QthreadPool管理,若超过最大线程数,将会进入队列等待),QtConcurrent创建线程的方法多种,以下举例map函数:
QImage scale(const QImage &image)
{
qDebug() < < "Scaling image in thread" << QThread::currentThread();
return image.scaled(QSize(100, 100), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
const int imageCount = 20;
// Create a list containing imageCount images.
QList images;
for (int i = 0; i < imageCount; ++i)
images.append(QImage(1600, 1200, QImage::Format_ARGB32_Premultiplied));
// Use QtConcurrentBlocking::mapped to apply the scale function to all the
// images in the list.
QList thumbnails = QtConcurrent::blockingMapped(images, scale);
return 0;
}
(4)Qt线程同步:
===QReadWriterLock:====
QReadWriteLock lock;
void ReaderThread::run()
{
lock.lockForRead();
read_file();
lock.unlock();
}
void WriterThread::run()
{
lock.lockForWrite();
write_file();
lock.unlock();
}
===QSemaphore:===QSemaphore 是QMutex的一般化,它可以保护一定数量的相同资源
const int DataSize = 100000;
const int BufferSize = 8192;
char buffer[BufferSize];
QSemaphore freeBytes(BufferSize);
QSemaphore usedBytes;
class Producer : public QThread
{
public:
void run();
};
void Producer::run()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
for (int i = 0; i < DataSize; ++i) {
freeBytes.acquire();
buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
usedBytes.release();
}
}
class Consumer : public QThread
{
public:
void run();
};
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
usedBytes.acquire();
fprintf(stderr, "%c", buffer[i % BufferSize]);
freeBytes.release();
}
fprintf(stderr, "\n");
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return 0;
}
===QWaitCondition:===一个或多个线程可以阻塞等待一QWaitCondition ,用wakeOne()或wakeAll()设置一个条件。wakeOne()随机唤醒一个,wakeAll()唤醒所有。
const int DataSize = 100000;
const int BufferSize = 8192;
char buffer[BufferSize];
QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;
class Producer : public QThread
{
public:
void run();
};
void Producer::run()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (numUsedBytes == BufferSize)
bufferNotFull.wait(&mutex);
mutex.unlock();
buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
mutex.lock();
++numUsedBytes;
bufferNotEmpty.wakeAll();
mutex.unlock();
}
}
class Consumer : public QThread
{
public:
void run();
};
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (numUsedBytes == 0)
bufferNotEmpty.wait(&mutex);
mutex.unlock();
fprintf(stderr, "%c", buffer[i % BufferSize]);
mutex.lock();
--numUsedBytes;
bufferNotFull.wakeAll();
mutex.unlock();
}
fprintf(stderr, "\n");
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return 0;
}
--------------------
Qt多线程解决方案
--------------------
一次调用
在另一个线程中运行一个函数,函数完成时退出线程
编写函数,使用QtConcurrent::run 运行它
派生QRunnable,使用QThreadPool::globalInstance()->start() 运行它
派生QThread,重新实现QThread::run() ,使用QThread::start() 运行它 ==>QThreadPool::globalInstance()->start(QRunable子类);
一次调用
需要操作一个容器中所有的项。使用处理器所有可用的核心。一个常见的例子是从图像列表生成缩略图。
QtConcurrent 提供了map()函你数来将操作应用到容器中的每一个元素,提供了fitler()函数来选择容器元素,以及指定reduce函数作为选项来组合剩余元素。
一次调用
一个耗时运行的操作需要放入另一个线程。在处理过程中,状态信息需要发送会GUI线程。
使用QThread,重新实现run函数并根据需要发送信号。使用信号槽的queued连接方式将信号连接到GUI线程的槽函数。
持久运行
生存在另一个线程中的对象,根据要求需要执行不同的任务。这意味着工作线程需要双向的通讯。
派生一个QObject对象并实现需要的信号和槽,将对象移动到一个运行有事件循环的线程中并通过queued方式连接的信号槽进行通讯。
持久运行
生存在另一个线程中的对象,执行诸如轮询端口等重复的任务并与GUI线程通讯。
同上,但是在工作线程中使用一个定时器来轮询。尽管如此,处理轮询的最好的解决方案是彻底避免它。有时QSocketNotifer是一个替代。
==================
Qt多线程替代方案:
QEventLoop::processEvents()
在一个耗时的计算操作中反复调用QEventLoop::processEvents() 可以防止界面的假死。尽管如此,这个方案可伸缩性并不太好,因为该函数可能会被调用地过于频繁或者不够频繁。
QTimer
后台处理操作有时可以方便地使用Timer安排在一个在未来的某一时刻执行的槽中来完成。在没有其他事件需要处理时,时间隔为0的定时器超时事件被相应
QSocketNotifier
QNetworkAccessManager
QIODevice::readyRead()
这是一个替代技术,替代有一个或多个线程在慢速网络执行阻塞读的情况。只要响应部分的计算可以快速执行,这种设计比在线程中实现的同步等待更好。与线程相比这种设计更不容易出错且更节能(energy efficient)。在许多情况下也有性能优势。
--------------------
QNetworkAccessManager:
--------------------
QNetworkAccessManager m_network = new QNetworkAccessManager(this);
m_cache = new QNetworkDiskCache(this);
m_cache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/imageanalyzer");
m_cache->setMaximumCacheSize(1000000); //set the cache to 10megs
m_network->setCache(m_cache);
QObject::connect(m_network, SIGNAL(finished(QNetworkReply*)),this, SLOT(handleReply(QNetworkReply*)));
QNetworkRequest request = QNetworkRequest(URL);
request.setRawHeader("User-Agent", "Digia - Custom Qt app");
m_network->get(request);
QNetworkDiskCache===
QUrl URL = QUrl(url);
QIODevice * pData = m_cache->data(URL);
QImage image;
image.load(pData, 0);
用webview:其中page()和setUrl()来自webview
page()->setNetworkAccessManager(m_network);
QUrl startURL = QUrl("qrc:/index.html");
setUrl(startURL);
page()->mainFrame()->addToJavaScriptWindowObject(QString("imageAnalyzer"), m_analyzer); //为web注入数据
--------------------
C++构造函数定义:
--------------------
ImageAnalyzer::ImageAnalyzer(QNetworkDiskCache* netcache, QObject* parent)
: QObject(parent), m_cache(netcache), m_outstandingFetches(0){
....
}
--------------------
QFuture/QFutureWatcher:
--------------------
QFuture类代表了一个异步调用的结果。而这个异步调用需要使用Qt Concurrent架构中的API。
如果这个结果在调用其result(), resultAt(), 或者 results()方法时还没有准备好,QFuture将会一直等到结果准备好
可以通过setPaused(),pause(), resume(), 或者 togglePaused()来停止执行。这里需要注意不是所有的异步执行都可以取消或者停止。
例如通过QtConcurrent::run()的future是不能被取消的,而通过QtConcurrent::mappedReduced()获得的future则可以被取消。
可以通过progressValue(), progressMinimum(), progressMaximum(), and progressText()方法来获得执行的进度信息。waitForFinished()方法会导致当前线程阻塞只到异步执行结束。
异步执行的状态信息可以通过isCanceled(), isStarted(), isFinished(), isRunning(), 或者 isPaused()方法来获得。
如果想通过信号槽来跟异步调用实现交互,则要使用下面的QFutureWatcher。
QFutureWatcher是为QFuture而生的。通过QFutureWatcher我们可以通过信号槽来监视一个QFuture。
通过setFuture()方法可以开始监视一个QFuture。
QFuture的很多方法我们可以直接通过QFutureWatcher来访问,如progressValue(), progressMinimum(), progressMaximum(),
progressText(), isStarted(), isFinished(), isRunning(), isCanceled(), isPaused(), waitForFinished(), result(), and resultAt().
The cancel(), setPaused(), pause(), resume(), and togglePaused(),这些方法都是以槽的形式出现的。
//构造QFutureWatcher以及连接信号槽
MyClass myObject;
QFutureWatcher<int> watcher;
connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished()));
//开始异步调用
QFuture<int> future = QtConcurrent::run(...);
watcher.setFuture(future);
--------------------
QStringList:
--------------------
QStringList m_URLQueue;
while (!m_URLQueue.isEmpty()){
QString url = m_URLQueue.takeFirst();
...
}
QStringList field = str.split("\t");
QStringList::iterator it = list.begin();
while (it != list.end()) {
myProcessing(*it);
++it;
}
--------------------
QApplication:
--------------------
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWin win;
win.show();
return app.exec();
}
--------------------
QVBoxLayout:
--------------------
mainLayout = new QVBoxLayout;
mainLayout->addWidget(descriptionLabel);
mainLayout->addWidget(addButton, 0, Qt::AlignCenter);
setLayout(mainLayout);
--------------------
QGridLayout:
--------------------
QGridLayout *gLayout = new QGridLayout;
layout2p1 -> setColumnStretch(0, 1);
layout2p1 -> setColumnStretch(1, 2); ==>第0列与第1列之比为 1:2
gLayout->addWidget(nameLabel, 0, 0);
gLayout->addWidget(nameText, 0, 1); ==>指定row,column添加widget
gLayout -> addWidget(labelList ->at(2), 0, 1, 2, 1); ==>指定行列跨度
gLayout->addWidget(addressLabel, 1, 0, Qt::AlignLeft|Qt::AlignTop);
gLayout->addWidget(addressText, 1, 1, Qt::AlignLeft); ==>指定对齐方式
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(okButton);
buttonLayout->addWidget(cancelButton);
gLayout->addLayout(buttonLayout, 2, 1, Qt::AlignRight); ==>添加其它布局
setLayout(mainLayout); ==>为主体界面设定layout
--------------------
QMenu/QAction: ==>菜单管理
--------------------
extends: QMainWindow
fileMenu = menuBar()->addMenu(tr("&File")); ==>添加菜单
openAct = new QAction(tr("&Open..."), this);
fileMenu->addAction(openAct);
connect(openAct, SIGNAL(triggered()), this, SLOT(openFile())); ==>加菜单项
toolMenu->addSeparator(); ==>菜单项分隔条
cutAct = new QAction(QIcon(":/images/cut.png"), tr("Cu&t"), this); ==>带图片菜单
removeAct->setEnabled(true);==>菜单项enable?
--------------------
QWidget:
--------------------
QString name = aDialog.nameText->text();==> QLineEdit
QString address = aDialog.addressText->toPlainText();==> QTextEdit
(1)QTabWidget: ==>extends QTabWidget
addTab(newAddressTab, "Address Book"); ==>添加名为"Address Book"的tab,newAddressTab为一个widget
insertTab(0, newAddressTab, "Address Book");
(2)QMessageBox:
QMessageBox::information(this, tr("Duplicate Name"), tr("The name \"%1\" already exists.").arg(name));
--------------------
QTableView:==>来自address book example
--------------------
//table model定义:
class TableModel : public QAbstractTableModel
{
Q_OBJECT
public:
TableModel(QObject *parent = 0);
TableModel(QList<QPair<QString, QString> > listofPairs, QObject *parent = 0);
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const; ==>定义header
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());
QList<QPair<QString, QString> > getList();
private:
QList<QPair<QString, QString> > listOfPairs; ==>用来存储table数据
};
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
switch (section) {
case 0:
return tr("Name");
case 1:
return tr("Address");
default:
return QVariant();
}
}
return QVariant();
}
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= listOfPairs.size() || index.row() < 0)
return QVariant();
if (role == Qt::DisplayRole) {
QPair<QString, QString> pair = listOfPairs.at(index.row());
if (index.column() == 0)
return pair.first;
else if (index.column() == 1)
return pair.second;
}
return QVariant();
}
bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
int row = index.row();
QPair<QString, QString> p = listOfPairs.value(row);
if (index.column() == 0)
p.first = value.toString();
else if (index.column() == 1)
p.second = value.toString();
else
return false;
listOfPairs.replace(row, p);
emit(dataChanged(index, index));
return true;
}
return false;
}
bool TableModel::insertRows(int position, int rows, const QModelIndex &index)
{
Q_UNUSED(index);
beginInsertRows(QModelIndex(), position, position + rows - 1);==>**
for (int row = 0; row < rows; ++row) {
QPair<QString, QString> pair(" ", " ");
listOfPairs.insert(position, pair);
}
endInsertRows();==>**
return true;
}
bool TableModel::removeRows(int position, int rows, const QModelIndex &index)
{
Q_UNUSED(index);
beginRemoveRows(QModelIndex(), position, position + rows - 1);==>**
for (int row = 0; row < rows; ++row) {
listOfPairs.removeAt(position);
}
endRemoveRows();==>**
return true;
}
//table view创建:
proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(table);//data model
QTableView *tableView = new QTableView;
tableView->setModel(proxyModel);
tableView->setSortingEnabled(true);//是否可排序,在header部分点击排序
tableView->setSelectionBehavior(QAbstractItemView::SelectRows);//选择模式,行选还单元格选
tableView->horizontalHeader()->setStretchLastSection(true);//最后一个column是否拉伸到全部空间
tableView->verticalHeader()->hide();//竖标题栏是否显示
tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);//编辑模式触发方式
tableView->setSelectionMode(QAbstractItemView::SingleSelection);//单选还是多选
tableView->setColumnWidth(0, 60);//列宽?
QString newStr = QString("^[%1].*").arg(str);
proxyModel->setFilterRegExp(QRegExp(newStr, Qt::CaseInsensitive));//过滤器
proxyModel->setFilterKeyColumn(0);//执行过滤的行
proxyModel->sort(0, Qt::AscendingOrder);//默认排序
//data model操作:
table->insertRows(0, 1, QModelIndex()); ==>插入一行
QModelIndex index = table->index(0, 0, QModelIndex()); ==>表格单无描述符QModelIndex
//remove entry:
QTableView *temp = static_cast<QTableView*>(currentWidget()); ==> 当前widget继承自QTableView
QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model());
QItemSelectionModel *selectionModel = temp->selectionModel();
QModelIndexList indexes = selectionModel->selectedRows();
foreach (QModelIndex index, indexes) {
int row = proxy->mapToSource(index).row(); ==>对应到原始行
table->removeRows(row, 1, QModelIndex()); ==> table为tableview之model
}
--------------------
QTableView之另一类用法:
--------------------
QTableView常用于实现数据的表格显示。下面我们如何按步骤实现学生信息表格:
一 添加表头
//准备数据模型
QStandardItemModel *student_model = new QStandardItemModel();
student_model->setHorizontalHeaderItem(0, new QStandardItem(QObject::tr("Name")));
student_model->setHorizontalHeaderItem(1, new QStandardItem(QObject::tr("NO.")));
//利用setModel()方法将数据模型与QTableView绑定
ui->student_tableview->setModel(student_model);
二 设置表格属性
//设置列宽不可变动,即不能通过鼠标拖动增加列宽
ui->student_tableview->horizontalHeader()->setResizeMode(0, QHeaderView::Fixed);
ui->student_tableview->horizontalHeader()->setResizeMode(1, QHeaderView::Fixed);
//设置表格的各列的宽度值
ui->student_tableview->setColumnWidth(0,100);
ui->student_tableview->setColumnWidth(1,100);
//默认显示行头,如果你觉得不美观的话,我们可以将隐藏
ui->student_tableview->verticalHeader()->hide();
//设置选中时为整行选中
ui->student_tableview->setSelectionBehavior(QAbstractItemView::SelectRows);
//设置表格的单元为只读属性,即不能编辑
ui->student_tableview->setEditTriggers(QAbstractItemView::NoEditTriggers);
//如果你用在QTableView中使用右键菜单,需启用该属性
ui->tstudent_tableview->setContextMenuPolicy(Qt::CustomContextMenu);
三 动态添加行
在表格中添加行时,我们只需要在model中插入数据即可,一旦model中的数据发生变化,QTabelView显示就会做相应的变动
//在第一行添加学生张三的个人信息(setItem函数的第一个参数表示行号,第二个表示列号,第三个为要显示的数据)
student_model->setItem(0, 0, new QStandardItem(“张三"));
student_model->setItem(0, 1, new QStandardItem("20120202"));
四 设置数据显示的样式
//设置单元格文本居中,张三的数据设置为居中显示
student_model->item(0, 0)->setTextAlignment(Qt::AlignCenter);
student_model->item(0, 1)->setTextAlignment(Qt::AlignCenter);
//设置单元格文本颜色,张三的数据设置为红色
student_model->item(0, 0)->setForeground(QBrush(QColor(255, 0, 0)));
student_model->item(0, 1)->setForeground(QBrush(QColor(255, 0, 0)));
--------------------
Tree之QFileSystemModel/TreeModel(from file):
--------------------
QFileSystemModel model;
model.setRootPath("");
QTreeView tree;
tree.setModel(&model);
// Demonstrating look and feel features
tree.setAnimated(false);==>展开/关闭时动画效果
tree.setIndentation(20);==>父子结点缩进
tree.setSortingEnabled(true);
tree.setWindowTitle(QObject::tr("Dir View"));
tree.resize(640, 480);
tree.show();
QFile file(":/default.txt");
file.open(QIODevice::ReadOnly);
TreeModel model(file.readAll());
file.close();
--------------------
Tree/TreeModel(from file): ==>来自editable tree model example
--------------------
view = new QTreeView(centralwidget);
view->setObjectName(QStringLiteral("view"));
view->setAlternatingRowColors(true);
view->setSelectionBehavior(QAbstractItemView::SelectItems);
view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
view->setAnimated(false);
view->setAllColumnsShowFocus(true);
QStringList headers;
headers << tr("Title") << tr("Description");
QFile file(":/default.txt");
file.open(QIODevice::ReadOnly);
TreeModel *model = new TreeModel(headers, file.readAll());
file.close();
view->setModel(model);
for (int column = 0; column < model->columnCount(); ++column) view->resizeColumnToContents(column); ==>根据列宽修正
//tree model定义:
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
TreeModel(const QStringList &headers, const QString &data,
QObject *parent = 0);
~TreeModel();
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole);
bool setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role = Qt::EditRole);
bool insertColumns(int position, int columns,
const QModelIndex &parent = QModelIndex());
bool removeColumns(int position, int columns,
const QModelIndex &parent = QModelIndex());
bool insertRows(int position, int rows,
const QModelIndex &parent = QModelIndex());
bool removeRows(int position, int rows,
const QModelIndex &parent = QModelIndex());
private:
void setupModelData(const QStringList &lines, TreeItem *parent);
TreeItem *getItem(const QModelIndex &index) const;
TreeItem *rootItem;==>内部存储结构,用TreeItem来表示
};
//由以上可知,tree model和table model一样,只关注通用、抽象层面的接口
而这些抽象通用的接口主要通过以下类/方法来描述:
QVariant、QModelIndex,然后是一些回调方法,至于这些怎么实现,框架一概不关注.
//tree model实现:
TreeModel::TreeModel(const QStringList &headers, const QString &data, QObject *parent)
: QAbstractItemModel(parent)
{
QVector<QVariant> rootData;
foreach (QString header, headers)
rootData << header;
rootItem = new TreeItem(rootData);
setupModelData(data.split(QString("\n")), rootItem);
}
int TreeModel::columnCount(const QModelIndex & /* parent */) const
{
return rootItem->columnCount();
}
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole && role != Qt::EditRole)
return QVariant();
TreeItem *item = getItem(index);
return item->data(index.column());
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
}
TreeItem *TreeModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (item)
return item;
}
return rootItem;
}
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return rootItem->data(section);
return QVariant();
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid() && parent.column() != 0)
return QModelIndex();
TreeItem *parentItem = getItem(parent);
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
bool TreeModel::insertColumns(int position, int columns, const QModelIndex &parent)
{
bool success;
beginInsertColumns(parent, position, position + columns - 1);
success = rootItem->insertColumns(position, columns);
endInsertColumns();
return success;
}
bool TreeModel::insertRows(int position, int rows, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);
bool success;
beginInsertRows(parent, position, position + rows - 1);
success = parentItem->insertChildren(position, rows, rootItem->columnCount());
endInsertRows();
return success;
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = getItem(index);
TreeItem *parentItem = childItem->parent();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->childNumber(), 0, parentItem);
}
bool TreeModel::removeColumns(int position, int columns, const QModelIndex &parent)
{
bool success;
beginRemoveColumns(parent, position, position + columns - 1);
success = rootItem->removeColumns(position, columns);
endRemoveColumns();
if (rootItem->columnCount() == 0)
removeRows(0, rowCount());
return success;
}
bool TreeModel::removeRows(int position, int rows, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);
bool success = true;
beginRemoveRows(parent, position, position + rows - 1);
success = parentItem->removeChildren(position, rows);
endRemoveRows();
return success;
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem = getItem(parent);
return parentItem->childCount();
}
bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::EditRole)
return false;
TreeItem *item = getItem(index);
bool result = item->setData(index.column(), value);
if (result)
emit dataChanged(index, index);
return result;
}
bool TreeModel::setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role)
{
if (role != Qt::EditRole || orientation != Qt::Horizontal)
return false;
bool result = rootItem->setData(section, value);
if (result)
emit headerDataChanged(orientation, section, section);
return result;
}
void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
{
QList<TreeItem*> parents;
QList<int> indentations;
parents << parent;
indentations << 0;
int number = 0;
while (number < lines.count()) {
int position = 0;
while (position < lines[number].length()) {
if (lines[number].mid(position, 1) != " ")
break;
++position;
}
QString lineData = lines[number].mid(position).trimmed();
if (!lineData.isEmpty()) {
// Read the column data from the rest of the line.
QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
QVector<QVariant> columnData;
for (int column = 0; column < columnStrings.count(); ++column)
columnData << columnStrings[column];
if (position > indentations.last()) {
// The last child of the current parent is now the new parent
// unless the current parent has no children.
if (parents.last()->childCount() > 0) {
parents << parents.last()->child(parents.last()->childCount()-1);
indentations << position;
}
} else {
while (position < indentations.last() && parents.count() > 0) {
parents.pop_back();
indentations.pop_back();
}
}
// Append a new item to the current parent's list of children.
TreeItem *parent = parents.last();
parent->insertChildren(parent->childCount(), 1, rootItem->columnCount());
for (int column = 0; column < columnData.size(); ++column)
parent->child(parent->childCount() - 1)->setData(column, columnData[column]);
}
++number;
}
}
//tree item定义:
class TreeItem
{
public:
explicit TreeItem(const QVector<QVariant> &data, TreeItem *parent = 0);
~TreeItem();
TreeItem *child(int number);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
bool insertChildren(int position, int count, int columns);
bool insertColumns(int position, int columns);
TreeItem *parent();
bool removeChildren(int position, int count);
bool removeColumns(int position, int columns);
int childNumber() const;
bool setData(int column, const QVariant &value);
private:
QList<TreeItem*> childItems;
QVector<QVariant> itemData;
TreeItem *parentItem;
};
TreeItem *TreeItem::child(int number)
{
return childItems.value(number);
}
int TreeItem::childCount() const
{
return childItems.count();
}
int TreeItem::childNumber() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
return 0;
}
QVariant TreeItem::data(int column) const
{
return itemData.value(column);
}
bool TreeItem::insertChildren(int position, int count, int columns)
{
if (position < 0 || position > childItems.size())
return false;
for (int row = 0; row < count; ++row) {
QVector<QVariant> data(columns);
TreeItem *item = new TreeItem(data, this);
childItems.insert(position, item);
}
return true;
}
bool TreeItem::insertColumns(int position, int columns)
{
if (position < 0 || position > itemData.size())
return false;
for (int column = 0; column < columns; ++column)
itemData.insert(position, QVariant());
foreach (TreeItem *child, childItems)
child->insertColumns(position, columns);
return true;
}
TreeItem *TreeItem::parent()
{
return parentItem;
}
bool TreeItem::removeChildren(int position, int count)
{
if (position < 0 || position + count > childItems.size())
return false;
for (int row = 0; row < count; ++row)
delete childItems.takeAt(position);
return true;
}
bool TreeItem::removeColumns(int position, int columns)
{
if (position < 0 || position + columns > itemData.size())
return false;
for (int column = 0; column < columns; ++column)
itemData.remove(position);
foreach (TreeItem *child, childItems)
child->removeColumns(position, columns);
return true;
}
bool TreeItem::setData(int column, const QVariant &value)
{
if (column < 0 || column >= itemData.size())
return false;
itemData[column] = value;
return true;
}
--------------------
QDialog: ==>动态显示对话框
--------------------
AddDialog aDialog;
if (aDialog.exec()) {
QString name = aDialog.nameText->text();
QString address = aDialog.addressText->toPlainText();
emit sendDetails(name, address);
}
--------------------
Qt与html/javascript互调:
--------------------
暴露c++接口给javascript:
page()->mainFrame()->addToJavaScriptWindowObject(QString("imageAnalyzer"), m_analyzer); //为web注入数据
javascript中调被暴露的接口:
imageAnalyzer.startAnalysis(stringlist);
javascript中signal/slot机制:
imageAnalyzer.finishedAnalysis.connect(this, finished);
imageAnalyzer.updateProgress.connect(this, updateProg);
demo project: webkit bridge tutorial
--------------------
QRegExp正则表达式:
--------------------
(1)提取:
http://qt-project.org/doc/qt-4.8/qregexp.html
QRegExp rx("(\\d+)");
QString str = "Offsets: 12 14 99 231 7";
QStringList list;
int pos = 0;
while ((pos = rx.indexIn(str, pos)) != -1) {
list << rx.cap(1);
pos += rx.matchedLength();
}
(2)测试:
QRegExp rx("*.txt");
rx.setPatternSyntax(QRegExp::Wildcard);//Wildcard Matching,unix文件名通配
rx.exactMatch("README.txt"); // returns true
rx.exactMatch("welcome.txt.bak"); // returns false
(3)替换:
QRegExp rx("&(?!amp;)"); // match ampersands but not &
QString line1 = "This & that";
line1.replace(rx, "&");
// line1 == "This & that"
QString line2 = "His & hers & theirs";
line2.replace(rx, "&");
// line2 == "His & hers & theirs"
(4)提取所有:
QRegExp rx("(\\d+)(\\s*)(cm|inch(es)?)");
int pos = rx.indexIn("Length: 36 inches");
QStringList list = rx.capturedTexts();
(5)正则表达式语法:
QRegExp::RegExp0A rich Perl-like pattern matching syntax. This is the default.
QRegExp::RegExp23Like RegExp, but with greedy quantifiers. This will be the default in Qt 5. (Introduced in Qt 4.2.)
QRegExp::Wildcard1This provides a simple pattern matching syntax similar to that used by shells (command interpreters) for "file globbing". See Wildcard Matching.
QRegExp::WildcardUnix4This is similar to Wildcard but with the behavior of a Unix shell. The wildcard characters can be escaped with the character "\".
QRegExp::FixedString2The pattern is a fixed string. This is equivalent to using the RegExp pattern on a string in which all metacharacters are escaped using escape().
QRegExp::W3CXmlSchema115The pattern is a regular expression as defined by the W3C XML Schema 1.1 specification.
--------------------
static_cast、dynamic_cast、reinterpret_cast和const_cast:
--------------------
(1)static_cast: ==> static_cast < type-id > ( expression )
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。
①用于类层次结构中基类和子类之间指针或引用的转换。
②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
int n=9; double d=static_cast < double > (n);
上面的例子中, 我们将一个变量从 int 转换到 double。 这些类型的二进制表达式是不同的。 要将整数 9 转换到 双精度整数
(2)dynamic_cast: ==> dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。
class A{
public:
int m_iNum;
virtual void f(){}
};
class B:public A{
};
class D:public A{
};
void foo(){
B *pb = new B;
pb->m_iNum = 100;
D *pd1 = static_cast<D *>(pb); //compile error
D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL
delete pb;
}
(3)reinterpret_cast: ==> reinterpret_cast<type-id> (expression)
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,
在把该整数转换成原类型的指针,还可以得到原先的指针值)。
int n=9;double d=reinterpret_cast<double & > (n);
这次, 结果有所不同. 在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析.
(4)const_cast: ==> const_cast<type_id> (expression)
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
--------------------
Qt Container:容器类
--------------------
template <class Key, class T> class QCache;
template <class Key, class T> class QHash;
template <class T> class QLinkedList;
template <class T> class QList;
template <class Key, class T> class QMap;
template <class Key, class T> class QMultiHash;
template <class Key, class T> class QMultiMap;
template <class T1, class T2> struct QPair;
template <class T> class QQueue;
template <class T> class QSet;
template <class T> class QStack;
template<class T, int Prealloc = 256> class QVarLengthArray;
template <class T> class QVector;
--------------------
Qt资源管理:
--------------------
Q_INIT_RESOURCE(editabletreemodel); //强制资源加载,必须存在有名为editabletreemodel.qrc的资源文件
QFile file(":/default.txt");==>访问资源文件
(1)Compiled-In Resources:编译进二进制文件
必须在应用程序的 .pro 文件中指定.qrc 文件, qmake 才能知道并将资源编译进二进制文件。例如:
RESOURCES=application.qrc
(2)外部资源:
rcc -binary myresource.qrc -o myresource.rcc ==>创建外部资源
QResource::registerResource("/path/to/myresource.rcc");