I try to use QSlider, QTimer and valueChanged() signal together but I need to differentiate whether value of slider is changed by user or timer tick. How can I accomplish this? By below code I try to decide when slider is changed by timer but I could not decide when signal changed by user. ( Moreover it is a OpenGL animation and slider behaves like timeline evenif timeline changes value every second animation plays 30 Hz therefore if user want to use slider for making animation forward or reverse I need to check signal of slider. However slider has one seconds ticks from timer)
connect(timer, SIGNAL(timeout()), this,SLOT(timerTick()));
connect(slider, SIGNAL(valueChanged(int)),this, SLOT(sliderChange()));
void MainWindow::sliderChange()
{
if (userInterrupt)
{
.. Call Function A
}
}
void MainWindow::timerTick()
{
slider->setValue(slider.value()+1);
userInterrupt=false;
}
EDIT : sender is added but due recursion it is fail to run clearly. Still I could not decide signal
connect(timer, SIGNAL(timeout()), this,SLOT(sliderChange()));
connect(slider, SIGNAL(valueChanged(int)),this, SLOT(sliderChange()));
void MainWindow::sliderChange()
{
QObject * obj =sender();
if (obj==slider)
{
.. Call Function A
}else
{
slider->setValue(slider.value()+1);
}
}
You can use QObject::sender to get a pointer to the QObject that emitted the signal.
After I try sender and blocksignals, I could not manage to solve the issue. Therefore, I find out another more primitive solution on slider handler like below. However, still I think that sender and blocksignal is better way to solve and try to do in that way also, until that time below code solve my issue. Basically, I use different signals for release, click and drag on slider.
connect(timer, SIGNAL(timeout()), this,SLOT(timerTick()));
connect(slider, SIGNAL(valueChanged(int)),this, SLOT(sliderChange()));
connect(slider, SIGNAL(sliderReleased()),this, SLOT(userRelease()));
connect(slider, SIGNAL(sliderPressed()),this, SLOT(userClick()));
void MainWindow::sliderChange()
{
// Action when it is changes
// in my case calculate time where animation will resume on
// but do not show any data on animation until release
// release slot will deal with it
}
void MainWindow::userClick()
{
// Actions when mouse button is pressed
// in my case pause animation
}
void MainWindow::userRelease()
{
// Action when mouse button is released
// in my case resume showing animation with slider value
}
void MainWindow::timerTick()
{
slider->setValue(slider.value()+1);
userInterrupt=false;
}
Related
I try to use a QProgressDialog to give the user some information on the progress of a long task, while allowing him to cancel this task.
Basically, I have a QDialog with a button Compute. By clicking on it, a time consuming method is called on a member of my QDialog's parent. This method takes a callback to tell the caller the progress of work.
The problem is that the progress dialog takes some time before appearing, and doesn't take into account immediately a click on its Cancel button.
It's clear that there is a glitch in my code, but I'm not accustomed with Qt, and I tried many things. I probably need a separate thread.
An extract of my code:
void MyDialog::callbackFunction(int value, void * objPtr) {
((QProgressDialog *)(objPtr))->setValue(value);
QCoreApplication::processEvents();
}
void MyDialog::on_mComputeBtn_clicked()
{
Compute();
}
void MyDialog::Compute()
{
QProgressDialog progressDialog("Optimization in progress...", "Cancel", 0, 100, this);
progressDialog.setMinimumDuration(500); // 500 ms
progressDialog.setWindowModality(Qt::WindowModal);
progressDialog.setValue(0);
connect(&progressDialog, SIGNAL(canceled()), this, SLOT(Cancel()));
QCoreApplication::processEvents();
parentMember->LongComputation(callbackFunction);
// probably useless
progressDialog.reset();
progressDialog.hide();
QCoreApplication::processEvents();
}
The dialog is not appearing immediately because you set a minimum duration of 500ms. Set it to 0 to make the dialog show immediately on progress change or call its show function manually.
In order to make the cancel button work, move your long computation to another thread ( e.g. use QThread or std::async ) and let your main event loop continue its execution.
Actually there are a lot of problems with your code but these two points should point you to the right direction. In my experience every manual call to processEvents is a big code smell.
What you do is trying to use modal paradigm of QProgressdialog, not letting main event pump to fire, also you set a 0.5 s timeout, minimal duration would ensure the pause. Maybe modeless variant is more appropriate for your case.
if process got discrete steps, you can do it without separate thread
class MyTask : public QObject
{
Q_OBJECT
public:
explicit MyTask(QObject *parent = 0);
signals:
public slots:
void perform();
void cancel();
private:
int steps;
QProgressDialog *pd;
QTimer *t;
};
...
void MyDialog::on_mComputeBtn_clicked()
{
myTask = new MyTask;
}
...
MyTask::MyTask(QObject *parent) :
QObject(parent), steps(0)
{
pd = new QProgressDialog("Task in progress.", "Cancel", 0, 100000);
connect(pd, SIGNAL(canceled()), this, SLOT(cancel()));
t = new QTimer(this);
connect(t, SIGNAL(timeout()), this, SLOT(perform()));
t->start(0);
}
void MyTask::perform()
{
pd->setValue(steps);
//... perform one percent of the operation
steps++;
if (steps > pd->maximum())
t->stop();
}
void MyTask::cancel()
{
t->stop();
//... cleanup
}
QThread example existed here: Qt 5 : update QProgressBar during QThread work via signal
I have a Ubuntu , and i'am working with IDE QT on C++ .
I will to pause and resume the Qtimer , for exampe :
void Ordonnancer_les_taches::on_pushButton_clicked()
{
connect(&dataTimer, SIGNAL(timeout()), this, SLOT(l_odonnancement()));
dataTimer.start(5000);
}
How to Pause and how Restart ?
give me an exmple
Since there is no dedicated method to achieve this behaviour, you could do something like this (you may move it to a subclass PausableTime or so):
void pause() {
int remaining = dataTimer.remainingTime();
dataTimer.stop();
dataTimer.setInterval(remaining);
}
void resume() {
dataTimer.start();
}
Of course you then need to adjust the interval in your timeout slot again.
I have one problem which I can't solve using the Internet. I have label and I set pixmap on it. I put it on main window (widget) where is button (QPushButton) too. I want to do that:
If I click on the button then on this pixmap will be drawn circles continuously
If I click this button for second then drawing must be stopped by function pause()
The second one is easy, it's empty slot:
void pause() {}
But at first I've tried to use loop
while(true)
draw();
but it crashed a program (loop).
Any idea how to solve it?
You should never block the main thread. This will cause the OS to consider your application has hanged. In fact it is a good practice to move any code, whose execution takes more than 50 milliseconds to another thread to keep the main thread responsive, especially in the case of Qt, where it is also the GUI thread.
You should use an event driven approach, which will not block the thread.
class YourClass : public QObject { // QObject or derived
Q_OBJECT
public:
YourClass() { connect(&timer, &Timer::timeout, this, &YourClass::draw); }
public slots:
void start() { timer.start(33); }
void pause() { timer.stop(); }
private:
QTimer timer;
void draw() { ... }
};
When start() is invoked, draw() will be called every 33 miliseconds. pause() will effectively stop that until start() is invoked again. You can control the rate at which draw() is invoked by adjusting the timer's interval, by default it is 0, which in the case of drawing is overkill, you should adjust for a desired framers per second. In the example above, the it is 33 milliseconds, or roughly 30 FPS.
You should then call draw() with some time interval, instead of halting the whole GUI thread with it.
For that, there's QTimer:
QTimer timer; // should be a member, a pointer optionally - you then do new Qtimer(this);
connect(&timer, &QTimer::timeout, draw);
timer.start(500); // in milliseconds
// assuming you are calling this from member function of QObject-deriving class
// and draw is a non-member function
If you know to do the connections, you can connect it to anything...
The same can be done with QThread and putting it to sleep in that loop.
Anyhow, I don't get how an empty pause() stops the drawing. Aren't you halting your application again? Just do timer.stop();.
i have the same problem like QMediaPlayer positionChanged(). Sound inteerupts on slider updating
I use QMediayPlayer and everytime when the signal positionChanged() is emitted to update my slider position and i set a new value to the slider, the sound interrupts for a moment.
This is in the Constructor:
soundfile = new QMediaPlayer(this, QMediaPlayer::LowLatency); //soundfile is a pointer of a QMediaPlayer Object
QObject::connect(soundfile, SIGNAL(positionChanged(qint64)), this, SLOT(changedPosition(qint64)));
This is the slot function:
void Soundfile::changedPosition(qint64 p) {
QTime time(0,0,0,0);
time = time.addMSecs(soundfile->position());
if(p != 0) recordSlider->setValue(p); //THIS IS THE LINE, WHERE IT INTERRUPTS
changeRecordTime(QString::number(p));
recordPositionLabel->setText("Aktuelle Zeit: " + time.toString());
}
recordSlider is a QSlider.
If i comment out the line with setValue, all works fine.
Does anyone have an idea?
I believe that the problem is: when the media player emits the SIGNAL the SLOT is called, and when you use setValue inside your function, setValue emits the SIGNAL again, and the process happens again.
In order to solve that problem I disabled the slider tracking and move the position using setSliderPosition.
Example:
slider->setTracking(false);
slider->setSliderPosition(pos);
I currently have a method which is as follows
void SomeMethod(int a)
{
//Delay for one sec.
timer->start(1000);
//After one sec
SomeOtherFunction(a);
}
This method is actually a slot that is attached to a signal. I would like to add a delay of one sec using Qtimer.However I am not sure on how to accomplish this. Since the timer triggers a signal when its finished and the signal would need to be attached to another method that does not take in any parameters. Any suggestion on how I could accomplish this task.?
Update :
The signal will be called multiple times in a second and the delay will be for a second. My issue here is passing a parameter to the slot attached to timeout() signal of a timer.
My last approach would be to store the value in a memeber variable of a class and then use a mutex to protect it from being changed while the variable is being used .however I am looking for simpler methods here.
Actually, there is a much more elegant solution to your question that doesn't require member variables or queues. With Qt 5.4 and C++11 you can run a Lambda expression right from the QTimer::singleShot(..) method! If you are using Qt 5.0 - 5.3 you can use the connect method to connect the QTimer's timeout signal to a Lambda expression that will call the method that needs to be delayed with the appropriate parameter.
Edit: With the Qt 5.4 release it's just one line of code!
Qt 5.4 (and later)
void MyClass::SomeMethod(int a) {
QTimer::singleShot(1000, []() { SomeOtherFunction(a); } );
}
Qt 5.0 - 5.3
void MyClass::SomeMethod(int a) {
QTimer *timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [=]() {
SomeOtherFunction(a);
timer->deleteLater();
} );
timer->start(1000);
}
I'm a bit confused by the way you phrase your question, but if you're asking how to get the timer's timeout() signal to call a function with a parameter, then you can create a separate slot to receive the timeout and then call the function you want. Something like this: -
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent);
public slots:
void TimerHandlerFunction();
void SomeMethod(int a);
private:
int m_a;
QTimer m_timer;
};
Implementation: -
MyClass::MyClass(QObject *parent) : QObject(parent)
{
// Connect the timer's timeout to our TimerHandlerFunction()
connect(&m_timer, SIGNAL(timeout()), this, SLOT(TimerHandlerFunction()));
}
void MyClass::SomeMethod(int a)
{
m_a = a; // Store the value to pass later
m_timer.setSingleShot(true); // If you only want it to fire once
m_timer.start(1000);
}
void MyClass::TimerHandlerFunction()
{
SomeOtherFunction(m_a);
}
Note that the QObject class actually has a timer that you can use by calling startTimer(), so you don't actually need to use a separate QTimer object here. It is included here to try to keep the example code close to the question.
If you are calling SomeMethod multiple times per second and the delay is always constant, you could put the parameter a to a QQueue and create a single shot timer for calling SomeOtherFunction, which gets the parameter from the QQueue.
void SomeClass::SomeMethod(int a)
{
queue.enqueue(a);
QTimer::singleShot(1000, this, SLOT(SomeOtherFunction()));
}
void SomeClass::SomeOtherFunction()
{
int a = queue.dequeue();
// do something with a
}
That doesn't work because QTimer::start is not blocking.
You should start the timer with QTimer::singleShot and connect it to a slot which will get executed after the QTimer times out.