一、信号槽的基本概念
通过信号槽机制,QT使对象间的通信变得非常简单:A对象声明信号(signal),B对象实现与之参数相匹配的槽(slot),通过调用connect进行连接,合适的时机A对象使用emit把信号带上参数发射出去,B对象的槽会就接收到响应。
信号槽机制有一些特点:
1. 类型安全:只有参数匹配的信号与槽才可以连接成功(信号的参数可以更多,槽会忽略多余的参数)。
2. 线程安全:通过借助QT自已的事件机制,信号槽支持跨线程并且可以保证线程安全。
3. 松耦合:信号不关心有哪些或者多少个对象与之连接;槽不关心自己连接了哪些对象的哪些信号。这些都不会影响何时发出信号或者信号如何处理。
4. 信号与槽是多对多的关系:一个信号可以连接多个槽,一个槽也可以用来接收多个信号。
二、信号与槽的定义
槽:用来接收信号,可以被看作是普通成员函数,可以被直接调用。支持public,protected,private修饰,用来定义可以调用连接到此槽的范围。
public slots:
void testslot(const QString& strSeqId);
信号:只需要声明信号名与参数列表即可,就像是一个只有声明没有实现的成员函数。
signals:
void testsignal(const QString&);
QT会在moc的cpp文件中实现它(参考下面代码)。下面代码中调用activate的第三个参数是类中信号的序列号。
// SIGNAL 0
void CTestObject:: testsignal (const QString & _t1)
{
void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
三、信号槽的连接与触发
通过调用connect()函数建立连接,会把连接信息保存在sender对象中;调用desconnect()函数来取消。
connect函数的最后一个参数来用指定连接类型(因为有默认,我们一般不填写)
static bool connect(const QObject *sender, const QMetaMethod &signal,
const QObject *receiver, const QMetaMethod &method,
Qt::ConnectionType type = Qt::AutoConnection);
在sender对象中调用:
emit testsignal(“test”);
# define emit
上面代码可以看到emit被定义为空,这样在发射信号时就相当于直接调用QT为我们moc出来的函数testsignal(constQString & _t1)。
具体的操作由QMetaObject::activate()来处理:遍历所有receiver并触发它们的slots。针对不同的连接类型,这里的派发逻辑会有不同。
QProgressDialog类提供了慢操作的进度的反馈。
四、QProgressDialog类的描述
进度对话框用于给用户这个操作还要有多长时间的指示,并且证明这个应用程序还没有冻结。它也给用于一个中止这个操作运行的机会。
进度对话框的一个常见问题是很难知道什么时候使用它们,操作在不同的硬件会占用不同的时间。QProgessDialog提供了对这个问题的解决方案:它估计操作将占用的时间(基于没步的时间),并且如果它超过minimumDuration()(默认为4秒)才显示自己。
使用setTotalSteps()(或者在构造函数中)设置操作中的“步”数并且调用setProgress()作为操作进度。步值的选择是任意的。它可以是复制的文件数,接收的字节数,在你的算法的主循环中的反复次数,或者一些其它的适合单元。进度从0开始,并且当你把totalSteps()作为参数调用setProgress(),这个进度对话框会显示这个操作已经完成。
对话框会在操作结束的时候自动重置并且隐藏自己。使用setAutoReset()和setAutoClose()可以改变这个行为。
你需要拥有一个正在运行的时间循环,把cancelled()信号和停止这个操作的槽连接起来,并且在间隔中调用setProgress()。例如:
Operation::Operation( QObject*parent = 0 )
: QObject(parent ), steps( 0 )
{
pd = newQProgressDialog( "Operation in progress.", "Cancel", 100 );
connect( pd, SIGNAL(cancelled()), this, SLOT(cancel()) );
t = new QTimer(this );
connect( t, SIGNAL(timeout()), this, SLOT(perform()) );
t->start( 0 );
}
void Operation::perform()
{
pd->setProgress( steps );
//……执行操作的一个半分比
steps++;
if ( steps> pd->totalSteps() )
t->stop();
}
void Operation::cancel()
{
t->stop();
//……清除
}
你可以通过使用setLabel()、setBar()和setCancelButton()用自定制的窗口部件来替换子窗口部件来定制这两种进度对话框。
使用QProgressDialog的两种方法:模式和非模式。
使用模式QProgressDialog是更简单的,但是必须调用qApp->processEvents()来保持事件循环的运行来确保应用程序没有冻结。在循环中执行这个操作,在间隔中调用setProgress(),并且检查wasCancelled()的取消。例如:
QProgressDialog progress( "Copyingfiles...", "Abort Copy", numFiles,
this,"progress", TRUE );
for ( int i = 0; i < numFiles; i++ ) {
progress.setProgress(i );
qApp->processEvents();
if (progress.wasCancelled() )
break;
//……复制文件
}
progress.setProgress( numFiles );
非模式进度对话框适合发生在后台的操作,用户还可以和应用程序进行交互。这样的操作通常是基于QTimer(或者QObject::timerEvent())、QSocketNotifier或QUrlOperator,或者在一个独立的进度中执行。你的主窗口的状态条中的QProgressBar常常可以做为模式进度对话框的替代。