How to dynamically add QPushButtons to UI - c++

FYI I am using Qt Creator 3.3.2 on Linux Mint.
Okay first I would like to admit I have seen a few questions very similar to the one I am asking... Such as How to add a widget (QPushButton for example) dynamically to a layout built in designer. However (for whatever reason) I have had no success in either understanding the answer or implementing it. I am extremely new to Qt and this is my first project in it so please be through so I can understand the answer.
What I have so far:
The UI:
I have a scroll area named srcFolderPairSelect and under that (in the 'object' window) I have a scroll contents named scrLayFolderPairSelect. In a different section/area (different frame and layout) I have a button that is to add a button, to the before mentioned scroll contents, named btnAddNewFolderPair, upon click. PS: I need it to be able to be clicked more than once (need to be able to add multiple dynamic buttons to the scroll contents).
The header file:
#ifndef SYNCCENTER_H
#define SYNCCENTER_H
#include <QMainWindow>
namespace Ui {
class SyncCenter;
}
class SyncCenter : public QMainWindow
{
Q_OBJECT
public:
explicit SyncCenter(QWidget *parent = 0);
~SyncCenter();
private slots:
void on_btnAddFolderPair_clicked();
private:
Ui::SyncCenter *ui;
};
#endif // SYNCCENTER_H
The UI cpp (synccenter.cpp):
#include "synccenter.h"
#include "ui_synccenter.h"
SyncCenter::SyncCenter(QWidget *parent) : QMainWindow(parent), ui(new Ui::SyncCenter)
{
ui->setupUi(this);
}
SyncCenter::~SyncCenter()
{
delete ui;
}
void SyncCenter::on_btnAddFolderPair_clicked()
{
QPushButton* button = new QPushButton("test");
button->setVisible(true);
ui->scrLayFolderPairSelect->layout()->addWidget(button);
}
Also how do I form onclick events for the dynamic buttons?

In a very fast way you can do it like this:
void SyncCenter::on_btnAddFolderPair_clicked()
{
static int count{};
QPushButton* button = new QPushButton(QString("test %1").arg(++count), this);
button->setVisible(true);
connect(button, &QPushButton::clicked, [&count](){
std::cout << "Clicked button number: " << count << std::endl;
});
ui->scrLayFolderPairSelect->layout()->addWidget(button);
}
The main idea is to store slots(handlers), then, if you need - store buttons itselves.
This my "very fast" solution is based on not a good way of implementing this.
You have a lot of ways of implementing this, however, I think, the best solution is to create QButtonGroup because it has a signals such as:
void buttonClicked(QAbstractButton *button);
void buttonClicked(int id);
So, your code will be like:
class SyncCenter {
//....
private slots:
void buttonInGroupClicked(QAbstractButton *);
private:
QButtonGroup *buttonGroup;
};
SyncCenter::SyncCenter(/*...*/) {
buttonGroup = new QButtonGroup(this);
connect(buttonGroup, SIGNAL(buttonClicked(QAbstractButton*)), SLOT(buttonInGroupClicked(QAbstractButton*)));
}
void SyncCenter::buttonInGroupClicked(QAbstractButton *b) {
// do anything with your button
}
void SyncCenter::on_btnAddFolderPair_clicked()
{
static int count{};
QPushButton* button = new QPushButton(QString("test %1").arg(++count), this);
button->setVisible(true);
buttonGroup->addButton(button);
ui->scrLayFolderPairSelect->layout()->addWidget(button);
}

Related

How create method for get the child (QWebEngineView) of th curent view tab (QTabWidget)?

I'm trying to make some project with QTabWidget (a litle browser and a text editor with multiple tab like notepad++) but I'm stuck in 2 project when I try to edit a value of widget (QWebEngine or QTextEdit) inside of QTabWidget. This is the code for the litle browser project:
fp.h :
#ifndef FP_H
#define FP_H
#include <QMainWindow>
#include <QWebEngineView>
#include <QWidget>
#include <QLabel>
QT_BEGIN_NAMESPACE
namespace Ui { class fp; }
QT_END_NAMESPACE
class fp : public QMainWindow
{
Q_OBJECT
public:
fp(QWidget *parent = nullptr);
~fp();
public slots:
void newtab();
void deltab();
void newpage();
QWebEngineView *ap();
int cui();
private:
Ui::fp *ui;
QWebEngineView *webnav;
};
#endif // FP_H
fp.cpp
#include "fp.h"
#include "ui_fp.h"
fp::fp(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::fp)
{
ui->setupUi(this);
ui->in_url->setText("https://google.com");
}
fp::~fp()
{
delete ui;
}
void fp::newtab()
{
QWebEngineView *webnav = new QWebEngineView;
webnav->load(QUrl("https://google.com"));
webnav->setObjectName("webnav");
ui->in_url->setText("https://google.com");
ui->onglet->addTab(webnav,"Home");
}
int fp::cui()
{
return ui->onglet->currentIndex();
}
QWebEngineView *fp::ap()
{
return ui->onglet->currentWidget()->findChild<QWebEngineView *>("webnav");
}
void fp::deltab()
{
ui->onglet->removeTab(cui());
}
void fp::newpage()
{
QString use_url = ui->in_url->text();
ap()->load(use_url);
}
and there what look like the .ui
Image of .ui in Qdesigner
I try to make work the method "ap" it should return the QWebEngineView child of current viewed tab but when I call the slots "newpage" (it currently using "ap" method) it just crash the application. This slot is trigered when I enter a new URL in the QLineEdit in .ui
(before that I create a new tab whit a QWebEngineView inside whit the slot "newtab" )
void fp::newtab()
{
QWebEngineView *webnav = new QWebEngineView;
webnav->load(QUrl("https://google.com"));
webnav->setObjectName("webnav");
ui->in_url->setText("https://google.com");
ui->onglet->addTab(webnav,"Home");
}
So what wrong whit that line it the good method for get child widget of a QTabWidget? If it the good method what should be modified to make it work?
QWebEngineView *fp::ap()
{
return ui->onglet->currentWidget()->findChild<QWebEngineView *>("webnav");
}
Try something like:
QWebEngineView *view = qobject_cast<QWebEngineView *>(
this->ui->onglet->widget(0)
);
Note to put above somewhere in fp's methods (where you need the reference).
I could use ui->onglet->currentWidget(), but the difference is, that will not work once you have multiple tabs.

Get clicked item from QListWidget and QTableWidget

I need to read an item from both QTableWidged and Q ListWidget as the user clicks on them.
For QListWidget I tried the solution described here, however the used SIGNAL itemClicked never seems to trigger.
For QTableWidget I tried multiple solutions, however they either didn't work or weren't what I need. Is there a simple solution for QTableWidget and am I just overlooking something with the solution provided for QListWidget?
Edit:
My Constructor of my MainWindow.cpp looks like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->TableWidget->verticalHeader()->setVisible(true);
QTableWidget* table = ui->TableWidget;
connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem*)),
this, SLOT(showBuchungsDetails(parseListWidgetBuchung(QListWidgetItem*))));
QHeaderView *header = qobject_cast<QTableView *>(table)->horizontalHeader();
connect(header, &QHeaderView::sectionClicked, [this](int logicalIndex){
QString text = ui->TableWidget->horizontalHeaderItem(logicalIndex)->text();
ui->lnBuchungsnummer->setText(text);
});
}
And here is my header file for MainWindow:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMessageBox>
#include <QFileDialog>
#include <QListWidget>
#include "TravelAgency.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_actionDatei_einlesen_triggered();
void on_actionProgramm_beenden_triggered();
void on_actionBuchungen_anzeigen_triggered();
Customer* parseListWidgetBuchung(QListWidgetItem* item);
Customer* parseTableWidgetBuchung(QString item);
void showBuchungsDetails(Customer* c);
private:
Ui::MainWindow *ui;
TravelAgency travelagency{};
bool inputReady = false;
QStringList m_TableHeader;
};
#endif // MAINWINDOW_H
Edit 2:
I am using Qt Creator 4.8.2
Do not use the SLOT/SIGNAL syntax for C++ signals and slots. This is error prone, since mistakes like this are not caught during compilation. Your code compiles fine but doesn't work.
Use Qt5 connect syntax. In this case, you can use a lambda:
connect(ui->listWidget, &QListWidget::itemClicked, this, [this](QListWidgetItem* item)
{
showBuchungsDetails(parseListWidgetBuchung(item));
});
The connect call is wrong. If you use the SIGNAL - SLOT syntax, the slot must be a single function (it's a "reference" to the function).
You can do something like this:
connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem*)),
this, SLOT(onItemClicked(QListWidgetItem*)));
And the onItemClicked implementation:
void MainWindow::onItemClicked(QListWidgetItem* item)
{
showBuchungsDetails(parseListWidgetBuchung(item));
}
Thanks for the suggestions, but I managed to sidestep the ´connect´ syntax completely by rightclicking on the widgeds in question on my UI window and use the "Go to slot..." functionality to create
void MainWindow::on_listWidget_itemClicked(QListWidgetItem *item)
{
parseListWidgetBuchung(item);
}
void MainWindow::on_TableWidget_cellClicked(int row, int column)
{
parseTableWidgetBuchung(ui->TableWidget->item(row, 0)->text());
}
which then allowed me to get the respective items. Either way, thanks again for the help!

Adding multiple text field in a Qt pushButton element

I am trying to implement a Qt push button and some of its properties are going to change under certain circumstances, such as the size, the color and the text field. While I have implemented most of the functionality, I'm looking to add (probably) another text field at a custom place over the button which is going also to change according to different input. For example:
Desired button layout
In this example, I want to be able to change dynamically both the numeric value and the currency. Is there a way to do it, or something similar?
EDIT: I tried to derive my button class from QPushButton and add some extra text in it, but with no luck. Below you'll find the relevant code:
.h file:
namespace Ui {
class myButton;
}
class myButton : public QPushButton
{
Q_OBJECT
public:
explicit myButton(QWidget *parent = 0);
~myButton();
void setSize(int _xs, int _ys);
void setPosition(int _xp, int _yp);
private:
Ui::Tile *ui;
int xSize = 95;
int ySize = 95;
protected:
void paintEvent(QPaintEvent *);
};
.cpp file
myButton::myButton(QWidget *parent) :
QPushButton(parent),
ui(new Ui::myButton)
{
ui->setupUi(this);
}
myButton::~myButton()
{
}
//Paint event of button
void myButton::paintEvent(QPaintEvent *paint){
QPushButton::paintEvent(paint);
QPainter p(this);
p.save();
p.drawText(QPoint(80,10),"FirstName"); // Simple Text.
p.setPen(Qt::blue); // Changing the color of pen.
p.setFont(QFont("Arial", 50)); // Changing the font.
p.drawText(QPoint(80,20),"MiddleName");
p.drawText(QPoint(80,30),"Lastname");
p.restore();
}
Then I'm calling my new button with something like:
myButton *newBtn2 = new myButton(this);
newBtn2->show();

How to connect in Qt signal and slot in dynamically added buttons to get in slot index of added button?

I have list with pointers QPushButton:
QList<QPushButton*> listButtons;
In this code I am adding dynamically buttons
listButtons.push_back(new QPushButton("Cancel", this));
connect(listButtons.last(), SIGNAL (clicked(listButtons.size)), this, SLOT(handleButton(int))); //this doesn't work
How can I also save index of every button, so I can keep track, what button user clicked, because every button has to cancel specific task.
I use C++98, so I can not use Lambda function
You have to use sender() function in your slot.
like this:
void MainWindow::buttonClicked()
{
QObject *senderObj = sender(); // This will give Sender object
QString senderObjName = senderObj->objectName();
qDebug()<< "Button: " << senderObjName;
}
See a complete example that i made for you:
.cpp file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
for(int i =0; i<10; i++)
{
listButtons.push_back(createNewButton());
connect(listButtons[i], &QPushButton::clicked, this, &MainWindow::buttonClicked);
QString text = listButtons[i]->text();
ui->widget->layout()->addWidget(listButtons[i]);
}
}
MainWindow::~MainWindow()
{
delete ui;
}
QPushButton* MainWindow::createNewButton()
{
QPushButton* button = new QPushButton("ButtonText");
button->setObjectName("ButtonName");
return button;
}
void MainWindow::buttonClicked()
{
QObject *senderObj = sender(); // This will give Sender object
QString senderObjName = senderObj->objectName();
qDebug()<< "Button: " << senderObjName;
}
.h file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QPushButton>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QList<QPushButton*> listButtons;
QPushButton *createNewButton();
void buttonClicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
More info:
sender() function returns a pointer to the object that sent the signal, if called in a slot activated by a signal; otherwise it returns 0. The pointer is valid only during the execution of the slot that calls this function from this object's thread context.
Warning: This function violates the object-oriented principle of modularity. However, getting access to the sender might be useful when many signals are connected to a single slot.
An easy way to do that would be to capture the index (or whatever you want to pass to the slot) in a lambda connected to the signal that then pass it on to the slot.
The general idea being like so:
auto button = new QPushButton("Cancel", this);
auto index = /* code to get what you want to pass */ ;
connect(button, &QPushButton::clicked,
this, [index, this](bool){ this->handleButton(index); });
You can refer to this page https://doc.qt.io/archives/qq/qq16-dynamicqobject.html.
Or looks up the implementation of QWebChannel. It maps Qt signal to the v8 environment, based on the above technology.

C++ GUI QSpinBox's not showing up

My program compiles and displays my push buttons correctly but for some reason its not displaying the QSpinBox's. I'm pretty new at C++ qt GUI so any input would be greatly appreciated. I also checked if the spinners were being overlapped by the buttons but they were not.
//
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtGui>
#include <QPushButton>
#include <QLabel>
#include <QSpinBox>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void clearx();
void equalsx();
void addx();
void subtractx();
void multiplyx();
void dividex();
void firstnumberx();
void secondnumberx();
private:
QLabel *label;
QPushButton *equal;
QPushButton *clear;
QPushButton *equals;
QPushButton *add;
QPushButton *subtract;
QPushButton *multiply;
QPushButton *divide;
QSpinBox *spinner;
QSpinBox *spinner2;
};
#endif // MAINWINDOW_H
//
// mainwindow.cpp
#include "mainwindow.h"
#include <QTCore/QCoreApplication>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
label = new QLabel("0,this");
label -> setGeometry(QRect(QPoint(75,75),QSize(50,200)));
clear = new QPushButton("Clear", this);
clear -> setGeometry(QRect(QPoint(80,300),QSize(50,50)));
connect(clear,SIGNAL(released()),this,SLOT(clearx()));
equal = new QPushButton("Equal", this);
equal -> setGeometry(QRect(QPoint(110,300),QSize(50,50)));
connect(equal,SIGNAL(released()),this,SLOT(equalx()));
add = new QPushButton("Add", this);
add -> setGeometry(QRect(QPoint(140,300),QSize(50,50)));
connect(add,SIGNAL(released()),this,SLOT(addx()));
subtract = new QPushButton("Subtract", this);
subtract -> setGeometry(QRect(QPoint(170,300),QSize(50,50)));
connect(subtract,SIGNAL(released()),this,SLOT(subtractx()));
multiply = new QPushButton("Multiply", this);
multiply -> setGeometry(QRect(QPoint(200,300),QSize(50,50)));
connect(multiply,SIGNAL(released()),this,SLOT(multiplyx()));
divide = new QPushButton("Divide", this);
divide -> setGeometry(QRect(QPoint(230,300),QSize(50,50)));
connect(divide,SIGNAL(released()),this,SLOT(dividex()));
spinner = new QSpinBox;
spinner -> setGeometry(QRect(QPoint(130,150),QSize(50,50)));
connect(divide,SIGNAL(released()),this,SLOT(firstnumberx()));
spinner->setRange(1,10);
spinner2 = new QSpinBox;
spinner2 -> setGeometry(QRect(QPoint(190,150),QSize(50,50)));
connect(divide,SIGNAL(released()),this,SLOT(secondnumberx()));
spinner2->setRange(1,10);
}
void MainWindow::clearx() {}
void MainWindow::equalsx() {}
void MainWindow::addx() {}
void MainWindow::subtractx() {}
void MainWindow::multiplyx() {}
void MainWindow::dividex() {}
void MainWindow::firstnumberx() {}
void MainWindow::secondnumberx() {}
MainWindow::~MainWindow() {}
In order for a widget to display inside another widget, you need to set up a parent-child relationship. The simplest way to do this is to pass a pointer to the parent to the child's constructor as an argument.
For your QSpinBox objects, this is done like so:
spinner = new QSpinBox(this);
spinner2 = new QSpinBox(this);
The rest of your code is very laden with hard-coded geometries. For small GUIs, this might not be a problem but can become a bit of a nightmare to maintain for larger ones. Have you considered using QtDesigner to design your GUI? You might also find the layout management classes helpful in making your GUI designs better maintenance of positions and sizing of child widgets.
Add this as parameter of QSpinBox constructor call.