Динамические свойства Qt и Style Sheet

Помечено: , , ,

В этой теме 0 ответов, 1 участник, последнее обновление  Васильев Владимир Сергеевич 2 мес., 2 нед. назад.

  • Автор
    Сообщения
  • #5535
    @admin

    Одним из базовых механизмов Qt, наряду с сигналами и слотами, является система свойств. Вы можете определить свойства для своих собственных классов, подобно тому как это сделано для стандартных. Например, среди свойств стандартного класса QLineEdit есть следующие:

    Q_PROPERTY(QString selectedText READ selectedText)
    Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition)
    Q_PROPERTY(bool undoAvailable READ isUndoAvailable)

    Свойства — это поля класса, для которых могут быть определены методы установки (READ) и получения (WRITE) значения, сигнал об его изменении (NOTIFY). Также, для свойств могут быть заданы некоторые опции — видимость для Qt Designer, возможность исопльзования в Qt Script и еще некоторые полезные опции.

    Чаще всего, при разработке с Qt можно обойтись обычными полями классов, однако, если вы пишите повторно-используемый код или планируете исопльзование своего класса в QML — то вам придется использовать свойства.

    В качестве примера разберем класс нестандартной кнопки, хранящей свое состояние (нажата/нет). Если кнопка нажата — она подсвечивается красным цветом, иначе — зеленым. Для задания цвета будем использовать систему стилей Qt, прекласно описанную тут: Разработка игры на С++, Qt.

    Класс кнопки можно описать так:

    class PickedButton: public QPushButton {
     Q_OBJECT
     Q_PROPERTY(bool picked READ picked WRITE pick NOTIFY picked)
    public:
      PickedButton(QWidget *parent = nullptr);
    public slots:
      void pick(bool);
      bool picked();
    protected slots:
      void invert();
    signals:
      void picked(bool);
    private:
      bool m_picked;
    };

    Слот invert будет привязываться к сигналу clicked и выполнять вызов pick с инвертированным значением:

    void PickedButton::invert() {
      pick(!m_picked);
    }

    В конструкторе производится связывание этих сигнала и слота, а также задание стиля:

    PickedButton::PickedButton(QWidget *parent):
      QPushButton(parent), m_picked(false) {
    
      connect(this, SIGNAL(clicked()), SLOT(invert()));
      setStyleSheet(
        " QPushButton[picked=true] { background-color: rgba(255, 0, 0, 200);}"
        " QPushButton[picked=false] { background-color: rgba(0, 255, 0, 200);}"
      );
    }

    Обратите внимание, мы можем использовать свои собственные свойства при описании стиля. Это работаем за счет мета-объектного компилятора Qt — реализовать аналогичный механизм вручную крайне трудно.

    Остается добавить описание слотов для получения и записи значения:

    bool PickedButton::picked() {
      return m_picked;
    }
    
    void PickedButton::pick(bool is_picked) {
      if (m_picked == is_picked)
        return;
    
      m_picked = is_picked;
      style()->unpolish(this);
      style()->polish(this);
    
      emit picked(m_picked);
    }

    Вызовы функций unpolish и polish необходимы чтобы информировать систему о том, что какое-то из свойств изменилось и приводит к перерисовке (в данном случае изменению цвета). Чтобы не забыть вызвать эти функции рекомендуется все изменения проводить через функцию-сеттер. Она же должна следить за тем, на самом ли деле произошло изменение и вырабатывать сигнал (который отмечен NOTIFY).

    Исходный код, как всегда, можно взять в репозитории.

Для ответа в этой теме необходимо авторизоваться.