QT QSlider不平滑

3
我有一个 qslider 用于控制地图的缩放,代码如下: connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int))); 但是因为这个在线地图相应比较慢,
我发现当在线地图响应较慢时,qslider 的响应也变得非常缓慢。也就是说,当你滑动滑块时,它的位置不会改变,直到鼠标松开后突然跳到新的位置。
怎样才能解决这个问题?

我之前曾经见过类似的事情发生,因为控制它的小部件的更新会导致整个ui等待其完成。你需要这样做,以使更新小部件不会导致其他所有操作都停滞。 - will
你可以尝试使用Qt::QueuedConnection参数调用connect函数。虽然我不是100%确定,但如果这样做会将新事件放入循环并继续线程执行,那么你的问题就会得到解决。 - Max Go
connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int)), Qt::QueuedConnection);连接(ui->zoomSlider,SIGNAL(valueChanged(int)),ui->map,SLOT(SetZoom(int)),Qt::QueuedConnection); - Max Go
@N1ghtLight 这两个对象在同一个线程(主线程)中。因此使用 Qt::QueuedConnection 是无用的。 - Nejat
@thuga,太棒了,谢谢你提供的参考! :) - Max Go
显示剩余8条评论
1个回答

0

延迟处理您的信号的一种可能解决方案是使用Qt::QueuedConnection将其连接到插槽。

connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int)), Qt::QueuedConnection);

使用Qt::QueuedConnection发出的valueChanged信号事件不会在生成时被处理,就像使用directly连接的信号一样。事件将被添加到事件循环队列中。这就是Qt::QueuedConnection在Qt内部的实现方式。

专门为Nejat设计测试这种方法,可以使用以下代码:

MainWindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void signalReceived();
    void signalReceivedQueued();

    void buttonPressed();

signals:

    void directConnectedSignal();
    void queuedConnectedSignal();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

MainWindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    connect(this, SIGNAL(directConnectedSignal()), SLOT(signalReceived()), Qt::DirectConnection);
    connect(this, SIGNAL(queuedConnectedSignal()), SLOT(signalReceivedQueued()), Qt::QueuedConnection);

    connect(ui->pushButton, SIGNAL(pressed()), SLOT(buttonPressed()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::signalReceived()
{
    qDebug() << "signalReceived";
}

void MainWindow::signalReceivedQueued()
{
    qDebug() << "signalReceivedQueued";
}

void MainWindow::buttonPressed()
{
    emit queuedConnectedSignal();
    emit directConnectedSignal();
}

如果您运行上面的代码片段,当按下按钮时,您将获得以下输出:
signalReceived 
signalReceivedQueued 

排队信号首先发出,但最后接收。这可以在您的情况下用于优先处理发出的信号。

然而,大多数情况下使用排队连接都无法帮助您,因为用户会过于频繁地发出滑块事件,UI 无论如何都会冻结。所以,我建议以下操作:

  1. 确定 UI 冻结的原因,哪部分代码导致了它。
  2. 尝试通过异步调用、将逻辑移动到单独的线程中或使用 QtConcurrent 来避免冻结。
  3. 如果您真的无法控制网页中地图缩放的方式,请尝试忽略 QSlider 生成的所有事件,并仅在 500 毫秒间隔内对最后一个事件做出反应。

@thuga,我写了“可能”和“最重要的是”。因为OP没有提供关于地图实现方式的精确细节,所以所有这些都是可以应用的。此外,我的解释也是对Nejat评论的回答。这并不是一个显而易见的事情。 - Max Go
嗨,感谢您的回答,我只是想知道,如果信号和槽在同一个线程中,并且在同一时间发出多个信号,而槽回调可能需要很长时间才能执行。会发生什么?在跨线程的情况下是否会有所不同?为了更清楚地说明:connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int)));如果同时发出多个valueChanged(int)信号,并且SetZoom(int)需要几秒钟才能完成一次运行。会发生什么? - Nyaruko
@Nyaruko,所有的Qt UI(您的MainWindow)都在一个主应用程序线程中工作,该线程具有事件队列。如果您在同一线程中处理信号,则当您更改滑块值时,“SetZoom”插槽会在QSlider生成“valueChanged”信号的那一刻被调用,并且MainWindow不会从操作系统检索新事件,因为主线程正忙于“SetZoom”方法内部。当应用程序UI在“SetZoom”方法内挂起时,您正在生成新的鼠标事件,这些事件将进入操作系统事件队列。 - Max Go
一旦SetZoom方法完成,UI主线程从操作系统事件队列中检索新事件,并且带有延迟的新鼠标事件生成新的valueChanged信号。操作系统和内部应用程序事件队列都有未处理事件的最大限制。因此,如果队列已满,则可以忽略新生成的事件。 - Max Go
如果您将 SetZoom 放在单独的线程中处理,那么执行过程不会阻塞主线程事件循环,并且所有 UI 事件都能够及时处理,您就不会遇到错误。希望这可以帮助您。 - Max Go
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接