3

I have a qslider to control the zooming of a map like this: connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int))); However, because this online-map response relatively slow.

I found that the qslider's response also becomes very slow which means when you slide the slider, it's position won't change until suddenly it jump to the position where you release your mouse.

How could I solve this?

Nyaruko
  • 4,329
  • 9
  • 54
  • 105
  • I've seen things like this happen before because the updating of the widget it's controlling causes the whole ui to wait for it to finish. You need to. Are it so the updating widget doesn't cause everything else to stall. – will Oct 14 '14 at 22:39
  • you can try to call connect with `Qt::QueuedConnection` argument. i'm not 100% sure, but if this will put new event to the loop and continue thread execution, then your problem will be solved – Max Go Oct 14 '14 at 22:55
  • connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int)), Qt::QueuedConnection); – Max Go Oct 14 '14 at 22:58
  • @N1ghtLight The two objects are in the same thread (main thread). So using `Qt::QueuedConnection` is useless. – Nejat Oct 15 '14 at 04:58
  • @Nejat, I described what I meant in my answer. And it works differently even in the same thread. – Max Go Oct 15 '14 at 07:34
  • @N1ghtLight What Nejat meant by his comment is that `Qt::QueuedConnection` will make no difference as the event loop is being blocked by a long running task. It doesn't matter if the task will be run directly when the signal is emitted, or after going back to the event loop. UI will still freeze for a moment. – thuga Oct 15 '14 at 10:11
  • @thuga, I think that Nejat meant just that by Qt documentation Qt::QueuedConnection should be used for objects in different threads and if objects in one thread, then Qt::QueuedConnection is useless and works the same as Qt::DirectConnection. My point was to try to not use DirectConnection and try to postpone processing of valueChanged(int) signal. Theoretically, this should allow the UI control to be updated and only after that slot will be processed. – Max Go Oct 15 '14 at 10:29
  • @N1ghtLight But it doesn't matter if the UI is updated before the long task is executed, as the UI will still freeze. There is no escaping that without moving the long task to a new thread. – thuga Oct 15 '14 at 10:31
  • @N1ghtLight And I don't think it states anywhere in the docs that *Qt::QueuedConnection should be used for objects in different threads and if objects in one thread, then Qt::QueuedConnection is useless and works the same as Qt::DirectConnection*. – thuga Oct 15 '14 at 10:35
  • @thuga, that's the point, documentation doesn't provide any info how QueuedConnection works for object in the same thread. – Max Go Oct 15 '14 at 10:37
  • @thuga, some visual difference exist. If slider step will be pretty big, then when queued connection is used, user will see the slider position update first and then freezing while map not get updated. As I already wrote, author of the question should decide what he really needs here and it's not clear why he can't run this long running job to not affect execution of main UI thread. – Max Go Oct 15 '14 at 10:41
  • @N1ghtLight The documentation is pretty clear how `Qt::QueuedConnection` works even if the receiver is in the same thread as the emitter. [*The slot is invoked when control returns to the event loop of the receiver's thread.*](http://qt-project.org/doc/qt-5/qt.html#ConnectionType-enum). The visual difference is only apparent if the slider is moved once. And the visual difference wouldn't fix the problem, the UI would still freeze. – thuga Oct 15 '14 at 10:52

1 Answers1

0

One possible solution to delay processing of your signal is to connect it with slot by using Qt::QueuedConnection.

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

With Qt::QueuedConnection emitted valueChanged signal event will be not processed at the time of generation, as it happens with directly connected signals. Event will be added to the event loop queue. This is how Qt::QueuedConnection is implemented inside Qt.

Specially for Nejat to test this approach it's possible to use following code:

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();
}

If you run code snippet above you will get following output on button press:

signalReceived 
signalReceivedQueued 

Queued signal is emitted first, but received last. And this can be used in your case to prioritize processing of emitted signals.

However most of all using of queued connection will not help you, because user emits slider event too frequently and UI will freeze in any case. So, I can suggest following:

  1. Determine why exactly UI is freezes, what part of code freezing it.
  2. Try to avoid freezing by asynchronous calls or by moving logic into separate thread, or by using QtConcurrent
  3. If you really can't control the way how map is scaled in your webpage, try to ignore all events generated by the QSlider and react only on last generated in 500 ms interval, for example.
Max Go
  • 2,092
  • 1
  • 16
  • 26
  • Why do you offer a solution which you state will not help the OP? You go through all the trouble of explaining how it works and posting a SSCCE just to say that it won't work in the OP's case. You should at least state this at the very beginning, before the OP starts implementing a solution which might not help him at all. – thuga Oct 15 '14 at 08:57
  • @thuga, I've wrote "possible" and "most of all". Because OP didn't provide exact details how map is implemented, all this can be applied. Also my explanation is answer to Nejat's comment. It's not obvious thing. – Max Go Oct 15 '14 at 09:08
  • @thuga, what is needed from OP is just to add ", Qt::QueuedConnection" text in correct place and test. That's it. Not a lot pain to try and not get it work :) – Max Go Oct 15 '14 at 09:14
  • Hi, thanks for the answer, just want to know, if the signal and slot are in the same thread and several signals are emitted in the same time while the slot call back could take very long time to execute. What will happen? Will this be different in a cross-thread case? To make it more clear:connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int)));if more than one valueChanged(int) is emitted simutaneously, and the SetZoom(int) takes several seconds to finish one run. What will happen? – Nyaruko Oct 15 '14 at 20:43
  • @Nyaruko, all Qt ui (your MainWindow) is working in one main application thread which has events queue. If you process your signals in the same thread, then when you change slider value the `SetZoom` slot is invoked just in the moment when QSlider generates `valueChanged` signal and MainWindow doesn't retrieve new events from the OS, because main thread is busy inside `SetZoom` method. While application ui is hanging inside `SetZoom` method, you are generating new mouse events that are goes to the OS events queue. – Max Go Oct 15 '14 at 22:15
  • Once `SetZoom` method is finished, ui main thread retrieve new events from OS events queue and new mouse event with delay generates new `valueChanged` signal. Both OS and internal app event queues have maximum limit of not processed events. So, new generated events can be ignored, if the queue is full. – Max Go Oct 15 '14 at 22:19
  • If you process your `SetZoom` in separate thread, then execution doesn't block your main thread event loop and all UI events processed in time and you don't experience glitches. Hope this helps. – Max Go Oct 15 '14 at 22:21