【问题标题】:How to create a joystick/controller widget in PyQt?如何在 PyQt 中创建操纵杆/控制器小部件?
【发布时间】:2019-04-30 07:57:33
【问题描述】:

我想创建一个类似这样的操纵杆小部件

我当前的实现使用QToolButton() 作为侧箭头,但我不确定如何在中间创建圆圈。当用户单击中间点并将其拖向箭头时,它应该记录该移动。我正在考虑使用paintEvent()drawEclipse() 甚至QDial(),但我不知道该怎么做。

from PyQt4 import QtCore, QtGui
import sys

class JoystickWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(JoystickWidget, self).__init__(parent)

        self.field_joystick_up_button = QtGui.QToolButton()
        self.field_joystick_up_button.setArrowType(QtCore.Qt.UpArrow)
        self.field_joystick_up_button.clicked.connect(self.joystick_up)
        self.field_joystick_up_button.setFixedWidth(75)
        self.field_joystick_down_button = QtGui.QToolButton()
        self.field_joystick_down_button.setArrowType(QtCore.Qt.DownArrow)
        self.field_joystick_down_button.clicked.connect(self.joystick_down)
        self.field_joystick_down_button.setFixedWidth(75)
        self.field_joystick_right_button = QtGui.QToolButton()
        self.field_joystick_right_button.setArrowType(QtCore.Qt.RightArrow)
        self.field_joystick_right_button.clicked.connect(self.joystick_right)
        self.field_joystick_right_button.setFixedWidth(75)
        self.field_joystick_left_button = QtGui.QToolButton()
        self.field_joystick_left_button.setArrowType(QtCore.Qt.LeftArrow)
        self.field_joystick_left_button.clicked.connect(self.joystick_left)
        self.field_joystick_left_button.setFixedWidth(75)

        self.joystick_layout = QtGui.QVBoxLayout()
        self.joystick_layout.addWidget(self.field_joystick_up_button,alignment=QtCore.Qt.AlignCenter)
        self.joystick_layout_row = QtGui.QHBoxLayout()
        self.joystick_layout_row.addWidget(self.field_joystick_left_button)
        self.joystick_layout_row.addWidget(self.field_joystick_right_button)
        self.joystick_layout.addLayout(self.joystick_layout_row)
        self.joystick_layout.addWidget(self.field_joystick_down_button,alignment=QtCore.Qt.AlignCenter)

    def get_joystick_layout(self):
        return self.joystick_layout
    def joystick_up(self):
        print("Up")
    def joystick_down(self):
        print("Down")
    def joystick_right(self):
        print("Right")
    def joystick_left(self):
        print("Left")

if __name__ == '__main__':
    # Create main application window
    app = QtGui.QApplication([])
    app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
    mw = QtGui.QMainWindow()
    mw.setWindowTitle('Joystick example')

    # Create and set widget layout
    # Main widget container
    cw = QtGui.QWidget()
    ml = QtGui.QGridLayout()
    cw.setLayout(ml)
    mw.setCentralWidget(cw)

    # Create joystick 
    joystick = JoystickWidget()

    ml.addLayout(joystick.get_joystick_layout(),0,0)

    mw.show()

    ## Start Qt event loop unless running in interactive mode or using pyside.
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

【问题讨论】:

    标签: python pyqt pyqt4


    【解决方案1】:

    有时,从头开始会更容易。

    如果您使用QWidget::mousePressEvent()QWidget::mouseReleaseEvent()QWidget::mouseMoveEvent()QWidget::paintEvent(),您将能够控制操纵杆。​​p>

    使用QWidget::paintEvent() 在小部件中心绘制操纵杆。​​p>

    QWidget::mousePressEvent() 将在用户按下鼠标按钮时被调用。您可以使用它来启动操纵杆的移动。

    QWidget::mouseReleaseEvent() 在用户释放鼠标按钮时被调用。用它来重置操纵杆。​​p>

    QWidget::mouseMoveEvent() 被称为鼠标在移动。使用它来计算操纵杆的偏移量和方向(上、左、下或右)。如果您想要某种模拟摇杆,您还可以使用中心与摇杆之间的距离来获得介于 0(不移动)和 1(最大)之间的数字。

    例如:

    from PyQt4.QtGui import *
    from PyQt4.QtCore import *
    import sys
    from enum import Enum
    
    class Direction(Enum):
        Left = 0
        Right = 1
        Up = 2
        Down = 3
    
    class Joystick(QWidget):
        def __init__(self, parent=None):
            super(Joystick, self).__init__(parent)
            self.setMinimumSize(100, 100)
            self.movingOffset = QPointF(0, 0)
            self.grabCenter = False
            self.__maxDistance = 50
    
        def paintEvent(self, event):
            painter = QPainter(self)
            bounds = QRectF(-self.__maxDistance, -self.__maxDistance, self.__maxDistance * 2, self.__maxDistance * 2).translated(self._center())
            painter.drawEllipse(bounds)
            painter.setBrush(Qt.black)
            painter.drawEllipse(self._centerEllipse())
    
        def _centerEllipse(self):
            if self.grabCenter:
                return QRectF(-20, -20, 40, 40).translated(self.movingOffset)
            return QRectF(-20, -20, 40, 40).translated(self._center())
    
        def _center(self):
            return QPointF(self.width()/2, self.height()/2)
    
    
        def _boundJoystick(self, point):
            limitLine = QLineF(self._center(), point)
            if (limitLine.length() > self.__maxDistance):
                limitLine.setLength(self.__maxDistance)
            return limitLine.p2()
    
        def joystickDirection(self):
            if not self.grabCenter:
                return 0
            normVector = QLineF(self._center(), self.movingOffset)
            currentDistance = normVector.length()
            angle = normVector.angle()
    
            distance = min(currentDistance / self.__maxDistance, 1.0)
            if 45 <= angle < 135:
                return (Direction.Up, distance)
            elif 135 <= angle < 225:
                return (Direction.Left, distance)
            elif 225 <= angle < 315:
                return (Direction.Down, distance)
            return (Direction.Right, distance)
    
    
        def mousePressEvent(self, ev):
            self.grabCenter = self._centerEllipse().contains(ev.pos())
            return super().mousePressEvent(ev)
    
        def mouseReleaseEvent(self, event):
            self.grabCenter = False
            self.movingOffset = QPointF(0, 0)
            self.update()
    
        def mouseMoveEvent(self, event):
            if self.grabCenter:
                print("Moving")
                self.movingOffset = self._boundJoystick(event.pos())
                self.update()
            print(self.joystickDirection())
    
    if __name__ == '__main__':
        # Create main application window
        app = QApplication([])
        app.setStyle(QStyleFactory.create("Cleanlooks"))
        mw = QMainWindow()
        mw.setWindowTitle('Joystick example')
    
        # Create and set widget layout
        # Main widget container
        cw = QWidget()
        ml = QGridLayout()
        cw.setLayout(ml)
        mw.setCentralWidget(cw)
    
        # Create joystick 
        joystick = Joystick()
    
        # ml.addLayout(joystick.get_joystick_layout(),0,0)
        ml.addWidget(joystick,0,0)
    
        mw.show()
    
        ## Start Qt event loop unless running in interactive mode or using pyside.
        if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
            QApplication.instance().exec_()
    

    您可以使用相同的逻辑来创建按钮:为按钮定义四个区域并检查鼠标何时在这些区域中按下。

    【讨论】:

    • 在尝试使用 PyQt5 重新创建相同内容时遇到错误 NameError: name 'QWidget' is not defined
    • QWidget 自 Qt5 以来已移至另一个模块。添加行from PyQt5.QtWidgets import QWidget
    猜你喜欢
    • 2016-03-07
    • 1970-01-01
    • 2018-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-14
    • 1970-01-01
    相关资源
    最近更新 更多