QToolTip::showText disappears after mouse release - c++

I want to show tool tip on the QGLWidget but i must the call; QToolTip::showText(pos, "Message", qglwidgetPtr, rect(), 5000);
in another class.
So, tooltip disappears after releasing the mouse button. If I don't release it, the tooltip disappears after that 5000 msecs. I do not understand the disappear problem. I think it could be trigger disappear QGL widget paint event but i'm not sure.

First of all, let's understand what is the reasion of the problem. Tooltips should hide when user move the mouse cursor not above them. So, when you release the mouse button somewhere else, your OS catches a mouse event not above the tooltip (not near starting point of that tooltip), so it hides the tooltip.
So, my solution is following: create QTimer and show your tooltip few times per second as long as you need (5 seconds). You can do it, because in the documentation it is said that
If the text is the same as the currently shown tooltip, the tip will not move
(i.e. it is ok to call showText many times with the same text)
To create the timer you can use this code:
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(100); // ten times per second
And inside the body of the update() function you can compare current time and the time of first showing of this tooltip, and to show your tooltip if it is still necessary (i.e. if it is shown less than 5 seconds).
howLongShown = curTime - startTime; // startTime here is the moment of first showing of the tooltip
if (howLongShown < 5000)
QToolTip::showText(pos, "Message", qglwidgetPtr, rect(), 5000 - howLongShown);

Related

How to disable the progression animation of a QProgressBar

I have a QProgressBar showing the progress of an ongoing algorithm. It works just fine in a normal utilization.
But I also have a STOP button allowing user to stop the algo, for example at 42%. At this point, I set the value of the QProgressBar at 42.
The problem is, the progress bar has an animation (like a white ray moving from left to right) showing progress.
I would like to keep this animation when the algorithm is still running, but stop it when user press the Stop button.
I could set the value to 0 or max when the button stop is pressed, but we still want to keep the value when user pressed stop (42% in my example).
It is similar to this question which was not really answered : disable progress bar animation in Qt

Change QWidget Parent During Mouse Event

I'm trying to create a detachable type style widget, like in the way Chrome tabs are detachable (class is called Tab). I have everything working, except for a bug where sometimes (maybe 50% of the time), the Tab object never gets the mouse release event, and stops getting mouse move events.
Essentially, the detaching system works by allowing drags in the mouse press/move/release functions, just like normal. The mouseMoveEvent checks the total distance moved from the start, and if over a certain amount, will start the "detaching" process. The detaching process involves setting the parent widget to 0 (top level widget, undecorated window), so the Tab object is pretty much floating above everything, under the mouse, and continues to be dragged along with it until released.
I ran through all the QEvent items being delivered, and I found that when this issue occurs, the QEvent::MouseMove items (and all mouse events after this) are being sent to the TabBar (the Tab object's original parent). This occurs directly after calling setParent(0) on the Tab.
Basic mouse handling overview:
void Tab::mousePressEvent(*) {
[set up some boolean, start positions, etc]
}
void Tab::mouseMoveEvent(*) {
[track the updated position]
if (positionChange > STATIC_AMOUNT)
detachTab();
}
void Tab::mouseReleaseEvent(*) {
[return the Tab to its original position, and set the parent back to the TabBar]
}
void Tab::detachTab() {
QPoint mappedPos = mapToGlobal(0, 0);
setParent(0); //The loss of MouseMove events occurs when this returns.
move(mappedPos);
show();
raise();
}
Here are the events that the Tab object receives (first row is QEvent type, second is the name)
[Tab::detachTab() started]
[setParent(0) started]
QEvent::Hide
QEvent::Leave
qApp QEvent::MouseMove [ TabBar ] <-- now the TabBar is soaking up the mouse events
QEvent::HideToParent
QEvent::ParentAboutToChange
QEvent::ParentChange
[setParent(0) returned]
....
Summed up: my draggable QWidget loses QEvent::MouseMove and QEvent::MouseButtonRelease events after having its parent set to 0.
Any advice would be really appreciated!
A bit tricky workaround. I didn't test it, it's just an idea.
When your mouse hovers draggable part of a widget you may create topmost widget (let's call it Shade) with Qt::FramelessWindowHint (and possible with Qt::WA_TranslucentBackground). You may manipulate with Shade apperance via reimplementing paintEvent. For example - draw content of original widget, or draw some transparent preview, etc.
Then you may resize a Shade during dragging, to show user that widget will be detached. You will not loose mouse capture.
When user release mouse - you remember position of Shade, destroy it and detach+move original widget.
Feel free to ask, if you want more details.
Here is similar question.
So you suppose to use QDocWidget and enforce stacking of this widgets using tabifyDockWidget.

Sleep() in Window Form Application with visual C++

My question is related to Window Form Application and System::Threading::Thread::Sleep(int delayTime) in Visual C++.
My window form has a panel, in which there are 3 ovalShape named ovalShape1, ovalShape2 and ovalShape3. A button name button is next to the panel. Button is associated to a click event. What I need is when I click the button, the color of every oval shape will be changed to red after every 1 second (1000 miliseconds).
This is what I’ve done:
private: System::Void buttonClick(System::Object^ sender, System::EventArgs^ e) {
this->ovalShape1->FillColor = System::Drawing::Color::Red;
System::Threading::Thread::Sleep(1000);
this->ovalShape1->FillColor = System::Drawing::Color::White;
this->ovalShape2->FillColor = System::Drawing::Color::Red;
System::Threading::Thread::Sleep(1000);
this->ovalShape2->FillColor = System::Drawing::Color::White;
this->ovalShape3->FillColor = System::Drawing::Color::Red;
System::Threading::Thread::Sleep(1000);
this->ovalShape3->FillColor = System::Drawing::Color::White;
}
But for some reason (I don’t know), the application doesn’t work the way I have expected. The ovalShape1 just flashes and that’s all.
Maybe you know the reason!
Any help or suggestion would be appreciated!
This is because of Windows UI drawing optimizations. When you set the fill color of the oval:
A new color is stored in FillColor property
A repaint is scheduled to be done when your processing is finished.
So you modify the FillColor three times and schedule repaint for the oval three times. Then your code finishes and the oval is finally repainted.
You have to call ovalShape.Refresh() after changing its color to force redraw that control.

Qt user resize event ends (stops)

I have a QWidget and i need to do some actions (refresh a picture in widget) when resize event ends. How can i catch this action?
I need to catch moment when user ENDs all his resize actions by releasing mouse button. It is not a good practice in my application to refresh image every pixel resized. It should calls only when mouse released and resize actions ends.
I am just tried to reimplement QMouseReleaseEvent to catch it, but it do not works when user presses on the border of widget to resize it. It means does not working in our situation.
Then i was tried to create my own QSizeGrip and insert it on the bottom of my widget, but reimplemented event QMouseReleaseEvent again did not work in it. Event did not generates any time user released mouse. I do not know why.
Anybody can help me with that problem?
Thanks in advance.
The timeout method is a decent idea, but if the user is resizing and then pauses for longer than the timer's interval then you end up not getting the true "user is done resizing the window" event. Setting the interval longer makes that situation less likely, but by doing that, you end up having a long delay between the time the user finished resizing and the time your function gets called. In my search for a solution, I found quite a few people solving it using the timer method, so apparently it's reliable enough for some use cases, but I think it's a bit hacky.
I like mhstnsc's idea, so after implementing it, I decided to add some code here that might be of use to someone trying to do something similar.
You could easily adapt it to catch the "user is done moving the window" event by making a m_bUserIsMoving flag and overriding "void MainWindow::moveEvent(QMoveEvent* pEvent)". I'm using it to save a config file whenever the user finishes resizing or moving the window, so that the last position will always be saved even if the app is killed in an unclean manner.
// constructor
MainWindow::MainWindow(QWidget* pParent, Qt::WindowFlags flags) : QMainWindow(pParent, flags)
{
m_bUserIsResizing = false;
qApp->installEventFilter(this);
}
// this will be called when any event in the application occurs
bool MainWindow::eventFilter(QObject* pObj, QEvent* pEvent)
{
// We need to check for both types of mouse release, because it can vary on which type happens when resizing.
if ((pEvent->type() == QEvent::MouseButtonRelease) || (pEvent->type() == QEvent::NonClientAreaMouseButtonRelease)) {
QMouseEvent* pMouseEvent = dynamic_cast<QMouseEvent*>(pEvent);
if ((pMouseEvent->button() == Qt::MouseButton::LeftButton) && m_bUserIsResizing) {
printf("Gotcha!\n");
m_bUserIsResizing = false; // reset user resizing flag
}
}
return QObject::eventFilter(pObj, pEvent); // pass it on without eating it
}
// override from QWidget that triggers whenever the user resizes the window
void MainWindow::resizeEvent(QResizeEvent* pEvent) { m_bUserIsResizing = true; }
It's slightly more complicated than the timer, but more robust.
I've do it in this way:
inherit my class from QWidget
define private variable int timerId = 0
overload QWidget::resizeEvent and QObject::timerEvent
void MapLoader::resizeEvent(QResizeEvent *){
if (timerId){
killTimer(timerId);
timerId = 0;
}
timerId = startTimer(5000/*delay beetween ends of resize and your action*/);
}
void MapLoader::timerEvent(QTimerEvent *te){
/*your actions here*/
killTimer(te->timerId());
timerId = 0;
}
Mouse events on windows decoration are managed by the underlying window system, this is why you can't catch them as you tried.
I had the same issue once, the solution I chose was to (re)start a singleshot QTimer on each resize event, and only process the update after the timer interval elapsed. Not very sexy but I did not find any other workaround..
Another way would be to install an event filter for the app and get all the events of the application, trap mouse press and mouse release and do not update the window in between .
"Installing an event filter on QCoreApplication::instance(). Such an event filter is able to process all events for all widgets, so it's just as powerful as reimplementing notify(); furthermore, it's possible to have more than one application-global event filter. Global event filters even see mouse events for disabled widgets. Note that application event filters are only called for objects that live in the main thread."
My Qt app uses image windows and does complex layered rebuilds, which can take some time even on a very fast machine. So not having the window redraw with every change in the window frame size was important to me so the response to the window frame resize would not be laggy.
So I solved it this way:
In my image window, I have enabled mouse tracking:
setMouseTracking(true);
Then, in the window class, I have a boolean, puntme; this is set when a resize event is caught:
bool puntme;
Then, in the mousemove event:
void imgWindow::mouseMoveEvent(QMouseEvent* event)
{
if (puntme)
{
puntme = false;
needRebuild = true;
update();
}
...
Basically, what this does is as soon as the user moves the mouse over the window -- which is a pretty natural thing for them to do if they were just resizing it -- then the window redraws with the new size. It doesn't happen during the resize, because Qt isn't forwarding move moves then.
Instead, during the resize, I just scale up an already existing bitmap, which gives a crude approximation of the change in scale with necessarily handling the actual newly more-or-less available resolution.
Worst case, user resizes, moves away from the window, and leaves the crudely scaled bitmap in place until the come back to it, at which point it will duly update to the actual new displayed bitmap == scale/size conditions.
There's no perfect way - what is really needed here is for Qt to provide (user has stopped resizing window" message, but in lieu of that, this has been working well for me.

progress bar in nokia Qt?

I'm using progress bar in my app. when i navigating first widget to second widget and in-between i'm using progress bar in another widget.
//code:
progressform *pgm = new progressform(); // calling progress bar widget
pgm->adjustSize();
pgm->show();
for(int i=24;i<=100;i++) //initial setvalue is 24
{
pgm->ui->progressBar->setValue(i);
}
detailWidget *dwt = new detailWidget(); // calling detail widget
dwt->show();
Before going to detailwidget i called progressform(widget) and after that the progressbar setvalue reach 100 it moves to detailwidget. the problem is i cant resize the progress widget in to center of the screen like popup screen and I want to sleep time because i want to see the progress value increased one by one?
Thanks in advance
I can recommend using QProgressDialog instead which pops up centered automatically.
You'll have to center your progress bar manually. Popup screens do so automatically, but progress bars normally are used as part of a dialog. Therefore they are positioned by the dialog layout.
As for the sleeping, that's probably a design error. If your task is so fast you'd need a sleep just to see the progress bar, you shouldn't have a progress bar in the first place.