论QT的信号/槽机制

 

 

     

 通识教育课程论文

      

      

 

课程名称:  C++图形界面编程技术    _  

学   院                         _     

专业班级                         

学   号                               

姓   名     爱尔兰(KEN                 

联系方式     irelandken@163.com   

任课教师      LZ                       

 

 

 

 

 

论QT的信号/槽机制

 

——WindowCut—一个简单实用的截图软件

 

   在图形界面编程技术,我比较喜欢QT这个框架,喜欢它的一次编写,到处编译这种方式,既高效,移植性又强,我试过在window下用Qt creator开发一个软件,然后直接将工程放到linux ubunto 11.04下,再次用Qt creator打开之前的工程,很简单地直接编译一次,一个代码也不用改,就将该软件移植到Linux下了,这一点很不错。。。

   更重要的是,Qt为我们带来了全新的思想:将一个工程逻辑分解为多个组件,利用强大的信号/槽机制,竟然可使这个工程分解为相互独立,互不干扰的若干个部分。以下,我将通过介绍我的WindowCut截图软件的编写过程来分析这种思想。

 

界面:

 

 

截图过程:


截图后的效果:

 

思想:

    这个软件由 1:主窗口mainWindow,2:全屏截图器 FullEditFEdit(就是半透明的全屏的组件) 和 3:截图查看器 PicSeePSee 组成,这3部分都是互相独立的类,在类的定义中,类与类之间无直接的函数调用,这些组件定义了若干个signal/slot, 而这3个类的实例是通过 signal/slot机制偶合在一起的。

   mainWindow上的Cut按键被按下后,mainWindow就隐藏,并发出一个StartCut()的信号,表示“开始截图”。。然后FEdit接收到StartCut()信号后,它就初始化,并记录下此时刻的屏幕背景,最后全屏显示半透明的背景。。。然PSee接收这个信号后,它就隐藏起来。。

   当用户在全屏截图器FEdit的全屏半透明背景上选定好区域后,FullEdit就会隐藏窗口并发出FinishCut“截图完成”的信号(信号中包含了截图的地址),然后截图查看器PSee接收到FinishCut信号及其中的截图的地址后,就会显示窗口,并在窗口上面绘制截图显示给用户,而mainWindow收到FinishCut信号后,就会显示主窗口,并使save按钮可见。

   此后,用户点击save按钮并成功保存后,mainWindow就会发出PicSaved“图片保存信号”,然后PSee会收到此信号,然后隐藏。

大概的流程就是这样。

  

   软件的功能是简单的,但是,里面的思想却很有意义的。

对于mainWindow,它只管发射信号,压根儿不用知道有多少个对象收到这些信号(当然也不可能知道),并且当收到特定的信号时,就去执行信号处理器slot就可以了,FullEdit 和 PicSee也一样。。因为具体哪个信号与哪个槽有连接关系,不是在类的定义在确定的,而且信号/槽的连接是面向类的实例的(所以不可能由类的抽象或定义来确定),QObject:: connect (sender, SIGNAL,receiver,SLOT)这个函数要传递对象的实例作参数,所以无法对抽象(未实例化)的对象之间建立连接关系。这些信号/槽的连接是在类外定义的(上述3个对象的连接关系是在main.cpp中面向这3个类的具体实例来定义的,与类的定义无关系)。

 

 




互相独立的组件+可靠的信号/槽机制 = 功能完善的软件

 

   在一个项目中,假如我是PM,那么只要我们确定好项目中各组件的“信号/槽”连接关系后,我们就可以将这个项目分解为若干个相互独立,无不干扰的的部分,开发过程在只要严格尊守约定好的信号和槽就可以了,待各部分组件完成后,建立后组件的实例间的“信号/槽”关系好,这个项目就完成了。。。

嗯,这思想,这开发模式,不正是全世界开发者所追求的目标吗??

 

SRC:

main.cpp

#include <QtGui/QApplication>
#include "widget.h"
#include "FullEdit.h"
#include "PicSee.h"
 
int main(int argc, char*argv[])
{
    QApplication a(argc, argv);
 
    Widget mainWindow;            //主窗口
 
   FullEdit FEdit;    //全屏截图器
 
   PicSee PSee;       //截图后的截图查看器
 
   //mainWindowto PSee:mainWindow发出StartCut信号后,PSee先最小化,然后再 隐藏
   QObject::connect(&mainWindow,SIGNAL(StartCut()),&PSee,SLOT(Minimized_Hide()));
 
   //mainWindowto FEdit:mainWindow发出StartCut信号后,FEdit先初始化再全屏显示
   QObject::connect(&mainWindow,SIGNAL(StartCut()),&FEdit,SLOT(Init_FCShow()));
 
   ----------------------------------------------------------------
 
   //当FEdit发出FinishCut信号时,mainWindow接收图片指针,并显示窗口(showNormal)
   QObject::connect(&FEdit,SIGNAL(FinishCut(QImage*)),&mainWindow,SLOT(Get_Pix_Show(QImage *)));
 
   //当FEdit发出FinishCut信号时,PSee接收图片指针,并显示窗口(showNormal)
   QObject::connect(&FEdit,SIGNAL(FinishCut(QImage*)),&PSee,SLOT(Get_Pix_Show(QImage *)));
 
   ----------------------------------------------------------------
  
   //当FEdit发出CancleCut截图被取消的信号,mainWindow接收后就显示主窗口
   QObject::connect(&FEdit,SIGNAL(CancleCut()),&mainWindow,SLOT(showNormal()));
 
   //mainWindowto PSee:mainWindow发出PicSaved图片保存信号后,PSee先最小化,然后再 隐藏
   QObject::connect(&mainWindow,SIGNAL(PicSaved()),&PSee,SLOT(Minimized_Hide()));
 
    mainWindow.show();
 
    returna.exec();
}


 

widget.ui

widget.h

widget.h
#ifndef WIDGET_H
#define WIDGET_H
 
#include <QWidget>
 
namespace Ui {
    classWidget;
}
 
class Widget : public QWidget
{
    Q_OBJECT
 
public:
    explicitWidget(QWidget *parent = 0);
    ~Widget();
 
signals:
   voidStartCut();//表示开始截图的信号
   voidPicSaved();//图片成功保存的信号
 
public slots:
   
   //收到FinishCut信号时,mainWindow接收图片指针,并显示窗口(showNormal)
    voidGet_Pix_Show(QImage *final_pix);
 
private slots:
    voidon_CutButton_clicked(); 
 
    voidon_SaveButton_clicked();
 
private:
    Ui::Widget *ui;
 
    QImage final_pix;  //最终的截图
 
};
 
#endif // WIDGET_H


 

widget.ccp

#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include<QMessageBox>
#include "FullEdit.h"
 
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(newUi::Widget)
{
    ui->setupUi(this);
 
    this->setFixedSize(265,90);
 
   this->setWindowTitle("WindowCut RC 3.1");
}
 
Widget::~Widget()
{
    deleteui;
}
 
 
 
 
/**
按钮Cut被按下后,截图查看器PSee隐藏,主窗口隐藏,然后调用全屏截图器FEdit的init()来获得
当前窗口的背景,并全屏显示
*/
 
void Widget::on_CutButton_clicked()
{
   this->showMinimized();
 
    this->hide();
 
   emit StartCut();   //发出开始截图StartCut信号
}
 
 
/**
保存按钮按下后,选择保存的位置,然后保存,保存成功后,截图查看器PSee隐藏,否则发出warning
 
*/
 
void Widget::on_SaveButton_clicked() 
{
     QString saveName =QFileDialog::getSaveFileName
             (this, tr("SaveFile"),"/",
            tr("BMP File(*.BMP);; PNG File(*.PNG);; JPEGFile(*.JPG)"));
    
    if(saveName.isNull())  //没有选择路径,则返回主窗口
       return;
 
 
     if(final_pix.save(saveName))
      emit PicSaved();  图片成功保存的信号
    else
       QMessageBox::warning(this,"Save Error","SaveError");
}
 
 
 
/**
Get_Pix() 是下个SLOT,当FEdit发出Send_Pix信号时,主窗口就用Get_Pix来接收发出的图片指针
然后Enable保存按钮,显示主窗口,
然后将此图片的指针发给截图查看器PSee,并在截图查看器PSee显示此图片
 
*/
 
void Widget::Get_Pix_Show(QImage *final_pix)
{
    this->final_pix= *final_pix;  //接收发出的图片指针
 
   ui->SaveButton->setEnabled(true);  //这里可保存了,则使保存按钮显示
 
   this->showNormal();//
}


 

 

FullEdit.h

#ifndef FULLEDIT_H
#define FULLEDIT_H
 
#include <QWidget>
 
class QPixmap;
 
class FullEdit : public QWidget
{
    Q_OBJECT
public:
    explicitFullEdit(QWidget *parent = 0);
 
    QImage ConverToShadow(QImagepix);   //将原图转换为半透明的图片
 
    //从原图pre在载取坐标(x1,y1),(x2,y2)所包围的部分区域的图片
    QImage _Cut(QImage pre,int x1,int y1,int x2,int y2);
 
 
signals:
    voidFinishCut(QImage *final_pix);  //当用户释放左键时,用signal发送图片的指针
 
   voidCancleCut();                 //当用户在FullEdit上单击左键时,发出截图取消信号
 
public slots:
 
   //收到StartCut信号后,FullEdit先初始化,记录下此时刻的屏幕背景,并全屏显示
   voidInit_FCShow(); 
 
protected:
    voidmousePressEvent(QMouseEvent *);
 
    voidmouseMoveEvent(QMouseEvent *);
 
    voidmouseReleaseEvent(QMouseEvent *);
 
    voidpaintEvent(QPaintEvent *);
 
private:
 
    QImage _screen;     //存放当前的全屏的截图
 
    QImage _shadow;    //存放当前全屏截图的半透明的幅本
 
    intx1,y1,x2,y2;   //当前选定区域的两角的坐标
 
    boolisLeftDown;  //记录当前左键是否被按下
 
};
 
#endif // FULLEDIT_H


 

FullEdit.cpp

#include "FullEdit.h"
#include<QPainter>
#include<QDesktopWidget>
#include<QMouseEvent>
#include<QApplication>
 
//#include<omp.h>
 
//#define myDebug
 
#ifdef myDebug
 
#include<QDebug>
#include<time.h>
 
#endif
 
 
 
FullEdit::FullEdit(QWidget *parent):
    QWidget(parent)
{
   //omp_set_num_threads(omp_get_max_threads());
 
   #ifdefmyDebug
   qDebug()<<"omp_get_max_threads(): "<<omp_get_max_threads();
   qDebug()<<"omp_get_num_threads(): "<<omp_get_num_threads();
   #endif
  
}
 
 
 
//收到StartCut信号后,FEdit先初始化再全屏显示
void FullEdit::Init_FCShow()   //初始化,并记录下此时刻的屏幕背景
{
   //取得当前全屏的窗口
   _screen =QPixmap::grabWindow(QApplication::desktop()->winId()).toImage();
 
   //取得当前全屏截图的半透明的幅本
    _shadow =ConverToShadow(_screen);
 
   //位置"左键被按下"的标志
    isLeftDown = false;
 
   this->showFullScreen();  //全屏显示
}
 
 
 
/**
将原图转换为半透明的图片
原理: 取得原图pix转换为QImage::Format_ARGB32格式的副本
 
此时得到的副本每个像素的格式为 0xAARRGGBB
其alpha通道 AA 一定 == FF(255)
然后我们将alpha通道改为(127),这样得到的就是半透明的效果
 
*/
 
QImage FullEdit::ConverToShadow(QImage pix)
{
   //取得原图pix转换为QImage::Format_ARGB32格式的副本
    QImage tmp =pix.convertToFormat(QImage::Format_ARGB32);
 
    intw = tmp.width();
    inth = tmp.height();
 
   //处理每一个像素
//#pragma omp parallel for
    for(int i=0; i<w; i++)
        for(int j=0; j<h; j++)
        {
            QRgb col =QRgb((tmp.pixel(i,j)<<1)>>1); //将a通道设为127
            tmp.setPixel(i,j,col);
        }
 
    returntmp;
}
 
 
/
template <class T>
inline void swap(T &a, T &b)
{
    a ^= b;
    b ^= a;
    a ^= b;
}
 
/**
//从原图pre在载取坐标(x1,y1),(x2,y2)所包围的部分区域的图片
 
就是建一个QImage对像,其大小就是截图区域的大小,
然后向这个对像中填充原图中对应的像素就可以了
 
*/
inline QImage FullEdit::_Cut(QImage pre,int _x1,int _y1,int _x2,int _y2)
{
#ifdef myDebug
   clock_t t = -clock();
#endif
 
    if(_x1> _x2) swap(_x1,_x2);
    if(_y1> _y2) swap(_y1,_y2);   //使(x1,y1)为left-top坐标, (x2,y2)为right-bottom坐标
 
    inth = _x2 - _x1;
    intw = _y2 - _y1;
 
    QImagetmp(h,w,QImage::Format_RGB32);
 
    QRgb col;
 
//#pragma omp parallel for
    for(int x=0; x<h; x++)
        for(int y=0; y<w; y++)
        {
            col = pre.pixel(_x1+x,_y1+y);
            tmp.setPixel(x,y,col);
        }
 
#ifdef myDebug
   t += clock();
   qDebug()<<"t: "<<t;
#endif
    returntmp;
}
 
 
/**
左键被按下,记录起始坐标 记录当前左键是被按下
 
如果是右键的话,通常就是想放弃截图,所以隐藏全屏截图器FullEdit,
然后发出窗口已隐藏的信号来通知主窗口
 
*/
 
void FullEdit::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)   //左键被按下,记录起始坐标
    {
        x1 = event->pos().x();
        y1 = event->pos().y();
 
        isLeftDown = true;              记录当前左键是否被按下
    }
   else    //右键被按下,则放弃截图并退出
   {
      this->hide();
      emit CancleCut();  //发出CancleCut取消截图信号
   }
}
 
void FullEdit::mouseMoveEvent(QMouseEvent *event)
{
    if(event->buttons() & Qt::LeftButton)  //左键被按下
    {
        x2 = event->pos().x();
        y2 = event->pos().y();
 
        update();                       //截图区域被改变,则重绘窗口
    }
 
}
 
 
/**
如果左键被释放,则用QImage对像记录截图区域,隐藏当前窗口,然后用信号机制将这个对象的指针发出
 
*/
void FullEdit::mouseReleaseEvent(QMouseEvent *event)
{
   if(event->button() == Qt::LeftButton)
   {
 
      isLeftDown = false;           记录当前左键是否被按下,这里是"否"
 
      x2 = event->pos().x();
      y2= event->pos().y();      //记录截图区域的对角的坐标
 
      if(x1> x2) swap(x1,x2);
      if(y1> y2) swap(y1,y2);   //使(x1,y1)为left-top坐标, (x2,y2)为right-bottom坐标
 
      //记录截图区域
      QImage *_final_screen = new QImage(this->_Cut(_screen,x1,y1,x2,y2));
 
      this->hide();                 //截图完成后,使这个窗口设为hide
 
      emitFinishCut(_final_screen);   //当用户释放左键时,发出FinishCut截图完成信号
   }
}
 
 
/**
全屏截图器的重绘,就是先绘制半透明背景,
然后从原背景中cut一个当前选中的区域,并绘在半透明背景的上方
再绘出蓝色边框包围起截图的区域,用于提示
*/
 
void FullEdit::paintEvent(QPaintEvent *)
{
    QPainter paint(this);
 
   //绘制半透明背景
    paint.drawImage(0,0,_shadow);
 
    if(isLeftDown)
    {
      //从原背景中cut一个当前选中的区域,并绘在半透明背景的上方
        QImage tmp = this->_Cut(_screen,x1,y1,x2,y2);
 
        intlx = (x1<=x2)? x1:x2;
        intly = (y1<=y2)? y1:y2;
 
        paint.drawImage(lx,ly,tmp);
      //
 
      //用蓝色边框包围起截图的区域
      QPen myPen;
      myPen.setWidth(1);
      myPen.setColor(Qt::blue);
      paint.setPen(myPen);
 
        paint.drawLine(x1,y1,x1,y2);
 
        paint.drawLine(x1,y1,x2,y1);
 
        paint.drawLine(x1,y2,x2,y2);
 
        paint.drawLine(x2,y1,x2,y2);
      //
   }
}


 

PicSee.h

#ifndef PICSEE_H
#define PICSEE_H
 
#include <QWidget>
 
class PicSee : public QWidget
{
    Q_OBJECT
 
public:
    explicitPicSee(QWidget *parent = 0);
 
public slots:
  
   voidMinimized_Hide();   //收到StartCut信号后,先最小化,然后再 隐藏
 
   //收到FinishCut,PSee接收图片指针,并显示窗口(showNormal)
    voidGet_Pix_Show(QImage *final_pix); 
 
protected:
    voidpaintEvent(QPaintEvent *);
 
private:
    QImage *_pix;
};
 
#endif // PICSEE_H


 

PicSee.cpp

#include "PicSee.h"
#include<QPainter>
 
PicSee::PicSee(QWidget *parent):
    QWidget(parent)
{
   this->setWindowTitle("WindowCut RC 3.1");
}
 
 
void PicSee::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.drawImage(0,0,*_pix);
}
 
void PicSee::Minimized_Hide()   //收到StartCut信号后,先最小化,然后再 隐藏
{
   this->showMinimized();
   this->hide();
}
 
void PicSee::Get_Pix_Show(QImage *final_pix)
{
   this->_pix= final_pix;
 
   this->setFixedSize(_pix->size());  //将此图片的指针发给截图查看器PSee
 
   this->showNormal();  //在截图查看器PSee显示此图片
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值