I've a problem in drawing a border/frame with QGraphicsView and QGraphicsScene. I'd like to create a frame with the same distance for all borders to QGraphicsView border.
I've written some code, but I've obtained a partial result; I was able to create borders for only two sides.
I've tested a bit and the rectangle that I've created has vertices coordinates {0, 0} and {100, 100}, while the scene rect starts from {-10, -10} and goes to {110, 110}, so I don't understand why the rect is not centered since fitInView should cover the entire scene rect.
What I'm doing wrong?
This is the code:
PlotAxes.hpp
#ifndef PLOTAXES_HPP_
#define PLOTAXES_HPP_
#include <QGraphicsObject>
#include <QPen>
#include <QPainter>
class PlotAxes : public QGraphicsObject {
Q_OBJECT
public:
PlotAxes() = delete;
PlotAxes(QGraphicsItem* parent = nullptr) : QGraphicsObject(parent) {}
PlotAxes(const PlotAxes&) = delete;
PlotAxes(PlotAxes&&) = delete;
~PlotAxes() = default;
void setLimits(const QPointF& minXY, const QPointF& maxXY) {
m_minXY = minXY;
m_maxXY = maxXY;
}
void setLimits(const QRectF& r) {
m_minXY = r.topLeft();
m_maxXY = r.bottomRight();
}
public:
QRectF boundingRect() const override {
return QRectF(
m_minXY.x(),
m_minXY.y(),
m_maxXY.x(),
m_maxXY.y()
);
}
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override {
painter->save();
QPen axisPen;
axisPen.setColor(QColor(Qt::GlobalColor::darkRed));
axisPen.setCosmetic(true);
painter->setPen(axisPen);
painter->drawRect(boundingRect());
painter->restore();
}
public:
PlotAxes& operator=(const PlotAxes&) = delete;
PlotAxes& operator=(PlotAxes&&) = delete;
private:
QPointF m_minXY;
QPointF m_maxXY;
};
#endif // !PLOTAXES_HPP_
Plot.hpp
#ifndef PLOT_HPP_
#define PLOT_HPP_
#include "PlotAxes.hpp"
#include <QWidget>
#include <QPointF>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QVBoxLayout>
class Plot : public QWidget {
Q_OBJECT
public:
Plot() = delete;
Plot(QWidget* parent = nullptr) :
QWidget(parent) {
createWidgets();
createLayout();
setMinimumSize(400, 400);
}
Plot(const Plot&) = delete;
Plot(Plot&&) = delete;
~Plot() = default;
void setLimits(const QPointF& minXY, const QPointF& maxXY) {
constexpr double BorderPercentage{ 0.1 };
m_width = maxXY.x() - minXY.x();
m_height = maxXY.y() - minXY.y();
const double originX = minXY.x() - m_width * BorderPercentage;
const double width = (1.0 + BorderPercentage) * m_width;
const double originY = minXY.y() - m_height * BorderPercentage;
const double height = (1.0 + BorderPercentage) * m_height;
m_originX = minXY.x();
m_originY = minXY.y();
m_axes->setLimits(QRectF(m_originX, m_originY, m_width, m_height));
m_scene.setSceneRect(originX, originY, width, height);
auto sr = m_scene.sceneRect();
m_view->fitInView(sr);
}
public:
Plot& operator=(const Plot&) = delete;
Plot& operator=(Plot&&) = delete;
private:
void createWidgets() {
m_view = new QGraphicsView(&m_scene, this);
QRect r = QRect(0, 0, m_view->width(), m_view->height());
m_scene.setSceneRect(r);
m_view->fitInView(m_scene.sceneRect());
m_axes = new PlotAxes(nullptr);
m_scene.addItem(m_axes);
}
void createLayout() {
auto layout = new QVBoxLayout(this);
layout->addWidget(m_view);
}
private:
QGraphicsView* m_view;
QGraphicsScene m_scene;
PlotAxes* m_axes;
double m_originX{ 0.0 };
double m_width{ 0.0 };
double m_originY{ 0.0 };
double m_height{ 0.0 };
};
#endif // !PLOT_HPP_
main.cpp
#include <iostream>
#include <QWidget>
#include <QVBoxLayout>
#include <QApplication>
#include "PlotAxes.hpp"
#include "Plot.hpp"
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
QWidget* w = new QWidget();
Plot* p = new Plot(w);
QVBoxLayout* layout = new QVBoxLayout(w);
layout->addWidget(p);
w->show();
p->setLimits({ 0.0, 0.0 }, { 100.0, 100.0 });
return a.exec();
}
And this is the result:
As you can see the frame it's not centered. How can I fix it?
Related
I am having issues with a weird bug which occurs after you adjust the size of a QGraphicsItem.
Here is a YouTube video showing the issue: https://youtu.be/gp1lQTkPf54
In my application, a slider is used to adjust the horizontal zoom of all the regions on the QGraphicsScene. I have made sure to call prepareGeometryChange(); when I am changing the geometry of a region and call update(); but that has not helped. It seems to affect regions that are being rendered out of view from the user.
Code:
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsScene *scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
RegionGraphicsItem *rgi = new RegionGraphicsItem(ui->graphicsView->scene());
itemList.append(rgi);
ui->graphicsView->scene()->addItem(rgi);
rgi->setHScaleFactor(ui->horizontalSlider->value());
}
void MainWindow::test() {
QWidget test;
test.show();
}
void MainWindow::on_horizontalSlider_valueChanged(int value)
{
for (int i = 0; i < itemList.size(); i++) {
itemList[i]->setHScaleFactor(value);
}
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "regiongraphicsitem.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QList<RegionGraphicsItem*> itemList;
private slots:
void on_pushButton_clicked();
void on_horizontalSlider_valueChanged(int value);
private:
void test();
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
regiongraphicsitem.cpp
#include "regiongraphicsitem.h"
RegionGraphicsItem::RegionGraphicsItem(QGraphicsScene *_scene) : QGraphicsItem()
{
regionColor = QColor::fromRgb(255,255,255);
setFlags(ItemIsMovable);
waveFormColor = regionColor.darker(80);
outlineColor = QColor("#0f0f0f");
selectedColor = selectedColor.lighter(30);
hScaleFactor = 100;
mainBrush = QBrush(regionColor);
mainPen = QPen(outlineColor, 1);
gridLength = 5;
height = 56;
oldPos = pos();
scene = _scene;
this->prepareGeometryChange();
gridLocation = 5;
setY((0 * 60) + 1);
}
QRectF RegionGraphicsItem::boundingRect() const
{
return QRectF(0, 0, float(gridLength * hScaleFactor), float(height));
}
void RegionGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
QRectF rect = boundingRect();
painter->setBrush(mainBrush);
painter->setPen(mainPen);
painter->drawRoundedRect(rect, 5, 5);
painter->setPen(mainPen);
if (selected) {
painter->setBrush(QBrush(mainPen.color()));
painter->drawRoundedRect(QRect(rect.x(), rect.y(), rect.width(), 20), 5, 5);
painter->drawRect(QRect(rect.x(), rect.y() + 5, rect.width(), 15));
}
QFont font = scene->font();
font.setPixelSize(10);
font.setBold(true);
QFontMetricsF fontMetrics(font);
QString text = "TEST";
int heightFont = fontMetrics.boundingRect(text).height();
if (selected) {
painter->setPen(QPen(mainBrush.color(), 1));
}
painter->drawText(5, heightFont + 3, text);
if (pressed == false ) {
setX((gridLocation - 1) * hScaleFactor);
}
}
void RegionGraphicsItem::setHScaleFactor(int value) {
prepareGeometryChange();
hScaleFactor = value;
update();
}
regiongraphicsitem.h
#ifndef RegionGraphicsItem_H
#define RegionGraphicsItem_H
#include <QGraphicsItem>
#include <QColor>
#include <QBrush>
#include <QPen>
#include <QtGui/QPainter>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <stdint.h>
class RegionGraphicsItem : public QGraphicsItem
{
public:
RegionGraphicsItem(QGraphicsScene *_scene);
float getGridLocation();
void setHScaleFactor(int value);
protected:
QColor outlineColor;
QColor selectedColor;
QColor regionColor;
QColor waveFormColor;
bool selected;
int penWidth;
int rounded;
QBrush mainBrush;
QBrush waveformBrush;
QPen mainPen;
int height;
float gridLength;
bool pressed = false;
QPointF oldPos, oldMousePos;
int oldTrackIndex;
float gridLocation;
QGraphicsScene *scene;
int oldHScaleFactor;
virtual QRectF boundingRect() const override;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
private:
int hScaleFactor;
};
#endif // RegionGraphicsItem_H
Steps to reproduce:
Add a RegionGraphicItem to the QGraphicsScene
Increase the slider until the region is off screen
Decrease the slider and the region will not be visible. <-- This is the issue.
I have a problem regarding the the non correct resize of the QDockWidget. Specifically when I launch the GUI, the QDockWidget appears like in the image below which is wrong. Also I adjust the size of the QDockWidget during the use of the .ui, however as soon as I interact with the .ui (e.g. using a QPushButton or using a QCheckBox) the QDockWidget gets bigger again:
The expected behavior is the one below, which it does not increase dimension abruptly during the interaction with the .ui but rather remains in position as below:
Below is the most important part of the code I am using for this project and I signed the 3 debugging errors notified by the compiler with // <-- ERROR HERE if that can be useful:
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mDockWidget_A = new QDockWidget(QLatin1String("Command Log"));
mDockWidget_A->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
mNewText = new QPlainTextEdit;
mNewText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mDockWidget_A->setWidget(mNewText);
mDockWidget_A->installEventFilter(new QDockResizeEventFilter(mNewText,dynamic_cast<QFluidGridLayout*>(mNewText->layout())));
addDockWidget(Qt::BottomDockWidgetArea, mDockWidget_A);
}
qdockresizeeventfilter.h
#include <QObject>
#include <QLayout>
#include <QEvent>
#include <QDockWidget>
#include <QResizeEvent>
#include <QCoreApplication>
#include <QMouseEvent>
#include "qfluidgridlayout.h"
#include "mainwindow.h"
class QDockResizeEventFilter : public QObject
{
public:
friend QMainWindow;
friend QLayoutPrivate;
QDockResizeEventFilter(QWidget* dockChild, QFluidGridLayout* layout, QObject* parent = nullptr)
: QObject(parent), m_dockChild(dockChild), m_layout(layout)
{
}
protected:
bool eventFilter(QObject *p_obj, QEvent *p_event)
{
if (p_event->type() == QEvent::Resize)
{
QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(p_event);
QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(p_obj->parent());
QDockWidget* dock = static_cast<QDockWidget*>(p_obj);
// determine resize direction
if (resizeEvent->oldSize().height() != resizeEvent->size().height())
{
// vertical expansion
QSize fixedSize(m_layout->widthForHeight(m_dockChild->size().height()), m_dockChild->size().height()); // <-- ERROR HERE
if (dock->size().width() != fixedSize.width())
{
m_dockChild->setFixedWidth(fixedSize.width());
dock->setFixedWidth(fixedSize.width());
// cause mainWindow dock layout recalculation
QDockWidget* dummy = new QDockWidget;
mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dummy);
mainWindow->removeDockWidget(dummy);
// adding dock widgets causes the separator move event to end
// restart it by synthesizing a mouse press event
QPoint mousePos = mainWindow->mapFromGlobal(QCursor::pos());
mousePos.setY(dock->rect().bottom());
QCursor::setPos(mainWindow->mapToGlobal(mousePos));
QMouseEvent* grabSeparatorEvent = new QMouseEvent(QMouseEvent::MouseButtonPress,mousePos,Qt::LeftButton,Qt::LeftButton,Qt::NoModifier);
qApp->postEvent(mainWindow, grabSeparatorEvent);
}
}
if (resizeEvent->oldSize().width() != resizeEvent->size().width())
{
// Do nothing
}
}
return false;
}
private:
QWidget* m_dockChild;
QFluidGridLayout* m_layout;
};
#endif // QDockResizeEventFilter_h_
and finally qfluidgridlayout.h
#ifndef QFluidGridLayout_h_
#define QFluidGridLayout_h_
#include <QLayout>
#include <QGridLayout>
#include <QRect>
#include <QStyle>
#include <QWidgetItem>
class QFluidGridLayout : public QLayout
{
public:
enum Direction { downToUp, UpToDown };
QFluidGridLayout(QWidget *parent = nullptr)
: QLayout(parent)
{
setContentsMargins(8,8,8,8);
setSizeConstraint(QLayout::SetMinAndMaxSize);
}
~QFluidGridLayout() {
QLayoutItem *item;
while ((item = takeAt(0)))
delete item;
}
void addItem(QLayoutItem *item) {
itemList.append(item);
}
Qt::Orientations expandingDirections() const {
return nullptr;
}
bool hasHeightForWidth() const {
return false;
}
int heightForWidth(int width) const {
int height = doLayout(QRect(0, 0, width, 0), true, true);
return height;
}
bool hasWidthForHeight() const {
return true;
}
int widthForHeight(int height) const { // <-- ERROR HERE
int width = doLayout(QRect(0, 0, 0, height), true, false);
return width;
}
int count() const {
return itemList.size();
}
QLayoutItem *itemAt(int index) const {
return itemList.value(index);
}
QSize minimumSize() const {
QSize size;
QLayoutItem *item;
foreach (item, itemList)
size = size.expandedTo(item->minimumSize());
size += QSize(2*margin(), 2*margin());
return size;
}
void setGeometry(const QRect &rect) {
QLayout::setGeometry(rect);
doLayout(rect);
}
QSize sizeHint() const {
return minimumSize();
}
QLayoutItem *takeAt(int index) {
if (index >= 0 && index < itemList.size())
return itemList.takeAt(index);
else
return nullptr; }
private:
int doLayout(const QRect &rect, bool testOnly = false, bool width = false) const
{
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom); // <-- ERROR HERE
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
int lineWidth = 0;
QLayoutItem* item;
foreach(item,itemList)
{
QWidget* widget = item->widget();
if (y + item->sizeHint().height() > effectiveRect.bottom() && lineWidth > 0) {
y = effectiveRect.y();
x += lineWidth + right;
lineWidth = 0;
}
if (!testOnly) {
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
}
y += item->sizeHint().height() + top;
lineHeight = qMax(lineHeight, item->sizeHint().height());
lineWidth = qMax(lineWidth, item->sizeHint().width());
}
if (width) {
return y + lineHeight - rect.y() + bottom;
}
else {
return x + lineWidth - rect.x() + right;
}
}
QList<QLayoutItem *> itemList;
Direction dir;
};
#endif // QFluidGridLayout_h_
I have been reading about this issue frequently here and in this post. However I have been reading about the possibility that this specific object may have some bugs and it was advised to overwrite a resiveEvent. However none of this worked.
I finally, after doing a good amount of research, found this useful post which almost replicates the problem I have and that carries the majority of the two classes above class QFluidGridLayout and class QDockResizeEventFilter.
Although I am using the same approach I am still not able to achieve a normal behavior of this object.
I am also including a snapshot of the debugger:
Can someone explain what am I doing wrong? Thanks so much for shedding light on this issue.
#Emanuele, The post you saw it is mainly for sub-classing the QDockWidget as a child and therefore that solution had to be implemented manually. I think that if you look at this alternative solution you will find it useful.
Try to modify on your constructor adding resizeDocks({dock}, {100}, Qt::Horizontal); as in the post so to have:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mDockWidget_A = new QDockWidget(QLatin1String("Command Log"));
mDockWidget_A->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
mNewText = new QPlainTextEdit;
mNewText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mDockWidget_A->setWidget(mNewText);
mDockWidget_A->installEventFilter(new QDockResizeEventFilter(mNewText,dynamic_cast<QFluidGridLayout*>(mNewText->layout())));
addDockWidget(Qt::BottomDockWidgetArea, mDockWidget_A);
resizeDocks({mDockWidget_A}, {100}, Qt::Horizontal);
}
I do not understand why the CodeEditor example from the Qt website does not appear to work as expected. Every time I run the code it displays it like this, really small and not expanding to take up all the available space. Does anyone have any idea why? I have even tried to set a fixed size, sizepolicy and minimum sizing. Not sure what I am missing here.
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QVBoxLayout>
#include <QLabel>
#include "codeeditor.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
// controls
auto *widget_main = new QWidget(this);
auto *lay_main = new QVBoxLayout(widget_main);
auto *label = new QLabel("Test");
auto *editor = new CodeEditor();
// layout
lay_main->addWidget(label);
lay_main->addWidget(editor);
lay_main->setContentsMargins(5, 5, 5, 5);
}
MainWindow::~MainWindow()
{
}
codeeditor.h:
#ifndef CODEEDITOR_H
#define CODEEDITOR_H
#include <QPlainTextEdit>
#include <QObject>
class QPaintEvent;
class QResizeEvent;
class QSize;
class QWidget;
class LineNumberArea;
// Main text editor
class CodeEditor : public QPlainTextEdit
{
Q_OBJECT
public:
CodeEditor(QWidget *parent = 0);
void lineNumberAreaPaintEvent(QPaintEvent *event);
int lineNumberAreaWidth();
protected:
void resizeEvent(QResizeEvent *event) override;
private slots:
void updateLineNumberAreaWidth(int newBlockCount);
void highlightCurrentLine();
void updateLineNumberArea(const QRect &, int);
private:
QWidget *lineNumberArea;
};
// Line number gutter
class LineNumberArea : public QWidget
{
public:
LineNumberArea(CodeEditor *editor) : QWidget(editor) {
codeEditor = editor;
}
QSize sizeHint() const override {
return QSize(codeEditor->lineNumberAreaWidth(), 0);
}
protected:
void paintEvent(QPaintEvent *event) override {
codeEditor->lineNumberAreaPaintEvent(event);
}
private:
CodeEditor *codeEditor;
};
#endif
codeeditor.cpp:
#include "codeeditor.h"
#include <QtWidgets>
#include <QFontMetrics>
CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
lineNumberArea = new LineNumberArea(this);
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
updateLineNumberAreaWidth(0);
highlightCurrentLine();
setFixedSize(200, 200);
setMinimumSize(200,200);
}
int CodeEditor::lineNumberAreaWidth()
{
int digits = 1;
int max = qMax(1, blockCount());
while (max >= 10) {
max /= 10;
++digits;
}
//int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
int space = 3 + 12 * digits;
return space;
}
void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */)
{
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}
void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
{
if (dy)
lineNumberArea->scroll(0, dy);
else
lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
if (rect.contains(viewport()->rect()))
updateLineNumberAreaWidth(0);
}
void CodeEditor::resizeEvent(QResizeEvent *e)
{
QPlainTextEdit::resizeEvent(e);
QRect cr = contentsRect();
lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}
void CodeEditor::highlightCurrentLine()
{
QList<QTextEdit::ExtraSelection> extraSelections;
if (!isReadOnly()) {
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(Qt::yellow).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = textCursor();
selection.cursor.clearSelection();
extraSelections.append(selection);
}
setExtraSelections(extraSelections);
}
void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
{
QPainter painter(lineNumberArea);
painter.fillRect(event->rect(), Qt::lightGray);
QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber();
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + (int) blockBoundingRect(block).height();
while (block.isValid() && top <= event->rect().bottom()) {
if (block.isVisible() && bottom >= event->rect().top()) {
QString number = QString::number(blockNumber + 1);
painter.setPen(Qt::black);
painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),
Qt::AlignRight, number);
}
block = block.next();
top = bottom;
bottom = top + (int) blockBoundingRect(block).height();
++blockNumber;
}
}
QMainWindow is a special widget since it has a preset layout
So you must set the main widget through setCentralWidget():
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto *widget_main = new QWidget;
auto *lay_main = new QVBoxLayout(widget_main);
auto *label = new QLabel("Test");
auto *editor = new CodeEditor();
setCentralWidget(widget_main); // <-- +++
// layout
lay_main->addWidget(label);
lay_main->addWidget(editor);
lay_main->setContentsMargins(5, 5, 5, 5);
}
On the other hand if you are going to use layouts then you should not set a fixed size to the widget, in your case remove setFixedSize(200, 200) on the other hand it is recommended that you make the connections with the new syntax:
CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
lineNumberArea = new LineNumberArea(this);
connect(this, &QPlainTextEdit::blockCountChanged, this, &CodeEditor::updateLineNumberAreaWidth);
connect(this, &QPlainTextEdit::updateRequest, this, &CodeEditor::updateLineNumberArea);
connect(this, &QPlainTextEdit::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
updateLineNumberAreaWidth(0);
highlightCurrentLine();
// setFixedSize(200, 200); <-- ---
setMinimumSize(200,200);
}
I have classes, ImageLabel, and ChoiceLabel. ChoiceLabel inherits from ImageLabel. When I try to make a new ChoiceLabel, I get an Unresolved External Symbol error. Here are the relevant files:
imagelabel.h
#ifndef IMAGELABEL_H
#define IMAGELABEL_H
#include <QLabel>
class QPixmap;
class QStringList;
class QFileDialog;
class QResizeEvent;
class ImageLabel : public QLabel
{
Q_OBJECT
public:
explicit ImageLabel(QWidget *parent = 0);
protected:
virtual int heightForWidth( int width ) const;
virtual QSize sizeHint() const;
QPixmap scaledPixmap() const;
void setPixmap ( const QPixmap & );
void resizeEvent(QResizeEvent *);
private:
QPixmap pix;
};
#endif // IMAGELABEL_H
choicelabel.h
#ifndef CHOICELABEL_H
#define CHOICELABEL_H
class QStringList;
class ChoiceLabel : public ImageLabel
{
Q_OBJECT
public:
ChoiceLabel();
private:
QStringList *imageFiles;
void mousePressEvent(QMouseEvent *);
void keyPressEvent(QKeyEvent *);
void focusInEvent(QFocusEvent *);
void focusOutEvent(QFocusEvent *);
};
#endif // CHOICELABEL_H
mainwindow.cpp
#include "mainwindow.h"
#include "imagelabel.h"
#include "choicelabel.h"
#include <QGridLayout>
#include <qDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
choiceLabelA = new ChoiceLabel; //the problem occurs here
resultLabel = new ImageLabel;
centralWidget = new QWidget(this);
this->setCentralWidget(centralWidget);
gridLayout = new QGridLayout(centralWidget);
gridLayout->addWidget(resultLabel);
}
EDIT:
imagelabel.cpp
#include "imagelabel.h"
#include <QDebug>
ImageLabel::ImageLabel(QWidget *parent) :
QLabel(parent)
{
setMinimumSize(250,250);
setAlignment(Qt::AlignCenter | Qt::AlignCenter);
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
QPixmap initialPixmap(250, 250);
initialPixmap.fill(Qt::black);
setPixmap(initialPixmap);
}
void ImageLabel::setPixmap ( const QPixmap & p)
{
pix = p;
QLabel::setPixmap(scaledPixmap());
}
QPixmap ImageLabel::scaledPixmap() const
{
return pix.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
int ImageLabel::heightForWidth( int width ) const
{
return pix.isNull() ? this->height() : ((qreal)pix.height()*width)/pix.width();
}
QSize ImageLabel::sizeHint() const
{
int w = this->width();
return QSize( w, heightForWidth(w) );
}
void ImageLabel::resizeEvent(QResizeEvent *)
{
if(!pix.isNull())
QLabel::setPixmap(scaledPixmap());
}
choicelabel.cpp
#include "choicelabel.h"
#include <QStringList>
#include <QFileDialog>
#include <QFrame>
ChoiceLabel::ChoiceLabel():
imageFiles()
{
setFocusPolicy(Qt::StrongFocus);
}
void ChoiceLabel::mousePressEvent(QMouseEvent *)
{
QFileDialog dialog(this);
dialog.setFileMode(QFileDialog::ExistingFiles);
dialog.setNameFilter("Images (*.png *.jpg)");
dialog.setDirectory("C:/Users/FrankFritz/Desktop/qt/uglypictures");
if (dialog.exec()){
imageFiles = dialog.selectedFiles();
QPixmap pixmap = imageFiles->first();
setPixmap(pixmap);
}
}
void ChoiceLabel::keyPressEvent(QKeyEvent *ev)
{
static int index(0);
if (ev->key() == Qt::Key_Right){
index = (index + 1) % imageFiles->length();
setPixmap( QPixmap(imageFiles->at(index)) );
}
if (ev->key() == Qt::Key_Left){
if (index == 0){
index = imageFiles->length() - 1;
}
else{
index = (index - 1) % imageFiles->length();
}
setPixmap( QPixmap(imageFiles->at(index)) );
}
}
void ChoiceLabel::focusInEvent(QFocusEvent *)
{
setFrameStyle(QFrame::StyledPanel);
}
void ChoiceLabel::focusOutEvent(QFocusEvent *)
{
setFrameStyle(QFrame::NoFrame);
}
Most probably
ChoiceLabel::ChoiceLabel():
imageFiles()
{
setFocusPolicy(Qt::StrongFocus);
}
ChoiceLabel::ChoiceLabel(QWidget *parent): ImageLabel(parent)
{
setFocusPolicy(Qt::StrongFocus);
}
Forgot to call the base class' constructor. Also forgot to specify a parent QWidget * for ChoiceLabel in its own constructor.
See What are the rules for calling the superclass constructor? for reference.
I create an application with Qt Scene Graph (Qt 5.6 on Ubuntu 16.04). It has about 12 instances of QSGNodes and calls update for them every 30ms.
I don't have any problem with updating one of them, but when i call update for all of them, cpu and ram usage began to increase. After some hours cpu usage will be near 100%. (under normal conditions cpu usage should be around 3-4).
Here is minimal version(Sorry for long code, It's simplest version that produces this bug):
main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "gui/diagramgraph.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include <QQuickView>
#include <QTimer>
#include "gui/diagramgraph.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qmlRegisterType<DiagramGraph>("DiagramGraph", 1, 0, "DiagramGraph");
QTimer *updateTimer = new QTimer(this);
for(int i = 1; i <= 12; i++){
QQuickView *view = new QQuickView();
view->setSource(QUrl("qrc:/ui/diagram.qml"));
view->setClearBeforeRendering(true);
QObject *object = view->rootObject();
DiagramGraph* graph = object->findChild<DiagramGraph *>("diagram");
QObject::connect(updateTimer, SIGNAL(timeout()), graph, SLOT(update()));
QWidget *container = QWidget::createWindowContainer(view, this);
ui->vetical->addWidget(container);
}
updateTimer->setTimerType(Qt::PreciseTimer);
updateTimer->start(30);
}
MainWindow::~MainWindow()
{
delete ui;
}
gui/diagramgraph.h
#ifndef DIAGRAMGRAPH_H
#define DIAGRAMGRAPH_H
#include <QQuickItem>
class DiagramGraph : public QQuickItem
{
Q_OBJECT
public:
DiagramGraph();
void init();
protected:
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
};
#endif // DIAGRAMGRAPH_H
gui/diagramgraph.cpp
#include "samples.h"
#include "diagramgraph.h"
class DiagramGraphNode : public QSGNode
{
public:
Samples * samples;
};
DiagramGraph::DiagramGraph()
{
setFlag(ItemHasContents, true);
}
QSGNode * DiagramGraph::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *){
DiagramGraphNode *n= static_cast<DiagramGraphNode *>(oldNode);
QRectF rect = boundingRect();
if (rect.isEmpty()) {
delete n;
return 0;
}
if (!n) {
n = new DiagramGraphNode();
n->samples = new Samples();
n->appendChildNode(n->samples);
}
n->samples->updateGeometry();
return n;
}
void DiagramGraph::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry){
update();
QQuickItem::geometryChanged(newGeometry, oldGeometry);
}
gui/samples.h
#ifndef SAMPLES_H
#define SAMPLES_H
#include <QSGGeometryNode>
class Samples : public QSGGeometryNode
{
public:
Samples();
void updateGeometry();
private:
QSGGeometry _geometry;
};
#endif // SAMPLES_H
gui/samples.cpp
#include "samples.h"
#include <QtGui/QColor>
#include <QtQuick/QSGSimpleMaterial>
struct LineMaterial
{
QColor color;
};
class LineShader : public QSGSimpleMaterialShader<LineMaterial>
{
QSG_DECLARE_SIMPLE_SHADER(LineShader, LineMaterial)
public:
LineShader() {
setShaderSourceFile(QOpenGLShader::Vertex, ":/shaders/shaders/samples.vsh");
setShaderSourceFile(QOpenGLShader::Fragment, ":/shaders/shaders/samples.fsh");
}
QList<QByteArray> attributes() const { return QList<QByteArray>() << "y" << "t"; }
void updateState(const LineMaterial *m, const LineMaterial *) {
program()->setUniformValue(id_color, m->color);
}
void resolveUniforms() {
id_color = program()->uniformLocation("color");
}
private:
int id_color;
};
struct Vertex {
float y;
float t;
inline void set(float yy, float tt) { y = yy; t = tt; }
};
static const QSGGeometry::AttributeSet &attributes()
{
static QSGGeometry::Attribute attr[] = {
QSGGeometry::Attribute::create(0, 1, GL_FLOAT, true),
QSGGeometry::Attribute::create(1, 1, GL_FLOAT),
};
static QSGGeometry::AttributeSet set = { 2, 2 * sizeof(float), attr };
return set;
}
Samples::Samples()
: _geometry(attributes(), 0){
setGeometry(&_geometry);
_geometry.setLineWidth(3);
_geometry.setDrawingMode(GL_LINES);
QSGSimpleMaterial<LineMaterial> *m = LineShader::createMaterial();
m->state()->color = QColor("red");
m->setFlag(QSGMaterial::Blending);
setMaterial(m);
setFlag(OwnsMaterial);
}
void Samples::updateGeometry()
{
_geometry.allocate(20);
Vertex *v = (Vertex *) _geometry.vertexData();
for (uint i=0; i < 20; i++){
v[i].set(.5, i);
}
}
mainwindow.ui
just a QVBoxLayout
diagram.qml
import QtQuick 2.0
import QtQuick.Controls 1.4
import DiagramGraph 1.0
Item{
id: diagram_main
DiagramGraph{
id: diagram_graph
objectName: "diagram"
anchors.left: diagram_main.left
height: 100
}
}
shaders/samples.fsh
uniform lowp vec4 color;
uniform lowp float qt_Opacity;
void main(void)
{
gl_FragColor = color * qt_Opacity;
}
shaders/samples.vsh
attribute highp float y;
attribute highp float t;
uniform highp mat4 qt_Matrix;
void main(void)
{
gl_Position = qt_Matrix * vec4(t, y * 100.f, 0.f, 1.f);
}