一、坐標系簡 介。
Qt中每一個窗口都有一個坐標系,默認的,窗口左 上角為坐標原點,然后水平向右依次增大,水平向左依次減小,垂直向下依次增大,垂直向上依次減小。原點即為(0,0)點,然后以像素為單位增減。
例如:
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::red);
painter.drawRect(0,0,100,100);
painter.setBrush(Qt::yellow);
painter.drawRect(-50,-50,100,100);
}
我們先在原點(0,0)繪制了一個長寬都是100 像素的紅色矩形,又在(-50,-50)點繪制了一個同樣大小的黃色矩形。可以看到,我們只能看到黃色矩形的一部分。效果如下圖。
二、坐標系變換。
坐標系變換是利用變換矩陣來進行的, 我們可以利用QTransform類來設置變換矩陣,因為一般我們不需要進行更改,所以這里不在涉及。下面我們只是對坐標系的平移,縮放,旋轉,扭曲等應 用進行介紹。
1.利用 translate()函數進行平移變換。
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,50,50);
painter.translate(100,100);//將點(100,100)設為原點
painter.setBrush(Qt::red);
painter.drawRect(0,0,50,50);
painter.translate(-100,-100);
painter.drawLine(0,0,20,20);
}
效果如下。
這里將 (100,100)點作為了原點,所以此時(100,100)就是(0,0)點,以前的(0,0)點就是
(-100,-100) 點。要想使原來的(0,0)點重新成為原點,就是將(-100,-100)設為原點。
2.利 用scale()函數進行比例變換,實現縮放效果。
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,100,100);
painter.scale(2,2);//放大兩倍
painter.setBrush(Qt::red);
painter.drawRect(50,50,50,50);
}
效果如下。
可以看 到,painter.scale(2,2),是將橫縱坐標都擴大了兩倍,現在的(50,50)點就相當於以前的
(100,100) 點。
3. 利用shear()函數就行扭曲變換。
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,50,50);
painter.shear(0,1);//縱向扭曲 變形
painter.setBrush(Qt::red);
painter.drawRect(50,0,50,50);
}
效果如下。
這里,painter.shear(0,1),是對縱向進行扭曲,0表示不扭曲,當將第一個0更改時就會對橫行進行扭曲,關於扭曲變換 到底是什么效果,你觀察一下是很容易發現的。
4.利用rotate()函數進行比例變換,實現縮放效果。
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawLine(0,0,100,0);
painter.rotate(30);//以原點為中心,順時針旋轉30度
painter.drawLine(0,0,100,0);
painter.translate(100,100);
painter.rotate(30);
painter.drawLine(0,0,100,0);
}
效果如下。
因為默認的rotate()函數是以原點為中心進行順時針旋轉的,所以我們要想使其以其他點為中心進行旋轉,就要先進行原點的變換。這 里的painter.translate(100,100)將(100,100)設置為新的原點,想讓直線以其為中心進行旋轉,可是你已經發現效果並非如 此。是什么原因呢?我們添加一條語句,如下:
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawLine(0,0,100,0);
painter.rotate(30); //以原點為中心,順時針旋轉30度
painter.drawLine(0,0,100,0);
painter.rotate(-30);
painter.translate(100,100);
painter.rotate(30);
painter.drawLine(0,0,100,0);
}
效果如下。
這時就是我們想要的效果了。我們加的一句代碼為painter.rotate(-30),這是因為前面已經將坐標旋轉了30度,我們需 要將其再旋轉回去,才能是以前正常的坐標系統。不光這個函數如此,這里介紹的這幾個函數均如此,所以很容易出錯。下面我們將利用兩個函數來很好的解決這個 問題。
三、坐標系狀態的保護。
我們可以先利用save()函數來保存坐標系現在的狀態,然后進行變換操作,操作完之后,再用restore()函數將以前的坐標系狀 態恢復,其實就是一個入棧和出棧的操作。
例如:
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.save();// 保存坐標系狀態
painter.translate(100,100);
painter.drawLine(0,0,50,50);
painter.restore();//恢復以前的坐標系狀態
painter.drawLine(0,0,50,50);
}
效果如下。
利用好這兩個函數,可以實現快速的坐標系切換,繪制出不同的圖形。
Qt中每一個窗口都有一個坐標系,默認的,窗口左 上角為坐標原點,然后水平向右依次增大,水平向左依次減小,垂直向下依次增大,垂直向上依次減小。原點即為(0,0)點,然后以像素為單位增減。
例如:
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::red);
painter.drawRect(0,0,100,100);
painter.setBrush(Qt::yellow);
painter.drawRect(-50,-50,100,100);
}
我們先在原點(0,0)繪制了一個長寬都是100 像素的紅色矩形,又在(-50,-50)點繪制了一個同樣大小的黃色矩形。可以看到,我們只能看到黃色矩形的一部分。效果如下圖。

二、坐標系變換。
坐標系變換是利用變換矩陣來進行的, 我們可以利用QTransform類來設置變換矩陣,因為一般我們不需要進行更改,所以這里不在涉及。下面我們只是對坐標系的平移,縮放,旋轉,扭曲等應 用進行介紹。
1.利用 translate()函數進行平移變換。
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,50,50);
painter.translate(100,100);//將點(100,100)設為原點
painter.setBrush(Qt::red);
painter.drawRect(0,0,50,50);
painter.translate(-100,-100);
painter.drawLine(0,0,20,20);
}
效果如下。

這里將 (100,100)點作為了原點,所以此時(100,100)就是(0,0)點,以前的(0,0)點就是
(-100,-100) 點。要想使原來的(0,0)點重新成為原點,就是將(-100,-100)設為原點。
2.利 用scale()函數進行比例變換,實現縮放效果。
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,100,100);
painter.scale(2,2);//放大兩倍
painter.setBrush(Qt::red);
painter.drawRect(50,50,50,50);
}
效果如下。

可以看 到,painter.scale(2,2),是將橫縱坐標都擴大了兩倍,現在的(50,50)點就相當於以前的
(100,100) 點。
3. 利用shear()函數就行扭曲變換。
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,50,50);
painter.shear(0,1);//縱向扭曲 變形
painter.setBrush(Qt::red);
painter.drawRect(50,0,50,50);
}
效果如下。

這里,painter.shear(0,1),是對縱向進行扭曲,0表示不扭曲,當將第一個0更改時就會對橫行進行扭曲,關於扭曲變換 到底是什么效果,你觀察一下是很容易發現的。
4.利用rotate()函數進行比例變換,實現縮放效果。
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawLine(0,0,100,0);
painter.rotate(30);//以原點為中心,順時針旋轉30度
painter.drawLine(0,0,100,0);
painter.translate(100,100);
painter.rotate(30);
painter.drawLine(0,0,100,0);
}
效果如下。

因為默認的rotate()函數是以原點為中心進行順時針旋轉的,所以我們要想使其以其他點為中心進行旋轉,就要先進行原點的變換。這 里的painter.translate(100,100)將(100,100)設置為新的原點,想讓直線以其為中心進行旋轉,可是你已經發現效果並非如 此。是什么原因呢?我們添加一條語句,如下:
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawLine(0,0,100,0);
painter.rotate(30); //以原點為中心,順時針旋轉30度
painter.drawLine(0,0,100,0);
painter.rotate(-30);
painter.translate(100,100);
painter.rotate(30);
painter.drawLine(0,0,100,0);
}
效果如下。

這時就是我們想要的效果了。我們加的一句代碼為painter.rotate(-30),這是因為前面已經將坐標旋轉了30度,我們需 要將其再旋轉回去,才能是以前正常的坐標系統。不光這個函數如此,這里介紹的這幾個函數均如此,所以很容易出錯。下面我們將利用兩個函數來很好的解決這個 問題。
三、坐標系狀態的保護。
我們可以先利用save()函數來保存坐標系現在的狀態,然后進行變換操作,操作完之后,再用restore()函數將以前的坐標系狀 態恢復,其實就是一個入棧和出棧的操作。
例如:
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.save();// 保存坐標系狀態
painter.translate(100,100);
painter.drawLine(0,0,50,50);
painter.restore();//恢復以前的坐標系狀態
painter.drawLine(0,0,50,50);
}
效果如下。

利用好這兩個函數,可以實現快速的坐標系切換,繪制出不同的圖形。
Qt 坐標系統 筆記 zz
http://cowboy.1988.blog.163.com/blog/static/7510579820115179312889/
World Corrdinates ==> Window Coordinates ==> Device Coordinates
(邏輯坐標) 世界變換 中間態坐標 窗口視口變換 (物理坐標,設備坐標)
在默認情況下,3個坐標系是一致的。世界變換
世界變換直接相關的函數:QPainter::setWorldMatrixEnabled 啟用、禁用世界變換
QPainter::setWorldTransform 設置世界變換
QPainter::worldTransform 獲取當前
QPainter::resetTransform 重置為 QTransform()
4個常用的函數
QPainter::scale
QPainter::shear
QPainter::rotate
QPainter::translate
注:它們通過直接調用的 QTransform 的相應成員直接修改世界變換void QPainter::scale(qreal sx, qreal sy)
{
...
d->state->worldMatrix.scale(sx,sy);
...
}
世界變換的兩個馬甲:
QPainter::setTransform
QPainter::transformvoid QPainter::setTransform(const QTransform &transform, bool combine )
{
setWorldTransform(transform, combine);
}
廢棄的函數(從Qt4.3開始,QTransform 取代了QMatrix 的位置,下列函數已不建議使用):
QPainter::setWorldMatrix
QPainter::worldMatrix
...窗口視口變換
直接相關:QPainter::setViewTransformEnabled 啟用、禁用 視口變換
QPainter::viewTransformEnabled
QPainter::setViewport 設置 視口(物理坐標)
QPainter::setWindow 設置 窗口(與視口是同一矩形,中間態坐標)
該變換是簡單的線性變換。復合變換
窗口視口變換和世界變換的復合:
QPainter::combinedTransformQTransform QPainter::combinedTransform() const
{
Q_D(const QPainter);
return d->state->worldMatrix * d->viewTransform();
}
典型應用:對鼠標事件的響應中,將坐標從物理坐標變換成QPainter需要的邏輯坐標仿射變換、透射變換
Qt4.3(包括)之前的 QMatrix 只支持仿射變換(Affine transformation)。
平移(Translation)
縮放(Scale)
旋轉(Rotation)
剪切(Shear)
QTransform 支持透射變換(perspective transformation)。m11 m12 m13
m21 m22 m23
m31
dx m32
dy m33
變換關系:x' = m11*x + m21*y + dx
y' = m22*y + m12*x + dy
if (is not affine) {
w' = m13*x + m23*y + m33
x' /= w'
y' /= w'
}
相關(有待學習):
射影幾何學、仿射幾何學、歐氏幾何學
World Corrdinates ==> Window Coordinates ==> Device Coordinates
(邏輯坐標) 世界變換 中間態坐標 窗口視口變換 (物理坐標,設備坐標)
在默認情況下,3個坐標系是一致的。世界變換
世界變換直接相關的函數:QPainter::setWorldMatrixEnabled 啟用、禁用世界變換
QPainter::setWorldTransform 設置世界變換
QPainter::worldTransform 獲取當前
QPainter::resetTransform 重置為 QTransform()
4個常用的函數
QPainter::scale
QPainter::shear
QPainter::rotate
QPainter::translate
注:它們通過直接調用的 QTransform 的相應成員直接修改世界變換void QPainter::scale(qreal sx, qreal sy)
{
...
d->state->worldMatrix.scale(sx,sy);
...
}
世界變換的兩個馬甲:
QPainter::setTransform
QPainter::transformvoid QPainter::setTransform(const QTransform &transform, bool combine )
{
setWorldTransform(transform, combine);
}
廢棄的函數(從Qt4.3開始,QTransform 取代了QMatrix 的位置,下列函數已不建議使用):
QPainter::setWorldMatrix
QPainter::worldMatrix
...窗口視口變換
直接相關:QPainter::setViewTransformEnabled 啟用、禁用 視口變換
QPainter::viewTransformEnabled
QPainter::setViewport 設置 視口(物理坐標)
QPainter::setWindow 設置 窗口(與視口是同一矩形,中間態坐標)
該變換是簡單的線性變換。復合變換
窗口視口變換和世界變換的復合:
QPainter::combinedTransformQTransform QPainter::combinedTransform() const
{
Q_D(const QPainter);
return d->state->worldMatrix * d->viewTransform();
}
典型應用:對鼠標事件的響應中,將坐標從物理坐標變換成QPainter需要的邏輯坐標仿射變換、透射變換
Qt4.3(包括)之前的 QMatrix 只支持仿射變換(Affine transformation)。
平移(Translation)
縮放(Scale)
旋轉(Rotation)
剪切(Shear)
QTransform 支持透射變換(perspective transformation)。m11 m12 m13
m21 m22 m23
m31
dx m32
dy m33
變換關系:x' = m11*x + m21*y + dx
y' = m22*y + m12*x + dy
if (is not affine) {
w' = m13*x + m23*y + m33
x' /= w'
y' /= w'
}
相關(有待學習):
射影幾何學、仿射幾何學、歐氏幾何學
【轉】Qt 坐標系統練習(三)
環境:PyQt4 或 PySide
目的:世界變換與窗口視口變換 練習
功能:在鼠標點擊位置對應的物理坐標和邏輯坐標下分別繪制"十"字和文字說明截圖
代碼#--*-- coding:utf-8 --*--
import sys
try:
from PySide import QtCore, QtGui
except ImportError:
from PyQt4 import QtCore, QtGui
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.setMinimumSize(600,500)
self.transform = QtGui.QTransform()
self.viewport = QtCore.QRect()
self.window = QtCore.QRect()
self.poslist = []
self.setToolTip("click me")
def setTransform(self, trans):
self.transform = trans
self.update()
def setViewPort(self, rect):
self.viewport = rect
self.update()
def setWindow(self, rect):
self.window = rect
self.update()
def clear():
self.poslist = []
self.update()
def paintEvent(self, evt):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
for pos in self.poslist:
self.doPaint(painter, pos, "original")
if not self.viewport.isNull():
painter.setViewport(self.viewport)
if not self.window.isNull():
painter.setWindow(self.window)
painter.setTransform(self.transform)
trans = painter.combinedTransform().inverted()[0]
for pos in self.poslist:
self.doPaint(painter, trans.map(pos), "with transform")
def mousePressEvent(self, evt):
self.poslist.append(evt.pos())
self.update()
return super(Widget, self).mousePressEvent(evt)
def doPaint(self, painter, pos, flag):
x, y = pos.x(), pos.y()
painter.save()
painter.drawLine(QtCore.QLineF(x-10, y, x+10, y))
painter.drawLine(QtCore.QLineF(x, y-10, x, y+10))
painter.setPen(QtCore.Qt.blue);
painter.drawText(x, y, flag)
painter.restore()
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.widget = Widget()
grid = QtGui.QGridLayout()
self.m11Edit = self.createEdit(-300, 300, 1, grid, 0, 0)
self.m12Edit = self.createEdit(-300, 300, 0, grid, 0, 1)
self.m13Edit = self.createEdit(-300, 300, 0, grid, 0, 2)
self.m21Edit = self.createEdit(-300, 300, 0, grid, 1, 0)
self.m22Edit = self.createEdit(-300, 300, 1, grid, 1, 1)
self.m23Edit = self.createEdit(-300, 300, 0, grid, 1, 2)
self.m31Edit = self.createEdit(-300, 300, 0, grid, 2, 0)
self.m32Edit = self.createEdit(-300, 300, 0, grid, 2, 1)
self.m33Edit = self.createEdit(-300, 300, 1, grid, 2, 2)
self.transformOkButton = QtGui.QPushButton("Use This Transform")
grid.addWidget(self.transformOkButton, 3, 0, 1, 3)
grid2 = QtGui.QGridLayout()
self.vxEdit = self.createEdit(-1, 800, 100, grid2, 0, 0, False)
self.vyEdit = self.createEdit(-1, 800, 100, grid2, 0, 1, False)
self.vwEdit = self.createEdit(-1, 800, 200, grid2, 0, 2, False)
self.vhEdit = self.createEdit(-1, 800, 200, grid2, 0, 3, False)
self.viewportOkButton = QtGui.QPushButton("Use This Viewport")
grid2.addWidget(self.viewportOkButton, 1, 0, 1, 4)
grid3 = QtGui.QGridLayout()
self.wxEdit = self.createEdit(-800, 800, -100, grid3, 0, 0, False)
self.wyEdit = self.createEdit(-800, 800, -100, grid3, 0, 1, False)
self.wwEdit = self.createEdit(-1, 800, 200, grid3, 0, 2, False)
self.whEdit = self.createEdit(-1, 800, 200, grid3, 0, 3, False)
self.windowOkButton = QtGui.QPushButton("Use This Window")
grid3.addWidget(self.windowOkButton, 1, 0, 1, 4)
vbox = QtGui.QVBoxLayout()
vbox.addLayout(grid)
vbox.addLayout(grid2)
vbox.addLayout(grid3)
vbox.addItem(QtGui.QSpacerItem(10,10, QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding))
hbox = QtGui.QHBoxLayout(self)
hbox.addWidget(self.widget)
hbox.addLayout(vbox)
self.setWindowTitle('Qt Coordinate System')
self.transformOkButton.clicked.connect(self.onTransformChanged)
self.viewportOkButton.clicked.connect(self.onViewportChanged)
self.windowOkButton.clicked.connect(self.onWindowChanged)
def createEdit(self, minimum, maximum, value, grid, i, j, d = True):
edit = QtGui.QDoubleSpinBox() if d else QtGui.QSpinBox()
edit.setRange(minimum, maximum)
edit.setValue(value)
edit.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons)
#edit.setMaximumWidth(50)
grid.addWidget(edit, i, j)
return edit
def onTransformChanged(self):
m11 = self.m11Edit.value()
m12 = self.m12Edit.value()
m13 = self.m13Edit.value()
m21 = self.m21Edit.value()
m22 = self.m22Edit.value()
m23 = self.m23Edit.value()
m31 = self.m31Edit.value()
m32 = self.m32Edit.value()
m33 = self.m33Edit.value()
self.widget.setTransform(QtGui.QTransform(m11, m12, m13, m21, m22, m23, m31, m32, m33))
def onViewportChanged(self):
x = self.vxEdit.value()
y = self.vyEdit.value()
w = self.vwEdit.value()
h = self.vhEdit.value()
self.widget.setViewPort(QtCore.QRect(x,y,w,h))
def onWindowChanged(self):
x = self.wxEdit.value()
y = self.wyEdit.value()
w = self.wwEdit.value()
h = self.whEdit.value()
self.widget.setWindow(QtCore.QRect(x,y,w,h))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dlg = Dialog()
dlg.show()
sys.exit(app.exec_())
環境:PyQt4 或 PySide
目的:世界變換與窗口視口變換 練習
功能:在鼠標點擊位置對應的物理坐標和邏輯坐標下分別繪制"十"字和文字說明截圖

import sys
try:
from PySide import QtCore, QtGui
except ImportError:
from PyQt4 import QtCore, QtGui
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.setMinimumSize(600,500)
self.transform = QtGui.QTransform()
self.viewport = QtCore.QRect()
self.window = QtCore.QRect()
self.poslist = []
self.setToolTip("click me")
def setTransform(self, trans):
self.transform = trans
self.update()
def setViewPort(self, rect):
self.viewport = rect
self.update()
def setWindow(self, rect):
self.window = rect
self.update()
def clear():
self.poslist = []
self.update()
def paintEvent(self, evt):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
for pos in self.poslist:
self.doPaint(painter, pos, "original")
if not self.viewport.isNull():
painter.setViewport(self.viewport)
if not self.window.isNull():
painter.setWindow(self.window)
painter.setTransform(self.transform)
trans = painter.combinedTransform().inverted()[0]
for pos in self.poslist:
self.doPaint(painter, trans.map(pos), "with transform")
def mousePressEvent(self, evt):
self.poslist.append(evt.pos())
self.update()
return super(Widget, self).mousePressEvent(evt)
def doPaint(self, painter, pos, flag):
x, y = pos.x(), pos.y()
painter.save()
painter.drawLine(QtCore.QLineF(x-10, y, x+10, y))
painter.drawLine(QtCore.QLineF(x, y-10, x, y+10))
painter.setPen(QtCore.Qt.blue);
painter.drawText(x, y, flag)
painter.restore()
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.widget = Widget()
grid = QtGui.QGridLayout()
self.m11Edit = self.createEdit(-300, 300, 1, grid, 0, 0)
self.m12Edit = self.createEdit(-300, 300, 0, grid, 0, 1)
self.m13Edit = self.createEdit(-300, 300, 0, grid, 0, 2)
self.m21Edit = self.createEdit(-300, 300, 0, grid, 1, 0)
self.m22Edit = self.createEdit(-300, 300, 1, grid, 1, 1)
self.m23Edit = self.createEdit(-300, 300, 0, grid, 1, 2)
self.m31Edit = self.createEdit(-300, 300, 0, grid, 2, 0)
self.m32Edit = self.createEdit(-300, 300, 0, grid, 2, 1)
self.m33Edit = self.createEdit(-300, 300, 1, grid, 2, 2)
self.transformOkButton = QtGui.QPushButton("Use This Transform")
grid.addWidget(self.transformOkButton, 3, 0, 1, 3)
grid2 = QtGui.QGridLayout()
self.vxEdit = self.createEdit(-1, 800, 100, grid2, 0, 0, False)
self.vyEdit = self.createEdit(-1, 800, 100, grid2, 0, 1, False)
self.vwEdit = self.createEdit(-1, 800, 200, grid2, 0, 2, False)
self.vhEdit = self.createEdit(-1, 800, 200, grid2, 0, 3, False)
self.viewportOkButton = QtGui.QPushButton("Use This Viewport")
grid2.addWidget(self.viewportOkButton, 1, 0, 1, 4)
grid3 = QtGui.QGridLayout()
self.wxEdit = self.createEdit(-800, 800, -100, grid3, 0, 0, False)
self.wyEdit = self.createEdit(-800, 800, -100, grid3, 0, 1, False)
self.wwEdit = self.createEdit(-1, 800, 200, grid3, 0, 2, False)
self.whEdit = self.createEdit(-1, 800, 200, grid3, 0, 3, False)
self.windowOkButton = QtGui.QPushButton("Use This Window")
grid3.addWidget(self.windowOkButton, 1, 0, 1, 4)
vbox = QtGui.QVBoxLayout()
vbox.addLayout(grid)
vbox.addLayout(grid2)
vbox.addLayout(grid3)
vbox.addItem(QtGui.QSpacerItem(10,10, QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding))
hbox = QtGui.QHBoxLayout(self)
hbox.addWidget(self.widget)
hbox.addLayout(vbox)
self.setWindowTitle('Qt Coordinate System')
self.transformOkButton.clicked.connect(self.onTransformChanged)
self.viewportOkButton.clicked.connect(self.onViewportChanged)
self.windowOkButton.clicked.connect(self.onWindowChanged)
def createEdit(self, minimum, maximum, value, grid, i, j, d = True):
edit = QtGui.QDoubleSpinBox() if d else QtGui.QSpinBox()
edit.setRange(minimum, maximum)
edit.setValue(value)
edit.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons)
#edit.setMaximumWidth(50)
grid.addWidget(edit, i, j)
return edit
def onTransformChanged(self):
m11 = self.m11Edit.value()
m12 = self.m12Edit.value()
m13 = self.m13Edit.value()
m21 = self.m21Edit.value()
m22 = self.m22Edit.value()
m23 = self.m23Edit.value()
m31 = self.m31Edit.value()
m32 = self.m32Edit.value()
m33 = self.m33Edit.value()
self.widget.setTransform(QtGui.QTransform(m11, m12, m13, m21, m22, m23, m31, m32, m33))
def onViewportChanged(self):
x = self.vxEdit.value()
y = self.vyEdit.value()
w = self.vwEdit.value()
h = self.vhEdit.value()
self.widget.setViewPort(QtCore.QRect(x,y,w,h))
def onWindowChanged(self):
x = self.wxEdit.value()
y = self.wyEdit.value()
w = self.wwEdit.value()
h = self.whEdit.value()
self.widget.setWindow(QtCore.QRect(x,y,w,h))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dlg = Dialog()
dlg.show()
sys.exit(app.exec_())
【轉】Qt 坐標系統練習(二)
環境:PySide 或 PyQt4
主角:Qt 世界變換(world transform) 和 窗口視口變換(viewport window transform)
分別在以下條件下繪制同一圖形:
禁用兩種變換
啟用窗口視口變換
啟用世界變換
啟用兩種變換截圖代碼#--*-- coding:utf-8 --*--
# (C) dbzhang800 2010
import sys
try:
from PySide import QtCore, QtGui
except ImportError:
from PyQt4 import QtCore, QtGui
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.setMinimumSize(600,500)
fontMetrics = QtGui.QFontMetrics(QtGui.QFont());
self.xBoundingRect = fontMetrics.boundingRect("x");
self.yBoundingRect = fontMetrics.boundingRect("y");
self.transform = QtGui.QTransform()
self.viewport = QtCore.QRect()
self.window = QtCore.QRect()
def setTransform(self, trans):
self.transform = trans
self.update()
def setViewPort(self, rect):
self.viewport = rect
self.update()
def setWindow(self, rect):
self.window = rect
self.update()
def paintEvent(self, evt):
painter = QtGui.QPainter(self)
self.doPaint(painter, "original")
if not self.viewport.isNull():
painter.setViewport(self.viewport)
if not self.window.isNull():
painter.setWindow(self.window)
self.doPaint(painter, "with view-window")
painter.setTransform(self.transform)
self.doPaint(painter, "with both")
painter.setViewTransformEnabled(False)
self.doPaint(painter, "with world transform")
def doPaint(self, p, flag):
p.save()
p.drawRect(0,0,40,40)
p.setPen(QtCore.Qt.blue);
p.drawText(20, 20, flag)
p.setPen(QtCore.Qt.red);
p.drawLine(0, 0, 40, 0);
p.drawLine(38, -2, 40, 0);
p.drawLine(38, 2, 40, 0);
p.drawText(50 - self.xBoundingRect.width() / 2,
0 + self.xBoundingRect.height() / 2, "x");
p.drawLine(0, 0, 0, 40);
p.drawLine(-2, 38, 0, 40);
p.drawLine(2, 38, 0, 40);
p.drawText(0 - self.yBoundingRect.width() / 2,
50 + self.yBoundingRect.height() / 2, "y");
p.restore()
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.widget = Widget()
grid = QtGui.QGridLayout()
self.m11Edit = self.createEdit(-300, 300, 1, grid, 0, 0)
self.m12Edit = self.createEdit(-300, 300, 0, grid, 0, 1)
self.m13Edit = self.createEdit(-300, 300, 0, grid, 0, 2)
self.m21Edit = self.createEdit(-300, 300, 0, grid, 1, 0)
self.m22Edit = self.createEdit(-300, 300, 1, grid, 1, 1)
self.m23Edit = self.createEdit(-300, 300, 0, grid, 1, 2)
self.m31Edit = self.createEdit(-300, 300, 0, grid, 2, 0)
self.m32Edit = self.createEdit(-300, 300, 0, grid, 2, 1)
self.m33Edit = self.createEdit(-300, 300, 1, grid, 2, 2)
self.transformOkButton = QtGui.QPushButton("Use This Transform")
grid.addWidget(self.transformOkButton, 3, 0, 1, 3)
grid2 = QtGui.QGridLayout()
self.vxEdit = self.createEdit(-1, 800, 100, grid2, 0, 0, False)
self.vyEdit = self.createEdit(-1, 800, 100, grid2, 0, 1, False)
self.vwEdit = self.createEdit(-1, 800, 200, grid2, 0, 2, False)
self.vhEdit = self.createEdit(-1, 800, 200, grid2, 0, 3, False)
self.viewportOkButton = QtGui.QPushButton("Use This Viewport")
grid2.addWidget(self.viewportOkButton, 1, 0, 1, 4)
grid3 = QtGui.QGridLayout()
self.wxEdit = self.createEdit(-800, 800, -100, grid3, 0, 0, False)
self.wyEdit = self.createEdit(-800, 800, -100, grid3, 0, 1, False)
self.wwEdit = self.createEdit(-1, 800, 200, grid3, 0, 2, False)
self.whEdit = self.createEdit(-1, 800, 200, grid3, 0, 3, False)
self.windowOkButton = QtGui.QPushButton("Use This Window")
grid3.addWidget(self.windowOkButton, 1, 0, 1, 4)
vbox = QtGui.QVBoxLayout()
vbox.addLayout(grid)
vbox.addLayout(grid2)
vbox.addLayout(grid3)
vbox.addItem(QtGui.QSpacerItem(10,10, QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding))
hbox = QtGui.QHBoxLayout(self)
hbox.addWidget(self.widget)
hbox.addLayout(vbox)
self.setWindowTitle('Qt Coordinate System')
self.transformOkButton.clicked.connect(self.onTransformChanged)
self.viewportOkButton.clicked.connect(self.onViewportChanged)
self.windowOkButton.clicked.connect(self.onWindowChanged)
def createEdit(self, minimum, maximum, value, grid, i, j, d = True):
edit = QtGui.QDoubleSpinBox() if d else QtGui.QSpinBox()
edit.setRange(minimum, maximum)
edit.setValue(value)
edit.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons)
#edit.setMaximumWidth(50)
grid.addWidget(edit, i, j)
return edit
def onTransformChanged(self):
m11 = self.m11Edit.value()
m12 = self.m12Edit.value()
m13 = self.m13Edit.value()
m21 = self.m21Edit.value()
m22 = self.m22Edit.value()
m23 = self.m23Edit.value()
m31 = self.m31Edit.value()
m32 = self.m32Edit.value()
m33 = self.m33Edit.value()
self.widget.setTransform(QtGui.QTransform(m11, m12, m13, m21, m22, m23, m31, m32, m33))
def onViewportChanged(self):
x = self.vxEdit.value()
y = self.vyEdit.value()
w = self.vwEdit.value()
h = self.vhEdit.value()
self.widget.setViewPort(QtCore.QRect(x,y,w,h))
def onWindowChanged(self):
x = self.wxEdit.value()
y = self.wyEdit.value()
w = self.wwEdit.value()
h = self.whEdit.value()
self.widget.setWindow(QtCore.QRect(x,y,w,h))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dlg = Dialog()
dlg.show()
sys.exit(app.exec_())
環境:PySide 或 PyQt4
主角:Qt 世界變換(world transform) 和 窗口視口變換(viewport window transform)
分別在以下條件下繪制同一圖形:
禁用兩種變換
啟用窗口視口變換
啟用世界變換
啟用兩種變換截圖代碼#--*-- coding:utf-8 --*--
# (C) dbzhang800 2010
import sys
try:
from PySide import QtCore, QtGui
except ImportError:
from PyQt4 import QtCore, QtGui
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.setMinimumSize(600,500)
fontMetrics = QtGui.QFontMetrics(QtGui.QFont());
self.xBoundingRect = fontMetrics.boundingRect("x");
self.yBoundingRect = fontMetrics.boundingRect("y");
self.transform = QtGui.QTransform()
self.viewport = QtCore.QRect()
self.window = QtCore.QRect()
def setTransform(self, trans):
self.transform = trans
self.update()
def setViewPort(self, rect):
self.viewport = rect
self.update()
def setWindow(self, rect):
self.window = rect
self.update()
def paintEvent(self, evt):
painter = QtGui.QPainter(self)
self.doPaint(painter, "original")
if not self.viewport.isNull():
painter.setViewport(self.viewport)
if not self.window.isNull():
painter.setWindow(self.window)
self.doPaint(painter, "with view-window")
painter.setTransform(self.transform)
self.doPaint(painter, "with both")
painter.setViewTransformEnabled(False)
self.doPaint(painter, "with world transform")
def doPaint(self, p, flag):
p.save()
p.drawRect(0,0,40,40)
p.setPen(QtCore.Qt.blue);
p.drawText(20, 20, flag)
p.setPen(QtCore.Qt.red);
p.drawLine(0, 0, 40, 0);
p.drawLine(38, -2, 40, 0);
p.drawLine(38, 2, 40, 0);
p.drawText(50 - self.xBoundingRect.width() / 2,
0 + self.xBoundingRect.height() / 2, "x");
p.drawLine(0, 0, 0, 40);
p.drawLine(-2, 38, 0, 40);
p.drawLine(2, 38, 0, 40);
p.drawText(0 - self.yBoundingRect.width() / 2,
50 + self.yBoundingRect.height() / 2, "y");
p.restore()
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.widget = Widget()
grid = QtGui.QGridLayout()
self.m11Edit = self.createEdit(-300, 300, 1, grid, 0, 0)
self.m12Edit = self.createEdit(-300, 300, 0, grid, 0, 1)
self.m13Edit = self.createEdit(-300, 300, 0, grid, 0, 2)
self.m21Edit = self.createEdit(-300, 300, 0, grid, 1, 0)
self.m22Edit = self.createEdit(-300, 300, 1, grid, 1, 1)
self.m23Edit = self.createEdit(-300, 300, 0, grid, 1, 2)
self.m31Edit = self.createEdit(-300, 300, 0, grid, 2, 0)
self.m32Edit = self.createEdit(-300, 300, 0, grid, 2, 1)
self.m33Edit = self.createEdit(-300, 300, 1, grid, 2, 2)
self.transformOkButton = QtGui.QPushButton("Use This Transform")
grid.addWidget(self.transformOkButton, 3, 0, 1, 3)
grid2 = QtGui.QGridLayout()
self.vxEdit = self.createEdit(-1, 800, 100, grid2, 0, 0, False)
self.vyEdit = self.createEdit(-1, 800, 100, grid2, 0, 1, False)
self.vwEdit = self.createEdit(-1, 800, 200, grid2, 0, 2, False)
self.vhEdit = self.createEdit(-1, 800, 200, grid2, 0, 3, False)
self.viewportOkButton = QtGui.QPushButton("Use This Viewport")
grid2.addWidget(self.viewportOkButton, 1, 0, 1, 4)
grid3 = QtGui.QGridLayout()
self.wxEdit = self.createEdit(-800, 800, -100, grid3, 0, 0, False)
self.wyEdit = self.createEdit(-800, 800, -100, grid3, 0, 1, False)
self.wwEdit = self.createEdit(-1, 800, 200, grid3, 0, 2, False)
self.whEdit = self.createEdit(-1, 800, 200, grid3, 0, 3, False)
self.windowOkButton = QtGui.QPushButton("Use This Window")
grid3.addWidget(self.windowOkButton, 1, 0, 1, 4)
vbox = QtGui.QVBoxLayout()
vbox.addLayout(grid)
vbox.addLayout(grid2)
vbox.addLayout(grid3)
vbox.addItem(QtGui.QSpacerItem(10,10, QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding))
hbox = QtGui.QHBoxLayout(self)
hbox.addWidget(self.widget)
hbox.addLayout(vbox)
self.setWindowTitle('Qt Coordinate System')
self.transformOkButton.clicked.connect(self.onTransformChanged)
self.viewportOkButton.clicked.connect(self.onViewportChanged)
self.windowOkButton.clicked.connect(self.onWindowChanged)
def createEdit(self, minimum, maximum, value, grid, i, j, d = True):
edit = QtGui.QDoubleSpinBox() if d else QtGui.QSpinBox()
edit.setRange(minimum, maximum)
edit.setValue(value)
edit.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons)
#edit.setMaximumWidth(50)
grid.addWidget(edit, i, j)
return edit
def onTransformChanged(self):
m11 = self.m11Edit.value()
m12 = self.m12Edit.value()
m13 = self.m13Edit.value()
m21 = self.m21Edit.value()
m22 = self.m22Edit.value()
m23 = self.m23Edit.value()
m31 = self.m31Edit.value()
m32 = self.m32Edit.value()
m33 = self.m33Edit.value()
self.widget.setTransform(QtGui.QTransform(m11, m12, m13, m21, m22, m23, m31, m32, m33))
def onViewportChanged(self):
x = self.vxEdit.value()
y = self.vyEdit.value()
w = self.vwEdit.value()
h = self.vhEdit.value()
self.widget.setViewPort(QtCore.QRect(x,y,w,h))
def onWindowChanged(self):
x = self.wxEdit.value()
y = self.wyEdit.value()
w = self.wwEdit.value()
h = self.whEdit.value()
self.widget.setWindow(QtCore.QRect(x,y,w,h))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dlg = Dialog()
dlg.show()
sys.exit(app.exec_())
Qt4 Gossip: QMatrix
分類: QT圖像加載及顯示 2012-08-27 10:19 271人閱讀 評論(0) 收藏 舉報
qtmatrixsystemfloat圖形api
轉載自 http://hi.baidu.com/touchao/item/7a51b2e3d0e08710595dd845
QPainter默認的坐標系統是繪圖裝置的坐標系統,也就是左上角為原點,向右為正X,向下為正Y的坐標系統,坐標系統的轉換常使用矩陣的方式來表現及進行運算,QMatrix的作用,正是讓您可以利用其內建的矩陣,設定好相關的參數,然后讓QPainter根據QMatrix的設定,來進行一些二維坐標系統的轉換動作。
QMatrix的內部使用一個3x3的矩陣:
m11 m12 0 m21 m22 0 dx dy 1
dx與dy定義了水平與垂直移動,m11與m22定義了水平與垂直縮放(scaling),m12與m21定義了垂直與水平扭曲(shearing),想象您是坐在宇宙飛船中,在宇宙飛船從左上原點開到某個點之后,(x, y)是以您為中心所看到的坐標,但實際上宇宙飛船相對於左上角為原點的坐標為(x',y'),QMatrix的矩陣可以如以下的公式,將(x, y)轉換為(x', y'):
x' = m11*x + m21*y + dx
y' = m22*y + m12*x + dy
當您使用QPainter要進行繪圖時,可以您為中心所看到的坐標系統(x, y),使用QPainter的相關API來進行相關圖形的繪制,這就像您在宇宙飛船中畫圖一樣的方便,若有設定QMatrix,則會自動轉換為計算機繪圖時所看到的坐標系統(x', y'),如此就不用親自進行一些復雜的轉換動作,進行繪圖時也較為直覺。
您可以藉由QMatrix的setMatrix()方法設定m11、m12、m21、m22、dx、dy,或者是直接使用translate()、rotate()、scale()與shear()等方法來直接進行移動、旋轉、縮放、扭曲等坐標轉換。
以下的范例為色彩輪的繪制,藉由設定HSV(Brightness,Hue,Saturation)中的「色相」來完成彩虹般的效果, HSV中的「色相」(Hue)是錂鏡分光,主要有紅、橙、黃、綠、藍、紫...等八個主要色相。「亮度」(Brightness)是明暗表現,由白至黑的表現,在P.C.C.S(Practical Color Coordinate System)配色系統中,將之分為白、淺灰(淺,深)、淺中灰、中灰、暗中灰、暗灰(淺,深)、黑等。「彩度」(Saturation)也就是色彩的飽和程度,彩度最高的稱之為「純色」,最低為「無顏色」。
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QMatrix>
class PainterWidget : public QWidget {
protected:
void paintEvent(QPaintEvent*);
};
void PainterWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
QFont font("times", 18, QFont::Bold);
painter.setFont(font);
painter.setPen(Qt::black);
for (int i = 0; i < 36; i++) { // 進行 36 次旋轉
QMatrix matrix;
matrix.translate(250, 250); // 移動中心至 (250, 250)
matrix.shear(0.0, 0.3); // 扭曲
matrix.rotate((float) i * 10); // 每次旋轉 10 度
painter.setWorldMatrix(matrix); // 使用這個 QMatrix
QColor color;
color.setHsv(i * 10, 255, 255); // 設定彩虹效果
painter.setBrush(color); // 設定筆刷顏色
painter.drawRect(70, -10, 80, 10); // 畫矩形
QString str;
str.sprintf("H=%d", i*10);
painter.drawText(80 + 70 + 5, 0, str); // 繪制角度文字
}
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
PainterWidget pWidget;
pWidget.setWindowTitle("QMatrix");
pWidget.resize(500, 500);
pWidget.show();
return app.exec();
}