I am a bit new to Qt C++ and I think there is a small thing I'm missing but can't figure what is it.
I am trying to make a simple Qt C++ application just to get familiar with it, but I face some problem, First, I have the votor class, which is the main application class, and another class which is called recorder, which will be used inside the main votor class. For simplicity, I omitted un-related parts,
Here are the files:
votor.h
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_votor.h"
#ifndef TSTRECORD_H
#define TSTRECORD_H
#endif
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QWidget>
#include "recorder.h"
class VOTor : public QMainWindow
{
Q_OBJECT
public:
VOTor(QWidget *parent = Q_NULLPTR);
recorder xs;
private:
Ui::VOTorClass ui;
};
votor.cpp
#include "votor.h"
VOTor::VOTor(QWidget *parent) : QMainWindow(parent), xs(parent)
{
ui.setupUi(this);
//xs = recorder();
}
recorder.h
#pragma once
#include <QDebug>
#include <QObject>
#include <QtCore/qbuffer.h>
#include <QtCore/qiodevice.h>
#include<QtMultimedia/qaudioformat.h>
#include<QtMultimedia/qaudiodeviceinfo.h>
#include<QtMultimedia/qaudioinput.h>
class recorder : public QObject
{
Q_OBJECT
public:
// recorder();
recorder(QObject *parent);
~recorder();
//Some functions related to recording omitted for more focus
private:
//omitted members for simplicity, just some integers, chars and qt objects not related to problem
recorder.cpp
#include "recorder.h"
recorder::recorder(QObject *parent) : QObject(parent)
{
//just initializing the omitted members normally
}
//recorder::recorder() {}
recorder::~recorder()
{
}
as you see, recorder object is a member inside votor class. Now, I need to call recorder constructor to initialize its parent. Now, I knew I can't just make (inside votor.h)
recorder xs(parent);
so,
1- is there a way I can call recorder constructor other than initialization list ?
I want another way as I was more convenient to use recorder xs(..) than initialization list, I feel (just feel) that using initialization list is heavy (not performance-wise but on readability ). I know also I can use dynamic allocation, but I don't want to use it without a good reason for.
2- I have decided to use initialization list to call recorder constructor and pass (Qobject* parent) to recorder. The code is built successfully, but when running, it gives access violation error which I can't figure why...
it gives:
"Access violation reading location 0x7C32F08D."
I think I am missing small thing.. I hope to know what is wrong.
Edit:
as #p-a-o-l-o suggested, the access violation was from the omitted code, So, I am posting it here as I don't know what is the problem in my code:
full version of recoder.h
class recorder : public QObject
{
Q_OBJECT
public:
recorder();
//recorder(QObject *parent);
~recorder();
void record();
void stop();
public slots:
void stateChanged(QAudio::State);
private:
unsigned char state;
QBuffer* voiceBuffer;
QAudioFormat* format;
QAudioDeviceInfo* info;
QAudioInput* audioIn;
unsigned char writeWav(QByteArray*);
};
and the part causes access violation according to debug mode, which is constructor of recorder class
recorder::recorder() : QObject(Q_NULLPTR)
{
state = 0;
voiceBuffer->open(QIODevice::WriteOnly);
format->setSampleRate(8000);
format->setChannelCount(1);
format->setSampleRate(16);
format->setByteOrder(QAudioFormat::LittleEndian);
format->setCodec("audio/pcm");
format->setSampleType(QAudioFormat::SignedInt);
*info = QAudioDeviceInfo::defaultInputDevice();
audioIn = &QAudioInput(*info, *format);
//audioIn->stateChanged.connect(stateChanged);
//connect(audioIn, &QAudioInput::stateChanged, stateChanged);
connect(audioIn, SIGNAL(stateChanged (QAudio::State) ), this, SLOT(stateChanged(QAudio::State)));
}
About question #1: as rightfully suggested in comments, a proper constructor for a QObject-derived class would have a nullptr default argument:
recorder(QObject *parent = Q_NULLPTR);
If you really need that parent object to initialize the other members in construction, you have no alternatives and must call that constructor, somehow.
Otherwise, have a no-arguments constructor and initialize the other members there:
recorder::recorder() : QObject(Q_NULLPTR)
{
//just initializing the omitted members normally
}
Such a constructor will be invoked automatically, no need of initialization list here.
If you still need a parent for the recorder object, give it one in the VOTor constructor:
VOTor::VOTor(QWidget *parent) : QMainWindow(parent)
{
ui.setupUi(this);
xs.setParent(parent);
}
About question #2: as far as I can see from the code you posted, the access violation have nothing to do with Qt parenting and must be related to the (omitted) code inside the recorder constructor.
Just to clear it out: parenting a member object is always safe, since it will go out of scope before ~QObject() gets called, so it will be removed from the children list before ~QObject() could possibly call delete on it.
Taking the OP code as an example, the sequence of destructors is the following:
~VOTor()
~recorder() ---> xs is removed from the children list
.
.
.
~QObject() ---> will call delete on all children, but ws is not in the list anymore
Qt documentation is quite clear about the order of construction/destruction of parents and children: in short, if the child is created on the stack everything will be fine until the child is created after the parent.
But, again, if the child happens to be a member of the parent class, it will be fine as well for the reasons mentioned above (even if its construction actually happens before its parent's).
Related
I need to access the mainWindow object in a different class. The problem is, that I can not give mainWindow to this class (I don't want to do this, it would make everything much more complicated). Question: Is there any way in C++ or Qt to put an object in something like a local "database" or sth where every other class in the project can look into and communicate with the objects.
What I want to have in the end is something like this:
// A.h
#ifndef A_H
#define A_H
class A{
public:
A() { /*here comes the ting*/ myMainWindow->sayHi(); }
};
#endif // A_H
// MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "a.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
A *a = new A;
}
MainWindow::~MainWindow(){delete ui;}
MainWindow::sayHi(){
// just do anything
}
I don't think, this is possible, but I give it a try...
Thanks for answers!
I need to access the mainWindow object in a different class. The
problem is, that I can not give mainWindow to this class (I don't want
to do this, it would make everything much more complicated).
That is doable. The author don't want to expose the "main window" variable holding the reference or pointer to the object. And, obviously the author wants that UI object to be callable from other objects. In Qt that implies either both objects on UI thread or communication is via queued signal-slot connection only. But the direct call wanted, hence on the same thread.
Is there any way in C++ or Qt to put an object in something like a
local "database" or sth where every other class in the project can
look into and communicate with the objects.
Local thread storage is a known pattern to implement things like that. Qt has own implementation of it called QThreadStorage. You can attempt something like that:
// myLts.h
void ltsRegisterObject(const QString &key, QObject *object);
void ltsRemoveObject(const QString &key);
QObject* ltsGetObject(const QString &key);
template <typename T> T* ltsGet(const QString &key) {
return qobject_cast<T*>(ltsGetObject(key));
}
// myLts.cpp
static QThreadStorage<QMap<QString, QObject*> > s_qtObjects;
void ltsRegisterObject(const QString &key, QObject *object)
{
s_qtObjects.localData().insert(key, object);
}
void ltsRemoveObject(const QString &key)
{
if (!s_qtObjects.hasLocalData())
return;
s_qtObjects.localData().remove(key);
}
QObject* ltsGetObject(const QString &key)
{
QObject *object;
auto it = s_qtObjects.localData().find(key);
return it != s_qtObjects.localData().end() ? it.value() : nullptr;
}
Register main window object in LTS:
#include "myLts.h"
// ...
// register main window in LTS
ltsRegisterObject("mainWindow", &mainWindow);
Find and use the object:
#include "myLts.h"
// ...
// use main window from LTS
auto* pMainWnd = ltsGet<QMainWindow>("mainWindow");
if (pMainWnd)
pMainWnd->show();
P.S. I did not compile this. But it is not hard to fix if so.
To be specific to my problem I've been trying to connect a a slot from one class to a regular function of another class. I'm trying to do this so I can close the ui of one the main window from a dialog window. I tried everything I could possibly think of but it seems like every time I think of something to try the Qt compiler shuts me down by throwing some error or not working for no apparent reason such as when I did this
function(Ui::MainWindow *ui)
{
copy = ui; // copy is a pointer declared as Ui::MainWindow *copy in the dialog class
}
I tried this so I could close the main window from the copy pointer in the dialog class and although it compiled for that, it wouldn't compile when I tried to use it in another function of that class even though it was a class variable. After that I then realized that I should be able to connect the two functions from the main function. I wrote this
QObject::connect(&i, SIGNAL(on_passInput_returnPressed()), &w, SLOT(initalizer()));
The above compiles but when debugging I discovered it never gets executed and not sure why despite the fact that the SIGNAL on_passInput_returnPressed() is actually declared as slot in the class header. If that is the reason why it doesn't get executed how do I connect the slot from the dialog class to a regular function from the MainWindow class? There must be a way because I spent a day practically thinking about this non-stop and can't think of any other way given the nature of object scopes (in OOP) on top of the fact that the pointer thing I thought of didn't work.
Also I just now recreated the error from the pointer thing described above here it is.
error: invalid use of incomplete type 'class Ui::MainWindow'
copy->close();
^
Again this error is referring to trying to access the close function via the copy pointer from another function in the same class as the pointer assignment that worked in the other function. The copy pointer is declared as follows in the class header. Also in case you're wondering, yes, the pointer assignment written above does get executed. I checked via debugging.
private:
Ui::InitalPrompt *ui;
Ui::MainWindow *copy;
If you need more info just let me know what you need and I'll edit this post with it. I've obsessively tried so much I can think so much so that I've given up without further help due to how unforgiving Qt has been with me.
Also if there's a way of doing what I'm trying to do with the new Qt5 syntax of the connect function can you please give me the exact thing to type because despite looking into that new syntax myself I can't get it to compile for the life of me. (and yes I'm using Qt5) That's why the code above is written as old-fashion syntax.
Added the following as suggested to do so in a reply which is the class of the copy pointer.
#include "initalprompt.h"
#include "ui_initalprompt.h"
#include "mainwindow.h"
#include <QLineEdit>
#include <QCloseEvent>
#include <QMessageBox>
InitalPrompt::InitalPrompt(QWidget *parent) :
QDialog(parent),
ui(new Ui::InitalPrompt)
{
ui->setupUi(this);
}
InitalPrompt::~InitalPrompt()
{
delete ui;
}
void InitalPrompt::on_passInput_returnPressed()
{
pass = ui->passInput->text();
}
void InitalPrompt::reject()
{
QMessageBox::StandardButton resBtn = QMessageBox::Yes;
bool changes = false;
if (changes) {
resBtn = QMessageBox::question( this, "APP_NAME",
tr("Are you sure?\n"),
QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes,
QMessageBox::Yes);
}
if (resBtn == QMessageBox::Yes) {
QDialog::reject();
}
// the only issue is with the statment below don't know why
copy->close();
}
void InitalPrompt::catcher(Ui::MainWindow *ui)
{
copy = ui;
}
"Invalid use of incomplete type" means the compiler does not (yet) have a definition for the class when it reaches that line. In the header file that contains your copy pointer, include a forward declaration above the class declaration (the file would look something liek this):
#ifndef BLAH_BLAH_H
#define BLAH_BLAH_H
/*All of your normal #includes here*/
namespace Ui {
class InitialPrompt;
class MainWindow; //Note the forward declaration here
}
#include "mainwindow.h"
class BlahBlah
{
/*Class stuff here*/
private:
Ui::InitalPrompt *ui;
Ui::MainWindow *copy;
};
#endif //BLAH_BLAH_H
If you post the entirety of your class file that has the *copy, we can look further into it.
No, it is not possible. You can either:
call w->initalizer() directly from on_passInput_returnPressed() or
define a signal signal_on_passInput_returnPressed() in your ui class and emit that signal from on_passInput_returnPressed(). Then
QObject::connect(&i, SIGNAL(signal_on_passInput_returnPressed()), &w, SLOT(initalizer()));
should work.
For example:
class Whatever :
public QObject {
Q_OBJECT
void on_passInput_returnPressed();
signals:
void signal_on_passInput_returnPressed();
};
void Whatever::on_passInput_returnPressed() {
emit signal_on_passInput_returnPressed();
}
I am trying to write an application where I would have a generic dialog window and specific dialog windows that would inherit some basic functionalities from the generic one. I am not sure this is the best approach for this, but this is how I did it (The CGenericProject class was created from Dialog template in Qt Creator):
CGenericProject.h:
#include <QDialog>
namespace Ui {
class CGenericProject;
}
class CGenericProject : public QDialog
{
Q_OBJECT
public:
explicit CGenericProject(QWidget *parent = 0);
~CGenericProject();
protected:
Ui::CGenericProject *ui;
};
CGenericProject.cpp:
#include "cgenericproject.h"
#include "ui_cgenericproject.h"
CGenericProject::CGenericProject(QWidget *parent) :
QDialog(parent),
ui(new Ui::CGenericProject)
{
ui->setupUi(this);
}
CGenericProject::~CGenericProject()
{
delete ui;
}
CEisProject.h:
#include "cgenericproject.h"
class CEisProject : public CGenericProject
{
public:
CEisProject();
~CEisProject();
};
CEisProject.cpp:
#include "ceisproject.h"
CEisProject::CEisProject()
{
ui-> NO ACCESS
}
CEisProject::~CEisProject()
{
}
As you see in the CEisProject.cpp file, I have no access to the ui field inherited from CGenericProject, even though it is protected. I mean, I see ui itself, but I dont see its methods and members. Any other variable that I would define there, would be accessible. What's wrong? I would appreciate all help in this manner.
You have to add the line
#include "ui_cgenericproject.h"
to the CEisProject.cpp file.
The CGenericProject.h file is included in CEisProject.h, but CEisProject.h does not have access to CGenericProject.cpp. In the header of your base class you have a only forward declaration of Ui::CGenericProject, and you include its file in the .cpp. So CGenericProject.cpp knows the implementation of this class.
But CEisProject.cpp doesn't have access to that, so you have to include the file again in here.
NOTE
Your forward declaration is confusing, you should indent it properly. Also, add some comments to your code to add some clarity for who is reading it, you're using two different classes with the same name.
I am trying to run QTimer and have it warn me when timeouting. To do so, I use slot and signal to link the two.
The guy.h:
#ifndef GUY_H
#define GUY_H
#include <QGraphicsItem>
#include <QTimer>
#include <QObject>
class Guy : public QGraphicsItem
{
public:
Guy(int x, int y);
void timerStart();
public slots:
void onTimeOutTimer();
[...]
QTimer *timer;
}
#endif // GUY_H
The guy.cpp:
#include "guy.h"
#include <QTimer>
#include <QObject>
#include <stdio.h>
#include <iostream>
Guy::Guy(int x, int y)
{
timer = new QTimer();
}
void Guy::timerStart()
{
QObject::connect(timer, SIGNAL(timeout()), this, SLOT(onTimeOutTimer()));
this->timer->setInterval(1000);
this->timer->start();
std::cout << "starting timer" << std::endl;
}
void Guy::onTimeOutTimer()
{
std::cout << "check" << std::endl;
}
But as an ouput, I get this error:
No matching function for call to 'QObject::connect(QTimer*&, const char*, Guy* const, const char*)'
As I undertsand it is that QTimer is no QObject required as first input of the function connect(), but the documentation specifies QTimer inherits from QObject.
I have no clue here.
You will need to inherit from QObject, too, to get this working as signals and slots are availabl for QObjects. QGraphicsItem does not inherit QObject, not even indirectly.
Not only that, you will also need to add the Q_OBJECT macro as follows:
class Guy : public QObject, public QGraphicsItem
{
Q_OBJECT
...
}
or even better because QGraphicsObject inherits QObject and QGraphicsItem.
...
#include <QGraphicsObject>
...
class Guy : public QGraphicsQObject
{
Q_OBJECT
...
}
Also, if you make this change, I suggest to change the QObject::connect to connect as you do not need to indicate the QObject:: scope then.
On a side note, including stdio.h does not seem to make sense here.
Furthermore, allocating the QTimer instance on the heap looks like wasteful to me. It is not only leaking the memory, but also adds additional complexity. Even if you allocate it on the heap, you should pass this as the parent and use the initializer list or C++11 style initialization. Also, if you allocate it on the heap, you could use forward declaration in the header.
If the slot is not used outside the class, you should also make that private.
It is probably also a bad idea to make the timer member public. Hopefully you do not do that.
You can inherit from QGraphicsObject which provides a base class for all graphics items that require signals, slots and inherits QGraphicsItem and QObject. Also add Q_OBJECT macro in your class declaration.
If you use new Qt5 style connects as in
QObject::connect(timer, &QTimer::timeout, this, &Guy::onTimeOutTimer)
The onTimeOutTimer function does not need to be marked as slot, and Guy could stay a non-QObject. Much slimmer and less macros involved.
I'm trying to call my function as a function slot in Qt,But i don't know how to go about it.
it seems the following approach is wrong :
Update:
According to an answer i updated my source code,but still something is apparently wrong with it.Trying to compile this snippet of code causes these errors:
C2515:' no appropriate default constructor is available.'
And
C2665: QObject::connect':none of the 3 overloads could convert all the
arguments.'
respectively in Visual studio 2010.
#include <QtGui/QApplication>
#include <QPushButton>
#include <QObject>
#include <QMessageBox>
class myclass;
int main(int argc,char *argv[])
{
QApplication a(argc,argv);
QPushButton btnshowmessage("show");
myclass *my=new myclass();
QObject::connect(&btnshowmessage,SIGNAL(clicked()),my,SLOT(warningmessage()));
btnshowmessage.show();
return a.exec();
}
//////////////////////////////////////////////////////////////////////////
class myclass: public QObject
{
Q_OBJECT
public:myclass(){}
public slots:
void warningmessage()
{
QMessageBox::warning(0,"Warning","Test Message!",QMessageBox::Ok);
}
};
You use signals and slots to connect one Object's signal to another Object's slot. Every signal or slot should be inside a class which must be also derived from QObject class and contain the Q_OBJECT macro.
So to make your code work, put the slot into some class of yours:
class MySlotClass:public QObject
{
Q_OBJECT
public slots:
void MyFunction()
{
QMessageBox::warning(0,"WarningTest","This is a waring text message",QMessageBox::Ok);
}
}
and connect like this:
MySlotClass m = new MySlotClass();
Qobject::connect(&btnShowaMessageBox,SIGNAL(clicked()), &m ,SLOT(MyFunction()));
Currently Qt does not allow connection of signals to functions that are not declared as slots on some QObject derivative. I believe Qt5 may offer this possibility, but connect will have different syntax to allow this.
basically your slot function must be in a QObject derived class and declared in a
public slots:
section.
Read the documentation on signals/slots.