StupidBeauty
Read times:1375Posted at:Wed Feb 8 18:57:44 2012
- no title specified

对QT中信号槽连接了解不够,导致逻辑错误

牵涉到的代码:

信号:

void MoneyChanged( qint32 NewMoney); //!<钱数已经改变。

成员函数:

/*!

* @brief 卖出当前货物。

* @param Good2Sell 当前要卖出的货物。

*/

void ShangHaiYanMoJi ::SellGood( Good * Good2Sell)

{

QSpinBox * CrtSellSpinBox= 0 ; //当前商品的卖出数量旋转框。

qint32 CrtPrice= 0 ; //当前商品的价格。

Good * CrtGood= 0 ; //当前商品。

qint32 EarnedMoney= 0 ; //此次赚到的钱。

qint32 CrtAvePrice= 0 ; //当前商品的平均进价。

CrtGood=Good2Sell; //当前商品的指针。

CrtPrice=CrtGood->GetPrice(); //获取当前商品的价格。

CrtSellSpinBox=CrtGood->GetSellSpinBox(); //获取卖出数量旋转框的指针。

if (CrtSellSpinBox->value()) //真正卖出咯东西,则进行结算。

{

CrtAvePrice=CrtGood->GetAvePrice(); //获取平均进价。

EarnedMoney=CrtSellSpinBox->value()*(CrtPrice-CrtAvePrice); //计算出赚到的钱数。

Money +=CrtSellSpinBox->value()*CrtPrice; //更新钱数。

emit MoneyChanged( Money ); //钱数已经改变。

if (EarnedMoney>= 0 ) //记录利润。

{

CrtGood->AddProfit(EarnedMoney); //记录利润。

} //if

else //记录亏损。

{

CrtGood->AddLose(EarnedMoney); //记录亏损。

} //else

CrtGood->AddTradeAmount(CrtSellSpinBox->value()); //记录交易量。

CrtGood->SubAmount(CrtSellSpinBox->value()); //减少存货。出货。

// CrtSellSpinBox->setMaximum(CrtSellSpinBox->maximum()-CrtSellSpinBox->value()); //设置新的最大值,这是在当前情况下能卖的商品个数。

// CrtAmountLcd=CrtGood->GetAmountLcd(); //获取商品存货数量二极管。

// CrtAmountLcd->display(CrtSellSpinBox->maximum()); //显示存货数量。

// ComputeBuySell(); //计算能买卖的货的个数。

//取出视口的矩形,将这个矩形向下移动一段距离,距离的值为当前滚动条的值。产生一个新的矩形,这个新的矩形就是用户当前实际看到的区域。

QRect CrtRect= ui -> PlayScrollArea ->viewport()->rect(); //取得视口的矩形。

CrtRect.moveTop( ui -> PlayScrollArea ->verticalScrollBar()->value()); //移动矩形。

qDebug() << __FILE__ << __LINE__ << __func__ << tr( "Scroll area height:" ) << ui -> PlayScrollArea ->height(); //Debug.

qDebug() << __FILE__ << __LINE__ << __func__ << tr( "Scroll area widget height:" ) << ui -> PlayScrollArea ->widget()->height(); //Debug.

if (Good2Sell->GetFrame()) //框架存在。

{

QuePourGold(Good2Sell); //将下泼金币动画添加到队列中。

PourGoldTimer .start(); //启动向下泼金币效果的定时器。

} //if

//创建“飞行文字”的队列:

QueFlyText(Good2Sell,EarnedMoney); //将飞行文字效果添加到队列中。

FlyTextTimer .start(); //启动飞行文字效果的定时器。

if (EarnedMoney> 0 ) //是赚咯钱,则显示庆祝视频。

{

ShowCelibVideo(Good2Sell); //显示庆祝视频。

} //if

} //if

return ;

} //void ShangHaiYanMoJi::SellGood(Good * Good2Sell)

信号槽:

/*!

* @brief 刷新总资产。

* @param NewDay 新的天数。

*/

void ShangHaiYanMoJi ::RefreshAsset( qint32 NewDay)

{

( void )(NewDay); //不使用勒個参数。

Good * CrtGood= 0 ; // 当前商品。

qint32 NewAsset= 0 ; //新的总资产数。

foreach (CrtGood, GoodList ) //一个个商品地计算。

{

NewAsset+=CrtGood->GetPrice()*CrtGood->GetAmount(); //加上当前商品的资产。

} //foreach

NewAsset+= Money ; //加上现金。

//在二极管中显示资产数:

if ( ui -> AssetLcdNumber ->checkOverflow(NewAsset)) //溢出。

{

qint32 DigitCount=ceil(log10(NewAsset)); //计算出位数。

if (NewAsset==pow( 10 ,DigitCount)) //正好是10的幂.

{

DigitCount++; //再多一位。

} //if

ui -> AssetLcdNumber ->setDigitCount(DigitCount); //设置位数。

ui -> AssetLcdNumber ->updateGeometry(); //更新几何属性。

} //if

ui -> AssetLcdNumber ->display(NewAsset); //显示资产数。

emit AssetChanged(NewAsset); //发射信号,资产发生变化。

return ;

} //void ShangHaiYanMoJi::RefreshAsset(qint32 NewDay)

连接:

connect( this , SIGNAL (MoneyChanged( qint32 )), this , SLOT (RefreshAsset( qint32 ))); //当钱数发生改变时,刷新总资产。

预期的行为及错误:

在界面中“卖出”一定数量的某個商品时,对其进行处理的就是void ShangHaiYanMoJi :: SellGood ( Good * Good2Sell)函数。在这個函数中,會从界面上对应于该商品的“卖出数量”控件( QSpinBox * CrtSellSpinBox= 0 ; //当前商品的卖出数量旋转框。,同时用来表示该商品当前有多少存货)里获取卖出的商品的数量,计算出这些数量的商品卖得的钱数,加到系统的现金数量( qint64 Money ; //!<当前拥有的钱。)中去,再从界面上对应于该商品的“卖出数量”控件里减去要“卖出”的商品数量。同时发送信号( void MoneyChanged ( qint32 NewMoney ); //!<钱数已经改变。)以告知系统的现金总数发生变化。

由“钱数变化”信号(void MoneyChanged ( qint32 NewMoney );)引发“更新资产数量”(void ShangHaiYanMoJi :: RefreshAsset ( qint32 NewDay))信号槽。在这個信号槽中會遍历所有种类的商品,获取其存货数量(通过前面所说的“卖出数量”控件),计算出其价值,加起来,再加上现金数量,即为系统中当前的总资产数。然后在界面上更新资产数。

本来由程序计算出来的资产数应当与由人来手动计算出的资产数相等,但是在实际调试中却不相等。程序计算出来的资产数总會多一些,而从具体的数值上来看,好像程序把刚“卖出”的商品计算咯两次一样。

出错原因:

根据出错的现象跟踪到void ShangHaiYanMoJi :: SellGood ( Good * Good2Sell)函数的代码中,就知道原因咯。具体的在这里:

EarnedMoney=CrtSellSpinBox->value()*(CrtPrice-CrtAvePrice); //计算出赚到的钱数。

Money +=CrtSellSpinBox->value()*CrtPrice; //更新钱数。

emit MoneyChanged ( Money ); //钱数已经改变。

if (EarnedMoney>= 0 ) //记录利润。

{

CrtGood->AddProfit(EarnedMoney); //记录利润。

} //if

else //记录亏损。

{

CrtGood->AddLose(EarnedMoney); //记录亏损。

} //else

CrtGood->AddTradeAmount(CrtSellSpinBox->value()); //记录交易量。

CrtGood->SubAmount(CrtSellSpinBox->value()); //减少存货。出货。

其中,是最后的 CrtGood-> SubAmount (CrtSellSpinBox-> value ()); //减少存货。出货。 这一句来将界面上的“卖出数量”控件的值减少的,此句代码位于发射“钱数改变”信号的代码( emit MoneyChanged ( Money ); //钱数已经改变。 )之后。当执行到发射信号的代码时,钱数已经增加,而存货数量还没有减少。由于前面进行信号槽连接时没有指定连接方式,因此使用的是默认的自动连接模式,信号与信号槽本身又处于同一线程,在这种情况下,得到的是一個直接连接,也就是说信号槽在发射信号的语句执行时立即被调用。于是“更新资产”信号槽在存货数量还没有减少时便被调用,相当于将“卖出”的那么多的数量的商品再加咯一次。

验证咯一下,将发射信号的代码放到函数最后,变成下面的代码之后就计算正常咯:

/*!

* @brief 卖出当前货物。

* @param Good2Sell 当前要卖出的货物。

*/

void ShangHaiYanMoJi ::SellGood( Good * Good2Sell)

{

QSpinBox * CrtSellSpinBox= 0 ; //当前商品的卖出数量旋转框。

qint32 CrtPrice= 0 ; //当前商品的价格。

Good * CrtGood= 0 ; //当前商品。

qint32 EarnedMoney= 0 ; //此次赚到的钱。

qint32 CrtAvePrice= 0 ; //当前商品的平均进价。

CrtGood=Good2Sell; //当前商品的指针。

CrtPrice=CrtGood->GetPrice(); //获取当前商品的价格。

CrtSellSpinBox=CrtGood->GetSellSpinBox(); //获取卖出数量旋转框的指针。

if (CrtSellSpinBox->value()) //真正卖出咯东西,则进行结算。

{

CrtAvePrice=CrtGood->GetAvePrice(); //获取平均进价。

EarnedMoney=CrtSellSpinBox->value()*(CrtPrice-CrtAvePrice); //计算出赚到的钱数。

Money +=CrtSellSpinBox->value()*CrtPrice; //更新钱数。

if (EarnedMoney>= 0 ) //记录利润。

{

CrtGood->AddProfit(EarnedMoney); //记录利润。

} //if

else //记录亏损。

{

CrtGood->AddLose(EarnedMoney); //记录亏损。

} //else

CrtGood->AddTradeAmount(CrtSellSpinBox->value()); //记录交易量。

CrtGood->SubAmount(CrtSellSpinBox->value()); //减少存货。出货。

// CrtSellSpinBox->setMaximum(CrtSellSpinBox->maximum()-CrtSellSpinBox->value()); //设置新的最大值,这是在当前情况下能卖的商品个数。

// CrtAmountLcd=CrtGood->GetAmountLcd(); //获取商品存货数量二极管。

// CrtAmountLcd->display(CrtSellSpinBox->maximum()); //显示存货数量。

// ComputeBuySell(); //计算能买卖的货的个数。

//取出视口的矩形,将这个矩形向下移动一段距离,距离的值为当前滚动条的值。产生一个新的矩形,这个新的矩形就是用户当前实际看到的区域。

QRect CrtRect= ui -> PlayScrollArea ->viewport()->rect(); //取得视口的矩形。

CrtRect.moveTop( ui -> PlayScrollArea ->verticalScrollBar()->value()); //移动矩形。

qDebug() << __FILE__ << __LINE__ << __func__ << tr( "Scroll area height:" ) << ui -> PlayScrollArea ->height(); //Debug.

qDebug() << __FILE__ << __LINE__ << __func__ << tr( "Scroll area widget height:" ) << ui -> PlayScrollArea ->widget()->height(); //Debug.

if (Good2Sell->GetFrame()) //框架存在。

{

QuePourGold(Good2Sell); //将下泼金币动画添加到队列中。

PourGoldTimer .start(); //启动向下泼金币效果的定时器。

} //if

//创建“飞行文字”的队列:

QueFlyText(Good2Sell,EarnedMoney); //将飞行文字效果添加到队列中。

FlyTextTimer .start(); //启动飞行文字效果的定时器。

if (EarnedMoney> 0 ) //是赚咯钱,则显示庆祝视频。

{

ShowCelibVideo(Good2Sell); //显示庆祝视频。

} //if

emit MoneyChanged( Money ); //钱数已经改变。

} //if

return ;

} //void ShangHaiYanMoJi::SellGood(Good * Good2Sell)

理论支持:QT文档中说明的信号槽连接类型:

Qt4.8:Qt命名空间,Qt Namespace

类型文档

enum Qt::ConnectionType

这個枚举量描述的是信号与信号槽之间可采用的连接的类型。尤其是用来指明某個信号是应当立即传递到信号槽中还是排队等待稍后传递。

常量

描述

Qt::AutoConnection

0

(默认)如果该信号是从一個与接收对象所处的线程不同的线程中发射的,则信号會被排队,其行为就像Qt::QueuedConnection一样。否则,该信号槽會立即被调用,其行为就像Qt::DirectConnection 一样。连接类型會在信号被发射时决定。

Qt::DirectConnection

1

当信号被发射时,信号槽會立即被调用。

Qt::QueuedConnection

2

当控件权转移到接收者的线程中的事件循环时,信号槽會被调用。信号槽是在接收者的线程中执行的。

Qt::BlockingQueuedConnection

4

与QueuedConnection 类似,但是当前线程會阻塞,直到信号槽返回为止。只有在发射者与接收者处于不同的线程时,才能使用这种连接类型。注意:如果不遵守这個规则,那么可能导致妳的程序死锁。

Qt::UniqueConnection

0x80

与AutoConnection 类似,但是只有在这個连接不會与已有的连接重复时才會建立。也就是说,如果相同的信号与信号槽已经在相同的一对对象之间连接起来,那么这次的连接将會失败。这個连接类型是从Qt 4.6 开始引入的。

Qt::AutoCompatConnection

3

如果启用咯Qt 3 支持,则这個會是默认的连接类型。与AutoConnection 类似,但是在某些情况下會导致输出警告信息。参考兼容性信号和信号槽(Compatibility Signals and Slots)以了解更多信息。

对于排队式连接,其参数必须是 Qt 的元对象系统认识的类型,因为Qt 需要复制这些参数并且将它们在后台的事件中储存。如果妳尝试使用一個排队式连接而碰到这样的错误信息的话:

QObject::connect: Cannot queue arguments of type 'MyType'

那么就在建立连接之前调用 qRegisterMetaType ()来注册一下这种数据类型。

如果想在跨线程使用信号和信号槽的话,参考一下 跨线程的信号和信号槽(Signals and Slots Across Threads)

参考 Qt 中的线程支持 QObject::connect () qRegisterMetaType ()

Your opinions
Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands