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. :)
Related
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 am trying to make the code found on this page compile. All I want to do is replicate what that page describes. I keep getting an error that says:
main.cpp:13: error: undefined reference to `vtable for myMainWindow'
Here's my code, which is pretty much exactly like the code on that page:
main.cpp
#include <QApplication>
#include <QDialog>
#include <QWidget>
#include <QGridLayout>
#include <QPushButton>
#include <QMainWindow>
#include <QBitmap>
class myMainWindow : public QMainWindow
{
public:
myMainWindow():QMainWindow()
{
setMask((new QPixmap("saturn.png"))->mask());
QPalette* palette = new QPalette();
palette->setBrush(QPalette::Background,QBrush(QPixmap("saturn.png")));
setPalette(*palette);
setWindowFlags(Qt::FramelessWindowHint);
QWidget *centralWidget = new QWidget(this);
QGridLayout *layout = new QGridLayout();
centralWidget->setLayout(layout);
QPushButton* button1 = new QPushButton("Button 1");
button1->setFixedSize(80,50);
layout->addWidget(button1,0,0);
setCentralWidget(centralWidget);
};
~myMainWindow();
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
myMainWindow *window = new myMainWindow();
window->resize(600, 316);
window->show();
return app.exec();
}
I read up on why this might be occurring and saw that it was something about needing to have the class defined in a header file. How would I go about doing that correctly given this code?
Aside from missing the destructor definition, you are also missing the Q_OBJECT macro, which is mandatory for all QObject derived classes. If you had that, you'd get another error about the MOC generated files, when you define QObject derived classes in main.cpp you need to include the MOC file manually. This is not the case if you use a dedicated h and cpp file for QObject derived classes.
As stated in the comments: The missing function body for ~myMainWindow() was the problem.
Hi i have made a gui in qt4 designer and want to add custom slots with custom class.
It project compiles nicely without errors but my custom function wont work what am i doing wrong? I will show u the header file qt4 designer made for me and ill show u my custom file as well as the main.cpp.. first the main.cpp
I have revised my code, here is what i have now i have added a file called sweetest.cpp and edited the sweetest.h here are my new file and the error i recieve..
First main.cpp
#include "ui_sweetguiform.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget *widget = new QWidget;
Ui::SweetGuiForm ui;
ui.setupUi(widget);
widget->show();
return app.exec();
}
now my custom header file sweetest.cpp
#include "sweetest.h"
// trying to include the .moc file wouldnt work at all.
now the sweettest.h file with my code
#include "ui_sweetguiform.h"
class SweetGuiForm : public QWidget
{
Q_OBJECT
public:
SweetGuiForm( ): ui( new Ui::SweetGuiForm )
{
ui->setupUi( this );
connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(on_buttonBox_accepted()));
}
public slots:
void on_buttonBox_accepted()
{
ui.textEdit->setText(QString::number(23));
}
protected:
Ui::SweetGuiForm* ui;
};
Here is the compile error i recieve.. I am really stuck
In file included from sweetest.cpp:1:
sweetest.h: In member function ‘void SweetGuiForm::on_buttonBox_accepted()’:
sweetest.h:16: error: request for member ‘textEdit’ in ‘((SweetGuiForm*)this)->SweetGuiForm::ui’, which is of non-class type ‘Ui::SweetGuiForm*’
make: *** [sweetest.o] Error 1
I think im getting closer
The way that signals and slots work is that you must connect a signal to a slot. In your code, the simplest way to do that is in the constructor for the SweetGuiForm. You need to add:
SweetGuiForm() : ui(new Ui::SweetGuiForm) {
ui->setupUi(this);
connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(on_buttonBox_accepted()));
}
When the buttonBox emits its accepted signal all slots connected to it will be called.
update 1
On further inspection of your code, you are also missing the Qt macros that are used by the Qt MOC (meta-object compiler) system (http://qt-project.org/doc/qt-4.8/moc.html):
class SweetGuiForm : public QWidget
{
Q_OBJECT
public:
...
};
You also have to push the code through the MOC tool. It will generate a source file that needs to be included in your source. As I recall, you must include that in a cpp file; inclusion in a header is problematic. The following should be sufficient:
sweetguiform.cpp:
#include "suiteguiform.h"
#include "sweetguiform.moc"
update 2
On further further reflection, I had forgotten about the automatic signal/slot connection feature when you name your slots using special names (such as on_buttonBox_accepted). There is a blog post on just that here: http://qtway.blogspot.com/2010/08/automatic-connections-using-qt-signals.html. I've not used it myself, so I can't comment on its ability to work when using a ui member variable, though I suspect that it does not work in that arrangement. Regardless, you still need the Q_OBJECT macro and MOC.
Ok guys i figured it out and thought ide share what i found.
First the documentation is excellent in qt4 use it.
I found you can use qt4 designer to generate the hearder files, first i complied it with out custom slots and generated the ui_sweetgui2.h, then i could open the sweetgui2.h file generated by the first compile i did delete what qt4 put in there and put my custom slots in at that stage. did my head in for hours.... days.
so here is the simplest app on earth but its got me started so here are the files and code that worked for me and the documentation basically got me to click on to whats going on.
main.cpp
Strait from the documentation just changed the class name "SweetGuiForm".
#include <QApplication>
#include "sweetgui2.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
SweetGuiForm sweetgui;
sweetgui.show();
return app.exec();
}
next sweetgui2.cpp
My first attempt at c++.. ugly but works. But again i found everything about getting the text from the textEdit and type casting it to a int in the calculator example and searching for toPlainText() in the qt4 assistant. notice below im including the file that i will define the new slots that ill show further on in my explanation. hope im making sense.
#include <QtGui>
#include "sweetgui2.h"
SweetGuiForm::SweetGuiForm(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
}
void SweetGuiForm::on_buttonBox_accepted()
{
QString stringamount = ui.textEdit->toPlainText();
int digitamount = stringamount.toInt();
ui.textEdit->setText(QString::number(25 + digitamount));
}
next sweetgui2.h the one we included above My custom header file with my custom slot.... simple as i said from the calculator example and twisted a lil.. you will get it this is not what it looks like when you generate it from designer on the first compile this is after i have deleted nearly all what was there and opened the calculator example and followed in the tutorial wich shows you how to make your first custom slot .
#ifndef SWEETGUI2_H
#define SWEETGUI2_H
#include "ui_sweetgui2.h"
class SweetGuiForm : public QWidget
{
Q_OBJECT
public:
SweetGuiForm(QWidget *parent = 0);
private slots:
void on_buttonBox_accepted();
private:
Ui::SweetGuiForm ui;
};
#endif // SWEETGUI2_H
Again Straight from the documentation. I used the calculator example to get the basic flow.
next ui_sweetgui2.h
I include this file because i was trying to add my slots to the sweetgui2.h that was generated by qt4 desinger. doesnt work guys ..so i compiled first with sweetgui2.h file you generate with the designer, i go to forms menu then view code that is where u can save header files. then of course save the ui file.
and compile then you end up with the ui_sweetgui2.h file wich looked like the sweetgui2.h generated by the designer
#ifndef UI_SWEETGUI2_H
#define UI_SWEETGUI2_H
#include <QtCore/QVariant>
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QButtonGroup>
#include <QtGui/QDialogButtonBox>
#include <QtGui/QHeaderView>
#include <QtGui/QTextEdit>
#include <QtGui/QWidget>
QT_BEGIN_NAMESPACE
class Ui_SweetGuiForm
{
public:
QDialogButtonBox *buttonBox;
QTextEdit *textEdit;
void setupUi(QWidget *SweetGuiForm)
{
if (SweetGuiForm->objectName().isEmpty())
SweetGuiForm->setObjectName(QString::fromUtf8("SweetGuiForm"));
SweetGuiForm->resize(486, 238);
buttonBox = new QDialogButtonBox(SweetGuiForm);
buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
buttonBox->setGeometry(QRect(150, 200, 181, 26));
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
textEdit = new QTextEdit(SweetGuiForm);
textEdit->setObjectName(QString::fromUtf8("textEdit"));
textEdit->setGeometry(QRect(150, 50, 221, 91));
retranslateUi(SweetGuiForm);
QObject::connect(buttonBox, SIGNAL(rejected()), SweetGuiForm, SLOT(close()));
QMetaObject::connectSlotsByName(SweetGuiForm);
} // setupUi
void retranslateUi(QWidget *SweetGuiForm)
{
SweetGuiForm->setWindowTitle(QApplication::translate("SweetGuiForm", "SweetGuiBack", 0, QApplication::UnicodeUTF8));
} // retranslateUi
};
namespace Ui {
class SweetGuiForm: public Ui_SweetGuiForm {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_SWEETGUI2_H
Then i recompiled again with my custom slots and shazam! now i can begin to learn some c++.
thanks for all the hints guys, between you all and the documentation i got there.
hope this helps. The main thing to look at is the order things are included i mean my sweetgui2.cpp file
includes the sweetgui2.h file. wich grabs all my custom stuff.
My sweetgui2.h file
includes the ui_sweetgui2.h wich has all the stuff the designer made when i did the first compile. Main.cpp calls my SweetGuiForm class .
As you all can see my first couple days with c++ but this is a good starting point. it made me put the basic flow together in my mind. qt4 assistant look at it.. its well explained and the examples seem very good. ho ho ho merry xmas. hope my explanation helps.
I am fairly new to Qt. I have done some simple modifications to an existing Qt application, but I haven't created any from scratch yet.
I also don't have really much experience with certain aspects of C++ in general (class inheritance etc).
I have created a new Code::Blocks Qt4-based project and modified the template a bit. I have added two files.
Right now the project contains three files: main.cpp, app.h and app.cpp.
This is the content of main.cpp:
#include <QTimer>
#include "app.h"
int main(int argc, char* argv[]) {
TestApp app(argc, argv);
QTimer::singleShot(1000, &app, SLOT(timeout()));
return app.exec();
}
This is what app.h looks like:
#ifndef APP_H_INCLUDED
#define APP_H_INCLUDED
#include <QApplication>
class TestApp: public QApplication {
public:
TestApp(int &argc, char **argv);
public slots:
void timeout();
};
#endif
And this is app.cpp:
#include "app.h"
#include <QDebug>
TestApp::TestApp(int &argc, char **argv): QApplication(argc, argv) {
}
void TestApp::timeout() {
qDebug() << "timeout called";
}
I expected the program to print "timeout called" one second after startup. Unfortunately, this doesn't work. When QTimer::singleShot() is called, the console says:
Object::connect: No such slot QApplication::timeout() in [path to the main.cpp file]
Object::connect: (receiver name: 'QtTests')
I have no idea how to deal with this. Thank you in advance.
You're simply missing the Q_OBJECT macro in your TestApp class:
class TestApp: public QApplication {
Q_OBJECT
public:
...
This is necessary for the whole signal/slot infrastructure to work (and deriving from a class that has this macro is not sufficient).
(Make sure you do a full, clean build after that change - and if you don't use qmake or some other Qt-aware build system, you'll need to run moc yourself.)
For reference, see the QObject docs:
Notice that the Q_OBJECT macro is mandatory for any object that implements signals, slots or properties. You also need to run the Meta Object Compiler on the source file. We strongly recommend the use of this macro in all subclasses of QObject regardless of whether or not they actually use signals, slots and properties, since failure to do so may lead certain functions to exhibit strange behavior.
You need to create a moc file, which is created with qmake if you put Q_OBJECT macro in your class.
So, to fix your example, you need your class changed to this :
class TestApp: public QApplication {
Q_OBJECT
public:
TestApp(int &argc, char **argv);
public slots:
void timeout();
};
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