如何在PyQt中创建一个操纵杆/控制器小部件?

5
我想创建一个类似于这个的摇杆小部件。

enter image description here

我的当前实现使用 QToolButton() 来表示侧边箭头,但我不确定如何创建中间的圆圈。当用户单击中间点并将其拖向箭头时,应该会注册移动。我考虑使用 paintEvent()drawEclipse() 或者甚至 QDial(),但我不确定如何做到这一点。

enter image description here

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_()

1个回答

7

enter image description here

有时,从头开始会更容易。
如果您使用 QWidget::mousePressEvent()QWidget::mouseReleaseEvent()QWidget::mouseMoveEvent()QWidget::paintEvent(),您将能够处理您的游戏手柄。
使用 QWidget::paintEvent() 在窗口小部件中心绘制游戏手柄。
当用户按下鼠标按钮时,将调用 QWidget::mousePressEvent()。您可以使用它来启动游戏手柄的移动。
当用户释放鼠标按钮时,将调用 QWidget::mouseReleaseEvent()。使用它重置游戏手柄。
当鼠标移动时,将调用 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”错误。 - Sid133
1
QWidget has been moved in another module since Qt5. Add the line from PyQt5.QtWidgets import QWidget - Dimitry Ernot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接