Sleep() in Window Form Application with visual C++ - 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.

Related

QToolTip::showText disappears after mouse release

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

Qt get mouse events outside of the application window

First I'm not certain this is even possible without some sort of hacking of X.11 input but the discussions i'd seen online made me think it was possible.
Allow me to explain what I hope to do. I want a Qt application which will most likely just be a small window that sides on the screen sort of like a widget. The application does nothing until the user drags another application window over the top of it. The way I was hoping to detect this was to track the mouse and see if the left click is down and the mouse is over the Qt window and Qt is not the active window then do some action. However currently I havent been able to get mouse events when my Qt application is not the active window. I think some of these posts I linked refer to 'window' as a QWindow inside the QApp.
What I mean by window however is a X.11 Window, any application opened in X. My screenshots I hope highlight my current plight. I've attached my code as well and am happy to take any suggestions. Any other hacks that are known to help me achieve this I would also appreciate being informed of.
The red shows where my cursor has clicked, and the mouse event is recorded outside of the Qt window. This was triggered by the 'FocusOut' event however and is the last event I have managed to detect.
As we can see in the console, the mouse has moved but no events are caught. I really want to detect when the mouse crosses over onto the position the Qt App Window is at regardless of whether it is on top of another window or not.
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
statusBar()->showMessage(QString("Mouse move (%1,%2)").arg(mouseEvent->pos().x()).arg(mouseEvent->pos().y()));
qDebug() << QString::number(mouseEvent->pos().x());
qDebug() << QString::number(mouseEvent->pos().y());
}
if (event->type() == QEvent::FocusOut)
{
QFocusEvent *focusEvent = static_cast<QFocusEvent*>(event);
focusEvent->accept();
qDebug()<<"event Filter Mouse Move111"<<QCursor::pos();
}
return false;
}
void MainWindow::initWindow()
{
//Makes the window frameless and always on top
//setWindowFlags(Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
//Makes the window transparent
//setAttribute(Qt::WA_TranslucentBackground);
//Allows 'mouseMoved' events to be sent, not sure yet if this will be useful, I think we want mouseDragged
setMouseTracking(true);
grabMouse();
//setup this as an event filter for mouse events
qApp->installEventFilter(this);
}
Alright heres how I solved this problem. The event system in Qt, any application I assume, won't register events when the window is not active. However the process is obviously still running so data you can access while the window is active you can access whilst the window is no longer active.
Use a timed poll method to get the mouse position every n seconds
//Method used to hopefully track the mouse regardless of whether or not it is inside the active window
void MainWindow::pollMouse(unsigned long sec)
{
//Loop forever
while ( true )
{
QPoint mouseLoc = QCursor::pos();
qDebug() << "Mouse position global: x,y" << mouseLoc.x() << mouseLoc.y();
QThread::sleep(sec);
}
}

How can I selectively make QWidget accept focus from a mouse click?

I'm creating an application that provides a visual representation of nodes and lines connecting them together. Both nodes and lines are represented by custom QWidgets, WidgetNode and WidgetLine, say.
I've implemented WidgetLine as a transparent widget that is large enough to contain the start and end point of the line, and a custom function to draw the line itself.
I would like it so that if the user clicks on or right next to the line then the WidgetLine receives focus, but if they click further away from the line (but still over the rectangular area covered by WidgetLine's geometry) then the click is completely ignored by WidgetLine and passed on to the widget below.
I first tried doing this with a custom focusInEvent() function on WidgetLine, but found the mouse clicks weren't propagating below. I then tried setting the focus policy to Qt::NoFocus and using a custom mousePressEvent() using setFocus() to manually set the focus when appropriate, but the mouse events are still not being propagated to widgets above even when I call ignore() on them.
Finally, I've tried installing an event filter to reject mouse events, with this event filter function
bool WidgetLineFilter::eventFilter(QObject* object, QEvent* event)
{
assert(object == mCord);
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* e = dynamic_cast<QMouseEvent*>(event);
assert(e);
if (e)
{
QPoint mouseRelativeToParent = mCord->mapToParent(e->pos());
// calculate distance of mouse click to patch cord
QLineF line(mCord->line());
float distance = distanceFromPointToLine(QVector2D(line.p1()), QVector2D(line.p2()), QVector2D(mouseRelativeToParent));
qDebug() << distance;
const float distanceThreshold = 2.f;
if (distance < distanceThreshold)
{
qDebug() << "consuming mouse click for focus";
mCord->setFocus(Qt::MouseFocusReason);
return true;
}
else{
qDebug() << "mousepressevent too far for focus";
return QObject::eventFilter(object, event);
}
}
}
return false;
}
But this still does not propagate the mouse events to the parent on the "mousepressevent too far for focus" case. I've also tried returning false and true from here, and calling ignore on e but the widgets below are not receiving the click.
(NB the above approaches do work in the sense that WidgetLine only gets focus at the right time, it's just the widgets below that aren't receiving the press events when it doesn't get focus.)
Any ideas on how to fix this?
Store the mouse pos in a global var. Have all of your widgets have enter/leave events like the following and use that to check what widget you're in / near when you run the func.
void QGLWidget::enterEvent(QEvent *)
{
setFocus();
}
void QGLWidget::leaveEvent(QEvent *)
{
clearFocus();
}
In the end, I created an event filter to selectively intercept mouse events and installed it on the base window and recursively on every single child widget of the base window (installing it on new child widgets when they are created). This filter calls the base window with each mouse press event which then iterates through each WidgetLine, testing if they should be selected by this mouse press and setting focus on them if they should. If they all test false then the filter releases the event, otherwise the filter consumes it.
WidgetLine's are then set to be transparent for mouse events with
setAttribute(Qt::WA_TransparentForMouseEvents);
It's messier than it ought to be to achieve this but does the trick.

C++ Win32 Trying to create an owner drawn toggle button

I have been trying for a very long time (well a few days) to create a toggle button. A button having a up or down state.
Took over a day to realize it is not possible to create an owner drawn toggle button, a checkbox and pushlike does not work. When using owner drawn there is no difference between a checkbox or regular button (MSDN also notes you cant use owner drawn with any of those styles.)
From reading I found out you have to do it yourself, normally not a problem at all, but I cannot get any real "responsiveness." That is, if I click fast, nothing happens, sometimes I will click and it changes states, other times not and only updates when I click a different button.
I created a global variable for if the button should be shown in up or down state. In the commands I have it set that when the button, IDC_BTN_TOGGLE, it will set the opposite value on the bool.
Then the draw item part:
// button down
if ((pDIS->itemState & ODS_SELECTED) || showButtonDown) {
oldBrush = (HBRUSH)SelectObject(pDIS->hDC, theme.hBrush[BRUSH_BUTTON2]);
}
// button up
else {
oldBrush = (HBRUSH)SelectObject(pDIS->hDC, theme.hBrush[BRUSH_BUTTON]);
}
All my buttons are owner drawn and run through this. showButtonDown is only true when it is drawing IDC_BTN_TOGGLE and the top bool is true as well.
The normal buttons function normally, when I click them, it instantly shows the down state, release, back to normal, the toggle button is barely responsive.

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.