How can I change QGraphicsView background using check box

╄→гoц情女王★ 提交于 2019-12-10 21:04:54

问题


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:

  1. deriving a class from QGraphicsView and overloading the paintEvent() and drawing the grid before falling back to QGraphicsView::paintEvent()

  2. 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.

  1. 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 the viewport().) However, the argument const QSize &size of makeGrid() was an unlucky choice concerning this intention. I fixed this design issue by replacing it with const QRect &rect.

  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!