How to access to widget class from another dialog class - c++

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

Related

How can I set UI widgets in a second window in Qt?

I'm trying to dynamically set a QList of files (or strings) in a second window that appears after Login (LoginWindow), via Qt.
The second window is a QDialog.
How can I interact with UI of LsWindow (second window) ?
I've alredy tried with a setUi() functions in LsWindow.h, but it doesn't run.
void LoginWindow::on_loginButton_clicked(){
// testing login
this->close(); // close login window
QStringList strList; //construct string list
strList.push_back("A");
strList.push_back("B");
QListWidget* l; // creating a widget
l->addItems(strList);
LsWindow* ls = new LsWindow(this);
ls->show();
//ls.ui.listWidget = l; (??)
ls->exec();
}
//LsWindow class implementation
class LsWindow : public QDialog
{
Q_OBJECT
public:
LsWindow(QWidget *parent = Q_NULLPTR);
~LsWindow();
private:
Ui::LsWindow ui;
};

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.

Mapping a Qt base class signal to a slot in a derived class

I am having a problem with Qt signals and slots. I am just learning Qt but I have lots of C++ experience. I have derived a class from QTreeView and I want to handle the columnResized signal. The slot is never being called and I am seeing this in the 'Application Output':
QObject::connect: No such signal TRecListingView::columnResized(int,int,int) in ../ec5/reclistingwidget.cpp:142
The class declaration looks like this:
class TRecListingView : public QTreeView
{
Q_OBJECT
public:
TRecListingView(QWidget *parent, TTopicPtr topic);
~TRecListingView();
private slots:
void onColumnResized(int index, int oldsize, int newsize);
private:
TRecListingModel *Model = 0;
};
In the constructor I am doing this:
connect(this,SIGNAL(columnResized(int,int,int)),
this,SLOT(onColumnResized(int,int,int)));
I had this working earlier before I implemented the derived class. Then I was mapping the signal to a slot in the parent widget.
I have tried running qmake and rebuilding the project. I also tried this:
QTreeView *tv = this;
connect(tv,SIGNAL(columnResized(int,int,int)),
this,SLOT(onColumnResized(int,int,int)));
columnResized is not a signal, but slot, so you cannot connect to it.
Instead you can connect to the QHeaderView::sectionResized
connect(this->horizontalHeader(),SIGNAL(sectionResized(int,int,int)),
this, SLOT(onColumnResized(int,int,int)));
Because it is not a signal:
From documentation:
void QTreeView::columnResized ( int column, int oldSize, int newSize ) [protected slot]
Try reimplement it:
#include <QTreeView>
#include <QHeaderView>
#include <QTimer>
#include <QDebug>
class TRecListingView : public QTreeView
{
Q_OBJECT
public:
TRecListingView(QWidget *parent=0):
QTreeView(parent)
{
QTimer::singleShot(0, this, SLOT(fixHeader()));
}
public slots:
void fixHeader()
{
QHeaderView *hv = new QHeaderView(Qt::Horizontal, this);
hv->setHighlightSections(true);
this->setHeader(hv);
hv->show();
}
protected slots:
void columnResized(int a, int b, int col)
{
qDebug() << "This is called";
}
public slots:
};
Simple usage:
TRecListingView trec;
QStringList stringList;
stringList << "#hello" << "#quit" << "#bye";
QStringListModel *mdl = new QStringListModel(stringList);
trec.setModel(mdl);
trec.show();
Now it works properly and when you resize header, you'll see many This is called strings.

Qt, can't display child widget

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

Qt - reloading widget contents

I'm trying to modify the fridge magnets example by adding a button that will reload the widget where the draggable labels are drawn, reflecting any changes made to the text file it reads. I defined another class that would contain the button and the DragWidget object, so there would be an instance of this class instead of DragWidget in main():
class wrapWidget: public QWidget
{
Q_OBJECT
public:
wrapWidget();
};
wrapWidget::wrapWidget()
{
QGridLayout *gridlayout = new QGridLayout();
DragWidget *w = new DragWidget();
QPushButton *b = new QPushButton("refresh");
gridlayout ->addWidget(w,0,0);
gridlayout ->addWidget(b,1,0);
setLayout(gridlayout );
connect(b,SIGNAL(clicked()),w,SLOT(draw()));
}
The call to connect is where I'm trying to do the refresh thing. In the original fridge magnets example, all the label drawing code was inside the constructor of the DragWidget class. I moved that code to a public method that I named 'draw()', and called this method from the constructor instead. Here's DragWidget definition and implementation:
#include <QWidget>
QT_BEGIN_NAMESPACE
class QDragEnterEvent;
class QDropEvent;
QT_END_NAMESPACE
class DragWidget : public QWidget
{
public:
DragWidget(QWidget *parent = 0);
public slots:
void draw();
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
void mousePressEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);
};
DragWidget::DragWidget(QWidget *parent)
: QWidget(parent)
{
draw();
QPalette newPalette = palette();
newPalette.setColor(QPalette::Window, Qt::white);
setPalette(newPalette);
setMinimumSize(400, 100);//qMax(200, y));
setWindowTitle(tr("Fridge Magnets"));
setAcceptDrops(true);
}
void DragWidget::draw(){
QFile dictionaryFile(":/dictionary/words.txt");
dictionaryFile.open(QFile::ReadOnly);
QTextStream inputStream(&dictionaryFile);
int x = 5;
int y = 5;
while (!inputStream.atEnd()) {
QString word;
inputStream >> word;
if (!word.isEmpty()) {
DragLabel *wordLabel = new DragLabel(word, this);
wordLabel->move(x, y);
wordLabel->show();
wordLabel->setAttribute(Qt::WA_DeleteOnClose);
x += wordLabel->width() + 2;
if (x >= 245) {
x = 5;
y += wordLabel->height() + 2;
}
}
}
}
I thought that maybe calling draw() as a slot would be enough to reload the labels, but it didn't work. Putting the draw() call inside the widget's overriden paintEvent() instead of the constructor didn't work out as well, the program would end up in an infinite loop.
What I did was obviously not the right way of doing it, so what should I be doing instead?
My quick guess is, you haven't added Q_OBJECT macro to dragwidget.h header, the moc file for DragWidget class wasn't generated and the connect failed with "no such slot as draw()" error.
It might be also a good idea to add "CONFIG += console" to .pro file - you'll see all warning messages (like the one about connect error), so tracking such mistakes would be easier. You might also check return value of connect.
I noticed that you opened file this way:
QFile dictionaryFile(":/dictionary/words.txt");
Note that the file name starts with ":", and it means that the file will be read from your qrc resource package instead of your local disk. So if you made the change on words.txt, it will be read by code only when you compiled qrc file next time. So you must have understood how to fix it, right? Good Luck:)