一:Qt信号和槽概述

信号与槽作为QT的核心机制在QT编程中有着广泛的应用,本章介绍了信号与槽的一些基本概念、元对象工具以及在实际使用过程中应注意的一些问题。

信号和槽机制是QT的核心机制,要精通QT编程就必须对信号和槽有所了解。信号和槽是一种高级接口,应用于对象之间的通信,它是QT的核心特性,也是QT区别于其它工具包的重要地方。信号和槽是QT自行定义的一种通信机制,它独立于标准的C/C++语言,因此要正确的处理信号和槽,必须借助一个称为 moc (Meta Object Compiler)的QT工具,该工具是一个C++预处理程序,它为高层次的事件处理自动生成所需要的附加代码。

在所有从 QObject或其子类(例如Qwidget)派生的类都能够包含信号和槽。当对象改变其状态时,信号就由该对象发射(emit)出去,这就是对象所要做的全部事情,它不知道另一端是谁在接收这个信号。这就是真正的信息封装,它确保对象被当作一个真正的软件组件来使用。槽用于接收信号,但它们是普通的对象成员函数。一个槽并不知道是否有任何信号与自己相连接。而且,对象并不了解具体的通信机制。

二:信号和槽示意图

三:信号

当某个信号对其客户或所有者发生的内部状态发生改变,信号被一个对象发射。

只有定义过这个信号的类及其派生类能够发射这个信号。当一个信号被发射时,与其相关联的槽将被立刻执行,就象一个正常的函数调用一样。

信号-槽机制完全独立于任何GUI事件循环。

如果存在多个槽与某个信号相关联,那么,当这个信号被发射时,这些槽将会一个接一个地执行,但是它们执行的顺序将会是随机的、不确定的,我们不能人为地指定哪个先执行、哪个后执行。

四:如何编写信号

信号的声明是在头文件中进行的,QT的signals关键字指出进入了信号声明区,随后即可 声明自己的信号。

下面定义了三个信号:

signals:

void mySignal();

void mySignal(int x);

void mySignalParam(int x,int y);

在上面的定义中,signals是QT的关键字,而非C/C++的。接下来的一行void mySignal() 定义了信号mySignal,这个信号没有携带参数;接下来的一行void mySignal(int x)定义 了重名信号mySignal,但是它携带一个整形参数,这有点类似于C++中的虚函数。从形式上 讲信号的声明与普通的C++函数是一样的,但是信号却没有函数体定义,另外,信号的返回 类型都是void

五:槽

槽是普通的C++成员函数,可以被正常调用,它们唯一的特殊性就是很多信号可以与其相关联。当与其关联的信号被发射时,这个槽就会被调用。槽可以有参数,但槽的参数不能有缺省值。

同普通的C++成员函数一样,槽函数也分为三种类型,即public slots、private slots和protected slots。

槽也能够声明为虚函数,这也是非常有用的。

六:槽函数类型

public slots:在这个区内声明的槽意味着任何对象都可将信号与之相连接。这对于组件编程非常有用,你可以创建彼此互不了解的对象,将它们的信号与槽进行连接以便信息能够正确的传递。

protected slots:在这个区内声明的槽意味着当前类及其子类可以将信号与之相连接。这适用于那些槽,它们是类实现的一部分,但是其界面接口却面向外部。

private slots:在这个区内声明的槽意味着只有类自己可以将信号与之相连接。这适用于联系非常紧密的类。

七:如何编写槽

槽的声明也是在头文件中进行的。例如,

下面声明了三个槽:

public slots:

void mySlot();

void mySlot(int x);

void mySignalParam(int x,int y);

八:QObject::connect

通过调用QObject对象的connect函数来将某个对象的信号与另外一个对象的槽函数相关联,这样当发射者发射信号时,接收者的槽函数将被调用。该函数的定义如下:

bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * member ) [static]

这个函数的作用就是将发射者sender对象中的信号signal与接收者receiver中的member槽函数联系起来。当指定信号 signal时必须使用QT的宏SIGNAL(),当指定槽函数时必须使用宏SLOT()。

如果发射者与接收者属于同一个对象的话,那么在connect 调用中接收者参数可以省略。

九:带有信号和槽的类

class TsignalApp:public QMainWindow
{Q_OBJECT//信号声明区signals: //声明信号mySignal()void mySignal();//声明信号mySignal(int)void mySignal(int x);//声明信号mySignalParam(int,int)void mySignalParam(int x,int y);//槽声明区public slots: //声明槽函数mySlot()void mySlot();//声明槽函数mySlot(int)void mySlot(int x);//声明槽函数mySignalParam (int,int)void mySignalParam(int x,int y);
}

十:一个信号与另一个信号相关联

class MyWidget : public QWidget{ public: MyWidget(); ... signals: void aSignal();... private:... QPushButton *aButton;
};
MyWidget::MyWidget()
{ aButton = new QPushButton( this );
connect( aButton, SIGNAL(clicked()), SIGNAL(aSignal()) );
}

十一:信号和槽例子

下面定义了两个对象:标签对象label和滚动条对象scroll,并将valueChanged()信号与标签对象的setNum()相关联,另外信号还携带了一个整形参数,这样标签总是显示滚动条所处位置的值。

QLabel     *label  = new QLabel;
QScrollBar *scroll = new QScrollBar;
QObject::connect( scroll, SIGNAL(valueChanged(int)),label,  SLOT(setNum(int)) );

十二:QObject::disconnect

当信号与槽没有必要继续保持关联时,我们可以使用disconnect函数来断开连接。

bool QObject::disconnect ( const QObject * sender, const char * signal, const Object * receiver, const char * member ) [static]

十三:槽和信号限制

限制1:类型宏不能被用于信号和槽的参数

#ifdef ultrix
#define SIGNEDNESS(a) unsigned a
#else
#define SIGNEDNESS(a) a
#endif
class Whatever : public QObject
{ ... signals: void someSignal( SIGNEDNESS(int) );//错误...
};

限制2:构造函数不能用于信号部分和槽部分

class SomeClass : public QObject
{ Q_OBJECT public slots: SomeClass( QObject *parent, const char *name ) : QObject( parent, name ) //错误... };

限制3:函数指针不能作为信号或槽的参数。 

class SomeClass : public QObject{ Q_OBJECT... public slots:void apply( void (*apply)(List *, void *), char * ); //错误};

限制4:信号和槽不能被升级  public  protected private

             QButtonGroup::buttonPressed()槽是保护的。

class Whatever : public QButtonGroup
{ ... public slots: void QButtonGroup::buttonPressed; // 错的 ...
};

其他限制如下:

如果一个信号与多个槽相联系的话,那么,当这个信号被发射时,与之相关的槽被激活的顺序将是随机的。

信号与槽也不能携带模板类参数。

嵌套的类不能位于信号或槽区域内也不能有信号或者槽。

友元声明不能位于信号或者槽声明区内。

十四:元对象工具

元对象编译器moc(meta object compiler)对C++文件中的类声明进行分析并产生用于初始化元对象的C++代码,元对象包含全部信号和槽的名字以及指向这些函数的指针。

元对象代码是signal/slot机制所必须的。用moc产生的C++源文件必须与类实现一起进行编译和连接,或者用#include语句将其包含到类的源文件中。moc并不扩展#include或者#define宏定义,它只是简单的跳过所遇到的任何预处理指令。

十五:moc编译

Moc元对象编译器的功能是把包含有信号和槽的源文件编译成特殊的文件名为moc_+实际源文件名称

它的具体使用格式如下:

moc – [option] <filename>

moc   mysingal.h  -o moc_mysingal.h

option包含如下参数:

-o <file> 
•把编译结果写到一个文件中而不是标志输出
-p  -p path
•使元对象编译器生成的(如果有生成的)#include声明的文件名称中预先考虑到 path/。
-q path
•使元对象编译器在生成的文件中的qt #include文件的名称中预先考虑到 path/。         

十六:类声明位置

类的声明放在一个头文件(.h文件)中

如果在上述的文件myclass.h中发现类的声明,元对象编译器的输出文件将会被放在一个叫moc_myclass.cpp的文件中。这个文件将会像通常情况一样被编译,作为对象文件的结果是moc_myclass.o

类的声明放在一个实现文件(.cpp文件)中

如果上述的文件myclass.cpp中发现类的声明,元对象编译器的输出文件将会被放在一个叫myclass.moc的文件中。这个文件需要被实现文件包含(#include),也就是说myclass.cpp需要包含下面这行 #include "myclass.moc" 放在所有的代码之后。

十七:事件机制

Qt中定义的事件是一个从QEvent类继承下来的,它表示应用程序内部或者外部发生某些应用程序必须知道的事情

Qt 使用一个事件队列对所有发出的事件进行维护,当新的事件产生时,会被追加到事件队列的尾部。前一个事件完成后,取出后面的事件进行处理。但是,必要的时候,Qt 的事件也可以不进入事件队列,而是直接处理

在Qt内部,Qt通过由函数QApplication::exec()函数启动的主事件循环从系统事件队列中抓取属于本程序事件并转化为QEvent对象

十八:事件类图

十九:事件来源分类

基于事件如何被产生与分发,可以把事件分为三类:

1. Spontaneous 事件,由窗口系统产生,它们被放到系统队列中,通过事件循环逐个处理

鼠标,键盘,移动

2. Posted 事件,由Qt或是应用程序产生,它们被Qt组成队列,再通过事件循环处理。update(),paintEvent()

3. Sent  事件,由Qt或是应用程序产生,但它们被直接发送到目标对象。 repaint()

二十:QEvent对象

QEvent对象是所有事件对象的基类,因此有必要了解该对象构成函数:

enum Type :标识事件类型

Type type () const   :调用该函数,返回事件发生事件的类型(该类型为枚举)

二十一:常用的事件类型

通过调用QEvent::type函数可以返回具体事件类型,Qt已经为具体的事件类型事先定义了:

QEvent::MouseButtonPress - 鼠标按下,QMouseEvent。

QEvent::MouseButtonRelease - 鼠标抬起,QMouseEvent。

QEvent::MouseButtonDblClick - 鼠标再次按下

QEvent::MouseMove - 鼠标移动,QMouseEvent。

QEvent::Close - 窗口部件被关闭(永久性地) QCloseEvent。

QEvent::KeyPress - 键按下(举例,包括Shift)QKeyEvent。

QEvent::KeyRelease - 键抬起,QKeyEvent。

QEvent::FocusIn - 窗口部件获得键盘焦点,QFocusEvent。

QEvent::FocusOut - 窗口部件失去键盘焦点,QFocusEvent。

QEvent::Timer - 规则的定时器事件,QTimerEvent。

二十二:常用的Qt事件类

Qt为多数事件定义了特殊的类:

QMouseEvent 鼠标事件
QKeyEvent  键盘事件
QResizeEvent窗体宿放事件
QPaintEvent 窗体重绘事件
QCloseEvent 窗体关闭事件
QFocusEvent 部件获得焦点事件

二十三:事件处理方法

Qt提供了几种处理事件的方法:

重新实现特定的事件处理器
重新实现QObject::event()函数
继承QApplication并重新实现notify()函数

二十四:实现特定的事件处理器

最常见的事件处理办法就是重载象mousePressEvent(), keyPressEvent(), paintEvent() 这样的特定事件处理函数. 以按键事件为例

二十五:QKeyEvent对象

QKeyEvent对象是Qt的键盘事件的包装对象,我们也经常需要捕获键盘事件.下面是该对象主要成员函数:

int key () const
int ascii () const
ButtonState state () const
ButtonState stateAfter () const
bool isAccepted () const
QString text () const
bool isAutoRepeat () const
int count () const
void accept ()

void ignore ()

二十六:Qt键盘常量

为了方便程序员捕获特殊键.qt已经对所有的键盘都进行了宏定义,下面列出常用的键值:

二十七:QMouseEvent对象

在Qt系统中对所有鼠标事件都被封装为QMouseEvent对象,有必要了解下该对象成员函数

Qt::MouseButton button () const

返回发生鼠标事件时按下的鼠标按钮

const QPoint & globalPos () const
返回发生鼠标事件时,鼠标在屏幕上的位置指针
int globalX () const
返回发生鼠标事件时,鼠标在屏幕上x轴位置
int globalY () const
返回发生鼠标事件时,鼠标在屏幕上y轴位置
int x () const
返回发生鼠标事件时,相对与本窗体的x轴位置
int y () const

返回发生鼠标事件时,相对与本窗体的x轴位置

二十八:事件处理器实现

void imageView::keyPressEvent(QKeyEvent * event)
{switch (event->key()) {case Key_Plus:zoomIn();break;case Key_Minus:zoomOut();break;case Key_Left:// … default:QWidget::keyPressEvent(event);}
}

二十九:重载event()函数

通过重载event()函数,我们可以在事件被特定的事件处理函数处理之前(象keyPressEvent())处理它. 比如, 当我们想改变tab键的默认动作时,一般要重载这个函数. 在处理一些不常见的事件当我们重载event()函数时, 需要调用父类的event()函数来处理我们不需要处理或是不清楚如何处理的事件

三十:重载event实现

下面这个例子演示了如何重载event()函数, 改变Tab键的默认动作: (默认的是键盘焦点移动到下一个控件上)

bool CodeEditor::event(QEvent * event)
{if (event->type() == QEvent::KeyPress){QKeyEvent *keyEvent = (QKeyEvent *) event;if (keyEvent->key() == Key_Tab){insertAtCurrentPosition('\t');return true;}}
return QWidget::event(event);
}

三十一:QApplication::notify()

Qt是用QApplication::notify()函数来分发事件的.想要在任何事件过滤器查看任何事件之前先得到这些事件,重载这个函数是唯一的办法. 通常来说事件过滤器更好用一些, 因为不需要去继承QApplication类. 而且可以给QApplication对象安装任意个数的事件过滤器, 相比之下, notify()函数只有一个

三十二:事件过滤器

Qt事件模型中有一项非常强大的功能是一个Object实例可以监视另外一个QObject实列中的事件.实现方法就是在目标对象安装事件过滤器.这样事件到打目标对象之前首先获得该事件.从而起到监视作用

三十三:安装事件过滤器

安装事件过滤器有两个步骤: (假设要用A来监视过滤B的事件)

首先调用B的installEventFilter( const QOject *obj ), 以A的指针作为参数. 这样所有发往B的事件都将先由A的eventFilter()处理.
然后, A要重载QObject::eventFilter()函数, 在eventFilter() 中书写对事件进行处理的代码

三十四:事件过滤器实现

MainWidget::MainWidget()
{  // …CodeEditor * ce = new CodeEditor( this, “code editor”);ce->installEventFilter( this );// …
}
bool MainWidget::eventFilter( QOject * target , QEvent * event )
{if( target == ce ){if( event->type() == QEvent::KeyPress ) {QKeyEvent *ke = (QKeyEvent *) event;if( ke->key() == Key_Tab ){ce->insertAtCurrentPosition('\t');return true;}} }return false;
}

Qt 信号和槽 事件处理相关推荐

  1. 【Qt】Qt信号与槽使用不当,使程序崩溃

    问题描述 跨线程使用Qt信号和槽,信号发送时间间隔小于槽函数处理时间时,造成程序崩溃. 原因分析 跨线程使用Qt信号和槽时,connect默认是QueuedConnection,队列连接方式. 信号传 ...

  2. QT信号与槽——观察者模式——回调函数

    QT信号与槽--观察者模式--回调函数 1.QT信号与槽机制 1.1信号本质 信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时候 Qt 对应的窗口类会发出某个信号.比如 ...

  3. Qt信号与槽传递自定义数据类型——两种解决方法

    Qt信号与槽传递自定义数据类型--两种解决方法 参考文章: (1)Qt信号与槽传递自定义数据类型--两种解决方法 (2)https://www.cnblogs.com/tid-think/p/9300 ...

  4. QT 信号与槽 最简单例子

    QT  信号与槽 最简单例子 main.cpp 和 my_head.h源码: [cpp] view plaincopy #ifndef MY_HEAD_H #define MY_HEAD_H #inc ...

  5. QT信号与槽(自定义带参数的信号)

    关于QT信号与槽的问题其实每个初学QT的人都会遇到,当时我需要做一个带界面的demo,在信号和槽的问题上,我需要的想法是让槽可以有参数的进行操作,但是系统内置的clicked()信号是不含参数的,这对 ...

  6. Qt信号与槽传递QList动态数组

    Qt信号与槽传递QList动态数组 根据实验,测试程序见下: - QString的QList动态数组能够通过signal-slot机制进行传递: - 自定义结构的QList动态数组也能通过signal ...

  7. Qt信号和槽函数连接不成功原因

    Qt信号和槽连接失败原因主要有以下几点: 1.槽函数并没有声明在类的public slots(或private slots或protected slots)里,因此,所想要成为槽函数的那个函数只是普普 ...

  8. qt信号与槽连接的书写规范

    环境 : vs2015 + qt  5.9.9 Qt信号和槽连接失败原因主要有以下几点: 1.槽函数并没有声明在类的public slots(或private slots或protected slot ...

  9. Qt信号与槽的五种连接方式

    qt信号与槽的五种连接方式: 1.默认连接:如果是在同一线程等价于直连,在不同线程等价于队列连接 2.直连:信号在哪,在哪个线程执行(最好只在同一线程中用) 3.队列连接: 槽在哪就在哪个线程执行 ( ...

最新文章

  1. 给力!斩获 GitHub 14000 Star,两周创办开源公司获数百万美元融资
  2. Python语法基础(长期)
  3. 修改win10 默认网卡 --其实就是改网卡接口跃点
  4. 【Java】forward redirect 的差异
  5. 2011下半年信息系统项目管理师考后感
  6. 菜鸟到高手:SQL开发进阶常用精妙语句
  7. 关于广告系统的定向,看这篇就够了
  8. 独立显卡驱动安装不成功解决办法
  9. 目标跟踪入门:使用OpenCV实现质心跟踪
  10. 如何快速获取设备ip地址
  11. 13个可以激励自己的名言
  12. iperf测试工具使用方法
  13. 数据结构 散列表 除留余数法 线性探测法解决冲突
  14. java语言可以编程无人机么_java可以作为第一门编程语言学习吗
  15. oracle数据库测评
  16. f429 discovery开发版 LVGL移植(带操作系统)
  17. 华为云服务器 网站 要备案吗6,云服务器网站需要备案吗
  18. 修改 jq weui cityPicker.js原来的值
  19. crc校验查表法原理
  20. 【无标题】Unknown custom element: <school> - did you register the component correctly? For recursive comp

热门文章

  1. 论文ddl:记录设置绪论从页码一开始
  2. jpeg解码库使用实例
  3. 陆 桂 华 三个字 写个4字一句两句话,表达美好的祝愿
  4. 码住!迟早会写报告!!网络攻击与防御实验指导
  5. swing 文本框回车事件
  6. win10修改线程数量
  7. WordPress代码实现网站地图sitemap的html和xml的方法
  8. python中列表下标_Python中如何在列表中找到某个元素的下标
  9. 微信小程序开发笔记--03
  10. 从苏宁电器到卡巴斯基第16篇:我在苏宁电器当营业员 VII