I am new to Qt and was trying to write a simple qt class that can plan a wav file.
After some reading and looking around I wrote the class and the code is as below. Questions follow after code
#include <QtGui/QApplication>
#include "playsound.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
playSound w;
int ch = 2;
int ready = 1;
cout<<"ready"<<ready<<endl;
// w.show();
w.playwav(ch);
return a.exec();
}
Source code for playsound.cpp
#include "playsound.h"
playSound::playSound(QWidget *parent): QWidget(parent) {}
playSound::~playSound() {}
void playSound::playwav(int ch)
{
switch (ch)
{
case 1: {QSound::play("/home/alok/qtworkspace/sounds/abc.wav"); break;}
case 2: {QSound::play("/home/alok/qtworkspace/sounds/xyz.wav"); break;}
case 3: {QSound::play("/home/alok/qtworkspace/sounds/abc.wav"); break;}
case 4: {QSound::play("/home/alok/qtworkspace/sounds/aaa.wav"); break;}
case 5: {QSound::play("/home/alok/qtworkspace/sounds/nnn.wav"); break;}
}
}
Problems and questions:
1) I want to close the application once the sound is played.As of now it says program running and I have to forcefully close it using the red button in the "Application Output" area in Qt creator. I tried using close() from Qwidget and quit() from QApplication but probably I am doing it wrong. How to go abt this?
2) Can there be a simpler implementation for this functionality? You know something that does not involve event loops. I was trying to do it in old school C++ style where I call a function when I need to play a sound and be done with it but could not get it done. Suggestions most welcome on this one.
Thanks
I can offer an answer using PyQt4 (since I'm a python coder), but the idea is the same.
1) QSound is a very basic interface for playing sounds. It doesn't have any useful signals (though I think it really should). In order to know when the QSound is complete, you have to keep checking its isFinished() method. You could either do this in a loop in your main thread, or get more advanced and create a QThread which will play your sound, go into the loop, and then emit its own custom signal when its complete. The QThread is preferred because you should never block your main thread. You would then be able to connect this custom soundIsFinished() SIGNAL to say the close() SLOT of your widget, or any other handler.
If you want more advanced options, you can use the phonon module, which does have all of these various signals built in. Its a litte more annoying to set up, but then you won't need a QThread.
2) Event loops are the core concept of how Qt functions. Your main application always enters an event loop so that widgets can post their events and have them processed. You could technically use Qt without an event loop, but then its really pointless because you are just fighting against the framework and losing everything that its capable of.
To exit from an application, you have to close the top level widget (if you're App has the property verbosely named quitOnLastWindowClosed set to true, but this is default so you don't have to worry with it) or emit a quit signal to the QCoreApplication you've created.
In the example below, I've taken the easy way: emit a close signal.
As stated by jdi, it would be better to create a Thread, but I've understood that you're only learning Qt and wrote this as an example, so busy waiting for isFinished is good enough. Below an example of how it should go:
#include "playsound.h"
playSound::playSound(QWidget *parent): QWidget(parent) {}
playSound::~playSound() {}
void playSound::playwav(int ch)
{
QSound *player = 0; // Creates an object so that you can call player.isFinished()
// the isFinished function is not static.
switch (ch)
{
case 1: {player = new QSound("/home/alok/qtworkspace/sounds/abc.wav"); break;}
// other cases
}
if(!player)
this->close();
while(!player->isFinished()); // waits until the player has finished playing
delete player;
this->close(); // closes this widget, and
// as this Widget has no parent, i.e. it's the "top-level" widget,
// it'll close the app.
}
edit: Shame on me for not reading the docs how I should have. QSound does not have a default constructor, I've edited the code.
A few notes: as this is only a test for you to learn how to use Qt, I've created a pointer to QSound and deleted it afterward. This is not a good approach, you should not play with pointers as I did there, a much better solution would be only instantiating the object you would use. Having to delete things manually is not good, and it's really better to rely on the good ol' stack for that.
Related
In my main window i used this code to open my game application
void MainWindow::on_playButton_clicked(){
CSpaceInwaders* pGame = new CSpaceInwaders(qApp->screens()[0]->size());
pGame->showFullScreen();
pGame->Run();
}
Then in there this is the run function that I called
void CSpaceInwaders::Run(){
scene()->clear();
setCursor(Qt::BlankCursor);
m_pCannon =new CCannon(EColor::Red);
m_pCannon->setPos(m_onScreenSize.width()/2,m_onScreenSize.height()-gCannonSize.height());
m_pCannon->setFlag(QGraphicsItem::ItemIsFocusable);
m_pCannon->setFocus();
scene()->addItem(m_pCannon);
connect(m_pCannon, &CCannon::sigIncreaseScore,this,&CSpaceInwaders::onIncreaseScore);
connect(m_pCannon, &CCannon::sigDecreseScore,this,&CSpaceInwaders::onDecreseScore);
m_pPoints = new CPoints();
scene()->addItem(m_pPoints);
QTimer* pTimer = new QTimer(this);
connect(pTimer, &QTimer::timeout,this,&CSpaceInwaders::onCreateEnemy);
pTimer->start(2000);}
after the game over I want to go back to my main window. So I used this function
void CSpaceInwaders::onGameOver(){
scene()->clear();
QMessageBox msgBox;
msgBox.setText("Game.");
msgBox.setInformativeText("You got hit ! Game Over");
msgBox.setStandardButtons(QMessageBox::Ok);
int ret = msgBox.exec();
switch (ret) {
case QMessageBox::Ok:
close();
MainWindow w;
w.show();
}}
This takes me back to the main window but after few seconds it closes.
I want to know how to fix this
Note : Created using QT
The problem is with the code in the case:
case QMessageBox::Ok:
close();
MainWindow w;
w.show();
The problem is two-fold: First of all you can't actually define variables inside a case like that. You need to add a scope. I'm surprised the compiler doesn't yell at you for that.
The second problem (and that's causing your problem) is that the variable w is a local variable inside the switch statement. Once the statement ends so does the life-time and w and it's destructed and ceases to exist.
The solution (as far as I know) seems to be simple: Don't create and open a new main window! When you start the "space invaders" game you never close the original main window, it's should still be running in the background.
However this is a very bad way to "run" what should essentially be either a separate program or at the very least part of your normal program flow and event loop. Either extract the mini-game into its own program that you then load and execute, or don't create a separate application object and just open a normal window and let the main application event loop handle it.
Using this I solved my problem
void CSpaceInwaders::onGameOver(){
this->close();
}
I created an application using Qt in GNU/Linux and I run in the background. I want to execute certain application functionalities when user presses some key combinations, for example Ctrl+Alt+A...
I know it is possible, Gnome Pie does it but I don't know how I can capture the keys. I tried using the examples provided in this question but none of them worked...also I wouldn't want to run my application as root...
Can anyone point me some resources or give me some hints on that?
EDIT:
#iharob suggested I should use libkeybinder. I found it, tried it but it uses GTK and GTK doesn't play well with Qt...I'm not even a GTK beginner, never worked with it but I think the GTK event loop conflicts with the Qt event loop; when I emit a Qt signal from the callback which gets called after the key was pressed(which is also after gtk_init was called) the application crashes.
What would be great is if I could create a class that emits a signal whenever a keyboard key combination was pressed(e.g. Ctrl+Alt+A).
As far as I see and as #SamVarshavchik pointed out libkeybinder uses libx11 in the background so you could just use libx11 in order to get rid of the GTK event loop which is not very Qt friendly. AFAIK KDE's KAction uses the same technique for their global short keys so I think this technique will play well with Qt's event loop.
These things being said, you can use a hot-key example as presented here:
x11_hot_key.pro:
#-------------------------------------------------
#
# Project created by QtCreator 2015-05-04T01:47:22
#
#-------------------------------------------------
QT += core
QT -= gui
TARGET = x11_hot_key
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
CONFIG += link_pkgconfig
PKGCONFIG += x11
main.cpp:
#include <QCoreApplication>
#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Display* dpy = XOpenDisplay(0);
Window root = DefaultRootWindow(dpy);
XEvent ev;
unsigned int modifiers = ControlMask | ShiftMask;
int keycode = XKeysymToKeycode(dpy,XK_Y);
Window grab_window = root;
Bool owner_events = False;
int pointer_mode = GrabModeAsync;
int keyboard_mode = GrabModeAsync;
XGrabKey(dpy, keycode, modifiers, grab_window, owner_events, pointer_mode,
keyboard_mode);
XSelectInput(dpy, root, KeyPressMask );
while(true)
{
bool shouldQuit = false;
XNextEvent(dpy, &ev);
switch(ev.type)
{
case KeyPress:
cout << "Hot key pressed!" << endl;
XUngrabKey(dpy,keycode,modifiers,grab_window);
shouldQuit = true;
default:
break;
}
if(shouldQuit)
break;
}
XCloseDisplay(dpy);
return a.exec();
}
or you could just use this simple library as presented here which also has some simple examples together with a handy Makefile for you to get along with.
As I don't have knowledge of an asynchronous correspondent to XGrabKey, a problem you will have is that the while(true) loop never returns and blocks the main thread thus the application so what you want is to move that in a separate thread and connect it to the main thread using signals and slots. This shouldn't be a big issue though and won't affect your application's performance because AFAIK XNextEvent blocks until your key is hit so the processor won't be uselessly processing...
Hope this helps.
A brief look at libkeybinder's very small source indicates that all it does in install a keygrab on the X display's root window.
This should be doable, but it won't be easy, and requires some knowledge and understanding of the low level X Window System protocol. It should be possible for both Qt and libxcb to coexist peacefully in one process. The way I would try to implement something like this would be as follows:
Start a separate thread.
The thread would open a separate connection to the X server, enumerate all screens on the display, obtain each screen's root window, install a key grab on each root window, then enter a loop reading X events from the xcb_connection_t handle.
Upon receipt of a key event (the only key events I expect to process in this loop would be the ones corresponding to the grabbed key), immediately ungrab the keyboard so that the X server can proceed on its merry way, then notify your application's main thread, in some form or fashion, that the key has been pressed.
Your application will have to have some means of stopping this thread, when it's time to quit.
Possible solution would be to simulate this behavior - have a small standalone application that sends signal to your background process (there are many variants doing this, signal() would be probably the simplest). Then attach that application for desired key binding in window manager for particular environment. It may require to learn how to do that for various window managers, but result could be cleaner and faster to implement.
I wrote in C++ a solver for the 8-puzzle game, and now I'm trying to use Qt to give it a GUI.
Basically I have an underlying object of type "Board" which represents the board of the puzzle, and I have organized the GUI as a grid of QPushButton. Then I have a method updateUI which associates to every button the correct text, based on the Board. Something like
for(int i=0; i<Board::MATRIX_DIM * Board::MATRIX_DIM; i++)
{
m_buttons[i]->setText(m_values[i]);
}
In another method (solveGUI) I have
void MainWindow::solveGUI()
{
m_game->solve();
int solutionDepth = m_game->getSolutionDepth();
Move *solutionMoves = m_game->getSolutionMoves();
for(int i=0; i<solutionDepth; i++)
{
Move m = solutionMoves[i];
m_board.performMove(m); /* perform the move on the Board object */
updateUI(); /* should update the GUI so that it represents the Board */
Sleep(1000);
}
}
where the first line (m_game->solve) takes some time. Then I obtain a list of the moves performed, in solutionMoves, and what I would like to do is showing this moves on the board, with some delay between a move and the next one. This method is called by my main, which looks like this:
QApplication app(argc, argv);
MainWindow w;
w.show();
w.solveGUI();
return app.exec();
The result is that the GUI hangs and, after some time, it displays only the solution, completely skipping the moves.
What am I missing? Thank you!
P.S. I don't think I need a different Thread for the solver because I want the solver to run before the solution is displayed. Is it right?
It's app.exec() that actually runs the main loop which handles all events, including displaying GUI. If you want to call solve() before that, it's OK, but if you want to actually display and update GUI before exec(), it's wrong. I'm not sure if it's totally impossible, but it's definitely not the right way to do it.
There are two ways around it. The more canonical way is to redesign a program using a QTimer. Then everything will be smooth and responsive. But that can be tedious sometimes. In your case it should be quite easy, though. Just save the results somewhere, and call a slot using a QTimer object every 1000 seconds - it will have the same effect as your Sleep(), but will keep everything responsive.
The other solution is to call your solveGUI() method after exec() starts its job. It can be done, for example, using QTimer::singleShot():
QTimer::singleShot(0, &w, SLOT(showGUI()));
return app.exec();
Then, before each Sleep(), you should call QApplication::processEvents(), which basically allows you to temporary yield control, processing all pending events, including GUI updates. This approach is somewhat easier, but it's inferior since the GUI still freezes at each Sleep(). For example, if the user wants to exit the application, or if the window is needed to be repainted, it will cause uncomfortable GUI lags.
You're stalling the main thread (which also does the event processing) and rendering it uncapable of responding to keyboard/mouse/window messages.
You should use an asynchronous timer operation instead of the sleep function: use a QTimer to delay showing the next solution and avoid messages being left unanswered for too long.
There is a nice article of methods to keep the GUI responsive during processing loops. if it's not a complicated case I think, just insert QCoreApplication::processEvents(); inside the long processing loops.
try the following:
void MainWindow::Wait(int interval ) {
QTime timer = new QTime;
timer.restart();
while(timer.elapsed() < interval) {
QApplication::processEvents();
}
}
...
for(...) {
//wait 1 second (1000 milliseconds) between each loop run at first
Wait(1000);
...
}
...
not tested yet - but should work (maybe there is some cpu load)!
I'm developing a game with QtQuick 2 (Qt5.2) QML and C++. I want most of the game-logic in C++ (I don't want to do it with JS), and I'm trying to use QStateMachines for a lot of the logic.
Now my question is, how do I implement a proper game-loop in that scenario?
The game has for example objects that are moving from and to certain waypoints on the screen, so I think I can't do everything state/event-based. At some point I need to say "my object should move 10 pixels in direction x every second". So for example when my object is in its state "moving", it should move by a certain amount every second and then of course check for some conditions if it has to switch the state (e.g. to "attacking").
Now all the demos in the Qt-examples and on the web seem to be either fully event-based (e.g. four-in-a-row-wins-like) or written in JavaScript. So I am a bit lost here.
One idea I could find was to create a QTimer with a timer of e.g. 30ms and connect that QTimer's timeout() signal to an advance() slot of every moving object, and start that timer before 'return app.exec();'. Like this:
QTimer timer;
QObject::connect(&timer, SIGNAL(timeout()), &scene, SLOT(advance()));
timer.start(1000 / 33);
return app.exec();
and then each object (e.g. the Mouse) has a
void Mouse::advance(int step)
However, this requires a QGraphicsScene and I'm not sure how well that goes with a QtQuick/QML project on Android/iOS.
Is that a good solution? Or is my view of the problem somehow wrong and I don't need a game loop to accomplish my goal?
The solution shouldn't use any desktop-only stuff from Qt, i.e. it should work on Android, iOS and desktops.
That's the way to go: QTimer. Here you find some detailed example on it:
A typical loop for game in Qt:
int main(int argc, char* argv[]) {
// init stuff
while(game.isRunning()) {
a.processEvents(); //(a is a QApplication created during the init, should use a better name i guess)
QTime currentTime= QTime::currentTime();
int timeSinceLastUpdate = lastUpdate.msecsTo(currentTime);
while(timeSinceLastUpdate>updateTimeStep){
game.update();
timeSinceLastUpdate-=updateTimeStep;
lastUpdate=lastUpdateaddMSecs(updateTimeStep);
}
renderer.setInterpolateFraction(static_cast<float>(timeSinceLastUpdate)/static_cast<float>updateTimeStep);
renderer.renderGameObjects();
renderer.renderGUI();
renderer.swap();
}
a.exit();
return 0;
}
Source: Game loop in Qt
Making a Simple Game Loop with QTimer
Qt as a game engine
That's should be enough info for you to get started.
Usual game loop of simple game can look like this (not sure if I understand you correctly though).
Each class that represents game object have 2 public methods: update(); and render();
On each call of QTimer object you iterate over all game objects and call their update method. After it you repeat the same for render method();
In update methods each object decides what to do on game map (move/shot/stand/...) and changes its coordinates/properties. In render methods each object just draws itself on display.
I'm having an issue. My textEditBox doesn't seem to be updating when my Addtext function is called.
Here's my Addtext:
void CTextBox::AddText(QString string, QString spriteString)
{
textBrowser->setText(string + spriteString);
update();
}
Another class then calls the function and it should add text to the textbox but it doesn't.
How do you call CTextBox::AddText()? update() only schedules a paintEvent() for later, when the program returns to the event loop. That means that
you actually need to have an event loop, ie. at some point you need to call qApp->exec();
you need to allow the programm some time to qApp->processEvents() (insert that after update()), if you want any paining done within a blocking while() {...} or something like that.
Edit: Come to think of it, you shouldn't even need to call update() nor processEvents() if your program returns to the event loop some time after AddText, so there really seems to be an issue with the event loop. Post your main.cpp, will you?
Here is a trivial example of what it sounds like you are trying to do. Maybe you can see where your design differs?
Notice: no explicit update() is needed. I think that's a red herring. I think it far more likely that (1) you are somehow calling your AddText method with empty strings, or (2) your real text edit is a different variable, and have somehow created two of them and are updating one that just exists in memory and was never added to a layout. Perhaps the code of your FileLoaderQT would help? (You can edit your question rather than posting in comments.)
#include <QtGui>
int main(int argc, char **argv) {
QApplication app(argc, argv);
QMainWindow w;
QTextEdit *edit = new QTextEdit;
w.setCentralWidget(edit);
edit->setText("Hello world!");
w.show();
edit->append("Hello world again!");
return app.exec();
}