How to create resizable cusom widget on the QGraphicsScene - qt

I study QGraphics framework and want to create custom resizable widget.
For example I created a proxy widget with QTextEdit
QGraphicsLinearLayout* l = new QGraphicsLinearLayout;
QGraphicsProxyWidget* proxy = new QGraphicsProxyWidget;
proxy->setWidget( new QTextEdit );
proxy->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
l->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
l->addItem( proxy );
QGraphicsWidget* w = new QGraphicsWidget;
w->setLayout( l );
w->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
w->setFlag( QGraphicsItem::ItemIsMovable );
scene->addItem( w );
Widget looks fine, but I can't find out how add ability to resize it.
I searched in the Qt Examples, and google, but can't find any example.

A GraphicsItem's size, of which QGraphicsProxyWidget derives, is defined by its bounding rectangle. I expect the size of the widget would define the initial size of its proxy widget, so you could try changing the actual widget first.
In order to change the QGraphicsItem's size, you'd need to derive from QGraphicsProxyWidget and override its boundingRect() function.
Then you'd be able to create a resize function to change the returned rectangle, but ensure you call prepareGeometryChange first.
If you do inherit from QGraphicsProxyWidget and change its size this way, the enclosed widget may or may not be resized, depending upon its implementation.
I suggest you start by trying to resize the enclosing widget first.
Also note that there exists a setScale function for QGraphicsItems, which may also be an option here, as well as being able to scale the QPainter, in the paint function, if you derive from QGraphicsProxyWidget.

You should have your QGraphicsView instance resizable, so your scene content will react on resizing too.
Here it's working example (you should add QGraphicsView element on your MainWindows form):
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTextEdit>
#include <QPushButton>
#include <QGraphicsLinearLayout>
#include <QGraphicsProxyWidget>
#include <QGraphicsScene>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsScene* scene = new QGraphicsScene(this);
QGraphicsWidget *textEdit = scene->addWidget(new QTextEdit);
QGraphicsWidget *pushButton = scene->addWidget(new QPushButton);
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout;
layout->addItem(textEdit);
layout->addItem(pushButton);
QGraphicsWidget *form = new QGraphicsWidget;
form->setLayout(layout);
scene->addItem(form);
ui->graphicsView->setScene(scene);
setCentralWidget(ui->graphicsView);
}
MainWindow::~MainWindow()
{
delete ui;
}
However this doesn't affect sizing of the widgets inside scene. To achieve that see Merlin's answer.
Also here it's example of working solution with using of scaling obtained from here:
void MyGraphicsView::resizeEvent(QResizeEvent* event)
{
QGraphicsView::resizeEvent(event);
QSize viewportSize = this->viewport().size();
QSize imageGridSize = ...; //size of all images (bounding rect)
qreal factor = viewportSize.width() / imageGridSize.width();
if( viewportSize.height() < (imageGridSize * factor).height() )
factor *= viewSize.height() / (imageSize * factor).height();
this->resetTransform();
QTransform transform = this->transform();
transform.scale(factor, factor);
this->setTransform(transform);
}

Related

Making a layout in Qt

I just begin to work on making a layout with Grid. I tried to make it using the HBoxlayout and VBoxlayout. But How to set the position of the layout. I searched out and I found the setAlignment option but it isn't working on it.
So how to do its postioning of layouts like the image?
this is the layout I want to make
check this out
#include "mainscreen.h"
#include "ui_mainscreen.h"
#include<QLayout>
#include<QPushButton>
MainScreen::MainScreen(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainScreen)
{
ui->setupUi(this);
QGridLayout *layout=new QGridLayout;
QHBoxLayout *hlayout=new QHBoxLayout;
QPushButton *x=new QPushButton;
hlayout->setAlignment(Qt::AlignTop);
hlayout->addWidget(x);
layout->addChildLayout(hlayout);
this->setLayout(layout);
this->show();
}
MainScreen::~MainScreen()
{
delete ui;
}
You can find good information about Layout in this Tutorial from Qt: http://doc.qt.io/qt-5/qtwidgets-layouts-basiclayouts-example.html
Two things that are important for your Layout:
You need to nest several layout. Themain layout will be a VBoxlayout that contains the top and bottom Widget and one HBoxLayout in the middle. The HBoxLayout contain the other two widgets.
Two get different size for the two middle widgtes you need to give either a Stretch factor to the Layout or define sizes for the widgets.
You may consider doing the following
QHBoxLayout *TopLayout = new QHBoxLayout;
QHBoxLayout *BottomLayout = new QHBoxLayout;
QHBoxLayout *MiddleLayout = new QHBoxLayout;
QVBoxLayout *mainLayout = new QVBoxLayout;
QPushButton *topBtn = new QPushButton;
QPushButton *bottomBtn = new QPushButton;
QPushButton *LeftMiddleBtn = new QPushButton;
QPushButton *RightMiddleBtn = new QPushButton;
TopLayout->addWidget(topBtn);
BottomLayout->addWidget(bottomBtn);
// order matters here (i.e. the first addWidget will be placed in the left)
MiddleLayout->addWidget(LeftMiddleBtn);
MiddleLayout->addWidget(RightMiddleBtn);
// order matters here
mainLayout->addLayout(TopLayout);
mainLayout->addLayout(MiddleLayout);
mainLayout->addLayout(BottomLayout);
setLayout(mainLayout);
More convenient and right approach is to use Qt Designer or Qt Creator for this matter.
QMainWindow is a spatial case. It already has a layout set and it is impossible to change layout in widget! Reason is that QMainWindow has lots of other functionalities like menu, docking, status bar.
So how to make it work?
You have to set central widget:
MainScreen::MainScreen(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainScreen)
{
ui->setupUi(this);
auto widget = new QWidget();
setCentalWidget(widget);
auto vLayout = new QVBoxLayout(widget);
// add your stuff here
// some example for testing:
vLayout->addWidget(new QButton("Test button"));
vLayout->addWidget(new QLabel("Nice label"));
vLayout->addWidget(new QTextEdit);
}
Also you are doing something wrong since this line ui->setupUi(this); indicates that you are using Qt Designer (design ui by mouse) and this means that layout should not be setup directly by code!

Button is not showing on main window even after successful execution of code in Qt

I have tried this code but the button isn't displaying on the main window.
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QGridLayout>
#include<QLabel>
#include<QPushButton>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QPushButton *l=new QPushButton();
l->setText("abc");
QGridLayout *q=new QGridLayout();
q->addWidget(l);
this->setLayout(q);
this->show();
}
MainWindow::~MainWindow()
{
delete ui;
}
I have tried to change the code even by passing enums for alignment but nothing worked.
When you create new Qt widgets Application, the default form (MainWindow ui) is created with centralWidget to put all other widgets. In your code you created the QGridLayout without a parent, typically such layout should be placed in ui->centralWidget (as far as you are not creating another widget to be set as centralWidget), moreover I assume your mainWindow is shown from main.cpp (need not use show()). your code could thus be:
QPushButton *l=new QPushButton();
l->setText("abc");
QGridLayout *q=new QGridLayout(ui->centralWidget);
q-> addWidget(l);
Try adding the widget to the GridLayout with index using addWidget function
void QGridLayout::addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment = ...)
like:
q-> addWidget(l, 0, 0);
P.S. also consider using better names for your variables!

Change property of random element in QListWidget

I have 5 QProgressBars in a QListWidget (ui->listWidget). How can I access the third QProgressBar element and change its value ex. ( progressBar->setValue(40) )
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
a = new QPushButton(this);
connect(a, SIGNAL (clicked()),this, SLOT (clickedSlot()));
}
void MainWindow::clickedSlot()
{
QProgressBar *prog = new QProgressBar(this);
QListWidgetItem *it;
it = new QListWidgetItem(ui->listWidget);
ui->listWidget->insertItem(ui->listWidget->size().height(),it);
it->setSizeHint(QSize(200,50));
ui->listWidget->setItemWidget(it, prog);
}
The following code will obtain the third element in the list and set the progress to 40%.
QProgressBar *bar = qobject_cast<QProgressBar*>(ui->listWidget->itemWidget(pList->item(2)));
if (bar)
bar->setValue(40);
qobject_cast will safely cast the QWidget to QProgressBar, only if the widget is indeed a QProgressBar. If you are sure the third element is a QProgressBar, you can omit the if test if(bar).
See the qt documentation QListWidget and qobject_cast for more information.
Create the definition for the QProgressBar(s) in the class header file, then you can connect things to the setValue slot, or access them directly.
It seems odd to be adding ProgressBars to QListWidgetItems... wouldn't QHBoxLayout be more suitable?

Controlling number of boxes in box plot with Qt Chart

I am trying to figure out how to control number of boxes (or box sets) in the box plot with Qt chart, and use scrollbar to scroll through the whole chart. It's similar to this example (http://www.advsofteng.com/doc/cdcppdoc/zoomscrolltrackqt.htm).
The code below is what I did, and it populates all the boxes in the chartview, no matter how many. I selected 'ScrollBarAsNeeded' for the vertical and horizontal scroll bar policies in Qt designer.
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QChartView>
#include <QBoxPlotSeries>
#include <QBoxSet>
#include <QValueAxis>
#include <QBarCategoryAxis>
#include <QtSql>
#include <QSqlDatabase>
#include <QSqlQuery>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
db.setHostName("192.168.2.103");
db.setPort(5433);
db.setUserName("vorlket");
db.setPassword("K1156312j");
db.setDatabaseName("fxproj");
QBoxPlotSeries *bidaskSeries = new QBoxPlotSeries(this);
bidaskSeries->setName("bidask");
QStringList categories;
if (db.open())
{
QSqlQuery query;
if (query.exec("SELECT EXTRACT(YEAR FROM month), EXTRACT(MONTH FROM month), bid_low, bid_lowquartile, bid_median, bid_upquartile, bid_high FROM audusd.ts_month_quotebid ORDER BY month"))
{
while (query.next())
{
categories << query.value(0).toString() + "-" + query.value(1).toString();
QBoxSet *set = new QBoxSet();
set->setValue(QBoxSet::LowerExtreme, query.value(2).toDouble());
set->setValue(QBoxSet::LowerQuartile, query.value(3).toDouble());
set->setValue(QBoxSet::Median, query.value(4).toDouble());
set->setValue(QBoxSet::UpperQuartile, query.value(5).toDouble());
set->setValue(QBoxSet::UpperExtreme, query.value(6).toDouble());
bidaskSeries->append(set);
}
}
db.close();
}
QChart *chart = new QChart();
chart->legend()->hide();
chart->addSeries(bidaskSeries);
QBarCategoryAxis * axisX = new QBarCategoryAxis();
axisX->append(categories);
chart->addAxis(axisX, Qt::AlignBottom);
chart->setAxisX(axisX, bidaskSeries);
QValueAxis *axisY = new QValueAxis();
chart->addAxis(axisY, Qt::AlignLeft);
chart->setAxisY(axisY, bidaskSeries);
axisY->setRange(0.65, 1.15);
ui->chartview->setChart(chart);
ui->chartview->setRenderHint(QPainter::Antialiasing);
}
MainWindow::~MainWindow()
{
delete ui;
}
Maybe I need to use QGraphicsScene, instead of QChartView.
http://doc.qt.io/qt-5/qchartview.html#details:
QChartView is a standalone widget that can display charts. It does not require separate QGraphicsScene to work. If you want to display a chart in your existing QGraphicsScene, you need to use the QChart (or QPolarChart) class instead.
http://doc.qt.io/qt-5/qgraphicsscene.html#details:
The QGraphicsScene class provides a surface for managing a large number of 2D graphical items.
The class serves as a container for QGraphicsItems. It is used together with QGraphicsView for visualizing graphical items, such as lines, rectangles, text, or even custom items, on a 2D surface. QGraphicsScene is part of the Graphics View Framework.
QGraphicsScene also provides functionality that lets you efficiently determine both the location of items, and for determining what items are visible within an arbitrary area on the scene. With the QGraphicsView widget, you can either visualize the whole scene, or zoom in and view only parts of the scene.

QT4 using QMdiArea and QScrollArea strange usage trouble

Here is what I am doing: mainwindow with MdiArea, and I add a scrollarea widget (which contains a image label) to MdiArea as a subwindow. It doesn't work (the picture doesn't show).
Here is my code:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QScrollArea sa;
QPixmap *image = new QPixmap("2.jpg");
QLabel* imageLabel = new QLabel();
imageLabel->setPixmap(*image);
sa.setWidget(imageLabel);
sa.show();
ui->mdiArea->addSubWindow(&sa);
}
But when I use a QLabel as subwindow directly, i.e. replace the last line with:
ui->mdiArea->addSubWindow(imageLabel);
it works perfectly.
Anyone know why this is happening?
QScrollArea sa;
This declares a QScrollArea on the stack. It gets destroyed immediately after the constructor finishes. Allocate it with new like you do for the other widgets and it should start working.
QScollArea *sa = new QScrollArea;
...
ui->mdiArea->addSubWindow(sa);
(And change the sa. to sa->.)

Resources