Повернуть определенные QWidgets в Qt

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

Я попытался использовать QGraphicsProxyWidget и Graphics View, это хорошо работало, если я хотел повернуть весь пользовательский интерфейс, но не давал возможности повернуть только определенные элементы.

Я попытался повысить виджет до пользовательского класса, который переопределяет paintEvent. Я не могу заставить этот способ работать, хотя. Ниже приведен мой текущий код

#include <QPushButton>
#include <QPainter>
#include <QPaintEvent>    

class QRotateButton : public QPushButton
{
    Q_OBJECT
public:
    QRotateButton (QWidget *parent = 0) {}

protected:
    void paintEvent(QPaintEvent *event) override {
        if (isDrawing)
            return QRotateButton::paintEvent(event);

        isDrawing = true;
        QPixmap buttonPixmap = grab();
        QPainter painter(this);
        painter.rotate(90);
        painter.drawPixmap(0, 0, height(), width(), buttonPixmap);
        isDrawing = false;
    }
private:
    bool isDrawing = false;
};

Это возвращает следующие ошибки

QWidget::repaint: Recursive repaint detected
Segmentation fault

заранее спасибо

Обновить

Следуя комментариям ниже, я изменил код для вызова QPushButton::paintEvent(Event) вместо QRotateButton::paintEvent(Event).

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

QWidget::repaint: Recursive repaint detected
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::rotate: Painter not active

person Alex Spicer    schedule 02.08.2017    source источник
comment
Что касается Recursive repaint detected - не используйте QRotateButton::paintEvent(event);, звоните QPushButton::paintEvent(event);. Вы столкнетесь с рекурсией, когда isDrawing истинно. Иначе что должен делать этот метод? Почему вы хотите вызывать базовый метод только тогда, когда isDrawing истинно?   -  person Rudolfs Bundulis    schedule 02.08.2017
comment
@RudolfsBundulis Идея переменной isDrawing заключается в том, что функция захвата() снова вызывает paintEvent. Если у меня этого нет, метод зацикливается, пока не столкнется с ошибкой сегментации. Я хочу вызвать базовый метод, чтобы получить растровое изображение кнопки, а затем повернуть его.   -  person Alex Spicer    schedule 02.08.2017
comment
Как вызвать paintEvent, если вы не передаете ему событие? А также решает ли это исправление вызова базового класса?   -  person Rudolfs Bundulis    schedule 02.08.2017
comment
@RudolfsBundulis Я считаю, что grab() создает новое событие из-за того, что требует рисования изображения, чтобы получить его растровое изображение. Но я не уверен, так как это функция QPushButton, а не моя. Я обновил основной вопрос новой проблемой, появившейся после исправления вызова базового класса. Спасибо за помощь   -  person Alex Spicer    schedule 02.08.2017


Ответы (1)


Поиграв с этим дальше, мне удалось понять это.

#include <QPushButton>
#include <QPainter>
#include <QPaintEvent>

class QRotateButton : public QPushButton
{
    Q_OBJECT
public:    
    QRotateButton (QWidget *) {        
        //Set variables to defaults
        _isDrawing = false;
        _recursiveCounter = 0;
        _recursiveMax = 1;
        _rotationSetting = 0;
        _rotationAngle = 0;
        _rotationX = 0;
        _rotationY = 0;
        _rotationWidth = 0;
        _rotationHeight = 0;
    }

    /* Set rotation setting */
    void SetRotation(int setting) {
        //Set rotation setting
        _rotationSetting = setting;

        //Call paintEvent
        this->update();
    }


protected:
    void paintEvent(QPaintEvent *event) override {        
        //If its drawing then it just wants to do the normal paintEvent
        if (_isDrawing)
        {
            QPushButton::paintEvent(event);
            return;
        }       

        //Setup the variables depending on setting
        setupRotationVariables();       

        //Setup painter and rotate to angle
        QPainter painter(this);
        painter.rotate(_rotationAngle);

        //Without this here, this function is called over and over,
        //this stops that whilst maintaing the rotated image we want
        if (_recursiveCounter > 0 && _requiresRotation)
        {
            _recursiveCounter--;
            painter.drawPixmap(_rotationX, _rotationY, _drawing.width(), _drawing.height(), _drawing);            
            return;
        }


        //When rotating 90/270 degrees, we resize the image so that we don't stretch the image
        //This is not required when flipping the image
        if (_requiresRotation)                    
            resize(_rotationWidth, _rotationHeight);



        //Get the button image, this will call this function so we set _isDrawing to true whilst it's going on so meaning that it's does the base method and returns
        _isDrawing = true;
        _drawing = grab();
        _isDrawing = false;

        //Now we have the image, if rotation is being done resize the widget back to it's previous measurements
        if (_requiresRotation)                   
            resize(_rotationHeight, _rotationWidth);

        //Draw the image to the picture
        painter.drawPixmap(_rotationX, _rotationY, _drawing.width(), _drawing.height(), _drawing);

        //Depending on if this has rotated or not, setup the catch the tailend of the rotation event
        if (_requiresRotation)
            _recursiveCounter = _recursiveMax;
        else
            _recursiveCounter = 0;
    }

private:
    /* Stops Recursive resizing when resizing is required */
    int _recursiveCounter;
    int _recursiveMax;

    /* If the widget is currently drawing, stops certain recursive calls */
    bool _isDrawing;    

    /* If the widget needs to rotated instead of flipped */
    bool _requiresRotation;

    /* Contains image of previous render of button, for use in recursive calls */
    QPixmap _drawing;

    /* Rotation Setting (0-3) defines rotation type. This is used since rotations should only be multiples of 90 */
    int _rotationSetting;

    /* Variables after rotation */
    int _rotationAngle;
    int _rotationX;
    int _rotationY;
    int _rotationWidth;
    int _rotationHeight;

    /* Fetch variables based on Rotation Setting */
    void setupRotationVariables () {
        //Figure out what rotation is required
        switch (_rotationSetting)
        {
        case 0: //Up side up
            _rotationAngle = 0;
            _rotationWidth = width();
            _rotationHeight = height();
            _rotationX = 0;
            _rotationY = 0;
            _requiresRotation = false;
            break;
        case 1: //Right side up
            _rotationAngle = 90;
            _rotationWidth = height();
            _rotationHeight = width();
            _rotationX = 0;
            _rotationY = -width();
            _requiresRotation = true;
            break;
        case 2: //Down side up
            _rotationAngle = 180;
            _rotationWidth = width();
            _rotationHeight = height();
            _rotationX = -width();
            _rotationY = -height();
            _requiresRotation = false;
            break;
        case 3: //Left side up
            _rotationAngle = 270;
            _rotationWidth = height();
            _rotationHeight = width();
            _rotationX = -height();
            _rotationY = 0;
            _requiresRotation = true;
            break;
        default:
            printf("INVALID ROTATION\n");
            break;
        }
    }

};

Примечание

Это по-прежнему будет вызывать QWidget::repaint: Recursive repaint detected каждый раз, когда вызывается paintEvent, но мы ожидаем этого, и это обрабатывается в коде. Хотя я бы предпочел, чтобы это не показывалось, я не могу придумать, как это остановить.

person Alex Spicer    schedule 08.08.2017