QWidget - resize animation - c++

Say I have a QHBoxLayout where there are 2 QTextEdits and between them a button with an arrow to the right. When you click on the button, the right-side QTextEdit gradually closes by moving the left border until it meets the right one. Simultaneously, the right border of the left QTextEdit takes the place which the right QTextEdit released. And after pressing on the button, the state of the system is coming to the former one.
EDIT: In order to organize this I have done the following:
1) In header file:
class MyWidget : public QWidget
{
Q_OBJECT
QTextEdit *m_textEditor1;
QTextEdit *m_textEditor2;
QPushButton *m_pushButton;
QHBoxLayout *m_layout;
int m_deltaX;
public:
MyWidget(QWidget * parent = 0);
~MyWidget(){}
private slots:
void closeOrOpenTextEdit2(bool isClosing);
};
2) In the source file:
MyWidget::MyWidget(QWidget * parent):QWidget(parent),m_deltaX(0)
{
m_pushButton = new QPushButton(this);
m_pushButton->setText(">");
m_pushButton->setCheckable(true);
connect(m_pushButton, SIGNAL(clicked(bool)), this, SLOT(closeOrOpenTextEdit2(bool)));
m_textEditor1 = new QTextEdit(this);
m_textEditor1->setText("AAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAA AAAAAAAAAAA AAAAAAAAAAA AA");
m_textEditor2 = new QTextEdit(this);
m_layout = new QHBoxLayout;
m_layout->addWidget(m_textEditor1);
m_layout->addWidget(m_pushButton);
m_layout->addWidget(m_textEditor2);
setLayout(m_layout);
}
void MyWidget::closeOrOpenTextEdit2(bool isClosing)
{
QPropertyAnimation *animation1 = new QPropertyAnimation(m_textEditor2, "geometry");
QPropertyAnimation *animation2 = new QPropertyAnimation(m_pushButton, "geometry");
QPropertyAnimation *animation3 = new QPropertyAnimation(m_textEditor1, "geometry");
if(isClosing) //close the second textEdit
{
m_pushButton->setText("<");
QRect te2_1 = m_textEditor2->geometry();
m_deltaX = te2_1.width()-3;
QRect te2_2(te2_1.x()+m_deltaX, te2_1.y(), 3 ,te2_1.height());
QRect pb_1 = m_pushButton->geometry();
QRect pb_2(pb_1.x()+m_deltaX, pb_1.y(), pb_1.width() ,pb_1.height());
QRect te1_1 = m_textEditor1->geometry();
QRect te1_2(te1_1.x(), te1_1.y(), te1_1.width()+m_deltaX, te1_1.height());
//animation->setDuration(10000);
animation1->setStartValue(te2_1);
animation1->setEndValue(te2_2);
animation2->setStartValue(pb_1);
animation2->setEndValue(pb_2);
animation3->setStartValue(te1_1);
animation3->setEndValue(te1_2);
}
else //open
{
m_pushButton->setText(">");
QRect te2_1 = m_textEditor2->geometry();
QRect te2_2(te2_1.x()-m_deltaX, te2_1.y(), 3+m_deltaX ,te2_1.height());
QRect pb_1 = m_pushButton->geometry();
QRect pb_2(pb_1.x()-m_deltaX, pb_1.y(), pb_1.width() ,pb_1.height());
QRect te1_1 = m_textEditor1->geometry();
QRect te1_2(te1_1.x(), te1_1.y(), te1_1.width()-m_deltaX, te1_1.height());
//animation->setDuration(10000);
animation1->setStartValue(te2_1);
animation1->setEndValue(te2_2);
animation2->setStartValue(pb_1);
animation2->setEndValue(pb_2);
animation3->setStartValue(te1_1);
animation3->setEndValue(te1_2);
}
animation1->start();
animation2->start();
animation3->start();
}
EDIT:
And I have the following problem:
When I close the second QTextEdit (by clicking on the button) and resize the MyWidget, then the QTextEdit restores its state (but it should stay closed of course). How can I solve this problem?
Please provide me with a code snippet.

Qt's Animation framework sounds like a good place to start. You could just try to follow their tutorials, adapting for you use case. I have used it already, and it seemed quite straight forward.

1) You could replace your button with a vertical layout, place the button inside this layout and finally add a vertical spacer below the button (in same the layout).
...
QVBoxLayout* m_buttonLayout = new QVBoxLayout();
m_layout = new QHBoxLayout();
m_layout->addWidget(m_textEditor1);
m_layout->addLayout(m_buttonLayout);
m_layout->addWidget(m_textEditor2);
m_buttonLayout->addWidget(m_pushButton);
m_buttonLayout->addItem( new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding) );
2) I guess you could (and should) animate widget's maximumSize (or just maximumWidth) property and let the layout take care of calculating actual geometries. This would also simplify your calculations. E.g.
QPropertyAnimation *animation1 = new QPropertyAnimation(m_textEditor2, "maximumWidth");
QPropertyAnimation *animation2 = new QPropertyAnimation(m_textEditor, "maximumWidth");
if (isClosing)
{
int textEdit2_start = m_textEditor2->maximumWidth();
int textEdit2_end = 0;
int textEdit_start = m_textEditor->maximumWidth();
int textEdit_end = textEdit_start + textEdit2_start;
animation1->setStartValue(textEdit2_start);
...
}
Also, now you don't have to animate buttons geometry at all (assuming that you have set fixed size to it).
PS. I didn't compile codes so there might be minor errors but you should get the idea.

Here what I wanted:
Header file
class MyWidget : public QWidget
{
Q_OBJECT
QTextEdit *m_textEditor1;
QTextEdit *m_textEditor2;
QPushButton *m_pushButton;
QHBoxLayout *m_layout;
QVBoxLayout *m_buttonLayout;
int m_deltaX;
bool m_isClosed;
public:
MyWidget(QWidget * parent = 0);
~MyWidget(){}
void resizeEvent( QResizeEvent * event );
private slots:
void closeOrOpenTextEdit2(bool isClosing);
};
Source file
MyWidget::MyWidget(QWidget * parent):QWidget(parent),m_deltaX(0)
{
m_pushButton = new QPushButton(this);
m_pushButton->setText(">");
m_pushButton->setCheckable(true);
m_pushButton->setFixedSize(16,16);
connect(m_pushButton, SIGNAL(clicked(bool)), this, SLOT(closeOrOpenTextEdit2(bool)));
m_textEditor1 = new QTextEdit(this);
m_textEditor1->setText("AAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAA AAAAAAAAAAA AAAAAAAAAAA AA");
m_textEditor2 = new QTextEdit(this);
m_buttonLayout = new QVBoxLayout();
m_buttonLayout->addWidget(m_pushButton);
m_buttonLayout->addItem( new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding) );
m_layout = new QHBoxLayout;
m_layout->addWidget(m_textEditor1, 10);
m_layout->addSpacing(15);
m_layout->addLayout(m_buttonLayout);
m_layout->setSpacing(0);
m_layout->addWidget(m_textEditor2, 4);
setLayout(m_layout);
resize(800,500);
}
void MyWidget::closeOrOpenTextEdit2(bool isClosing)
{
m_isClosed = isClosing;
QPropertyAnimation *animation1 = new QPropertyAnimation(m_textEditor2, "maximumWidth");
if(isClosing) //close the second textEdit
{
m_textEditor2->setMaximumWidth(m_textEditor2->width());
int textEdit2_start = m_textEditor2->maximumWidth();
m_deltaX = textEdit2_start;
int textEdit2_end = 3;
animation1->setDuration(500);
animation1->setStartValue(textEdit2_start);
animation1->setEndValue(textEdit2_end);
m_pushButton->setText("<");
}
else //open
{
int textEdit2_start = m_textEditor2->maximumWidth();
int textEdit2_end = m_deltaX;
animation1->setDuration(500);
animation1->setStartValue(textEdit2_start);
animation1->setEndValue(textEdit2_end);
m_pushButton->setText(">");
}
animation1->start();
}
void MyWidget::resizeEvent( QResizeEvent * event )
{
if(!m_isClosed)
m_textEditor2->setMaximumWidth( QWIDGETSIZE_MAX );
}

Related

How to resize QGraphicsView properly?

I try to create a simple application with QGraphicsView. The application can load graphical files with different ratios in the view. I would like to implement the following feature: on the application window resize, the content of the view should be also resized and centered. I do not know this part of the Qt well, so I can resize the content, but I cannot center it. How can I fix it?
MainWindow.h snippet:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
void resizeEvent(QResizeEvent *event) override;
public slots:
void onButtonClicked();
private:
bool m_flag = false;
QGraphicsPixmapItem *m_item = nullptr;
QGraphicsView *m_view = nullptr;
QPixmap m_pixmap;
};
MainWindow.cpp:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto central = new QWidget(this);
setCentralWidget(central);
// layouts
auto mainLayout = new QVBoxLayout;
mainLayout->setAlignment(Qt::AlignTop);
central->setLayout(mainLayout);
// top layout
auto topLayout = new QHBoxLayout;
topLayout->setAlignment(Qt::AlignLeft);
mainLayout->addLayout(topLayout);
auto btn = new QPushButton(this);
btn->setText("Test");
connect(btn, &QPushButton::clicked, this, &MainWindow::onButtonClicked);
topLayout->addWidget(btn);
m_view = new QGraphicsView;
mainLayout->addWidget(m_view);
auto scene = new QGraphicsScene;
m_view->setScene(scene);
m_view->setMinimumSize(800, 600);
m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QString name = ":/pic1.jpg";
m_pixmap = QPixmap{ name }.scaled(800, 600, Qt::KeepAspectRatio);
m_item = scene->addPixmap(m_pixmap);
m_view->viewport()->resize(m_pixmap.size());
}
void MainWindow::onButtonClicked()
{
m_flag = !m_flag;
QString name = m_flag ? ":/pic2.png" : ":/pic1.jpg";
m_pixmap = QPixmap{ name }.scaled(m_view->size(), Qt::KeepAspectRatio);
m_item->setPixmap(m_pixmap);
m_view->fitInView(m_item, Qt::KeepAspectRatio);
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
QMainWindow::resizeEvent(event);
m_view->fitInView(m_item, Qt::KeepAspectRatio);
}
Test result for the pic2.png:
When an image is scaled, it is done with respect to the top left, so it will always look up if the height is smaller than the prefix or to the right if the width is smaller.
The solution is to repaint the scaled QPixmap in the middle of the final QPixmap.
const QSize pixmap_size{800, 600};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto central = new QWidget(this);
setCentralWidget(central);
// layouts
auto mainLayout = new QVBoxLayout;
mainLayout->setAlignment(Qt::AlignTop);
central->setLayout(mainLayout);
// top layout
auto topLayout = new QHBoxLayout;
topLayout->setAlignment(Qt::AlignLeft);
mainLayout->addLayout(topLayout);
auto btn = new QPushButton(this);
btn->setText("Test");
connect(btn, &QPushButton::clicked, this, &MainWindow::onButtonClicked);
topLayout->addWidget(btn);
m_view = new QGraphicsView;
mainLayout->addWidget(m_view);
auto scene = new QGraphicsScene;
m_view->setScene(scene);
m_view->setMinimumSize(pixmap_size);
m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_pixmap = QPixmap(pixmap_size);
m_pixmap.fill(Qt::transparent);
m_item = new QGraphicsPixmapItem;
m_view->scene()->addItem(m_item);
show();
onButtonClicked();
}
QPixmap MainWindow::createPixmap(const QString & filename, const QSize & size) const{
QPixmap tmp = QPixmap{ filename }.scaled(size, Qt::KeepAspectRatio);
QPixmap pixmap(size);
pixmap.fill(Qt::transparent);
QPainter p(&pixmap);
QPoint point(QRect({}, size).center()-tmp.rect().center());
p.drawPixmap(point, tmp);
p.end();
return pixmap;
}
void MainWindow::onButtonClicked()
{
QString name = m_flag ? ":/pic2.png" : ":/pic1.jpg";
m_item->setPixmap(createPixmap(name, pixmap_size));
m_flag = !m_flag;
m_view->fitInView(m_item, Qt::KeepAspectRatio);
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
QMainWindow::resizeEvent(event);
m_view->fitInView(m_item, Qt::KeepAspectRatio);
}

When qlabel is resized Layout is not updated

I am trying to implement a camera view app in Qt, but I don't know why It doesn't fix correctly layout / buttons. First, before to update the qlabel, I have this:
But when I click the start button. the GUI changes to this:
The code to create the window is:
cameraLabel = new QLabel(tr("No camera loaded"));
cameraLabel->setAlignment(Qt::AlignCenter);
cameraLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
cameraLabel->setBackgroundRole(QPalette::Dark);
cameraLabel->setAutoFillBackground(true);
createButtons();
...
..
.
mainLayout = new QHBoxLayout;
mainLayout->addWidget(cameraLabel);
mainLayout->addLayout(buttonsLayout);
setLayout(mainLayout);
void CameraPlayer::createButtons()
{
QSize iconSize(36, 36);
playButton = new QToolButton;
playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
playButton->setIconSize(iconSize);
playButton->setToolTip(tr("Play"));
connect(playButton, SIGNAL(clicked()), this, SLOT(play()));
snapshotButton = new QToolButton;
snapshotButton->setIcon(style()->standardIcon(QStyle::SP_DialogSaveButton));
snapshotButton->setIconSize(iconSize);
snapshotButton->setToolTip(tr("Snapshot"));
connect(snapshotButton, SIGNAL(clicked()), this, SLOT(snapshot()));
stopButton = new QToolButton; cameraLabel = new QLabel(tr("No camera loaded"));
cameraLabel->setAlignment(Qt::AlignCenter);
cameraLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
cameraLabel->setBackgroundRole(QPalette::Dark);
cameraLabel->setAutoFillBackground(true);
createButtons();
SParameters& globalParams = GlobalParameters::Instance().params;
globalParams.IPAddress = "169.251.0.1";
globalParams.isColor = true;
globalParams.timer_s =10;
timerIdCamera = 0;
mainLayout = new QHBoxLayout;
mainLayout->addWidget(cameraLabel);
mainLayout->addLayout(buttonsLayout);
setLayout(mainLayout);
stopButton->setIcon(style()->standardIcon(QStyle::SP_MediaStop));
stopButton->setIconSize(iconSize);
stopButton->setToolTip(tr("Stop"));
connect(stopButton, SIGNAL(clicked()), this, SLOT(stop()));
colorButton = new QPushButton;
colorButton->setText(tr("WB / Color"));
connect(colorButton, SIGNAL(clicked()), this, SLOT(changeColor()));
buttonsLayout = new QVBoxLayout;
buttonsLayout->addStretch();
buttonsLayout->addWidget(playButton);
buttonsLayout->addWidget(snapshotButton);
buttonsLayout->addWidget(stopButton);
buttonsLayout->addWidget(colorButton);
buttonsLayout->addStretch();
}
To udpate the qLabel,It is configured a timer / trigger which is called every x milsecs and update the qlabel object:
void CameraPlayer::timerEvent(QTimerEvent *event){
try
{
if (timerIdCamera!=0)
{
if (camera.CameraHasImage())
{
QImage *qImage = camera.CameraGrab();
if (qImage!=NULL && SIZE_IMAGE!=QString::number(qImage->byteCount()))
{
cameraLabel->setPixmap(QPixmap::fromImage(*qImage));
cameraLabel->setFixedWidth(qImage->width());
cameraLabel->show();
}
delete[] qImage->bits();
delete qImage;
}
}
}
catch (std::exception &ex)
{
QMessageBox qMBox;
qMBox.setText(QString::fromUtf8(ex.what()));
qMBox.exec();
}
}
The problem is here
cameraLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
I was ignoring layout policies from other widgets / layouts. I commented this and It started to work well.

QT dynamically generate label, LineEdit, Button

I want to do serial communication in the QT inside, according to the number of serial ports to dynamically generate label, LineEdit, Button, and these three buttons can pull down the scroll bar when the size of the interface, how to do well, I write this below is dead of.
The effect of encapsulation into a method
The interface was washed last
Define QGridLayout insude QScrollArea. And add your new widgets into
that layout in code. QGridLayout::addWidget
Define table where you will show several widgets in table cells. That real complicated way.
void BaseUi::BaseScrollArea()
{
QScrollArea *pArea = new QScrollArea();
QWidget *pWidget = new QWidget();
pWidget->setStyleSheet("QWidget" "{background:white;}");
m_vbox_layout = new QVBoxLayout();
m_vbox_layout->addSpacerItem(new QSpacerItem(100, 30,
QSizePolicy::Expanding, QSizePolicy::Expanding));
pWidget->setLayout(m_vbox_layout);
pArea->setWidget(pWidget);
pArea->setWidgetResizable(true);
m_main_layout = new QVBoxLayout();
m_main_layout->addWidget(pArea);
}
void BaseUi::addAutoRecordUi(QString lab_neme, QString ledit_name)
{
QWidget *page = new QWidget;
QGridLayout *layout = new QGridLayout(page);
QLabel *label = new QLabel;
label->setText(lab_neme);
label->setFont(font());
QLineEdit *ledit = new QLineEdit;
ledit->setText(ledit_name);
ledit->setFont(font());
layout->addWidget(label, 0, 1);
layout->addWidget(ledit, 0, 2);
page->setLayout(layout);
m_vbox_layout->insertWidget(m_vbox_layout->count()-1, page);
}
void BaseUi::addMulRecordUi(QString lab_neme, QString ledit_name, QString
but_name)
{
QWidget *page = new QWidget;
QGridLayout *layout = new QGridLayout(page);
QLabel *label = new QLabel;
label->setText(lab_neme);
label->setFont(font());
QLineEdit *ledit = new QLineEdit;
ledit->setText(ledit_name);
ledit->setFont(font());
QPushButton *but = new QPushButton(but_name);
but->setFont(font());
layout->addWidget(label, 0, 1);
layout->addWidget(ledit, 0, 2);
layout->addWidget(but, 0, 3);
page->setLayout(layout);
m_vbox_layout->insertWidget(m_vbox_layout->count()-1, page);
}

Why Qt::AlignTop doesn't work in QVBoxLayout that I use like main layout?

I have simple class that inherits QDialog, I add dynamically elements
and my elements are located in the center, but I want to add them at the top.
class CustomDialog : public QDialog {
Q_OBJECT
private:
QVBoxLayout *mainLayout;
CustomDialog()
{
mainLayout = new QVBoxLayout();
setLayout(mainLayout);
}
public:
void update()
{
QLabel* label = new QLabel("some text");
QVBoxLayout *verLayout = new QVBoxLayout;
verLayout->addStretch();
verLayout->setAlignment(Qt::AlignTop);
verLayout->addWidget(label, Qt::AlignTop);
mainLayout->setAlignment(Qt::AlignTop);
mainLayout->addLayout(verLayout, Qt::AlignTop);
}
};
What am I doing wrong? and why my dynamically added elements always in center?
I understand that you want to place it and that the top is shown, so you could use QSpacerItem to push it.
class CustomDialog : public QDialog {
Q_OBJECT
QVBoxLayout *mainLayout;
public:
CustomDialog(QWidget *parent=0): QDialog(parent)
{
mainLayout = new QVBoxLayout(this);
QSpacerItem *verticalSpacer = new QSpacerItem(20, 217, QSizePolicy::Minimum, QSizePolicy::Expanding);
mainLayout->addItem(verticalSpacer);
addWidgets("1");
addWidgets("2");
}
private:
void addWidgets(const QString &text)
{
QLabel* label = new QLabel(text);
QVBoxLayout *verLayout = new QVBoxLayout;
verLayout->addStretch();
verLayout->setAlignment(Qt::AlignTop);
verLayout->addWidget(label, Qt::AlignTop);
mainLayout->setAlignment(Qt::AlignTop);
mainLayout->insertLayout(mainLayout->count()-1, verLayout);
}
};
Or if you want it to have a reverse order you must insert it in the first position with:
mainLayout->insertLayout(0, verLayout);
Note: The use of addLayout is incorrect since the second parameter is stretch.

Add widgets into a QTabWidget

Can I add some widgets like QLabel and QPushButton into a QTabWidget?
Actually, I want to do something like this:
I'm using C++ and Qt.
Thanks
It's possible, just use QTabWidget::setCornerWidget.
Quick example:
QWidget* pTabCornerWidget = new QWidget(this);
QLabel* pLabelTime = new QLabel(pTabCornerWidget);
pLabelTime->setText("10:22:20");
QPushButton* pButton = new QPushButton(pTabCornerWidget);
pButton->setText("?");
pButton->setMaximumSize(QSize(25, 25));
QHBoxLayout* pHLayout = new QHBoxLayout(pTabCornerWidget);
pHLayout->addWidget(pLabelTime);
pHLayout->addWidget(pButton);
mUI.tabWidget->setCornerWidget(pTabCornerWidget, Qt::TopRightCorner);
If you want do this stuff, I recommend you use QTabBar instead of QTabWidget. For example, your code can be (remember, that it's just a very simple example):
// Here some first widget
QWidget *wid1 = new QWidget(this);
QHBoxLayout *wid1Lay = new QHBoxLayout(wid1);
wid1Lay->addWidget(new QLabel(tr("Widget1")));
// Here some second widget
QWidget *wid2 = new QWidget(this);
QHBoxLayout *wid2Lay = new QHBoxLayout(wid2);
wid2Lay->addWidget(new QLabel(tr("Widget2")));
// Here some third widget
QWidget *wid3 = new QWidget(this);
QHBoxLayout *wid3Lay = new QHBoxLayout(wid3);
wid3Lay->addWidget(new QLabel(tr("Widget3")));
// Here your Tab bar with only bars
QTabBar *bar = new QTabBar(this);
bar->addTab("One");
bar->addTab("Two");
bar->addTab("Three");
// Here some label (for example, current time) and button
QLabel *lab = new QLabel(tr("Some text"), this);
QPushButton *but = new QPushButton(tr("Push"), this);
// Main layouts
QVBoxLayout *vLay = new QVBoxLayout(ui->centralWidget);
QHBoxLayout *hLay = new QHBoxLayout();
vLay->addLayout(hLay);
hLay->addWidget(bar);
// Spacer for expanding left and right sides
hLay->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
hLay->addWidget(lab);
hLay->addWidget(but);
vLay->addWidget(wid1);
vLay->addWidget(wid2);
vLay->addWidget(wid3);
// Some simple connect with lambda for navigation
connect(bar, &QTabBar::currentChanged, [=] (int index) {
wid1->setVisible(false);
wid2->setVisible(false);
wid3->setVisible(false);
switch(index) {
case 0: wid1->setVisible(true);
break;
case 1: wid2->setVisible(true);
break;
case 2: wid3->setVisible(true);
break;
default:{}
}
});
emit bar->currentChanged(0);