#!/usr/bin/python # $Id: xformer.py 87 2009-06-14 18:17:35Z dal $ ## ## Xformer - Test script demonstrating the QGraphics system's use of QTransform. ## ## Copyright (c) 2008 by Doug Letterman ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## """A small pyQt program demonstrating the Qt Graphics System's use of a 2D transformation matrix in the form of QTransform. """ __version__ = "$Revision: 87 $" from PyQt4 import QtGui, QtCore import sys import math import sys import os ## A checker pattern for use as the triangle's background checker = [ ## columns rows colors chars-per-pixel ## "16 16 2 1", " c gray100", ". c black", ## pixels "........ ", "........ ", "........ ", "........ ", "........ ", "........ ", "........ ", "........ ", " ........", " ........", " ........", " ........", " ........", " ........", " ........", " ........" ] ## Begin auto-generated ui from PyQt4 import QtCore, QtGui class Ui_Xformer(object): def setupUi(self, Xformer): Xformer.setObjectName("Xformer") Xformer.resize(467,518) self.mainGridLayout = QtGui.QGridLayout(Xformer) self.mainGridLayout.setMargin(4) self.mainGridLayout.setSpacing(4) self.mainGridLayout.setObjectName("mainGridLayout") self.graphicsView = QtGui.QGraphicsView(Xformer) self.graphicsView.setObjectName("graphicsView") self.mainGridLayout.addWidget(self.graphicsView,0,0,1,1) self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setSpacing(4) self.verticalLayout.setObjectName("verticalLayout") self.matrixGroupBox = QtGui.QGroupBox(Xformer) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed,QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.matrixGroupBox.sizePolicy().hasHeightForWidth()) self.matrixGroupBox.setSizePolicy(sizePolicy) self.matrixGroupBox.setObjectName("matrixGroupBox") self.gridLayout = QtGui.QGridLayout(self.matrixGroupBox) self.gridLayout.setMargin(4) self.gridLayout.setObjectName("gridLayout") self.m11Box = QtGui.QDoubleSpinBox(self.matrixGroupBox) self.m11Box.setEnabled(False) self.m11Box.setReadOnly(False) self.m11Box.setMinimum(-99.0) self.m11Box.setMaximum(99.0) self.m11Box.setSingleStep(0.01) self.m11Box.setObjectName("m11Box") self.gridLayout.addWidget(self.m11Box,0,0,1,1) self.m12Box = QtGui.QDoubleSpinBox(self.matrixGroupBox) self.m12Box.setEnabled(False) self.m12Box.setReadOnly(False) self.m12Box.setMinimum(-99.0) self.m12Box.setMaximum(99.0) self.m12Box.setSingleStep(0.01) self.m12Box.setObjectName("m12Box") self.gridLayout.addWidget(self.m12Box,0,1,1,1) self.m21Box = QtGui.QDoubleSpinBox(self.matrixGroupBox) self.m21Box.setEnabled(False) self.m21Box.setReadOnly(False) self.m21Box.setMinimum(-99.0) self.m21Box.setMaximum(99.0) self.m21Box.setSingleStep(0.01) self.m21Box.setObjectName("m21Box") self.gridLayout.addWidget(self.m21Box,1,0,1,1) self.m22Box = QtGui.QDoubleSpinBox(self.matrixGroupBox) self.m22Box.setEnabled(False) self.m22Box.setReadOnly(False) self.m22Box.setMinimum(-99.0) self.m22Box.setMaximum(99.0) self.m22Box.setSingleStep(0.01) self.m22Box.setObjectName("m22Box") self.gridLayout.addWidget(self.m22Box,1,1,1,1) self.dxBox = QtGui.QDoubleSpinBox(self.matrixGroupBox) self.dxBox.setEnabled(False) self.dxBox.setReadOnly(False) self.dxBox.setMinimum(-99.99) self.dxBox.setObjectName("dxBox") self.gridLayout.addWidget(self.dxBox,2,0,1,1) self.dyBox = QtGui.QDoubleSpinBox(self.matrixGroupBox) self.dyBox.setEnabled(False) self.dyBox.setReadOnly(False) self.dyBox.setMinimum(-99.99) self.dyBox.setObjectName("dyBox") self.gridLayout.addWidget(self.dyBox,2,1,1,1) self.verticalLayout.addWidget(self.matrixGroupBox) self.row1 = QtGui.QHBoxLayout() self.row1.setObjectName("row1") self.rotationLabel = QtGui.QLabel(Xformer) self.rotationLabel.setObjectName("rotationLabel") self.row1.addWidget(self.rotationLabel) self.rotationSpinBox = QtGui.QDoubleSpinBox(Xformer) self.rotationSpinBox.setEnabled(False) self.rotationSpinBox.setMinimum(-180.0) self.rotationSpinBox.setMaximum(180.0) self.rotationSpinBox.setObjectName("rotationSpinBox") self.row1.addWidget(self.rotationSpinBox) self.verticalLayout.addLayout(self.row1) self.rotationSlider = QtGui.QSlider(Xformer) self.rotationSlider.setEnabled(False) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.rotationSlider.sizePolicy().hasHeightForWidth()) self.rotationSlider.setSizePolicy(sizePolicy) self.rotationSlider.setMinimum(-180) self.rotationSlider.setMaximum(180) self.rotationSlider.setOrientation(QtCore.Qt.Horizontal) self.rotationSlider.setObjectName("rotationSlider") self.verticalLayout.addWidget(self.rotationSlider) self.row3 = QtGui.QHBoxLayout() self.row3.setObjectName("row3") self.scaleXLabel = QtGui.QLabel(Xformer) self.scaleXLabel.setObjectName("scaleXLabel") self.row3.addWidget(self.scaleXLabel) self.scaleXSpinBox = QtGui.QDoubleSpinBox(Xformer) self.scaleXSpinBox.setEnabled(False) self.scaleXSpinBox.setMinimum(-100.0) self.scaleXSpinBox.setMaximum(100.0) self.scaleXSpinBox.setSingleStep(0.01) self.scaleXSpinBox.setProperty("value",QtCore.QVariant(1.0)) self.scaleXSpinBox.setObjectName("scaleXSpinBox") self.row3.addWidget(self.scaleXSpinBox) self.verticalLayout.addLayout(self.row3) self.scaleXSlider = QtGui.QSlider(Xformer) self.scaleXSlider.setEnabled(False) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.scaleXSlider.sizePolicy().hasHeightForWidth()) self.scaleXSlider.setSizePolicy(sizePolicy) self.scaleXSlider.setMinimum(-1000) self.scaleXSlider.setMaximum(1000) self.scaleXSlider.setProperty("value",QtCore.QVariant(100)) self.scaleXSlider.setOrientation(QtCore.Qt.Horizontal) self.scaleXSlider.setObjectName("scaleXSlider") self.verticalLayout.addWidget(self.scaleXSlider) self.row5 = QtGui.QHBoxLayout() self.row5.setObjectName("row5") self.scaleYLabel = QtGui.QLabel(Xformer) self.scaleYLabel.setObjectName("scaleYLabel") self.row5.addWidget(self.scaleYLabel) self.scaleYSpinBox = QtGui.QDoubleSpinBox(Xformer) self.scaleYSpinBox.setEnabled(False) self.scaleYSpinBox.setMinimum(-100.0) self.scaleYSpinBox.setMaximum(100.0) self.scaleYSpinBox.setSingleStep(0.01) self.scaleYSpinBox.setProperty("value",QtCore.QVariant(1.0)) self.scaleYSpinBox.setObjectName("scaleYSpinBox") self.row5.addWidget(self.scaleYSpinBox) self.verticalLayout.addLayout(self.row5) self.scaleYSlider = QtGui.QSlider(Xformer) self.scaleYSlider.setEnabled(False) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.scaleYSlider.sizePolicy().hasHeightForWidth()) self.scaleYSlider.setSizePolicy(sizePolicy) self.scaleYSlider.setMinimum(-1000) self.scaleYSlider.setMaximum(1000) self.scaleYSlider.setProperty("value",QtCore.QVariant(100)) self.scaleYSlider.setOrientation(QtCore.Qt.Horizontal) self.scaleYSlider.setObjectName("scaleYSlider") self.verticalLayout.addWidget(self.scaleYSlider) self.row7 = QtGui.QHBoxLayout() self.row7.setObjectName("row7") self.shearXLabel = QtGui.QLabel(Xformer) self.shearXLabel.setObjectName("shearXLabel") self.row7.addWidget(self.shearXLabel) self.shearXSpinBox = QtGui.QDoubleSpinBox(Xformer) self.shearXSpinBox.setEnabled(False) self.shearXSpinBox.setMinimum(-100.0) self.shearXSpinBox.setMaximum(100.0) self.shearXSpinBox.setSingleStep(0.01) self.shearXSpinBox.setProperty("value",QtCore.QVariant(0.0)) self.shearXSpinBox.setObjectName("shearXSpinBox") self.row7.addWidget(self.shearXSpinBox) self.verticalLayout.addLayout(self.row7) self.shearXSlider = QtGui.QSlider(Xformer) self.shearXSlider.setEnabled(False) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.shearXSlider.sizePolicy().hasHeightForWidth()) self.shearXSlider.setSizePolicy(sizePolicy) self.shearXSlider.setMinimum(-1000) self.shearXSlider.setMaximum(1000) self.shearXSlider.setProperty("value",QtCore.QVariant(1)) self.shearXSlider.setOrientation(QtCore.Qt.Horizontal) self.shearXSlider.setObjectName("shearXSlider") self.verticalLayout.addWidget(self.shearXSlider) self.row9 = QtGui.QHBoxLayout() self.row9.setObjectName("row9") self.shearYLabel = QtGui.QLabel(Xformer) self.shearYLabel.setObjectName("shearYLabel") self.row9.addWidget(self.shearYLabel) self.shearYSpinBox = QtGui.QDoubleSpinBox(Xformer) self.shearYSpinBox.setEnabled(False) self.shearYSpinBox.setMinimum(-100.0) self.shearYSpinBox.setMaximum(100.0) self.shearYSpinBox.setSingleStep(0.01) self.shearYSpinBox.setProperty("value",QtCore.QVariant(0.0)) self.shearYSpinBox.setObjectName("shearYSpinBox") self.row9.addWidget(self.shearYSpinBox) self.verticalLayout.addLayout(self.row9) self.shearYSlider = QtGui.QSlider(Xformer) self.shearYSlider.setEnabled(False) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.shearYSlider.sizePolicy().hasHeightForWidth()) self.shearYSlider.setSizePolicy(sizePolicy) self.shearYSlider.setMinimum(-1000) self.shearYSlider.setMaximum(1000) self.shearYSlider.setProperty("value",QtCore.QVariant(1)) self.shearYSlider.setOrientation(QtCore.Qt.Horizontal) self.shearYSlider.setObjectName("shearYSlider") self.verticalLayout.addWidget(self.shearYSlider) spacerItem = QtGui.QSpacerItem(179,17,QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Expanding) self.verticalLayout.addItem(spacerItem) self.mainGridLayout.addLayout(self.verticalLayout,0,1,1,1) self.retranslateUi(Xformer) QtCore.QMetaObject.connectSlotsByName(Xformer) def retranslateUi(self, Xformer): Xformer.setWindowTitle(QtGui.QApplication.translate("Xformer", "Xformer", None, QtGui.QApplication.UnicodeUTF8)) self.matrixGroupBox.setTitle(QtGui.QApplication.translate("Xformer", "Transformation matrix", None, QtGui.QApplication.UnicodeUTF8)) self.rotationLabel.setText(QtGui.QApplication.translate("Xformer", "Rotation", None, QtGui.QApplication.UnicodeUTF8)) self.scaleXLabel.setText(QtGui.QApplication.translate("Xformer", "ScaleX", None, QtGui.QApplication.UnicodeUTF8)) self.scaleYLabel.setText(QtGui.QApplication.translate("Xformer", "ScaleY", None, QtGui.QApplication.UnicodeUTF8)) self.shearXLabel.setText(QtGui.QApplication.translate("Xformer", "ShearX", None, QtGui.QApplication.UnicodeUTF8)) self.shearYLabel.setText(QtGui.QApplication.translate("Xformer", "ShearY", None, QtGui.QApplication.UnicodeUTF8)) ## End autogenerated uic code class XformScene(QtGui.QGraphicsScene): """Class defines a simple QGraphicsScene with a single triangle drawn in the center of the view. """ def __init__(self, parent=None): QtGui.QGraphicsScene.__init__(self, parent) self.triangle = None def setupScene(self): polygon = QtGui.QPolygonF( [ QtCore.QPointF(-10,20), QtCore.QPointF(0,-10), QtCore.QPointF(10,20), QtCore.QPointF(-10,20) ] ) self.triangle = QtGui.QGraphicsPolygonItem(polygon) self.triangle.setFlags(QtGui.QGraphicsItem.ItemIsSelectable | QtGui.QGraphicsItem.ItemIsMovable) self.triangle.setBrush(QtGui.QBrush(QtGui.QPixmap(checker))) self.addItem(self.triangle) self.triangle.setSelected(1) def transformChanged(self, xform): items = self.selectedItems() for item in items: item.setTransform(xform) class Xformer(QtGui.QWidget, Ui_Xformer): """Class making up the main widget and its behavior. Contains an XformScene the left of the window and a large vertical panel to the right showing the properties of the selected item's QTransform. All of the rotation/scale/shear sliders directly modify the triangle's transformation matrix before calling slotTransformChanged. This slot resets all of the matrix widgets at the top so that they display the new matrix. Each of the matrix widgets' valueChanged signals are connected to the slotTransformBoxChanged method. This enables blockSignals on all the widgets to prevent infinite looping and sets the final state of all the sliders and matrix spin boxes so that they are in agreement. """ def __init__(self, parent=None): super(Xformer, self).__init__(parent) self.setupUi(self) self.scene = XformScene(self) self.graphicsView.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform) self.graphicsView.setScene(self.scene) QtCore.QObject.connect(self.m11Box, QtCore.SIGNAL("valueChanged(double)"), self.slotTransformBoxChanged) QtCore.QObject.connect(self.m12Box, QtCore.SIGNAL("valueChanged(double)"), self.slotTransformBoxChanged) QtCore.QObject.connect(self.m21Box, QtCore.SIGNAL("valueChanged(double)"), self.slotTransformBoxChanged) QtCore.QObject.connect(self.m22Box, QtCore.SIGNAL("valueChanged(double)"), self.slotTransformBoxChanged) QtCore.QObject.connect(self.dxBox, QtCore.SIGNAL("valueChanged(double)"), self.slotTransformBoxChanged) QtCore.QObject.connect(self.dyBox, QtCore.SIGNAL("valueChanged(double)"), self.slotTransformBoxChanged) QtCore.QObject.connect(self.scene, QtCore.SIGNAL("selectionChanged()"), self.slotSceneSelectionChanged) QtCore.QObject.connect(self.rotationSpinBox, QtCore.SIGNAL("valueChanged(double)"), self.slotRotationSpinBoxChanged) QtCore.QObject.connect(self.rotationSlider, QtCore.SIGNAL("valueChanged(int)"), self.slotRotationSliderChanged) QtCore.QObject.connect(self.scaleXSpinBox, QtCore.SIGNAL("valueChanged(double)"), self.slotScaleXSpinBoxChanged) QtCore.QObject.connect(self.scaleXSlider, QtCore.SIGNAL("valueChanged(int)"), self.slotScaleXSliderChanged) QtCore.QObject.connect(self.scaleXSpinBox, QtCore.SIGNAL("valueChanged(double)"), self.slotScaleXSpinBoxChanged) QtCore.QObject.connect(self.scaleYSlider, QtCore.SIGNAL("valueChanged(int)"), self.slotScaleYSliderChanged) QtCore.QObject.connect(self.scaleYSpinBox, QtCore.SIGNAL("valueChanged(double)"), self.slotScaleYSpinBoxChanged) QtCore.QObject.connect(self.shearXSpinBox, QtCore.SIGNAL("valueChanged(double)"), self.slotShearXSpinBoxChanged) QtCore.QObject.connect(self.shearXSlider, QtCore.SIGNAL("valueChanged(int)"), self.slotShearXSliderChanged) QtCore.QObject.connect(self.shearYSpinBox, QtCore.SIGNAL("valueChanged(double)"), self.slotShearYSpinBoxChanged) QtCore.QObject.connect(self.shearYSlider, QtCore.SIGNAL("valueChanged(int)"), self.slotShearYSliderChanged) self.scene.setupScene() def slotCleanup(self): """Method that's connected to the aboutToQuit signal on the QApplication that makes sure the scene doesn't fire selectionChanged during its deletion. This prevents an 'underlying c/c++ object deleted...' runtime error. """ self.scene.blockSignals(1) def slotSceneSelectionChanged(self): selectedItems = self.scene.selectedItems() if len(selectedItems): ## If something is selected enable all the widgets self.m11Box.setEnabled(1) self.m12Box.setEnabled(1) self.m21Box.setEnabled(1) self.m22Box.setEnabled(1) self.dxBox.setEnabled(1) self.dyBox.setEnabled(1) self.rotationSlider.setEnabled(1) self.rotationSpinBox.setEnabled(1) self.scaleXSlider.setEnabled(1) self.scaleXSpinBox.setEnabled(1) self.scaleYSlider.setEnabled(1) self.scaleYSpinBox.setEnabled(1) self.shearXSpinBox.setEnabled(1) self.shearYSpinBox.setEnabled(1) self.shearXSlider.setEnabled(1) self.shearYSlider.setEnabled(1) self.slotTransformChanged() else: ## Nothing's selected, disable all the widgets self.m11Box.setEnabled(0) self.m12Box.setEnabled(0) self.m21Box.setEnabled(0) self.m22Box.setEnabled(0) self.dxBox.setEnabled(0) self.dyBox.setEnabled(0) self.rotationSlider.setEnabled(0) self.rotationSpinBox.setEnabled(0) self.scaleXSlider.setEnabled(0) self.scaleXSpinBox.setEnabled(0) self.scaleYSlider.setEnabled(0) self.scaleYSpinBox.setEnabled(0) self.shearXSpinBox.setEnabled(0) self.shearYSpinBox.setEnabled(0) self.shearXSlider.setEnabled(0) self.shearYSlider.setEnabled(0) def slotTransformChanged(self): """Slot is called when one of the rotation/shear/scale sliders is changed. Updates the xform boxes at the top to reflect the new state. """ selectedItems = self.scene.selectedItems() if len(selectedItems): xform = selectedItems[0].transform() ## Suppress all signals so we don't fire transformBoxChanged ## multiple times when we change the xform boxes self.m11Box.blockSignals(1) self.m12Box.blockSignals(1) self.m21Box.blockSignals(1) self.m22Box.blockSignals(1) self.dxBox.blockSignals(1) self.dyBox.blockSignals(1) ## Now update the xform boxes to reflect the new transform self.m11Box.setValue(xform.m11()) self.m12Box.setValue(xform.m12()) self.m21Box.setValue(xform.m21()) self.m22Box.setValue(xform.m22()) self.dxBox.setValue(xform.dx()) self.dyBox.setValue(xform.dy()) ## Re-enable signals self.m11Box.blockSignals(0) self.m12Box.blockSignals(0) self.m21Box.blockSignals(0) self.m22Box.blockSignals(0) self.dxBox.blockSignals(0) self.dyBox.blockSignals(0) ## Now manually call transformBoxChanged self.slotTransformBoxChanged() def slotTransformBoxChanged(self): """Slot is called when one of the matrix boxes at the top changes. It updates the transform rotation/shear/scale sliders at the bottom to reflect the new values. """ xform = QtGui.QTransform(self.m11Box.value(), self.m12Box.value(), self.m21Box.value(), self.m22Box.value(), self.dxBox.value(), self.dyBox.value()) selectedItems = self.scene.selectedItems() if len(selectedItems): selectedItems[0].setTransform(xform) ## Find the rotation by transforming a point at 1, 0 rotation = 180./math.pi * math.atan2(-xform.m21(), xform.m11()) ## Block all signals coming out of the sliders ## to prevent an infinite loop of signals self.rotationSpinBox.blockSignals(1) self.rotationSlider.blockSignals(1) self.scaleXSpinBox.blockSignals(1) self.scaleXSlider.blockSignals(1) self.scaleYSpinBox.blockSignals(1) self.scaleYSlider.blockSignals(1) self.shearXSpinBox.blockSignals(1) self.shearXSlider.blockSignals(1) self.shearYSpinBox.blockSignals(1) self.shearYSlider.blockSignals(1) ## Set all the slider values manually so ## that they reflect any changes made to the xform boxes above self.rotationSpinBox.setValue(rotation) self.rotationSlider.setValue(rotation) self.scaleXSpinBox.setValue(xform.m11()) self.scaleXSlider.setValue(xform.m11()*100) self.scaleYSpinBox.setValue(xform.m22()) self.scaleYSlider.setValue(xform.m22()*100) self.shearXSpinBox.setValue(xform.m21()) self.shearXSlider.setValue(xform.m21()*100) self.shearYSpinBox.setValue(xform.m12()) self.shearYSlider.setValue(xform.m12()*100) ## Restore the sliders so they start sending signals again self.rotationSlider.blockSignals(0) self.rotationSpinBox.blockSignals(0) self.scaleXSlider.blockSignals(0) self.scaleXSpinBox.blockSignals(0) self.scaleYSlider.blockSignals(0) self.scaleYSpinBox.blockSignals(0) self.shearXSlider.blockSignals(0) self.shearXSpinBox.blockSignals(0) self.shearYSlider.blockSignals(0) self.shearYSpinBox.blockSignals(0) def slotRotationSliderChanged(self, val): selectedItems = self.scene.selectedItems() if len(selectedItems): xform = selectedItems[0].transform() xform2 = QtGui.QTransform(xform.m11(), xform.m12(), xform.m21(), xform.m22(), xform.dx(), xform.dy()) ## Find the starting rotation by transforming a point at 1, 0 rotation = 180./math.pi * math.atan2(-xform.m21(), xform.m11()) xform2.rotate(-rotation) xform2.rotate(float(val)) selectedItems[0].setTransform(xform2) self.slotTransformChanged() def slotScaleXSliderChanged(self, val): selectedItems = self.scene.selectedItems() if len(selectedItems): xform = selectedItems[0].transform() xform2 = QtGui.QTransform(val/100., xform.m12(), xform.m21(), xform.m22(), xform.dx(), xform.dy()) selectedItems[0].setTransform(xform2) self.slotTransformChanged() def slotScaleYSliderChanged(self, val): selectedItems = self.scene.selectedItems() if len(selectedItems): xform = selectedItems[0].transform() xform2 = QtGui.QTransform(xform.m11(), xform.m12(), xform.m21(), val/100., xform.dx(), xform.dy()) selectedItems[0].setTransform(xform2) self.slotTransformChanged() def slotShearXSliderChanged(self, val): selectedItems = self.scene.selectedItems() if len(selectedItems): xform = selectedItems[0].transform() xform2 = QtGui.QTransform(xform.m11(), xform.m12(), (val/100.), xform.m22(), xform.dx(), xform.dy()) selectedItems[0].setTransform(xform2) self.shearXSpinBox.setValue(float(val/100.)) self.slotTransformChanged() def slotShearYSliderChanged(self, val): selectedItems = self.scene.selectedItems() if len(selectedItems): xform = selectedItems[0].transform() xform2 = QtGui.QTransform(xform.m11(), (val/100.), xform.m21(), xform.m22(), xform.dx(), xform.dy()) selectedItems[0].setTransform(xform2) self.shearYSpinBox.setValue(float(val/100.)) self.slotTransformChanged() def slotRotationSpinBoxChanged(self, val): self.rotationSlider.setValue(int(val+0.5)) def slotScaleXSpinBoxChanged(self, val): self.scaleXSlider.setValue(int(val*100)) def slotScaleYSpinBoxChanged(self, val): self.scaleYSlider.setValue(int(val*100)) def slotShearXSpinBoxChanged(self, val): self.shearXSlider.setValue(int(val*100)) def slotShearYSpinBoxChanged(self, val): self.shearYSlider.setValue(int(val*100)) def main(): app = QtGui.QApplication(sys.argv) xformer = Xformer() ## Make sure that the xformer does some cleanup before quit to prevent runtime errors QtCore.QObject.connect(app, QtCore.SIGNAL("aboutToQuit()"), xformer.slotCleanup) xformer.show() sys.exit(app.exec_()) if __name__ == "__main__": main()