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();
}
Related
I'm trying to create a C++ Widget Application in QT with multiple windows where you can return to the mainwindow by clicking a Pushbutton.
I'm a complete noobie so I try to follow YouTube tutorial and for opening windows by pushbuttons I watched this one (minute 8:00): https://youtu.be/tP70B-pdTH0
It works when opening secondary windows from the main one but if I try to do the same from a secondary windows to the mainwindow it doesn't. It appears an error "cannot initialize object parameter of type 'Widget' with an expression of type 'MainWindow'"
in the source file I wrote:
void Crediti::on_pushButton_clicked()
{
close();
mainwindow = new MainWindow(this);
mainwindow->show();
}
mainwindow->show(); is the incriminated part
I also included mainwindow in the header of the secondary window and specified the class
MainWindow *mainwindow
in the header so It recognizes mainwindow initially in the source.
I'm doubting if doing this thing is possible at all, and if not so how can I make a pushbutton that, when clicked, can redirect me to the mainwindow?
Please I need this for a school application, thanks
So here you're creating a new main window each time you click on the button. From your description that's not the behaviour you want. I understand you have an application with a main window and other secondary windows and want to bring up the main window when clicking on the button, assuming the main window still exists somewhere and hasn't been deleted.
What I would try is to find the main window when hitting the push button and show / raise it, something along the line of:
#include <QApplication>
#include "MainWindow.h" // Adapt that one to you main window header
// ... some code of your secondary window
void SecondaryWindow::on_pushButton_clicked()
{
for(auto widget : QApplication::topLevelWidgets())
{
// This will return a nullptr if the widget is not the main window
auto mainWindow = dynamic_cast<MainWindow*>(widget);
// skip if not the main window
if(!mainWindow)
continue;
// Show it if hidden
if(mainWindow->isHidden())
mainWindow->show();
// raise it, as in bring it forward, over all other windows
mainWindow->raise();
}
// eventually close the current window if that's what you want
close();
// if you close it and don't need it any more you might also want to delete it
deleteLater();
}
Note that this function won't do anything if the main window has been deleted in the meantime, which might be the case if you closed it and the Qt::WA_DeleteOnClose attribute is set.
Hope that helps.
I went throught suggested "questions" about my problem. However neither does not solve it.
I program two windows. The second window is opening from first window. I need active the both windows, however to start the first window(MainWindow) I use:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.setWindowModality(Qt::NonModal);
return a.exec();
}
As was mentioned, the second window is started from pushButton, which is situated in first window(MainWindow) by same way.
void MainWindow::on_pushButton_2_clicked()
{
Graphics gr;
gr.setWindowModality(Qt::NonModal);
gr.exec();
}
I changed modality to NonModal,however the problem is without change. The Non-Modal mean:"The window is not modal and does not block input to other windows." <- from documentation
By documentation is recommended to avoid used .exec(). The alternatives are .show() and open(), which i tried. After the modification, the second window is shut down immediately after opening. after open immediately shut down.
Do you have any idea, how to solve that?
Graphics gr; defines a local variable, so the object is destructed as soon as it goes out of scope (at the end of your function).
In Qt, the typical approach is to work with pointers to Qt widgets (and, more generally, QObjects), but have a parent for each – the parent will clean it up.
Try this instead:
auto gr = new Graphics(this);
gr->setWindowModality(Qt::NonModal); // this is the default, no need for this call
gr->show();
This way the window will survive until your main window is destructed. In Qt there is also a mechanism for widgets/objects to self-destruct:
this->deleteLater();
So for example if you want to close and cleanup gr you can use that mechanism (you could also store gr as a member and call gr->deleteLater().
It feels like this question has been asked about a hundred times before (e.g. here) but I haven't found a working solution yet..
I have a Qt5 program (Linux) which takes some time (about 2sec) for initialization. I don't want to spawn a thread (for several reasons) and before initialization is done the program is not usable anyway.
Currently the program starts and it shows a black window, until initialization is done.
I'd like to have the window content be drawn as soon as possible and queue a method which does the rest which gets executed right after the main window has been drawn.
This is what I tried:
class my_window : public QMainWindow {
Q_OBJECT
explicit my_window(QWidget *parent = 0) : QMainWindow(parent) {
initializeUI();
/// UI is ready and should be drawn. initializeRest() should
/// be queued
/// tried to repaint() or update() the MainWindow and to 'force'
/// processing outstanding events - with no effect
update();
repaint();
QApplication::processEvents();
/// don't call - just queue
QMetaObject::invokeMethod(this, "initializeRest", Qt::QueuedConnection);
}
void initializeRest() {
// do stuff which takes a while
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
But the main window stayes black until initializeRest() has been executed.
How can I tell Qt to execute initializeRest() right after the window has been constructed?
I can think of starting a timer (bad, introduces extra latency) or an event handler which reacts on some kind of "WindowDrawn" event (bad, complicated).
What's the Qt-way to do this?
Update:
I've also tried to put the initializeRest() method into the main() function like suggested by Murphy:
my_window::my_window(QWidget *parent = 0) : QMainWindow(parent) {
initializeUI();
}
int main(int a_argsc, char *a_argsv[]) {
QApplication l_application(a_argsc, a_argsv);
my_window mainWindow;
mainWindow.show();
QApplication::processEvents();
mainWindow.initializeRest();
return l_application.exec();
}
With same results: Waiting for a couple of seconds inside initializeRest() makes show up the initially black main window and be drawn right after initializeRest() returned (which seems to be logical to me because the event loop has not been started yet..)
Note: This suggestion doesn't solve the issue; it's left here for completeness.
You can split the startup into smaller steps in main():
Create the QApplication instance.
Instantiate the main window (I'll call the variable mainWindow here). You can safely remove all that repaint-workaround stuff after initializeUI(); from the constructor of your code example.
Call mainWindow.show() to enforce showing the main window, followed by a call to QApplication::processEvents() to enforce the paint events being handled.
Do all the other initialization stuff of your application.
Start the event loop as usual by calling QApplication::exec().
Be aware that with complex applications/main window implementations it can get quite hairy to do everything in the right order; a QSplashScreen would surely be the less tedious solution.
I have the same problem. I think the problem is based not on show() function. Try to run the next code.
If add a button to boxLayout the application starts fast. But if I try setGeometry() the application takes a long time to start.
I'm using the QT Creator on Ubuntu.
I have GUI with a mainwindow and another window called "progress".
Upon clicking a button the QProcess starts and executes an rsync command which copies a folder into a specific directory. I created a textbrowser which reads the output from the rsync command. Also clicking the button causes the "progress" window to pop up.
So far so so good, now my problem.
Instead of showing the rsync output in my mainwindow i want it to be in progress.
I've tried several methods to get the QProcess into the progress via connect but that doesn't seem to work.
mainwindow.cpp
void MainWindow::on_pushButton_clicked()
{
if (ui->checkBox->isChecked()
)
m_time ="-t";
QObject parent;
m_myProcess = new QProcess();
connect(m_myProcess, SIGNAL(readyReadStandardOutput()),this, SLOT(printOutput()));
QString program = "/usr/bin/rsync";
arguments << "-r" << m_time << "-v" <<"--progress" <<"-s"
<< m_dir
<< m_dir2;
m_myProcess->start(program, arguments);
}
progress.cpp
void Progress::printOutput()
{
ui->textBrowser->setPlainText(m_myProcess->readAllStandardOutput());
}
I know it's pretty messy iv'e tried alot of things and haven't cleaned the code yet also I'm pretty new to c++.
My goal was to send the QProcess (m_myProcess) to progress via connect but that didn't seem to work.
Can you send commands like readyReadAllStandardOutput via connect to other windows (I don't know the right term )?
Am I doing a mistake or is there just another way to get the output to my progress window ?
m_myProcess is a member of the class MainWindow and you haven't made it visible to the class Progress. That's why you have the compilation error
m_myProcess was not declared in this scope
What you could do:
Redirect standard error of m_myProcess to standard output, such that you also print what is sent to standard error (unless you want to do something else with it). Using
m_myProcess.setProcessChannelMode(QProcess::MergedChannels);
Make the process object available outside MainWindow
QProcess* MainWindow::getProcess()
{
return m_myProcess;
}
Read the process output line by line in Progress. It needs to be saved in a buffer because readAllStandardOutput() only return the data which has been written since the last read.
... // somewhere
connect(window->getProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput())
...
void Progress::printOutput(){
//bigbuffer is member
bigbuffer.append(myProcess->readAllStandardOutput();)
ui->textBrowser->setPlainText(bigbuffer);
}
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.