答案 0 :(得分:2)
如果使用鼠标事件,这种小部件很容易创建:
然后,使用paintEvent
将光标显示在其位置。
要定义光标的值,必须将像素位置转换为基于轴边界的值。
H值为-10至20且V值为-10至10的操纵杆的简单示例:
from PyQt4.QtGui import QWidget, QApplication, QPainter
from PyQt4 import QtCore
import sys
class Joystick(QWidget):
def __init__(self, parent=None):
super(Joystick, self).__init__(parent)
self.setFixedSize(100, 100)
self._minimumX = -10
self._maximumX = 20
self._minimumY = -10
self._maximumY = 10
self.cursorPosition = QtCore.QPointF(10, 90)
self.grabCursor = False
def valueX(self):
return (self.cursorPosition.x() - 10) * (self._maximumX - self._minimumX) / (self.width() - 20) + self._minimumX
def valueY(self):
return (self.cursorPosition.y() - 10) * (self._maximumY - self._minimumY) / (self.width() - 20) + self._minimumY
def paintEvent(self, event):
painter = QPainter(self)
painter.setBrush(QtCore.Qt.lightGray)
painter.setPen(QtCore.Qt.NoPen)
painter.drawRect(0, 0, self.width(), self.height())
painter.setBrush(QtCore.Qt.blue)
painter.drawEllipse(self.cursorRect())
def boundedCursor(self, position):
def bound(low, high, value):
return max(low, min(high, value))
x = bound(10, self.width() - 10, position.x())
y = bound(10, self.height() - 10, position.y())
return QtCore.QPointF(x, y)
def cursorRect(self):
return QtCore.QRectF(-5, -5, 10, 10).translated(self.cursorPosition)
def mousePressEvent(self, ev):
self.grabCursor = self.cursorRect().contains(ev.pos())
return super().mousePressEvent(ev)
def mouseReleaseEvent(self, event):
self.grabCursor = False
self.update()
def mouseMoveEvent(self, event):
if self.grabCursor:
print("Moving")
self.cursorPosition = self.boundedCursor(event.pos())
self.update()
print(self.valueX(), self.valueY())
if __name__ == '__main__':
# Create main application window
app = QApplication([])
joystick = Joystick()
joystick.show()
sys.exit(app.exec_())
答案 1 :(得分:0)
我根据罗姆·科列夫(Romha Korev)给出的answer进行了修改。
此示例允许设置不同的参数,并可以跟踪鼠标位置,无论您拥有什么数据和限制。它没有考虑任何固定的窗口小部件尺寸/范围(至少在我的测试中如此),但仍会根据窗口小部件尺寸进行调整。
class Bubble(QtWidgets.QWidget):
xChanged = QtCore.pyqtSignal(int)
yChanged = QtCore.pyqtSignal(int)
valuesChanged = QtCore.pyqtSignal(int, int)
moving = False
def __init__(self, minX=0, maxX=100, minY=0, maxY=100, cursorSize=10):
QtWidgets.QWidget.__init__(self)
self.margin = 10
self.setContentsMargins(0, 0, 0, 0)
self.minX = minX
self.minY = minY
self.maxX = maxX
self.maxY = maxY
self.extentX = abs(maxX - minX)
self.extentY = abs(maxY - minY)
self.setMinimumSize(self.extentX + self.margin * 2, self.extentY + self.margin * 2)
self.cursorSize = cursorSize
self.halfCursorSize = cursorSize * .5
self.cursorColor = QtGui.QColor(0, 174, 239)
self.movingCursorColor = QtGui.QColor(120, 124, 205)
self.cursorRect = QtCore.QRectF(-self.halfCursorSize, -self.halfCursorSize, cursorSize, cursorSize)
self.cursorX = self.minX + self.extentX * .5
self.cursorY = self.minY + self.extentY * .5
self.trackAnywhere = True
def visualPos(self):
# convert values to visual coordinates
return QtCore.QPointF(self.margin + self.halfCursorSize + ((self.cursorX - self.minX) / self.extentX) * (self.width() - self.margin * 2 - self.cursorSize),
self.margin + self.halfCursorSize + ((self.cursorY - self.minY) / self.extentY) * (self.height() - self.margin * 2 - self.cursorSize))
def realX(self, visualX):
visualExtent = self.width() - self.margin * 2 - self.cursorSize
realX = max(0, min(float(visualX - self.margin - self.halfCursorSize) / visualExtent, 1.)) * self.extentX
return self.minX + realX
def realY(self, visualY):
visualExtent = self.height() - self.margin * 2 - self.cursorSize
realY = max(0., min(float(visualY - self.margin - self.halfCursorSize) / visualExtent, 1.)) * self.extentY
return self.minY + realY
def realValues(self, pos):
return self.realX(pos.x()), self.realY(pos.y())
def mousePressEvent(self, event):
pos = event.pos()
visualPos = self.visualPos()
if self.trackAnywhere:
self.moving = True
if pos in self.cursorRect.translated(visualPos):
self.mouseDelta = visualPos - event.pos()
else:
self.mouseDelta = QtCore.QPoint()
elif pos in self.cursorRect.translated(visualPos):
self.moving = True
self.mouseDelta = visualPos - event.pos()
else:
self.moving = False
self.update()
def mouseMoveEvent(self, event):
if self.moving:
self.cursorX = self.realX(event.x() + self.mouseDelta.x())
self.cursorY = self.realY(event.y() + self.mouseDelta.y())
self.xChanged.emit(self.cursorX)
self.yChanged.emit(self.cursorY)
self.valuesChanged.emit(self.cursorX, self.cursorY)
self.update()
def mouseReleaseEvent(self, event):
self.moving = False
self.update()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
# *** unnecessary, but it could be useful to show the visible range
qp.save()
qp.translate(.5, .5)
qp.drawRoundedRect(self.rect().adjusted(self.margin - 1, self.margin - 1, -self.margin, -self.margin), 2, 2)
qp.restore()
# comment the lines from the *** to this point, if not interested in the visual range
qp.setPen(QtCore.Qt.NoPen)
if self.moving:
qp.setBrush(self.movingCursorColor)
else:
qp.setBrush(self.cursorColor)
qp.drawEllipse(self.cursorRect.translated(self.visualPos()))
此解决方案的众多优势之一是,可以保持单击“滑块”的位置,同时保持一致性。
请注意,realX
,realY
和realValues
是浮点型的,由于它们的声明使用该对象类型,因此它们会在信号中自动转换为int。