-
Notifications
You must be signed in to change notification settings - Fork 929
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
make46
committed
Dec 17, 2021
1 parent
82e2787
commit c0e5283
Showing
122 changed files
with
10,670 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,30 @@ | ||
# 前言 | ||
# 项目介绍 | ||
翻译自 zetcode 的 PyQt 教程。 | ||
|
||
PyQt5中文教程,翻译自 [zetcode](http://zetcode.com/gui/pyqt5/),GitBook 预览地址:[https://maicss.gitbook.io/pyqt5-chinese-tutoral/](https://maicss.gitbook.io/pyqt5-chinese-tutoral/) | ||
GitBook 预览地址:[https://maicss.gitbook.io/pyqt-chinese-tutoral/](https://maicss.gitbook.io/pyqt-chinese-tutoral/)。 | ||
|
||
这个教程比较好的地方是,能讲解每一段代码的含义。 | ||
开始阅读 [PyQt5](./pyqt5/index.md) 教程。 | ||
|
||
虽然PyQt的函数命名已经非常语义化了,但是对于新手来说,有这一步还是更好的。 | ||
开始阅读 [PyQt6](./pyqt6/index.md) 教程。 | ||
|
||
所以我选择了翻译这篇教程,希望能给刚入门的你带来帮助。 | ||
## 前言 | ||
|
||
翻译的水平有限\(如有错误,请指出\),而且有些地方是自己的理解,也加入了自己的提示在里面(并没有标注出来),所以并不完全等于原文。 | ||
|
||
我尽量让翻译不带英语腔,做到即使一个完全不懂编程的人来看,虽然不知道说的啥,但是最起码语句通顺,不会读着别扭。也算是对老师的一点敬意吧~~ | ||
这个教程比较好的地方是,能讲解每一段代码的含义。虽然PyQt的函数命名已经非常语义化了,但是对于新手来说,有这一步还是更好的。希望这篇能给刚入门的你带来帮助。 | ||
|
||
## 翻译吐槽: | ||
翻译的水平有限\(如有错误,请指出\),而且有些地方是自己的理解,也加入了自己的提示在里面(并没有标注出来),所以并不完全等于原文。 | ||
|
||
* label 这个词好难翻译,有时候就是个占位符的意思,说是文字说明吧,有专门的词 caption,但是像 checkbox 的名称这种的,不是文字说明又是啥...,但是居然还用 label 说图标这种事情,也是醉了。 | ||
* 源文档更新了,但是没有更新日志,只能一段段的比对……长了记性,创建了一个源文档的文件,下次直接 copy 的时候用 VCS 对比就可以了。 | ||
* 更新了一些图片,主要是原来没有的。因为手头没有Windows,而且源文档的图片也是不是Windows10,都不是一个风格的,凑合着看吧…… | ||
我尽量让翻译不带英语腔,做到即使一个完全不懂编程的人来看,虽然不知道说的啥,但是最起码语句通顺,不会读着别扭。 | ||
|
||
## 更新: | ||
添加了一些原文没有带的图片。所以有的图片风格跟原文的不一样。 | ||
|
||
### 2017-8 | ||
## 更新日志: | ||
|
||
### 2017-08 | ||
* [原文地址](https://zetcode.com/gui/pyqt5/) | ||
* 菜单和工具栏 【新增】 右键菜单 子菜单 勾选菜单 | ||
* 事件和信号 【新增】 事件对象 | ||
* 绘图 【新增】 贝塞尔曲线 | ||
|
||
### 2021-12 | ||
|
||
* 新增 PyQt6 教程,原文地址:[Python PyQt6](https://zetcode.com/pyqt6/) |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
# Custom widgets in PyQt6 | ||
*last modified May 17, 2021* | ||
|
||
PyQt6 has a rich set of widgets. However, no toolkit can provide all widgets that programmers might need in their applications. Toolkits usually provide only the most common widgets like buttons, text widgets, or sliders. If there is a need for a more specialised widget, we must create it ourselves. | ||
|
||
Custom widgets are created by using the drawing tools provided by the toolkit. There are two basic possibilities: a programmer can modify or enhance an existing widget or he can create a custom widget from scratch. | ||
|
||
## PyQt6 burning widget | ||
This is a widget that we can see in Nero, K3B, or other CD/DVD burning software. | ||
|
||
``` python | ||
# file: burning_widget.py | ||
#!/usr/bin/python | ||
|
||
""" | ||
ZetCode PyQt6 tutorial | ||
In this example, we create a custom widget. | ||
Author: Jan Bodnar | ||
Website: zetcode.com | ||
""" | ||
|
||
from PyQt6.QtWidgets import (QWidget, QSlider, QApplication, | ||
QHBoxLayout, QVBoxLayout) | ||
from PyQt6.QtCore import QObject, Qt, pyqtSignal | ||
from PyQt6.QtGui import QPainter, QFont, QColor, QPen | ||
import sys | ||
|
||
|
||
class Communicate(QObject): | ||
updateBW = pyqtSignal(int) | ||
|
||
|
||
class BurningWidget(QWidget): | ||
|
||
def __init__(self): | ||
super().__init__() | ||
|
||
self.initUI() | ||
|
||
|
||
def initUI(self): | ||
|
||
self.setMinimumSize(1, 30) | ||
self.value = 75 | ||
self.num = [75, 150, 225, 300, 375, 450, 525, 600, 675] | ||
|
||
|
||
def setValue(self, value): | ||
|
||
self.value = value | ||
|
||
|
||
def paintEvent(self, e): | ||
|
||
qp = QPainter() | ||
qp.begin(self) | ||
self.drawWidget(qp) | ||
qp.end() | ||
|
||
|
||
def drawWidget(self, qp): | ||
|
||
MAX_CAPACITY = 700 | ||
OVER_CAPACITY = 750 | ||
|
||
font = QFont('Serif', 7, QFont.Weight.Light) | ||
qp.setFont(font) | ||
|
||
size = self.size() | ||
w = size.width() | ||
h = size.height() | ||
|
||
step = int(round(w / 10)) | ||
|
||
till = int(((w / OVER_CAPACITY) * self.value)) | ||
full = int(((w / OVER_CAPACITY) * MAX_CAPACITY)) | ||
|
||
if self.value >= MAX_CAPACITY: | ||
|
||
qp.setPen(QColor(255, 255, 255)) | ||
qp.setBrush(QColor(255, 255, 184)) | ||
qp.drawRect(0, 0, full, h) | ||
qp.setPen(QColor(255, 175, 175)) | ||
qp.setBrush(QColor(255, 175, 175)) | ||
qp.drawRect(full, 0, till - full, h) | ||
|
||
else: | ||
|
||
qp.setPen(QColor(255, 255, 255)) | ||
qp.setBrush(QColor(255, 255, 184)) | ||
qp.drawRect(0, 0, till, h) | ||
|
||
pen = QPen(QColor(20, 20, 20), 1, | ||
Qt.PenStyle.SolidLine) | ||
|
||
qp.setPen(pen) | ||
qp.setBrush(Qt.BrushStyle.NoBrush) | ||
qp.drawRect(0, 0, w - 1, h - 1) | ||
|
||
j = 0 | ||
|
||
for i in range(step, 10 * step, step): | ||
|
||
qp.drawLine(i, 0, i, 5) | ||
metrics = qp.fontMetrics() | ||
fw = metrics.horizontalAdvance(str(self.num[j])) | ||
|
||
x, y = int(i - fw/2), int(h / 2) | ||
qp.drawText(x, y, str(self.num[j])) | ||
j = j + 1 | ||
|
||
|
||
class Example(QWidget): | ||
|
||
def __init__(self): | ||
super().__init__() | ||
|
||
self.initUI() | ||
|
||
|
||
def initUI(self): | ||
|
||
OVER_CAPACITY = 750 | ||
|
||
sld = QSlider(Qt.Orientations.Horizontal, self) | ||
sld.setFocusPolicy(Qt.FocusPolicy.NoFocus) | ||
sld.setRange(1, OVER_CAPACITY) | ||
sld.setValue(75) | ||
sld.setGeometry(30, 40, 150, 30) | ||
|
||
self.c = Communicate() | ||
self.wid = BurningWidget() | ||
self.c.updateBW[int].connect(self.wid.setValue) | ||
|
||
sld.valueChanged[int].connect(self.changeValue) | ||
hbox = QHBoxLayout() | ||
hbox.addWidget(self.wid) | ||
vbox = QVBoxLayout() | ||
vbox.addStretch(1) | ||
vbox.addLayout(hbox) | ||
self.setLayout(vbox) | ||
|
||
self.setGeometry(300, 300, 390, 210) | ||
self.setWindowTitle('Burning widget') | ||
self.show() | ||
|
||
|
||
def changeValue(self, value): | ||
|
||
self.c.updateBW.emit(value) | ||
self.wid.repaint() | ||
|
||
|
||
def main(): | ||
|
||
app = QApplication(sys.argv) | ||
ex = Example() | ||
sys.exit(app.exec()) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() | ||
``` | ||
In our example, we have a `QSlider` and a custom widget. The slider controls the custom widget. This widget shows graphically the total capacity of a medium and the free space available to us. The minimum value of our custom widget is 1, the maximum is OVER_CAPACITY. If we reach value MAX_CAPACITY, we begin drawing in red colour. This normally indicates overburning. | ||
|
||
The burning widget is placed at the bottom of the window. This is achieved using one `QHBoxLayout` and one `QVBoxLayout`. | ||
|
||
```python | ||
class BurningWidget(QWidget): | ||
|
||
def __init__(self): | ||
super().__init__() | ||
``` | ||
The burning widget it based on the `QWidget` widget. | ||
|
||
```python | ||
self.setMinimumSize(1, 30) | ||
``` | ||
We change the minimum size (height) of the widget. The default value is a bit small for us. | ||
```python | ||
font = QFont('Serif', 7, QFont.Weight.Light) | ||
qp.setFont(font) | ||
``` | ||
We use a smaller font than the default one. This better suits our needs. | ||
|
||
```python | ||
size = self.size() | ||
w = size.width() | ||
h = size.height() | ||
|
||
step = int(round(w / 10)) | ||
|
||
|
||
till = int(((w / OVER_CAPACITY) * self.value)) | ||
full = int(((w / OVER_CAPACITY) * MAX_CAPACITY)) | ||
``` | ||
We draw the widget dynamically. The greater is the window, the greater is the burning widget and vice versa. That is why we must calculate the size of the widget onto which we draw the custom widget. The till parameter determines the total size to be drawn. This value comes from the slider widget. It is a proportion of the whole area. The full parameter determines the point where we begin to draw in red colour. | ||
|
||
The actual drawing consists of three steps. We draw the yellow or the red and yellow rectangle. Then we draw the vertical lines which divide the widget into several parts. Finally, we draw the numbers which indicate the capacity of the medium. | ||
|
||
``` python | ||
metrics = qp.fontMetrics() | ||
fw = metrics.horizontalAdvance(str(self.num[j])) | ||
|
||
x, y = int(i - fw/2), int(h / 2) | ||
qp.drawText(x, y, str(self.num[j])) | ||
``` | ||
We use font metrics to draw the text. We must know the width of the text in order to center it around the vertical line. | ||
|
||
``` python | ||
def changeValue(self, value): | ||
|
||
self.c.updateBW.emit(value) | ||
self.wid.repaint() | ||
``` | ||
When we move the slider, the changeValue method is called. Inside the method, we send a custom updateBW signal with a parameter. The parameter is the current value of the slider. The value is later used to calculate the capacity of the Burning widget to be drawn. The custom widget is then repainted. | ||
|
||
![The burning widget](./images/burning.png) | ||
|
||
Figure: The burning widget | ||
|
||
In this part of the PyQt6 tutorial, we created a custom widget. |
Oops, something went wrong.