I wanted to ask if it is possible to use only the Color Gradient(red surrounded) part of the QColorDialog.
I am using PyQt5 and Python3 on different Linux machines (ubuntu + raspian).
It is only necessary to hide all the elements except the QColorPicker and QColorLuminancePicker.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class ColorDialog(QtWidgets.QColorDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setOptions(self.options() | QtWidgets.QColorDialog.DontUseNativeDialog)
for children in self.findChildren(QtWidgets.QWidget):
classname = children.metaObject().className()
if classname not in ("QColorPicker", "QColorLuminancePicker"):
children.hide()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
colordialog = ColorDialog()
label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
def onCurrentColorChanged(color):
label.setStyleSheet("background-color: {}".format(color.name()))
colordialog.currentColorChanged.connect(onCurrentColorChanged)
onCurrentColorChanged(colordialog.currentColor())
w = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(w)
lay.addWidget(colordialog, alignment=QtCore.Qt.AlignCenter)
lay.addWidget(label)
w.show()
sys.exit(app.exec_())
The solution proposed by eyllanesc is perfect (and smart) as always.
Nonetheless, some months ago I had to implement my own color picker widget, which creates the "color rainbow" widget only from code, and I'd like to share part of that code as I believe it could be of some interest, especially regarding the creation of the "rainbow" and the color detection.
I've edited it down to make it a basic usable "color picker" widget.
from PyQt5 import QtCore, QtGui, QtWidgets
# a helper function that ensures that a value is between the given range
sanitize = lambda m, value, M: min(max(m, value), M)
class RgbPicker(QtWidgets.QLabel):
colorGrads = QtGui.QLinearGradient(0, 0, 1, 0)
colorGrads.setCoordinateMode(colorGrads.ObjectBoundingMode)
xRatio = 1. / 6
# the basic "rainbow" gradient
colorGrads.setColorAt(0, QtCore.Qt.red)
colorGrads.setColorAt(1, QtCore.Qt.red)
colorGrads.setColorAt(xRatio, QtCore.Qt.magenta)
colorGrads.setColorAt(xRatio * 2, QtCore.Qt.blue)
colorGrads.setColorAt(xRatio * 3, QtCore.Qt.cyan)
colorGrads.setColorAt(xRatio * 4, QtCore.Qt.green)
colorGrads.setColorAt(xRatio * 5, QtCore.Qt.yellow)
# the superimposed white component
maskGrad = QtGui.QLinearGradient(0, 0, 0, 1)
maskGrad.setCoordinateMode(maskGrad.ObjectBoundingMode)
maskGrad.setColorAt(0, QtCore.Qt.transparent)
maskGrad.setColorAt(1, QtCore.Qt.white)
# the pseudo arrow cursor
cursorPath = QtGui.QPainterPath()
cursorPath.moveTo(-10, 0)
cursorPath.lineTo(-4, 0)
cursorPath.moveTo(0, -10)
cursorPath.lineTo(0, -4)
cursorPath.moveTo(4, 0)
cursorPath.lineTo(10, 0)
cursorPath.moveTo(0, 4)
cursorPath.lineTo(0, 10)
cursorPen = QtGui.QPen(QtCore.Qt.black, 3)
colorChanged = QtCore.pyqtSignal(QtGui.QColor)
def __init__(self, color=None):
QtWidgets.QLabel.__init__(self)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.setFixedSize(220, 200)
# create a pixmap that shows the "rainbow" and draw its contents
pixmap = QtGui.QPixmap(self.size())
qp = QtGui.QPainter(pixmap)
qp.fillRect(pixmap.rect(), self.colorGrads)
qp.fillRect(pixmap.rect(), self.maskGrad)
qp.end()
self.setPixmap(pixmap)
self.image = pixmap.toImage()
# a multiplier, used when an arrow key is kept pressed
self.keyTimer = QtCore.QTimer()
self.keyTimer.setInterval(1000)
self.keyTimer.timeout.connect(lambda: setattr(self, 'keyMulti', self.keyMulti + 1))
self._cursorPos = QtCore.QPoint()
self._color = color
self.setColor(color)
#property
def color(self):
return self._color
#property
def cursorPos(self):
return self._cursorPos
#cursorPos.setter
def cursorPos(self, pos):
self._cursorPos = pos
self.update()
def sanitizePos(self, pos):
# sanitize the position within the color rainbow margins
return QtCore.QPoint(sanitize(0, pos.x(), self.width() - 1), sanitize(0, pos.y(), self.height() - 1))
def setColor(self, color):
h, s, v, a = color.getHsv()
# compute the coordinates based on hsv components
x = (360 - h) * (self.width() - 1) / 360.
y = (255 - s) * (self.height() - 1) / 255.
self.cursorPos = QtCore.QPoint(x, y)
def translateColorCursor(self, pos):
# return the color of the given pixel
return QtGui.QColor(self.image.pixel(pos))
def mousePressEvent(self, event):
pos = self.sanitizePos(event.pos())
self._color = self.translateColorCursor(pos)
self.colorChanged.emit(QtGui.QColor(self._color))
self.cursorPos = pos
def mouseMoveEvent(self, event):
pos = self.sanitizePos(event.pos())
self._color = self.translateColorCursor(pos)
self.colorChanged.emit(QtGui.QColor(self._color))
self.cursorPos = pos
def keyPressEvent(self, event):
deltaX = deltaY = 0
# emulation of the Qt internal color picker keyboard navigation
if event.modifiers() & QtCore.Qt.KeypadModifier:
if event.key() in (QtCore.Qt.Key_7, QtCore.Qt.Key_Home):
deltaX = deltaY = -1
elif event.key() in (QtCore.Qt.Key_8, QtCore.Qt.Key_Up):
deltaY = -1
elif event.key() in (QtCore.Qt.Key_9, QtCore.Qt.Key_PageUp):
deltaX = 1
deltaY = -1
elif event.key() in (QtCore.Qt.Key_4, QtCore.Qt.Key_Left):
deltaX = -1
elif event.key() in (QtCore.Qt.Key_6, QtCore.Qt.Key_Right):
deltaX = 1
elif event.key() in (QtCore.Qt.Key_1, QtCore.Qt.Key_End):
deltaX = -1
deltaY = 1
elif event.key() in (QtCore.Qt.Key_2, QtCore.Qt.Key_Down):
deltaY = 1
elif event.key() in (QtCore.Qt.Key_3, QtCore.Qt.Key_PageDown):
deltaX = deltaY = 1
elif event.key() == QtCore.Qt.Key_Left:
deltaX = -1
elif event.key() == QtCore.Qt.Key_Right:
deltaX = 1
elif event.key() == QtCore.Qt.Key_Up:
deltaY = -1
elif event.key() == QtCore.Qt.Key_Down:
deltaY = 1
elif event.key() == QtCore.Qt.Key_Home:
if event.modifiers() == QtCore.Qt.ShiftModifier:
deltaY = -1000
elif event.modifiers() == QtCore.Qt.ControlModifier:
deltaX = deltaY = -1000
else:
deltaX = -1000
elif event.key() == QtCore.Qt.Key_End:
if event.modifiers() == QtCore.Qt.ShiftModifier:
deltaY = 1000
elif event.modifiers() == QtCore.Qt.ControlModifier:
deltaX = deltaY = 1000
else:
deltaX = 1000
elif event.key() == QtCore.Qt.Key_PageUp and not event.modifiers() & QtCore.Qt.KeypadModifier:
deltaY = -10
elif event.key() == QtCore.Qt.Key_PageDown and not event.modifiers() & QtCore.Qt.KeypadModifier:
deltaY = +10
else:
return QtWidgets.QWidget.keyPressEvent(self, event)
if not event.isAutoRepeat():
self.keyTimer.start()
self.keyMulti = 1
if deltaX or deltaY:
multi = self.keyMulti
if event.modifiers() & QtCore.Qt.ShiftModifier:
multi *= 10
deltaX *= multi
deltaY *= multi
pos = self.sanitizePos(QtCore.QPoint(self.cursorPos.x() + deltaX, self.cursorPos.y() + deltaY))
self._color = self.translateColorCursor(pos)
self.colorChanged.emit(QtGui.QColor(self._color))
self.cursorPos = pos
def keyReleaseEvent(self, event):
if not event.isAutoRepeat():
self.keyTimer.stop()
QtWidgets.QWidget.keyReleaseEvent(self, event)
def paintEvent(self, event):
QtWidgets.QLabel.paintEvent(self, event)
qp = QtGui.QPainter(self)
qp.setPen(self.cursorPen)
# translate to the crosshair position and paint it
qp.translate(self.cursorPos)
qp.drawPath(self.cursorPath)
class BlackPicker(QtWidgets.QWidget):
# the "black" selector, which has a gradient based on the current selected
# color of the RgbPicker (selected color -> black)
_rect = QtCore.QRect(0, 8, 16, 200)
grad = QtGui.QLinearGradient(0, 0, 0, 1)
grad.setCoordinateMode(grad.ObjectBoundingMode)
grad.setColorAt(1, QtCore.Qt.black)
arrow = QtGui.QPainterPath()
arrow.lineTo(4, -4)
arrow.lineTo(4, 4)
arrow.closeSubpath()
_color = QtGui.QColor()
_black = -1
blackChanged = QtCore.pyqtSignal(float)
def __init__(self, color):
QtWidgets.QWidget.__init__(self)
self.color = QtGui.QColor(color)
self.setFixedSize(22, 216)
#property
def black(self):
return self._black
#black.setter
def black(self, black):
if black == self._black:
return
self._black = black
self.update()
self.blackChanged.emit(black)
#property
def color(self):
return self._color
#color.setter
def color(self, color):
if color == self._color:
return
self._color = QtGui.QColor(color)
self.grad.setColorAt(0, color)
self.black = color.getCmykF()[3]
def setWhiteColor(self, color):
self.grad.setColorAt(0, color)
self.update()
def setColor(self, color):
self.color = color
def mousePressEvent(self, event):
self.black = sanitize(0, event.pos().y() - self._rect.top(),
self._rect.height()) / 200.
def mouseMoveEvent(self, event):
self.black = sanitize(0, event.pos().y() - self._rect.top(),
self._rect.height()) / 200.
def wheelEvent(self, event):
if event.pixelDelta().y() < 0:
delta = .01
else:
delta = -.01
self.black = sanitize(0, self.black + delta, 1)
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
qp.fillRect(self._rect, self.grad)
qp.translate(self._rect.right() + 2, self._rect.top() + self.black * self._rect.height())
qp.setBrush(QtCore.Qt.black)
qp.translate(.5, .5)
qp.drawPath(self.arrow)
class ColorPicker(QtWidgets.QWidget):
colorChanged = QtCore.pyqtSignal(QtGui.QColor)
def __init__(self, color=None, parent=None):
super().__init__(parent)
layout = QtWidgets.QGridLayout(self)
if not (isinstance(color, QtGui.QColor) and color.isValid()):
if isinstance(color, QtCore.Qt.GlobalColor):
color = QtGui.QColor(color)
else:
color = self.palette().color(QtGui.QPalette.WindowText)
self.rgbPicker = RgbPicker(color)
layout.addWidget(self.rgbPicker, 0, 0)
self.blackPicker = BlackPicker(color)
layout.addWidget(self.blackPicker, 0, 1)
self.colorWidget = QtWidgets.QWidget()
layout.addWidget(self.colorWidget, 1, 0, 1, 2)
self.colorWidget.setMinimumHeight(16)
self.colorWidget.setAutoFillBackground(True)
self.colorLabel = QtWidgets.QLabel()
layout.addWidget(self.colorLabel)
self.rgbPicker.colorChanged.connect(self.updateColor)
self.rgbPicker.colorChanged.connect(self.blackPicker.setWhiteColor)
self.blackPicker.blackChanged.connect(self.updateColor)
self.updateColor()
def updateColor(self):
color = self.rgbPicker.color
c, m, y, _, _ = color.getCmykF()
color.setCmykF(c, m, y, self.blackPicker.black)
palette = self.colorWidget.palette()
palette.setColor(palette.Window, color)
self.colorWidget.setPalette(palette)
r, g, b = color.getRgb()[:-1]
hexColor = '{:02X}{:02X}{:02X}'.format(r, g, b)
self.colorLabel.setText('R:{:03} G:{:03} B:{:03} - #{}'.format(
r, g, b, hexColor))
self.colorChanged.emit(color)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
picker = ColorPicker(QtGui.QColor(QtCore.Qt.white))
picker.show()
sys.exit(app.exec())
You can check out this Circular Color Picker design for PyQt
PyQt Circular ColorPicker
It looks like this and can be easily modified
Related
I am currently making simple shooting game in python kivy. My character is moving but when pressed space bar, the bullet is generated in the same position. So the bullet is not mapped onto the character.
I tried creating bullet as a class but it didn't help.
I will be glad for any help.
Here is my code:
def __init__(self, *args, **kwargs):
super(Auto,self).__init__(*args,**kwargs)
self.rect = Rectangle(size = (150, 150), pos = (0, 0), source = "spriteshd00.png")
self.canvas.add(self.rect)
def pohyb(self, vector):
self.rect.pos = Vector(self.rect.pos) + Vector(vector)
class Hra(BoxLayout):
def __init__(self, *args, **kwargs):
super(Hra,self).__init__(*args,**kwargs)
self.orientation = "vertical"
self.car = Auto(size_hint = (1,.9))
self.bullet = Rectangle(pos=(0,0), size=(20, 5))
Clock.schedule_interval(self.movebullet, .025)
self.add_widget(self.car)
ovladaci_panel_obal = BoxLayout(orientation = "horizontal", size_hint = (1,.1))
self.up_btn = ToggleButton(text = "Up", group = "smer")
self.up_btn.bind(state = self.on_state)
ovladaci_panel_obal.add_widget(self.up_btn)
self.left_btn = ToggleButton(text = "Left", group = "smer")
self.left_btn.bind(state = self.on_state)
ovladaci_panel_obal.add_widget(self.left_btn)
self.down_btn = ToggleButton(text = "Down", group = "smer")
self.down_btn.bind(state = self.on_state)
ovladaci_panel_obal.add_widget(self.down_btn)
self.right_btn = ToggleButton(text = "Right", group = "smer")
self.right_btn.bind(state = self.on_state)
ovladaci_panel_obal.add_widget(self.right_btn)
self.add_widget(ovladaci_panel_obal)
# Připojíme klávesnici k aplikaci
self.Keyboard = Window.request_keyboard(self._keyboard_closed,self,'text')
self.Keyboard.bind(on_key_down = self._on_keyboard_down)
self.Keyboard.bind(on_key_up = self._on_keyboard_up)
self.Keyboard.bind(on_key_up=self.shoot)
def _keyboard_closed(self):
#Funkce už nebude volána při stisku klávesy
self.Keyboard.unbind(on_key_down = self._on_keyboard_down())
self.Keyboard.unbind(on_key_up=self._on_keyboard_down())
self.Keyboard.unbind(on_key_up=self.shoot())
self.Keyboard = None #odstraneni klavesnice
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
if(keycode[1] == "up"):
self.reset_state()#resetovani stavu tlacitek
self.up_btn.state = "down"
elif(keycode[1] == "left"):
self.reset_state()#resetovani stavu tlacitek
self.left_btn.state = "down"
elif(keycode[1] == "right"):
self.reset_state()#resetovani stavu tlacitek
self.right_btn.state = "down"
elif(keycode[1] == "down"):
self.reset_state()#resetovani stavu tlacitek
self.down_btn.state = "down"
if(keycode[1] == "escape"):
keyboard.release()#stisknu escape a aplikace prestane fungovat
return True
def _on_keyboard_up(self,keyboard, keycode):
self.reset_state()
def movebullet(self, *args):
y = self.car.pos[1]*3 + self.car.pos[0]*3
x = self.bullet.pos[0] + 15
#self.bullet.pos = self.car.pos
self.bullet.pos = (x,y) #tahle konfiiguarce umozni jit kulce horizontalne
#blx, bly = self.ball.pos
# Check for hit. Ball size is 15. Bullet size is 5.
#if (x + 5 > blx and x < blx + 15 and y > bly and y < 400):
#self.canvas.remove(self.ball)
#self.newball()
#self.lblRes.text = str(int(self.lblRes.text) + 100)
def shoot(self, keyboard, keycode):
if (keycode[1] == 'spacebar'):
with self.canvas:
Color(0, 0, 0, 1, mode="rgba")
self.bullet = Rectangle(pos= (300, 0), size=(20, 5))
g1 = self.car.pos[0] + 15
g2 = self.car.pos[1] + 15
self.bullet.pos = (g1, g2)
def reset_state(self):
self.up_btn.state = "normal"
self.left_btn.state = "normal"
self.down_btn.state = "normal"
self.right_btn.state = "normal"
def on_state(self, obj, state):
if(obj.text == "Up"):
if(state == "down"):
self.timer = Clock.schedule_interval(lambda dt: self.car.pohyb((0, 1)), 0.01)
#self.auto.rect.source = "up.png" # Změníme obrázek
if(state == "normal"):
#Zastavíme timer
Clock.unschedule(self.timer)
elif(obj.text == "Left"):
if(state == "down"):
self.timer = Clock.schedule_interval(lambda dt: self.car.pohyb((-1, 0)), 0.01)
#self.car.rect.source = "left.png" # Změníme obrázek
if(state == "normal"):
Clock.unschedule(self.timer)
elif(obj.text == "Down"):
if(state == "down"):
self.timer = Clock.schedule_interval(lambda dt: self.car.pohyb((0, -1)), 0.01)
#self.car.rect.source = "down.png" # Změníme obrázek
if(state == "normal"):
Clock.unschedule(self.timer)
elif(obj.text == "Right"):
if(state == "down"):
self.timer = Clock.schedule_interval(lambda dt: self.car.pohyb((1, 0)), 0.01)
#self.car.rect.source = "right.png" # Změníme obrázek
if(state == "normal"):
Clock.unschedule(self.timer)
class MainApp(App):
def build(self):
Window.clearcolor = (1,1,1,1)
return Hra()
root = MainApp()
root.run()``
It looks like your Auto instance is nearly the size of the Hra (size_hint = (1,.9)), and your Bullet position is based on the position of the Auto instance. Your Up, Left, Right, and Down buttons are moving the Rectangle that is drawn on the Canvas, but have no effect on the actual position of the Auto instance. So your Bullet is always created at the same position (the Auto position never changes). One way to fix it is to make the Bullet position depend on the Rectangle instead. Like this:
def movebullet(self, *args):
y = self.car.rect.pos[1] # use position of Rectangle
x = self.bullet.pos[0] + 15
#self.bullet.pos = self.car.pos
self.bullet.pos = (x,y) #tahle konfiiguarce umozni jit kulce horizontalne
and
def shoot(self, keyboard, keycode):
if (keycode[1] == 'spacebar'):
with self.canvas:
Color(0, 0, 0, 1, mode="rgba")
self.bullet = Rectangle(pos= (300, 0), size=(20, 5))
# use position of Rectangle
g1 = self.car.rect.pos[0] + 15
g2 = self.car.rect.pos[1] + 15
self.bullet.pos = (g1, g2)
I'm new to PyQt5. I'm trying to implement item control like this
there you can rotate item by dragging the rotation handle. What a have for now:
import math
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from math import sqrt, acos
class QDMRotationHandle(QGraphicsPixmapItem):
def __init__(self, item):
super().__init__(item)
self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable)
self.setPixmap(QPixmap('rotation_handle.png').scaledToWidth(20))
self.setTransformOriginPoint(10, 10)
self.item = item
self.item.boundingRect().moveCenter(QPointF(50, 50))
self.hypot = self.parentItem().boundingRect().height()
self.setPos(self.item.transformOriginPoint().x()-10, self.item.transformOriginPoint().y() - self.hypot)
def getSecVector(self, sx, sy, ex, ey):
return {"x": ex - sx, "y": 0}
def getVector(self, sx, sy, ex, ey):
if ex == sx or ey == sy:
return 0
return {"x": ex - sx, "y": ey - sy}
def getVectorAngleCos(self, ax, ay, bx, by):
ma = sqrt(ax * ax + ay * ay)
mb = sqrt(bx * bx + by * by)
sc = ax * bx + ay * by
res = sc / ma / mb
return res
def mouseMoveEvent(self, event):
pos = self.mapToParent(event.pos())
# parent_pos = self.mapToScene(self.parent.transformOriginPoint())
parent_pos = self.parentItem().pos()
parent_center_pos = self.item.boundingRect().center()
parent_pos_x = parent_pos.x() + self.item.transformOriginPoint().x()
parent_pos_y = parent_pos.y() + self.item.transformOriginPoint().y()
print("mouse: ", pos.x(), pos.y())
print("handle: ", self.pos().x(), self.pos().y())
print("item: ", parent_pos.x(), parent_pos.y())
#print(parent_center_pos.x(), parent_center_pos.y())
vecA = self.getVector(parent_pos_x, parent_pos_y, pos.x(), pos.y())
vecB = self.getSecVector(parent_pos_x, parent_pos_y, pos.x(), parent_pos_y)
#
vect.setLine(parent_pos_x, parent_pos_y, pos.x(), pos.y())
if pos.x() > parent_pos_x:
#
secVect.setLine(parent_pos_x, parent_pos_y, pos.x(), parent_pos_y)
vecB = self.getSecVector(parent_pos_x, parent_pos_y, pos.x(), parent_pos_y)
elif pos.x() < parent_pos_x:
#
secVect.setLine(parent_pos_x, parent_pos_y, pos.x(), parent_pos_y)
vecB = self.getSecVector(parent_pos_x, parent_pos_y, pos.x(), -parent_pos_y)
if vecA != 0:
cos = self.getVectorAngleCos(vecA["x"], vecA["y"], vecB["x"], vecB["y"])
cos = abs(cos)
if cos > 1:
cos = 1
sin = abs(sqrt(1 - cos ** 2))
lc = self.hypot * cos
ld = self.hypot * sin
#self.ell = scene.addRect(parent_pos_x, parent_pos_y, 5, 5)
if pos.x() < parent_pos_x and pos.y() < parent_pos_y:
print(parent_pos_x, parent_pos_y )
#self.ell.setPos(parent_pos.x(), parent_pos.y())
self.setPos(parent_pos_x - lc, parent_pos_y - ld)
elif pos.x() > parent_pos_x and pos.y() < parent_pos_y:
#self.ell.setPos(parent_pos_x, parent_pos_y)
self.setPos(parent_pos_x + lc, parent_pos_y - ld)
elif pos.x() > parent_pos_x and pos.y() > parent_pos_y:
#self.ell.setPos(parent_pos_x, parent_pos_y)
self.setPos(parent_pos_x + lc, parent_pos_y + ld)
elif pos.x() < parent_pos_x and pos.y() > parent_pos_y:
#self.ell.setPos(parent_pos_x, parent_pos_y)
self.setPos(parent_pos_x - lc, parent_pos_y + ld)
else:
if pos.x() == parent_pos_x and pos.y() < parent_pos_y:
self.setPos(parent_pos_x, parent_pos_y - self.hypot)
elif pos.x() == parent_pos_x and pos.y() > parent_pos_y:
self.setPos(parent_pos_x, parent_pos_y + self.hypot)
elif pos.y() == parent_pos_x and pos.x() > parent_pos_y:
self.setPos(parent_pos_x + self.hypot, parent_pos_y)
elif pos.y() == parent_pos_x and pos.x() < parent_pos_y:
self.setPos(parent_pos_x - self.hypot, parent_pos_y)
item_position = self.item.transformOriginPoint()
handle_pos = self.pos()
#print(item_position.y())
angle = math.atan2(item_position.y() - handle_pos.y(),
item_position.x() - handle_pos.x()) / math.pi * 180 - 90
self.item.setRotation(angle)
self.setRotation(angle)
class QDMBoundingRect(QGraphicsRectItem):
def __init__(self, item, handle):
super().__init__()
self.item = item
self.handle = handle
item.setParentItem(self)
handle.setParentItem(self)
self.setRect(0, 0, item.boundingRect().height(), item.boundingRect().width())
self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable)
app = QtWidgets.QApplication([])
scene = QtWidgets.QGraphicsScene()
item = scene.addRect(0, 0, 100, 100)
item.setTransformOriginPoint(50, 50)
handle_item = QDMRotationHandle(item)
#handle_item.setParentItem(item)
# handle_item.setOffset(10, 10)
#handle_item.setPos(40, -40)
scene.addItem(handle_item)
vect = scene.addLine(0, 0, 100, 100)
secVect = scene.addLine(50, 50, 100, 100)
secVect.setPen(QPen(Qt.green))
boundingRect = QDMBoundingRect(item, handle_item)
scene.addItem(boundingRect)
view = QtWidgets.QGraphicsView(scene)
view.setFixedSize(500, 500)
view.show()
app.exec_()
It works fine when item is in the initial position, but if you move it, the rotation stops working as it should. It seems like i do something wrong with coordinates, but i cant understand what. Please help...
Your object structure is a bit disorganized and unnecessarily convoluted.
For instance:
you're adding handle_item to the scene, but since you've made it a child of item, you shall not try to add it to the scene again;
the ItemIsMovable is useless if you don't call the default implementation in mouseMoveEvent, but for your purpose you actually don't need to make it movable at all;
the whole computation is unnecessarily complex (and prone to bugs, since you're both setting the object position and rotation);
using a pixmap for the handle seems quite unnecessary, use Qt primitives if you can, to avoid graphical artifacts and unnecessary transform computations;
What you could do, instead, is to use a single "bounding rect item", and add controls as its children. Then, by filtering mouse events of those children, you can then alter the rotation based on the scene position of those events.
In the following example, I took care of the above, considering:
control/handle items should always be positioned at 0, 0, so that the computation required for getting/setting their position is much easier;
since items (including vector items) are actually children of the QDMBoundingRect, you don't need to compute coordinates for each point of the vectors: the rotation of the parent will be applied to them automatically;
the secVect (still a child item) is simplified by just setting its x offset using the mapped scene positions of center and handle;
Also, by calling setFiltersChildEvents(True) the sceneEventFilter() receives any scene event from its children, allowing us to track mouse events; we return True for all the events that we handle so that they are not propagated to the parent, and also because move events can only received if mouse press events are accepted (the default implementation ignores them, unless the item is movable).
class QDMBoundingRect(QGraphicsRectItem):
_startPos = QPointF()
def __init__(self, item):
super().__init__()
self.setRect(item.boundingRect())
self.setFlags(self.ItemIsMovable)
self.setFiltersChildEvents(True)
self.item = item
self.center = QGraphicsEllipseItem(-5, -5, 10, 10, self)
self.handle = QGraphicsRectItem(-10, -10, 20, 20, self)
self.vect = QGraphicsLineItem(self)
self.secVect = QGraphicsLineItem(self)
self.secVect.setPen(Qt.green)
self.secVect.setFlags(self.ItemIgnoresTransformations)
self.setCenter(item.transformOriginPoint())
def setCenter(self, center):
self.center.setPos(center)
self.handle.setPos(center.x(), -40)
self.vect.setLine(QLineF(center, self.handle.pos()))
self.secVect.setPos(center)
self.setTransformOriginPoint(center)
def sceneEventFilter(self, item, event):
if item == self.handle:
if (event.type() == event.GraphicsSceneMousePress
and event.button() == Qt.LeftButton):
self._startPos = event.pos()
return True
elif (event.type() == event.GraphicsSceneMouseMove
and self._startPos is not None):
centerPos = self.center.scenePos()
line = QLineF(centerPos, event.scenePos())
self.setRotation(90 - line.angle())
diff = self.handle.scenePos() - centerPos
self.secVect.setLine(0, 0, diff.x(), 0)
return True
if (event.type() == event.GraphicsSceneMouseRelease
and self._startPos is not None):
self._startPos = None
return True
return super().sceneEventFilter(item, event)
app = QApplication([])
scene = QGraphicsScene()
item = scene.addRect(0, 0, 100, 100)
item.setPen(Qt.red)
item.setTransformOriginPoint(50, 50)
boundingRect = QDMBoundingRect(item)
scene.addItem(boundingRect)
view = QGraphicsView(scene)
view.setFixedSize(500, 500)
view.show()
app.exec_()
With the above code you can also implement the movement of the "center" handle, allowing to rotate around a different position:
def sceneEventFilter(self, item, event):
if item == self.handle:
# ... as above
elif item == self.center:
if (event.type() == event.GraphicsSceneMousePress
and event.button() == Qt.LeftButton):
self._startPos = event.pos()
return True
elif (event.type() == event.GraphicsSceneMouseMove
and self._startPos is not None):
newPos = self.mapFromScene(
event.scenePos() - self._startPos)
self.setCenter(newPos)
return True
if (event.type() == event.GraphicsSceneMouseRelease
and self._startPos is not None):
self._startPos = None
return True
return super().sceneEventFilter(item, event)
I created a program that allows the user to randomly generate buttons on a grid, but I cannot detect if the user presses them or not. Here is what I have so far:
from graphics import *
from time import *
from random import *
class Button:
def __init__(self, win, center, width, height, label):
w,h = width/2.0, height/2.0
x,y = center.getX(), center.getY()
self.xmax, self.xmin = x+w, x-w
self.ymax, self.ymin = y+h, y-h
p1 = Point(self.xmin, self.ymin)
p2 = Point(self.xmax, self.ymax)
self.rect = Rectangle(p1,p2)
self.rect.setFill("blue")
self.rect.draw(win)
self.label = Text(center, label)
self.label.draw(win)
self.label.setSize(8)
self.activate()
def clicked(self, p):
#print("clicked", p.getX(), p.getY(), self.xmin, self.xmax)
return (self.active and
self.xmin <= p.getX() <= self.xmax and
self.ymin <= p.getY() <= self.ymax)
def getLabel(self):
return self.label.getText()
def activate(self):
self.label.setFill("black")
self.rect.setWidth(2)
self.active = True
def deactivate(self):
self.label.setFill("darkgray")
self.rect.setWidth(1)
self.active = False
def setColor(self, color):
self.rect.setFill(color)
class Grid:
def __init__(self, win, startX, startY, numCols, numRows, squareWidth, squareHeight):
self.ButtonMatrix = []
self.numCols = numCols
self.numRows = numRows
for y in range(startY, numRows):
buttonList = []
for x in range(startX,numCols):
label = str(x) + str(y)
buttonList.append(Button(win,Point(x,y), squareWidth, squareHeight, label))
self.ButtonMatrix.append(buttonList)
sleep(0.03)
def getClickPos(self, clickPt):
for y in range(self.numRows):
for x in range(self.numCols):
if self.ButtonMatrix[y][x].clicked(clickPt):
return y,x
def GenerateRandomColor(self, X,Y, color):
self.ButtonMatrix[X][Y].setColor(color)
#def insideBox(x,y):
def setSquareColor(self,r,c,color):
self.ButtonMatrix[r][c].setColor(color)
def setRowColor(self,rowNum,color):
for c in range(15):
self.ButtonMatrix[rowNum][c].setColor(color)
def main():
SIZE = 15
#application window
win = GraphWin("Memory Game", 600, 600)
win.setBackground(color_rgb(45,59,57))
win.setCoords(-3, -3, SIZE + 2, SIZE + 2)
grid = Grid(win, 0, 1, SIZE, SIZE, 1, 1)
quitButton = Button(win, Point(SIZE, SIZE+1), 2, 1, "Quit")
for i in range(10):
X = randrange(13)
Y = randrange(13)
grid.GenerateRandomColor(X,Y, "white")
Coords = X,Y
sleep(0.1)
print(Coords) #checking to see if each button coord will be printed out
pt = win.getMouse()
if grid.getClickPos(pt) == Coords:
print("pressed random button")
else:
print("did not press a random button")
if __name__ == "__main__":
main()
I am adding QgraphicsPathItem to scene and want to erase particular portion of the Qgraphicspathitem. I am using QgraphicsrectItem as Eraser here.Both the items are colliding, but the QgraphicsPathItem is not getting erased.It remains the same in the scene.I am subtracting the two paths and adding it to the QGraphicspathItem
class GraphicsSceneClass(QGraphicsScene):
def __init__(self):
super().__init__()
self.setItemIndexMethod(QGraphicsScene.NoIndex)
self.setBackgroundBrush(QBrush(Qt.black))
self.setSceneRect(0,0,1000,1000)
self.horizontal=QHBoxLayout()
self.widget=QWidget()
self.drawrect=False
self.gridOn = 0
self.selectionMode = 1
self.targetForLine = QRect()
self.drawEnd = True
self.resizemode=0
self.eraser_item = None
self.erasing=False
self.rect=QGraphicsRectItem()
def mousePressEvent(self,event):
try:
sampleTransform = QTransform()
self.objectAtMouse = self.itemAt(event.scenePos(), sampleTransform)
if self.selectionMode == 2:
if self.drawEnd == True:
self.drawEnd = False
elif self.drawEnd == False:
self.drawEnd = True
self.drawLineObj = None
if self.drawEnd == False:
self.lineInsertPoint = self.TargPosForLine(event.scenePos(), "ForLine")
tempPainterPath = QPainterPath()
self.drawLineObj = QGraphicsPathItem()
self.drawLineObj.setPos(self.lineInsertPoint.x(), self.lineInsertPoint.y())
self.drawLineObj.setPen(QPen(Qt.NoPen))
self.drawLineObj.setBrush(QBrush(QColor(Qt.gray)))
self.drawLineObj.setPath(tempPainterPath)
self.addItem(self.drawLineObj)
self.drawLineObj.setFlag(QGraphicsItem.ItemIsSelectable)
if event.buttons() == Qt.RightButton and self.resizemode == 1:
self.erasing = True
self.rect.setRect(event.scenePos().x()-5, event.scenePos().y()-5, 10, 10)
print(event.pos().x(),event.pos().y())
self.addItem(self.rect)
self.rect.setPen(QColor(Qt.red))
except Exception as e:
print("Error in mousepress",e)
def mouseMoveEvent(self,event):
try:
if self.selectionMode == 2 and self.drawEnd == False and self.erasing == False:
f1 = QPainterPath()
gridPos = self.TargPosForLine(event.scenePos(), "ForLine")
f1.moveTo(0, 0)
f1.lineTo(0, 6)
f1.lineTo((gridPos.x() - self.lineInsertPoint.x()), 6)
f1.lineTo((gridPos.x() - self.lineInsertPoint.x()), 0)
f1.closeSubpath()
self.drawLineObj.setPath(f1)
self.drawLineObj.setPos(self.lineInsertPoint.x(), self.lineInsertPoint.y() + 5)
self.drawLineObj.setBrush(QBrush(QColor(Qt.gray)))
pen = QPen(QColor(Qt.gray))
pen.setWidth(0)
self.drawLineObj.setPen(pen)
self.drawLineObj._property = []
self.drawLineObj._angle = 0
if self.selectionMode == 2 and self.drawEnd == True:
self.targetForLine = self.TargPosForLine(event.scenePos(), "ForRect")
self.update()
if event.buttons() & Qt.RightButton and self.erasing:
print(event.scenePos().x(),event.scenePos().y())
self.rect.setRect(event.scenePos().x() - 5, event.scenePos().y() - 5,
10, 10)
for item in self.collidingItems(self.rect):
new = item.path()-(self.rect.shape())
item.setPath(new)
print('collided')
else:
self.targetForLine = QRect()
except Exception as e:
print("Error in mousemove",e)
def mouseReleaseEvent(self, event):
if self.drawrect==True or self.tLext==2 or self.tRext==2 or self.BLext==2 or self.BRext==2:
self.drawrect=False
self.tLext=0
self.tRext=0
self.BLext=0
self.BRext=0
QApplication.changeOverrideCursor(Qt.ArrowCursor)
QApplication.setOverrideCursor(Qt.ArrowCursor)
mainWindow.pointer.setChecked(False)
if self.rect != None:
self.removeItem(self.rect)
def TargPosForLine(self, position, mode):
try:
clicked_column = int((position.y() // 16)) * 16
clicked_row = int((position.x() // 16)) * 16
if clicked_column < 0:
clicked_column = 0
if clicked_row < 0:
clicked_row = 0
if (mode == "ForRect"):
return QRect(clicked_row, clicked_column, 16, 16)
elif (mode == "ForLine"):
return QPointF(clicked_row, clicked_column)
except Exception as e:
print("Error in TargPosForLine", e)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.scene = GraphicsSceneClass()
self.view=QGraphicsView(self.scene)
self.view.setMouseTracking(True)
self.view.setAcceptDrops(True)
self.view.setRenderHint(QPainter.HighQualityAntialiasing)
self.linePointerButton = QToolButton()
self.linePointerButton.setCheckable(True)
self.linePointerButton.setText("Drawline")
self.linePointerButton.setToolTip("Draw Track")
self.linePointerButton.clicked.connect(self.setPointerMode)
self.resizebutton = QToolButton()
self.resizebutton.setText("Resize")
self.resizebutton.setCheckable(True)
self.resizebutton.clicked.connect(self.resizepath)
self.widg=QWidget()
self.horizontal=QHBoxLayout()
self.horizontal.addWidget(self.view)
self.widg.setLayout(self.horizontal)
self.setCentralWidget(self.widg)
self.tool=self.addToolBar("Tool")
self.tool.addWidget(self.linePointerButton)
self.tool.addWidget(self.resizebutton)
def keyPressEvent(self, event):
try:
key = event.key()
if key == Qt.Key_Escape:
but = self.linePointerButton
self.scene.selectionMode = 1
but.setChecked(True)
except Exception:
print("Keypress is not working")
def setPointerMode(self):
try:
self.scene.selectionMode =2
except Exception:
print("Not able to change the selectionmode")
def resizepath(self):
self.scene.resizemode=1
self.scene.selectionMode = 1
self.linePointerButton.setChecked(False)
if __name__=="__main__":
import sys
app=QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
The path and the item and the shape from self.rect don't share the same coordinates system. So, when you want to remove the eraser from the line, the rect, you have to use a common coordinates system (for example, the one of you scene):
for item in self.collidingItems(self.rect):
new = item.mapToScene(item.path())-self.rect.mapToScene(self.rect.shape()) # translate the paths to the scene coords system
item.setPath(item.mapFromScene(new)) # translate the path from the scene coords system
Is there some simple way in PyQt5 to get real dimensions of the pixmap displayed in QLabel? I am trying to select part of the image with rubber band. But I can't find a way to limit the rubberband only to pixmap. The QLabel.pixmap().rect() returns dimensions of the whole QLabel not only the pixmap. The problem arises when the pixmap is scaled and there are stripes on the sides of the picture.
The Example image
Example image 2
I posted are quite self explanatory. I don't want the rubberband to be able to move out of the picture to the white stripes.
class ResizableRubberBand(QWidget):
def __init__(self, parent=None):
super(ResizableRubberBand, self).__init__(parent)
self.aspect_ratio = None
self.setWindowFlags(Qt.SubWindow)
self.layout = QHBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
self.grip1 = QSizeGrip(self)
self.grip2 = QSizeGrip(self)
self.layout.addWidget(self.grip1, 0, Qt.AlignLeft | Qt.AlignTop)
self.layout.addWidget(self.grip2, 0, Qt.AlignRight | Qt.AlignBottom)
self.rubberband = QRubberBand(QRubberBand.Rectangle, self)
self.rubberband.setStyle(QStyleFactory.create("Fusion"))
self.rubberband.move(0, 0)
self.rubberband.show()
self.show()
class ResizablePixmap(QLabel):
def __init__(self, bytes_image):
QLabel.__init__(self)
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.setAlignment(Qt.AlignVCenter | Qt.AlignHCenter)
self.setStyleSheet("background-color:#ffffff;")
self.update_pixmap(bytes_image)
def resizeEvent(self, event):
if event:
x = event.size().width()
y = event.size().height()
else:
x = self.width()
y = self.height()
self.current_pixmap = self._bytes2pixmap(self.bytes_image_edit)
self.setPixmap(self.current_pixmap.scaled(x, y, Qt.KeepAspectRatio))
self.resize(x, y)
def update_pixmap(self, bytes_image):
self.bytes_image_edit = bytes_image
self.current_pixmap = self._bytes2pixmap(bytes_image)
self.setPixmap(self.current_pixmap)
self.resizeEvent(None)
#staticmethod
def _bytes2pixmap(raw_image):
image = QImage()
image.loadFromData(raw_image)
return QPixmap(image)
#staticmethod
def _pixmap2bytes(pixmap):
byte_array = QByteArray()
buffer = QBuffer(byte_array)
buffer.open(QIODevice.WriteOnly)
pixmap.save(buffer, 'PNG')
return byte_array.data()
#property
def image_dims(self):
return self.width(), self.height()
def force_resize(self, qsize):
self.resizeEvent(QResizeEvent(qsize, qsize))
class SelectablePixmap(ResizablePixmap):
def __init__(self, bytes_image):
super().__init__(bytes_image)
self.currentQRubberBand = None
self.move_rubber_band = False
self.rubber_band_offset = None
def cancel_selection(self):
self.currentQRubberBand.hide()
self.currentQRubberBand.deleteLater()
self.currentQRubberBand = None
self.selectionActive.emit(False)
def mousePressEvent(self, eventQMouseEvent):
if not self.currentQRubberBand:
self.currentQRubberBand = ResizableRubberBand(self)
self.selectionActive.emit(True)
if self.currentQRubberBand.geometry().contains(eventQMouseEvent.pos()):
self.move_rubber_band = True
self.rubber_band_offset = (eventQMouseEvent.pos() -
self.currentQRubberBand.pos())
else:
self.originQPoint = eventQMouseEvent.pos()
if self.pixmap().rect().contains(self.originQPoint):
self.currentQRubberBand.setGeometry(QRect(self.originQPoint,
QSize()))
self.currentQRubberBand.show()
def mouseMoveEvent(self, eventQMouseEvent):
if self.move_rubber_band:
pos = eventQMouseEvent.pos() - self.rubber_band_offset
if self.pixmap().rect().contains(pos):
self.currentQRubberBand.move(pos)
else:
rect = QRect(self.originQPoint, eventQMouseEvent.pos())
self.currentQRubberBand.setGeometry(rect.normalized())
def mouseReleaseEvent(self, eventQMouseEvent):
if self.move_rubber_band:
self.move_rubber_band = False
The "easy" answer to your question is that you can get the actual geometry of the QPixmap by moving its QRect. Since you're using center alignment, that's very simple:
pixmap_rect = self.pixmap.rect()
pixmap_rect.moveCenter(self.rect().center())
Unfortunately you can't just use that rectangle with your implementation, mostly because you are not really using a QRubberBand.
The concept of a child rubberband, using size grips for resizing, is clever, but has a lot of limitations.
While QSizeGrips make resizing easier, their behavior can't be easily "restricted": you'll probably end up trying to reimplement resize and resizeEvent (risking recursions), maybe with some tricky and convoluted mouse event checking. Also, you'll never be able to resize that "virtual" rubberband to a size smaller to the sum of the QSizeGrips' sizes, nor to a "negative" selection.
Also, in your code you never resize the actual QRubberBand geometry (but that can be done within the ResizableRubberBand.resizeEvent()).
Finally, even if you haven't implemented the selection resizing after an image resizing, you would have a lot of issues if you did (mostly because of the aforementioned minimum size restrainings).
I think that a better solution is to use a simple QRubberBand and implement its interaction directly from the widget that uses it. This lets you have finer control over it, also allowing complete resize features (not only top left and bottom right corners).
I slightly modified your base class code, as you should avoid any resizing within a resizeEvent() (even if it didn't do anything in your case, since the size argument of resize() was the same) and did unnecessary calls to _bytes2pixmap.
class ResizablePixmap(QLabel):
def __init__(self, bytes_image):
QLabel.__init__(self)
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.setAlignment(Qt.AlignCenter)
self.setStyleSheet("background-color: #ffffff;")
self.update_pixmap(bytes_image)
def update_pixmap(self, bytes_image):
self.bytes_image_edit = bytes_image
self.current_pixmap = self._bytes2pixmap(bytes_image)
def scale(self, fromResize=False):
# use a single central method for scaling; there's no need to call it upon
# creation and also resize() won't work anyway in a layout
self.setPixmap(self.current_pixmap.scaled(self.width(), self.height(),
Qt.KeepAspectRatio, Qt.SmoothTransformation))
def resizeEvent(self, event):
super(ResizablePixmap, self).resizeEvent(event)
self.scale(True)
#staticmethod
def _bytes2pixmap(raw_image):
image = QImage()
image.loadFromData(raw_image)
return QPixmap(image)
class SelectablePixmap(ResizablePixmap):
selectionActive = pyqtSignal(bool)
def __init__(self, bytes_image):
super().__init__(bytes_image)
# activate mouse tracking to change cursor on rubberband hover
self.setMouseTracking(True)
self.currentQRubberBand = None
self.rubber_band_offset = None
self.moveDirection = 0
def create_selection(self, pos):
if self.currentQRubberBand:
self.cancel_selection()
self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.currentQRubberBand.setStyle(QStyleFactory.create("Fusion"))
self.currentQRubberBand.setGeometry(pos.x(), pos.y(), 1, 1)
self.currentQRubberBand.show()
self.originQPoint = pos
self.currentQRubberBand.installEventFilter(self)
def cancel_selection(self):
self.currentQRubberBand.hide()
self.currentQRubberBand.deleteLater()
self.currentQRubberBand = None
self.originQPoint = None
self.selectionActive.emit(False)
def scale(self, fromResize=False):
if fromResize and self.currentQRubberBand:
# keep data for rubber resizing, before scaling
oldPixmapRect = self.pixmap().rect()
oldOrigin = self.currentQRubberBand.pos() - self.pixmapRect.topLeft()
super(SelectablePixmap, self).scale()
# assuming that you always align the image in the center, get the current
# pixmap rect and move the rectangle center to the current geometry
self.pixmapRect = self.pixmap().rect()
self.pixmapRect.moveCenter(self.rect().center())
if fromResize and self.currentQRubberBand:
# find the new size ratio based on the previous
xRatio = self.pixmapRect.width() / oldPixmapRect.width()
yRatio = self.pixmapRect.height() / oldPixmapRect.height()
# create a new geometry using 0-rounding for improved accuracy
self.currentQRubberBand.setGeometry(
round(oldOrigin.x() * xRatio, 0) + self.pixmapRect.x(),
round(oldOrigin.y() * yRatio + self.pixmapRect.y(), 0),
round(self.currentQRubberBand.width() * xRatio, 0),
round(self.currentQRubberBand.height() * yRatio, 0))
def updateMargins(self):
# whenever the rubber rectangle geometry changes, create virtual
# rectangles for corners and sides to ease up mouse event checking
rect = self.currentQRubberBand.geometry()
self.rubberTopLeft = QRect(rect.topLeft(), QSize(8, 8))
self.rubberTopRight = QRect(rect.topRight(), QSize(-8, 8)).normalized()
self.rubberBottomRight = QRect(rect.bottomRight(), QSize(-8, -8)).normalized()
self.rubberBottomLeft = QRect(rect.bottomLeft(), QSize(8, -8)).normalized()
self.rubberLeft = QRect(self.rubberTopLeft.bottomLeft(), self.rubberBottomLeft.topRight())
self.rubberTop = QRect(self.rubberTopLeft.topRight(), self.rubberTopRight.bottomLeft())
self.rubberRight = QRect(self.rubberTopRight.bottomLeft(), self.rubberBottomRight.topRight())
self.rubberBottom = QRect(self.rubberBottomLeft.topRight(), self.rubberBottomRight.bottomLeft())
self.rubberInnerRect = QRect(self.rubberTop.bottomLeft(), self.rubberBottom.topRight())
def eventFilter(self, source, event):
if event.type() in (QEvent.Resize, QEvent.Move):
self.updateMargins()
return super(SelectablePixmap, self).eventFilter(source, event)
def mousePressEvent(self, event):
pos = event.pos()
if not self.currentQRubberBand or not pos in self.currentQRubberBand.geometry():
if pos not in self.pixmapRect:
self.originQPoint = None
return
self.create_selection(pos)
elif pos in self.rubberTopLeft:
self.originQPoint = self.currentQRubberBand.geometry().bottomRight()
elif pos in self.rubberTopRight:
self.originQPoint = self.currentQRubberBand.geometry().bottomLeft()
elif pos in self.rubberBottomRight:
self.originQPoint = self.currentQRubberBand.geometry().topLeft()
elif pos in self.rubberBottomLeft:
self.originQPoint = self.currentQRubberBand.geometry().topRight()
elif pos in self.rubberTop:
self.originQPoint = self.currentQRubberBand.geometry().bottomLeft()
self.moveDirection = Qt.Vertical
elif pos in self.rubberBottom:
self.originQPoint = self.currentQRubberBand.geometry().topLeft()
self.moveDirection = Qt.Vertical
elif pos in self.rubberLeft:
self.originQPoint = self.currentQRubberBand.geometry().topRight()
self.moveDirection = Qt.Horizontal
elif pos in self.rubberRight:
self.originQPoint = self.currentQRubberBand.geometry().topLeft()
self.moveDirection = Qt.Horizontal
else:
self.rubber_band_offset = pos - self.currentQRubberBand.pos()
def mouseMoveEvent(self, event):
pos = event.pos()
if event.buttons() == Qt.NoButton and self.currentQRubberBand:
if pos in self.rubberTopLeft or pos in self.rubberBottomRight:
self.setCursor(Qt.SizeFDiagCursor)
elif pos in self.rubberTopRight or pos in self.rubberBottomLeft:
self.setCursor(Qt.SizeBDiagCursor)
elif pos in self.rubberLeft or pos in self.rubberRight:
self.setCursor(Qt.SizeHorCursor)
elif pos in self.rubberTop or pos in self.rubberBottom:
self.setCursor(Qt.SizeVerCursor)
elif pos in self.rubberInnerRect:
self.setCursor(Qt.SizeAllCursor)
else:
self.unsetCursor()
elif event.buttons():
if self.rubber_band_offset:
target = pos - self.rubber_band_offset
rect = QRect(target, self.currentQRubberBand.size())
# limit positioning of the selection to the image rectangle
if rect.x() < self.pixmapRect.x():
rect.moveLeft(self.pixmapRect.x())
elif rect.right() > self.pixmapRect.right():
rect.moveRight(self.pixmapRect.right())
if rect.y() < self.pixmapRect.y():
rect.moveTop(self.pixmapRect.y())
elif rect.bottom() > self.pixmapRect.bottom():
rect.moveBottom(self.pixmapRect.bottom())
self.currentQRubberBand.setGeometry(rect)
elif self.originQPoint:
if self.moveDirection == Qt.Vertical:
# keep the X fixed to the current right, so that only the
# vertical position is changed
pos.setX(self.currentQRubberBand.geometry().right())
else:
# limit the X to the pixmapRect extent
if pos.x() < self.pixmapRect.x():
pos.setX(self.pixmapRect.x())
elif pos.x() > self.pixmapRect.right():
pos.setX(self.pixmapRect.right())
if self.moveDirection == Qt.Horizontal:
# same as before, but for the Y position
pos.setY(self.currentQRubberBand.geometry().bottom())
else:
# limit the Y to the pixmapRect extent
if pos.y() < self.pixmapRect.y():
pos.setY(self.pixmapRect.y())
elif pos.y() > self.pixmapRect.bottom():
pos.setY(self.pixmapRect.bottom())
rect = QRect(self.originQPoint, pos)
self.currentQRubberBand.setGeometry(rect.normalized())
def mouseReleaseEvent(self, event):
self.rubber_band_offset = None
self.originQPoint = None
self.moveDirection = 0
You could store width and height of the image (before you create the pixmap from bytes) into global variable and then use getter to access it from outside of class.