I'm a beginner to both C++ and Qt, so perhaps this is trivial. It certainly feels like it should be simple, but I've been searching for an answer for a few hours now and can't find the solution. I'm making a simple board game where the MainWindow's ui (made in QtDesigner) contains a canvas for the game board (a QGraphicsView). Now, the main.cpp is as simple as can be:
MainWindow Game;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Game.show();
return a.exec();
}
Since I need to access and edit the MainWindow Widgets from another totally unrelated class, I thought the easiest way would be to just make MainWindow a global variable. It seems that this approach was very wrong, though. Upon trying to run the project in QtDesigner I get a Microsoft Visual C++ runtime library error: the application has requested runtime to terminate it in an unusual way.
So what is the correct way to do what I need?
Aside from the MainWindow I have a dialog for a new game (QDialog, generated from QtDesigner) that is displayed after clicking a menu item in MainWindow. When the user inputs all parameters for the game and clicks OK in the dialog, I instantiate a custom non-Qt class called GameState. This class is meant to operate the game itself, draw the board, prompt the user, etc. However, as this class is created in the QDialog, it does not know of the existence of a MainWindow and so I cannot do anything with the MainWindow from this class. How can I modify the MainWindow from an unrelated class, then?
Also, jsut how does the setEnabled() function work? It never seems to do anything. Any widget I set as disabled in the QtDesigner and then try to enable through this function still stays disabled in the GUI...
First off it's a bad idea to create MainGame before you create your QApplication object.
If you want to have your MainGame object globally available like this it should be a pointer:
MainWindow *Game;
int main (int argc, char **argv)
{
QApplication a (argc, argv);
Game = new MainWindow();
Game->show();
int result = a.exec();
delete Game;
Game = NULL;
return result;
}
This approach is however not the most elegant. There are two much better choices.
The QApplication object actually stores all top level windows like your MainGame which means you can allways aquire it through QApplication::topLevelWidgets() which is a static function and returns a list with all top level widgets. Since you only have one, the first one is your MainGame. The drawback is you'll have to cast it, but using Qts qobject_cast<MainGame*>(...) is fairly safe. You'll have to check the result though to make sure it isn't a NULL pointer.
Use the singelton design pattern. You should store the global Game pointer in the source (cpp) file of the Game class itself (subclass QMainWindow) and your Game class should implement a static public method which returns this global pointer. So if any other class needs the Game pointer, it simply calls:
MyGame *theGame = MyGame::getInstance();
for example.
Regarding your setEnabled() problem. Please post the relevant code. If it's too much feel free to send me the *.ui file and the piece of code via mail.
Best regardsD
I am doing it this way:
QMainWindow* getMainWindow()
{
foreach (QWidget *w, qApp->topLevelWidgets())
if (QMainWindow* mainWin = qobject_cast<QMainWindow*>(w))
return mainWin;
return nullptr;
}
If your application has only one window you can simply use:
MainWindow * win = (MainWindow *) qApp::activeWindow();
The easiest way to do this is to first set up a signal in the header file of your other class to say perform a function to manipulate an object in the main class like this
signals:
void disableLoadButtton();
Then create a slot under private slots in the header file of the main window like this
private slots:
void disableLoadButtton();
Then create the function as a members function in the main window to manipulate the object
void MainWindow::disableLoadButton()
{
ui->loadButton->setenabled(false);
}
Then add the following line in another member function of the main window which say sets up the page. My other class is called searchWidget
void MainWindow::setUpPage()
{
connect(searchWidget, SIGNAL(disableLoadButton()), this, SLOT(disableLoadButton()));
}
Then all you have to do to disable the loadButton (which is a object in MainWindow) is to add the following line in any members function of my other class searchWidget
void searchWidget::performSomething()
{
emit disableLoadButton();
}
This will then manipulate the object loadButton in the mainwindow from within a member function of the other class searchWidget.
In the past I used approach described in this answer (found in Qtractor project).
Now I use QObject 'name' property and discover it anywhere as described here.
main.c
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include <QString>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "c.h"
MainWindow * MainWindow::pMainWindow = nullptr;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
pMainWindow = this;
setCentralWidget(&m_pb);
connect(&m_pb, SIGNAL(clicked()), this, SLOT(on_pb_clicked()));
}
MainWindow::~MainWindow() {delete ui;}
// kind of singleton reference.
MainWindow *MainWindow::getMainWinPtr()
{
return pMainWindow;
}
void MainWindow::pbSetText()
{
m_pb.setText(QString{"Call from c."});
}
void MainWindow::on_pb_clicked()
{
c C; // call of MainWindow from class c ctor
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QString>
#include <QPushButton>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
static MainWindow * getMainWinPtr();
void pbSetText();
public slots:
void on_pb_clicked();
private:
static MainWindow * pMainWindow;
Ui::MainWindow *ui;
QPushButton m_pb{QString{"Press me."}, this};
};
#endif // MAINWINDOW_H
c.cpp
#include "c.h"
#include "mainwindow.h"
c::c()
{
MainWindow * mw = MainWindow::getMainWinPtr();
mw->pbSetText();
}
c.h
#ifndef C_H
#define C_H
class c
{
public:
explicit c();
};
#endif // C_H
If you have to access your MainWindow from another window, you are probably doing it wrong. Using another class to pass information with signals/slots is probably a much better approach
Related
I am creating a desktop application in qt using c++ and am struggling with linking two projects together. The first project is a chess game and the second one is from where i want to start . I want to add a new QPushbutton "Play a game" then a new window will open showing the game . I added all .cpp and .h file from the first projet to the main one and i don't know how can i call it the second window . I could've done it if it had a .ui file with just setmodel but i can't because it doesn't have one. I don't know If i am making any sense right now but i'll provide you with screenshots just so you get me more.
The first project (the game): https://github.com/subeshb1/Chess
My project:
enter image description here
the game :
enter image description here
You do not need .ui files. You just have to write the function yourself. I don't know what the main widget that owns the button is called so I'll call it TLWidget for now. and use ... to represent some other code that may be in there.
#include<QtWidgets/QDialog>
#include "game.h" // this is the widget you want in a new window
class TLWidget ... //this is the class definition of your top level widget. it usually owns the thing created from the .ui
{
...
QDialog* m_ChessWindow; //a member is needed to hold or own the window
Game* m_ChessWidget; //the game itself
...
TLWidget (...) //this is the constructor (where the ui item is created or initialized)
{
...
//by making in the constructor, there's only one and it is easy to track.
//you can alternatively spawn a new window per button push by following the 3 calls here: the two new calls below with appropriate parents, and the show() call further below.
m_ChessWindow = new QDialog(this);
m_ChessWidget = new Game(m_ChessWindow);
...
}
...
void SpawnChess() //function that creates your widget. You may want to put other steps in here or slot this, or call it from your button slot function
{
...
m_ChessWindow->show();//this will show the dialog
...
}
...
};
I tried that and i did some other steps too . Here's where am at right now.
Mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "joueur.h"
#include "partie.h"
#include "game.h"
#include
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_ajouter_joueur_clicked();
void on_lancer_partie_clicked();
..etc
private:
Ui::MainWindow *ui;
Joueur J;
Partie P;
*Game game;
};
#endif // MAINWINDOW_H
Mainwindow.cpp
void MainWindow::on_lancer_partie_clicked()
{
game = new Game();
game->show();
game->displayMainMenu();
}
Main.cpp
#include "mainwindow.h"
#include
#include
#include
#include "game.h"
*Game game;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyle("fusion");
MainWindow w;
game = new Game();
w.show();
return a.exec();
}
At this point , a new window pops when i click on the button but the chessboard and the chess pieces are nowhere to be found ! BTW i have all the files and ressources with the right path !
If i do this tho :
Main.cpp
#include "mainwindow.h"
#include
#include
#include
#include "game.h"
*Game game;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyle("fusion");
MainWindow w;
game = new Game();
game->show();
game->displayMainMenu();
w.show();
return a.exec();
This way it works pretty fine not missing a single piece . So i figured it has something to do with the QApplication but i don't know how to use QApplication in mainwindow.cpp
I'm working on a C++ Qt GUI to remote control a ROS robot. I've read that the ros::spin() command should be issued in a seperate Thread so I basically have the usual MainWindow derived from QMainWindow whose constructor sets up the GUI elements, makes the Subscriber Objects subscribe to their respective topic (e.g. image_transport::Subscriber for sensor_msgs/Image topics) and also starts another Thread. For that I have derived a "RosThread" class from QThread that doesn't do anything but starting a ros:MultiThreadedSpinner when RosThread::run() is called.
As you can probably tell, I'm not exactly experienced when it comes to programming in general so my question is, wether the basic concept behind my project makes any sense to you?
Especially should I leave the NodeHandle and the Subscriber Objects in the MainWindow and setup the Subscriptions from the MainWindow Constructor?
Relevant code snippets:
mainwindow.cpp:
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), itLeft(nh), itArm(nh)
{
//subscribe to cameras
imageSubLeft = itLeft.subscribe("/camera_1/image_raw", 1000, &MainWindow::camCallbackLeft, this);
imageSubArm = itArm.subscribe("/camera_2/image_raw", 1000, &MainWindow::camCallbackArm, this);
pagestack = new QStackedWidget;
page1 = new QWidget;
grid = new QGridLayout;
page1->setLayout(grid);
pagestack->addWidget(page1);
labelLeft = new QLabel;
labelMid = new QLabel;
grid->addWidget(labelLeft, 0, 0);
grid->addWidget(labelMid, 0, 1);
this->startSpinThread(); //starts the seperate Thread where run() is executed
this->setCentralWidget(pagestack);
this->setWindowState(Qt::WindowMaximized);
this->setMinimumSize(1024, 768);
}
MainWindow::~MainWindow(){}
void MainWindow::camCallbackLeft(const sensor_msgs::Image::ConstPtr &msg){/*some code*/}
void MainWindow::camCallbackArm(const sensor_msgs::Image::ConstPtr &msg){/*some code*/}
void MainWindow::closeEvent(QCloseEvent *event){/*some code*/}
void MainWindow::startSpinThread()
{
if(rosSpin.isRunning())
{
return;
}
//rosSpin is an Object of the of QThread derived class
rosSpin.start();
}
rosthread.h:
#ifndef ROSTHREAD_H
#define ROSTHREAD_H
#include <ros/ros.h>
#include <QThread>
class RosThread : public QThread
{
Q_OBJECT
public:
RosThread();
protected:
void run();
private:
ros::MultiThreadedSpinner spinner;
};
#endif // ROSTHREAD_H
rosthread.cpp:
#include "rosthread.h"
RosThread::RosThread()
{
}
void RosThread::run() {
spinner.spin();
}
main.cpp:
#include "mainwindow.h"
#include <QApplication>
#include <ros/ros.h>
int main(int argc, char **argv)
{
ros::init(argc, argv, "gui_node");
QApplication app (argc, argv);
MainWindow *win = new MainWindow();
win->show();
return app.exec();
}
Actually, QThread is not intended to be used this way. Take a look at this blog article and this example.
But anyway, I would suggest the standard C++ threads.
Add std::unique_ptr<std::thread> thread; to your MainWindow class instead of the RosThread object.
To start a thread, use thread.reset(new std::thread([](){ static ros::MultiThreadedSpinner spinner; spinner.spin(); });. The smart pointer std::unique_ptr will automatically delete the thread object although you shouldn't forget to use std::thread::join() or std::thread::detach() before the resetting/destroying your std::unique_ptr object.
Another solution would be to put the ros::MultiThreadedSpinner object into your MainWindow class and create a std::thread using thread.reset(new std::thread(&ros::MultiThreadedSpinner::spin, spinner));.
In my opinion, you should put you NodeHandle and Subscriber objects into another class and use an object of this class as a member of MainWindow if they don't belong directly to MainWindow.
I'm working with Qt, using the latest version of Qt Creator on windows 8.1. Once I finished my GUI, I tried to communicate some of my QML elements in C ++ and vice versa, i.e. send data from the two sides.
Example I've tried
I had no idea how to do this, then I have forwarded to read the official documentation and examples from this site, but no one works for me.
Code:
#include <QQmlApplicationEngine>
#include <QDebug>
#include <QObject>
#include <QGuiApplication>
#include <QQuickView>
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot(const QString &msg) {
qDebug() << "Called the C++ slot with message:" << msg;
}
};
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
QObject *item = view.rootObject();
MyClass myClass;
QObject::connect(item, SIGNAL(qmlSignal(QString)),
&myClass, SLOT(cppSlot(QString)));
view.show();
return app.exec();
}
But i'm getting an error:
C:\Users\Tomi\qml\main.cpp:20: error: cannot convert 'QQuickItem*' to 'QObject*' in initialization
QObject *item = view.rootObject();
What I want
All I need is that when a button is pressed from QML, certain data to C++ are requested and when they are ready to be sent to QML. Is this possible?, could you show me a simplistic and functional example?
Thanks!
The error is because the compiler isn't aware of what a QQuickItem is. You need to include it:
#include <QQuickItem>
QQuickItem is only forward-declared in QQuickView's header, for example, so you can't rely on it to include QQuickItem for you, and shouldn't anyway.
Also, the comment about moc not working with classes defined in main.cpp is wrong; you just need to include main.moc after your class definition for the QObject stuff to work:
#include "main.moc"
I dislike this myth, because it turns short snippets and examples into three files, when they could be contained in just one, which is much more useful on Stack Overflow, bug trackers, etc. :)
I'm trying to write a simple Qt program which takes text inside a QLineEdit and appends it into a QTextEdit object when the return key is pressed.
Here is the code for my program:
#include <QApplication>
#include <QtGui>
#define WIDTH 640
#define HEIGHT 480
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QTextEdit textArea;
textArea.setReadOnly(true);
QLineEdit lineEdit;
QPushButton quit("Quit");
QObject::connect(&quit, SIGNAL(clicked()), qApp, SLOT(quit()));
QHBoxLayout hLayout;
hLayout.addWidget(&lineEdit);
hLayout.addWidget(&quit);
QVBoxLayout vLayout;
vLayout.addWidget(&textArea);
vLayout.addLayout(&hLayout);
QWidget window;
window.setBaseSize(WIDTH, HEIGHT);
window.setLayout(&vLayout);
window.show();
//This is the line I can not get to work
QObject::connect(&lineEdit, SIGNAL(returnPressed()), &textArea, SLOT(append(lineEdit.text())));
return app.exec();
}
Essentially, the problem is connecting the QLineEdit returnPressed() SIGNAL to the QTextEdit append() SLOT. I am hoping someone can point out what is wrong with my code.
Thank you very much in advance for your time.
When you run your program, you should notice on the console the following Qt error output..
Object::connect: No such slot QTextEdit::append(lineEdit.text()) in ..
You would need to qualify the append reference in your call to connect with the QTextEdit variable name textArea.
But that's not going to help much because you can only specify signal and slot method names and parameter types when calling connect so you can't specify lineEdit.text() in there.
Since the append() slot expects a QString, ideally you would want to connect a signal that includes a QString but there is no such signal for QLineEdits.
You pretty much have to write a slot yourself that you can connect to returnPressed() and call textArea.append(lineEdit.text()) from there. You will need to subclass a QObject of some kind to write a slot which would usually mean subclassing QWidget and putting all of your UI building code in its constructor.
You might also notice that your program crashes when you close it. Since Qt likes to manage the destruction of most QObjects itself, it is usually best to allocate all QObject instances on the heap with new. This isn't technically necessary all the time but it is much easier :)
QObject::connect(&lineEdit, SIGNAL(returnPressed()), &textArea, SLOT(append(lineEdit.text())));
returnPressed() doesn't take any arguments, but append(QString) does take one argument; a QString. Thus, if this would work, you would theoretically call append(""), meaning you wouldn't append anything. Using lineEdit.text() wouldn't work either at this place.
I would recommend you to create a class for the widget:
class Widget : public QWidget
{
public:
Widget(QWidget parent = 0);
//Other public functions
private:
//Private functions and variables
public slots:
void boom();
};
Then you can just use
Widget w(0);
w.show();
in your main function.
void boom() would be called by returnPressed(), and it would take lineEdit.text() and append it to the QTextEdit.
I hope this helps.
here is the code it might be helpful.....
#include "hwidget.h"
Hwidget::Hwidget(QWidget *parent) :
QWidget(parent)
{
}
void Hwidget::mainform_init(void)
{
lineeditp = new QLineEdit;
quitp = new QPushButton("&Exit");
hboxlayoutp = new QHBoxLayout;
hboxlayoutp->addWidget(lineeditp);
hboxlayoutp->addWidget(quitp,0,0);
vboxlayoutp = new QVBoxLayout;
texteditp = new QTextEdit;
texteditp->setReadOnly(true);
vboxlayoutp->addWidget(texteditp,0,0);
vboxlayoutp->addLayout(hboxlayoutp);
QWidget *mywin = new QWidget;
mywin->setLayout(vboxlayoutp);
mywin->setWindowTitle("My Sig and Slot");
mywin->show();
lineeditp->setFocus();
}
void Hwidget::mcopy(void)
{
qDebug() <<"i am your copy slot";
texteditp->setText(lineeditp->text());
lineeditp->clear();
}
#include <QApplication>
#include "hwidget.h"
int main (int argc, char *argv[])
{
QApplication app(argc,argv);
Hwidget *hwin = new Hwidget;
hwin->mainform_init();
hwin->connect(hwin->quitp,SIGNAL(pressed()),
qApp,SLOT(quit()));
hwin->connect(hwin->lineeditp,SIGNAL(returnPressed()),
hwin,SLOT(mcopy()));
return app.exec();
return 0;
}
#ifndef HWIDGET_H
#define HWIDGET_H
#include <QWidget>
#include <QTextEdit>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QObject>
#include <QString>
#include <QDebug>
class Hwidget : public QWidget
{
Q_OBJECT
public:
explicit Hwidget(QWidget *parent = 0);
void mainform_init(void);
signals:
public slots:
void mcopy(void);
private:
QHBoxLayout *hboxlayoutp;
QVBoxLayout *vboxlayoutp;
public:
QPushButton *quitp;
QLineEdit *lineeditp;
QTextEdit *texteditp;
};
#endif // HWIDGET_H
here is my code:
//MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui>
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QTextEdit *textEdit;
};
#endif // MAINWINDOW_H
// MainWindow.cpp
#include "mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
{
textEdit = new QTextEdit();
}
MainWindow::~MainWindow()
{
delete textEdit;
}
//main.cpp
#include <QtGui>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Is it more efficient (here's the "Q[Objects] VS using Pointers?" part of the question) to:
1) Use pointers as I am actually doing or
2) Use objects (removing * + delete statement)
Thank you!
MainWindow::MainWindow(QWidget *parent)
{
textEdit = new QTextEdit(this);
}
MainWindow::~MainWindow()
{
}
For QObject members as pointers, you shouldn't use delete, the QTextEdit will probably be a child of MainWindow, so it will be deleted automatically.
It would, of course, be theoretically faster to use non-pointer QObject members, with one less level of indirection. Like this (for those who didn't understand the question):
class MainWindow : public QMainWindow {
...
private:
QTextEdit textEdit;
};
and there is also less code to type, because you don't have to retype the class name of the members to initialize them in the constructor.
But since QObject are themselves already heavily using indirection (with their d-pointer), the gain will probably be negligible. And the extra code you type with pointer members allows you to have a lower coupling between your header files and Qt, because you can use forward declarations instead of header inclusion, which means faster compilations (especially if you are not using precompiled headers yet) and recompilations.
Also,
manually deleting QObject pointer members, or
declaring QObject as non-pointers members
can causes double deletion, if you don't respectively delete/declare them in the right order (children then parents for deletion, or parents then children for declaration).
For example:
class MainWindow : public QMainWindow {
...
private:
QTextEdit textEdit;
QScrollArea scrollArea;
};
MainWindow::MainWindow() {
setCentralWidget(&scrollArea);
QWidget *scrolledWidget = new QWidget(&scrollArea);
QVBoxLayout *lay = new QVBoxLayout(scrolledWidget);
lay->addWidget(...);
lay->addWidget(&textEdit); // textEdit becomes a grand-child of scrollArea
scrollArea.setWidget(scrolledWidget);
}
When MainWindow is deleted, its non-static class members are deleted in the reverse order of their declaration in the class. So scrollArea is destroyed first then textEdit, but scrollArea also destroys its children including textEdit, so textEdit is effectively destroyed twice causing a crash.
So it is less error prone to use QObject members as pointers, and to not delete the QObject which have a parent yourself.
Try creating QLineEdit on the stack and then put it into layout... Quit your application... What do you see? HINT: launch your application in debugger.
ksming is right about reading documentation. It is not language specific issue. If you are asking what is faster: heap or stack allocation then form your question properly.