QScrollArea - изменение размера виджетов содержимого с сохранением соотношения сторон - PullRequest
0 голосов
/ 16 июня 2020

У меня есть макет, который выглядит следующим образом.

Где:

Синий: прямоугольник, это ScrollArea

Оранжевый: прямоугольники - это виджеты из этой ScrollArea

enter image description here


Мой код:

#include <QtWidgets>
///////////////////////////////////////////////////////////////////////////////////////

class RoundedPolygon : public QPolygon {
public:
    RoundedPolygon() { SetRadius(10); }

    void SetRadius(unsigned int iRadius) { m_iRadius = iRadius; }

    const QPainterPath &GetPath() {
        m_path = QPainterPath();

        if (count() < 3) {
            qDebug() << "!! Polygon should have at least 3 points !!";
            return m_path;
        }

        QPointF pt1;
        QPointF pt2;
        for (int i = 0; i < count(); i++) {
            pt1 = GetLineStart(i);

            if (i == 0)
                m_path.moveTo(pt1);
            else
                m_path.quadTo(at(i), pt1);

            pt2 = GetLineEnd(i);
            m_path.lineTo(pt2);
        }

        // close the last corner
        pt1 = GetLineStart(0);
        m_path.quadTo(at(0), pt1);

        return m_path;
    }

private:
    QPointF GetLineStart(int i) const {
        QPointF pt;
        QPoint pt1 = at(i);
        QPoint pt2 = at((i + 1) % count());
        float fRat = m_iRadius / GetDistance(pt1, pt2);
        if (fRat > 0.5f)
            fRat = 0.5f;

        pt.setX((1.0f - fRat) * pt1.x() + fRat * pt2.x());
        pt.setY((1.0f - fRat) * pt1.y() + fRat * pt2.y());
        return pt;
    }

    QPointF GetLineEnd(int i) const {
        QPointF pt;
        QPoint pt1 = at(i);
        QPoint pt2 = at((i + 1) % count());
        float fRat = m_iRadius / GetDistance(pt1, pt2);
        if (fRat > 0.5f)
            fRat = 0.5f;
        pt.setX(fRat * pt1.x() + (1.0f - fRat) * pt2.x());
        pt.setY(fRat * pt1.y() + (1.0f - fRat) * pt2.y());
        return pt;
    }

    float GetDistance(QPoint pt1, QPoint pt2) const {
        int fD = (pt1.x() - pt2.x()) * (pt1.x() - pt2.x()) + (pt1.y() - pt2.y()) * (pt1.y() - pt2.y());
        return sqrtf(fD);
    }

private:
    QPainterPath m_path;
    unsigned int m_iRadius{};
};

class PolygonButtonWidget : public QWidget {
Q_OBJECT
public:
    explicit PolygonButtonWidget(QWidget *parent = nullptr) : QWidget(parent) {}

    ~PolygonButtonWidget() override = default;

protected:
    void resizeEvent(QResizeEvent *event) override {
        float ratioW = 8;
        float ratioH = 3;
//        ui->scrollAreaWidgetContents->setFixedSize(5000, h);
        float thisAspectRatio = (float) event->size().width() / event->size().height();
        if (thisAspectRatio < ratioW / ratioH) {
            float w = event->size().height() * ratioW / ratioH;
            float h = event->size().height();
            qDebug() << hasHeightForWidth() << " " << w << " " << h;
            this->resize(w, h);
            if (m_nrButtons != 0) {
                this->move((w + 20) * m_nrButtons, this->y());
            }
        }

        QWidget::resizeEvent(event);
    }

    int m_nrButtons{};
public:
    void setMNrButtons(int mNrButtons) {
        m_nrButtons = mNrButtons;
    }

protected:
    void paintEvent(QPaintEvent *event) override {
        int offset = 50;

        m_polygon.clear();
        m_polygon.emplace_back(0, height()); //DOWN-LEFT
        m_polygon.emplace_back(width() - offset, height()); //DOWN-RIGHT
        m_polygon.emplace_back(width(), 0); //TOP-RIGHT
        m_polygon.emplace_back(0 + offset, 0);


        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        RoundedPolygon poly;
        poly.SetRadius(15);
        for (QPoint point: m_polygon) {
            poly << point;
        }

        QBrush fillBrush;
        fillBrush.setColor(Qt::darkBlue);
        fillBrush.setStyle(Qt::SolidPattern);

        QPainterPath path;
        path.addPath(poly.GetPath());

        painter.fillPath(path, fillBrush);
    }

    void mousePressEvent(QMouseEvent *event) override {
        auto cursorPos = mapFromGlobal(QCursor::pos());
        qDebug() << "X: " << cursorPos.x() << " Y: " << cursorPos.y();

        inside(cursorPos, m_polygon);
        qDebug() << "Pressed";
    }

private:
    std::vector<QPoint> m_polygon;

    bool inside(QPoint point, std::vector<QPoint> polygon) {
        auto x = point.x();
        auto y = point.y();

        auto inside = false;
        auto i = 0;
        auto j = polygon.size() - 1;
        while (i < polygon.size()) {
            auto xi = polygon[i].x();
            auto yi = polygon[i].y();
            auto xj = polygon[j].x();
            auto yj = polygon[j].y();

            auto intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
            if (intersect) inside = !inside;

            j = i++;
        }
        qDebug() << inside;

        return inside;
    }
};

///////////////////////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QWidget root;
    QHBoxLayout layout{&root};

    for (int i = 0; i < 10; ++i) {
        auto p = new PolygonButtonWidget();
        p->setMinimumSize(100, 100);
        p->setMNrButtons(i);
        layout.addWidget(p);
    }
    root.setStyleSheet("background-color: rgb(19,19,19);");
    QScrollArea view;
    view.setWidget(&root);
    view.show();
    app.exec();
}

#include "main.moc"

Проблема возникает, когда я Пытаюсь изменить размер окна. В момент изменения размера я хочу, чтобы мои виджеты сохранили свое соотношение сторон. Но этого не произойдет.

У меня есть прокручиваемый список виджетов, который выглядит так (если он слишком сильно затрачен на X) enter image description here

Если Я масштабирую его по оси Y, он будет выглядеть так.

enter image description here

После того, как я изменил resizeEvent, теперь он будет выглядеть примерно так. это

enter image description here

или вот так

enter image description here

Как исправить этот? По какой-то причине некоторые из моих виджетов исчезнут, как мне следует решить эту проблему?

1 Ответ

1 голос
/ 17 июня 2020

Проблема вызвана предположением, что существует какой-либо механизм, который автоматически изменяет размер виджетов за вас. Нет. QScrollArea действует как барьер макета, и любые макеты внутри него изолированы от его размера и, следовательно, от любых событий изменения размера.

Вы должны изменить размер виджета контейнера (тот, у которого синий набросайте схему на диаграмме) каждый раз, когда область прокрутки меняет размер, и вам сначала нужно подготовить тестовый пример для виджетов, чтобы их изменения размера правильно управлялись при размещении в выбранном вами макете, а указанный макет изменял размер.

Наконец, моя любимая мозоль: маловероятно, что QMainWindow вам действительно понадобится для чего-нибудь. Это просто глупый шаблон Qt Creator. Но если вам не нужен интерфейс MDI и стыковка, вам не следует использовать QMainWindow - и особенно при создании автономного примера. Все, что вам нужно здесь, это QScrollArea как виджет верхнего уровня. Буквально все. Любой QWidget может быть окном верхнего уровня!

Для будущих отправок предоставьте весь необходимый код в одном файле main.cpp, который начинается с #include <QtWidgets> и заканчивается на #include "main.moc". Вам не понадобятся какие-либо другие включения для классов Qt, и вы можете писать определения классов в стиле Java со всеми методами, определенными в самом объявлении класса. Это обеспечивает сокращенный код - в конце концов, вопрос SO - это не корпоративный проект. Он должен быть минимальным, а это действительно означает, что все ненужное нужно удалить. Нет необходимости в файлах заголовков, множественных включениях и прочей ерунде - то есть используйте контейнеры Qt вместо C ++ STL, чтобы вам не требовалось больше включений et c.

Ваш пример должен выглядеть примерно так:

#include <QtWidgets>

class PolygonButtonWidget : public QAbstractButton {
  Q_OBJECT
  /* without seeing the code here, your question is unanswerable */
};

int main(int argc, char* argv[]) {
  QApplication app(argc, argv);
  QWidget root;
  QHBoxLayout layout{&root};
  PolygonButtonWidget buttons[10];
  for (auto &button : buttons)
    layout.addWidget(&button);
  QScrollArea view;
  view.setWidget(&root);
  view.show();
  app.exec();
  view.takeWidget();
}

#include "main.moc"

Без такого примера на ваш вопрос сложно ответить, так как:

  1. Как мы можем отладить его? Отладка означает использование отладчика. Если ваш код не может быть немедленно скомпилирован, то маловероятно, что кто-то потрудится отладить его, а отладка путем проверки часто подвержена ошибкам.

  2. Как мы можем предоставить проверенный ответ, если мы Придется ли сначала написать для него весь «тестовый пример»?

  3. Как мы можем узнать, что внутри вашего виджета-кнопки? Поведение этого виджета действительно влияет на окончательное решение.

Было бы также полезно, если бы вы описали несколько вариантов использования, которые, как вы ожидаете, будут работать. То есть смоделируйте (с помощью рисунка) состояние виджетов до и после изменения размера представления, чтобы мы могли легко увидеть, что именно должно произойти. Многие из них очень легко упустить, объясняя свои потребности словами. Варианты использования - это язык спецификаций программного обеспечения . Если вы их не используете, весьма вероятно, что вы сами не знаете, какое поведение вы ожидаете во всех случаях.

...