Qt, can't display child widget - c++

I have two widgets defined as follows
class mainWindow : public QWidget
{
Q_OBJECT
public:
mainWindow();
void readConfig();
private:
SWindow *config;
QVector <QString> filePath;
QVector <QLabel*> alias,procStatus;
QVector <int> delay;
QGridLayout *mainLayout;
QVector<QPushButton*> stopButton,restartButton;
QVector<QProcess*> proc;
QSignalMapper *stateSignalMapper, *stopSignalMapper, *restartSignalMapper;
public slots:
void openSettings();
void startRunning();
void statusChange(int);
void stopProc(int);
void restartProc(int);
void renew();
};
class SWindow : public QWidget
{
Q_OBJECT
public:
SWindow(QWidget *parent=0);
void readConfig();
void addLine(int);
private:
QVector<QPushButton*> selectButton;
QVector<QLabel*> filePath;
QVector<QLineEdit*> alias;
QSignalMapper *selectSignalMapper;
QVector<QSpinBox*> delay;
QGridLayout *mainLayout;
public slots:
void selectFile(int);
void saveFile();
void addLineSlot();
};
when i create and display SWindow object from mainWindow like this
void mainWindow::openSettings()
{
config = new SWindow();
config->show();
}
everything is ok, but now i need to access the mainWindow from SWindow, and
void mainWindow::openSettings()
{
config = new SWindow(this);
config->show();
}
doesn't display SWindow. How can i display SWindow?
How do i call a function on widget close?

By default a QWidget isn't a window. If it is not a window and you specify a parent, it will be displayed inside the parent (so in your case it is probably hidden by other widgets inside your mainWindow).
Look at windowFlags() too. Or you could make your SWindow inherit from QDialog, depending on what you use it for.
As for calling a function on widget close : you could reimplement closeEvent().

When you do config = new SWindow(this); you're setting the parent of config to be the instance of mainWindow.
This means config is no longer a top-level widget, therefore it won't display outside the mainWindow instance (specifically, it would need to be the central widget or inside the mainWindow instance's layout to be displayed).
EDIT: Sorry - I missed your last question; How do i call a function on widget close
You will want to override the QWidget::closeEvent(QCloseEvent *event) method. This gets called when you close a top-level widget. The most practical thing to do is emit() a signal so that another class can handle it having been closed.

As noted by Leiaz, you can use the windowsFlags flag when you create the widget. It would look like this:
void mainWindow::openSettings()
{
config = new SWindow(this, Qt::window);
config->show();
}
To reimplement the closeEvent:
header:
protected:
virtual void closeEvent ( QCloseEvent * event )
cpp:
void sWindow::closeEvent(QCloseEvent *event)
{
this->parentWidget()->SomeFunction();
qWidget::closeEvent(event);
}
However, its probably better to use signal/slots for your case here. Since you said you want to call the parent's renew method on some button click in sWindow, what you want is to EMIT a signal everytime the button is clicked, and connect this signal in the parent with the parent's refresh slot.
void sWindow::sWindow()
{
...
connect(ui.button, SIGNAL(clicked()), this, SLOT(btnClicked()));
}
void sWindow::btnClicked()
{
// whatever else the button is supposed to do
emit buttonClicked();
}
and in your parent class
void mainWindow::openSettings()
{
config = new SWindow(this, Qt::window);
connect(config, SIGNAL(buttonClicked()), this, SLOT(refresh()));
config->show();
}

Related

qt - send signal between objects in a class with different parents

I have a class ("controller" for example)
and in this class, I have created many objects of different other classes
with different parents.
How to send signal between that classes and "controller" to call a function in "controller" class?
#include "controller.h"
Controller::Controller(QObject *parent) : QObject (parent){
connect(sender(), SIGNAL(recivedCall(QString)), this, SLOT(alert(QString)));
}
void Controller::onCall(QJsonObject callinfo){
nodes[callinfo["username"].toString()]= new PanelManager();
nodes[callinfo["username"].toString()]->handleCallStateChanged(callinfo);
}
void Controller::alert(QString callinfo){
qDebug()<<callinfo;
}
For example, how to send signal from "recivedCall" in each "PanelManager" object to call "alert" function in "controller" class ?
The object which creates your two components has to set the connections between your signal and your slot. But, you shouldn't expose inner components (i.e. create getter to return a pointer on a attribute).
A way to tackle the last problem with Qt is to create a signal in your parent and let it broadcast the calls.
For example, if I need to connect a QCheckBox to a QLineEdit in two different widgets:
class Parent1: public QWidget
{
Q_OBJECT
public:
Parent1(): QWidget(), myCheckBox(new QCheckBox("Edit", this))
{
connect(myCheckBox, &QCheckBox::clicked, this, &Parent1::editForbidden);
}
private:
QCheckBox* myCheckBox;
signals:
void editForbidden(bool);
};
class Parent2: public QWidget
{
Q_OBJECT
public:
Parent2(): QWidget(), myLineEdit(new QLineEdit("Edit", this))
{
connect(this, &Parent2::forbidEdit, myLineEdit, &QLineEdit::setReadOnly);
}
private:
QLineEdit* myLineEdit;
signals:
void forbidEdit(bool);
};
// In the parent of Parent1 and Parent2 (or in the main if there is no parent)
QObject::connect(p1, &Parent1::editForbidden, p2, &Parent2::forbidEdit);
In this example, when I click on the checkbox in parent1, the lineEdit in parent2 is disabled. But, Parent1 doesn't know anything about Parent2.

Qt: Which object/item should I use to create clickable icons

I'm trying to write an editor for an rpg (Role Playing Game) (npc / quests / items etc.). I need to create an icon with a "white background" that represents the npc's image. It should be clickable (when it's clicked, current selected npc's icon ID will be set according to the selection).
I've managed to build a pop-up dialog to show all the icons, but couldn't manage to find a way to create clickable icons. Which class should I implement in order to get it working?
Clickable icons can be achieved using either QPushButton or QToolButton:
QPushButton* button = new QPushButton;
button->setIcon(QIcon("/path/to/my/icon"));
Clickable QLabel : https://wiki.qt.io/Clickable_QLabel
Use with a QPixmap : http://doc.qt.io/qt-4.8/qlabel.html#pixmap-prop
Header
class ClickableLabel : public QLabel
{
Q_OBJECT
public:
explicit ClickableLabel( const QString& text="", QWidget* parent=0 );
~ClickableLabel();
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent* event);
};
Source
ClickableLabel::ClickableLabel(const QString& text, QWidget* parent)
: QLabel(parent)
{
setText(text);
}
ClickableLabel::~ClickableLabel()
{
}
void ClickableLabel::mousePressEvent(QMouseEvent* event)
{
emit clicked();
}
I've done something similar but didn't want something that looked like a button, nor did I want to get into style overrides or special painting. Instead, I created a ClickableLabel class that derives from QLabel.
The pertinent part of the code is:
class ClickableLabel : public QLabel
{
protected:
virtual void mouseReleaseEvent (QMouseEvent *evt)
{
emit clicked (evt->button ());
}
signals:
void clicked (int button);
...rest of class definition...
}
You can adjust the signal parameters as desired.

How can I connect two windows in Qt?

I am trying make window with chat, and "main" window. If I click at username in chat window, it should show profile in main window. What's the best way to do something like this?
You should pass pointer to one window class from another and connect them by slots/signals:
class MainWindow
{
Q_OBJECT
...
public slots:
void onUsernameSelected(...);
};
class ChatWindow
{
Q_OBJECT
...
MainWindow *mainWindow;
...
ChatWindow(QObject *parent, MainWindow *mainWindow):
...
mainWindow(mainWindow)
{
connect(this, &ChatWindow::usernameSelected, mainWindow, &MainWindow::onUsernameSelected);
}
};

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 ;)