In the application I'm developing, I've got sets of QPushButtons that overlay each other like so:
resize(300, 300);
QRect geometry( 100, 100 , 100 , 100 );
m_button[0] = new QPushButton( this );
m_button[0]->setGeometry( geometry );
m_button[0]->setText( "Button 1" );
m_button[1] = new QPushButton( this );
m_button[1]->setGeometry( geometry );
m_button[1]->setText( "Button 2" );
m_button[1]->raise();
Result:
But when the follows style is added at application level
QApplication app(argc, argv);
app.setStyleSheet( "QPushButton{ border-width: 1px; border-color: blue; border-style: solid;}" );
the buttons are rendered as like so:
What would be the reason for this artefact, and how can I workaround it.
Information about my application
The overlapping buttons are keys belonging to different character sets of a virtual keyboard. These keys are raised or lowered depending on the current character set.
You want to use a QStackedWidget and place your QPushButtons in that stack. Then you just need to raise the button you want with QStackedWidget::setCurrentIndex or QStackedWidget::setCurrentWidget.
Edit: sample code:
resize(300, 300);
QStackedWidget* stack = new QStackedWidget(this);
stack->setGeometry( 100, 100 , 100 , 100 );
m_button[0] = new QPushButton( this );
m_button[0]->setText( "Button 1 );
stack->addWidget(m_button[0]);
m_button[1] = new QPushButton( this );
m_button[1]->setText( "Button 2" );
stack->addWidget(m_button[1]);
stack->setCurrentWidget(m_button[1]); //or stack->setCurrentIndex(1);
Related
I'm creating dynamically unknown number of buttons and put them to QScrollArea.
someDialogButton = new QPushButton();
usersDialogs.push_back(someDialogButton);
ui->usersArea->setWidget(someDialogButton);
usersDialogs is QVector<QPushButton*>
usersArea is QScrollArea
But the buttons overlap and can be seen only the last button added. I tried use: setLayout(QLayout* layout) method, but it was useless.
How I can compose the buttons to make all buttons visible?
The Qt documentation says
void QScrollArea::setWidget(QWidget * widget)
Sets the scroll area's widget.
The widget becomes a child of the scroll area, and will be destroyed
when the scroll area is deleted or when a new widget is set.
Therefore, whenever you call QScrollArea::setWidget() you overwrite the previous widget. What you need to do is to fill a QWidget with the buttons and then you call QScrollArea::setWidget() on that container widget.
Here's an example, of what your classes constructor could look like:
MainWindow::MainWindow(QWidget *parent)
: QWidget(parent)
{
this->resize( 300, 200 );
auto * layout = new QVBoxLayout(this);
auto * scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable( true );
layout->addWidget( scrollArea );
auto * container = new QWidget();
scrollArea->setWidget( container );
layout = new QVBoxLayout(container);
auto * button1 = new QPushButton( "1", container);
auto * button2 = new QPushButton( "2", container);
auto * button3 = new QPushButton( "3", container);
layout->addWidget( button1 );
layout->addWidget( button2 );
layout->addWidget( button3 );
}
For me it gives this:
I seem to be having an issue with getting the behavior I want from a QScrollArea. As it stands, whenever I add something to the layout of the widget set as the target of the scroll area it will rather opt to expand the entire window rather than fit to scroll.
Here's my current setup:
QSplitter * mainArea = new QSplitter( Qt::Vertical );
QWidget * containment = new QWidget;
containment->setLayout( new QVBoxLayout );
currentStructures = new QWidget;
currentStructures->setLayout( new QVBoxLayout );
currentStructures->layout()->setAlignment( Qt::AlignTop );
QScrollArea * scroll = new QScrollArea();
scroll->setWidget( currentStructures );
containment->layout()->addWidget( currentStructures );
mainArea->addWidget( containment );
mainArea->addWidget( new QWidget ); //TODO: create preview bar
this->layout()->addWidget( mainArea );
This makes it so that the scroll area only ever expands and never shows the scroll bars.
By inserting this line:
containment->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Ignored );
I can get the area to ignore the size of its children but it also doesn't take up the space needed nor does it show scroll bars -- it just crunches the widget inside of it.
I'm a bit of a greenhorn to using Qt, but I was wondering how I would achieve the behavior that I need: I would like the scroll area to greedily take up the area it has available from its parent layout but not expand the the containing layouts vertically when adding but instead actually shows scroll bars. I'm planning on allowing a lot of resizing, so it needs to actually scale to the parent instead of just being a fixed size. I am at a loss as to how I should proceed in solving this aspect of my GUI. Thank you for your time.
If you would like to tackle this problem visually, here's a harness that you can use. I greatly appreciate your help.
#include <QtWidgets\qapplication.h>
#include <QtWidgets\qsplitter.h>
#include <QtWidgets\qlayout.h>
#include <QtWidgets\qscrollarea.h>
#include <QtWidgets\qpushbutton.h>
#include <QtWidgets\qlabel.h>
#include <QtWidgets\qsizepolicy.h>
int main( int argc, char * argv[] )
{
QApplication app( argc, argv );
QWidget * testWidget = new QWidget;
testWidget->setLayout( new QVBoxLayout );
//////////////////CODE IN QUESTIION//////////////////////
QSplitter * mainArea = new QSplitter( Qt::Vertical );
QWidget * containment = new QWidget;
containment->setLayout( new QVBoxLayout );
//containment->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Ignored );
QWidget * currentStructures = new QWidget;
currentStructures->setLayout( new QVBoxLayout );
currentStructures->layout()->setAlignment( Qt::AlignTop );
QScrollArea * scroll = new QScrollArea();
scroll->setWidget( currentStructures );
containment->layout()->addWidget( currentStructures );
mainArea->addWidget( containment );
///////////////////////////////////////////////////////////
QPushButton * pushIntoLayout = new QPushButton( "Add Element to Widget" );
QWidget::connect( pushIntoLayout, &QPushButton::clicked, [currentStructures](){ currentStructures->layout()->addWidget( new QLabel( "A generated label" ) ); } );
mainArea->addWidget( pushIntoLayout );
currentStructures->setStyleSheet(
"QWidget {"
"background-color: #FAA;"
"}"
);
testWidget->layout()->addWidget( mainArea );
testWidget->show();
return app.exec();
}
Even after copying and pasting my code to make a test harness I had not noticed my grievous error. I had mistakenly pushed the currentStructures widget to the container's layout rather than scroll, the scrolling area after giving it its child.
An excerpt from the Qt docs on void QScrollArea::setWidget( QWidget * widget ) for those unfamiliar:
The widget becomes a child of the scroll area, and will be destroyed when the scroll area is deleted or when a new widget is set.
Thanks to everyone who looked it over.
For those wondering, the fixed code would look like the following:
QSplitter * mainArea = new QSplitter( Qt::Vertical );
QWidget * containment = new QWidget;
containment->setLayout( new QVBoxLayout );
QWidget * currentStructures = new QWidget;
currentStructures->setLayout( new QVBoxLayout );
currentStructures->layout()->setAlignment( Qt::AlignTop );
QScrollArea * scroll = new QScrollArea();
scroll->setWidget( currentStructures );
scroll->setWidgetResizable( true );
containment->layout()->addWidget( scroll );
mainArea->addWidget( containment );
mainArea->addWidget( new QWidget ); //TODO: create preview bar
this->layout()->addWidget( mainArea );
Cheers!
For the last couple of days i am trying to solve a particular problem with the Qt Layout system. I'll try to generalize it:
I have a widget with two rows. On the first row there are tree buttons( or whatever other controls ). Their layout is shown on the picture:
The center button is taking all the extra space available( and is expanding as the widget increases its size ). The thing is that i want to programmatically want to resize the center widget( button ) and maintain the layout. With my current implementation when i resize the center button this is what happens:
I want the right button to be aligned on the right of the center button( without the extra space ). Now when i resize the widget it is going back to position 1 ( center takes all the extra space ), but this is not the effect i want.
Here is my current implementation:
#include "Widget_Old.h"
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QDebug>
WidgetOld::WidgetOld(QWidget *parent) :
QWidget(parent)
{
QVBoxLayout* mainLayout = new QVBoxLayout( this );
QWidget* firstRowWidget = new QWidget( this );
QPushButton* left = new QPushButton;
left->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
left->setText( "left" );
m_center = new QPushButton;
m_center->setText( "centerr");
m_center->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
QPushButton* right = new QPushButton;
right->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
right->setText( "right" );
QHBoxLayout* firstRowLayout = new QHBoxLayout( firstRowWidget );
firstRowLayout->addWidget( left );
firstRowLayout->addWidget( m_center );
firstRowLayout->addWidget( right );
QHBoxLayout* secondRowLayout = new QHBoxLayout;
QPushButton* button = new QPushButton( "resize" );
connect( button, SIGNAL(clicked()), SLOT(decrement()) );
secondRowLayout->addWidget( button );
mainLayout->addWidget( firstRowWidget );
mainLayout->addLayout( secondRowLayout );
}
WidgetOld::~WidgetOld()
{
}
void WidgetOld::decrement()
{
qDebug() << "Changing width from " << m_center->width() << " to " << m_center->width()/2;
m_center->resize( m_center->width()/2, m_center->height() );
}
Notes:
I've tried aligning the center, and right widget Qt::AlignLeft, but no result. Actually when
aligning widget left in a layout it tries to take the minimal size which breaks the expanding functionality:(
I've shared my code if any reference is needed:
https://drive.google.com/file/d/0B-mc4aKkzWlxTWdNNmpuQ0ptQ3M/edit?usp=sharing
Thanks for reading my post, hope you know the solution:)
You have to set the maximum size of your central widget. For the right widget to adapt, set its size policy to MinimumExpanding when you resize the central one:
void WidgetOld::decrement ()
{
qDebug() << "Changing width from " << m_center->width() << " to " << m_center->width()/2;
m_center->setMaximumWidth (m_center->width()/2);
m_right->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred );
}
Alternatively, if you resize the central widget manually (which is deprecated, as the above commenter noted), you must also update the geometry of the right one manually. Then your decrement method should look like
void WidgetOld::decrement ()
{
qDebug() << "Changing width from " << m_center->width() << " to " << m_center->width()/2;
m_center->resize( m_center->width()/2, m_center->height() );
int rightPosX = m_center->pos().x() + m_center->width() + m_firstRowLayout->spacing ();
int rightWidth = centralWidget()->pos().x() + centralWidget()->width() - m_mainLayout->margin() - rightPosX;
m_right->setGeometry (rightPosX, m_right->pos().y(), rightWidth, m_right->height());
}
In this case, you should also update your custom sizes on resize, as the layout will try to restore default proportions.
when you use layouts you move responsibility of managing the size to a layout, so you should NEVER resize items manually when they are in layout.
try this:
WidgetOld::WidgetOld(QWidget *parent) :
QWidget(parent)
{
QVBoxLayout* mainLayout = new QVBoxLayout( this );
QWidget* firstRowWidget = new QWidget( this );
QPushButton* left = new QPushButton;
left->setText( "left" );
left->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
m_center = new QPushButton;
m_center->setText( "centerr");
m_center->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
QPushButton* right = new QPushButton;
right->setText( "right" );
right->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
QHBoxLayout* firstRowLayout = new QHBoxLayout( firstRowWidget );
firstRowLayout->addWidget( left );
firstRowLayout->addStretch();
firstRowLayout->addWidget( m_center );
firstRowLayout->addStretch();
firstRowLayout->addWidget( right );
QHBoxLayout* secondRowLayout = new QHBoxLayout;
QPushButton* button = new QPushButton( "resize" );
connect( button, SIGNAL(clicked()), SLOT(decrement()) );
secondRowLayout->addWidget( button );
mainLayout->addWidget( firstRowWidget );
mainLayout->addLayout( secondRowLayout );
}
I don't know if it's a bad idea.
Could you make your firstRowLayout global and then dynamically add a stretch to the layout in the end in your decrement() SLOT function?
void WidgetOld::decrement(){
qDebug() << "Changing width from " << m_center->width() << " to " << m_center->width()/2;
m_center->resize( m_center->width()/2, m_center->height() );
firstRowLayout->addStretch(1);
}
this way the stretch will push all your buttons to the left
How can I maintain an aspect ratio between two QHBoxLayouts?
For instance I want a QHBoxLayout to be one third of the entire window width and the other to be two thirds of the entire window width:
How can I achieve this? I tried messing with the size hints of the controls in them but that didn't work out
void QSizePolicy::setHorizontalStretch(uchar stretchFactor)
Example:
QHBoxLayout* layout = new QHBoxLayout(form);
QWidget* left = new QWidget(form);
QSizePolicy spLeft(QSizePolicy::Preferred, QSizePolicy::Preferred);
spLeft.setHorizontalStretch(1);
left->setSizePolicy(spLeft);
layout->addWidget(left);
QWidget* right = new QWidget(form);
QSizePolicy spRight(QSizePolicy::Preferred, QSizePolicy::Preferred);
spRight.setHorizontalStretch(2);
right->setSizePolicy(spRight);
layout->addWidget(right);
The answer of york.beta is working, but I prefer much less code.
At least the sizePolicy is by default Prefered/Prefered.
The default policy is Preferred/Preferred, which means that the widget can be freely resized, but prefers to be the size sizeHint() returns.
You can simply use the second parameter of addWidget to stretch the widgets.
QHBoxLayout *layout = new QHBoxLayout( this );
layout->setContentsMargins( 0, 0, 0, 0 );
layout->setSpacing( 0 );
QPushButton *left = new QPushButton( "133px", this );
left->setStyleSheet( "QPushButton{border: 1px solid red;}" );
QPushButton *right = new QPushButton( "267px", this );
right->setStyleSheet( "QPushButton{border: 1px solid blue;}" );
layout->addWidget( left, 33 );
layout->addWidget( right, 66 );
this->setLayout( layout );
this->setFixedWidth( 400 );
See http://doc.qt.io/qt-5/qboxlayout.html#addWidget
and http://doc.qt.io/qt-5/qwidget.html#sizePolicy-prop
You can also use the layoutStretch property:
https://doc.qt.io/qt-5/layout.html#stretch-factors
In your case it'd be
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,2">
You can edit the sizePolicy for the widgets and set a higher horizontalStretch for the widget in the right.
I'm trying to learn the Qt framework. My QStateMachine code does the correct thing (pressing the button makes the chat window popup change size).
I can't change the speed of the animation to get a nice visual transition.
Any suggestions?
Here's the code:
MainWindow::MainWindow()
{
widget.setupUi( this );
// chat window - Chat button opens
ChatWindowClosedState = new QState();
ChatWindowOpenState = new QState();
ChatWindowOpenGeometry = widget.groupBox->geometry();
ChatWindowClosedGeometry = widget.pushButton->geometry();
ChatWindowClosedGeometry.translate( -ChatWindowClosedGeometry.width(), 0 );
ChatWindowClosedState->assignProperty( widget.groupBox, "geometry", ChatWindowClosedGeometry );
ChatWindowOpenState->assignProperty( widget.groupBox, "geometry", ChatWindowOpenGeometry );
ChatWindowCloseTransition = ChatWindowClosedState->addTransition( widget.pushButton, SIGNAL( clicked() ), ChatWindowOpenState );
ChatWindowCloseAnimation = new QPropertyAnimation( widget.pushButton, "geometry" );
ChatWindowCloseAnimation->setDuration( 5000 );
ChatWindowCloseTransition->addAnimation( ChatWindowCloseAnimation );
ChatWindowOpenTransition = ChatWindowOpenState->addTransition( widget.pushButton, SIGNAL( clicked() ), ChatWindowClosedState );
ChatWindowOpenAnimation = new QPropertyAnimation( widget.pushButton, "geometry" );
ChatWindowOpenAnimation->setDuration( 5000 );
ChatWindowOpenTransition->addAnimation( ChatWindowOpenAnimation );
machine = new QStateMachine( this );
machine->addState( ChatWindowClosedState );
machine->addState( ChatWindowOpenState );
machine->setInitialState( ChatWindowClosedState );
machine->start();
}
The code
ChatWindowOpenAnimation = new QPropertyAnimation( widget.pushButton, "geometry" );
should be
ChatWindowOpenAnimation = new QPropertyAnimation( widget.groupBox, "geometry" );
The animation was being applied to the wrong widget.