Snail    07年7月第一个星期制作
制作冒泡式弹出对话框主要涉及2个技术要点(主要供触摸屏使用)
1 实用的Qtimer类的使用(计算鼠标在对象上的停留时间确定是否弹出属性对话框)
2 漂亮的对话框的绘制(主要是QWidget::setMask(const QBitmap& bitmap)的使用)
 
详细分析:
QTimer类的使用
QTimer的一般性使用
QTimer *timer = new QTimer( myObject );
    connect( timer, SIGNAL(timeout()),
             myObject, SLOT(timerDone()) );
timer->start( 2000, TRUE );                 // 2 seconds single-shot
注意: 1 myObject销毁时,Qtimer会自动销毁。不需要手工删除。
2         QTimerint start ( int msec, bool sshot = FALSE )  方法第二个参数sshot如果
ture,则myObject的槽方法只会执行一次。不然的话会一直执行直到QTimer停止或者对象被销毁为止。
 
在本程序中如何使用QTimer
//头文件
class MainPanel : public QWidget {                 // MainPanel为被测试面板,测试弹出属性框
         Q_OBJECT
public:
         MainPanel(QWidget *parent=0, const char *name=0, WFlags fl = 0);
         ~MainPanel() {}
public slots:
         void holdMouseToPopupDialog();            //QTimer链接的槽,确定是否弹出属性框
protected:
         virtual void mousePressEvent ( QMouseEvent * );    //这三个虚函数需要被覆盖用作计时
         virtual void mouseReleaseEvent(QMouseEvent *);
         virtual void mouseMoveEvent(QMouseEvent *);
private:
         QPoint* __popupPoint;                     //弹出面板的坐标,也就是要显示属性的物体
         PopupInfoDialog* __propDialog;                //这个属性框的制作放到后面
         QTimer* __mouseHoldClicker;                 //创建一个Qtimer类成员对象
};
//代码文件
MainPanel::MainPanel(QWidget *parent, const char *name, WFlags fl) :QWidget(parent, name, fl) {
         __propDialog = new PopupInfoDialog(720,480,this,0,0,QDialog::WStyle_Customize | QDialog::WStyle_NoBorder | QDialog::WStyle_StaysOnTop);
         __mouseHoldClicker = new QTimer(this);                   
         __popupPoint = new QPoint();                                                      //创建一个默认弹出点
         connect(__mouseHoldClicker, SIGNAL(timeout()), this, SLOT(holdMouseToPopupDialog()));   //链接QTimer和面板
}
 
void MainPanel::mousePressEvent(QMouseEvent * e) {     //这个方法的意思是如果当鼠标按下(触摸屏的话就是手指按下)
         __popupPoint->setX(e->x());                               //保存当前的坐标点
         __popupPoint->setY(e->y());
         __propDialog->hide();                                    //隐藏以前出现的弹出式属性框
         __mouseHoldClicker->start(500, true);                     //0.5秒计时,如果手指没有离开这个点,或者没有移动,则
                                                          //触发holdMouseToPopupDialog()槽一次。
}
 
void MainPanel::mouseReleaseEvent(QMouseEvent * e) {          //这个方法的意思是如果手指离开触摸屏,就。。。
         __mouseHoldClicker->stop();                   //如果在槽还没有触发前就停止计时器,这样不用弹出属性框了
}
 
void MainPanel::mouseMoveEvent(QMouseEvent * e) {            //这个方法的意思是如果手指移动了的话,就。。。。。。。。
         if (e->x()-__popupPoint->x()>5 || e->y()- __popupPoint->y()>5) {  //如果手指在5个像素内移动,就认为可接受的,如果超过5
                   __mouseHoldClicker->stop();                           //像素,则不予弹出对话框
         }
}
 
void MainPanel::holdMouseToPopupDialog() {                    //测试面板的槽,用来弹出属性框
         __propDialog->popupAtPoint(__popupPoint->x(), __popupPoint->y());
         __propDialog->show();
}
 
 
在本程序中绘制漂亮的对话框
class PopupInfoDialog : public QDialog {
         Q_OBJECT
public:
         PopupInfoDialog (int areaWidth = 800 ,int areaHeight = 600,QWidget * parent=0, const char * name=0, bool modal=FALSE, WFlags f=0 );
         void popupAtPoint(int x,int y);
 
private:
         void __refreshMask(int popX, int popY);                        //重新刷新蒙板,隐藏对话框该的一些不需要显示的地方。
         void __fillArrow(QPainter&,int,int,int,int,int,int);                   //这个是画这个东东的---à
void PopupInfoDialog::popupAtPoint(int x, int y) {                   //对话框就弹出在这个点,在测试面板的             
//holdMouseToPopupDialog()中调用此方法。
         this->__refreshMask(x, y);
         this->show();
}
 
/*在这里,大致的实现过程是这样,QWidget可以通过一个QBitmap蒙板来大致决定需要显示哪些部分,我们就通过绘制蒙板来让QWidget显示蒙板同样大小区域来实现绘制任意形状的对话框的效果*/
 
void PopupInfoDialog::__refreshMask(int popX, int popY) {            //重新刷新蒙板
         QBitmap mask(__PopupWidth, __PopupHeight, true);             //创建一个蒙板
         QPainter painter(&mask);                                   
         painter.setPen(popupBorder);
         painter.setBrush(blackBrush);                                 //必须设置笔刷,得把绘制区域全部填充满
         painter.drawRoundRect(50, 0, 300, 300, 10, 10);//300是实际显示内容框,10为圆角矩形圆角半径
 
         int distanceToBorder;
         if (__areaWidth-popX >= __PopupWidth-50) {//右边能容纳弹出框  //这是一个简单的算法,在对象右边能显示对话框就在右边
                   if (popY <= __areaHeight/2) {//三角符号在弹出框的上边             //绘制属性对话框,不然就在左边绘制属性对话框。
                            if (popY <= __DialogToBorderDistance) {
                                     distanceToBorder = popY;
                            } else {
                                     distanceToBorder = __DialogToBorderDistance;
                            }
                            __fillArrow(painter, 0, popY-distanceToBorder+20, 50, 100-20, 50, 100+20);
                            this->move(popX, distanceToBorder);
                   } else {//三角符号在弹出框的下边
                            int dx = popX;
                            int dy = __areaHeight-__PopupHeight-__DialogToBorderDistance;
                            if ((__areaHeight - popY) <= __DialogToBorderDistance) {
                                     dy = __areaHeight-__PopupHeight-(__areaHeight - popY)+20;
                            }
                            __fillArrow(painter, 0, popY-(dy-20), 50, 200-20, 50, 200+20);
                            this->move(dx, dy);
                   }
         } else {//弹出框放在左边
                   if (popY <= __areaHeight/2) {//三角符号在弹出框的上边
                            if (popY <= __DialogToBorderDistance) {
                                     distanceToBorder = popY;
                            } else {
                                     distanceToBorder = __DialogToBorderDistance;
                            }
                            __fillArrow(painter, __PopupWidth, popY-distanceToBorder+20, __PopupWidth-51, 100-20, __PopupWidth-51,
                                               100+20);
                            this->move(popX-__PopupWidth, distanceToBorder);
                   } else {//三角符号在弹出框的下边
                            int dx = popX-__PopupWidth;
                            int dy = __areaHeight-__PopupHeight-__DialogToBorderDistance;
                            if ((__areaHeight - popY) <= __DialogToBorderDistance) {
                                     dy = __areaHeight-__PopupHeight-(__areaHeight - popY)+20;
                            }
                            __fillArrow(painter, __PopupWidth, popY-dy+20, __PopupWidth-51, 200-20, __PopupWidth-51, 200+20);
                            this->move(dx, dy);
                   }
         }
         this->clearMask();                                            //清除原来的蒙板效果
         this->setMask(mask);                                         //添加新的蒙板效果
}
 
/*这个方法通过在给定的三点绘制多边形,达到绘制属性框箭头的效果 */
void PopupInfoDialog::__fillArrow(QPainter& painter, int p1x, int p1y, int p2x, int p2y, int p3x, int p3y) {
         QPointArray points;
         points.setPoints(3, p1x, p1y, p2x, p2y, p3x, p3y);
         painter.drawPolygon(points);
}
 
最后的效果图如下:
 
 Qt2.3.10制作冒泡式弹出对话框_职场