I'm having extreme trouble getting QWidgets to expand as needed. This isn't the first time I've run into this problem, and last time I solved it by hacking in a large sizeHint(). This is the WRONG approach and I would really like to learn the CORRECT approach.
If anyone could help me out it would be greatly appreciated. Here's what it looks like and the layouts I have written in code. If necessary I can supply the code. Please help me learn Layouts.
Edit: The first layout mock up actually works correctly. I am attaching another layout mockup which causes a problem.
Code. Three classes.
MainWindow Class:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QWidget * w = new MainCentralWidget(this);
setCentralWidget(w);
}
MainCentralWidget Class:
MainCentralWidget::MainCentralWidget(QWidget *parent) :
QWidget(parent)
{
SetupLayout();
}
void MainCentralWidget::SetupLayout()
{
QVBoxLayout * main_layout;
QFormLayout * plugin_layout;
//Start
main_layout = new QVBoxLayout();
//Setup the plugin chooser
plugin_layout = new QFormLayout();
QComboBox * plugins_box = new QComboBox();
plugin_layout->addRow("Choose Plugin: ", plugins_box);
QFrame* line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
plugin_layout->addRow(line);
main_layout->insertLayout(0, plugin_layout);
main_layout->insertWidget(1, new SubWidget());
//Finish
setLayout(main_layout);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
SubWidget Class:
SubWidget::SubWidget(QWidget *parent) :
QWidget(parent)
{
setStyleSheet("QWidget { background: yellow }");
setMaximumSize(10000,10000);
setMinimumSize(100,100);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
SetupLayout();
//setMaximumSize(10000,10000);
}
void SubWidget::SetupLayout()
{
QHBoxLayout main_layout;
main_layout.setAlignment(Qt::AlignTop | Qt::AlignLeft);
main_layout.addWidget(&m_graphics_view);
m_graphics_view.setMaximumSize(10000,100000);
setLayout(&main_layout);
}
void SubWidget::SetupLayout()
{
QHBoxLayout main_layout;
main_layout.setAlignment(Qt::AlignTop | Qt::AlignLeft);
main_layout.addWidget(&m_graphics_view);
m_graphics_view.setMaximumSize(10000,100000);
setLayout(&main_layout);
}
Your main_layout gets destroyed immediately after SetupLayout finished, as it goes out of scope. So, actually, your SubWidget has no any layout. That is why widgets are shown incorrectly.
Related
I want to be able to change page of QStackedWidget with some kind of animation (like fade in/out or others...)
after some research I find out maybe its possible with QGraphicsOpacityEffect, then I found this codes in here
Fade In Your Widget
// w is your widget
QGraphicsOpacityEffect *eff = new QGraphicsOpacityEffect(this);
w->setGraphicsEffect(eff);
QPropertyAnimation *a = new QPropertyAnimation(eff,"opacity");
a->setDuration(350);
a->setStartValue(0);
a->setEndValue(1);
a->setEasingCurve(QEasingCurve::InBack);
a->start(QPropertyAnimation::DeleteWhenStopped);
Fade Out Your Widget
// w is your widget
QGraphicsOpacityEffect *eff = new QGraphicsOpacityEffect(this);
w->setGraphicsEffect(eff);
QPropertyAnimation *a = new QPropertyAnimation(eff,"opacity");
a->setDuration(350);
a->setStartValue(1);
a->setEndValue(0);
a->setEasingCurve(QEasingCurve::OutBack);
a->start(QPropertyAnimation::DeleteWhenStopped);
connect(a,SIGNAL(finished()),this,SLOT(hideThisWidget()));
// now implement a slot called hideThisWidget() to do
// things like hide any background dimmer, etc.
but looks like these codes have some problem when used in QWidget inside of QStackedWidget i mean widget successfully fade in and out, but after animation finish if I minimize the windows the widget will disappear completely! (Im still able to see widget in bottom right corner of my window, looks like its pos changed?!)
btw my program is frameless.
thanks for help.
here is a example from my problem
test.cpp
Test::Test(QWidget *parent)
: CustomMainWindow(parent)
{
ui.setupUi(this);
setShadow(ui.bg_app);
connect(ui.close_app_btn, &QPushButton::clicked, this, &QWidget::close);
connect(ui.minimize_app_btn, &QPushButton::clicked, this, &QWidget::showMinimized);
QGraphicsOpacityEffect* eff = new QGraphicsOpacityEffect(this);
ui.checking->setGraphicsEffect(eff); // checking is my widget inside of QStackedWidget.
QPropertyAnimation* a = new QPropertyAnimation(eff, "opacity");
a->setDuration(350);
a->setStartValue(0);
a->setEndValue(1);
a->setEasingCurve(QEasingCurve::InBack);
a->start(QPropertyAnimation::DeleteWhenStopped);
}
CustomMainWindow.cpp
CustomMainWindow::CustomMainWindow(QWidget *parent)
: QMainWindow(parent)
{
setWindowFlags(windowFlags() | Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
setAttribute(Qt::WA_TranslucentBackground);
}
void CustomMainWindow::setShadow(QWidget* window)
{
QGraphicsDropShadowEffect* windowShadow = new QGraphicsDropShadowEffect;
windowShadow->setBlurRadius(9.0);
windowShadow->setColor(palette().color(QPalette::Highlight));
windowShadow->setOffset(0.0);
window->setGraphicsEffect(windowShadow);
}
when I run my program with this code, at first its successfully Fade In, but if I for example minimize the window the widget move from its original position to somewhere else, look at this gif
Note: MainWindow is the name of my class.
Header file:
//...
private slots:
void animationStackedWidgets();
void whenAnimationFinish();
//....
CPP file:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->button, &QPushButton::clicked, this, &MainWindow::animationStackedWidgets);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::animationStackedWidgets()
{
QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(this);
ui->stackedWidget->setGraphicsEffect(effect);
QPropertyAnimation *anim = new QPropertyAnimation(effectSw,"opacity");
anim->setDuration(350);
anim->setStartValue(0);
anim->setEndValue(1);
anim->setEasingCurve(QEasingCurve::InBack);
anim->start(QPropertyAnimation::DeleteWhenStopped);
connect(anim, SIGNAL(finished()), this, SLOT(whenAnimationFinish()));
}
void MainWindow::whenAnimationFinish()
{
ui->stackedWidget->setGraphicsEffect(0); // remove effect
}
To be more clear explaining my problem, I've done a screenshot with some notes on it, hope it helps:
QGroupBox Layout format problem
As you can see from it, I have one big QVBoxLayout for the main layout of the app, inside it I've put a Qwidget, then a QGridLayout and then a QGridLayout again.
Inside this last one QGridLayout, I've put two QGroupBoxes, one in position 0,0 and one in position 0,1. Each QGroupBox has its own inner Layout, both of QGridLayout type again.
The screenshot shows that the firs QGroupBox works good, while the second one, that's quite smaller than the first, has two problems:
1) The label shoul be "Specific Operations" but it is trunked, and the only way to show it completely seems to be to put the buttons one next to the other horizontally... but I don't want it!
2) I managed to align the QGroupbox on the left of its "grid" but I need it to be on the upper-left corner of it, while it is centered for the moment... How can I achieve this?
Here is part of the code that should help you understand. Here is the kalk.h file:
class Kalk : public QWidget
{
Q_OBJECT
public:
Kalk(QWidget *parent = 0);
private slots:
void kalkChange(QString);
//....
private:
QComboBox *chooser;
QVBoxLayout *mainLayout;
QGridLayout *subLayout;
QGridLayout *operationsLayout;
QGroupBox *baseOperators;
QGridLayout *baseOperatorsLayout;
QGroupBox *specificOperators;
QGridLayout *specificOperatorsLayout;
};
Then the corresponding kalk.cpp file:
Kalk::Kalk(QWidget *parent) : QWidget(parent){
chooser = new QComboBox();
//...
connect(chooser,SIGNAL(currentIndexChanged(QString)),this,SLOT(kalkChange(QString)));
mainLayout = new QVBoxLayout;
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
subLayout = new QGridLayout;
subLayout->setEnabled(false);
subLayout->setSizeConstraint(QLayout::SetFixedSize);
mainLayout->addWidget(chooser);
mainLayout->addLayout(subLayout);
//operationsLayout = new QHBoxLayout;
operationsLayout = new QGridLayout;
operationsLayout->setSizeConstraint(QLayout::SetFixedSize);
baseOperators = new QGroupBox(tr("Base Operations"));
baseOperatorsLayout = new QGridLayout(baseOperators);
baseOperatorsLayout->setSizeConstraint(QLayout::SetFixedSize);
specificOperators = new QGroupBox(tr("Specific Operations"));
specificOperatorsLayout = new QGridLayout(specificOperators);
specificOperatorsLayout->setSizeConstraint(QLayout::SetFixedSize);
operationsLayout->addWidget(baseOperators,0,0);
operationsLayout->setAlignment(baseOperators,Qt::AlignLeft);
operationsLayout->addWidget(specificOperators,0,1);
operationsLayout->setAlignment(specificOperators,Qt::AlignLeft);
mainLayout->addLayout(operationsLayout);
setLayout(mainLayout);
//...
}
In another function I load the buttons inside the Layout of the QGroupBox, but I don't think the problem is here...
I have a groupbox that contains some pushbuttons and sliders. I want that when I click on a button, a new groupbox that is the same with the former one should appear under the first one. Whenever I click on the button, same situation should happen dynamically. Since I need up to 32 groupbox like that, I don't want to put all groupboxes manually. So, how can I do this?
First off, a layout is highly recommended.
Here is an example (I have done this before). You can derive a class from QScrollArea, then set in the constructor the layouts you want to have.
In here a simple button called Add is in the window.
If you press it, a row gets added and initialized with default values (0, 0, 0) <- integers.
In the live program, I load the values from a file/database and initialize it then.
You may want to use different layout(s) and a different setup, but this should give you the idea. I'm sure you get where you want with a little more experimenting.
//Structure to keep track of the added widgets easier
struct ItemRow
{
ItemRow(QLineEdit *entry, QLineEdit *amount, QComboBox *box)
: m_Entry(entry)
, m_Amount(amount)
, m_Box(box)
{ }
ItemRow(void)
: m_Entry(nullptr)
, m_Amount(nullptr)
, m_Box(nullptr)
{ }
QLineEdit *m_Entry;
QLineEdit *m_Amount;
QComboBox *m_Box;
};
The class declaration.
class MyScrollArea : public QScrollArea
{
Q_OBJECT
public:
explicit MyScrollArea(QWidget *parent = 0);
~MyScrollArea();
//...
void OnAddButtonPressed(void);
void DrawButtonLayout(void);
void AddRow(int val1, int val2, int val3); //Use own parameters
private:
QVBoxLayout *m_LayoutFirstRow;
QVBoxLayout *m_LayoutSecondRow;
QVBoxLayout *m_LayoutThirdRow;
//...
QVBoxLayout *m_LayoutButton;
//...
QList<QPushButton*> m_Buttons;
QVector<ItemRow> m_ItemRows;
}
The implementation.
MyScrollArea::MyScrollArea(QWidget *parent) :
QScrollArea(parent),
ui(new Ui::MyScrollArea)
{
ui->setupUi(this);
setWidget(new QWidget);
setWidgetResizable(true);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
QHBoxLayout *mainLayout = new QHBoxLayout(this);
m_LayoutFirstRow = new QVBoxLayout();
m_LayoutSecondRow = new QVBoxLayout();
m_LayoutThirdRow = new QVBoxLayout();
m_LayoutButton = new QVBoxLayout();
widget()->setLayout(mainLayout);
mainLayout->addLayout(m_LayoutFirstRow);
mainLayout->addLayout(m_LayoutSecondRow);
mainLayout->addLayout(m_LayoutThirdRow);
mainLayout->addLayout(m_LayoutButton);
DrawButtonLayout();
}
RewardDialog::~RewardDialog()
{
delete ui;
}
void MyScrollArea::OnAddButtonPressed(void)
{
AddRow(0, 0, 0);
}
void MyScrollArea::DrawButtonLayout(void)
{
QPushButton *addBtn = new QPushButton("Add");
connect(addBtn, SIGNAL(clicked()), this, SLOT(OnAddButtonPressed()));
m_LayoutButton->addWidget(addBtn);
m_Buttons.push_back(addBtn); //Keep somewhere track of the button(s) if needed - example: put in QList (not the best approach though)
}
void MyScrollArea::AddRow(int val1, int val2, int val3)
{
QLineEdit *pEntry = new QLineEdit(QString::number(val1));
pEntry->setValidator(new QIntValidator());
QLineEdit *pAmount = new QLineEdit(QString::number(val2));
pAmount->setValidator(new QIntValidator());
QComboBox *pBox = new QComboBox();
InitComboBox(pBox, val3); //Initialize the combo-box (use connect if you wish) - code not included
m_LayoutFirstRow->addWidget(pEntry);
m_LayoutSecondRow->addWidget(pAmount);
m_LayoutThirdRow->addWidget(pBox);
ItemRow row;
row.m_Entry = pEntry;
row.m_Amount = pAmount;
row.m_Box = pBox;
m_ItemRows.push_back(row);
}
Leave a comment if something seems wrong, I put this together in Notepad++.
Note: The documentation-link is for QT4.8, as 5.3 is not available anymore, but my code is from version 5.3 too.
I'm trying to find the settings or size policy so that each page in my QToolBox instance only takes up the space needed by its content. I've tried everything I could see in the properties for both the instance and for each of the individual pages.
Am I misconstruing the functionality of QToolBox widget or just missing the right setting?
What I am going for is something similar to the accordion fold type widget in Qt Creator:
I can't seem to get this "Sort" page to take only the size needed to display the button and field.
Unfortunately you can't do that directly because it will span all the available space that the title widgets don't occupy. You can emulate what you want by setting a fixed height on the QToolBox if you know the exact height your page(s). But you do not want to do that in practise.
If you want the behavior you ask for then you need to write your own custom control. It doesn't have to be hard. Use a QVBoxLayout and fill into it items of a custom class, let's call it ToolItem, which is a QWidget with a title (perhaps a button to show/hide) and another QWidget for showing the contents that is either visible or not.
The following very simple example will toggle the visibility of the ToolItem when it is clicked. And only when visible will it occupy any space.
class ToolItem : public QWidget {
public:
ToolItem(const QString &title, QWidget *item) : item(item) {
QVBoxLayout *layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(new QLabel(title));
layout->addWidget(item);
setLayout(layout);
item->setVisible(false);
}
protected:
void mousePressEvent(QMouseEvent *event) {
item->setVisible(!item->isVisible());
}
private:
QWidget *item;
};
class ToolBox : public QWidget {
public:
ToolBox() : layout(new QVBoxLayout) {
setLayout(layout);
}
void addItem(ToolItem *item) {
// Remove last spacer item if present.
int count = layout->count();
if (count > 1) {
layout->removeItem(layout->itemAt(count - 1));
}
// Add item and make sure it stretches the remaining space.
layout->addWidget(item);
layout->addStretch();
}
private:
QVBoxLayout *layout;
};
And simple usage of it:
QWidget *window = new QWidget;
window->setWindowTitle("QToolBox Example");
QListWidget *list = new QListWidget;
list->addItem("One");
list->addItem("Two");
list->addItem("Three");
ToolBox *toolBox = new ToolBox;
toolBox->addItem(new ToolItem("Title 1", new QLabel("Some text here")));
toolBox->addItem(new ToolItem("Title 2", list));
toolBox->addItem(new ToolItem("Title 3", new QLabel("Lorem Ipsum..")));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(toolBox);
window->setLayout(layout);
window->resize(500, 500);
window->show();
You can now tweak it to look like the QToolBox if needed.
Please don't hesitate to ask follow-up questions.
The example shown from Qt Designer may not be using a QToolBox, which behaves more like a stacked tab widget only displaying a single page at a time. The example in Qt Designer appears to be a QTreeWidget with custom drawing or styling.
This is not the complete answer.
I traced down the actual component, it can be included outside designer (kind of). Here is a minimal example showing how to do that (modified from https://github.com/zdenekzc/qtdesigner-integration).
form.h
#ifndef FORM_H
#define FORM_H
#include <QMainWindow>
class FormWindow : public QMainWindow {
Q_OBJECT
public:
explicit FormWindow (QWidget * parent = 0);
};
#endif // FORM_H
form.cc
#include "form.h"
#include <QApplication>
#include <QtDesigner/QtDesigner>
#include <QtDesigner/QDesignerComponents>
FormWindow::FormWindow (QWidget* parent) : QMainWindow (parent) {
QDesignerFormEditorInterface* core = QDesignerComponents::createFormEditor (this);
core->setWidgetBox (QDesignerComponents::createWidgetBox (core, 0));
this->setCentralWidget (core->widgetBox());
}
extern "C" int main (int argc, char * * argv) {
QApplication app (argc, argv);
FormWindow * win = new FormWindow ();
win->show ();
return app.exec();
}
qt-designer.pro
QT += designer
HEADERS = form.h
SOURCES = form.cc
LIBS += -lQt5DesignerComponents
Build it:
mkdir -p build
cd build
qmake-qt5 ../qt5-design.pro
make
./qt5-design
This is obviously not useful by itself unless you want to build a designer but another step towards isolating the actual component.
I'm working with the Qt KDE Necessitas project. I have a project built in Qt Creator and I am installing the apk on an emulator API-15 (also tested on API-10).
The following code is setup to clear the text of two different QLineEdit objects when a button is clicked, but this isn't the case. Randomly, only one of the two QLineEdit objects are cleared.
mainwindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
public slots:
void slotClear();
private:
QLineEdit* line1;
QLineEdit* line2;
//...
};
mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
QVBoxLayout* mainLayout = new QVBoxLayout;
QFormLayout* form = new QFormLayout;
line1 = new QLineEdit;
form->addRow(tr("Line 1: "), line1);
line2 = new QLineEdit;
form->addRow(tr("Line 2:"), line2);
QPushButton* button = new QPushButton;
mainLayout->addLayout(form);
mainLayout->addWidget(button);
QWidget* centralWid = new QWidget(this);
centralWid->setLayout(mainLayout);
this->setCentralWidget(centralWid);
connect(button, SIGNAL(clicked()), this, SLOT(slotClear()));
}
void MainWindow::slotClear()
{
line1->clear();
line2->clear();
}
//...
Calling the function QLineEdit::setText("") produces the same results. Additionally, connecting the clicked() signal from the button directly to the clear() slot of the QLineEdit has no effect.
I haven't been programming in Qt for very long, so I am unsure if there is something I am doing wrong. Is anybody seeing something needs to be corrected in order to have the text cleared from BOTH QLineEdits? I am not sure if this is unique to Qt itself or Qt Necessitas. Any input would be greatly appreciated.
EDIT
I have also just noticed that entering text in one line, switching to another line and entering text there, and then switching back to the original line results in the original text being erased once the field is clicked (note, the button was never clicked). I think this is a pretty clear indication that something funky is going on.
EDIT 2
Registered as a bug with KDE