Edited at

PyQt5とpython3によるGUIプログラミング[1]

More than 1 year has passed since last update.


シンプルなウィンドウ


window01.py

#!/usr/bin/python

# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import *

def main():
app = QApplication(sys.argv)
w = QWidget()
w.resize(250, 150)
w.setWindowTitle('Window01')
w.show()
sys.exit(app.exec_())

if __name__ == '__main__':
main()


window01.png

これが全ての始まりです。


ウィンドウのリサイズを許さない


DonotResize.py

import sys

import time
from PyQt5.QtWidgets import *

class SampleWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self.setWindowTitle("Sample Window")
self.setGeometry(300, 300, 200, 150)
self.setMinimumHeight(100)
self.setMinimumWidth(250)
self.setMaximumHeight(100)
self.setMaximumWidth(250)

if __name__ == '__main__':

myApp = QApplication(sys.argv)
myWindow = SampleWindow()
myWindow.show()
myApp.exec_()
sys.exit(0)


MinimumとMaximumに同じ値を設定することで、リサイズを抑止します。

スクリーンショット 2016-10-29 13.50.50.png


ボタンでイベント発生


Button01.py

#!/usr/bin/python3

# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication

class Button01(QMainWindow):
def __init__(self):
super().__init__()

self.initUI()

def initUI(self):
btn1 = QPushButton("Button01", self)
btn1.clicked.connect(self.button01Clicked)

self.statusBar()

self.setWindowTitle('Button01')
self.show()

def button01Clicked(self):
sender = self.sender()
self.statusBar().showMessage(sender.text() + ' Push Button01')

if __name__ == '__main__':
app = QApplication(sys.argv)
win = Button01()
sys.exit(app.exec_())


image

senderメソッドはQWidgetクラスに入っているのでほとんどのウィジットで利用できます。

senderメソッドを使用することで、直前にシグナルを送ってきたウィジットを返してくれます。

帰ってくるのは、ウィジットのインスタンスになります。

何個でも設定できるようです。

複数のボタンがある場合など、どのボタンが押されたのか判定するときなど有効です。


Color Dialogで色を変える


ColorDialog.py

import sys

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.Qt import *

class ColorDialog(QWidget):

def __init__(self):
super(ColorDialog, self).__init__(parent=None)
thisColor = QColor(0, 0, 0)

self.ColorButton = QPushButton('Select Color', self)
self.ColorButton.move(27, 15)

#Qpainterを使う方法もあるが、ここではbackground-colorを設定することで色を変えてみる
self.myFrame = QFrame(self)
self.myFrame.setStyleSheet("QWidget { background-color: %s }" % thisColor.name())
self.myFrame.setGeometry(75, 50, 25, 25)

self.ColorButton.clicked.connect(self.showDialog)

self.setGeometry(200, 100, 175, 100)
self.setWindowTitle('Color Dialog')

self.show()

def showDialog(self):
#ダイアログを表示する
getcolor = QColorDialog.getColor()

if getcolor.isValid():
self.myFrame.setStyleSheet("QWidget { background-color: %s }" % getcolor.name())

if __name__ =='__main__':
App = QApplication(sys.argv)
dialog = ColorDialog()
dialog.show()
App.exec_()
sys.exit()


スクリーンショット 2016-10-05 23.15.32.png


Signals and Slots

PyQt4とPyQt5ではSignalとSlotのコーディングスタイルが全くというほど異なります。

以下のページはpysideのコンテンツですが、脳内変換すればPyQt5でも全く問題なく利用できます。

https://wiki.qt.io/Signals_and_Slots_in_PySide/ja

自分で作成したSignal、Slotを利用する場合、大変役に立つはずです。


カスタムシグナルサンプル


MySignal.py

import sys

from PyQt5.QtCore import *

class CustomSignal(QObject):

mySignal = pyqtSignal(str)

def printstr(text):
print(text)

if __name__ == '__main__':

myObject = CustomSignal()

myObject.mySignal.connect(printstr)

myObject.mySignal.emit("カスタムシグナル!")


実行例.png

PyScripterでの実行例です。


イベントサンプル


EventTest01.py

import sys

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class EventTest01(QWidget):
def __init__(self):
super().__init__()
self.initUI()

def initUI(self):
WiNum = QLCDNumber(self)
Stool = QSlider(Qt.Horizontal, self)
BoxLay = QVBoxLayout()
BoxLay.addWidget(WiNum)
BoxLay.addWidget(Stool)
self.setLayout(BoxLay)
Stool.valueChanged.connect(WiNum.display)
self.setGeometry(500, 500, 300, 200)
self.setWindowTitle('EventTest')
self.show()

if __name__ == '__main__':
app = QApplication(sys.argv)
win = EventTest01()
sys.exit(app.exec_())


image


イベントサンプルその2

フレームの上でマウスボタンをクリックするとコンソールにボタンの位置をプリントします。


EventTest02.py

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.Qt import *

class Test(QWidget):
def __init__(self,parent=None):
super(Test, self).__init__(parent)
self.label = QLabel(self)
self.label.setText('テストボックス')

#http://doc.qt.io/qt-5/qt.html
#上記のページに他の設定もあります
self.label.setAlignment(Qt.AlignCenter)

#http://doc.qt.io/qt-5/qframe.html
#上記のページに他の設定もあります
self.label.setFrameStyle(QFrame.Panel | QFrame.Raised)
self.label.setLineWidth(2)

#マウストラッキングが有効になっている場合、ウィジェットはボタンを押さない場合でも、マウス移動イベントを受け取ります。
self.label.setMouseTracking(True)

self.label.installEventFilter(self)
layout = QVBoxLayout(self)
layout.addWidget(self.label)

def eventFilter(self, object, event):
if event.type() == QEvent.MouseButtonPress and object is self.label:
pos = event.pos()
print('MouseButtonPress: (%d, %d)' % (pos.x(), pos.y()))
return QWidget.eventFilter(self, object, event)

if __name__ == '__main__':

import sys
app = QApplication(sys.argv)
window = Test()
window.show()
window.resize(300, 150)
sys.exit(app.exec_())


スクリーンショット 2016-11-04 14.17.07.png


PyQt4とPyQt5のコーディングスタイルの違い

PyQt4とpython2.Xで動いていたものが、PyQt5とpython3.Xでは動かないことが

よくあります。

これなどもその例で、

PyQt4:python2.X

settings.value("/myboolsetting", True).toBool()
settings.value("/myintsetting", 10).toInt()[0]
settings.value("/myintsetting").toByteArray()

PyQt5:python3.X

settings.value("/myboolsetting", True, type=bool)
settings.value("/myintsetting", 10, type=int)
settings.value("/myintsetting", QByteArray(), type=QByteArray)

上記のようにコーディングする必要があるそうだ。

これは、"toXXXX"系のメソッドが削除されていることが原因のようだ。

QStringのメソッドは軒並み使えない、そもそもPyQt5にはQStringは存在しない。

PyQt4:python2.X

if mystr.isEmpty()
mystr = QStringList()
myval = QVariant(5)
myval = QVariant("Good Morning")

PyQt5:python3.X

if not mystr
mystr = []
myval = 5
myval = "Good Morning"

他にもあるようだけれども、今後見つけたら追加したい。


QByteArrayを返すQt5のクラスを使用するとPython3で動かない場合がある

PyQt5のexamplesにscribble.pyがあります、これは2.xでは動くのですが、3.xでは保存時のformat部分でエラーが出てしまう。

この場合、以下の部分を

    def createActions(self):

self.openAct = QAction("&Open...", self, shortcut="Ctrl+O",
triggered=self.open)

for format in QImageWriter.supportedImageFormats():
format = str(format)

text = format.upper() + "..."

こうすることで動きます。

    def createActions(self):

self.openAct = QAction("&Open...", self, shortcut="Ctrl+O",
triggered=self.open)

for format in QImageWriter.supportedImageFormats():
aaaaa = bytes(format)
#format = str(format)
format = aaaaa.decode('utf-8')

text = format.upper() + "..."

2.xの時はstrでキャストするだけで動いたようですが、3.xではdecodeしてあげないと上手くいきません。

一応コードは掲載しておきます。

ただ、これはあくまでもPyQt5のexamplesのコードです。


Scribble.py

from PyQt5.QtCore import QDir, QPoint, QRect, QSize, Qt

from PyQt5.QtGui import QImage, QImageWriter, QPainter, QPen, qRgb
from PyQt5.QtWidgets import (QAction, QApplication, QColorDialog, QFileDialog,
QInputDialog, QMainWindow, QMenu, QMessageBox, QWidget)
from PyQt5.QtPrintSupport import QPrintDialog, QPrinter

class ScribbleArea(QWidget):
def __init__(self, parent=None):
super(ScribbleArea, self).__init__(parent)

self.setAttribute(Qt.WA_StaticContents)
self.modified = False
self.scribbling = False
self.myPenWidth = 1
self.myPenColor = Qt.blue
self.image = QImage()
self.lastPoint = QPoint()

def openImage(self, fileName):
loadedImage = QImage()
if not loadedImage.load(fileName):
return False

newSize = loadedImage.size().expandedTo(self.size())
self.resizeImage(loadedImage, newSize)
self.image = loadedImage
self.modified = False
self.update()
return True

def saveImage(self, fileName, fileFormat):
visibleImage = self.image
self.resizeImage(visibleImage, self.size())

if visibleImage.save(fileName, fileFormat):
self.modified = False
return True
else:
return False

def setPenColor(self, newColor):
self.myPenColor = newColor

def setPenWidth(self, newWidth):
self.myPenWidth = newWidth

def clearImage(self):
self.image.fill(qRgb(255, 255, 255))
self.modified = True
self.update()

def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.lastPoint = event.pos()
self.scribbling = True

def mouseMoveEvent(self, event):
if (event.buttons() & Qt.LeftButton) and self.scribbling:
self.drawLineTo(event.pos())

def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton and self.scribbling:
self.drawLineTo(event.pos())
self.scribbling = False

def paintEvent(self, event):
painter = QPainter(self)
dirtyRect = event.rect()
painter.drawImage(dirtyRect, self.image, dirtyRect)

def resizeEvent(self, event):
if self.width() > self.image.width() or self.height() > self.image.height():
newWidth = max(self.width() + 128, self.image.width())
newHeight = max(self.height() + 128, self.image.height())
self.resizeImage(self.image, QSize(newWidth, newHeight))
self.update()

super(ScribbleArea, self).resizeEvent(event)

def drawLineTo(self, endPoint):
painter = QPainter(self.image)
painter.setPen(QPen(self.myPenColor, self.myPenWidth, Qt.SolidLine,
Qt.RoundCap, Qt.RoundJoin))
painter.drawLine(self.lastPoint, endPoint)
self.modified = True

rad = self.myPenWidth / 2 + 2
self.update(QRect(self.lastPoint, endPoint).normalized().adjusted(-rad, -rad, +rad, +rad))
self.lastPoint = QPoint(endPoint)

def resizeImage(self, image, newSize):
if image.size() == newSize:
return

newImage = QImage(newSize, QImage.Format_RGB32)
newImage.fill(qRgb(255, 255, 255))
painter = QPainter(newImage)
painter.drawImage(QPoint(0, 0), image)
self.image = newImage

def print_(self):
printer = QPrinter(QPrinter.HighResolution)

printDialog = QPrintDialog(printer, self)
if printDialog.exec_() == QPrintDialog.Accepted:
painter = QPainter(printer)
rect = painter.viewport()
size = self.image.size()
size.scale(rect.size(), Qt.KeepAspectRatio)
painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
painter.setWindow(self.image.rect())
painter.drawImage(0, 0, self.image)
painter.end()

def isModified(self):
return self.modified

def penColor(self):
return self.myPenColor

def penWidth(self):
return self.myPenWidth

class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()

self.saveAsActs = []

self.scribbleArea = ScribbleArea()
self.setCentralWidget(self.scribbleArea)

self.createActions()
self.createMenus()

self.setWindowTitle("Scribble")
self.resize(500, 500)

def closeEvent(self, event):
if self.maybeSave():
event.accept()
else:
event.ignore()

def open(self):
if self.maybeSave():
fileName, _ = QFileDialog.getOpenFileName(self, "Open File",
QDir.currentPath())
if fileName:
self.scribbleArea.openImage(fileName)

def save(self):
action = self.sender()
fileFormat = action.data()
self.saveFile(fileFormat)

def penColor(self):
newColor = QColorDialog.getColor(self.scribbleArea.penColor())
if newColor.isValid():
self.scribbleArea.setPenColor(newColor)

def penWidth(self):
newWidth, ok = QInputDialog.getInt(self, "Scribble",
"Select pen width:", self.scribbleArea.penWidth(), 1, 50, 1)
if ok:
self.scribbleArea.setPenWidth(newWidth)

def about(self):
QMessageBox.about(self, "About Scribble",
"<p>The <b>Scribble</b> example shows how to use "
"QMainWindow as the base widget for an application, and how "
"to reimplement some of QWidget's event handlers to receive "
"the events generated for the application's widgets:</p>"
"<p> We reimplement the mouse event handlers to facilitate "
"drawing, the paint event handler to update the application "
"and the resize event handler to optimize the application's "
"appearance. In addition we reimplement the close event "
"handler to intercept the close events before terminating "
"the application.</p>"
"<p> The example also demonstrates how to use QPainter to "
"draw an image in real time, as well as to repaint "
"widgets.</p>")

def createActions(self):
self.openAct = QAction("&Open...", self, shortcut="Ctrl+O",
triggered=self.open)

for format in QImageWriter.supportedImageFormats():
aaaaa = bytes(format)
#format = str(format)
format = aaaaa.decode('utf-8')
print("format: %s" % format)

text = format.upper() + "..."

action = QAction(text, self, triggered=self.save)
action.setData(format)
self.saveAsActs.append(action)

self.printAct = QAction("&Print...", self,
triggered=self.scribbleArea.print_)

self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q",
triggered=self.close)

self.penColorAct = QAction("&Pen Color...", self,
triggered=self.penColor)

self.penWidthAct = QAction("Pen &Width...", self,
triggered=self.penWidth)

self.clearScreenAct = QAction("&Clear Screen", self, shortcut="Ctrl+L",
triggered=self.scribbleArea.clearImage)

self.aboutAct = QAction("&About", self, triggered=self.about)

self.aboutQtAct = QAction("About &Qt", self,
triggered=QApplication.instance().aboutQt)

def createMenus(self):
self.saveAsMenu = QMenu("&Save As", self)
for action in self.saveAsActs:
self.saveAsMenu.addAction(action)

fileMenu = QMenu("&File", self)
fileMenu.addAction(self.openAct)
fileMenu.addMenu(self.saveAsMenu)
fileMenu.addAction(self.printAct)
fileMenu.addSeparator()
fileMenu.addAction(self.exitAct)

optionMenu = QMenu("&Options", self)
optionMenu.addAction(self.penColorAct)
optionMenu.addAction(self.penWidthAct)
optionMenu.addSeparator()
optionMenu.addAction(self.clearScreenAct)

helpMenu = QMenu("&Help", self)
helpMenu.addAction(self.aboutAct)
helpMenu.addAction(self.aboutQtAct)

self.menuBar().addMenu(fileMenu)
self.menuBar().addMenu(optionMenu)
self.menuBar().addMenu(helpMenu)

def maybeSave(self):
if self.scribbleArea.isModified():
ret = QMessageBox.warning(self, "Scribble",
"The image has been modified.\n"
"Do you want to save your changes?",
QMessageBox.Save | QMessageBox.Discard |
QMessageBox.Cancel)
if ret == QMessageBox.Save:
return self.saveFile('png')
elif ret == QMessageBox.Cancel:
return False

return True

def saveFile(self, fileFormat):

print("fileFormat:%s" % fileFormat)
initialPath = QDir.currentPath() + '/untitled.' + fileFormat

fileName, _ = QFileDialog.getSaveFileName(self, "Save As", initialPath,
"%s Files (*.%s);;All Files (*)" % (fileFormat.upper(), fileFormat))

if fileName:
return self.scribbleArea.saveImage(fileName, fileFormat)

return False

if __name__ == '__main__':

import sys

app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())


スクリーンショット 2016-08-15 22.58.57.png

まあこんな感じです。

これちょと、いじってみようと思っています。


Macでアプリケーションのアイコンを表示したい

しばらく見ていなかったら、Macでアプリケーションのアイコンが表示されていません。というか、アイコンが出てこない。

うーん、OSのアップグレードが原因かとか調べていると、こんなページを発見

https://stackoverflow.com/questions/35864177/why-using-pyqt5-on-mac-can-not-add-a-icon

以下のiconを表示してみようと思います。

icon0.png


icontest.py

import sys

import os
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QIcon

class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()

def initUI(self):
self.setGeometry(300,300,300,220)
self.setWindowTitle('Icon')
self.show()

if __name__ == '__main__':
app = QApplication(sys.argv)
path = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'icon_2.png')
app.setWindowIcon(QIcon(path))
ex = Example()
sys.exit(app.exec_())


スクリーンショット 2018-05-03 15.56.32.png


QGraphicsSceneとQGraphicsItemを少々いじってみた

ずっとこの動画が気になっていたんですが、なかなかうまく行かず、何度もも挑戦してました。

QGraphics Basic Example

似たようなことができるようになったんで、YouTubeに動画アップしてみようと思います。

画像的にはこんな感じ、codeは雑です、利用しやすいようにclassでガチガチにはしていません。

これが、正しい使い方なのかも正直わかりません、間違いがあったらどんどんご指摘ください。

PyQt5_mov

スクリーンショット 2018-07-03 15.48.06.png

ソースは下記に入れますが、以下に承諾された方のみご使用ください。

1.ソースのご利用は利用者の責任において行ってください。個人・法人に限らず利用者は自由に使用することができます。

2.ソースの利用、またはリンク先により生じた損害に対する責任を作者(ケンあずま)は負いません。

3.ソフトを利用した事によるいかなる損害も作者(ケンあずま)は一切の責任を負いません。

4.ソフトは自己の責任の上で使用して下さい。


ui_test_Dialog01.py

# -*- coding: utf-8 -*-


# Form implementation generated from reading ui file 'test_Dialog01.ui'
#
# Created by: PyQt5 UI code generator 5.10.1
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Dialog_graphic(object):
def setupUi(self, Dialog_graphic):
Dialog_graphic.setObjectName("Dialog_graphic")
Dialog_graphic.resize(1187, 988)
self.graphicsView = QtWidgets.QGraphicsView(Dialog_graphic)
self.graphicsView.setGeometry(QtCore.QRect(0, 0, 991, 971))
self.graphicsView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.graphicsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.graphicsView.setObjectName("graphicsView")
self.pushButton_rect = QtWidgets.QPushButton(Dialog_graphic)
self.pushButton_rect.setGeometry(QtCore.QRect(1020, 30, 113, 32))
self.pushButton_rect.setObjectName("pushButton_rect")
self.pushButton_rotate = QtWidgets.QPushButton(Dialog_graphic)
self.pushButton_rotate.setGeometry(QtCore.QRect(1020, 210, 113, 32))
self.pushButton_rotate.setObjectName("pushButton_rotate")
self.pushButton_linecolor = QtWidgets.QPushButton(Dialog_graphic)
self.pushButton_linecolor.setGeometry(QtCore.QRect(1020, 500, 112, 34))
self.pushButton_linecolor.setObjectName("pushButton_linecolor")
self.pushButton_fillcolor = QtWidgets.QPushButton(Dialog_graphic)
self.pushButton_fillcolor.setGeometry(QtCore.QRect(1020, 570, 112, 34))
self.pushButton_fillcolor.setObjectName("pushButton_fillcolor")
self.frame_linecolor = QtWidgets.QFrame(Dialog_graphic)
self.frame_linecolor.setGeometry(QtCore.QRect(1060, 530, 41, 21))
self.frame_linecolor.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_linecolor.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_linecolor.setObjectName("frame_linecolor")
self.frame_fillcolor = QtWidgets.QFrame(Dialog_graphic)
self.frame_fillcolor.setGeometry(QtCore.QRect(1060, 600, 41, 21))
self.frame_fillcolor.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_fillcolor.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_fillcolor.setObjectName("frame_fillcolor")
self.layoutWidget = QtWidgets.QWidget(Dialog_graphic)
self.layoutWidget.setGeometry(QtCore.QRect(1030, 420, 111, 28))
self.layoutWidget.setObjectName("layoutWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.layoutWidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label_line = QtWidgets.QLabel(self.layoutWidget)
font = QtGui.QFont()
font.setPointSize(13)
self.label_line.setFont(font)
self.label_line.setObjectName("label_line")
self.horizontalLayout.addWidget(self.label_line)
self.spinBox_line = QtWidgets.QSpinBox(self.layoutWidget)
font = QtGui.QFont()
font.setPointSize(13)
self.spinBox_line.setFont(font)
self.spinBox_line.setLayoutDirection(QtCore.Qt.LeftToRight)
self.spinBox_line.setMinimum(0)
self.spinBox_line.setMaximum(20)
self.spinBox_line.setObjectName("spinBox_line")
self.horizontalLayout.addWidget(self.spinBox_line)
self.Button_elli = QtWidgets.QPushButton(Dialog_graphic)
self.Button_elli.setGeometry(QtCore.QRect(1020, 70, 113, 32))
self.Button_elli.setObjectName("Button_elli")
self.Button_Pory = QtWidgets.QPushButton(Dialog_graphic)
self.Button_Pory.setGeometry(QtCore.QRect(1020, 110, 113, 32))
self.Button_Pory.setObjectName("Button_Pory")
self.layoutWidget1 = QtWidgets.QWidget(Dialog_graphic)
self.layoutWidget1.setGeometry(QtCore.QRect(1030, 370, 111, 26))
self.layoutWidget1.setObjectName("layoutWidget1")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.layoutWidget1)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label = QtWidgets.QLabel(self.layoutWidget1)
self.label.setObjectName("label")
self.horizontalLayout_2.addWidget(self.label)
self.spinBox_z = QtWidgets.QSpinBox(self.layoutWidget1)
self.spinBox_z.setMinimum(-10)
self.spinBox_z.setMaximum(10)
self.spinBox_z.setObjectName("spinBox_z")
self.horizontalLayout_2.addWidget(self.spinBox_z)
self.widget = QtWidgets.QWidget(Dialog_graphic)
self.widget.setGeometry(QtCore.QRect(1030, 320, 132, 26))
self.widget.setObjectName("widget")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.label_2 = QtWidgets.QLabel(self.widget)
self.label_2.setObjectName("label_2")
self.horizontalLayout_3.addWidget(self.label_2)
self.doubleSpinBox_scale = QtWidgets.QDoubleSpinBox(self.widget)
self.doubleSpinBox_scale.setDecimals(2)
self.doubleSpinBox_scale.setMinimum(0.1)
self.doubleSpinBox_scale.setMaximum(10.0)
self.doubleSpinBox_scale.setSingleStep(0.1)
self.doubleSpinBox_scale.setProperty("value", 1.0)
self.doubleSpinBox_scale.setObjectName("doubleSpinBox_scale")
self.horizontalLayout_3.addWidget(self.doubleSpinBox_scale)

self.retranslateUi(Dialog_graphic)
QtCore.QMetaObject.connectSlotsByName(Dialog_graphic)

def retranslateUi(self, Dialog_graphic):
_translate = QtCore.QCoreApplication.translate
Dialog_graphic.setWindowTitle(_translate("Dialog_graphic", "Dialog"))
self.pushButton_rect.setText(_translate("Dialog_graphic", "四角形"))
self.pushButton_rotate.setText(_translate("Dialog_graphic", "回転"))
self.pushButton_linecolor.setText(_translate("Dialog_graphic", "線の色"))
self.pushButton_fillcolor.setText(_translate("Dialog_graphic", "面の色"))
self.label_line.setText(_translate("Dialog_graphic", "線幅"))
self.Button_elli.setText(_translate("Dialog_graphic", "円形"))
self.Button_Pory.setText(_translate("Dialog_graphic", "多角形"))
self.label.setText(_translate("Dialog_graphic", "Z深度"))
self.label_2.setText(_translate("Dialog_graphic", "拡大縮小"))



test_graph_dialog_up05.py

import sys

import os
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from ui_test_Dialog01 import Ui_Dialog_graphic

class Test (QWidget):
def __init__(self, parent=None):
super (Test, self).__init__ (parent)
self.setWindowFlags(
Qt.Window | Qt.CustomizeWindowHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint)
self.ui = Ui_Dialog_graphic()
self.ui.setupUi (self)
self.scene = QGraphicsScene()
self.ui.graphicsView.setScene(self.scene)

#線色の初期設定 StyleSheetで
self.line_col = QColor('black')
self.ui.frame_linecolor.setStyleSheet("QWidget { background-color: black }")

#塗り色の初期設定 paletteで
self.fill_col = QColor('white')
pal = self.ui.frame_fillcolor.palette()
pal.setColor(QPalette.Background,Qt.white)
self.ui.frame_fillcolor.setAutoFillBackground(True)
self.ui.frame_fillcolor.setPalette(pal)

#signal & slot
self.ui.pushButton_rect.clicked.connect(self.addBOX)
self.ui.Button_elli.clicked.connect(self.addElli)
self.ui.Button_Pory.clicked.connect(self.addPory)
self.ui.pushButton_rotate.clicked.connect(self.rotate)
self.ui.doubleSpinBox_scale.valueChanged.connect(self.scale)
self.ui.pushButton_linecolor.clicked.connect(self.linecolor)
self.ui.pushButton_fillcolor.clicked.connect(self.fillcolor)
self.ui.spinBox_line.valueChanged.connect(self.setlinewidth)
self.ui.spinBox_z.valueChanged.connect(self.setZvalue)
self.scene.selectionChanged.connect(self.item_selectChanged)

#penの設定
self.pen = QPen(Qt.SolidLine)
self.pen.setColor(Qt.black)
self.linewidth = 5
self.pen.setWidth(self.linewidth)
self.pen.setCapStyle(Qt.FlatCap)
self.pen.setJoinStyle(Qt.MiterJoin)

#QGraphicsItemの初期化
self.rect = None
self.ellipse = None
self.polygon = None

def item_selectChanged(self):
self.ui.doubleSpinBox_scale.setValue(1.0)
width = self.linewidth
self.ui.spinBox_line.setValue(width)
self.pen.setWidth(self.linewidth)

item = self.selectedItem()

if item:
item.setBrush(self.fill_col)
item.setPen(self.pen)

def setZvalue(self):
for item in self.scene.items():
if item.isSelected() == True:
item.setZValue(self.ui.spinBox_z.value())
else:
pass

def linecolor(self):
col = QColorDialog.getColor()

if col.isValid():
self.line_col = col
self.ui.frame_linecolor.setStyleSheet("QWidget { background-color: %s }" % self.line_col.name())

item = self.selectedItem()
if item:
self.pen.setColor(self.line_col)
item.setPen(self.pen)

def fillcolor(self):
col = QColorDialog.getColor()
if self.fill_col.isValid():
self.fill_col = col
self.ui.frame_fillcolor.setStyleSheet("QWidget { background-color: %s }" % self.fill_col.name())

item = self.selectedItem()
if item:
item.setBrush(self.fill_col)

def setlinewidth(self):
self.linewidth = self.ui.spinBox_line.value()

self.pen.setColor(self.line_col)
self.pen.setWidth(self.linewidth)

item = self.selectedItem()

if item:
item.setBrush(self.fill_col)
item.setPen(self.pen)
self.linewidth = self.ui.spinBox_line.value()
self.pen.setWidth(self.linewidth)
item.setPen(self.pen)

def addBOX(self):

self.rect = QGraphicsRectItem(0, 0, 240, 240)
self.scene.addItem(self.rect)
self.rect.setFlag(QGraphicsItem.ItemIsSelectable)
self.rect.setFlag(QGraphicsItem.ItemIsMovable)
self.rect.setFlag(QGraphicsItem.ItemIsFocusable)

self.pen.setColor(self.line_col)
self.rect.setBrush(self.fill_col)
self.rect.setPen(self.pen)

def addElli(self):

self.ellipse = QGraphicsEllipseItem(0,0,240,240)
self.scene.addItem(self.ellipse)
self.ellipse.setFlag(QGraphicsItem.ItemIsSelectable)
self.ellipse.setFlag(QGraphicsItem.ItemIsMovable)
self.ellipse.setFlag(QGraphicsItem.ItemIsFocusable)

self.pen.setColor(self.line_col)
self.ellipse.setBrush(self.fill_col)
self.ellipse.setPen(self.pen)

def addPory(self):
mypoly = QPolygonF([
QPointF(80, 0), # 1
QPointF(160, 0), # 2
QPointF(160, 80), # 3
QPointF(240, 80), # 4
QPointF(240, 160), # 5
QPointF(160, 160), # 6
QPointF(160, 240), # 7
QPointF(80, 240), # 8
QPointF(80, 160), # 9
QPointF(0, 160), # 10
QPointF(0, 80), # 11
QPointF(80, 80) # 12
])

self.polygon = QGraphicsPolygonItem(mypoly)
self.scene.addItem(self.polygon)
self.polygon.setFlag(QGraphicsItem.ItemIsSelectable)
self.polygon.setFlag(QGraphicsItem.ItemIsMovable)
self.polygon.setFlag(QGraphicsItem.ItemIsFocusable)

self.pen.setColor(self.line_col)
self.polygon.setBrush(self.fill_col)
self.polygon.setPen(self.pen)

def rotate(self):

item = self.selectedItem()

if item:

centerX = item.boundingRect().width() / 2
centerY = item.boundingRect().height() / 2
item.setTransformOriginPoint(centerX , centerY)
item.setRotation(item.rotation() + 30)

def scale(self):
rate = self.ui.doubleSpinBox_scale.value()
if rate <= 0.0:
return

item = self.selectedItem()
if item:

centerX = item.boundingRect().width() / 2
centerY = item.boundingRect().height() / 2
item.setTransformOriginPoint(centerX, centerY)

item.setScale(rate)

self.linewidth = self.ui.spinBox_line.value()
self.pen.setWidth(self.linewidth)
item.setBrush(self.fill_col)
item.setPen(self.pen)

def selectedItem(self):
items = self.scene.selectedItems()
if len(items) == 1:
return items[0]
return None

if __name__ == '__main__':
app = QApplication (sys.argv)
path = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'icon_2.png')
app.setWindowIcon(QIcon(path))

window = Test ()
window.show ()
sys.exit (app.exec_ ())


いろんなことができるんでしょうが、2Dは難しいです。

ソースについて少々解説します。

setWindowFlagsはWindowをDialog風にしたりと手動でWindowのスタイルを決めることのできるメソッドです。

別に、Qt Designerで画面作っているなら設定する必要がないように思いますが、Macの最新版のOSの場合、普通に画面を作ると以下のように余計なものがついてきます。

スクリーンショット 2018-07-07 11.33.45.png

タイトルバーの下にタイトルバーみたいなものが、これはっきり言って邪魔です。setWindowFlagsを設定することでこれは消えます。

selectedItemはselectedItemsで選択されているitemの先頭を返します。

ソースでは、複数選択を許していないので、選択されているitemそのものが返却されます。

回転も拡大も以下のコードで選択itemの中心座標を取得します。

centerX = item.boundingRect().width() / 2

centerY = item.boundingRect().height() / 2

setTransformOriginPointによってitem座標に変換の原点を設定します