Hi,
I have a question regarding QGraphicsItem. What I am trying to do is to recreate an “Osipa Style” face controls but in PyQt so that animators can control the face from the gui and not from a node in the viewport.
This is the code I currently wrote. As you can see I started from the example found in the PyQt folder and modified for my purposes, so I apologize if it’s still in a messy state.
If you save the code in a .pyw and run it, from the command prompt, you will see that this prototype is working, as it’s spitting out the values that I will need to plug into my node in Maya, however, I can’t find a way to have the Item go over the rectangle area when the mouse moves.
Do you have an idea on how I can fix it?
#!/usr/bin/env python
from PyQt4 import QtCore, QtGui
class Node(QtGui.QGraphicsItem):
Type = QtGui.QGraphicsItem.UserType + 1
def __init__(self, graphWidget):
super(Node, self).__init__()
self.graph = graphWidget
self.newPos = QtCore.QPointF()
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable)
self.setFlag(QtGui.QGraphicsItem.ItemSendsGeometryChanges)
self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
self.setZValue(1)
def type(self):
return Node.Type
def calculateForces(self):
# normalize the coordinates
# print self.x()
# print self.y()
norX = self.x()/50
norY = (self.y()/50) * (-1)
if not self.scene() or self.scene().mouseGrabberItem() is self:
#check where X is
if (norX>1.0):
self.newPos.setX(50)
elif (norX<(-1.0)):
self.newPos.setX(-50)
else:
self.newPos.setX(norX*50)
#check where Y is
if (norY>1.0):
self.newPos.setY(-50)
elif (norY<(-1.0)):
self.newPos.setY(50)
else:
self.newPos.setY(norY*50*-1)
print self.newPos.x()/50
print (self.newPos.y()/50) * -1
#self.newPos = self.pos()
return
def advance(self):
if self.newPos == self.pos():
return False
self.setPos(self.newPos)
return True
def boundingRect(self):
adjust = 2.0
return QtCore.QRectF(-10 - adjust, -10 - adjust, 23 + adjust,
23 + adjust)
def shape(self):
path = QtGui.QPainterPath()
path.addEllipse(-10, -10, 20, 20)
return path
def paint(self, painter, option, widget):
painter.setPen(QtCore.Qt.NoPen)
painter.setBrush(QtCore.Qt.darkGray)
painter.drawEllipse(-7, -7, 20, 20)
gradient = QtGui.QRadialGradient(-3, -3, 10)
if option.state & QtGui.QStyle.State_Sunken:
gradient.setCenter(3, 3)
gradient.setFocalPoint(3, 3)
gradient.setColorAt(1, QtGui.QColor(QtCore.Qt.yellow).light(120))
gradient.setColorAt(0, QtGui.QColor(QtCore.Qt.darkYellow).light(120))
else:
gradient.setColorAt(0, QtCore.Qt.yellow)
gradient.setColorAt(1, QtCore.Qt.darkYellow)
painter.setBrush(QtGui.QBrush(gradient))
painter.setPen(QtGui.QPen(QtCore.Qt.black, 0))
painter.drawEllipse(-10, -10, 20, 20)
def itemChange(self, change, value):
if change == QtGui.QGraphicsItem.ItemPositionHasChanged:
self.graph.itemMoved()
return super(Node, self).itemChange(change, value)
def mousePressEvent(self, event):
self.update()
super(Node, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
self.update()
super(Node, self).mouseReleaseEvent(event)
class GraphWidget(QtGui.QGraphicsView):
def __init__(self):
super(GraphWidget, self).__init__()
self.timerId = 0
scene = QtGui.QGraphicsScene(self)
scene.setItemIndexMethod(QtGui.QGraphicsScene.NoIndex)
scene.setSceneRect(-50, -50, 100, 100)
self.setScene(scene)
self.setCacheMode(QtGui.QGraphicsView.CacheBackground)
self.setViewportUpdateMode(QtGui.QGraphicsView.BoundingRectViewportUpdate)
self.setRenderHint(QtGui.QPainter.Antialiasing)
self.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtGui.QGraphicsView.AnchorViewCenter)
self.message = "The coordinates are:"
self.coordX = 0
self.coordY = 0
self.coord = ""
self.centerNode = Node(self)
scene.addItem(self.centerNode)
self.centerNode.setPos(0, 0)
self.scale(1, 1)
self.setMinimumSize(100, 100)
self.setWindowTitle("Face Controls")
def itemMoved(self):
if not self.timerId:
self.timerId = self.startTimer(1000 / 25)
def keyPressEvent(self, event):
key = event.key()
if key == QtCore.Qt.Key_Up:
self.centerNode.moveBy(0, -20)
elif key == QtCore.Qt.Key_Down:
self.centerNode.moveBy(0, 20)
elif key == QtCore.Qt.Key_Left:
self.centerNode.moveBy(-20, 0)
elif key == QtCore.Qt.Key_Right:
self.centerNode.moveBy(20, 0)
elif key == QtCore.Qt.Key_Plus:
self.scaleView(1.2)
elif key == QtCore.Qt.Key_Minus:
self.scaleView(1 / 1.2)
elif key == QtCore.Qt.Key_Space or key == QtCore.Qt.Key_Enter:
for item in self.scene().items():
if isinstance(item, Node):
item.setPos(-150 + QtCore.qrand() % 300, -150 + QtCore.qrand() % 300)
else:
super(GraphWidget, self).keyPressEvent(event)
def timerEvent(self, event):
nodes = [item for item in self.scene().items() if isinstance(item, Node)]
for node in nodes:
node.calculateForces()
itemsMoved = False
for node in nodes:
if node.advance():
itemsMoved = True
if not itemsMoved:
self.killTimer(self.timerId)
self.timerId = 0
def drawBackground(self, painter, rect):
# Shadow.
sceneRect = self.sceneRect()
rightShadow = QtCore.QRectF(sceneRect.right(), sceneRect.top() + 5, 5,
sceneRect.height())
bottomShadow = QtCore.QRectF(sceneRect.left() + 5, sceneRect.bottom(),
sceneRect.width(), 5)
if rightShadow.intersects(rect) or rightShadow.contains(rect):
painter.fillRect(rightShadow, QtCore.Qt.darkGray)
if bottomShadow.intersects(rect) or bottomShadow.contains(rect):
painter.fillRect(bottomShadow, QtCore.Qt.darkGray)
# Fill.
gradient = QtGui.QLinearGradient(sceneRect.topLeft(),
sceneRect.bottomRight())
gradient.setColorAt(0, QtCore.Qt.white)
gradient.setColorAt(1, QtCore.Qt.lightGray)
painter.fillRect(rect.intersect(sceneRect), QtGui.QBrush(gradient))
painter.setBrush(QtCore.Qt.NoBrush)
painter.drawRect(sceneRect)
# Text.
textRect = QtCore.QRectF(sceneRect.left() + 4, sceneRect.top() + 4,
sceneRect.width() - 4, sceneRect.height() - 4)
font = painter.font()
font.setPointSize(8)
painter.setFont(font)
painter.setPen(QtCore.Qt.lightGray)
painter.drawText(textRect.translated(2, 2), self.message)
painter.setPen(QtCore.Qt.black)
painter.drawText(textRect, self.message)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
QtCore.qsrand(QtCore.QTime(0,0,0).secsTo(QtCore.QTime.currentTime()))
widget = GraphWidget()
widget.show()
sys.exit(app.exec_())
Thanks
Carlo