I am trying to create a gui that has a QTabWidget with multiple tabs. As a preliminary test I have created one that has two tabs using the same layouts. The first tab (Page 1) is blank but then the second one shows the QTableView I created. Both tabs were created the exact same way, yet they perform differently. Anyone know why the first one is blank...?
I noticed that if I comment out the line int8Window->setLayout(_layout);. Page 1 shows up with the proper layout and the second tab is blank this time...
Here is the code for the gui:
// Main window and layout
QWidget* mainWindow = new QWidget;
QVBoxLayout *mainLayout = new QVBoxLayout;
// Tab widget
QTabWidget* tabWidget = new QTabWidget;
// The pages in the tab widget
QWidget* uInt8Window = new QWidget;
uInt8Window->setWindowTitle(QString("Page 1"));
QWidget* int8Window = new QWidget;
int8Window->setWindowTitle(QString("Page 2"));
QTableView* tableView = new QTableView;
QStandardItemModel* model = new QStandardItemModel(5, 5);
for (int row = 0; row < 5; ++row) {
model->setItem(row, 0, new QStandardItem("3"));
model->setItem(row, 1, new QStandardItem(5));
model->setItem(row, 2, new QStandardItem(2));
model->setItem(row, 3, new QStandardItem(1));
model->setItem(row, 4, new QStandardItem(5));
}
tableView->setModel(model);
// Setting the tab page layouts
_layout = new QVBoxLayout;
_layout->addWidget(tableView);
uInt8Window->setLayout(_layout);
int8Window->setLayout(_layout);
// Add the pages to the tab widget
tabWidget->addTab(uInt8Window, "Page 1");
tabWidget->addTab(int8Window, "Page 2");
// Add the tab widget to the main layout and show
mainLayout->addWidget(tabWidget);
mainWindow->setLayout(mainLayout);
mainWindow->show();
Read logs! I'm sure you have a respective warning.
You are assigning same layout to two different widgets. Once layout is assigned to a widget, it is owned by this widget forever.
You need create separate layout for each widget.
I recommend to split this onto couple methods. One is creating a widget for a page (you can use this couple times). Other creating a data model, and other composing tab widget.
Please remember also about memory management! Best approach is to set parent during construction (as parameter of constructor). You have a leak in data model.
Related
I am adding several push buttons in QVBoxLayout and adding this layout in QDockWidget, now I want to add some scroll bar in the same Layout(DockWidget) to change(Increase/Decrease) data shown in separate EditBox.
The problem is the Scroll bar is added above the buttons,I want all the buttons at right side and the scroll bar in the left side.
The reason why I am using QDockWidget is I am also adding animation effect using QPropertyAnimation class.
Below is the sample code
QDockWidget dock(QLatin1String("Last filters"));
QWidget* multiWidget = new QWidget();
QVBoxLayout* layout = new QVBoxLayout();
QPushButton* filter1 = new QPushButton(QLatin1String("Filter number 1"));
QPushButton* filter2 = new QPushButton(QLatin1String("Filter number 2"));
QPushButton* filter3 = new QPushButton(QLatin1String("Filter number 3"));
QPushButton* filter4 = new QPushButton(QLatin1String("Filter number 4"));
QPushButton* filter5 = new QPushButton(QLatin1String("Filter number 5"));
QLabel* label = new QLabel(QLatin1String("QPushButtons"));
layout->addWidget(filter1);
layout->addWidget(filter2);
layout->addWidget(filter3);
layout->addWidget(filter4);
layout->addWidget(filter5);
layout->addWidget(label);
multiWidget->setLayout(layout);
dock.setWidget(multiWidget);
** For animation
QPropertyAnimation *animation = new QPropertyAnimation(dockMenu, "geometry");
animation->setDuration(250);
QRect startRect(-100,20,100,80);
QRect endRect(20,20,100,80);
dockMenu->show();
animation->setStartValue(startRect);
animation->setEndValue(endRect);
animation->start();
For this application this answer at Multiple widgets on a QDockWidget
helped me but now to add the scrollbar I am not able to relocate/move the widgets
Your code sample does not reflect the attached screenshot so I'm not 100% sure if this is what you're looking, but...
If you swap the QVBoxLayout for a QGridLayout, you can place the scrollbar in the first column and set it to expand over the max number of rows. The buttons can be placed in the second column.
QGridLayout documentation: https://doc.qt.io/archives/qt-4.8/qgridlayout.html#details
And some basic layout examples from Qt which may give you some ideas: https://doc.qt.io/qt-5/qtwidgets-layouts-basiclayouts-example.html
Qt has a layout system kind of separate from the widgets, which is causing me problems. I know that when I have a QSplitter, I can add stuff to it just by doing
new SomeCustomQWidget(splitter);
However, if I have a QWidget, shouldn't I be able to do something similar? The Qt web pages only explain how to add layoutItems to layouts, only connecting them to widgets by inheriting both in a subclass. So, right no I am doing something like this
item = new QWidget(parentsplitter); //this works
subitem = new customWidget(item); //this too
subitem2 = new QSlider(item); //this isn't laid out with subitem
However this results in the slider being on top of the subitem instead of being next to it (either right or below I'd expect). Calling
//either
new QVBoxLayout(item);
//or
item->setLayout(new QVBoxLayout());
//or
item->setLayout(new QVBoxLayout(item));
after initializing the item doesn't help. (My logic is that the item takes the layout as it's child and will add subsequent children to it).
I went around this problem earlier but now I'm facing the exact same problem when making a widget inside a QGraphicsView. I feel like it shouldn't be necessary to make a custom subclass of every item I could ever want to dynamically add to those widgets, like the Qt website seems to expect. (If I simply want to add some text and a couple buttons, do I really need to make 2 custom subclasses?) I am using a .ui file for basic layout but these items need to be created dynamically.
Solution
Forgot to call item->show(); on the widget used for layout.
Turns out, I had tested otherwise correct code, but since some of my subclasses called show() but others didn't, it just seemed broken to me.
This however isn't the issue with the graphicswidgets, as graphicslayout can only be used to add graphicslayoutitems, which don't include for example graphicstextitems.
Solution 2
Using the QGraphicsProxyWidget instead of QGraphicsWidget fixed the issue!
QWidget *parent = new QWidget();
QVBoxLayout *rootLayout = new QVBoxLayout(parent);
QHBoxLayout *subLayout1 = new QHBoxLayout();
QHBoxLayout *subLayout2 = new QHBoxLayout();
rootLayout->addLayout(subLayout1);
rootLayout->addLayout(subLayout2);
subLayout1->addWidget(new QLabel("Foo"));
subLayout1->addWidget(new QLabel("Bar"));
subLayout2->addWidget(new QPushButton("Foo button"));
subLayout2->addWidget(new QPushButton("Bar button"));
parent->show();
Note once layout is assigned to a widget by setLayout or when widger is passed to constructor fo layout, the are in 1 to 1 relationship which last until widget or layout is destroyed. See documentation:
If there already is a layout manager installed on this widget, QWidget won't let you install another. You must first delete the existing layout manager (returned by layout()) before you can call setLayout() with the new layout.
That is why subitem2 = new QSlider(item) didn't work for you.
I think you're confusing parent/child relationship with managing layout.
Lets start with layouts. When set on a widget the layout becomes a child of that widget. When constructed with a widget parent the widget becomes a parent and the layout is set on the widget. When adding widgets to layout they become children of the widget that layout is set on, not the layout itself. Setting a parent on a widget or constructing widget with a parent does not automatically add the widget to its parent layout. Deleting a layout does not delete widgets governed by it, because they are not its children.
To summarize - layouting and parenting are two distinct mechanisms. first is for governing position and size of a widget, the other is for object hierarchy and resource management (parents delete their children). Layouts have a convenience constructor though, that does both - sets a parent of the layout and sets that layout on the parent widget.
In code:
item = new QWidget()
layout = new QVBoxLayout(item);
is equivalent to:
item = new QWidget();
layout = new QVBoxLayout();
layout->setParent(item);
item->setLayout(layout);
This code adds a child to a layout and item becomes its parent:
item = new QWidget();
layout = new QVBoxLayout(item);
child = new QWidget();
layout->addWidget(child);
This adds a child to a parent item, but does not add child to a layout:
item = new QWidget();
layout = new QVBoxLayout(item);
child = new QWidget(item);
A QSplitter is special in that it is a widget and it does its own layout of children i.e. when you use a splitter as a parent of a widget that widget's geometry is governed by the splitter. Other widgets don't behave like that.
So to comment your code:
item = new QWidget(parentsplitter); //this works because splitter does layout
subitem = new customWidget(item); //item is not splitter so subitem geometry is not managed
subitem2 = new QSlider(item); //item is not splitter so subitem2 geometry is not managed
If you want to lay out subitems in the item then item either has to be a splitter too or have a layout that will manage children, so:
childsplitter = new QWidget(parentsplitter); //note that parentsplitter has only 1 item
subitem = new customWidget(childsplitter);
subitem2 = new customWidget(childsplitter);
or
widget = new QWidget(parentsplitter); //note that parentsplitter has only 1 item
lay = new QHBoxLayout(widget);
subItem = new customWidget();
subItem2 = new customWidget();
lay->addWidget(subItem);
lay->addWidget(subItem2);
If you want parent splitter to have two items then you do:
item = new customWidget(parentsplitter);
item2 = new customWidget(parentsplitter);
I'm trying to create a custom widget in QT that looks something like this:
The red squares will be displaying an image/icon.
How can I achieve this layout through coding the widget? I will need to create many of those widgets with the same layout but different values in their labels. Ideally, I will display those widgets as a list with a scrollbar in my mainwindow. But right now I'm struggling to just create the layout for those widgets through code. Any help is much appreciated.
You need to split you design on to separate segments. Each segment can be either a separate sub layout or a widget. In you example, I see the following segments:
Large red icon,
Two labels: TextLabel and 06-November-2014...
Two labels make a vertical box layout,
Vertical box layout and large red icon make a horizontal box layout,
Small red rectangle makes a separate layout,
All layouts make a main layout.
Now lets code this composition:
QLabel *largeRed = new QLabel(this); // Should set an image for this label
QLabel *lbl1 = new QLabel("06-November-2014...", this);
QLabel *lbl2 = new QLabel("TextLabel", this);
QVBoxLayout *vLayout = new QVBoxLayout;
vLayout->addWidget(lbl1);
vLayout->addWidget(lbl2);
vLayout->addStretch();
QHBoxLayout *hLayout = new QHBoxLayout;
hLayout->addWidget(largeRed);
hLayout->addLayout(vLayout);
QLabel *smallRed = new QLabel(this); // Should set an image for this label
QHBoxLayout *hLayout2 = new QHBoxLayout;
hLayout2->addWidget(smallRed, 0, Qt::AlignRight);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(hLayout);
mainLayout->addLayout(hLayout2);
[..]
Use this.
QPixmap big(75,65);
big.fill(Qt::red);
QPixmap small(25,15);
QVBoxLayout *box = new QVBoxLayout;
QWidget *window = new QWidget;
QLabel *bigLab = new QLabel;
QLabel *smallLab = new QLabel;
QLabel *textLab = new QLabel("Two");
bigLab->setPixmap(big);
smallLab->setPixmap(small);
QHBoxLayout *hLay = new QHBoxLayout;
hLay->addWidget(bigLab);
hLay->addWidget(textLab);
QHBoxLayout *vLay = new QHBoxLayout;
vLay->addWidget(smallLab,0,Qt::AlignRight);
box->addLayout(hLay);
box->addLayout(vLay);
window->setLayout(box);
window->show();
Result:
What I normally do is:
Design the layout with Qt Designer/Creator using rich features of layouting.
Set its instances (from code) as the item widget of a list or table widget.
Be careful, If the item count of the list is too large, it will perform very slowly.
P.S. If you really need the layout coded, just use the code generated by Qt designer.
I would like to add two widgets (say QPushButton) to the status bar, one to the left and other to the right side.
I am thinking of adding horizontal spacer in between the two widgets, but don't know how to add.
PS: I tried using addWidget() to add to the left and addPermanentWidget() to add to the right but it doesn't look neat and also it doesn't feel right.
You can add two buttons to a layout in a widget and add the widget to the status bar using QStatusBar::addWidget :
QWidget * widget = new QWidget();
QPushButton * leftBut = new QPushButton("Left");
QPushButton * rightBut = new QPushButton("Right");
QGridLayout * layout = new QGridLayout(widget);
layout->addWidget(leftBut,0,0,1,1,Qt::AlignVCenter | Qt::AlignLeft);
layout->addWidget(rightBut,0,1,1,1,Qt::AlignVCenter | Qt::AlignRight);
ui->statusBar->addWidget(widget,1);
I am thinking of adding horizontal spacer in between the two widgets, but don't know how to add.
Here is a way to use a "fake" spacer.
QPushButton *leftButton = new QPushButton("Left");
QPushButton *rightButton = new QPushButton("Right");
QLabel *spacer = new QLabel(); // fake spacer
ui->statusBar->addPermanentWidget(leftButton);
ui->statusBar->addPermanentWidget(spacer, 1);
ui->statusBar->addPermanentWidget(rightButton);
The second parameter in addPermanentWidget is "used to compute a suitable size for the given widget as the status bar grows and shrinks".
Demo:
I think the simplest way is using a QGridLayout (honestly I never tried to modify a status bar anyway) supposing that the status bar is or descends from widget you can do this:
QGridLayout *myGridLayout = new QGridLayout();
statusbar->setLayout(myGridLayout)
QPushButton *button1 = new QPushButton(this);
myGridLayout->addWidget(button1,0,0,1,1);
QPushButton *button2 = new QPushButton(this);
myGridLayout->addWidget(button2,X,0,1,1);
The biggest is X the more space you want to leave in between, I would suggest to start with 3 and then make few tests to see how it looks.
I've got a dynamically created list of image extensions that I want to display checkboxes for.
I have a QStringList that contains .png, .jpg, .bmp. This list can be altered by the user so I need to generate a checkbox on the UI for each possibility.
QStringList filters;
filters << "*.jpg" << "*.png" << "*.jpeg";
I was thinking about a foreach or for loop to achieve this.
foreach(QString filt, filters){
QCheckBox *checkbox = new QCheckBox(filt, this);
}
This puts 3 checkboxes on the UI but they are all on top of one another.
How can I space them and also, how can I work with the change in state from check to unchecked on the fly?
Absolutely lost about how to do this when it's generating checkboxes from a stringlist.
Thanks.
The checkboxes are stacking on top of one another because you are not adding them to the widget's layout. Here's an example that will put each checkbox into a vertical layout.
QWidget *w = new QWidget;
QVBoxLayout *vbox = new QVBoxLayout;
foreach(QString filt, filters){
QCheckBox *checkbox = new QCheckBox(filt, this);
checkbox->setChecked(true);
vbox->addWidget(checkbox);
}
w->setLayout(vbox);
w->show()
Read up on QVBoxLayout at http://qt-project.org/doc/qt-5.0/qtwidgets/qvboxlayout.html