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.
Related
I am implementing a small example using a QTableWidget with specific headers.
However, as soon as I run the example the rows do not stretch properly as it is possible to see in the following example (which is the wrong behavior):
After manual resizing I obtain what I am looking for (which is the expected behavior):
prescriptiondialog.h
class PrescriptionDialog : public QDialog
{
Q_OBJECT
public:
PrescriptionDialog();
~PrescriptionDialog();
QPushButton *mAddButton;
QPushButton *mRemoveButton;
QLineEdit *durationEdit;
QLabel *durationLbl;
DrugTable *mTable;
};
#endif // PRESCRIPTIONDIALOG_H
prescriptiondialog.cpp
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QHeaderView>
PrescriptionDialog::PrescriptionDialog()
{
setWindowTitle("Drug Mixer");
mTable = new DrugTable();
mTable->horizontalHeader()->setStretchLastSection(4);
mTable->verticalHeader()->setStretchLastSection(QHeaderView::Interactive);
mTable->show();
QObject::connect(mAddButton, &QPushButton::clicked, mTable, &DrugTable::addCustomRow);
QObject::connect(mRemoveButton, &QPushButton::clicked, mTable, &DrugTable::removeCustomRow);
setLayout(mLay);
show();
}
What I have done so far:
1) I tried to use the headers in the following way, but that did not give the expected behavior.
The problem with this approach is that columns are equally spaced (I am not looking for this specific behavior because I need the user to adjust them as they want) and, most importantly, the row takes the whole space of the application window making the row extremely big.
PrescriptionDialog::PrescriptionDialog()
{
setWindowTitle("Drug Mixer");
mTable = new DrugTable();
mTable->horizontalHeader()->setStretchLastSection(4);
mTable->verticalHeader()->setStretchLastSection(QHeaderView::Interactive);
QHeaderView* header = mTable->horizontalHeader();
header->setSectionResizeMode(QHeaderView::Stretch);
QHeaderView* headerRows = mTable->verticalHeader();
headerRows->setSectionResizeMode(QHeaderView::Stretch);
mTable->show();
}
2) I tried the option of using the horizontalHeader() provided by the QTableWidget but that didn't provide any improvements and actually I obtained the effect of the first screenshot (the "When To Take" column is all compressed until I manually adjust )
PrescriptionDialog::PrescriptionDialog()
{
setWindowTitle("Drug Mixer");
mTable = new DrugTable();
mTable->horizontalHeader()->setStretchLastSection(4);
mTable->verticalHeader()->setStretchLastSection(QHeaderView::Interactive);
mTable->resizeRowsToContents();
mTable->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Stretch);
mTable->show();
}
3) I came across this source, this other source but none of them provided light on how to solve the issue.
4) I dug more into the problem and went through this which is using the property of resizeRowsToContents() which I used in the example but didn't change anything in the final result.
Thanks for shedding light ob this and provide guidance on how to solve the problem.
I tried to make a small example using resizeRowsToContents() and it works well for me.
Tested on Qt 5.15.1 MinGW.
#include "mainwindow.h"
#include <QTableView>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QStandardItemModel>
#include <QHeaderView>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QStandardItemModel *model = new QStandardItemModel{this};
model->appendRow({new QStandardItem{tr("Drug")}, new QStandardItem{}});
QTableView *view = new QTableView{this};
view->setModel(model);
QHBoxLayout *horz_layout = new QHBoxLayout;
horz_layout->addWidget(new QPushButton{tr("Add when"), this});
horz_layout->addWidget(new QPushButton{tr("Remove when"), this});
QStandardItemModel *inner_model = new QStandardItemModel{this};
inner_model->setHorizontalHeaderLabels({tr("Select"), tr("When to take")});
QTableView *inner_view = new QTableView{this};
inner_view->setModel(inner_model);
QWidget *widget = new QWidget;
QVBoxLayout *vert_layout = new QVBoxLayout{widget};
vert_layout->addLayout(horz_layout);
vert_layout->addWidget(inner_view);
view->horizontalHeader()->setStretchLastSection(true);
view->setIndexWidget(model->index(0, 1), widget);
view->resizeRowToContents(0);
this->setCentralWidget(view);
this->resize(500, 500);
}
MainWindow::~MainWindow()
{
}
Result:
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!
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);
}
Ok, so basically I have a simple table with a QWidget and two buttons as shown below:
QGridLayout *layout = new QGridLayout;
layout->addWidget(viewcontainer,0,0,1,2);
layout->addWidget(reset,1,0);
layout->addWidget(done,1,1);
This is basically what I want, where "reset" and "done" are buttons. Essentially it's a QWidget, viewcontainer, which resizes as the window size is changed by the user while the buttons' heights remains the same. But, the default for the gridlayout is to align the contents to the left. If I change this with:
layout->addWidget(viewcontainer,0,0,1,2, Qt::AlignCenter);
It does sort of what I want, but the graphicsscene no longer resizes (remains a small constant size). I'd like to retain the resizing while just aligning the widget to the center. Thanks.
I think the easiest solution which provides a clean solution is to nest 2 layouts.
Your 'outer' (parent) layout should be a QHBoxLayout and you can add your QGridLayout into it as an 'inner' (child) layout with addLayout().
Based on my experience you should avoid to set Qt::Alignment every time you can. It can really mess up your layout. For simple layouts it can work but for more complex ones you should avoid it. And you never know that you should extend your layout in the future or not so my suggestion is to use nested layouts.
Of course you can create a QWidget for the 'outer' layout and for the 'innser' layout as well but most of the times it should be fine to just nest 2 layouts.
Also you can use QSpacerItem to fine-tune your layout.
Have a look at this example code, I think it does what you want:
#include <QApplication>
#include <QPushButton>
#include <QGraphicsView>
#include <QGridLayout>
#include <QPalette>
class MyWidget : public QWidget
{
public:
MyWidget()
{
QGridLayout * layout = new QGridLayout(this);
QGraphicsView * gv = new QGraphicsView;
layout->addWidget(gv, 0,0, 1,2);
layout->setRowStretch(0, 1); // make the top row get more space than the second row
layout->addWidget(new QPushButton("reset"), 1,0);
layout->addWidget(new QPushButton("done"), 1,1);
}
};
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
MyWidget w;
w.show();
return app.exec();
}
Consider following simple example:
Area.hh
#pragma once
class Area;
#include <QScrollArea>
class Area : public QScrollArea {
Q_OBJECT
public:
Area (QWidget *_parent = 0);
};
Area.cc
#include "main.hh"
#include "Area.hh"
#include <QLabel>
Area::Area (QWidget *_parent) :
QScrollArea (_parent)
{
QLabel *label = new QLabel ("Show me please");
setWidget (label);
}
This scroll area should show a label inside it. And it does so well if you just create an Area object and show it like this:
Area *area = new Area();
area->show();
However, if you add a QScrollArea with Qt Creator and promote it to Area class, then it shows nothing inside and there are no scrollbars. What can I do to show it properly?
Qt Designer adds an empty widget inside the QScrollArea, overwriting yours.
To prevent that, use a base QWidget instead of a QScrollArea, and promote that widget to an Area class. Qt's Ui compiler won't considered it to be a QScrollArea, so it won't generate a call to setWidget anymore.