Parents and setters - c++

This is a general question, which I will illustrate with two examples.
First example: Suppose I have a widget and a Layout associated with it:
QWidget myWidget;
QVBoxLayout* mainLayout = new QVBoxLayout(&myWidget);
Note that I have specified my widget as the parent of my layout. Question is, is this sufficient to associate the layout with my widget or do I have to additionally explicitly set the layout as in:
myWidget.setLayout(mainLayout);
If I do have to explicitly set the layout, then AFAIK setting will also make myWidget the parent of my layout, so what is the point of specifying the parent in the constructor?
Second example:
Suppose I have a line edit and a validator for it.
QLineEdit* lineEdit = new QLineEdit(whatever);
QIntValidator* validator = new QIntValidator(0, 100, lineEdit);
Note that I have set the parent for my validator. In order for the validator to "work", i.e. listen to lineEdit's value change events, do I also have to explicitly set the validator as in
lineEdit->setValidator(validator);
If so, will the setValidator function itself set the parent, and if so, why should I bother specifying the parent in the Validator's constructor?
Hopefully my questions are clear.

WRT the first question:
I think QWidget::setLayout() function call and setting the parent widget to a layout are equivalent operations. This pretty much fits to the Qt documentation, that states for QWidget::setLayout():
An alternative to calling this function is to pass this widget to the layout's constructor.
Thus, you do need to call QWidget::setLayout() if the parent widget is already set for the layout.
WRT the second question:
When you construct the validator with line edit as a parent you do not set validator to the line edit - this is just defines the QObjects hierarchy and objects ownership. I think you do not need to mix hierarchical and functional concepts here.

Related

Replace a widget in Qt

I have a base class which has some gui items that i have set positions of using the designer in Qt creator. Those items are:
QWidget* w1;
QWidget* w2;
QWidget* w3;
Now in a class that inherits that base class, I would like to "transform" those widgets into lineEdit items, that would keep all the geometrical parameters of that widgets. So I do something like this:
QLineEdit* leAmplitude;
leAmplitude = new QLineEdit(ui->w1);
leAmplitude->setGeometry(ui->w1->geometry());
ui->glControls->addWidget(leAmplitude);
But the added QLineEdit item doesn't appear in the exact same place as w1 item. Its just added at the bottom of other controls in the QGridLayout glControls. How to make the lineEdit to take all geometric parameters from w1?
Layout takes care of the widgets placed in the layout, according to the hints given by the widget, so calling setGeometry, then doing addLayout is not useful. Also, adding widget to layout resets it parent, so you setting new widget's parent to ui->w1 is not useful either.
Fortunately, there is QLayout::replaceWidget method! Just use that. Example:
QLineEdit* leAmplitude;
leAmplitude = new QLineEdit;
QLayoutItem *previous = ui->glControls->replaceWidget(ui->w1, leAmplitude);
// possibly assert that previous is ui->w1, or just delete it, or whatever
This method was added as late as in Qt 5.2 it seems, so if you need to support older versions, I can expand this answer to cover how to (try to) do the same manually. But in short, you have to use the right QGridLayout::addWidget overload and make sure relevant properties (including at least sizeHint and sizePolicy) match.
try this, it is works:
QLineEdit* leAmplitude;
leAmplitude = new QLineEdit(ui->w1->parentWidget());
ui->w1->parentWidget()->layout()->replaceWidget(ui->w1, leAmplitude);
ui->w1 = leAmplitude;

How to logically group widgets in QT for easy show/hide?

I'm grouping a set of widgets in a parent and then I control the visibility/flow of these widgets by hiding/showing the parent. Is this a good way to achieve what I'm trying to do? Here is the code:
QVBoxLayout* l = new QVBoxLayout(this);
// .....
QWidget* toolset_frame = new QWidget(this);
{
QVBoxLayout* l = new QVBoxLayout(toolset_frame);
l->addWidget(new QLabel(tr("Stuff")));
this->Toolset = new QLineEdit(toolset_frame);
l->addWidget(this->Toolset);
}
l->addWidget(toolset_frame);
// Call toolset_frame->hide() and this hides everything inside the parent
The problem with this solution is that the children shrink in size slightly, I think this is due to some padding or border in the parent. Ideally the children should appear as if they are not contained in an intermediate object, but rather flow with the parent. In this case the horizontal size of the children should not be affected.
http://doc.qt.io/qt-5/qtwidgets-dialogs-extension-example.html
This example shows that your approach is correct. Using a widget to contain the elements you want to hide, and so on.
If you want the margins/content margins/padding to be less, then change it.
// in finddialog.cpp
extensionLayout->setMargin(0);
To quickly prototype what properties to change to get it to look right, try laying it out in the Qt Designer, and modify the property editor to get the look and feel you want.
Hope that helps.

QT add widgets to UI anywhere

The application that I'm building is supposed to create, destroy, and manipluate widgets that I've created
The problem is I'm not making a simple program with nice buttons where everything is symmetrical and needs to be evenly spaced and handled via a layout that will automatically move everything around and space it.
And yet, the only way I know of is to manually instance a layout and add the widgets to it, but then I can't set the coordinates of them
How can I simply instance my widget, and add it to the project generated frame?
This is how I'm instantiating my class, in which case I then set my own parameters:
Tile *tile = new Tile;
tile->setImg("://Images/placeholderTile.png");
tile->setCol(true);
tile->setGeometry(retX(line),retY(line),50,50);
To reiterate, I want to add my own widgets to a frame outside of the editor (only by code), and be able to manually move them around the frame by code.
I don't see an ".addWidget() as a method accessible from the QFrame, and yet they can be children within the designer, why can't I do this by code? Whenever I try to do it manually and add them to any layout, any attempt I make to manually set the widgets location doesn't do anything. I haven't overridden the setGeometry
I fixed my problem
After 2 hours of continual searching I finally came across my answer
I never thought that you could set the parent of a widget by code, as I thought you strictly had to add it in as a child of something else, not the reverse and declare that it should have a parent
So, by simply adding the line:
tile->setParent(ui->frame);
completely fixed my problem.
I will change this post back and submit the answer tomorrow when I'm allowed to by this site.
Thank you to those who actually came though. I'm just glad I managed to fix it before that.
All you need is to pass the parent to the widget's constructor:
Tile *tile = new Tile(ui->frame); // <-- here
tile->setImg("://Images/placeholderTile.png");
tile->setCol(true);
tile->setGeometry(retX(line),retY(line),50,50);
Since Tile is your own class, you should definitely have a Qt-style, parent-taking explicit constructor for it:
class Tile : public QWidget {
...
public:
explicit Tile(QWidget * parent = 0) : QWidget(parent) { ... }
};
Another approach is to write your own layout that would know about the relationships that are to be held between your objects. After you do it once, writing custom layouts isn't that hard.

Replace QWidget with a new QWidget

This questions to me reeks of maybe a lack of understanding of C++, as the possibilities I've considered for my problem all seem to make no sense on why this could be occuring. Feedback appreciated.
I'm using the form designer to create a form class with a table in it. I'm trying to replace the table with another table generated in a helper class. I'm only doing this so I can (hopefully) maintain the nice grid layout I've designed, and through pointer manipulation, get the replacement I desire. Here's some code snippets from the table form constructor and relevant calls :
//tableData is defined in the header file as a QTableWidget*
tableData = this->findChild<QTableWidget *>("tableData");
....
setup();
void setup(){
tableData = Utilities::createTable(this->file, tableDelim);
//createTable returns QTableWidget*
... other assignments, and label text updates, which seem to all work
}
My understanding is that tableData is a pointer, and if printed, will give the address of the QTableWidget from the layout. So then if I create a QTableWidget* and then assign tableData to that, tableData should now point to the new widget. Instead, I see only a blank screen.
I tried checking what the tableData pointer is before I assign it to the new QTableWidget*, and after. The second pointer shown is what is generated by createTable() :
QTableWidget(0x101272d40, name = "tableData") QTableWidget(0x10127b3b0, name = "test_sample2.nuc.stats")
QTableWidget(0x10127b3b0, name = "test_sample2.nuc.stats") QTableWidget(0x10127b3b0, name = "test_sample2.nuc.stats")
It seems the pointer is being reassigned, but the table drawn isn't the right one.
What gives?
My understanding is that you want to design the table layout in designer but fill in the data from an external source.
I would suggest, to just use the QTableWidget that is created in setupUi() and modify Utilities::createTable() such that it becomes Utilities::populateTable(QTableWidget & table, <all the other parameters you need>). (Or use QTableWidget * if you prefer - however I like putting the non-zero assertion responsibility on the caller...)
Apart from that, I agree with Sebastian Lange.
You are right with your assumption. You do set a variable to be a pointer to a object and next you set the variable to be a pointer to another object. You never change any objects, just your variable which is not used to display anything.
You would need to do something like:
//tableData is defined in the header file as a QTableWidget*
tableData = this->findChild<QTableWidget *>("tableData");
parentLayout = tableData->parent()->layout(); //Get the parent widget to add another table.
parentLayout->removeWidget(tableData);
delete tableData;
parentLayout->addWidget(createTable());
You need to use pTheContainerOfTheOriginalTableWidget->addWidget(tableData); See here: http://qt-project.org/forums/viewthread/16547
Be sure you remove the original tableWidget so you don't have two (I assume you don't want two).
If I understand you correctly we have such situation.
call of setupUi (which generated by qt tootls),
there there is something like this(pseudo code):
oldTablePtr = new QTableWidget(parent);
someLayout->addWidget(oldTablePtr);
So parent and layout hold value of oldTablePtr.
And if you set variable oldTablePtr nothing changed.
parent send QPaintEvent to oldTablePtr.
So you need call delete oldTablePtr, that remove this widget from list of childs of parent, and move newTablePtr to the same layout.
There's no need to replace it in code, you can do it in Qt Designer. Just place QTableWidget on form, then rightclick it and choose Promote widget in menu, then you will need just enter your classname.
Currently I don't have Qt Designer near me, so edits will be appreciated.

Remove QLayoutItem from a Layout, but still use it later on?

Here is the environment first:
I have a self defined "Property Editor" which is a QGroupBox (derives from QWidget)
Currently I have a class let's call it "Holder", which has a reference to two of the Property Editors.
Now I have multiple "Holder" classes and one vertical QVBoxLayout (called Sidebar).
In this layout I want both of the Property Editors of the currently selected Holder class to be displayed.
And there is the issue:
When the user selects another holder class, I want the Property Editors of the previously selected Holder class to disappear, and add the Property Editors of the new selected Holder class.
Selecting another Holder class works once. But when I select the first Holder class again, The editors don't seem to change. Why? Does "takeAt(..)" destroy the reference in the holder class? How can I get the desired behavior?
Here is the code, thanks in advance:
void App::setSelection(Holder * holder){
if(m_currentlySelected == holder) return;
m_viewer->sideBarRemoveAt(0);
m_viewer->sideBarInsertAt(0, holder->firstPropEditor);
m_viewer->sideBarRemoveAt(1);
m_viewer->sideBarInsertAt(1, holder->secondPropEditor);
m_currentlySelected = holder;
}
void QtViewer::sideBarRemoveAt(int i){
m_sideBar->m_layout->takeAt(i);
}
void QtViewer::sideBarInsertAt(int i, QWidget * widget){
m_sideBar->m_layout->insertWidget(i, widget);
}
QLayout::takeAt() doesn't remove the widget of the QLayoutItem from its parent widget. The only reason it may seem to work the first time is probably because the other widgets were above (z-index wise) the first ones.
Rather than playing with the layout, you could
just hide/show your 2 PropertyEditor whenever the holder changes, hidden items don't generate holes in the layout, the next visible item is displayed as if the hidden items were not part of the layout, or
use a QStackedWidget to stack all the PropertyEditor at the same place and select which one is displayed (with QStackedWidget::setCurrentIndex()).
Does "takeAt(..)" destroy the reference in the holder class?
No, this method removes the QLayoutItem from the layout. See reference page for takeAt. This class doesn't release the layout item (it is your responsibility to do).
But when I select the first Holder class again, The editors don't seem
to change. Why?
I am not quite clear what you are trying to achieve (not enough code in your example), but if you are trying to change the layout using QLayoutItem's, then it is the simplest to create new layout and add items you want to display to it. Or simply, remove all items from the layout, and add the items that should be visible.