在滚动区域内放置可调整大小的小部件

时间:2019-03-22 23:30:17

标签: c++ qt qt5

我有一个滚动区域,其中包含可调整大小的窗口小部件。用户可以增加和减少此内部小部件的整数比例因子(这会改变其大小)。当用户增加比例时,内部窗口小部件在左上角可见的部分保持固定。这样可以放大视图的左上角。

当内部窗口小部件的大小更改时,我需要滚动内部窗口小部件,以使其看起来像用户正在放大视图的中心。我想在调整大小时将窗口小部件的一部分固定在视图的中心。

我绘制了一些图表以帮助可视化问题。粉色矩形是内部小部件。棕色矩形是通过滚动区域看到的小部件的视图。绿点是内部小部件上的固定点。

Before scaling 缩放之前

After scaling (current undesirable behaviour) 缩放后(当前不良行为)

After scaling (desired behaviour) 缩放后(期望的行为)

您可以(希望)从这些粗略的图中看到。只需增加滚动区域内小部件的大小,即可放大到左上角或视图。我必须做更多的事情才能放大视图的中心。而且,内部窗口小部件可以比滚动区域小得多。我只想在内部小部件大于滚动区域时移动它。

这是不良行为的最小示例。按下Z键(单击内部小部件以更改焦点后)将放大视图的左上角。我想放大视图的中心。

#include <QtGui/qpainter.h>
#include <QtWidgets/qscrollbar.h>
#include <QtWidgets/qscrollarea.h>
#include <QtWidgets/qmainwindow.h>
#include <QtWidgets/qapplication.h>

class InnerWidget final : public QWidget {
public:
  explicit InnerWidget(QScrollArea *parent)
    : QWidget{parent}, parent{parent} {
    updateSize();
    setFocusPolicy(Qt::StrongFocus);
  }

private:
  QScrollArea *parent;
  int scale = 1;

  void updateSize() {
    setFixedSize(256 * scale, 256 * scale);
  }

  void paintEvent(QPaintEvent *) override {
    QPainter painter{this};
    const QColor green = {0, 255, 0};
    painter.fillRect(0, 0, width(), height(), {255, 255, 255});
    painter.fillRect(32 * scale, 32 * scale, 16 * scale, 16 * scale, green);
    painter.fillRect(128 * scale, 128 * scale, 16 * scale, 16 * scale, green);
  }

  void keyPressEvent(QKeyEvent *event) override {
    if (event->isAutoRepeat()) return;
    QScrollBar *hbar = parent->horizontalScrollBar();
    QScrollBar *vbar = parent->verticalScrollBar();
    if (event->key() == Qt::Key_Z) {
      // need to call bar->setValue and bar->value here
      scale = std::min(scale + 1, 64);
      updateSize();
    } else if (event->key() == Qt::Key_X) {
      // here too
      scale = std::max(scale - 1, 1);
      updateSize();
    }
  }
};

int main(int argc, char **argv) {
  QApplication app{argc, argv};
  QMainWindow window;
  QScrollArea scrollArea{&window};
  InnerWidget inner{&scrollArea};
  window.setBaseSize(512, 512);
  window.setCentralWidget(&scrollArea);
  scrollArea.setAlignment(Qt::AlignCenter);
  scrollArea.setWidget(&inner);
  window.show();
  return app.exec();
}

要重现该问题,请放大几次,然后将一个矩形放置在窗口中心。缩放会将矩形移向右下角。我想缩放以将矩形保持在中心。

这似乎是一个简单的问题,但我似乎无法理解数学。我已经尝试过对滚动值进行各种计算,但它们似乎都没有达到我想要的效果。

1 个答案:

答案 0 :(得分:0)

我把数字弄乱了一段时间,然后才知道了。这是完整的解决方案。

#include <QtGui/qpainter.h>
#include <QtWidgets/qscrollbar.h>
#include <QtWidgets/qmainwindow.h>
#include <QtWidgets/qscrollarea.h>
#include <QtWidgets/qapplication.h>

class InnerWidget final : public QWidget {
public:
  explicit InnerWidget(QScrollArea *parent)
    : QWidget{parent}, parent{parent}, scale{1} {
    updateSize();
    setFocusPolicy(Qt::StrongFocus);
  }

private:
  QScrollArea *parent;
  int scale;

  void updateSize() {
    setFixedSize(256 * scale, 256 * scale);
  }

  void paintEvent(QPaintEvent *) override {
    QPainter painter{this};
    const QColor green = {0, 255, 0};
    painter.fillRect(0, 0, width(), height(), {255, 255, 255});
    painter.fillRect(32 * scale, 32 * scale, 16 * scale, 16 * scale, green);
    painter.fillRect(128 * scale, 128 * scale, 16 * scale, 16 * scale, green);
  }

  void adjustScroll(const int oldScale) {
    if (scale == oldScale) return;
    QScrollBar *hbar = parent->horizontalScrollBar();
    QScrollBar *vbar = parent->verticalScrollBar();
    if (width() >= parent->width()) {
      const int halfWidth = parent->width() / 2;
      hbar->setValue((hbar->value() + halfWidth) * scale / oldScale - halfWidth);
    }
    if (height() >= parent->height()) {
      const int halfHeight = parent->height() / 2;
      vbar->setValue((vbar->value() + halfHeight) * scale / oldScale - halfHeight);
    }
  }

  void keyPressEvent(QKeyEvent *event) override {
    if (event->isAutoRepeat()) return;
    const int oldScale = scale;
    if (event->key() == Qt::Key_Z) {
      scale = std::min(scale + 1, 64);
      updateSize();
      adjustScroll(oldScale);
    } else if (event->key() == Qt::Key_X) {
      scale = std::max(scale - 1, 1);
      updateSize();
      adjustScroll(oldScale);
    }
  }
};

int main(int argc, char **argv) {
  QApplication app{argc, argv};
  QMainWindow window;
  QScrollArea scrollArea{&window};
  InnerWidget inner{&scrollArea};
  window.setBaseSize(512, 512);
  window.setCentralWidget(&scrollArea);
  scrollArea.setAlignment(Qt::AlignCenter);
  scrollArea.setWidget(&inner);
  window.show();
  return app.exec();
}