How to pass data between preestablished windows in Qt? - c++

I have a Wizard class, which creates three pages in the form of QStackedWidget in its constructor. Each page is its own class.
In page one, I have a variety of forms and I've implemented the slot that assigns them when the next button is clicked. They're assigned to their respective variables in the Wizard class. My goal is to pass this information to the PageTwo class to do the appropriate computations. Any pointers on how to do this?
Wizard class
class Wizard : public QDialog
{
Q_Object
public:
Wizard();
QString nameInput;
etc
}
constructor
Wizard::Wizard() : QDialog()
{
pages = new QStackedWidget();
pages->addWidget(pageOne = new PageOne(pages));
pages->addWidget(pageTwo = new PageTwo(pages));
connect(next, SIGNAL(clicked(bool)), this, SLOT(saveFormInfo()));
}
assignment function
void Wizard::saveFormInfo()
{
nameInput = pageOne->nameEdit->text();
etc
}
PageTwo class
class PageTwo : public QWidget
{
public:
PageTwo(QWidget *parent = 0);
void displayOutput(QHboxLayout *layout);
}
is this even the right way going about doing this? This is my first GUI project and I can't seem to figure out how to pass the variables in, I'm assuming because the page is created before the data is passed

There are many ways you could do it. Without changing too much your code, there is a suggestion below.
Wizard class
class Wizard : public QDialog
{
Q_Object
public:
Wizard();
QString nameInput;
etc
}
Wizard::Wizard() : QDialog()
{
pages = new QStackedWidget();
pages->addWidget(pageOne = new PageOne(pages, this));
pages->addWidget(pageTwo = new PageTwo(pages, this));
connect(next, SIGNAL(clicked(bool)), this, SLOT(saveFormInfo()));
}
void Wizard::saveFormInfo()
{
nameInput = pageOne->nameEdit->text();
etc
}
On pagetwo.h
class Wizard;
class PageTwo : public QWidget
{
public:
PageTwo(QWidget *parent = 0, Wizard * wizard);
void displayOutput(QHboxLayout *layout);
private:
Wizard * wizard;
}
On pagetwo.cpp
#include <wizard.h>
PageTwo::PageTwo(QWidget * parent, Wizard * w) :
QWidget(parent), wizard(w)
{
}
Then you can use this->wizard->nameInput to get the value you want on PageTwo

Related

Linking a QWidget to another QWidget new created

I am try to, by pressing a button in the main QWidget, to create a new QWidget. In that new created QWidget I want to have a button connected to a slot of the main QWidget.
class UI : public QWidget
{
public:
UI(){connection();};
private:
QPushButton* all = new QPushButton{ "ALL" };
void connection(){
QObject::connect(all,QPushButton::clicked,[](){
SmallGUI* s=new SmallGUI{};
s->show();
});
}
void something(){
//something
}
and the second class
class SmallGUI1 :
public QWidget
{
public:
SmallGUI(){connection();};
private:
QPushButton* N =new QPushButton;
void connection(){
//to connect N to something()
}
I want to connect N to something() .
Before we start, there are some other problems with you code.
Note that in your second class, the constructor is not named the same as the class, which will cause some... Problems.
You also forgot to put a parent for your buttons (which may thus cause some unexpected results) AND for your Widgets (which is again not a good idea).
So, that being said, let us get to the main topic.
I tend to only put prototypes and declare the attributes in the .h file to make the code clearer, but you may of course adapt it to your needs or to your own programming convention.
There are several ways to do something like this, but the simplest one should look like this :
SmallGUI1.h :
#include "UI.h" //The file in which the class UI is declared
//OR :
//class UI; //If you need to include this file in UI.h
class SmallGUI1 : public QWidget{
Q_OBJECT //Q_OBJECT macro, check the doc for more explainations about it
public:
explicit SmallGUI1(UI *parent = nullptr); //Explicit means that this constructor cannot be used for implicit casts
~SmallGUI1();//Destructor needed because I only put the constructor above
private:
QPushButton* N; //Not very good looking to initialize attributes in the .h in my opinion, but works fine.
}
SmallGUI1.cpp :
SmallGUI1::SmallGUI1(UI *parent) : QWidget(parent){
N = new QPushButton(tr("Some text on the button") , this); //tr to enable translation on this string
//************* If I understood your question correctly, this is what you are looking for *************
connect(N , &QPushButton::clicked , parent , &UI::doSomething); //Select the signal you want
/*
Some code here
*/
show();
}
SmallGUI1::~SmallGUI1(){qDeleteAll(children());}
UI.h :
class UI : public QWidget{
Q_OBJECT
public:
explicit UI(QWidget *parent = nullptr);
~UI();
private:
QPushButton* all;
private slots :
void createSmallGUI1();
void doSomething();
}
UI.cpp :
#include "SmallGUI1.h"
UI::UI(QWidget *parent) : QWidget(parent){
all = new QPushButton(tr("ALL") , this);
connect(all , &QPushButton::clicked , this , &UI::createSmallGUI1);
/*
Some code here
*/
}
UI::~UI(){qDeleteAll(children());}
void UI::createSmallGUI1(){SmallGUI1 *gui = new SmallGUI1(this);}
void UI::doSomething(){
/*
Clever code here
*/
}
You can define the second widget as a child of the main widget to make things easier:
class UI : public QWidget {
...
private:
SmallGUI* s;
...
and then initialize it in the UI constructor, along with your all button. You can initially hide the child widget or disable it:
UI() {
all = new QPushButton{"ALL", this};
setWindowTitle("UI"); // just for clarification
s = new SmallGUI(this);
s->hide();
connection();
};
and 'show' it with button clicked signal
connect(all, &QPushButton::clicked, s, &SmallGUI::show);
Doing so gives you the option to connect the clicked signal of your N button to the something function in the parent class
connect(s->N, &QPushButton::clicked, this, &UI::something);
The complete program would be as follows,
#include <QApplication>
#include <QMessageBox>
#include <QPushButton>
#include <QWidget>
class SmallGUI : public QWidget {
public:
SmallGUI(QWidget* parent) : QWidget(parent) {
N = new QPushButton{"btn2", this};
connection();
};
QPushButton* N;
private:
void connection(){};
};
class UI : public QWidget {
public:
UI() {
all = new QPushButton{"ALL", this};
setWindowTitle("UI"); // just for clarification
s = new SmallGUI(this);
s->hide();
connection();
};
private:
SmallGUI* s;
QPushButton* all;
void connection() {
connect(all, &QPushButton::clicked, s, &SmallGUI::show);
connect(s->N, &QPushButton::clicked, this, &UI::something);
}
void something() { QMessageBox::information(this, "Hello", "Hello"); }
};
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
UI w;
w.show();
return a.exec();
}
It is not good idea to connect to parent's slots from "nested" class, since SmallGUI1 will be tied to class UI.
Here is better solution, I think:
class UI : public QWidget
{
public:
UI(){connection();};
private:
QPushButton* all = new QPushButton{ "ALL" };
void connection(){
QObject::connect(all,QPushButton::clicked,[](){
SmallGUI1* s=new SmallGUI1;
connect(s,&USmallGUI1::button_clicked,this,&UI::something);
s->show();
});
}
void something(){
//something
}
And SmallGUI1 class:
class SmallGUI1 :
public QWidget
{
public:
SmallGUI1(){connection();};
signals:
void button_clicked();
private:
QPushButton* N;
void connection(){
//to connect N to something()
N = new QPushButton;
connect(N,&QPushButton::clicked,this,&SmallGUI1::button_clicked)
}
This way, you are connecting QPusButton::clicked signal from SmallGUI1 to the signal SmallGUI1::button_clicked(). Dont need to implement additional slot, just connect signal to signal.
And in UI you are connecting button_clicked() signal to the slot dosomething()
DONT FORGET THE CONSTRUCTOR OF SmallGUI1! In your code, SmallGUI() will be just a method, which will not be called when SmallGUI1 is instantiated, and you have to call it by yourself.

How to set visibility on widgets within a subclass of QWidgetAction

Qt 5.6.3, eglfs Linux platform.
I have a selection of classes derived from QWidgetAction. The QWidgetActions are all parented from a menu, and the widgets they contain are parented from the same menu. The contained widgets are all set as the default widget for the QWidgetAction. Nothing has been reimplemented from QWidgetAction.
I thought that setting the visibility of the QWidgetAction would automatically set the visibility of the custom widget set contained within? Is this not true, as doing so is certainly not showing and hiding the widgets as required!? Must I do something else to pass the visibility change to the contained widgets? Must I directly request the widget from the QWidgetAction and then apply visibility to it directly (which seems like a hack)?
I'm interested in how the QWidgetActions are supposed to be implemented. The documentation is almost non-existent, so I'm after peoples experience with them as much as anything. I have intermittent issues with what looks like a double delete of a custom widget and visibility not behaving as it should.
class Base : public QWidgetAction
{
Q_OBJECT
public:
explicit Base(QWidget* parent, QString labelText = "", QString iconPath = "", Qt::AlignmentFlag alignment = Qt::AlignHCenter) :
QWidgetAction(parent),
mCustomWidget(nullptr),
mParentWidget(nullptr),
mTextLabel(nullptr),
mAlignment(alignment),
mLabelText(labelText),
mIconPath(iconPath) {}
virtual ~Base() {}
protected:
QWidget *mCustomWidget;
QWidget *createTheWidgetSet(QWidget *parent)
{
if (mParentWidget == nullptr) {
mParentWidget = new QWidget(parent);
mCustomWidget = createCustomWidget(mParentWidget);
if (mCustomWidget != nullptr) {
if (!mLabelText.isEmpty()) {
mCustomWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
}
}
int rightMargin = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, mParentWidget);
layout->setContentsMargins(1, 2, rightMargin, 2);
if (!mLabelText.isEmpty()) {
QString some_calced_text{};
mTextLabel = new QLabel(some_calced_text, mParentWidget);
layout->addWidget(mTextLabel);
} else {
if(mAlignment == Qt::AlignLeft){
int some_calced_val{20};
layout->addSpacing(some_calced_val);
}
}
if(mAlignment == Qt::AlignRight){
layout->addStretch();
}
layout->addWidget(mCustomWidget);
if(mAlignment == Qt::AlignLeft){
layout->addStretch();
}
}
setDefaultWidget(mParentWidget);
return mCustomWidget;
}
virtual QWidget *createCustomWidget(QWidget *parent) = 0;
private:
Q_DISABLE_COPY(Base)
QWidget *mParentWidget;
QLabel *mTextLabel;
Qt::AlignmentFlag mAlignment;
QString mLabelText;
QString mIconPath;
};
class SpinBoxActionWidget : public Base
{
Q_OBJECT
public:
explicit SpinBoxActionWidget(QWidget* parent, QString labelText = "", QString iconPath = "") :
Base(parent, labelText, iconPath),
mSpinBox(nullptr)
{
createTheWidgetSet(parent);
}
virtual ~SpinBoxActionWidget() {}
QSpinBox* getSpinBox() const
{
return mSpinBox;
}
protected:
QWidget *createCustomWidget(QWidget *parent) override
{
if (mSpinBox == nullptr) {
mSpinBox = new QSpinBox(parent);
mSpinBox->setFixedHeight(22);
}
return mSpinBox;
}
private:
Q_DISABLE_COPY(SpinBoxActionWidget)
QSpinBox *mSpinBox;
};
/* Elsewhere in code.... */
{
QMenu theMenu = new QMenu(parentWindow);
SpinBoxActionWidget theAct = new SpinBoxActionWidget(theMenu);
SpinBoxActionWidget theSecondAct = new SpinBoxActionWidget(theMenu);
theMenu->addAction(theAct);
theMenu->addAction(theSecondAct);
/* I now assume that I can do this, and the entire entry in the menu
* represented by "theAct" can be made visible and invisible.
* This doesn't work however, either the widget remains visible,
* or is partially hidden.
theAct->setVisible(true);
theAct->setVisible(false);
*/
}
You are not reimplementing the interface, that's why it doesn't work.
First, note that QWidgetAction derives from QAction which is not a QWidget; however, it does have a setVisible() function, which will actually just forward the call to all widgets created by the action.
You have to reimplement QWidgetAction::createWidget(parent) to add a new widget; your createCustomWidget was doing nothing useful. Here is a very simple example:
class SpinAction : public QWidgetAction
{
Q_OBJECT
public:
SpinAction(QObject* parent) : QWidgetAction(parent) {}
virtual ~SpinAction() {}
QWidget* createWidget(QWidget* parent) { return new QSpinBox(parent); }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// reimplement this function
};
You can add your action to whatever container you want, menus, toolbars, etc... This example will create a new widget for each container, and these created widgets won't be synchronized (for example on the spinbox value).
I just tested it in a main window, with a widget action added to a menu and a toolbar, and calling setVisible() works flawlessly.

Signal to it's own instance doesn't work

I connected the clicked(bool) event from QPushButton to a private slot mySlot() of my own Widget. But the slot is never called (I placed a breakpoint in mySlot()). I'm using c++ and Qt5.
I wrote a minimal version of my code:
MyLayout.h
class MyLayout : public QWidget
{
Q_OBJECT
public:
MyLayout(QWidget* parent = NULL);
private:
QPushButton *next;
private slots:
void mySlot();
}
MyLayout.cpp
MyLayout::MyLayout(QWidget* parent) : QWidget(parent)
{
next = new QPushButton("Next Step");
QObject::connect(next, SIGNAL(clicked(bool)), this, SLOT(mySlot()));
}
void MyLayout::mySlot() { /* do something */ }
Any ideas?
You created a parentless button and never showed it. Start by giving it parent (this), so it gets shown together with your widget:
next = new QPushButton("Next Step", this);
Then learn how to use layouts.

Change object of other class qt

I am using Qt and C++, after click on menu item second window appears, and after click on a button in second menu (slot saveData()), I want to change object (obj_map) of class MainMenu. Is it possible, and how to do it the best way? Because I now cannot modify obj_map, because it is in different class. I tried to do something with pointers, but the result was a segmentation fault.
Main Window:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
map obj_map;
public Q_SLOTS:
void saveMap();
private:
Ui::MainWindow *ui;
};
Other window which appears after click in on menu item in main window.
namespace Ui
{
class PreferencesWindow;
}
class PreferencesWindow : public QWidget
{
Q_OBJECT
public:
explicit PreferencesWindow(QWidget *parent = 0);
public Q_SLOTS:
void saveData();
private:
Ui::PreferencesWindow *uip;
};
From here I need to change obj_map
void PreferencesWindow::saveData()
{
// FROM HERE I NEED TO CHANGE obj_map
}
Preferences object is created in a slot:
void MainWindow::saveMap()
{
PreferencesWindow *p = new PreferencesWindow();
p->show();
}
You could use signals and slots: when saveData() is called, emit a signal, like emit saveDataClicked() and catch that signal in the MainWindow with a slot called change_obj_map. There, you can do your changes.
So, in MainWindow you can write:
connect (PreferencesWindow, SIGNAL(saveDataClicked()), this, SLOT(change_obj_map());
and then in the slot:
void change_obj_map()
{
// Do your changes here
}
Another way is having a local obj_map in PreferencesWindow that is a pointer to the address of obj_map in MainWindow. So, when you create PreferencesWindow, you can just pass the address of MainWindow's obj_map to the constructor and assign that address to the local variable obj_map.
As PreferencesWindow objects are created by MainWindow, the easiest is to have PreferencesWindow objects store a pointer to MainWindow:
class MainWindow;
class PreferencesWindow : public QWidget
{
Q_OBJECT
public:
explicit PreferencesWindow(MainWindow *parent = 0);
public Q_SLOTS:
void saveData();
private:
Ui::PreferencesWindow *uip;
MainWindow* m_mainwindow;
};
Pass the pointer upon construction:
void MainWindow::saveMap()
{
PreferencesWindow *p = new PreferencesWindow( this );
p->show();
}
Then use it:
PreferencesWindow::PreferencesWindow(MainWindow *parent) :
QWidget( parent ),
m_mainwindow( parent )
{
}
void PreferencesWindow::saveData()
{
// FROM HERE I NEED TO CHANGE obj_map
m_mainwindow->obj_map.....it's accessible!
}

How to access to widget class from another dialog class

Firstly, I have two classes.
The First class called Widget, and the second class called addItem.
The Widget class is the main class(main window) of application ui, but the addItem class is just window, appears when click on add person to add new contact.
Also the Widget class has a child element called tableWidget .
Now I'm in addItem class, How to access to tableWidget element that is a child followed to Widget class?
Widget Class (.h)
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
void resizeEvent(QResizeEvent *event);
private slots:
void on_delete_btn_clicked();
void on_add_btn_clicked();
private:
Ui::Widget *ui;
};
addItem Class (.h)
namespace Ui {
class addItem;
}
class addItem : public QDialog
{
Q_OBJECT
public:
explicit addItem(QWidget *parent = 0);
~addItem();
private slots:
void on_addBtn_clicked();
private:
Ui::addItem *ui;
};
Edit
The following method belong to addItem class.
addItem.cpp:
void addItem::on_addBtn_clicked(){
emit mySignal(ui->name_txt->text(), ui->address_txt->text(), ui->phoneNum_txt->text(), ui->mobileNum_txt->text());
Widget *widget = new Widget;
connect(this, SIGNAL(mySignal(QString,QString,QString,QString)), widget, SLOT(addMyItem(QString,QString,QString,QString)));
this->close();
}
See also the rest of the code that I wrote :
addItem.h:
signals:
void mySignal(QString, QString, QString, QString);
Widget.h (mainwindow):
private slots:
void addMyItem(QString, QString, QString, QString);
Widget.cpp (mainwindow):
void Widget::addMyItem(QString name, QString address, QString phone_number, QString mobile_number){
qDebug() << name << "\n" << address << "\n" << phone_number << "\n" << mobile_number;
}
Setting
public:
Ui::Widget *ui;
isn't good practice, so you can use getters and setters.
For example, if you want take text from QTableWidget, provide method for example
QString Widget::getCellData(int row, int col)
{
return ui->tableWidget->item(row,col)->text();
}
To set data:
void Widget::setCellData(int row, int col, QString txt)
{
//create item if you need
ui->tableWidget->item(row,col)->setText(txt);
}
and so on. I hope you will understand main idea and how to do it.
But you can use also signals and slots:
For example user clicks on add button and you want add new data in table, then emit from addItem button signal:
emit mySignal(name, address, phone , mobile);
where name is ui->nameLineEdit->text() and so on. Just take text from lineEdit and send it.
Catch it with special slot in Widget class. In this slot you can create new row and set data.
void Widget::mySlot(QString name,QString address,QString phone ,QString mobile)
{
//create cells and set data in it
}
And of course you should connect mySignal from addItem class to mySlot in Widget class.
addItem *mDialog = new addItem;
connect(mDialog,SIGNAL(mySignal(QString,QString,QString,QString)),this,SLOT(mySlot(QString,QString,QString,QString)));
Conclusion: I think, you should use signals and slots.
Edit
Explanation in detail:
You have Dialog class and mainwindow:
dialog.h:
signals:
void mySignal(QString,QString,QString,QString);
dialog.cpp When user clicks we get data, emit signal and close dialog.
void Dialog::on_addItemButton_clicked()
{
emit mySignal(ui->lineEdit_2->text(),ui->lineEdit_3->text(),ui->lineEdit_4->text(),ui->lineEdit_5->text());
this->close();
}
mainwindow.h
private slots:
void addMyItems(QString,QString,QString,QString);
mainwindow.cpp
void MainWindow::addMyItems(QString name,QString address,QString phone ,QString mobile)
{
ui->tableWidget->setRowCount(ui->tableWidget->rowCount() + 1);//increase row count by 1
int row = ui->tableWidget->rowCount() - 1;
//set all data in cells
ui->tableWidget->setItem(row,0,new QTableWidgetItem(name));
ui->tableWidget->setItem(row,1,new QTableWidgetItem(address));
ui->tableWidget->setItem(row,2,new QTableWidgetItem(phone));
ui->tableWidget->setItem(row,3,new QTableWidgetItem(mobile));
}
Slot for button in mainwindow which opens dialog:
void MainWindow::on_Add_clicked()
{
Dialog *dia = new Dialog;//our dialog
dia->setAttribute(Qt::WA_DeleteOnClose);//we don't want memory leak
//connection
connect(dia,SIGNAL(mySignal(QString,QString,QString,QString)),this,SLOT(addMyItems(QString,QString,QString,QString)));
dia->setModal(true);//you can use modal window instead of exe()
dia->show();
}
Result:
I clicked on Add button and get dialog:
Now I set some data and clicked Additem button:
As you can see, our connection works, data properly placed in the tableWidget.
The way I normally handle these things is quite non Qt-ish, but it makes unit testing easier. I have something like MVC (model-view-controller), but in Qt the V and C are one.
For example the model could be:
class Record
{
public:
std::string mName;
std::string mAddress;
std::string mPhoneNumber;
std::string mMobileNumber;
};
class Model
{
public:
void AddRecord(std::unique_ptr<Record> rec)
{
mRecords.emplace_back(std::move(rec));
}
const Record* ItemAt(size_t index) const
{
if (index > mRecords.size()) { return nullptr; }
return mRecords(index);
}
size_t NumRecords() const
{
return mRecords.size();
}
private:
std::vector<std::unique_ptr<Record> mRecords;
};
Then in your main function you can construct a model and pass it to your main/view window:
int main( int argc, char **argv )
{
// Create app instance
QApplication a( argc, argv );
Model model;
MainWindow mainWindow(model); // MainWindow stores a reference to the model
mainWindow.exec();
And the when you create the sub dialog simply pass that the model too. Or have it return a std::unique_ptr<Record>
void MainWindow::on_Add_clicked()
{
Dialog *dia = new Dialog(this, mModel);//Where mModel is the Model instance reference we passed to Widget in main()
dia->exec();
}
Its worth noting that Qt comes with its own model classes for the views. You could take your own Model class and have Qts view model auto update the UI when your view model changes. This is much more advanced though. For now you could just manually keep your model in sync with your UI, i.e if you add a record or remove a record from model, then update your table widget.
Disclaimer: I didn't actually try to compile any of this code, treat it as pesudo code ;)