consider this scenario:
I need to create a ui for some settings. As, data and ui should be separated in theory, I defined a separate class which takes care of the configuration data. The question I have is how to instantiate the data class inside the settings class.
One way is to create the data class in the caller object, I mean the object that calls the settings menu class.
The other way, which my question is involved with, is to create a DATA class variable inside the settings class. I doubt what happens when the settings class gets destroyed then! would the data class object inside settings class also get destroyed? what about if it is defined as a static member of settings class?
#ifndef SETTINGSWINDOW_H
#define SETTINGSWINDOW_H
#include <QMainWindow>
#include <QModelIndex>
#include <QSignalMapper>
#include <QRadioButton>
#include <QSpinBox>
#include <QTimer>
#include "cameracommands.h"
struct Config
{
/* General Options */
QString general_key_lock;
QString general_back_light;
};
//class IConfigSource
//{
//public:
// virtual Config config() const;
// virtual void setConfig(const Config& cfg);
//};
class ConfigSource /* : public IConfigSource*/
{
public:
ConfigSource() {
config_.general_back_light = "OFF";
config_.general_key_lock = "OFF";
}
Config config() const {return config_;}
void setConfig(const Config& cfg) {config_ = cfg;}
private:
Config config_;
};
class ConfigUpdater : public QObject
{
Q_OBJECT
public:
ConfigUpdater(ConfigSource& src) : src_(src) {}
public slots:
void apply () {src_.setConfig(tempConfig_);}
void cancel() {tempConfig_ = src_.config();}
public:
void updateGeneralBackLight(QString s) {tempConfig_.general_back_light = s; qDebug() << "BackLight updated :)";}
void updateGeneralKeyLock(QString s) {tempConfig_.general_key_lock = s; qDebug() << "KeyLock updated :)";}
Config tempConfig_;
ConfigSource& src_;
};
//----------------------------
namespace Ui {
class SettingsWindow;
}
class SettingsWindow : public QMainWindow
{
Q_OBJECT
public:
explicit SettingsWindow(QWidget *parent = 0);
~SettingsWindow();
signals:
void clicked(const QString &text);
void sendToPLC(QByteArray );
public slots:
void updateGeneralBackLight();
void updateGeneralKeyLock();
void getRow(QModelIndex);
void MySlot(QString);
private slots:
void on_pushButton_5_clicked();
void on_pushButton_3_clicked();
private:
void set_mappings();
Ui::SettingsWindow *ui;
ConfigUpdater *config_updater;
QSignalMapper *mapper;
};
#endif // SETTINGSWINDOW_H
and this is the source:
QMainWindow(parent),
ui(new Ui::SettingsWindow)
{
/* initializations */
ui->setupUi(this);
ConfigSource src;
config_updater = new ConfigUpdater(src);
That depends on how do you need to use it.
Scenario 1. The settings need to be held in memory when the program is working.
Scenario 2. The settings need to be saved to the disc immediately, and then will be read on-demand.
In scenario 1, you need to be able to always access the data in the memory. So it is better to separate the settingsUI class and the settingsData class, so you will have access to the latter.
class settingsUI
{
<...>
private:
settingsData * data;//pointer to the data object
}
class settingsData
{
}
In scenario 2, you can aggregate settingsData into the settingsUI, and save the data to a file when the UI is destroyed.
class settingsUI
{
public:
<...>
~settingsUI();
private:
class settingsData
{
<..>
}data;
<...>
}
class settingsUI::~settingsUI()
{
data.saveToFile();
}
And yes, uf you aggregate settings into the UI, it will be destroyed when the UI is destroyed. Holding data as a static member is not the best idea, it is better to separate the data from the visual representation (which is in your case the UI class).
UPD:
If you want to hold it just until the program exits, I would suggest you to hold a static pointer to the data in the UI class. Here is an example with raw pointers, but you can use smart pointers too, ofc.
class data
{
}
class UI
{
private:
static data * data_;
}
data* UI::data_;
When your program starts, allocate memory for the data: UI::data_ = new data(), and when your program ends (or if you don't need the data anymore), free the memory: delete UI::data_. Once again, it is better to use smart pointers, so this is just an example.
If the settings class is used by only the UI then it makes sense to keep it within the UI class:
class Settings {
int a;
int b;
};
class Ui {
private:
Settings settings;
};
settings will be destroyed during the destruction of Ui.
If you are using the Settings object in many places, it makes more sense to keep a shared pointer to it:
class Ui {
public:
Ui(std::shared_ptr<Settings> someSettings)
: settings(someSettings)
{}
private:
std::shared_ptr<Settings> settings;
};
This Settings object will be destroyed when the last owner of the shared_ptr is destroyed.
Yes the data object would get destroyed when the settings object is destroyed. If you make it a static member then it would not. But that's probably not such a good idea. A better way would be to persist the data object to a file (say). You can read the file in the settings object constructor and write the file in the settings object destructor.
EDIT
class SettingsWindow : public QMainWindow
{
Q_OBJECT
public:
explicit SettingsWindow(ConfigSource& src , QWidget *parent = 0);
...
}
SettingsWindow::SettingsWindow(ConfigSource& src , QWidget *parent)
QMainWindow(parent),
ui(new Ui::SettingsWindow)
{
ui->setupUi(this);
config_updater = new ConfigUpdater(src);
...
}
thanks, it is correct. The program terminates when I attempt to pass the below variable to the user-defined function:
(inside header)
void print_config(Config cfg);
Ui::SettingsWindow *ui;
ConfigUpdater *config_updater;
inside cpp:
void SettingsWindow::print_config(Config config)
{
qDebug() << config.general_back_light;
qDebug() << config.general_key_lock;
}
void SettingsWindow::on_sendToMainButton_clicked() /* cancel */
{
print_config(config_updater->tempConfig_);
print_config(config_updater->src_.config());
}
the first, print_config instruciton works fine, as for tempConfig_, but the when I pass src_ in the second statement, it jump outta program.
//------------
I know where the problem comes from, but I cannot solve it, i hope this can help:
class ConfigUpdater : public QObject
{
Q_OBJECT
public:
ConfigUpdater(ConfigSource& src) : src_(src) {}
public slots:
void apply () {src_.setConfig(tempConfig_);}
void cancel() {tempConfig_ = src_.config();}
public:
Config tempConfig_;
ConfigSource& src_;
};
Here, src_ is passed by reference, everywhere, even in the constructor of Settings window.
the program fails when I attemp to access it inside memory, for example:
config_updater->cancel();
which does:
void cancel() {tempConfig_ = src_.config();}
Related
i am new in juce and getting an error like "allocating an object of abstract class type 'CreateAccount'" i am beginner in c++. i am try to call the class from the main window of juce and getting error like this.Below is my code
Main.cpp file
#include <JuceHeader.h>
#include "CreateAccount.h"
//==============================================================================
class TestApplication : public JUCEApplication
{
public:
//==============================================================================
TestApplication() {
}
const String getApplicationName() override {
return "Test";
}
const String getApplicationVersion() override { return "1.0.0"; }
void initialise (const String&) override {
mainWindow.reset (new MainWindow ("Test", new CreateAccount(), *this));
// splash = new SplashScreen("Welcome to Screen!",ImageFileFormat::loadFrom(File("/Resources/bell.png")),
// true);
// splash->deleteAfterDelay(RelativeTime::seconds(5), false);
}
void shutdown() override {
mainWindow = nullptr;
}
private:
class MainWindow : public DocumentWindow
{
public:
MainWindow (const String& name, Component* c, JUCEApplication& a)
: DocumentWindow (name, Desktop::getInstance().getDefaultLookAndFeel()
.findColour (ResizableWindow::backgroundColourId),
DocumentWindow::allButtons),
app (a)
{
setUsingNativeTitleBar (true);
setContentOwned (c, true);
#if JUCE_ANDROID || JUCE_IOS
setFullScreen (true);
#else
setResizable (true, false);
setResizeLimits (300, 250, 10000, 10000);
centreWithSize (getWidth(), getHeight());
#endif
setVisible (true);
}
void closeButtonPressed() override
{
app.systemRequestedQuit();
}
private:
JUCEApplication& app;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE (MainWindow)
};
std::unique_ptr<MainWindow> mainWindow;
};
//==============================================================================
START_JUCE_APPLICATION (TestApplication)
CreateAccount.h file
#include <JuceHeader.h>
//new page for create profile
class CreateAccount : public Component,
public Button::Listener
{
public:
CreateAccount() {
addAndMakeVisible(lblloginwithfb);
lblloginwithfb.setFont(Font(18.0f));
lblloginwithfb.setText("Login with Facebook", dontSendNotification);
lblloginwithfb.setColour(Label::textColourId, Colours::white);
//ready to play button
btncreteprofile.setButtonText("Create your profile");
btncreteprofile.setColour(TextButton::textColourOffId, Colours::white);
btncreteprofile.setColour(TextButton::textColourOnId, Colours::white);
btncreteprofile.setColour(TextButton::buttonColourId, Colour::fromRGB(235,135,15));
btncreteprofile.setColour(TextButton::buttonOnColourId, Colour::fromRGB(235,135,15));
addAndMakeVisible(btncreteprofile);
}
//==============================================================================
void paint(Graphics &g) override {
g.fillAll(Colours::black);
}
void resized() override {
/*Rectangle<int> bounds = getLocalBounds();
FlexBox flexBox;
flexBox.flexDirection = FlexBox::Direction::column;
flexBox.flexWrap = FlexBox::Wrap ::noWrap;
flexBox.alignContent = FlexBox::AlignContent::center;
Array<FlexItem> itemarray;
itemarray.add(FlexItem(80,50,btncreteprofile));
itemarray.add(FlexItem(getWidth(),50,lblloginwithfb));
flexBox.items = itemarray;
flexBox.performLayout(bounds.removeFromBottom(200));*/
Rectangle<int> bounds = getLocalBounds();
const int insetX = getWidth() / 4;
const int insetY = getHeight() / 4;
btncreteprofile.setBounds(insetX,bounds.getCentre().y+80,getWidth() - insetX * 2, 50);
lblloginwithfb.setBounds(insetX,insetY,getWidth() - insetX * 2,getHeight());
}
private:
//==============================================================================
// Your private member variables go here...
Label lblloginwithfb;
TextButton btncreteprofile;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CreateAccount)
};
Please help me when i try to add the listner in createaccount file then will get an error.
You need to implement all pure virtual methods in Button::Listener to make your class not abstract. Button::Listener contains these two methods
virtual void buttonClicked (Button *)=0
virtual void buttonStateChanged (Button *)
The second method is not pure virtual, so you don't have to implement it.
So add a method to your class
virtual void buttonClicked (Button *) override {}
and add the code needed when the button is clicked.
Depending on the C++ version you're using, the override might not get accepted by the compiler. In that case, just omit it. It is used to allow
the compiler to emit warnings/errors with respect to overridden methods.
In JUCE, most UI related things are implemented as a subclass of Component.
Some of the subclasses have the concept of a content component: all subclasses
of ResizableWindow, which adds the setContentOwned() and setContentNonOwned()
methods, inherited by all subclasses of ResizableWindow (especially
DocumentWindow).
To define the content of the DocumentWindow, the example code in your question
uses setContentOwned() in the MainWindow constructor which it gets from the
initialise() in TestApplication.
To implement the navigation you could do the following (there are other ways to do it):
1. Implement the navigation in the MainWindow, so you have the navigation logic in one central place
2. Give each of your "Pages" (let's call one step a Page.) a pointer to the MainWindow. For CreateAccount that would be
CreateAccount.h file:
void setMainWindow(MainWindow * _mainWindow)
{
mainWindow = _mainWindow;
}
3. Add an instance variable, too, somewhere in the CreateAccount class:
MainWindow * mainWindow;
and update the constructor:
CreateAccount()
: mainWindow(nullptr)
{
// ...
}
4. Change the creation code
replace
mainWindow.reset (new MainWindow ("Test", new CreateAccount(), *this));
by:
CreateAccount * ca = new CreateAccount();
MainWindow * mw = new MainWindow ("Test", ca, *this)
ca->setMainWindow(mw);
mainWindow.reset(mw);
5. Implement your navigation in a set of custom methods (need to be public) in MainWindow, e.g.
public:
void createProfileClicked()
{
// ...get entered data...
// ...process entered data...
// ...implement next navigation step...
// (you would create or recycle the "Page" for the next step here)
setContentOwned(
// ...
);
}
6. Call that method from the buttonClicked() event handler in CreateAccount:
mainWindow->createProfileClicked();
This is just one way to implement what you want. It all depends on the complexity of your project.
Edit:
My suggestion given in this answer introduces cyclic dependencies of classes.
Let me first restate what I think you currently have, deduced from your comments:
mainwindow.h
#include "splashpage.h"
class MainWindow : public DocumentWindow
{
public:
// ...
void alaramReadyBtnClicked()
{
setContentOwned(new SplashPage(), false);
}
// ...
};
splashpage.h:
#include "mainwindow.h"
class SplashPage : public Component, public Button::Listener
{
public:
SplashPage()
: mainWindow(nullptr)
{}
void setMainWindow(MainWindow * _mainWindow)
{ mainWindow = _mainWindow; }
void buttonClicked (Button *) override
{
if (button == &tvbtnSomething) {
mainWindow->splashSomethingClicked();
}
}
private:
MainWindow * mainWindow;
};
There's a cyclic dependency of declarations in there between MainWindow and the Page classes. When the compiler sees
mainWindow->splashSomethingClicked();
it needs to have seen the declaration of MainWindow but in mainwindow.h
it needs the declaration of SplashPage for this:
void alaramReadyBtnClicked()
{
setContentOwned(new SplashPage(), false);
}
First, it is good practice to use include guards in your header files
e.g. mainwindow.h
// you need these include guards to prevent that the compiler
// sees this file a second time; it's a very good rule of thumb to always do this
// in you header files
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "splashpage.h"
class MainWindow : public DocumentWindow
{
// ...
};
#endif // !defined(MAINWINDOW_H)
There are specific situations in C++ for which the compiler doesn't need to know the complete declaration: if you only use pointers or references and don't reference them. You can then use a forward declaration of class names.
But you can't do this with everything in header files only. Because there's a concept of a "compilation unit".
To break the cyclic dependency, change your ...Page.h:
#ifndef SPLASHPAGE_H
#define SPLASHPAGE_H
// don't include mainwindow.h here
class MainWindow; // forward declaration
class SplashPage : public Component, public Button::Listener
{
public:
SplashPage()
: mainWindow(nullptr)
{}
void setMainWindow(MainWindow * _mainWindow)
{
mainWindow = _mainWindow; // pointer copy does not need full declaration
}
// note: implementation not in this header file anymore
void buttonClicked (Button *) override;
private:
// ...
MainWindow * mainWindow; // does not need full declaration
};
#endif // !defined(SPLASHPAGE_H)
splashpage.cpp:
#include "mainwindow.h"
#include "splashpage.h"
void SplashPage::buttonClicked (Button *) override
{
if (button == &tvbtnSomething) {
mainWindow->splashSomethingClicked();
}
}
If you use the JUCE Projucer tool, you can add pairs of these files easily which should already contain the include guards stuff.
We are developing in C++ (and Qt) using Visual Studio 2015 and the Qt Designer for our UI (via Form / .ui files).
We now need to share some Common Data between our UI elements (e.g. most recently used paths etc.) and I would like to do this via dependency injection (i.e. providing the UI with a Reference to the common object during construction) instead of e.g. (ab)using the singleton pattern.
Has someone faced (and solved) similar problems, and is there a sensible way to do this?
Update (to elaborate):
For example I have a custom FooWidget which I wish to use in my FooDialog.ui form file.
// My custom Widget
class FooWidget : public QWidget {
Q_OBJECT
public:
FooWidget(QWidget* parent);
//...
}
// The FooDialog class (scaffolding created by Qt Designer)
class FooDialog : public QDialog {
Q_OBJECT
public:
FooDialog(QWidget* parent) : QDialog(parent), ui (new Ui::FooDialog()) {
ui->setupUp(this);
//...
}
private:
Ui::FooDialog* ui;
}
// The Ui::FooDialog class autogenerated(!) by Qt Designer
// I cannot (must not) change this code, as it will be regenerated every time
class Ui_FooDialog {
public:
FooWidget* widget;
void setupUi(QWidget *fooDialog) {
//...
widget = new FooWidget(fooDialog);
//...
}
}
namespace Ui { class ScannerStatus: public Ui_ScannerStatus {}; }
I would like to provide the FooWidget with a common data object (e.g. text size and colours shared across all my Ui classes), but I can't do so in the constructor (since the autogenerated Ui_FooDialog treats FooWidget as a generic QWidget, which only needs/takes a QWidget* parent in the constructor - I cannot provide a pointer or reference to my shared TextColourAndSize object.
I am aware I could create a second ui->widget->setupTextColourAndSize(...) step in FooDialog (after the initial ui->setupUi(this)) which provides that common data object, but having two init() type functions seems like a rather bad code smell (one is bad enough).
FooWidget needs two constructors, and a setter for the dependency:
explicit FooWidget(QObject *parent = nullptr) : QWidget(parent), … {
…
}
FooWidget(Dependency *dep, QObject *parent = nullptr) : FooWidget(parent) {
setDependency(dep);
}
void setDependency(Dependency *dep) {
…
}
Then you’d set the dependency after the widget is constructed:
FooDialog(Dependency *dep, …) … {
setupUi(this);
ui->fooWidget->setDependency(dep);
}
This could be automated: the parent widget can have a property that holds the pointer to the dependency, and the child widgets can find it automatically:
FooDialog(Dependency *, …) : … {
setProperty("dependency", dep);
setupUi(this);
}
FooWidget(QWidget *parent) : … {
auto *dep = parent() ? parent()->property("dependency").value<Dependency*>() : nullptr;
if (dep) setDependency(dep);
}
This will work with no extra effort if Dependency derives from QObject. Otherwise, you’ll need to have the following in a suitable header file:
class Dependency { … };
Q_DECLARE_METATYPE(Dependency*)
In all circumstances, you do need to promote the fooWidget object to FooWidget class within Qt Designer.
Ok from what I'm seeing you do not need "dependency injection". Question was incorrectly stated.
You can use this custom widget directly from Qt designer.
When you create your FooDialog place regular widget QWidget in place where you need to have a FooWidget.
Then "promote" regular this widget to FooWidget (possibly you have to add some simple information about that type) - (I did that long time ago and do not remember all details).
For detailed instruction just google: qt promote widget qt designer, you will find lots of examples how to do it.
These were good solutions, but talking about dependency injections, there is also an option to have some fun with C++. It's not a wise solution at all, of course I know it, but nevertheless...
foowidget.h
#ifndef FOOWIDGET_H
#define FOOWIDGET_H
#include <QWidget>
class Something
{
public:
QString getHello() const
{ return "Hello world!"; }
};
/***************************************************/
template<typename T>
class Injector
{
public:
QString getHello() const
{ return m_dataContainer.getHello(); }
private:
T m_dataContainer;
};
/***************************************************/
class FooWidget : public QWidget, public Injector<Something>
{
Q_OBJECT
public:
explicit FooWidget(QWidget* parent = nullptr);
protected:
virtual void mousePressEvent(QMouseEvent*) override;
};
#endif // FOOWIDGET_H
foowidget.cpp
#include "foowidget.h"
#include <QMessageBox>
FooWidget::FooWidget(QWidget *parent)
: QWidget(parent)
{ }
void FooWidget::mousePressEvent(QMouseEvent*)
{
QMessageBox::information(nullptr, "Test", getHello());
}
foodialog.h
#ifndef FOODIALOG_H
#define FOODIALOG_H
#include <QDialog>
class SomethingElse
{
public:
QString getHello() const
{ return "OMG! OMG"; }
};
#include "foowidget.h"
namespace Ui {
class FooDialog;
}
class FooDialog : public QDialog, public Injector<SomethingElse>
{
Q_OBJECT
public:
explicit FooDialog(QWidget *parent = nullptr);
~FooDialog();
protected:
void showEvent(QShowEvent *) override;
private:
QScopedPointer<Ui::FooDialog> ui;
};
#endif // FOODIALOG_H
foodialog.cpp
#include "foodialog.h"
#include "ui_foodialog.h"
#include <QMessageBox>
FooDialog::FooDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::FooDialog)
{
ui->setupUi(this);
}
FooDialog::~FooDialog()
{ }
void FooDialog::showEvent(QShowEvent *)
{
QMessageBox::information(nullptr, "Test", getHello());
}
Multiple inheritance + deriving widgets from some small template proxy class works for both custom widgets and those, having UI forms. I've put a FooWidget on a FooDialog (via propagation mechanizm) in the sketch above and got two message boxes.
The idea itself can be implemented better, with smarter template usage, just tried to reduce sample code, anyway it's an unnessesary complication. But technically it works without any additional initializations =)
I'm new to programming. I can not understand how to make a reference to the methods of another class.
I have several files and classes:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtCore/QtGlobal>
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class Valve;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
void openValve(int id);
void closeValve(int id);
private:
Ui::MainWindow *ui;
Settings *settings;
Valve *valve;
};
class A {
...
private:
void start();
}
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowFlags(Qt::CustomizeWindowHint);
this->setFixedSize(this->geometry().width(),this->geometry().height());
//класс для 7 клапанов
valve = new Valve(7);
}
MainWindow::~MainWindow()
{
delete settings;
delete ui;
}
void MainWindow::valveSwitch(int id)
{
if (valve->getState(id))
closeValve(id);
else
openValve(id);
}
void MainWindow::openValve(int id)
{
QString str = "Valve №" + QString::number(id);
valveButton[id-1]->setEnabled(false);
if (valve->open(id)) {
valveButton[id-1]->setEnabled(true);
//valveButton[id-1]->setPalette(QPalette(Qt::green));
//valveButton[id-1]->setStyleSheet(VALVE_OPEN_COLOR);
QString style = QString(DEFAULT_STYLE_BUTTON) + QString(DEFAULT_BACKGROUND_BUTTON);
valveButton[id-1]->setStyleSheet(style);
ui->mainLabel->setText(str + " open! :)");
}
else {
valveButton[id-1]->setEnabled(true);
ui->mainLabel->setText("Cant open " + str);
remoteDisconnect();
}
}
void MainWindow::closeValve(int id)
{
QString str = "Valve №" + QString::number(id);
valveButton[id-1]->setEnabled(false);
if (valve->close(id)) {
valveButton[id-1]->setEnabled(true);
//valveButton[id-1]->setPalette(style()->standardPalette());
valveButton[id-1]->setStyleSheet("");
ui->mainLabel->setText(str + " close! :)");
}
else {
valveButton[id-1]->setEnabled(true);
ui->mainLabel->setText("Cant close " + str);
remoteDisconnect();
}
}
A::A
{
}
A::~A
{
}
void A::start()
{
//MainWindow::openValve(2);
//valve.open(3);
}
How do I access MainWindow class methods openValve/closeValve from class A?
Or how can I access an instance valve of a class Valve of MainWindow's constructor from class A?
//MainWindow::openValve(2);
//valve.open(3);
At very first:
openValve is not static, so you need an instance of MainWindow to be able to call it:
MainWindow* mw_ex0;
// alternatively, if more appropriate:
MainWindow& mw_ex1;
mw_ex0->openValve(2);
mw_ex1.openValve(2);
The MainWindow instance could be a parameter of your function start or a member variable of class A – depending on your concrete needs.
Same applies if you want to access the valve member (valve is a pointer, so you need operator->): mw_ex0->valve->open(3); or mw_ex1.valve->open(3);).
However, you need to grant class A access to those currently private members; three options:
Make A a friend class of MainWindow - this allows A to access MainWindow's private members (might apply for Valve class, too, if open is not public).
Make the appropriate functions public (MainWindow::openValve and Valve::open); to access the valve member of MainWindow, too, you could make it public, too, but it is in general not recommendable to make the internals of a class publicly available to the outside world - someone might simply change your valve member to something else - and your program is broken... So rather provide a simple getter for.
Make A an inner class of MainWindow. Then it gets access to its outer class members implicitly (depending on the requirements for class A, this might not be suitable – up to you to decide...).
Sidenotes:
In your constructor, you do not initialise the settings member.
You do not clean up the valve member in your destructor (potential memory leak).
To avoid having to clean up, you could to incorporate valve directly in your class - this is not always suitable, but might be a good option here (up to you to decide, just showing the alternative):
class MainWindow
{
Valve valve;
};
MainWindow::MainWindow()
: valve(7) // calls constructor directly
{ }
Be aware that you now do use operator. to access the valve's members (mw_ex0->valve.open(3);). Advantage is that Valve will no be automatically cleaned up together with MainWindow. Alternatively, a std::unique_ptr could be used to hold the pointer to your Valve instance, then you get automatic cleanup, too.
You should pass a MainWindow object into the A::start method:
class A {
...
private:
void start(MainWindow & w);
}
void A::start(MainWindow & w) {
w._MainWindow_method_name_here_();
}
Or you should declare a static method in MainWindow class:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
static void aStaticMethod();
};
void MainWindow::aStaticMethod() {
...
}
void A::start() {
MainWindow::aStaticMethod();
}
To access protected/private methods of MainWindow you should declare the A class as a friend of MainWindow:
class MainWindow : public QMainWindow
{
friend class A;
...
};
Update
I create a new class for it to work in a separate thread, and call its methods from the main class (by clicking on the button). Accordingly, I need class A to open / close valves, etc.
The "true Qt way" is to use signals & slots mechanism.
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
// Transform open/closeValve methods into slots
// (a method that can be assigned as an event handler)
//
public slots:
void openValve(int id);
void closeValve(int id);
private:
Ui::MainWindow *ui;
Settings *settings;
Valve *valve;
};
// This class should be a descendant of QObject
//
class A : public QObject
{
Q_OBJECT
// Transform this method to a slot, so it can be called
// as regular method, or can be assigned as an event handler,
// for instance, as QPushButton::click handler.
//
public slots:
void start();
// Add signals
//
signals:
void openValveSignal(int id);
void closeValveSignal(int id);
}
void A::start()
{
// do something and then emit the signal to open valve,
// MainWindow::openValve(2) will be called
emit openValveSignal(2);
...
// do something and then emit the signal to close valve,
// MainWindow::closeValve(3) will be called
emit closeValveSignal(3);
}
// connects A signals with MainWindow slots,
// so when you `emit A::***Signal()` then corresponding
// `MainWindow::***` method will be called
//
void initialize(MainWindow * pWnd, A * pA)
{
QObject::connect(pA, &A::openValveSignal, pWnd, &MainWindow::openValve);
QObject::connect(pA, &A::closeValveSignal, pWnd, &MainWindow::closeValve);
}
You can call a->start() method from MainWindow methods as usual. Or you can connect button clicked signal with A::start method, for instance:
void initialize(MainWindow * pWnd, QAbstractButton * pBtn, A * pA)
{
// C++11 lambda function is used here because A::start has no arguments
QObject::connect(pBtn, &QAbstractButton::clicked, [pA](){ pA->start(); });
QObject::connect(pA, &A::openValveSignal, pWnd, &MainWindow::openValve);
QObject::connect(pA, &A::closeValveSignal, pWnd, &MainWindow::closeValve);
}
so when you click a button then A::start method will be called automatically. And then MainWindow::open/closeValve methods will be called from A::start method.
Declare openValve as public method and valve as public object (open must be public too)
Then use as:
MainWindow mainWindow;
mainWindow.openValve(2);
mainWindow.valve.open(3);
I've to use Singleton pattern for widget in my app.
So I've made implementation for this.
testwidget.h
class TestWidget;
class TstWidgetHolder
{
static TestWidget* wobj;
public:
static const TestWidget* instance();
static void setParent(QWidget*);
};
class TestWidget : public QWidget
{
Q_OBJECT
friend class TstWidgetHolder;
private:
Ui::TestWidget ui;
explicit TestWidget(QWidget *parent = 0);
~TestWidget();
};
testwidget.cpp
TestWidget::TestWidget(QWidget *parent) :
QWidget(parent)
{
ui.setupUi(this);
}
TestWidget::~TestWidget()
{}
TestWidget* TstWidgetHolder::wobj = NULL;
void TstWidgetHolder::setParent(QWidget* obj)
{
static TestWidget tst(obj);
TstWidgetHolder::wobj = &tst;
}
const TestWidget* TstWidgetHolder::instance()
{
return wobj;
}
As simple as this.
Then is main program I'm setting up parent to this singleton.
TstWidgetHolder::setParent(this);
And there the real problem comes.
When main widget closes, application crashes.
According to debugger, the destructor of singleton widget is being called twice.
And that's of course is the cause of crash.
What's the problem?
Is it bug in Qt or mine logic?
HERE SOURCE CODE
When you make TstWidgetHolder::setParent(this) you are delegating ownership (in other words, responsability to destruct) of the TestWidget instance to this. So, right before the object pointed to by this is destructed, it tries to delete tst, which is an static object... and that is what makes your application crash. Either you don't use setParent or you should change TstWidgetHolder::setParent to:
void TstWidgetHolder::setParent(QWidget* obj)
{
TstWidgetHolder::wobj = new TestWidget(obj);
}
I have a problem with accessing ui elements from another class(with instance). I have a second QMainWindow in my application, I can access in secondWindow.cxx class all ui elements but not in read.cxx class. My code looks like following. Where is my mistake? Thank you for your help.
-------------------------------secondWindow.h------------------------------------
#ifndef __secondWindow_h
#define __secondWindow_h
#include "ui_secondwindow.h"
class secondWindow : public QMainWindow
{
friend class read;
igstkStandardClassBasicTraitsMacro(secondWindow, QMainWindow);
Q_OBJECT
public:
igstkStateMachineMacro();
secondWindow();
virtual ~secondWindow();
void createSignalAndSlots();
public slots:
void secondWindowTest();
protected:
private:
Ui::secondMainWindow m_secondWindowUI;
};
#endif
-------------------------------secondWindow.cxx------------------------------------
#include "secondWindow.moc"
#include "secondWindow.h"
#include "read.h"
secondWindow::secondWindow() :m_StateMachine(this)
{
m_secondWindowUI.setupUi(this);
createSignalAndSlots();
}
void secondWindow::createSignalAndSlots()
{
connect(m_secondWindowUI.pushButton1, SIGNAL(clicked()),this, SLOT(secondWindowTest()));
connect(m_secondWindowUI.pushButton2, SIGNAL(clicked()), read::instance(), SLOT(readTest()));
}
void secondWindow::secondWindowTest()
{
m_secondWindowUI.pushButton1->setEnabled(true); //OK
}
secondWindow::~secondWindow(){}
---------------------------------read.h--------------------------------------
#pragma once
#include "secondWindow.h"
class read : public QObject
{
Q_OBJECT
public:
static read *instance();
read();
virtual ~read() {}
public slots:
void readTest();
protected:
secondWindow *m_readUI;
static read *m_read;
private:
};
---------------------------------read.cxx--------------------------------------
#include <read.moc>
#include "secondWindow.h"
#include "read.h"
read *read::m_read= NULL;
read::read()
{
m_readUI = dynamic_cast<secondWindow*>( QApplication::instance() );
}
read *read::instance()
{
if(m_read == NULL)
m_read = new read();
return m_read;
}
void read::readTest()
{
m_readUI->m_secondWindowUI.qlabelTest->setText("test"); //segmentation fault
}
You are casting a QApplication::instance(), which is a QApplication * deriving from QCoreApplication * deriving from QObject *. That won't work, it's not a secondWindow *, not even a QMainWindow *, not even a QWidget *.
Apart from that, your coding style is rather strange -- in Qt, it's customary to use CamelCase for classes, not thisStuff which usually applies to functions and methods. Including <read.moc> is just wrong. Why is read::m_read static? Finally, the coupling between the two window classes is set up in a strange way (accessing global stuff like QApplication just to get a reference to another window smells ugly code). A much better and more obvious approach is to either wrap all of your windows in a parent object or setting up the dependencies explicitly, perhaps like this:
MainWindow *mainWindow = new MainWindow();
SecondWindow *second = new SecondWindow(mainWindow);
UtilityWindow *utilityWin = new UtilityWindow(second);