--1992 ~ (currently v5.8) -** Made by C ++ ** -** Cross-platform ** (drawing in OS native style) -** Rich widgets ** _ -** Easy-to-use API ** (Signal, Layout, MV) -** Tools ** (IDE, UI Designer)
A tool called SIP generates the C ++ <=> Python part.
--You can use almost all the functions of Qt
--The easy-to-use API of Qt remains the same
--More Pythonic writing
--Convert strings and arrays to Python types
--You can pass lambda
or method to the method
Display the current value of the scroll bar as a character string
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Sample1(QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
s = QScrollBar(orientation=Qt.Horizontal)
t = QLabel()
s.valueChanged.connect(lambda v: t.setText(str(v)))
l = QVBoxLayout()
l.addWidget(s)
l.addWidget(t)
self.setLayout(l)
app = QApplication(sys.argv)
s = Sample1()
s.show()
sys.exit(app.exec_())
--Approximately the same as .Net etc. --Even if the window size changes, it expands and contracts appropriately
--Each widget has its own signal
--Notify of value changes (valueChanged
), clicks (clicked
), etc.
--Register the destination function or method with .connect
--Observer pattern
→ Prevent widgets from tightly coupling with the model layer → More flexible than the callback method
--You may loop connect / setValue
--You may connect
to the same signal multiple times, or connect
to multiple signals.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Sample2(QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
l = QVBoxLayout()
t = QLabel()
s1 = QScrollBar(orientation=Qt.Horizontal)
s1.valueChanged.connect(lambda v: t.setText(str(v)))
s2 = QScrollBar(orientation=Qt.Horizontal)
s3 = QSpinBox()
s4 = QSpinBox()
s5 = QSpinBox()
s1.valueChanged.connect(s2.setValue)
s2.valueChanged.connect(s3.setValue)
s3.valueChanged.connect(s4.setValue)
s4.valueChanged.connect(s5.setValue)
s5.valueChanged.connect(s1.setValue)
l.addWidget(s1)
l.addWidget(s2)
l.addWidget(s3)
l.addWidget(s4)
l.addWidget(s5)
l.addWidget(t)
self.setLayout(l)
app = QApplication(sys.argv)
s = Sample2()
s.show()
sys.exit(app.exec_())
--Implemented data model widget --You can also define signals in your model
#Data (current board)
class BoardState:
#model
class BoardModel(QObject):
stateChanged = pyqtSignal(BoardState)
def moveKnightTo(self, x, y):
def rollback(self):
#Widget (each cell)
class BoardCellWidget(QLabel):
knightMoved = pyqtSignal(tuple)
def setState(self, state):
#A widget that has a model and displays 64 cells
class BoardWidget(QWidget):
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class BoardState:
def __init__(self, x, y):
self._x = x
self._y = y
def knightIn(self, x, y):
return (self._x, self._y) == (x, y)
def canKnightMoveTo(self, x, y):
dx = abs(self._x - x)
dy = abs(self._y - y)
return (dx, dy) in [(1, 2), (2, 1)]
class BoardModel(QObject):
stateChanged = pyqtSignal(BoardState)
def __init__(self, parent=None):
super().__init__(parent=parent)
self._states = [BoardState(0, 0)]
def moveKnightTo(self, x, y):
newState = BoardState(x, y)
self._states.append(newState)
self.stateChanged.emit(newState)
def rollback(self):
if len(self._states) <= 1:
return
self._states = self._states[:-1]
self.stateChanged.emit(self._states[-1])
def state(self):
return self._states[-1]
class BoardCellWidget(QLabel):
knightMoved = pyqtSignal(tuple)
def __init__(self, parent, x, y):
super().__init__(parent=parent)
self._x = x
self._y = y
self.setMinimumSize(QSize(32, 32))
self.setMouseTracking(True)
self.setAcceptDrops(True)
def setState(self, state):
self._state = state
self.update()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
drag = QDrag(self)
data = QMimeData()
data.setText("knight")
drag.setMimeData(data)
drag.exec()
event.accept()
def dropEvent(self, event):
if self._state.canKnightMoveTo(self._x, self._y):
self.knightMoved.emit((self._x, self._y))
event.acceptProposedAction()
def dragEnterEvent(self, event):
if self._state.canKnightMoveTo(self._x, self._y):
event.acceptProposedAction()
def paintEvent(self, event):
p = QPainter(self)
if (self._x + self._y) % 2 == 1:
p.setBackground(QColor(0xD3, 0x8C, 0x40))
else:
p.setBackground(QColor(0xFF, 0xCF, 0x9B))
p.eraseRect(self.rect())
rect = QRect(4, 4, self.width() - 8, self.height() - 8)
if self._state.knightIn(self._x, self._y):
p.fillRect(rect, Qt.white)
elif self._state.canKnightMoveTo(self._x, self._y):
p.fillRect(rect, Qt.green)
class BoardWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self._model = BoardModel(parent=self)
l = QGridLayout()
for x in range(8):
for y in range(8):
cell = BoardCellWidget(parent, x, y)
cell.setState(self._model.state())
self._model.stateChanged.connect(cell.setState)
cell.knightMoved.connect(lambda xy: self._model.moveKnightTo(*xy))
l.addWidget(cell, x, y)
l1 = QHBoxLayout()
l1.addLayout(l, 1)
self._rollback = QPushButton("rollback", parent=self)
self._rollback.pressed.connect(self._model.rollback)
l1.addWidget(self._rollback, 0)
self.setLayout(l1)
app = QApplication(sys.argv)
b = BoardWidget()
b.show()
sys.exit(app.exec_())
--There is also a DSL called QML / QtQuick
--You can embed a browser with QtWebEngineWidgets
(→ React.js can also be used)
--Even on Android, you can create an app by building using NDK.
--In the old days, it was difficult to build. Now OK with pip install
--License is GPL or paid ――Is there any problem for internal / individual use? --Installation work required --PyInstaller can be used to generate executable files for Windows --There should be a similar tool on Mac and Linux
Q&A
** Q. ** Why doesn't an infinite loop occur even if the widgets are connected in a loop with ʻa.valueChanged.connect (b.setValue) ? ** A. **
valueChanged` is only issued" when the value is changed ".
Recommended Posts