此篇展示了使用Qt编写多线程程序。
概述
此案例创建一Block类,用于存储数据,并且在元对象系统中注册后,在多线程中进行信号与槽函数的连接中充当参数。
Block类
在元对象系统中,注册类,需要类在public部分提供默认构造函数、拷贝构造函数、析构函数。如下所示:
class Block
{
public:
Block();
Block(const Block &other);
~Block();
Block(const QRect &rect, const QColor &color);
QColor color() const;
QRect rect() const;
private:
QRect m_rect;
QColor m_color;
};
Q_DECLARE_METATYPE(Block);
qRegisterMetaType()将类注册到元对象系统前,需要使用Q_DECLARE_METATYPE声明下新类型。
Window类
Window类的public槽函数用于接收Block对象的信号。功能包含了构造界面及图像相关处理。
class Window : public QWidget
{
Q_OBJECT
public:
Window();
void loadImage(const QImage &image);
public slots:
void addBlock(const Block &block);
private slots:
void loadImage();
void resetUi();
private:
QLabel *label;
QPixmap pixmap;
QPushButton *loadButton;
QPushButton *resetButton;
QString path;
RenderThread *thread;
};
Window类中包含了RenderThread对象,这个是工作线程,RenderThread将发射信号,参数是Block对象,在Window类中使用addBlock(Block)槽进行接收。
在Window类的构造函数中渲染了图像,设置用户界面(2个按钮+1个label)并且进行了信号与槽的连接。
Window::Window()
{
thread = new RenderThread();
label = new QLabel();
label->setAlignment(Qt::AlignCenter);
loadButton = new QPushButton(tr("&Load image..."));
resetButton = new QPushButton(tr("&Stop"));
resetButton->setEnabled(false);
connect(loadButton, SIGNAL(clicked()), this, SLOT(loadImage()));
connect(resetButton, SIGNAL(clicked()), thread, SLOT(stopProcess()));
connect(thread, SIGNAL(finished()), this, SLOT(resetUi()));
connect(thread, SIGNAL(sendBlock(Block)), this, SLOT(addBlock(Block)));
上面最后2行代码是RenderThread对象发出的信号,其中sendBlock是本例的关键。
...
setWindowTitle(tr("Queued Custom Type"));
}
上面是界面标题设置。
addBlock(Block)槽函数如下:
void Window::addBlock(const Block &block)
{
QColor color = block.color();
color.setAlpha(64);
QPainter painter;
painter.begin(&pixmap);
painter.fillRect(block.rect(), color);
painter.end();
label->setPixmap(pixmap);
}
当信号发过来,会调用到此槽函数,随后使用paint在label上画图。
RenderThread类
RenderThread类用于处理图像,创建Block对象并发送sendBlock(Block)信号。
class RenderThread : public QThread
{
Q_OBJECT
public:
RenderThread(QObject *parent = 0);
~RenderThread();
void processImage(const QImage &image);
signals:
void sendBlock(const Block &block);
public slots:
void stopProcess();
protected:
void run();
private:
bool m_abort;
QImage m_image;
QMutex mutex;
};
首先调用processImage()函数,随后这个函数调用start()方法,调用start()后,会回调到从QThread中继承的run()方法:
void RenderThread::processImage(const QImage &image)
{
if (image.isNull())
return;
m_image = image;
m_abort = false;
start();
}
void RenderThread::run()
{
int size = qMax(m_image.width()/20, m_image.height()/20);
for (int s = size; s > 0; --s) {
for (int c = 0; c < 400; ++c) {
如何处理图片的,在此案例中不用太多考虑,看此函数最后,发出了sendBlock()信号了。
...
Block block(QRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1),
QColor(red/n, green/n, blue/n));
emit sendBlock(block);
if (m_abort)
return;
msleep(10);
}
}
}
在多线程中每一个sendBlock()信号,都会被放到一个队列中,然后依次调用Window类的addBlock(Block)槽函数。
类型注册
在main()函数中使用qRegisterMetaType()函数对Block类进行注册
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qRegisterMetaType<Block>();
Window window;
window.show();
window.loadImage(createImage(256, 256));
return app.exec();
}
此类注册完成后,才能在信号与槽中,作为参数使用。