问题
In this code,which change the QGraphicsView
background.Now I need to change the background when I check true checkBox
.When I set to checkBox
to check true
I need to set the background like this code.When I set checkBox
to check false
. I need to set QGraphicsView
for normal default way. How can I do this.
here is my code:
mainwindow.cpp
#include "mainwindow.h"
#include <QGraphicsTextItem>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
scene = new Scene(this);
scene->setSceneRect(10,10,260,200);
view = new QGraphicsView(scene);
setCentralWidget(view);
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsView>
#include "scene.h"
#include "customrectitem.h"
class MainWindow : public QMainWindow
{
public:
explicit MainWindow(QWidget *parent = 0);
private:
QGraphicsView* view;
QGraphicsScene* scene;
};
#endif // MAINWINDOW_H
Scene.h
#ifndef SCENE_H
#define SCENE_H
#include <QGraphicsScene>
#include <QPainter>
#include <QApplication>
class Scene : public QGraphicsScene
{
Q_OBJECT
public:
explicit Scene(QObject *parent = 0);
int getGridSize() const {return this->gridSize;}
protected:
void drawBackground (QPainter* painter, const QRectF &rect);
private:
int gridSize;
};
#endif // SCENE_H
Scene.cpp
#include "scene.h"
Scene::Scene(QObject *parent) : QGraphicsScene(parent), gridSize(20)
{
Q_ASSERT(gridSize > 0);
}
void Scene::drawBackground(QPainter *painter, const QRectF &rect)
{
QColor c (10,140,255,155);
painter->setPen(c);
qreal left = int(rect.left()) - (int(rect.left()) % gridSize);
qreal top = int(rect.top()) - (int(rect.top()) % gridSize);
QVarLengthArray<QLineF,100> lines;
for (qreal x = left; x < rect.right(); x += gridSize)
lines.append(QLineF(x,rect.top(),x,rect.bottom()));
for (qreal y = top; y < rect.bottom(); y += gridSize)
lines.append(QLineF(rect.left(),y,rect.right(),y));
painter->drawLines(lines.data(),lines.size());
}
回答1:
Although @Scheff's solution is good, it has the following problems:
the first solution makes it only cover a small space, if you change the size of QGraphicsView it will not cover all the space.
The second solution has the problem that you are using the
viewport()
coordinate, and not the scene, if you add an item and then enlarge the QGraphicsView without moving the item, then you see a scroll.
Initial:
After changing the size:
A solution that corrects both errors is to overwrite the drawBackground method of QGraphicsScene:
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QPainter>
class GraphicsScene: public QGraphicsScene{
int mGridSize;
QColor mGridColor;
bool mGridVisible;
Q_OBJECT
public:
GraphicsScene(QObject *parent = Q_NULLPTR):QGraphicsScene(parent),
mGridSize(20), mGridColor(0x0a8affu), mGridVisible(true)
{
}
bool gridVisible() const {
return mGridVisible;
}
void setGridVisible(bool gridVisible){
if(mGridVisible != gridVisible){
mGridVisible = gridVisible;
update();
}
}
protected:
void drawBackground(QPainter *painter, const QRectF &rect){
if(mGridVisible) {
QRect r = rect.toRect();
int xmin =r.left() - r.left()%mGridSize - mGridSize;
int ymin = r.top() - r.top()%mGridSize - mGridSize;
int xmax = r.right() - r.right()%mGridSize + mGridSize;
int ymax = r.bottom() - r.bottom()%mGridSize + mGridSize;
for(int x=xmin ; x <= xmax; x += mGridSize ){
painter->drawLine(x, r.top(), x, r.bottom());
}
for(int y=ymin ; y <= ymax; y+= mGridSize ){
painter->drawLine(r.left(), y, r.right(), y);
}
}
}
};
#endif // GRAPHICSSCENE_H
The advantage of this solution is that it does not load any new object, besides that the graph is in the coordinates of the scene, so there is no displacement of the second solution.
Initial:
After changing the size:
The complete example can be found at the following link
回答2:
I have two opportunities in mind to draw a grid:
deriving a class from
QGraphicsView
and overloading thepaintEvent()
and drawing the grid before falling back toQGraphicsView::paintEvent()
adding the grid lines as scene items under a
QGraphicsItemGroup
which allows control of visibility.
The first method has the advantage that it can seamlessly adapt to any resizing of viewport.
This is my sample code testQGraphicsView-BgGrid.cc
:
#include <QtWidgets>
class Canvas: public QGraphicsView {
private:
int _gridSize;
QColor _gridColor;
bool _gridVisible;
public:
explicit Canvas(QWidget *pQParent = nullptr):
QGraphicsView(pQParent),
_gridSize(20), _gridColor(0x0a8affu), _gridVisible(true)
{ }
bool gridVisible() const { return _gridVisible; }
void setGridVisible(bool gridVisible)
{
_gridVisible = gridVisible;
viewport()->update();
}
protected:
virtual void paintEvent(QPaintEvent *pQEvent) override
{
QPainter qPainter(viewport());
if (_gridVisible) {
const int wView = viewport()->width(), hView = viewport()->height();
qPainter.setPen(_gridColor);
for (int x = _gridSize / 2; x < wView; x += _gridSize) {
qPainter.drawLine(x, 0, x, hView - 1);
}
for (int y = _gridSize / 2; y < hView; y += _gridSize) {
qPainter.drawLine(0, y, wView - 1, y);
}
}
QGraphicsView::paintEvent(pQEvent);
}
};
void makeGrid(
QGraphicsItemGroup &qItemGrp, const QSize &size,
int gridSize = 20, const QColor &gridColor = 0x0a8affu)
{
const int wView = size.width(), hView = size.height();
for (int x = gridSize / 2; x < wView; x += gridSize) {
QGraphicsLineItem *pQItem = new QGraphicsLineItem(x, 0, x, hView - 1);
pQItem->setPen(gridColor);
qItemGrp.addToGroup(pQItem);
}
for (int y = gridSize / 2; y < hView; y += gridSize) {
QGraphicsLineItem *pQItem = new QGraphicsLineItem(0, y, wView - 1, y);
pQItem->setPen(gridColor);
qItemGrp.addToGroup(pQItem);
}
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
// setup GUI
QWidget qWnd;
QGridLayout qGrid;
QLabel qLblL(QString::fromUtf8("Grid as part of scene"));
qGrid.addWidget(&qLblL, 0, 0);
QCheckBox qTglGridL(QString::fromUtf8("Show Grid"));
qGrid.addWidget(&qTglGridL, 0, 1);
QGraphicsView qGraphView;
qGrid.addWidget(&qGraphView, 1, 0, 1, 2);
QLabel qLblR(QString::fromUtf8("Grid painted as background"));
qGrid.addWidget(&qLblR, 0, 2);
QCheckBox qTglGridR(QString::fromUtf8("Show Grid"));
qGrid.addWidget(&qTglGridR, 0, 3);
Canvas canvas;
qGrid.addWidget(&canvas, 1, 2, 1, 2);
qWnd.setLayout(&qGrid);
qWnd.show();
// init GUI
QGraphicsScene qGraphSceneL;
QGraphicsItemGroup qItemGrid;
makeGrid(qItemGrid, qGraphView.viewport()->size());
qGraphSceneL.addItem(&qItemGrid);
qGraphView.setScene(&qGraphSceneL);
qTglGridL.setCheckState(
qItemGrid.isVisible() ? Qt::Checked : Qt::Unchecked);
qTglGridR.setCheckState(
canvas.gridVisible() ? Qt::Checked : Qt::Unchecked);
// install signal handlers
QObject::connect(&qTglGridL, &QCheckBox::stateChanged,
[&qItemGrid](int state)
{
qItemGrid.setVisible(state != Qt::Unchecked);
});
QObject::connect(&qTglGridR, &QCheckBox::stateChanged,
[&canvas](int state)
{
canvas.setGridVisible(state != Qt::Unchecked);
});
// runtime loop
return app.exec();
}
Compiled with VS2013 and Qt 5.9.2, tested on Window 10 (64 bit):
Update:
The QMake script testQGraphicsView-BgGrid.pro
:
SOURCES = testQGraphicsView-BgGrid.cc
QT += widgets
Building and testing in bash
on cygwin:
$ qmake-qt5 testQGraphicsView-BgGrid.pro
$ make
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQGraphicsView-BgGrid.o testQGraphicsView-BgGrid.cc
g++ -o testQGraphicsView-BgGrid.exe testQGraphicsView-BgGrid.o -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread
$ ./testQGraphicsView-BgGrid
The same program as X11 application:
Yet Another Update:
I've to admit that, in my first version, I focused on How can I change QGraphicsView background using check box with emphasis on check box ignoring QGraphicsScene::drawBackground() (which I didn't know) and ignoring issues in the grid rendering itself (which I used instead). As pointed out in eyllanesc's answer, both showed solutions offer some weaknesses which (at least) I should have documented.
The left view (which involves the grid into scene) doesn't necessarily fill the
viewport()
. IMHO, this is acceptable if the grid fills the relevant range which is used by scene. (It might be intended to visualize that the scene covers only a part of theviewport()
.) However, the argumentconst QSize &size
ofmakeGrid()
was an unlucky choice concerning this intention. I fixed this design issue by replacing it withconst QRect &rect
.The right view (which overloads
paintEvent()
) didn't consider possible scrolling of scene. I fixed this by mapping the upper left corner to scene coordinates (const QPointF offs = mapToScene(0, 0);
) and consider the offset as start value in the loops.
The update sample code testQGrapicsView-BgGrid.cc
:
#include <QtWidgets>
class Canvas: public QGraphicsView {
private:
int _gridSize;
QColor _gridColor;
bool _gridVisible;
public:
explicit Canvas(QWidget *pQParent = nullptr):
QGraphicsView(pQParent),
_gridSize(20), _gridColor(0x0a8affu), _gridVisible(true)
{ }
bool gridVisible() const { return _gridVisible; }
void setGridVisible(bool gridVisible)
{
_gridVisible = gridVisible;
viewport()->update();
}
protected:
virtual void paintEvent(QPaintEvent *pQEvent) override
{
QPainter qPainter(viewport());
if (_gridVisible) {
const int wView = viewport()->width(), hView = viewport()->height();
const QPointF offs = mapToScene(0, 0);
qPainter.setPen(_gridColor);
for (int x = (int)offs.x() % _gridSize; x < wView; x += _gridSize) {
qPainter.drawLine(x, 0, x, hView - 1);
}
for (int y = (int)offs.y() % _gridSize; y < hView; y += _gridSize) {
qPainter.drawLine(0, y, wView - 1, y);
}
}
QGraphicsView::paintEvent(pQEvent);
}
};
void makeGrid(
QGraphicsItemGroup &qItemGrp, const QRect &rect,
int gridSize = 20, const QColor &gridColor = 0x0a8affu)
{
for (int x = rect.x(), xE = x + rect.width(); x < xE; x += gridSize) {
QGraphicsLineItem *pQItem
= new QGraphicsLineItem(x, rect.y(), x, rect.height() - 1);
pQItem->setPen(gridColor);
qItemGrp.addToGroup(pQItem);
}
for (int y = rect.y(), yE = y + rect.height(); y < yE; y += gridSize) {
QGraphicsLineItem *pQItem
= new QGraphicsLineItem(rect.x(), y, rect.width() - 1, y);
pQItem->setPen(gridColor);
qItemGrp.addToGroup(pQItem);
}
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
// setup GUI
QWidget qWnd;
QGridLayout qGrid;
QLabel qLblL(QString::fromUtf8("Grid as part of scene"));
qGrid.addWidget(&qLblL, 0, 0);
QCheckBox qTglGridL(QString::fromUtf8("Show Grid"));
qGrid.addWidget(&qTglGridL, 0, 1);
QGraphicsView qGraphView;
qGrid.addWidget(&qGraphView, 1, 0, 1, 2);
QLabel qLblR(QString::fromUtf8("Grid painted as background"));
qGrid.addWidget(&qLblR, 0, 2);
QCheckBox qTglGridR(QString::fromUtf8("Show Grid"));
qGrid.addWidget(&qTglGridR, 0, 3);
Canvas canvas;
qGrid.addWidget(&canvas, 1, 2, 1, 2);
qWnd.setLayout(&qGrid);
qWnd.show();
// init GUI
QGraphicsScene qGraphSceneL;
QGraphicsItemGroup qItemGrid;
makeGrid(qItemGrid, QRect(0, 0, 320, 240));
qGraphSceneL.addItem(&qItemGrid);
qGraphView.setScene(&qGraphSceneL);
qTglGridL.setCheckState(
qItemGrid.isVisible() ? Qt::Checked : Qt::Unchecked);
qTglGridR.setCheckState(
canvas.gridVisible() ? Qt::Checked : Qt::Unchecked);
// install signal handlers
QObject::connect(&qTglGridL, &QCheckBox::stateChanged,
[&qItemGrid](int state)
{
qItemGrid.setVisible(state != Qt::Unchecked);
});
QObject::connect(&qTglGridR, &QCheckBox::stateChanged,
[&canvas](int state)
{
canvas.setGridVisible(state != Qt::Unchecked);
});
// runtime loop
return app.exec();
}
The following snapshots show the application after start and after resizing:
来源:https://stackoverflow.com/questions/47728237/how-can-i-change-qgraphicsview-background-using-check-box