Qt5 paintEvent not called inside QScrollArea - c++

I'm having a bit of a problem with Qt. I'm trying to create a 2D drawing of cells, with QRect, by overloading paintEvent for a custom class which inherits QWidget and which is placed inside a QScrollArea. The problem is, paintEvent does not trigger at all (not on resize events, not when I call repaint() or update(), nor when I launch my program). Here is where I overload paintEvent, in GOL.cpp:
void GOL::paintEvent(QPaintEvent *) {
QPainter painter(this);
//painter.setPen(Qt::black);
int x1Rect = rectPaint.x();
int y1Rect = rectPaint.y();
int x2Rect = x1Rect + rectPaint.width();
int y2Rect = y1Rect + rectPaint.height();
int xCell;
int yCell = 0;
for (int i = 0; i < rows; i++) {
xCell = 0;
for (int j = 0; j < cols; j++) {
if (xCell <= x2Rect && yCell <= y2Rect && xCell + cellSize >= x1Rect &&
yCell + cellSize >= y1Rect) {
if (principalMatrix->get(i,j)) {
painter.fillRect(xCell, yCell, cellSize - 1, cellSize - 1, cellColourAlive);
}
else {
painter.fillRect(xCell, yCell, cellSize - 1, cellSize - 1, cellColourDead);
}
}
xCell += cellSize;
}
yCell += cellSize;
}
}
And my layout is as follows, in DisplayGame.cpp:
DisplayGame::DisplayGame(QWidget *parent, int threads_no, int generations, char* file_in, char* file_out) :
QWidget(parent) {
gol = new GOL(threads_no, generations, file_in, file_out);
QHBoxLayout *title = setupTitle();
QHBoxLayout *buttons = setupButtons();
QVBoxLayout *layout = new QVBoxLayout();
scrlArea = new QScrollArea;
scrlArea->setWidget(gol);
layout->addLayout(title);
layout->addWidget(scrlArea);
layout->addLayout(buttons);
setLayout(layout);
}
I honestly have no idea why it does not draw anything. Any ideas?

I've fixed it by modifying as follows:
DisplayGame::DisplayGame(QWidget *parent, int threads_no, int generations, char* file_in, char* file_out) :
QWidget(parent) {
gol = new GOL(this, threads_no, generations, file_in, file_out);
QSize *adjustSize = new QSize(gol->cellSize, gol->cellSize); //QSize object that is as big as my QRect matrix
adjustSize->setWidth(gol->cellSize * gol->rows);
adjustSize->setHeight(gol->cellSize * gol->cols);
gol->setMinimumSize(*adjustSize);
QVBoxLayout *layout = new QVBoxLayout;
QHBoxLayout *title = setupTitle();
layout->addLayout(title);
QHBoxLayout *buttons = setupButtons();
layout->addLayout(buttons);
QPalette pal(palette()); //Setting the background black, so the white spaces between QRect items cannot be seen (though I could have modified the margins?)
pal.setColor(QPalette::Background, Qt::black);
scrlArea = new QScrollArea(this);
scrlArea->setAutoFillBackground(true);
scrlArea->setPalette(pal);
scrlArea->setWidget(gol);
layout->addWidget(scrlArea);
setLayout(layout);
}
And I've left the paintEvent as it was. It was, ultimately, a size problem, as AlexanderVX said.

Related

Qt Grid Layout doesn't fit into Scroll Area [duplicate]

This question already has an answer here:
QScrollArea missing Scrollbar
(1 answer)
Closed 1 year ago.
I am trying to put a grid view that will contain some buttons into a scroll area but I don't know why the grid layout only fits into the initial size of the scroll area (it doesn't go downside so I could use the scroll bar to see all elements).
ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
QGridLayout* lay=new QGridLayout(this);
QPushButton *name[100];
for(int i=0;i<10;i++){
QString str1="project"+QString::number(i);
QString str2="3/6task";
QString str3="ALEX";
name[i]=new QPushButton(str1);
name[i]->setObjectName("btn_1");
name[i]->setStyleSheet("QPushButton#btn_1{background:transparent;Text-align:left;font-family:century gothic;font-size:18px;color:red;}"
"QPushButton#btn_1:hover{color:yellow;Font-size:22px;}");
name[i]->setFixedSize(100,40);
lay->addWidget(name[i]);
}
ui->scrollArea->setLayout(lay);
The result is in the next photo:
I fixed it by this way:
ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
QGridLayout *lay = new QGridLayout(ui->centralwidget);
lay->addWidget(ui->scrollArea, 0, 0, 1, 1);
auto gridLayout = new QGridLayout(ui->scrollAreaWidgetContents);
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
auto widget = new QWidget(ui->scrollAreaWidgetContents);
widget->setObjectName(QString::fromUtf8("widget"));
auto gridLayout_2 = new QGridLayout(widget);
gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2"));
gridLayout->addWidget(widget, 0, 0, 1, 1);
for (int i = 0; i < 10; i++)
{
QString str1 = "project" + QString::number(i);
QString str2 = "3/6task";
QString str3 = "ALEX";
QPushButton *btn = new QPushButton(str1);
btn->setObjectName(QString("btn_%1").arg(i));
btn->setStyleSheet("QPushButton#btn_1{background:transparent;Text-align:left;font-family:century gothic;font-size:18px;color:red;}"
"QPushButton#btn_1:hover{color:yellow;Font-size:22px;}");
btn->setFixedSize(100, 40);
widget->layout()->addWidget(btn);
}
}
You have to use the following code:
This code has been tested.
QScrollArea* scroll = new QScrollArea(this);
scroll->setGeometry(50,0,250,300);
scroll->setWidgetResizable(true);
QWidget *container = new QWidget;
scroll->setWidget(container);
QGridLayout* lay = new QGridLayout(container);
QPushButton *name[100];
for(int i = 0; i < 20; ++i) {
name[i]=new QPushButton(scroll);
lay->addWidget(name[i]);
}
Of cource you can modify the button and the scrollArea as well.
There is no problem of using ui->scrollArea
Your applying the QGridLayout to the QScollArea when you actually need to apply it to a QWidget managed by the scroll area using QScrollArea::setWidget...
#include <QApplication>
#include <QGridLayout>
#include <QPushButton>
#include <QScrollArea>
#include <QString>
#include <QWidget>
int main (int argc, char **argv)
{
QApplication app(argc, argv);
QScrollArea scroll_area;
scroll_area.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
QWidget viewport;
QGridLayout layout(&viewport);
for (int i = 0; i < 10; ++i) {
QString str1 = "project" + QString::number(i);
QString str2 = "3/6task";
QString str3 = "ALEX";
auto *pb = new QPushButton(str1);
pb->setObjectName("btn_1");
pb->setStyleSheet("QPushButton#btn_1{background:transparent;Text-align:left;font-family:century gothic;font-size:18px;color:red;}"
"QPushButton#btn_1:hover{color:yellow;Font-size:22px;}");
pb->setFixedSize(100,40);
layout.addWidget(pb);
}
scroll_area.setWidget(&viewport);
scroll_area.show();
return app.exec();
}

How to properly drop a widget on a QGraphicsView right under the mouse?

Following my previous post I have been trying to solve a positioning problem withing a QGraphicsView.
After a widget is dragged from a QListWidget and dropped inside a QGraphicsView, it jumps everywhere.
The problem I have been trying to solve with also the help of another user is the following:
As soon as the widget is dragged inside the QGraphicsView and dropped. It just goes in random locations and have to pay attention to retrieve it. This behavior is not user friendly and sometimes it takes a bit to locate the widget.
Below the code:
scene.h
#ifndef SCENE_H
#define SCENE_H
#include <QGraphicsScene>
class Scene : public QGraphicsScene
{
public:
Scene(QObject *parent = nullptr);
protected:
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
void dropEvent(QGraphicsSceneDragDropEvent *event);
};
#endif // SCENE_H
scene.cpp
void Scene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
QByteArray encoded =
event->mimeData()->data("application/x-qabstractitemmodeldatalist");
QDataStream stream(&encoded, QIODevice::ReadOnly);
QStringList rosTables;
QString newString;
while (!stream.atEnd()) {
int row, col;
QMap<int, QVariant> roleDataMap;
stream >> row >> col >> roleDataMap;
rosTables << roleDataMap[Qt::DisplayRole].toString();
}
for (const QString &tableType : rosTables) {
if (tableType == "Images") {
QPoint initPos(0, 0);
auto *wgt = new CustomTableWidget;
auto *proxyControl = addRect(0, 0, 0, 0, QPen(Qt::black),
QBrush(Qt::darkGreen));
auto *sizeGrip = new QSizeGrip(wgt);
auto *layout = new QHBoxLayout(wgt);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(sizeGrip, 0, Qt::AlignRight | Qt::AlignBottom);
connect(wgt, &CustomTableWidget::sizeChanged, [wgt, proxyControl](){
proxyControl->setRect(wgt->geometry().adjusted(-10, -10, 10, 10));
});
wgt->setColumnCount(2);
wgt->setRowCount(2);
for (int ridx = 0; ridx < wgt->rowCount(); ridx++) {
for (int cidx = 0; cidx < wgt->columnCount(); cidx++) {
auto *item = new QTableWidgetItem();
item->setText(QString("%1").arg(ridx));
wgt->setItem(ridx,cidx,item);
}
}
auto *const proxy = addWidget(wgt);
proxy->setPos(initPos.x(), initPos.y()
+ proxyControl->rect().height());
proxy->setParentItem(proxyControl);
proxyControl->setPos(initPos.x(), initPos.y());
proxyControl->setFlag(QGraphicsItem::ItemIsMovable, true);
proxyControl->setFlag(QGraphicsItem::ItemIsSelectable, true);
proxyControl->setRect(wgt->geometry().adjusted(-10, -10, 10, 10));
}
}
}
What it was tried so far to solve the problem:
Now the QGraphicsScene have been subclassed to re-write the mouse events, in particular and in this case attention was given to the dropEvent(QGraphicsSceneDragDropEvent *event) as that is the responsible function to "see the widget"
Several trials and errors were conducted and in the same function it was tried to add the following part:
for (const QString &tableType : rosTables) {
if (tableType == "Images") {
QPoint initPos(event->scenePos()); // <-- Tried this but no change
auto *wgt = new CustomTableWidget;
auto *proxyControl = addRect(0, 0, 0, 0, QPen(Qt::black),
QBrush(Qt::darkGreen));
auto *sizeGrip = new QSizeGrip(wgt);
auto *layout = new QHBoxLayout(wgt);
}
An additional thing tried was to provide the QPoint the event->scenePos().toPoint() as deemed proper for the goal, but unfortunately that didn't solve the problem either:
for (const QString &tableType : rosTables) {
if (tableType == "Images") {
QPoint initPos(event->scenePos().toPoint()); // <-- Tried this too but no change
auto *wgt = new CustomTableWidget;
auto *proxyControl = addRect(0, 0, 0, 0, QPen(Qt::black),
QBrush(Qt::darkGreen));
auto *sizeGrip = new QSizeGrip(wgt);
auto *layout = new QHBoxLayout(wgt);
}
If anyone has an idea of what the problem might be please provide guidance on how to solve it.
Change proxy->setPos(initPos.x(), initPos.y() + proxyControl->rect().height()); to proxy->setPos(10, 10);.
Set scene rect:
Scene::Scene(QObject *parent) :
QGraphicsScene(parent)
{
setBackgroundBrush(Qt::lightGray);
setSceneRect(0, 0, 1000, 1000);
}
Set view alignment, e.g. in MainWindow.cpp:
ui->graphicsView->setAlignment(Qt::AlignLeft | Qt::AlignTop);

Custom Widgets which inherits from QTreeWidget crashes while destructing?

Recently I've met some problems about QT.As we have a lot of custom widgets (inherit from QT's basic widgets such as QWidget,QTreeWidget...),they worked fine at runtime,BUT,IT IS EASY TO CRASH when exiting program or destructing them,I don't know IF THERE ANYTHING WRONG or inappropriate while implementing a widget which inherits from a basic widget.
Here is an example:
ChatTreeWidget.cpp:
#include "ChatTreeWidget.h"
#include <QPicture>
#include <QDebug>
ChatTreeWidget::ChatTreeWidget(QWidget *parent)
:QTreeWidget(parent)
{
setRootIsDecorated(false);
setIndentation(0);
setHeaderHidden(true);
setIconSize(QSize(30, 30));
setFocusPolicy(Qt::NoFocus);
setObjectName(QString("chatTreeWidget"));
setStyleSheet("#chatTreeWidget{border:0px;}"
"QTreeWidget::item{border-bottom:1px solid #d9d9d9;padding-left:0px;}"
"QTreeWidget::item:hover{background-color: #def0ff;}"
"QTreeWidget::item:selected{background-color:#c2e2fd;}");
connect(this, &QTreeWidget::itemExpanded, this, [=](QTreeWidgetItem *item)
{
auto indexvar = item->data(0, Qt::UserRole).toInt();
indexvar -= 10000;
if (indexvar > m_iconList.size() || indexvar < 0)
return;
auto lbl = m_iconList.at(indexvar);
lbl->setPixmap(QPixmap(":/Widgets/Resources/chattree/sanjiao1.png"));
});
connect(this, &QTreeWidget::itemCollapsed, this, [=](QTreeWidgetItem *item)
{
auto indexvar = item->data(0, Qt::UserRole).toInt();
indexvar -= 10000;
if (indexvar > m_iconList.size() || indexvar < 0)
return;
auto lbl = m_iconList.at(indexvar);
lbl->setPixmap(QPixmap(":/Widgets/Resources/chattree/sanjiao2.png"));
});
connect(this, &QTreeWidget::itemClicked, this, [=](QTreeWidgetItem *item, int column) {
auto indexvar = item->data(0, Qt::UserRole).toInt();
indexvar -= 10000;
if (indexvar > m_iconList.size() || indexvar < 0)
return;
if (item->isExpanded())
{
setItemExpanded(item, false);
}
else
{
setItemExpanded(item, true);
}
});
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
}
ChatTreeWidget::~ChatTreeWidget()
{
qDebug() << "123123";
}
QTreeWidgetItem* ChatTreeWidget::addTreeWidgetTopItem(QString str)
{
return insertTreeWidgetTopItem(topLevelItemCount(), str);
}
QTreeWidgetItem* ChatTreeWidget::insertTreeWidgetTopItem(int index, QString str)
{
QWidget *widget = new QWidget(this);
QLabel *textLabel = new QLabel(widget);
QHBoxLayout *hlayout = new QHBoxLayout(widget);
QLabel *iconLabel = new QLabel(widget);
QSpacerItem *spacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
textLabel->setText(str);
textLabel->setStyleSheet(u8"font-family: \"Microsoft YaHei\"");
hlayout->addWidget(textLabel);
hlayout->addSpacerItem(spacer);
iconLabel->setPixmap(QPixmap(":/Widgets/Resources/chattree/sanjiao2.png"));
hlayout->addWidget(iconLabel);
hlayout->setContentsMargins(5, 0, 10, 0);
widget->setLayout(hlayout);
m_iconList.append(iconLabel);
QTreeWidgetItem *rootItem = new QTreeWidgetItem();
rootItem->setData(0, Qt::UserRole, m_index);
rootItem->setSizeHint(0, QSize(30, 30));
m_index++;
this->insertTopLevelItem(index, rootItem);
setItemWidget(rootItem, 0, widget);
rootItem->setBackground(0, Qt::green);
setExpandsOnDoubleClick(false);
return rootItem;
}
void ChatTreeWidget::setRootItemStyleSheet(QTreeWidgetItem *item,QString css)
{
auto widgets = itemWidget(item, 0);
widgets->setStyleSheet(css);
}
void ChatTreeWidget::setRootItemStyleSheet(QString css)
{
for (qint32 i = 0; i < topLevelItemCount(); i++)
{
auto rootItem = topLevelItem(i);
auto widgets = itemWidget(rootItem,0);
widgets->setStyleSheet(css);
}
}
this code is trying to implement a custom-treewidget to display chat members,so it inherits the QTreeWidget (once I thought I should implement it with QTreeView,but as I'm stupid and lazy,I used QTreeWidget Directly),and uses a special style-sheet to change display style.and as it's a public widget,it offers some interfaces to add top-level items.there is an example image:
when I destruct this widget (I' sure that this widget is token from UI or layout) and even sometimes QT's automatic destructing will cause a crash.It happens very frequently but not everytime.
the two main ways of destroying the widget are:
1.Add it to some Ui,and let it managed by that UI.
2.delete by hand:
chatTreeWidget->setParent(0);
chatTreeWidget->disconnect();
chatTreeWidget->deleteLater();
both of them can cause a random crash.
Is there anything wrong? or it's just a bug?
Thanks for any kind of help!

How to replace QPushButton with QLabel in CentralWidget in Qt?

I am trying to learn and build a small minesweeper gui app.Here is how this looks like:
The next thing I want to do is after clicking one button, then the button will be set to hide() and a QLabel will appear in the same place.
My code is like this:
.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void setUI();
void clickedBtnInfo();
private:
Ui::MainWindow *ui;
QWidget *centralWidget;
QGridLayout *centralLayout;
QPushButton *btn[81];
QPushButton *btnSender;
QLabel *lbl[81];
QString clickedBtnName;
private slots:
void btnClicked();
};
.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
lbl[81] = new QLabel(centralWidget);
setUI();
for(int i = 0; i < 81; i++) {
connect(btn[i], SIGNAL(clicked(bool)), btn[i], SLOT(hide()));
connect(btn[i], SIGNAL(clicked(bool)), this, SLOT(btnClicked()));
}
centralWidget->setLayout(centralLayout);
}
void MainWindow::setUI()
{
...
centralLayout = new QGridLayout(centralWidget);
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
centralLayout->addWidget(btn[j + i * 9], 0 + i, j);
centralLayout->setSpacing(0);
}
}
...
}
void MainWindow::clickedBtnInfo()
{
btnSender = qobject_cast<QPushButton*>(sender());
clickedBtnName = btnSender->objectName();
}
void MainWindow::btnClicked()
{
clickedBtnInfo();
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
if(btn[j + i * 9]->objectName() == clickedBtnName) {
centralLayout->addWidget(lbl[j + i * 9], 0 + i, j);
centralLayout->setSpacing(0);
}
}
}
}
When I ran this and clicked one of the buttons, the app just force quit(The program has unexpectedly finished.)
So how can I solve this problem and replace QPushButton with QLabel after clicking? Thanks.
The Problem why your code leads to a crash was correctly pointed out by #G.M. - lbl[81] = new QLabel(centralWidget); will create only 1 label, and place it in the 81st array field. That are 2 errors at once:
If your array is 81 elements long, they are numbered: 0, 1, ..., 79, 80. The last element is 80, because you start counting at 0. So placing something at position 81 is not possible
To actually create 81 new labels, you have to create them in a loop:
Sample code:
for(int i = 0; i < 81; i++) { //goes from 0 to 80
lbl[i] = new QLabel(centralWidget);
lbl[i]->setObjectName(QStringLiteral("Label %1").arg(i));
}
The second line gives each label a custom name. See QString::arg for details.
One more tip: Avoid C-arrays, unless you need high performace/low memory (which is not the case for your example). Instead try to use one of the Qt container classes, e.g. QList or QVector. (You can use std::vector etc. as well, but when working with Qt, I would recommend to use the Qt containers)
For your case, I would recommend QVector, as it performs best with fixed-sized arrays. With both of these changes, update your code to:
class MainWindow : public QMainWindow
{
//...
private:
Ui::MainWindow *ui;
QWidget *centralWidget;
QGridLayout *centralLayout;
QVector<QPushButton> btn;
QPushButton *btnSender;
QVector<QLabel> lbl;
QString clickedBtnName;
};
In your cpp file, update the part where you create the arrays:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
lbl.resize(81);
for(int i = 0; i < lbl.size(); i++) { //goes from 0 to 80
lbl[i] = new QLabel(centralWidget);
lbl[i]->setObjectName(QStringLiteral("Label %1").arg(i));
}
setUI();
//...
}
void MainWindow::setUI()
{
//keep your code, but remember to prepare the btn vector with:
btn.resize(81);
//then you can fill the vector just like you are used to:
btn[0] = ui->btn0;
//...
}
And the rest stays the same, as these classes allow you to keep the standard array access syntax you know.

QWidget - resize animation

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 );
}