【Qt】常用控件|消息事件机制

举报
xcc-2022 发表于 2022/11/28 18:56:58 2022/11/28
【摘要】 6、容器效果:tacked Widget 页面切换需要我们自己去实现,一般使用按钮点击的时候切换setCurrentIndex 方式切换到第几页,序号从0开始 7、常用控件Qt为我们应用程序界面开发提供的一系列的控件,下面我们介绍两种最常用一些控件,所有控件的使用方法我们都可以通过帮助文档获取。 7.1 QLabel控件使用QLabel是我们最常用的控件之一,其功能很强大,我们可以用来...

6、容器

效果:

动画23

tacked Widget 页面切换需要我们自己去实现,一般使用按钮点击的时候切换
setCurrentIndex 方式切换到第几页,序号从0开始

7、常用控件

Qt为我们应用程序界面开发提供的一系列的控件,下面我们介绍两种最常用一些控件,所有控件的使用方法我们都可以通过帮助文档获取。

7.1 QLabel控件使用

QLabel是我们最常用的控件之一,其功能很强大,我们可以用来显示文本,图片和动画等。

7.1.1 显示文字 (普通文本、html)

通过QLabel类的setText函数设置显示的内容:

void setText(const QString &)

  • 可以显示普通文本字符串
QLable *label = new QLable;
label->setText(“Hello, World!);
  • 可以显示HTML格式的字符串

比如显示一个链接:

QLabel * label = new QLabel(this);
label ->setText("Hello, World");
label ->setText("<h1><a href=\"https://www.baidu.com\">百度一下</a></h1>");
label ->setOpenExternalLinks(true);

其中setOpenExternalLinks()函数是用来设置用户点击链接之后是否自动打开链接,如果参数指定为true则会自动打开。

7.1.2 显示图片

可以使用QLabel的成员函数setPixmap设置图片

void setPixmap(const QPixmap &)

首先定义QPixmap对象

QPixmap pixmap;

然后加载图片

pixmap.load(":/Image/boat.jpg");

最后将图片设置到QLabel中

QLabel *label = new QLabel;

label.setPixmap(pixmap);

7.1.3 显示动画

可以使用QLabel 的成员函数setMovie加载动画,可以播放gif格式的文件

void setMovie(QMovie * movie)

首先定义QMovied对象,并初始化:

QMovie *movie = new QMovie(":/Mario.gif");

播放加载的动画:

movie->start();

将动画设置到QLabel中:

QLabel *label = new QLabel;

label->setMovie(movie);

7.2 QLineEdit

Qt提供的单行文本编辑框。

7.2.1 设置/获取内容

获取编辑框内容使用text(),函数声明如下:

QString   text() const

设置编辑框内容

void setText(const QString &)

7.2.2 设置显示模式

使用QLineEdit类的setEchoMode () 函数设置文本的显示模式,函数声明:

void setEchoMode(EchoMode mode)

EchoMode是一个枚举类型,一共定义了四种显示模式:

QLineEdit::Normal 模式显示方式,按照输入的内容显示。

QLineEdit::NoEcho 不显示任何内容,此模式下无法看到用户的输入。

QLineEdit::Password 密码模式,输入的字符会根据平台转换为特殊字符。

QLineEdit::PasswordEchoOnEdit 编辑时显示字符否则显示字符作为密码。

另外,我们再使用QLineEdit显示文本的时候,希望在左侧留出一段空白的区域,那么,就可以使用QLineEdit给我们提供的setTextMargins函数:

void setTextMargins(int left, int top, int right, int bottom)

用此函数可以指定显示的文本与输入框上下左右边界的间隔的像素数。

7.3 自定义控件

在搭建Qt窗口界面的时候,在一个项目中很多窗口,或者是窗口中的某个模块会被经常性的重复使用。一般遇到这种情况我们都会将这个窗口或者模块拿出来做成一个独立的窗口类,以备以后重复使用。

在使用Qt的ui文件搭建界面的时候,工具栏栏中只为我们提供了标准的窗口控件,如果我们想使用自定义控件怎么办?

例如:我们从QWidget派生出一个类SmallWidget,实现了一个自定窗口,

// smallwidget.h
class SmallWidget : public QWidget

{
  Q_OBJECT

public:

  explicit SmallWidget(QWidget *parent = 0);

signals:

public slots:

private:

  QSpinBox* spin;

  QSlider* slider;

};

// smallwidget.cpp
SmallWidget::**SmallWidget**(QWidget *parent) : QWidget(parent)

{
  spin = new QSpinBox(this);
  slider = new QSlider(Qt::Horizontal, this);

  // 创建布局对象
  QHBoxLayout* layout = new QHBoxLayout;
  // 将控件添加到布局中
  layout->addWidget(spin);
  layout->addWidget(slider);
  // 将布局设置到窗口中
  setLayout(layout);

  // 添加消息响应
  connect(spin, 
      static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
     slider, &QSlider::setValue);
  connect(slider, &QSlider::valueChanged, 
      spin, &QSpinBox::setValue);

}

img

那么这个SmallWidget可以作为独立的窗口显示,也可以作为一个控件来使用:

打开Qt的.ui文件,因为SmallWidget是派生自Qwidget类,所以需要在ui文件中先放入一个QWidget控件, 然后再上边鼠标右键

img

弹出提升窗口部件对话框

img

添加要提升的类的名字,然后选择 添加

img

添加之后,类名会显示到上边的列表框中,然后单击提升按钮,完成操作.

我们可以看到, 这个窗口对应的类从原来的QWidget变成了SmallWidget

img

再次运行程序,这个widget_3中就能显示出我们自定义的窗口了.

8、消息事件机制

8.1 事件

事件(event)是由系统或者 Qt 应用程序本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。01_事件分发

8.2 事件处理函数

在所有组件的父类QWidget中,定义了很多事件处理的函数,如

-  keyPressEvent():键盘按键按下事件
-  keyReleaseEvent():键盘按键松开事件
-  mouseDoubleClickEvent():鼠标双击事件
-  mouseMoveEvent():鼠标移动事件
-  mousePressEvent():鼠标按键按下事件
-  mouseReleaseEvent() :鼠标按键松开事件

这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。下面来看一个例子:

我们先设置ui文件,给一个label控件,并设置带边框属性

image-20221031085046388

然后再新建个c++文件Mylabel重写label,再回去把ui文件的label对象类提升为Mylabel

按键处理函数代码:

protected:
    //重写鼠标按键处理函数
    void mousePressEvent(QMouseEvent *ev);
void Mylabel::mousePressEvent(QMouseEvent *ev)
{
    //输出鼠标事件一些信息
    //获取坐标
    int x = ev->x();
    int y = ev->y();
    //Qstring("%d,%d",x,y);
    QString str = QString("[%1,%2]").arg(x).arg(y) ;
    this->setText(str);
}
image-20221031093938101

效果已经出来了,样式需要完善,label是可以使用html语言的,比如居中(center)、标题(h1)、链接等;

我们在来添加获取鼠标按键

 //获取鼠标按键
    Qt::MouseButton btn = ev->button();
    QString strButton = "";
    if(btn == Qt::LeftButton)
    {
           strButton = "LeftButton"  ;
    }
    if(btn == Qt::RightButton)
    {
           strButton = "RightButton"  ;
    }
    if(btn == Qt::MidButton)
    {
           strButton = "MidButton"  ;
    }
 QString str = QString("<h1><center>[%1,%2,%3]</center></h1>").arg(x).arg(y).arg(strButton) ;

EventLabel继承了QLabel,覆盖了mousePressEvent()、mouseMoveEvent()和MouseReleaseEvent()三个函数。我们并没有添加什么功能,只是在鼠标按下(press)、鼠标移动(move)和鼠标释放(release)的时候,把当前鼠标的坐标值显示在这个Label上面。由于QLabel是支持 HTML 代码的,因此我们直接使用了 HTML 代码来格式化文字。

QString的arg()函数可以自动替换掉QString中出现的占位符。其占位符以 % 开始,后面是占位符的位置,例如 %1,%2 这种。

QString("[%1, %2]").arg(x).arg(y);

语句将会使用x替换 %1,y替换 %2,因此,生成的QString为[x, y]。

image-20221031095435159

鼠标移动函数代码:

 //重写鼠标移动的处理函数
    void mouseMoveEvent(QMouseEvent *ev) ;

我们把上面的鼠标按键定义代码Ctrl+v下来,运行之后,发现有问题,首先我们按动鼠标移动,没有显示是左键还是右键,这是因为,函数是根据位操作进行识别的,我们需要使用位判断,这里就不能是==,还有长按移动鼠标是一个连续的动作,不再是单击按键,这里也是需要使用另一个按键Buttons有个s,也是按位或

image-20221031100536151

 Qt::MouseButtons btns = ev->buttons();
QString strButton = "";
    if(btns & Qt::LeftButton)
    {
           strButton = "LeftButton"  ;
    }

从mouseButton->改为mouseButtons,button->改为buttons,==->改为&

为了可以同时按多个按键,可以使用+=

if(btns & Qt::LeftButton)
    {
           strButton += "LeftButton"  ;
    }

并且为了分清楚是按键还是移动,添加打印信息,如果是按键打印press和如果是移动鼠标就打印move

动画24

为什么要点击鼠标之后才能在mouseMoveEvent()函数中显示鼠标坐标值?

这是因为QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标。只有鼠标被追踪时,mouseMoveEvent()才会发出。如果mouseTracking是 false(默认即是),组件在至少一次鼠标点击之后,才能够被追踪,也就是能够发出mouseMoveEvent()事件。如果mouseTracking为 true,则mouseMoveEvent()直接可以被发出。

 this->setMouseTracking(true);

8.3 事件分发函数event

事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。

 bool event(QEvent *e);

如上所述,event()函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。

事件先会到达窗口的event函数 , event函数 返回值:true表示该事件得到处理,如果是false,没被处理,事件会继续传递到父窗口

if(e->type()==QEvent::MouseMove)
{
	this->mouseMoveEvent(static_cast<QMouseEvent *>(e));//强转
	return true;
}

其他没有被处理的事件,就使用父类的eventreturn QLabel::event(e);

bool Mylabel::event(QEvent *e)
{
    if(e->type()==QEvent::MouseMove)
    {
        //注释了下面一行,将不再触发鼠标移动事件
      this->mouseMoveEvent(static_cast<QMouseEvent*>(e) );
        return true;
    }
    return QLabel::event(e);
}

Mylabel是一个普通的QWidget子类。我们重写了它的event()函数,这个函数有一个QEvent对象作为参数,也就是需要转发的事件对象。函数返回值是 bool 类型。

  • 如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。

  • 在event()函数中,调用事件对象的accept()和ignore()函数是没有作用的,不会影响到事件的传播。

8.4 事件过滤器

有时候,对象需要查看、甚至要拦截发送到另外对象的事件。例如,对话框可能想要拦截按键事件,不让别的组件接收到;或者要修改回车键的默认处理。

通过前面的章节,我们已经知道,Qt 创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发。显然,我们可以在event()函数中实现拦截的操作。由于event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的:事件过滤器。

QObject有一个eventFilter()函数,用于建立事件过滤器。函数原型如下:

virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );

这个函数正如其名字显示的那样,是一个“事件过滤器”。所谓事件过滤器,可以理解成一种过滤代码。事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false。==事件过滤器的调用时间是目标对象==(也就是参数里面的watched对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。

事件过滤器的使用

1 窗口调用installEventFilter来安装一个事件过滤器

2 参数是一个事件过滤器对象QObject,该对象的类要重写eventFilter的函数
事件过滤的时候,事件会先到达事件过滤器的eventFilter函数
对象可以使用自己作为自己的过滤器

this->installEventFilter(this)

拦截鼠标移动事件:

bool MyLabel::eventFilter(QObject *watched, QEvent *event)
		{
			if(event->type()==QEvent::MouseMove)
			{
				//返回true 表示拦截该事件
				return true;
			}
			return false;
		}

8.5 定时器事件

闹钟就是定时器,闹钟响了就是定时器事件:timerEvent

protected:
    void timerEvent(QTimerEvent *event);
 //创建定时器
    startTimer(1000);//1秒响
void Widget::timerEvent(QTimerEvent *event)
{
    static int num = 1;
    qDebug()<<num++;
}
动画25

每过一秒都会响一次,为了实现下面的,我们再编写代码

image-20221031175033890

打开ui设计:添加两个按键,和一个LCD Number,把对象名改为start和stop

image-20221031175401533

然后我们点击按键转到槽,在start中我们创建一个定时器startTimer(1000),在stop中我们需要杀死这个killTimer(int *id*)id,我们需要在头文件定义一个变量,接受定时器的idint mTimerId;

void Widget::on_pushButton_start_clicked()
{
   this->mTimerId= startTimer(1000);
}

void Widget::on_pushButton_stop_clicked()
{
    //停止计时器
    killTimer(mTimerId);
}

我们需要把计时的数字显示到lcd中,需要调用lcdNumber: this->ui->lcdNumber->display(num++)

动画26

我们已经实现了一个计时器,再实现一个只需把上面的ui设计Ctrl+V一下;为了区分不重复问题后面都标识个2

修改完槽和信后之后,还有就是lcd显示,需要区分定时器的id,找到这个id并显示在lcd中。

当你以为大公告成的时候,运行一看,会出现一点小问题,就是第二次运行start的时候,第一个定时器会出问题,这就是初始化的问题了;所以我们需要初始化和清零

//widget.h
private:
    int mTimerId;
     int mTimerId2;

private slots:
    void on_pushButton_start_clicked();
    void on_pushButton_stop_clicked();
    void on_pushButton_start_2_clicked();
    void on_pushButton_stop_2_clicked();
//widget.cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget),
      mTimerId(0),
      mTimerId2(0)
{
    ui->setupUi(this);
}

void Widget::timerEvent(QTimerEvent *event)
{
    if(event->timerId()==this->mTimerId)
    {
        static int num = 1;
       this->ui->lcdNumber->display(num++);
    }
    if(event->timerId()==this->mTimerId2)
    {
        static int num2= 1;
       this->ui->lcdNumber_2->display(num2++);
    }

}
Widget::~Widget()
{
    delete ui;
}
void Widget::on_pushButton_start_clicked()
{
   this->mTimerId= startTimer(1000);
}

void Widget::on_pushButton_stop_clicked()
{
    //停止计时器
    killTimer(mTimerId);
    this->mTimerId=0;
}

void Widget::on_pushButton_start_2_clicked()
{
    this->mTimerId2= startTimer(100);
}

void Widget::on_pushButton_stop_2_clicked()
{
     killTimer(mTimerId2);
     this->mTimerId2=0;
}

动画27

另一种定时器
使用QTimer 的方式创建定时器
通过关注信号 timeout 来接受定时器到时间的信号
通过start 来启动定时器,参数是信号触发间隔的毫秒
通过stop来停止定时器

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。