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.
Related
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);
Here's a simple task that looks innocent. Small Widget window. Whenever it's dragged around the desktop (by catching its titlebar with the mouse and moving it around) it should print out its X and Y position IN REAL TIME. during the move.
Sounds simple, but it seems it's impossible to do this in Qt.
No matter what I do, I cannot get real time events for when I catch the caption and move the window around. I have an event filter, but during drag only sporadic few events come in -- the moveEvents etc are only after I release the mouse button. I need real time events for every pixel of movement (or at least for every time the window is actually moves on the screen, as close a possible to real time)
Is there a way to do that without drilling holes into the OS?
I'm using Qt on Mac.
Here is what came to my mind first. This solution does not provide the real time events handling, but I think it is very close to it. If you move your window slowly enough, you can catch movements in a pixel precision. Ok, now the implementation (I believe the code explains how it works):
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0) : QWidget(parent)
{
m_timer.setInterval(1);
connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimer()));
m_timer.start();
}
private slots:
void onTimer()
{
if (m_lastPos != pos()) {
// Window moved.
m_lastPos = pos();
qDebug() << "Moved to:" << m_lastPos;
}
}
private:
QPoint m_lastPos;
QTimer m_timer;
};
I'm writing a program using Qt 4.8 that displays a table (QTableWidget) filled with filenames and file's params. First an user adds files to the list and then clicks process. The code itself updates the contents of the table with simple progress description. I want the table by default to be scrolled automatically to show the last processed file and that code is ready.
If I want to scroll it by hand the widget is being scrolled automatically as soon as something changes moving the viewport to the last element. I want to be able to override the automated scroll if I detect that it was the user who wanted to change view.
This behavior can be seen in many terminal emulator programs. When there's a new line added the view is scrolled but when user forces the terminal to see some previous lines the terminal does not try to scroll down.
How could I do that?
Solution:
I created an object which filters event processed by my QTableWidget and QScrollBar embedded inside. If I spot the event that should turn off automatic scrolling I just set a flag and stop scrolling view if that flag is set.
Everything is implemented inside tableController class. Here are parts of three crucial methods.
bool tableController::eventFilter(QObject* object, QEvent* event)
{
switch (event->type())
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::Wheel:
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
_autoScrollEnabled = false;
default:
break;
}
return QObject::eventFilter(object, event);
}
void tableController::changeFile(int idx)
{
[...]
if (_autoScrollEnabled)
{
QTableWidgetItem* s = _table.item(_engine.getLastProcessed(), 1);
_table.scrollToItem(s);
}
[...]
}
void tableController::tableController()
{
[...]
_autoScrollEnabled = true;
_table.installEventFilter(this);
_table.verticalTableScrollbar()->installEventFilter(this);
[...]
}
Thanks for all the help. I hope somebody will find it useful :)
Subclass QTableWidget and overload its wheelEvent. You can use the parameters of the supplied QWheelEvent object in order to determine if the user scrolled up or down.
Then use a simple boolean flag which is set (or reset) in your wheelEvent override. The method which is responsible for calling scrollToBottom() should then consider this boolean flag.
You will have to find a way to figure out when to set or reset that flag, e.g. always set it when the user scrolls up and reset it when the user scrolls down and the currently displayed area is at the bottom.
connect(_table->view()->verticalScrollBar(), &QAbstractSlider::actionTriggered, this, [this](int) {
_autoScrollEnabled = false;
});
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 :)
I am trying to develop an image gallery application using Qt Framework. The application loads all the images from the selected folder and those images are displayed using QListView control.
But now i want to reduce the memory consumption by loading only the images that are visible to user. Since there is no direct function to get all the visible items in the view, i am not able to achieve this.
You can get the visible items of a list view using the indexAt function. For more details and an example you can check the following article:
http://qt-project.org/faq/answer/how_can_i_get_hold_of_all_of_the_visible_items_in_my_qlistview
I found it! You have to connect the vertical scrollbar of the listwidget to a signal:
connect(ui->listWidget->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(launch_timer()));
Every time the user scrolls, the valuechanged(int) signal is being omitted! The thing is that you shouldn't run the code provided by webclectic in this question every time the value of the vertical scrollbar of the listwidget changes, because the program will be unresponsive with so much code to run in so little time.
So, you have to have a singleshot timer and point it to the function that webclectic posted above. When launch_timer() is called, you do something like this:
if(timer->isActive()){
timer->stop();
timer->start(300);
}
else
timer->start(300);
and the timeout() signal of timer will be connected to the slot webclectic talked about. This way, if the user scrolls quickly all the way down only the last items will be updated. Generally, it will be updated anything visible for more than 300 milliseconds!
I think what you need is to implement your own model (take a look to the QAbstractListModel documentation) so that way you could decide when you have to load more images to show and maybe free some of the images that became non-visible.
although this is not so simple in Qt 4 but
it is always simple to copy below:
#include <private/qlistview_p.h>
class QListViewHelper : public QListView
{
typedef QListView super;
inline QListViewHelper() {} //not intended to be constructed
public:
inline static QVector<QModelIndex> indexFromRect(const QListView *view,
const QRect &rect)
{
const QListViewPrivate *d = static_cast<const QListViewPrivate *>(QObjectPrivate::get(view)); //to access "QListViewPrivate::intersectingSet(...)"
const QListViewHelper *helper = static_cast<const QListViewHelper *>(view); //to access "QListView::horizontalOffset()"
return d->intersectingSet(rect.translated(helper->horizontalOffset(), helper->verticalOffset()), false);
}
inline static QVector<QModelIndex> visibleItems(const QListView *view)
{ return indexFromRect(view, view->rect()); }
inline static QModelIndex firstVisible(const QListView *view)
{ return visibleItems(view).value(0); }
inline static QModelIndex lastVisible(const QListView *view) {
const QVector<QModelIndex> &items = visibleItems(view);
return items.value(items.count() - 1);
}
};
void ourTest(const QListView *view) {
QModelIndex &index = QListViewHelper::firstVisible(view);
qDebug("QListViewHelper: first visible row is %d", index.row());
index = QListViewHelper::lastVisible(view);
qDebug("QListViewHelper: last visible row is %d", index.row());
}
usage:
QModelIndex &index =
QListViewHelper::firstVisible(listViewPointerHere)
note: since it does use Qt 4.8 private-headers it may no longer work in latter versions and will need some changes.
You can keep track of all the elements that are drawn per paint event. I used a delegate and overloaded the paint event.
I also overloaded the paint event in the view. During this call, all the visible delegates will get a paint event.
If you just need to know if an item is visible, you can increment a frame count in view->paintEvent and set that number in the delegate item. The item is visible of the item matches the current frame number.
If you need a list of all visible items, clear the visible item list in view->paintEvent and add each item in the int the delegate->paintEvent to the visible items list.