Рисуем свою кнопку в QGraphicsScene в PyQt4

Казалось бы, что может быть проще обычной кнопки с её стандартным поведением и привычным всему миру видом? Однако, сам процесс создания своей кнопки не менее интересен, чем создание целого приложения с помощью PyQt4.

Здесь приводится пример создания в QGraphicsScene своей рисованной кнопки с помощью QGraphicsWidget.

Рекомендуется к прочтению людям, имеющим опыт работы с PyQt4.

Для начала, разберёмся, что из себя представляет кнопка.
Обычная кнопка — это, чаще всего, прямоугольник с текстом и/или картинкой, щелчок мышью на который сопровождается визуальным откликом. Основное назначение кнопок — реагировать на простое событие нажатия и отжатия указателем мыши (или пальцем), находящегося в поле координат этой кнопки.

  1. # -*- coding: utf-8 -*-
  2. from PyQt4 import QtCore, QtGui
  3. import sys
  4.  
  5. class AButton(QtGui.QGraphicsWidget):
  6.     # нажата ли кнопка мыши
  7.     mouse_isPressed = False
  8.  
  9.     def __init__(self, parent = None):
  10.         QtGui.QGraphicsWidget.__init__(self)
  11.  
  12.     def boundingRect(self):
  13.         # размеры кнопки
  14.         # для наглядности возвращается фиксированное значение
  15.         # хотя можно считать размеры текста и иконки и
  16.         # использовать их динамически.
  17.         # QRectF, в отличие от QRect, позволяет оперировать
  18.         # числами с плавающей точкой.
  19.         return QtCore.QRectF(0, 0, 40, 40)
  20.  
  21.     def paint(self, painter, option, widget = 0):
  22.         # метод прорисовки кнопки со стилями
  23.         opt = QtGui.QStyleOptionButton()
  24.  
  25.         # стиль нажатой и отжатой кнопки в зависимости от того,
  26.         # нажата ли кнопка мыши
  27.         opt.state = ((QtGui.QStyle.State_Sunken if self.mouse_isPressed else QtGui.QStyle.State_Raised) | QtGui.QStyle.State_Enabled)
  28.         # текст на кнопке
  29.         opt.text = self.text()
  30.         # иконка кнопки
  31.         opt.icon = self.icon()
  32.         # геометрия
  33.         opt.rect = option.rect
  34.         # палитра для стиля
  35.         opt.palette = option.palette
  36.  
  37.         # сама прорисовка кнопки с определённым выше стилем и опциями
  38.         QtGui.QApplication.style().drawControl(QtGui.QStyle.CE_PushButton, opt, painter)
  39.  
  40.     def text(self):
  41.         # метод, возвращающий текст, отображаемый на кнопке
  42.         # для наглядности возвращаем фиксированное значение
  43.         return QtCore.QString("hi")
  44.  
  45.     def icon(self):
  46.         # метод, возвращающий иконку кнопки
  47.         # пока возвращаем пустую иконку
  48.         # вроде можно использовать QPixmap вместо QIcon
  49.         return QtGui.QIcon()
  50.  
  51.     def mousePressEvent(self, event):
  52.         # событие нажания кнопки мыши и обновление
  53.         # внешнего вида состояния кнопки
  54.         self.mouse_isPressed = True
  55.         self.update()
  56.  
  57.     def mouseReleaseEvent(self, event):
  58.         # отжатие кнопки
  59.         self.mouse_isPressed = False
  60.         # метод update - обязательный, он отвечает за перерисовку
  61.         # любого графического виджета
  62.         self.update()
  63.  
  64. if __name__ == "__main__":
  65.     app = QtGui.QApplication(sys.argv)
  66.  
  67.     # создаём графическую область, на которой будет рисоваться
  68.     # наша кнопка
  69.     scene = QtGui.QGraphicsScene()
  70.     # создаём кнопку
  71.     button = AButton()
  72.     # добавляем кнопку в графическую область
  73.     scene.addItem(button)
  74.  
  75.     # и создаём графическое поле, на которое накладывается
  76.     # графическая область с кнопкой
  77.     view = QtGui.QGraphicsView(scene)
  78.     # сглаживание
  79.     view.setRenderHint(QtGui.QPainter.Antialiasing)
  80.     # задаём размер графического поля
  81.     view.resize(200, 100)
  82.     # фон
  83.     view.setBackgroundBrush(QtGui.QApplication.palette().background())
  84.     view.show()
  85.  
  86.     sys.exit(app.exec_())



В моём случае, этот код исправно работает в PyQt 4.9.1 for Python 2.7.

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

Изначально, вышеописанный код был найден в интернете, написанный на C++, благополучно сохранён на компьютер, а источник утерян. Позже и сам код был переписан на язык Python с некоторыми особенностями, а оригинал удалён с компьютера.

Благодарю за внимание.
Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 22

    0
    А не проще сделать ещё один контейнер с, например, абсолютным расположением и положить туда QGraphicsScene и обычную кнопку?
      0
      Действительно непонятно зачем делать свой отдельный класс для контрола, который выглядит как обычная кнопка, если есть метод, который позволяет добавить виджет на сцену:
      qt-project.org/doc/qt-4.8/qgraphicsscene.html#addWidget
      И тогда получается одна строчка: scene.addItem(QtGui.QPushButton(«hi»))
      Ну, или как-то так, в Python не силён.

      Или я совсем не понял целей автора.
        0
        * scene.addWidget(QtGui.QPushButton(«hi»))
          0
          Например, кнопку можно «нарисовать» в виде сложной геометрической фигуры:
            0
            Например, такой формы:
              +1
              1) Для большинства нормальных кнопок хватает QPushButton::setStyleSheet()
              2) Для совсем странной формы — может быть проще сделать наследование (хотя я всё равно бы наследовал от QPushButton, дабы можно было добавить не обязательно на сцену). Но тогда вопрос, почему это не раскрыто? Самое интересное было бы как раз разобрать «когда» и «что» рисовать.
            0
            proxyWidget'ы вообще чаще всего как костыль выглядят.
              0
              Так Вы только за стандартные контролы и виджеты или спользование qml?
              А как же творчество и гибкость реализации?
                0
                Нарисовать стандартные контролы можно через QStyle вообще-то или даже напрямую через нативное API для системы а потом обернуть в qml, QtComponents Desktop так и сделаны.
                  0
                  На примере стандартного контрола можно сделать и свой нестандартный.
          +1
          Не проще все на qml сделать?
            0
            Разумеется, проще — для тех, кто с ним знаком. Причём, учитывая гибкость при использовании qml — можно сделать лучше.
              0
              Какова цель всего этого? Чем не подходит такой вариант?
                0
                Бывает, что не хочется быть сторонником утверждения, что «есть несколько способов сделать что-то, но только один из них правильный». Гибкость в реализации той или иной задачи — это выбор, который хочется иметь.
                +2
                Вопрос номер два, не проще ли с ним познакомится, чем заниматься странной хренью?
                  0
                  Не будьте так категоричны, ведь не все свежие и недавние «технологии» нужно срочно начинать использовать. QML ещё и трёх лет нет, да сорс я привёл на Python — о нём и речь. Кто-то может найти для себя в этом пользу, особенно, когда срочно необходимо найти пример реализации и воспользоваться им.
                  Считаю это логичным.
                0
                Добавить обычную кнопку в QGGraphicsScene — это одна строчка.
                0
                А PyQt шевелится что ли еще? Я уж думал все на PySide перебрались, особенно учитывая недавнюю новость о его включении в Qt Project.
                  0
                  Ничего не мешает в этом коде изменить одну строчку. Хотя, не исключены некоторые коллизии из-за различий между PySide и PyQt.
                    0
                    Ну и в PySide можно точно так:

                    import sys
                    
                    from PySide.QtGui import QApplication, QGraphicsScene, QGraphicsView, QPushButton
                    
                    app = QApplication( sys.argv )
                    
                    scene = QGraphicsScene()
                    view = QGraphicsView( scene )
                    
                    someButton = QPushButton( "button" )
                    scene.addWidget( someButton )
                    
                    someButton.clicked.connect( view.close )
                    
                    view.show()
                    
                    sys.exit( app.exec_() )


                    PS PyQt ещё нормально шевелится)
                      0
                      QGraphicsScene ведь позволяет рисовать объекты разной формы, не только ректанглы, да полигоны, да и контролы и виджеты не ограничиваются одними только кнопками, поэтому QGraphicsScene является отличным полем для применения интерактивности в приложении.

                  Only users with full accounts can post comments. Log in, please.