How to get a direct jump in QSlider without damaging performance - qt

I am now creating a simple video player in QT.
I created a slider that is connected with Connect to a multimedia player (he is responsible for running the movie back and forth) and I want it to be moved by a mouse click anywhere on the slide and not just by dragging the cursor.
I tried to do this by adding my own method as follows:
class MySlider : public QSlider
{
protected:
void mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
if (orientation() == Qt::Horizontal)
{
setValue(minimum() + (maximum() - minimum()) * (static_cast<float>(event->x()) / static_cast<float>(width())));
}
event->accept();
}
QSlider::mousePressEvent(event);
}
};
This is the way I connected the slide to the player:
connect(player, &QMediaPlayer::durationChanged,pos_slider, &MySlider::setMaximum );
connect(player, &QMediaPlayer::positionChanged,pos_slider, &MySlider::setValue );
connect(pos_slider, &MySlider::sliderMoved, player, &QMediaPlayer::setPosition);
connect(pos_slider, &MySlider::valueChanged ,player, &QMediaPlayer::setPosition );
My problem is that now when the movie is playing, there is lag (the movie hangs for a few seconds every few seconds). In my opinion, because of this addition, I am actually putting a lot more pressure on the player because he has now added events to listen and send.
How can I get the slider moving in a way that will not damage the performance of the player? (Or reduce the performance degradation to a minimum)
thank you
yoko
p.s.
The center of the problem is that I use connect(player, &QMediaPlayer::positionChanged,pos_slider, &MySlider::setValue ); in the media player and also connect(pos_slider, &MySlider::valueChanged ,player, &QMediaPlayer::setPosition ); , this duplication is causing performance problems, but I have no idea how I can get rid of this duplication

As you pointed out yourself, the problem is (probably) due to resonance between QMediaPlayer::position and MySlider::value.
Solution 1:
So, you should avoid changing the QMediaPlayer::position when MySlider::value is updated programmatically, i.e. do not use the following connection:
connect(pos_slider, &MySlider::valueChanged ,player, &QMediaPlayer::setPosition );
Instead, you should use the sliderReleased and sliderMoved signals to update QMediaPlayer::position and call QMediaPlayer::setPosition manually inside mousePressEvent (when appropriate).
Solution 2: (thanks too Karsten Koop)
Create your own slot to update the slider value when QMediaPlayer::position changed to block emitting signals.
connect(player, &QMediaPlayer::positionChanged,pos_slider, &MySlider::updateValueFromMediaPlayer );
void MySlider::updateValueFromMediaPlayer(int pos)
{
blockSignals(true);
setValue(pos);
blockSignals(false);
}
Note that you do not need the following connection in this case:
connect(pos_slider, &MySlider::sliderMoved, player, &QMediaPlayer::setPosition);

Related

QDateTimeEdit focus to seconds

I have a QDateTimeEdit widget in my dialog showing minutes and seconds in the form of mm:ss
When the widget get focus by clicking the up/down arrows the minutes are increased/decreased by default. Is there a way to set default focus to the seconds?
In the QDateTimeEdit, there is a method named setCurrentSection(). All you have to do is to set the current selection to the seconds, like this:
dateTimeEdit->setCurrentSection(QDateTimeEdit::SecondSection);
In case anyone is interested.
A couple of the comments state the solution (dateTimeEdit->setCurrentSection(QDateTimeEdit::SecondSection);) "does not work".
If you use this code as-is immediately after creating a QDateTimeEdit it fails to present the user with the desired section selected. It appears the Qt internals reset the selection (to the first section) at some point after initially showing the widget. I did not bother to delve into the source code to discover precisely where. But the following two samples show how to "workaround" this.
If you do not want to subclass QDateTimeEdit I had to do this:
QTimer::singleShot(1000, [timeEdit]() { timeEdit->setSelectedSection(QDateTimeEdit::SecondSection); });
It works, and you can see the selection move from the first section when it is initially shown to the seconds when the timer fires. Note that the time delay size seems to be critical here. I tried with 0 and even with 100 and it did not work. For me, I needed 1000 (1 second) before it worked! My 1 second was on a standalone program which had to start up. You will need to play with that figure depending on your machine and where you call it/show the widget.
If you want "better performance" and are prepared to subclass so as to override showEvent(), I found (I am using QTimeEdit, but same for QDateTimeEdit):
class MyTimeEdit : public QTimeEdit
{
public:
explicit MyTimeEdit(QWidget *parent = nullptr) : QTimeEdit(parent)
{
}
explicit MyTimeEdit(const QTime &time, QWidget *parent = nullptr) : QTimeEdit(time, parent)
{
}
void showEvent(QShowEvent *event) override
{
QTimeEdit::showEvent(event);
// next line commented out because still does not work
// this->setSelectedSection(QDateTimeEdit::SecondSection);
// next line still requires `100` delay on my machine, but better than `1000`
QTimer::singleShot(100, [this]() { this->setSelectedSection(QDateTimeEdit::SecondSection); });
}
};
Still needed to use a time delay, but getting that down to 100 makes it so the user does not see the selection start and then move.

Using the closeEvent in a qt application does not close it

I am learning qt, and experimenting with examples from a textbook.
The original textbook code has the following, set up to save and close on the x button:
void MainWindow::closeEvent(QCloseEvent *event)
{
if (okToContinue()) {
writeSettings();
event->accept();
} else {
event->ignore();
}
}
i experimented with a simple exit in its menu - and it works:
void MainWindow::close()
{
if (okToContinue()) {
QApplication::quit();
}
}
But I want to take advantage of the already written closeEvent, so i replaced the code above with
void MainWindow::close()
{
QCloseEvent *event = new QCloseEvent();
closeEvent(event);
}
I get the checking for changes and saving app, implemented through the okToContinue function. But the application does not close.
i tried to follow through debugging and.. with my small understanding, it seems that there is a close signal being sent...
I don't have a good understanding of this, can somebody please help me figure out what am i doing wrong and how to fix it ?
(the sample code is from C++ GUI Programming with Qt 4, chapter 3)
You don't have to reimplement MainWindow::close() in your subclass.
From the Qt Docs:
...QCloseEvent sent when you call QWidget::close() to close a widget
programmatically...
So you just have to reimplement MainWindow::closeEvent(QCloseEvent *event) if you want to control this event.
This event fires when you click x or call close() from the code.
The closeEvent and related methods don't actually execute the action that happens when a given event is received. They merely allow you to act on the event and perhaps disable its further processing. The closing of the window is done within QWidget::event, where closeEvent is called from.

in Qt, what QEvent means loses window focus, regain window focus? (Set transparency)

I need to set transparency when my application loses focus.
I also need to reset the transparency when it regains focus (from a mouse click or alt-tab or whatever)
I know how to set the transparency, so that is not the issue:
setWindowOpacity(0.75);
The issue is WHEN?
I agree with Kévin Renella that there are sometimes issues with QWidget::focusInEvent and QWidget::focusOutEvent. Instead, a better approach would be to implement QWidget::changeEvent():
void MyQWidget::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if (event->type() == QEvent::ActivationChange)
{
if(this->isActiveWindow())
{
// widget is now active
}
else
{
// widget is now inactive
}
}
}
You can also achieve the same thing by installing an event-filter. See The Event System on Qt Documentation for more information.
When a QFocusEvent event occurs. Just re-implement
void QWidget::focusInEvent ( QFocusEvent * event );
void QWidget::focusOutEvent ( QFocusEvent * event );
from QWidget. Make sure to always call the super-class method before or after doing your work. i.e., (before case)
void Mywidget::focusInEvent (QFocusEvent * event ){
QWidget::focusInEvent(event);
// your code
}
But, there are sometimes issues with QWidget::focusInEvent and QWidget::focusOutEvent. See this answer for a more reliable approach.
There are sometimes issues with QWidget::focusInEvent and QWidget::focusOutEvent events of QWidget
There is an alternative using QWidget::windowActivationChange(bool state). True, your widget is active, false otherwise.

Design pattern for mouse interaction

I need some opinions on what is the "ideal" design pattern for a general mouse
interaction.
Here the simplified problem. I have a small 3d program (QT and openGL) and
I use the mouse for interaction. Every interaction is normally not only a
single function call, it is mostly performed by up to 3 function calls (initiate, perform, finalize).
For example, camera rotation: here the initial function call will deliver the current first mouse position,
whereas the performing function calls will update the camera etc.
However, for only a couple of interactions, hardcoding these (inside MousePressEvent, MouseReleaseEvent MouseMoveEvent or MouseWheelEvent etc)
is not a big deal, but if I think about a more advanced program (e.g 20 or more interactions) then a proper design is needed.
Therefore, how would you design such a interactions inside QT.
I hope I made my problem clear enough, otherwise don't bother complain :-)
Thanks
I suggest using polymorphism and the factory method pattern. Here's an example:
In my Qt program I have QGraphicsScenes and QGraphicsItems with mousePressEvent, mouseMoveEvent, and mouseReleaseEvent, which look like this:
void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
// call factory method, which returns a subclass depending on where click occurred
dragHandler = DragHandler::createDragHandler(event /* and other relevant stuff */);
}
void CustomItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
dragHandler->onMouseMove(event);
}
void CustomItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
dragHandler->onMouseRelease(event);
delete dragHandler;
}
The idea in this particular case is that depending on where I click on CustomItem, mouse pressing, moving, and releasing will have different functionality. For example, if I click on the edge of the item, dragging will resize it, but if I click in the middle of the item, dragging will move it. DragHandler::onMouseMove and DragHandler::onMouseRelease are virtual functions that are reimplemented by subclasses to provide the specific functionality I want depending on where the mouse press occurred. There's no need for DragHandler::onMousePress because that's basically the constructor.
This is of course a rather specific example, and probably not exactly what you want, but it gives you an idea of how you can use polymorphism to clean up your mouse handling.
Qt makes this beautifully simple.
Instead of all the switch mouse_mode: stuff you used to write, simply have each mouse event handler function emit a signal ie. mouseDown/mouseUp/mousePosition and use signals/slots to route those to the appropriate model functions.
Then you can accommodate different uses of the mouse (selecting, rotating, editing etc) by connect/disconnect different SLOTS to the signal sent in the Mouse...Event()
I find Apple's UIGestureRecognizer design quite nice and extendable.
The idea is to decouple the recognition of the gesture (or interaction) and the action that will be triggered.
You need to implement a basic or abstract GestureRecognizer class that is able to recognize a certain interaction or gesture based on events MousePressEvent, MouseReleaseEvent MouseMoveEvent or MouseWheelEvent etc. GestureRecongnizers have a target to report changes periodically.
For example your very basic class would be like: (sorry my poor semi c++ pseudo-code ... recently I don't use it that much)
class Recognizer {
int state; // ex: 0:possible, 1:began, 2:changed, 3:ended/recognized 4:cancelled
protected:
void setTarget(void &theTarget); // or even better a touple, target/method. In this case target is assumed to have a method gestureHandle(Recognizer *r);
virtual void mouserPress() = 0;
virtual void mouserRelease() = 0;
virtual void mouserMove() = 0;
virtual void mouserWheel() = 0;
...
}
And if you want to detect a swipe with the mouse
class SwipeRecognizer : Recognizer {
int direction; // ex: 0:left2right 1:bottom2top 2:...
private:
void mouserPress() {
state = 0; // possible. You don't know yet is the mouse is going to swipe, simple click, long press, etc.
// save some values so you can calculate the direction of the swipe later
target.gestureHandle(this);
};
void mouserMove() {
if (state == 0) {
state = 1; // it was possible now you know the swipe began!
direction = ... // calculate the swipe direction here
} else if (state == 1 || state == 2) {// state is began or changed
state = 2; // changed ... which means is still mouse dragging
// probably you want to make more checks here like you are still swiping in the same direction you started, maybe velocity thresholds, if any of your conditions are not met you should cancel the gesture recognizer by setting its state to 4
}
target.gestureHandler(this);
};
void mouserRelease() {
if (state == 2) { // is swipping
state = 3; // swipe ended
} else {
state = 4; // it was not swiping so simple cancel the tracking
}
target.gestureHandler(this);
};
void mouserWheel() {
// if this method is called then this is definitely not a swipe right?
state = 4; // cancelled
target.gestureHandler(this);
}
Just make sure above methods are called when the events are happening and they should call the target when needed.
This is how the target will look to me:
class Target {
...
void gestureHandler(Recognizer *r) {
if (r->state == 2) {
// Is swipping: move the opengl camera using some parameter your recognizer class brings
} else if (r->state == 3) {
// ended: stop moving the opengl camera
} else if (r->state == 4) {
// Cancelled, maybe restore camera to original position?
}
}
Implementation of UIGestureRecognizer is quite nice and will allow to register several targets /method for the same recognizer and several recognizers to the same view.
UIGestureRecognizers have a delegate object that is used to get information about other gesture recognizers, for example, if two gestures can be detected at the same time, or should one must fail as soon as the other is detected, etc.
Some gesture recognizer will require more overrides than others but the big PRO of this is that their output is the same: a handler method that informs about the current state (and other info).
I think is worth taking a look at it
Hope it helps :)

When I send or post a QMouseEvent, at the position of a QPushbutton, its clicked() signal is not emitted

I try to design a gui for an augmented reality application using the kinect. The idea is, to use the hands detected by the kinect skeleton tracking to control an application via gestures.
This question is not about gestures, as this part of my appilcation works fine.
In my application i want to simulate a mouse click whenever a click gesture is performed. To do this, i am sending two events one, mousebuttonpressed, and one mousebuttonreleased, as a normal click is also a series of press and release.
The whole thing works fine on a QWebView. In the browser window, i can "click" on links.
But for some reason i cannot "click" on a QPushButton. The clicked() signal is not emitted.
I have a short example to illustrate my problem:
First the main function:
int main(int argc, char **argv){
QApplication app( argc, argv );
QWebViewButtons mainapp( 0, Qt::Window );
app.setActiveWindow( &mainapp );
mainapp.show();
mainapp.setApplication( &app ); //this is a setter to also get access to the main application in the widget
return app.exec();
}
This is my own widget:
QWebViewButtons::QWebViewButtons(QWidget* parent, Qt::WindowFlags flags ): QWidget(parent, flags ){
this->m_ui.setupUi( this );
QObject::connect( this->m_ui.pushButton, SIGNAL(clicked(bool)), this, SLOT( buttonClicked(bool) ) );
}
void QWebViewButtons::mousePressEvent( QMouseEvent* event ){
printf("mouse click, %d, %d\n", event->pos().x(), event->pos().y() );
}
void QWebViewButtons::buttonClicked( bool clicked ){
printf("slot button clicked\n");
}
void QWebViewButtons::keyPressEvent( QKeyEvent* event ){
printf("Key pressed\n");
QPoint pos( this->width()/2, this->height()/2 );
QPoint global = this->mapToGlobal(pos);
QWidget *w = this->m_app->widgetAt(global);
//printf("widget under point of click: %s", w);
QApplication::sendEvent( w, new QMouseEvent( QEvent::MouseButtonPress, pos, Qt::MouseButton::LeftButton, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier ) );
QApplication::sendEvent( w, new QMouseEvent( QEvent::MouseButtonRelease, pos, Qt::MouseButton::LeftButton, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier ) );
}
I followed the suggestion here:
Qt Artificial mouse clicks doesnt work properly
and send to send my mouse events directly to the QPushButton. But that didn't help.
I also tried to send the mouse events to "this", or the main app.
Now I am running out of ideas.
What I want to have is, that the buttonClicked() slot is called if i press any key. But i only is called if i click the button with my mouse.
How can i accomplish this? Or is my basic idea completely false?
Thanks for your ideas.
So, do I get that right? When doing your "click-gesture" you come into keyPressEvent? If so, you can check whether the "click" has been done above the button and explicitly call the button's clicked() signal. But that's not what you want?
And what exactly is QWebViewButtons? The area the user does his gestures in?
Have you debugged into the keyPressEvent to see if your sendEvent has the correct widget (w)? I can't see why the widget should not recieve the event...
And remember that when new'ing a QMouseEvent and sending it via sendEvent is is never deleted. When using sendEvent you should create your event on the stack.
Maybe you should have a look at this thread, where a QTestEventList is recommended: Mimicking/faking a Mouse Click and Mouse Wheel using Qt. But I can imagine that you don't want test functions do the trick ;)
Ok, the trick was to really send the click to the EXACT widget at the desired click position. In my application i had some trouble because of semi transparent widgets lying over each other.
So the widgetAt(global) command wouldnt help me.
But you can use childAt(global) instead. Of course you need to know from which widget you want to find the child.
That did the trick for me. :-)

Resources