Retrieving QPushButton from QWidget - c++

I have a QPushButton with related icon, and I add it to a scene:
QPushButton *button = new QPushButton;
button->setIcon(QIcon(myIcon));
buttonWidget = detail->scene()->addWidget(button);
// buttonWidget is declared globally and is a QGraphicsProxyWidget*
Later on, in a different function, the buttonWidget is still accessible. How can I retrieve the QPushButton object from the buttonWidget? I would like to change its icon.

Perhaps you could use QGraphicsProxyWidget::widget to get the underlying QWidget* and then use dynamic_cast to cast to QPushButton*:
QPushbutton *otherButton = dynamic_cast<QPushButton*>(buttonWidget->widget());

You can retrieve the object as a QWidget using QGraphicsProxyWidget::widget and then casting to a QPushButton.
But I'd recommand to make the QPushButton be an attribute of your class (always better that casting). Then you access it later whenevr you want.
button = new QPushButton; // declare button as an attribute of your class in the header file
button->setIcon(QIcon(myIcon));
buttonWidget=detail->scene()->addWidget(button);

Related

Accessing widgets inside QStackedWidget

I am developing a Qt app with QtDesigner.
Previously it was quite easy to access specific widgets to do something with them like connecting signals. After I added QStackedWidget I can no longer access specific widgets with something like ui->stack->page1->widget.
Is there a way to do it somehow? Or should I always call findChild method? Or maybe it is possible to at least assign some of the nested widgets in stack widget to properties of the main windwo class?
QStackedWidget provides a method to get child widgets by index, as well as the current widget.
A quick example is as follows:
MOCed Header
class MyWidget: QWidget
{
Q_OBJECT
public:
using QWidget::QWidget
QWidget *ptr;
};
Source File
QStackedWidget *stackedWidget = new QStackedWidget;
stackedWidget->addWidget(new MyWidget); // index 0
stackedWidget->addWidget(new QWidget); // index 1
stackedWidget->addWidget(new MyWidget); // index 2
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(stackedWidget);
setLayout(layout);
// do something specific with the first widget's ptr element
auto* widget = stackedWidget->widget(0);
auto* mywidget = qobject_cast<MyWidget*>(widget);
if (mywidget) {
mywidget->ptr->setObjectName("FirstPage");
}
Now, Qt uses virtual interfaces by default, so if you have a custom subwidget you need to extract, you can use qobject_cast. qobject_cast is basically a fast dynamic_cast, and works even without RTTI. In template-driven code, dynamic_cast is a bit of a code-smell: it means you lost useful type information too early. With virtual interfaces, the exact opposite is true: you should use qobject_cast as needed.
Why you get the widget layer by layer, if your widgets are added in Qt designer, you can get it by ui->widget directly.

How to make a QVBoxLayout available globally

I am trying to make a QVBoxLayout available globally in Qt. I cannot access it from any other functions.
void MainWindow::on_actionF451_triggered()
{
QVBoxLayout *compLay = new QVBoxLayout(ui->scrollArea);
I have created it in this function then below have added tons of widgets, but need to be able to clear these widgets in another function.
Thanks
If you want a variable compLay to be accessed anywhere in your MainWindow class, then define it in mainwindow.h's definition of MainWindow class (after Q_OBJECT macro) like this: QVBoxLayout *compLay;
and you can change compLay within MainWindow class however you like:
void MainWindow::on_actionF451_triggered(){
compLay = new QVBoxLayout(ui->scrollArea);
or you can make return type of void MainWindow::on_actionF451_triggered() not void, but QVBoxLayout * and return your layout directly (but you will need to correct the connect statement then (i assume, that you don't have one and it is autoconnected) and connect it to slot, where you will need this layout to clean or modify or remember )

New window doesn't show

I have a button, when it is clicked a new window show with a QLineEdit, and a QLabel on it, my connection between the button and the function works fine, but the new window doesn't show.
void windowManager::addQuestionDialog(){
QWidget window(&parent);
QLineEdit question;
QLabel label;
QVBoxLayout layout;
layout.addWidget(&question);
layout.addWidget(&label);
window.setLayout(&layout);
window.resize(200,200);
window.setWindowTitle(QObject::trUtf8("Kérdés bevitele..."));
window.show();
}
You need to create class tag variables for the new window and the stuff you want to put into it, than create the objects themselves with the new keyword in the function, because if you create all of these simply in a function, than they will created in the stack, and you should know that after a function returns/finishes, the stack to that function is deleted (with your new window and the stuff on it too).
Include the headers for the classes you want to use in your windowManager header file:
#include <QDialog>
#include <QLineEdit>
#include <QLabel>
#include <QVBoxLayout>
Then add the tag variables to the private part:
private:
QDialog *window;
QLineEdit *question;
QLabel *label;
QVBoxLayout *layout;
In your button's click event set the tag variables, and create the UI setup:
void windowManager::addQuestionDialog()
{
window = new QDialog();
question = new QLineEdit();
label = new QLabel();
layout = new QVBoxLayout();
layout->addWidget(question);
layout->addWidget(label);
window->setLayout(layout);
window->resize(200,200);
window->setWindowTitle(QObject::trUtf8("Kérdés bevitele..."));
window->show();
}
Also don't forget that you should use -> instead of . for calling functions here, because these tag variables are pointers. Also that's the reason why you don't need to use the & operator to get their address.
Also keep in mind that you should delete these objects, because C++ doesn't delete these automatically for you. You should delete everything you new. A good place to do this is in the destructor in your windowManager class. Just check if the tag variables are not NULL (if there is an object) before you try to delete them, otherwise errors may occur.
A better solution is to pass a parent pointer as the constructor's parameter, so this way Qt will delete them as they are closed, because if the parent is destroyed, the children will be destroyed too.
As an extra, you don't have to set manually where does the objects are go, because Qt will now it from the hierarchy (in some cases).
In this case your button's click event function would look like this:
void windowManager::addQuestionDialog()
{
window = new QDialog(this);
question = new QLineEdit(window);
label = new QLabel(window);
layout = new QVBoxLayout(window);
//The following two lines are optional, but if you don't add them, the dialog will look different.
layout->addWidget(question);
layout->addWidget(label);
window->resize(200,200);
window->setWindowTitle(QObject::trUtf8("Kérdés bevitele..."));
window->show();
}
You create the window QWidget object on the stack. Therefore, this object will be deleted when the call to the function addQuestionDialog finishes. Change the code to create the new window widget using "new", and arrange it to be deleted after it was closed. Some possible solutions are presented here:
destructors in Qt4

Multiple QWidets into one QMainWindow

I am currently learing Qt, and I am stuck on the problem of using multiple QWidgets and one QMainWindow.
I have setup a project which contains 2 QWidgets and one QMainWindow. This is my idea of using it: design both QWidgets as needed, add them to the mainwindow object, connect the buttons to the correct slots and switch the centerwidget when needed. So I started off with one QMainWindow and then added two QWidgets, including the cpp file, the h file and the ui file. On both QWidgets I added one QPushButton, and called it pushButtonConvert.
Then I went to the cpp file attached to the QMainWindow (mainwindow.cpp) and did the following:
EpochToHuman * epochToHuman = new EpochToHuman();
HumanToEpoch * humanToEpoch = new HumanToEpoch();
Up until this point everything is fine. Now I want to connect the buttons to slots in the mainwindow object, but I can not find the buttons. epochToHuman->pushButtonConvert does not seem to exist, and I can not find any other way to get to the buttons. So am I thinking in a way that is not correct, according to Qt or am I missing something?
Another try at clarifying what I want:
I want to use the elements in a QWidget in QMainWindows' cpp file. I want to be able to do things like this:
//In object MainWindow.cpp
QWidget * a = new QWidget
//Let's say a is a custom widget with a label in it. This label is called Label
a->Label->setText("Hello, World!");
//This gives an error because a does not have a member called Label
//How can I change the text on the label of a?
//And I think if I will be able to change the text of this label, I will also be able to dance around with buttons as needed.
You can connect the pushButtonConvert button to MainWindow::convertFromEpochToHuman in the constructor of MainWindow, with:
connect(epochToHuman->ui->pushButtonConvert, SIGNAL(clicked(bool)), this, SLOT(convertFromEpochToHuman()));
You'll need to make the ui member public first, like you have done for HumanToEpoch.
You should move the declaration of your widgets into MainWindow.h:
// ...
private:
Ui::MainWindow *ui;
EpochToHuman * epochToHuman;
HumanToEpoch * humanToEpoch;
// ...
and initialise them like this:
epochToHuman = new EpochToHuman(this);
humanToEpoch = new HumanToEpoch(this);

Why do we pass "this" pointer to setupUi function?

I'm fairly new in QT. Taking below fairly simply explain from qt docs :
class CalculatorForm : public QWidget
{
Q_OBJECT
public:
CalculatorForm(QWidget *parent = 0);
private slots:
void on_inputSpinBox1_valueChanged(int value); //why is that slots are private?
private:
Ui::CalculatorForm ui;
};
and implementation of constructor
CalculatorForm::CalculatorForm(QWidget *parent)
: QWidget(parent) {
ui.setupUi(this); // <-- Question below
}
Q: I was wondering why do we pass this pointer to setupUi function?, what does it do ?
So that the dialog will have the caller as parent, so that eg when the parent is closed the dialog can be closed automatically. Generally all gui elements have a pointer to their parent.
private slots:
void on_inputSpinBox1_valueChanged(int value); //why is that slots are private?
These are auto generated slots which exactly match the naming of the gui elments in QtDesigner. They are only meant to do the direct hookup to those gui elements and so should be dealt with in this class. If these signals were extended to other classes then any change in the gui would require changing a lot of other code which doesn't need to know details of the gui.
In the handler slot for the specific gui element you can then emit another more general signal to the rest of the app.
The only widget that setupUi doesn't create is the widget at the top of the hierarchy in the ui file, and as the Ui::CalculatorForm class instance doesn't know the widget it has to fill, it (this) has to be passed explicitly to the class at some point.
this or any other widget you would pass to it, is used as the parent to all other subwidgets. For example, you could fill a widget without inheritance like this:
QWidget *widget = new QWidget;
Ui::CalculatorForm *ui = new Ui::CalculatorForm;
ui->setupUi(widget);
widget->show();
But really, it would be easier to understand if you read the content of the uic generated file (probably named ui_calculatorform.h).
setupUi creates the instances of widgets (QLabel, QTextEdit and so on). The [user interface compiler] (http://qt-project.org/doc/qt-4.8/uic.html) gets information for you from the .UI form and generates widget-creation code in the generated moc source files.
The manual way of creating widgets without using the Qt Designer or a UI file would be like so:
QWidget* pWidget = new QWidget(this);
I think it is to add the caller widget to the layout of this UI.
This widget will be the toplevel widget.
Martin Beckett answer might be correct as well, as what he described is a common behavior in Qt (cf the 'parent' argument in most of widget's derived class constructor)
Note that you have alternative ways how designer can auto-generate code.
In this case you have a separate 'UI' class for this code which is not QObject so it also is not a QWidget.
Auto generated code needs information about parent widget and to make auto-conections of slots and signals so this is why you have to pass this.
This pater is less intrusive then other pasterns (that is why it is default). You can also try alternative patters (check Qt Creator Designer options), but I recommend you to see what is generated by designer tools in default settings.