QMediaPlayer: Sound interrupts when positionChanged() is emitted - c++

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

Related

Qt show() executed after function

I would like to show a label and execute a function after displaying the label. Unfortunately, the label is always displayed after the function is executed.
void MainWindow::showLabel(){
myLabel->show();
doSomething();
}
void MainWindow::doSomething(){
QThread::msleep(3000);
myLabel->hide();
}
So, when i execute my code, the programm waits for three seconds and does show me an empty window afterwards (since it directly hides the label before even showing it; if I comment the hide function, the label is shown after waiting three seconds).
What I've tried to do is modifying the showEvent like this:
void MainWindow::showEvent(QShowEvent *event) {
QMainWindow::showEvent(event);
doSomething();
}
Am I doing something wrong by modifying the method or is there any other way to show the label before executing the followed function?
I would solve your problem in the following way:
void MainWindow::showLabel()
{
myLabel->show();
// Wait for 3sec. and hide the label.
QTimer::singleShot(3000, myLabel, SLOT(hide()));;
}
i.e. you don't need the second function and to block the current thread with QThread::msleep(), which is the reason why your label appears after the timeout is fired.
Update
If you need to do more than just hiding a label, define a slot and call it like:
void MainWindow::showLabel()
{
myLabel->show();
// Wait for 3sec. and call a slot.
QTimer::singleShot(3000, this, SLOT(doSomething()));
}
// This is a slot
void MainWindow::doSomething()
{
myLabel->hide();
[..]
// some more stuff
}
QThread::msleep(3000); is blocking the main thread where event loop is processed. So it prevent to show myLabel until sleep time is end. The solution is either to use QTimer as vahancho recomended or call event loop processing manualy by calling QEventLoop::exec() after myLabel->show();.

Value of spin box, just before it changes

I'm writig a code using qt libraries, in which I need to get the value of a spin box (by a signal) just before it changes.
I've got:
QSpinBox spinBoxWidth:
QSpinBox spinBoxScale;
I want to connect a signal from spinBoxWidth to spinBoxScale, so that the value of SpinBoxScale is always "the Value of SpinBoxWidth after changing" to "its value before changing".
(Scale = width_new/width_old)
I didn't find any slot in Qt which returns the old value of a spin box while changing the value. Can I somehow write a slot for that?
Best Regards
There are two ways of doing this:
Catch the change before it happens and store the old value using the event system (QKeyEvent, QMouseEvent). This is error-prone, as the value of spinBoxWidth can be set manually.
Connect spinBoxWidth's valueChanged(int) signal to a slot and reference the last value it was called with. I recommend this method.
Try something like this:
class MonitoringObject : public QObject
{
Q_OBJECT
int lastValue;
int currentValue;
...
public Q_SLOTS:
void onValueChanged(int newVal)
{
lastValue = currentValue;
currentValue = newVal;
if (lastValue == 0) //catch divide-by-zero
emit ratioChanged(0);
else
emit ratioChanged(currentValue/lastValue);
}
Q_SIGNALS:
void ratioChanged(int);
After your signals are connected, the flow should look like this:
spinBoxWidth emits valueChanged(int)
MonitoringObject::onValueChanged(int) is invoked, does its work and emits ratioChanged(int)
spinBoxScale receives the signal in its setValue(int) slot and sets the appropriate value.
The easiest way is probably a lambda that caches the value of valueChanged for the next call:
auto const width = new QSpinBox();
width->setValue(200);
connect(width, &QSpinBox::valueChanged,
[prev_value = width->value()](int const value) mutable {
auto const scale = double(value) / double(prev_value);
// do stuff
prev_value = value;
});
I believe there is no specific signal to the "value before change" because you can always store it from the previous signal "onValueChanged()" you received.
So the basic idea would be:
First time, receive signal onValueChanged(value) and store the value value_old;
Next time you receive the signal, you can compute you scale!value/value_old;
Then you can send a new signal, or directly modify the object with the new value.
You can derived your own version of QSpinBox including this code or implemented in the class it has to receive the signal. It depends on your architecture.

QGraphicsItem disappears when calling setPos from a different thread

I have two types of ­­­­­­­­­­QGraphicsItem­­­­­­s on a QGraphicsView, one of those two types are in the scene like grid with z-index 1, the other ones, ants, are on top of them with z-index 2. When starting the program, I set all ants to position 0,0 and add them to the scene. But then I start moving those ants from another Thread by calling setPos() on them - and then my computer eats the ants! They disapper at their old position, but don't appear at their new position. The new position is inside the scene.
Here is the code of Ant class (that inherits QGraphicsItem):
#include "ant.h"
#include "constants.h"
#include <QPainter>
Ant::Ant()
{
setZValue(2);
}
QRectF Ant::boundingRect() const
{
return QRect(QPoint(0,0), G_FIELD_RECT_SIZE);
}
void Ant::paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget)
{
Q_UNUSED(item)
Q_UNUSED(widget)
QBrush b = painter->brush();
if (food())
painter->setBrush(Qt::blue);
else
painter->setBrush(Qt::black);
painter->drawEllipse(2,3, G_FIELD_RECT_WIDTH - 4, G_FIELD_RECT_HEIGHT - 6);
painter->setBrush(b);
}
After a bit more testing, I found out that everything is working as long as I call setPos from the Qt Event Thread. As soon as I call it in an custom thread, the ants disappear. Any idea how I can solve this?
You must go back to the main thread to do the setPos. As commented by ddriver, you should not modify GUI from a thread (you usually get qDebug messages when doing so, didn't you get any in your debugger window?).
You just need to:
Add a new signal to your Ant class (like signalSetPos( QPoint pos ))
Add a new slot to your Ant class (like doSetPos( QPoint pos )). This slot implementation simply calls setPos(pos).
Connect them using Qt::QueuedConnection or Qt::BlockingQueuedConnection (fifth parameter of the connect function, for GUI update, Qt::QueuedConnection may be preferable because it won't block your thread).
From the thread where you used to do setPos( newPos ), just do emit signalSetPos( newPos ). Then, doSetPos will be executed (later if you used Qt::QueuedConnection, right away if you used Qt::BlockingQueuedConnection) from the main thread.
Check this post for more information about emitting signals from threads:
Qt - emit a signal from a c++ thread

QSlider , QTimer and valueChanged usage

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

How to force Qt to update GUI from not main thread

I'm fighing since last week with problem caused by update of QPlainTextEdit. I'm trying to create separate from QMainWindow Dialog window with QPlainTextEdit inside. The problem begins when I try to use appendHtml signal (also tried with appendText), text that is placed is not visible unless marked by by mouse. Repainting or updating cause in program crash or no visible action.
Simplified code of QDialog with QPlainTextEdit header:
namespace Ui {
class LogWindow;
}
class LogWriter: public QDialog
{
Q_OBJECT
QMutex print_lock;
public:
class Log{
Q_OBJECT
const static int MAX_SIZE = 100;
bool to_terminal;
QString color;
QMutex *print_lock;
QPlainTextEdit *text_place;
QVector< QPair<QString,time_t> > history;
LogWriter * obj;
public:
bool print;
Log(bool _print,QString _color,LogWriter *obj_ = NULL)
{print = _print; color = _color; obj = obj_;}
void setLock(QMutex *print_lock_){print_lock = print_lock_;}
void setTextField(QPlainTextEdit *_text) {text_place = _text;}
Log& operator<<(QString &a);
Log& operator<<(const char* a);
};
static LogWriter* getInstance()
{
static LogWriter instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return &instance;
}
~LogWriter();
Log LOW,MEDIUM,HIGH;
Ui::LogWindow *ui;
signals:
void signalLogAppend(QString);
};
Simplified code of methods definitions:
LogWriter::LogWriter(QWidget * parent): QDialog(parent) {
ui = new Ui::LogWindow;
ui->setupUi(this);
LOW.setLock(&print_lock);
MEDIUM.setLock(&print_lock);
HIGH.setLock(&print_lock);
connect(this,SIGNAL(signalLogAppend(QString)),ui->plainTextEdit,
SLOT(appendHtml(QString)),Qt::DirectConnection);
}
LogWriter::Log& LogWriter::Log::operator<< (QString &s){
history.push_front(qMakePair(s,time(NULL)));
if(history.size() > MAX_SIZE) history.pop_back();
if(print){
//print_lock->lock();
QString text = "<font color=\"";
text += color + "\">";
text += s + "</font>";
//cout << text.toStdString() << endl;
//text_place->appendHtml(text);
//text_place->repaint();
emit (obj)->signalLogAppend(text);
//print_lock->unlock();
}
return *this;
}
I have two separate ui files (first for main window, second for log window).
I have to use log window all across my program (something about 10 threads), and I stucked on this issue. My question is - is it possible to force GUI update without using main thread and if not - what else possibilities I have. If possible I would rather avoid reconstructing all my code - it would take me some time to do it. Right now logging is super easy - I ony need:
LogWindow *log = LogWindow::getInstance();
log->MEDIUM << "something";
As additional info I add QTCreator warning:
QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
If I understand your code correctly, you're trying to log from a background thread and are using a direct connection to pass the signal to the GUI thread? That's not going to work, you have to send the signal via the default connection so Qt can figure out that it's a cross-thread signal and pass it across threads accordingly (ie, via the message loop on the foreground thread).
In Qt, any GUI interaction has to happen in the Main/foreground thread otherwise bad things happen as you discovered. You can certainly send a signal from a background thread to trigger a GUI update - I do this all the time - but you need to ensure that you're using the correct connection for it. The direct connection results in a direct function call and is not going to work for you in this case.
In your code, the problem is the call to connect() - you explicitly specify the connection mode for the signal to slot connection when you should just use the default setting. If you set the connection explicitly to Qt::DirectConnection, the underlying code will execute a direct call to the specified slot, which means that you end up calling the slot in the thread context of the signal. You don't want that because the signal is triggered in a background thread.
You can't pass arbitrary types/classes to signals and slots. The list is limited and not all Qt classes are in the list. To add types/classes to the list of those that can be passed to a signal/slot, you must call qRegisterMetaType for that class. I recommend calling it in the constructor of the class you're trying to pass to a signal like this:
MyClass::MyClass() : MyParentClass()
{
static int reg = qRegisterMetaType<MyClass>("MyClass");
}
The static int ensures the registration is only called once and before any instance of MyClass could ever be used.