I'm developing a Qt 4.8.4 GUI application targeting Windows 7. I'm trying to implement the "Solving a Problem Step by Step" approach to keep the GUI responsive during a long-running computation, which is nicely divisible into many small steps.
Here is a minimal working example of this technique:
Computation.h
#pragma once
#include "QtCore/QCoreApplication"
#include "QtCore/QDebug"
#include "QtCore/QObject"
#include "QtCore/QTimer"
class Computation : public QObject {
Q_OBJECT
public:
Computation() : amount_(0) {}
public Q_SLOTS:
void start() {
amount_ = 100000;
QTimer::singleShot(0, this, SLOT(calculate()));
}
private Q_SLOTS:
void calculate() {
if (--amount_ > 0) {
qDebug() << "Calculating..." << amount_;
//QCoreApplication::processEvents();
QTimer::singleShot(0, this, SLOT(calculate()));
} else {
qDebug() << "Finished";
}
}
private:
int amount_;
};
main.cpp
#include "Computation.h"
#include "QtCore/QDebug"
#include "QtGui/QApplication"
#include "QtGui/QMainWindow"
#include "QtGui/QPushButton"
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
Computation computation;
QMainWindow window;
QPushButton button(&window);
button.setText("Test");
QObject::connect(&button, SIGNAL(clicked()), &computation, SLOT(start()));
window.show();
return app.exec();
}
There's also a CMakeLists.txt for this example in case anyone wants to try it out.
Now, on to the actual problem: when the computation is running, certain GUI interactions lead to a lockup of the mouse. The cursor can still be moved, but clicking on any part of the desktop has no effect at all in this state. The computation still goes on. The only way to escape this lockup is to switch to another application with the keyboard (e.g. Alt+Tab, pressing the Windows key, or Ctrl+Alt+Del) or to wait until the computation has finished.
The GUI actions which lead to this state include attempting to move or resize the application's main window. Instead of changing the window's geometry, the mouse lockup explained above happens. However, the window instantaneously jumps to the position is was to supposed to be moved to as soon as the computation finishes (and you didn't switch to another window in between).
Opening the system menu (by clicking on the application symbol in the title bar) also leads to similar behavior, but this time only the application (including the system menu) is indifferent to mouse clicks.
I tried to work around this problem by issuing QCoreApplication::processEvents() in my calculate() method (commented line in the above example). This only helped a little: instead of locking up the mouse every time one tries to move or resize the window, you now have to do it about 3-5 times to trigger the behavior. Different combinations of QCoreApplication::sendPostedEvents() and QCoreApplication::flush() didn't help either.
How can I solve this problem? Is this a known Qt bug and/or is there a workaround?
If you need to make an application multi-threaded, you can use Qt's slots and signals architecture. Just emit a signal from your thread when something changes. Then in your UI thread, connect a slot to the signal from your thread:
Qt: Background thread refreshing UI thread
Using QThread is definitely a better strategy than chunking the computation and trying to do everything in one thread.
Then in the calculate thread, use a timer every 1/15 sec (for example) to call a signal that has been connected to a slot in the main thread, which informs the UI to update any data you want to see from the calculations.
Related
When I run and exit my applications, it leaves too many tray icons in the tray instead of just one. I have also setup my application so only one instances can be instantiated at one time but still after several start and exit of the program, system tray seems to accommodate all the icons which than slowly drops when I hover mouse on them to last (legit) one. How can I stop creating this duplicate icons?
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
systemTray = new QSystemTrayIcon();
systemTray->setIcon( QIcon(":icons/Resources/Orange.ico") );
systemTray->setVisible( true );
systemTray->show();
systemTray->setToolTip("Rigaku sync application");
systemTrayIconMenu = new QMenu();
systemTrayIconMenu->addAction( ui->actionOpen_App );
systemTrayIconMenu->addAction( ui->actionSettings );
systemTrayIconMenu->addAction( ui->actionClose );
systemTrayIconMenu->addAction( ui->actionQuit );
systemTray->setContextMenu( systemTrayIconMenu );
}
I delete the systemTray pointer in the destructor.
Since we at it, I also want to be able to double click the tray icon which should bring up the app. How can I do that? I understand I have to setup default option on double click (which also appears bold in context menu) but how can I do that? Thanks!
Update
I can show the default menu now with setDefaultAction() and double click on tray. Now my only issue is how to get rid of extra icons in system tray.
If I understood correctly, you are using the C/C++ exit function.
In order to properly quit the Qt application, you'll have to call this function:
QCoreApplication::quit(); // Return code is 0
If you would like to specify the return code, use the following function:
QCoreApplication::exit(YOUR_RETURN_CODE);
You can also use QApplication instead of QCoreApplication, there is no difference.
So, when using one of these methods, the tray icon is correctly destroyed after you exit your application.
I have a sort of UI being made in a 'responsive' like manor, drawing a box at the bottom of the terminal using tput.
I was wondering if there is anyway to have it "redraw" the box when the terminal is resized?
You should be able to trap this event with bash, like this:
trap 'do something' SIGWINCH
Yes. ncurses, the program which handles screen manipulation in linux (not bash!) sends a signal out when the window size is changed: SIGWINCH (window size changed). You'll want to handle the event and call a function to redraw your program when this event is received, which differs in implementation based on which programming language you're using. But for example, in C, it would be something like:
#include <signal.h>
void handleResize(int dummy)
{
// redraw things here
}
int main(...)
{
signal(SIGWINCH, handleResize);
...
}
Hope that helps!
I have a MFC-based ActiveX control, where some important things do not work. A size-event is never called and the controls contents are redrawn only when I click the border of the control (in ActiveX test container).
That's my code for the size-event in header file:
public:
afx_msg void OnPaint();
...and in source file:
BEGIN_MESSAGE_MAP(CBeamConstruXCtrl, COleControl)
ON_WM_SIZE()
ON_WM_PAINT()
ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()
void MyCtrl::OnPaint()
{
//this is never called also when I change the size of the control in test container
}
Any ideas what is missing here?
Edit: just a clarification: OnSize() is called once in initialisation phase of the OCX, but never when I change the controls size.
As mentioned by Roger Rowland: OnDraw() does the trick.
I'm writing a game that currently runs in both Windows and Mac OS X. My main game loop looks like this:
while(running)
{
ProcessOSMessages(); // Using Peek/Translate message in Win32
// and nextEventMatchingMask in Cocoa
GameUpdate();
GameRender();
}
Thats obviously simplified a bit, but thats the gist of it. In Windows where I have full control over the application, it works great. Unfortunately Apple has their own way of doing things in Cocoa apps.
When I first tried to implement my main loop in Cocoa, I couldn't figure out where to put it so I created my own NSApplication per this post. I threw my GameFrame() right in my run function and everything worked correctly.
However, I don't feel like its the "right" way to do it. I would like to play nicely within Apple's ecosystem rather than trying to hack a solution that works.
This article from apple describes the old way to do it, with an NSTimer, and the "new" way to do it using CVDisplayLink. I've hooked up the CVDisplayLink version, but it just feels....odd. I don't like the idea of my game being driven by the display rather than the other way around.
Are my only two options to use a CVDisplayLink or overwrite my own NSApplication? Neither one of those solutions feels quite right.
I am curious to see if anyone who has actually done this cares to weigh in, but here is my understanding:
Apple pushes the CVDisplayLink solution over doing a loop on the main thread that uses -nextEventMatchingMask:untilDate:inMode:dequeue: because, I think, it provides better responsiveness for UI controls. This may not be relevant for full-screen games. (Note: You don't need to replace NSApplication to use that form of game loop.) I think the main potential issue with using CVDisplayLink is that it will only run one frame in advance and it does this determination early, which is even stronger than vertical sync. On the plus side, it might improve latency.
Other solutions include decoupling rendering from game logic and running game logic periodically on the main thread and rendering on the CVDisplayLink thread. I would probably only recommend this, however, if you run into issues with the game-driven-by-display paradigm.
You don't necessarily have to make your own NSApplication based class or use CVDisplayLink to get around the fact that an app's runloop is hidden from you in Cocoa.
You could just create a thread and have your run loop in there instead.
For what it's worth though, I just use CVDisplayLink.
I'm sticking something up here to revive this question...mainly out of portability. I found from studying the OLC Pixel Game Engine, that it works with a do{}while loop and std::chrono to check the timing of the frame to calculate fElapsed Time. Below is some code I wrote to do the same thing. It also adds a makeup portion, to govern the framerate from shooting above a certain value, in this case, 60 FPS.
c++ code
int maxSpeedMicros = 16700;
float fTimingBelt; //used to calculate fElapsedTime for internal calls.
std::chrono::steady_clock::time_point timingBelt[2];
bool engineRunning = false; //always have it true, until the engine stops.
bool isPaused = false;
do {
timingBelt[1] = std::chrono::steady_clock::now();
fTimingBelt = std::chrono::duration_cast<std::chrono::microseconds>(timingBelt[1] - timingBelt[0]).count() * 0.000001;
if (isPaused) {
do {
std::this_thread::sleep_for (std::chrono::milliseconds(100));
timingBelt[1] = std::chrono::steady_clock::now();
} while (isPaused);
}
timingBelt[0] = std::chrono::steady_clock::now();
// do updating stuff here.
timingBelt[1] = std::chrono::steady_clock::now();
int frameMakeup = std::chrono::duration_cast<std::chrono::microseconds>(timingBelt[1] - timingBelt[0]).count();
if (frameMakeup < maxSpeedMicros) {
int micros = maxSpeedMicros - frameMakeup;
std::this_thread::sleep_for (std::chrono::microseconds(micros));
}
} while (engineRunning);
However, that code was in direct conflict with Cocoa's event driven model.
Custom main application loop in cocoa
So as a bandaid, I commented out the whole loop, and created a new method that runs one iteration of the loop. I then implemented this in my AppDelegate:
Objective C Code
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
engine->resetTimer();
[NSTimer scheduledTimerWithTimeInterval:0.016666666667 target:self selector:#selector(engineLoop) userInfo:nil repeats:YES];
}
-(void) engineLoop { //Let's handle this by the engine object. That's too complicated!
engine->updateState();
[glView update]; //Since the engine is doing all of its drawing to a GLView
[[glView openGLContext] flushBuffer];
}
Still to do is adjust the tolerance of the timer object. Apple Developer documentation states that if a timer object misses the next window, it will wait for the next frame time. However, a tolerance allows it to shift the timing of future events to make smoother framerate transitions and better use of CPU power.
So at this point I am open to suggestions and input about what others have done to make more portable code. I am planning on a boolean argument in the constructor of the engine named "eventDriven" and if false, will start its own game loop thread, then split out the top event loop to call an "engineUpdate" method that handles all of the code that can be event driven. Then in the case of building on an event driven system, the delegate can just construct the engine with a engineUpdate = TRUE and have their events drive the gameUpdate.
Has anyone done this? and if so, how does it perform cross platform?
I need to perform several operations on a list of windows (minimize some of them, restore others) in order to switch between two or more set of windows at once.
The problem with this are those animations you can see when minimizing and restoring a window. The whole process look terrible with all those animations going in and out, up and down.
I cannot, however, disable those animations because this is for other computers and i dont want to change other people's settings, plus those animations are actually useful when you minimize/restore one window only (i.e. when YOU do it manually) because you can see what is happening, but for doing it programmatically on several windows at a time, it's not nice.
I'm currenlty using the SendMessage function to send the WM_SYSCOMMAND message with params SC_MINIMIZE/SC_RESTORE. I dont know whether there is another way.
So, the question:
How can I minimize/restore a window programatically without the animation effect??
PS: The programming language is not important. I can use any language that's nessesary for accomplishing this.
SetWindowPlacement with SW_SHOWMINIMIZED or SW_RESTORE as appropriate for showCmd in WINDOWPLACEMENT seems to bypass window animation. I'd keep an eye on the functionality for future versions of the OS though since documentation does not mention anything about animation.
How about Hide > Minimize > Show ?
You could temporarily disable the animations and then restore the user's original setting.
class WindowsAnimationSuppressor {
public:
WindowsAnimationSuppressor() : m_suppressed(false) {
m_original_settings.cbSize = sizeof(m_original_settings);
if (::SystemParametersInfo(SPI_GETANIMATION,
sizeof(m_original_settings),
&m_original_settings, 0)) {
ANIMATIONINFO no_animation = { sizeof(no_animation), 0 };
::SystemParametersInfo(SPI_SETANIMATION,
sizeof(no_animation), &no_animation,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
m_suppressed = true;
}
}
~WindowsAnimationSuppressor() {
if (m_suppressed) {
::SystemParametersInfo(SPI_SETANIMATION,
sizeof(m_original_settings),
&m_original_settings,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
}
private:
bool m_suppressed;
ANIMATIONINFO m_original_settings;
};
void RearrangeWindows() {
WindowsAnimationSuppressor suppressor;
// Rearrange the windows here ...
}
When the suppressor is constructed, it remembers the user's original setting and turns off the animation. The destructor restores the original settings. By using a c'tor/d'tor, you ensure that the user's settings are restored if your rearranging code throws an exception.
There is a small window of vulnerability here. In theory, the user could change the setting during the operation, and then you'll slam the original setting back. That's extremely rare and not really that bad.