diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d702b3ce6612fb0c46bb1688b585f93a1247ed9d
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,7 @@
+# These are supported funding model platforms
+
+liberapay: Irony
+custom:
+ - https://github.com/PyQt5/PyQt#donate-打赏
+ - https://github.com/PyQt5/PyQt/blob/master/Donate/zhifubao.png
+ - https://github.com/PyQt5/PyQt/blob/master/Donate/weixin.png
diff --git a/.github/ISSUE_TEMPLATE/discussion---.md b/.github/ISSUE_TEMPLATE/discussion---.md
new file mode 100644
index 0000000000000000000000000000000000000000..532805df9742e0fcd3968b4cbc622bf072ea7bde
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/discussion---.md
@@ -0,0 +1,10 @@
+---
+name: Discussion/讨论
+about: 一些反复被提及的问题.
+title: "[讨论]"
+labels: 归纳
+assignees: ''
+
+---
+
+
diff --git a/.github/ISSUE_TEMPLATE/question---.md b/.github/ISSUE_TEMPLATE/question---.md
new file mode 100644
index 0000000000000000000000000000000000000000..22171af42368152601f8f52f05a03c177be7c17a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/question---.md
@@ -0,0 +1,35 @@
+---
+name: Question/提问
+about: Describe this issue template's purpose here./创建报告供帮助者复现你的问题
+title: "[问题] (保留前面格式)"
+labels: ''
+assignees: ''
+
+---
+
+**Environment :** / 环境
+ - OS: [e.g. Win 10]
+ - Python [e.g. 3.6.4 X64]
+ - PyQt5 [e.g. 5.10.1]
+
+( 读后删除以下段)
+**Describe the bug** or **Expected behavior** ,
+**Additional context** and **Screenshots**
+A clear and concise description of what the bug is.
+A clear and concise description of what you expected to happen.
+If applicable, add screenshots to help explain your problem.
+Add any other context about the problem here.
+
+// 描述你的问题 , 描述程序正常运行你希望的结果 , 提供程序的错误信息 和 源代码 截图 .
+// 一次性把问题描述清楚 , 帮助者没时间一来一回地协助你描述清楚问题后再帮助你解决 ;
+// 2个来回后帮助者还不清楚你描述的问题 , 则会关闭提问.
+// 如果代码过长 , 请创建一个仓库并附上链接.
+---
+如果需要贴代码 , 从下面2中格式 选一种
+
+格式2 :
+```python
+(代码段)
+```
+
+( 读后删除以上段)
diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e3084824e3dd57faf473da9f91264658e770625a
--- /dev/null
+++ b/.github/workflows/mirror.yml
@@ -0,0 +1,15 @@
+name: 'GitHub Actions Mirror'
+
+on: [push, delete]
+
+jobs:
+ mirror_to_gitee:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: pixta-dev/repository-mirroring-action@v1
+ with:
+ target_repo_url:
+ git@gitee.com:PyQt5/PyQt.git
+ ssh_private_key:
+ ${{ secrets.GIT_KEY }}
diff --git a/.gitignore b/.gitignore
index 258a4323bd5d39d5bba6691b3b92db0d6d24f5fb..9f409dbe579add7a3270bc6c36d1ce4c06a93199 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,16 @@ __pycache__/
*.py[co]
*$py.class
+debug
+release
+.qmake.stash
+Makefile
+Makefile.Debug
+Makefile.Release
+
+Tmp
+Tmp/*
+
# C extensions
*.so
@@ -14,8 +24,6 @@ dist/
downloads/
eggs/
.eggs/
-lib/
-lib64/
parts/
sdist/
var/
@@ -103,6 +111,10 @@ venv.bak/
# mypy
.mypy_cache/
+.idea
+.settings
+.project
+.pydevproject
*.pyc
Tmps/*
logs/*
@@ -116,6 +128,8 @@ log.txt
/tmp/*
/Tmps
/Tmps/*
+cache/*
+cache/
/tmp/Material
/tmp/Material/*
*.pyc
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index b81c7954b78b3bc416ac236a505b0fd58627746e..0000000000000000000000000000000000000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.xml
\ No newline at end of file
diff --git a/.idea/PyQt.iml b/.idea/PyQt.iml
deleted file mode 100644
index b38140f61f87d7566690ec03d76f1cdaa9acb179..0000000000000000000000000000000000000000
--- a/.idea/PyQt.iml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100644
index a55e7a179bde3e4e772c29c0c85e53354aa54618..0000000000000000000000000000000000000000
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index 60caf800435d1e29ff75121690c2759adfd668a9..0000000000000000000000000000000000000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index efebe5b5b8ba55563916c6ecf46a39a35d456743..0000000000000000000000000000000000000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
-
-
-
- Buildout
-
-
- EditorConfig
-
-
- General
-
-
- HTML
-
-
- Internationalization
-
-
- JSON and JSON5
-
-
- Markdown
-
-
- Properties Files
-
-
- Python
-
-
- RELAX NG
-
-
- ReST
-
-
- RegExp
-
-
- XML
-
-
- YAML
-
-
-
-
- Buildout
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index b32bc4cee70f43435868caa3f050f11b452d22ae..0000000000000000000000000000000000000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7f4cb416c083d265558da75d457237d671..0000000000000000000000000000000000000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.project b/.project
deleted file mode 100644
index d12d5878b8aa74a907f2da0657bf064ffeb353b2..0000000000000000000000000000000000000000
--- a/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- PyQt
-
-
-
-
-
- org.python.pydev.PyDevBuilder
-
-
-
-
-
- org.python.pydev.pythonNature
-
-
diff --git a/.pydevproject b/.pydevproject
deleted file mode 100644
index 5dc29c50112c6fb3c8539bd062b9bbab2befe721..0000000000000000000000000000000000000000
--- a/.pydevproject
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-Default
-python 3.0
-
-/${PROJECT_DIR_NAME}
-
-
diff --git a/.settings/.gitignore b/.settings/.gitignore
deleted file mode 100644
index 5983f9280a86956565e1b8667990bfef5fa855b9..0000000000000000000000000000000000000000
--- a/.settings/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.prefs
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index 141fb3c4ddb973fb84d8658784700c428d1e14f0..0000000000000000000000000000000000000000
--- a/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,44 +0,0 @@
-eclipse.preferences.version=1
-encoding//Demo/EmbedWindow.py=utf-8
-encoding//Demo/FacePoints.py=utf-8
-encoding//Demo/FollowWindow.py=utf-8
-encoding//Demo/FramelessWindow.py=utf-8
-encoding//Demo/Lib/Application.py=utf-8
-encoding//Demo/Lib/FramelessWindow.py=utf-8
-encoding//Demo/NativeEvent.py=utf-8
-encoding//Demo/Notification.py=utf-8
-encoding//Demo/ProbeWindow.py=utf-8
-encoding//Demo/RestartWindow.py=utf-8
-encoding//Demo/SharedMemory.py=utf-8
-encoding//Demo/SingleApplication.py=utf-8
-encoding//Demo/VerificationCode.py=utf-8
-encoding//Demo/WeltHideWindow.py=utf-8
-encoding//Demo/WindowNotify.py=utf-8
-encoding//QChart/LineChart.py=utf-8
-encoding//QFont/AwesomeFont.py=utf-8
-encoding//QFont/Lib/FontAwesome.py=utf-8
-encoding//QListView/CustomWidgetSortItem.py=utf-8
-encoding//QListView/SortItemByRole.py=utf-8
-encoding//QMessageBox/CustomColorIcon.py=utf-8
-encoding//QProgressBar/Lib/WaterRippleProgressBar.py=utf-8
-encoding//QProgressBar/MetroCircleProgress.py=utf-8
-encoding//QProgressBar/PercentProgressBar.py=utf-8
-encoding//QProgressBar/SimpleStyle.py=utf-8
-encoding//QProgressBar/WaterProgressBar.py=utf-8
-encoding//QProxyStyle/Lib/TabBarStyle.py=utf-8
-encoding//QProxyStyle/TabTextDirection.py=utf-8
-encoding//QPushButton/BottomLineProgress.py=utf-8
-encoding//QPushButton/FontRotate.py=utf-8
-encoding//QPushButton/NormalStyle.py=utf-8
-encoding//QScrollBar/StyleScrollBar.py=utf-8
-encoding//QSlider/PaintQSlider.py=utf-8
-encoding//QSlider/QssQSlider.py=utf-8
-encoding//QSplitter/RewriteHandle.py=utf-8
-encoding//QThread/moveToThread.py=utf-8
-encoding//QTreeWidget/ParsingJson.py=utf-8
-encoding//QWebView/DreamTree.py=utf-8
-encoding//QWidget/Lib/CustomPaintWidget.py=utf-8
-encoding//QWidget/Lib/CustomWidget.py=utf-8
-encoding//QWidget/WidgetStyle.py=utf-8
-encoding//Test/\u5168\u5C40\u70ED\u952E/HotKey.py=utf-8
-encoding//Test/\u81EA\u52A8\u66F4\u65B0/test.py=utf-8
diff --git a/.settings/org.eclipse.ltk.core.refactoring.prefs b/.settings/org.eclipse.ltk.core.refactoring.prefs
deleted file mode 100644
index cfcd1d3c22f7ad061b0ffb72377bff737ba31120..0000000000000000000000000000000000000000
--- a/.settings/org.eclipse.ltk.core.refactoring.prefs
+++ /dev/null
@@ -1,2 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false
diff --git a/Demo/AutoRestart.py b/Demo/AutoRestart.py
index 23a3ff13e2a5e972860ddb35afae5e34ec143682..024d61bbbd1dfbfaae8ac383adb4c89a6a74d63a 100644
--- a/Demo/AutoRestart.py
+++ b/Demo/AutoRestart.py
@@ -1,30 +1,62 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年3月31日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: AutoRestart
@description:
-'''
+"""
-from optparse import OptionParser
import os
import sys
-import time
+from optparse import OptionParser
+
+try:
+ from PyQt5.QtWidgets import QApplication, QPushButton, QWidget, QHBoxLayout
+except ImportError:
+ from PySide2.QtWidgets import QApplication, QPushButton, QWidget, QHBoxLayout
+
+canRestart = True
def restart(twice):
os.execl(sys.executable, sys.executable, *[sys.argv[0], "-t", twice])
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 400)
+ layout = QHBoxLayout(self)
+
+ self.buttonRestart = QPushButton(
+ "app start...%s...twice\napp pid: %s\n点击按钮重启...\n" %
+ (options.twice, os.getpid()), self)
+ self.buttonRestart.clicked.connect(self.close)
+
+ self.buttonExit = QPushButton('退出', self, clicked=self.doExit)
+
+ layout.addWidget(self.buttonRestart)
+ layout.addWidget(self.buttonExit)
+
+ def doExit(self):
+ global canRestart
+ canRestart = False
+ self.close()
+
+
if __name__ == "__main__":
parser = OptionParser(usage="usage:%prog [optinos] filepath")
- parser.add_option("-t", "--twice", type="int", dest="twice", default=1, help="运行次数")
+ parser.add_option("-t", "--twice", type="int",
+ dest="twice", default=1, help="运行次数")
options, _ = parser.parse_args()
- print("app start...%s...twice\n" % options.twice)
- print("app pid: ",os.getpid())
- print("3秒后自动重启...\n")
- time.sleep(3)
- restart(str(options.twice + 1))
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ app.exec_()
+ if canRestart:
+ restart(str(options.twice + 1))
diff --git a/Demo/BubbleTips.py b/Demo/BubbleTips.py
index 8af85c76bef7a44ec67aa0064d4fcbb2eb77e78e..6f842a3cbb5c5b5cac494fee50f91f74f4c58467 100644
--- a/Demo/BubbleTips.py
+++ b/Demo/BubbleTips.py
@@ -1,30 +1,31 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月27日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: BubbleTips
@description:
-'''
+"""
import sys
-from PyQt5.QtCore import QRectF, Qt, QPropertyAnimation, pyqtProperty, \
- QPoint, QParallelAnimationGroup, QEasingCurve
-from PyQt5.QtGui import QPainter, QPainterPath, QColor, QPen
-from PyQt5.QtWidgets import QLabel, QWidget, QVBoxLayout, QApplication,\
- QLineEdit, QPushButton
-
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QRectF, Qt, QPropertyAnimation, pyqtProperty, \
+ QPoint, QParallelAnimationGroup, QEasingCurve
+ from PyQt5.QtGui import QPainter, QPainterPath, QColor, QPen
+ from PyQt5.QtWidgets import QLabel, QWidget, QVBoxLayout, QApplication, \
+ QLineEdit, QPushButton
+except ImportError:
+ from PySide2.QtCore import QRectF, Qt, QPropertyAnimation, Property as pyqtProperty, \
+ QPoint, QParallelAnimationGroup, QEasingCurve
+ from PySide2.QtGui import QPainter, QPainterPath, QColor, QPen
+ from PySide2.QtWidgets import QLabel, QWidget, QVBoxLayout, QApplication, \
+ QLineEdit, QPushButton
class BubbleLabel(QWidget):
-
BackgroundColor = QColor(195, 195, 195)
BorderColor = QColor(150, 150, 150)
@@ -133,10 +134,10 @@ class BubbleLabel(QWidget):
opacity = pyqtProperty(float, windowOpacity, setWindowOpacity)
-class TestWidget(QWidget):
+class Window(QWidget):
def __init__(self, *args, **kwargs):
- super(TestWidget, self).__init__(*args, **kwargs)
+ super(Window, self).__init__(*args, **kwargs)
layout = QVBoxLayout(self)
self.msgEdit = QLineEdit(self, returnPressed=self.onMsgShow)
self.msgButton = QPushButton("显示内容", self, clicked=self.onMsgShow)
@@ -158,6 +159,6 @@ class TestWidget(QWidget):
if __name__ == "__main__":
app = QApplication(sys.argv)
- w = TestWidget()
+ w = Window()
w.show()
sys.exit(app.exec_())
diff --git a/Demo/CallVirtualKeyboard.py b/Demo/CallVirtualKeyboard.py
new file mode 100644
index 0000000000000000000000000000000000000000..4afe18ba3c1a46f90bc28757c8dad15db44858fa
--- /dev/null
+++ b/Demo/CallVirtualKeyboard.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年5月22日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: Demo.CallVirtualKeyboard
+@description: 调用系统虚拟键盘
+"""
+import glob
+
+try:
+ from PyQt5.QtCore import QProcess, QSysInfo
+ from PyQt5.QtWidgets import QApplication, QWidget, QTextEdit, QVBoxLayout, QPushButton
+except ImportError:
+ from PySide2.QtCore import QProcess, QSysInfo
+ from PySide2.QtWidgets import QApplication, QWidget, QTextEdit, QVBoxLayout, QPushButton
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+ self.resultEdit = QTextEdit(self)
+ self.resultEdit.setReadOnly(True)
+ layout.addWidget(self.resultEdit)
+ layout.addWidget(QPushButton(
+ '打开虚拟键盘', self, clicked=self._onOpenKeyboard))
+
+ def _onOpenKeyboard(self):
+ kernelType = QSysInfo.kernelType()
+ if kernelType == 'winnt':
+ try:
+ path = glob.glob(
+ r'C:\Windows\WinSxS\amd64_microsoft-windows-osk_*\osk.exe')[0]
+ ret = QProcess.startDetached(path)
+ self.resultEdit.append('start 64 osk: %s' % ret)
+ except Exception as e:
+ self.resultEdit.append('start osk error: %s' % e)
+ try:
+ # 32位程序调用64位操作系统下的程序会被重定向到SysWOW64目录
+ # 可通过`Wow64DisableWow64FsRedirection`和`Wow64RevertWow64FsRedirection`控制
+ ret = QProcess.startDetached(r'C:\Windows\system32\osk.exe')
+ self.resultEdit.append('start 32 osk: %s' % ret)
+ except Exception as e:
+ self.resultEdit.append('start osk error: %s' % e)
+ elif kernelType == 'darwin':
+ pass
+ # elif kernelType=='linux':
+ else:
+ ret = QProcess.startDetached('florence')
+ self.resultEdit.append('start florence: %s' % ret)
+ ret = QProcess.startDetached('onboard')
+ self.resultEdit.append('start onboard: %s' % ret)
+ ret = QProcess.startDetached('kvkbd')
+ self.resultEdit.append('start kvkbd: %s' % ret)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/Demo/CircleLine.py b/Demo/CircleLine.py
new file mode 100644
index 0000000000000000000000000000000000000000..83102c7ef6234ece6fc2731955dbdf6ccfc27c8b
--- /dev/null
+++ b/Demo/CircleLine.py
@@ -0,0 +1,267 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年3月19日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CircleLine
+@description:
+"""
+
+from math import floor, pi, cos, sin
+from random import random, randint
+from time import time
+
+try:
+ from PyQt5.QtCore import QTimer, Qt
+ from PyQt5.QtGui import QColor, QPainter, QPainterPath, QPen
+ from PyQt5.QtWidgets import QWidget, QApplication
+except ImportError:
+ from PySide2.QtCore import QTimer, Qt
+ from PySide2.QtGui import QColor, QPainter, QPainterPath, QPen
+ from PySide2.QtWidgets import QWidget, QApplication
+
+# 最小和最大半径、半径阈值和填充圆的百分比
+radMin = 10
+radMax = 80
+filledCircle = 30 # 填充圆的百分比
+concentricCircle = 60 # 同心圆百分比
+radThreshold = 25 # IFF special, over this radius concentric, otherwise filled
+# 最小和最大移动速度
+speedMin = 0.3
+speedMax = 0.6
+# 每个圆和模糊效果的最大透明度
+maxOpacity = 0.6
+
+colors = [
+ QColor(52, 168, 83),
+ QColor(117, 95, 147),
+ QColor(199, 108, 23),
+ QColor(194, 62, 55),
+ QColor(0, 172, 212),
+ QColor(120, 120, 120)
+]
+circleBorder = 10
+backgroundLine = colors[0]
+backgroundColor = QColor(38, 43, 46)
+backgroundMlt = 0.85
+
+lineBorder = 2.5
+
+# 最重要的是:包含它们的整个圆和数组的数目
+maxCircles = 8
+points = []
+
+# 实验变量
+circleExp = 1
+circleExpMax = 1.003
+circleExpMin = 0.997
+circleExpSp = 0.00004
+circlePulse = False
+
+
+# 生成随机整数 a<=x<=b
+
+
+def randint(a, b):
+ return floor(random() * (b - a + 1) + a)
+
+
+# 生成随机小数
+
+
+def randRange(a, b):
+ return random() * (b - a) + a
+
+
+# 生成接近a的随机小数
+
+
+def hyperRange(a, b):
+ return random() * random() * random() * (b - a) + a
+
+
+class Circle:
+
+ def __init__(self, background, width, height):
+ self.background = background
+ self.x = randRange(-width / 2, width / 2)
+ self.y = randRange(-height / 2, height / 2)
+ self.radius = hyperRange(radMin, radMax)
+ self.filled = (False if randint(
+ 0, 100) > concentricCircle else 'full') if self.radius < radThreshold else (
+ False if randint(0, 100) > concentricCircle else 'concentric')
+ self.color = colors[randint(0, len(colors) - 1)]
+ self.borderColor = colors[randint(0, len(colors) - 1)]
+ self.opacity = 0.05
+ self.speed = randRange(speedMin, speedMax) # * (radMin / self.radius)
+ self.speedAngle = random() * 2 * pi
+ self.speedx = cos(self.speedAngle) * self.speed
+ self.speedy = sin(self.speedAngle) * self.speed
+ spacex = abs((self.x - (-1 if self.speedx < 0 else 1) *
+ (width / 2 + self.radius)) / self.speedx)
+ spacey = abs((self.y - (-1 if self.speedy < 0 else 1) *
+ (height / 2 + self.radius)) / self.speedy)
+ self.ttl = min(spacex, spacey)
+
+
+class CircleLineWindow(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(CircleLineWindow, self).__init__(*args, **kwargs)
+ # 设置背景颜色
+ palette = self.palette()
+ palette.setColor(palette.Background, backgroundColor)
+ self.setAutoFillBackground(True)
+ self.setPalette(palette)
+ # 获取屏幕大小
+ geometry = QApplication.instance().desktop().availableGeometry()
+ self.screenWidth = geometry.width()
+ self.screenHeight = geometry.height()
+ self._canDraw = True
+ self._firstDraw = True
+ self._timer = QTimer(self, timeout=self.update)
+ self.init()
+
+ def init(self):
+ points.clear()
+ # 链接的最小距离
+ self.linkDist = min(self.screenWidth, self.screenHeight) / 2.4
+ # 初始化点
+ for _ in range(maxCircles * 3):
+ points.append(Circle('', self.screenWidth, self.screenHeight))
+ self.update()
+
+ def showEvent(self, event):
+ super(CircleLineWindow, self).showEvent(event)
+ self._canDraw = True
+
+ def hideEvent(self, event):
+ super(CircleLineWindow, self).hideEvent(event)
+ # 窗口最小化要停止绘制, 减少cpu占用
+ self._canDraw = False
+
+ def paintEvent(self, event):
+ super(CircleLineWindow, self).paintEvent(event)
+ if not self._canDraw:
+ return
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.Antialiasing)
+ painter.setRenderHint(QPainter.SmoothPixmapTransform)
+ self.draw(painter)
+
+ def draw(self, painter):
+ if circlePulse:
+ if circleExp < circleExpMin or circleExp > circleExpMax:
+ circleExpSp *= -1
+ circleExp += circleExpSp
+
+ painter.translate(self.screenWidth / 2, self.screenHeight / 2)
+
+ if self._firstDraw:
+ t = time()
+ self.renderPoints(painter, points)
+ if self._firstDraw:
+ self._firstDraw = False
+ # 此处有个比例关系用于设置timer的时间,如果初始窗口很小,没有比例会导致动画很快
+ t = (time() - t) * 1000 * 2
+ # 比例最大不能超过1920/800
+ t = int(min(2.4, self.screenHeight / self.height()) * t) - 1
+ t = t if t > 15 else 15 # 不能小于15s
+ print('start timer(%d msec)' % t)
+ # 开启定时器
+ self._timer.start(t)
+
+ def drawCircle(self, painter, circle):
+ # circle.radius *= circleExp
+ if circle.background:
+ circle.radius *= circleExp
+ else:
+ circle.radius /= circleExp
+ radius = circle.radius
+
+ r = radius * circleExp
+ # 边框颜色设置透明度
+ c = QColor(circle.borderColor)
+ c.setAlphaF(circle.opacity)
+
+ painter.save()
+ if circle.filled == 'full':
+ # 设置背景刷
+ painter.setBrush(c)
+ painter.setPen(Qt.NoPen)
+ else:
+ # 设置画笔
+ painter.setPen(
+ QPen(c, max(1, circleBorder * (radMin - circle.radius) / (radMin - radMax))))
+
+ # 画实心圆或者圆圈
+ painter.drawEllipse(circle.x - r, circle.y - r, 2 * r, 2 * r)
+ painter.restore()
+
+ if circle.filled == 'concentric':
+ r = radius / 2
+ # 画圆圈
+ painter.save()
+ painter.setBrush(Qt.NoBrush)
+ painter.setPen(
+ QPen(c, max(1, circleBorder * (radMin - circle.radius) / (radMin - radMax))))
+ painter.drawEllipse(circle.x - r, circle.y - r, 2 * r, 2 * r)
+ painter.restore()
+
+ circle.x += circle.speedx
+ circle.y += circle.speedy
+ if (circle.opacity < maxOpacity):
+ circle.opacity += 0.01
+ circle.ttl -= 1
+
+ def renderPoints(self, painter, circles):
+ for i, circle in enumerate(circles):
+ if circle.ttl < -20:
+ # 重新初始化一个
+ circle = Circle('', self.screenWidth, self.screenHeight)
+ circles[i] = circle
+ self.drawCircle(painter, circle)
+
+ circles_len = len(circles)
+ for i in range(circles_len - 1):
+ for j in range(i + 1, circles_len):
+ deltax = circles[i].x - circles[j].x
+ deltay = circles[i].y - circles[j].y
+ dist = pow(pow(deltax, 2) + pow(deltay, 2), 0.5)
+ # if the circles are overlapping, no laser connecting them
+ if dist <= circles[i].radius + circles[j].radius:
+ continue
+ # otherwise we connect them only if the dist is < linkDist
+ if dist < self.linkDist:
+ xi = (1 if circles[i].x < circles[j].x else -
+ 1) * abs(circles[i].radius * deltax / dist)
+ yi = (1 if circles[i].y < circles[j].y else -
+ 1) * abs(circles[i].radius * deltay / dist)
+ xj = (-1 if circles[i].x < circles[j].x else 1) * \
+ abs(circles[j].radius * deltax / dist)
+ yj = (-1 if circles[i].y < circles[j].y else 1) * \
+ abs(circles[j].radius * deltay / dist)
+ path = QPainterPath()
+ path.moveTo(circles[i].x + xi, circles[i].y + yi)
+ path.lineTo(circles[j].x + xj, circles[j].y + yj)
+ # samecolor = circles[i].color == circles[j].color
+ c = QColor(circles[i].borderColor)
+ c.setAlphaF(min(circles[i].opacity, circles[j].opacity)
+ * ((self.linkDist - dist) / self.linkDist))
+ painter.setPen(QPen(c, (
+ lineBorder * backgroundMlt if circles[i].background else lineBorder) * (
+ (self.linkDist - dist) / self.linkDist)))
+ painter.drawPath(path)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = CircleLineWindow()
+ w.resize(800, 600)
+ w.show()
+ sys.exit(app.exec_())
diff --git a/Demo/CustomProperties.py b/Demo/CustomProperties.py
index 8a053a2e5a2767a3816cc8cc93783d8b40b0dd8f..43eab24d83db382d0d58cf4a3df42311e09116d2 100644
--- a/Demo/CustomProperties.py
+++ b/Demo/CustomProperties.py
@@ -1,25 +1,25 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年4月12日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: 自定义属性测试
@description:
-'''
+"""
from random import randint
-from PyQt5.QtCore import pyqtProperty, pyqtSignal
-from PyQt5.QtWidgets import QPushButton
-
-
-__version__ = "0.0.1"
+try:
+ from PyQt5.QtCore import pyqtProperty, pyqtSignal
+ from PyQt5.QtWidgets import QPushButton, QApplication
+except ImportError:
+ from PySide2.QtCore import Property as pyqtProperty, Signal as pyqtSignal
+ from PySide2.QtWidgets import QPushButton, QApplication
class Window(QPushButton):
-
bgChanged = pyqtSignal(str, str)
def __init__(self):
@@ -56,7 +56,7 @@ class Window(QPushButton):
if __name__ == "__main__":
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.setStyleSheet(
diff --git a/Demo/Data/Images/Cursors/0.png b/Demo/Data/Images/Cursors/0.png
new file mode 100644
index 0000000000000000000000000000000000000000..27923f3baf91dd64e402a45c6eb645f819724ea1
Binary files /dev/null and b/Demo/Data/Images/Cursors/0.png differ
diff --git a/Demo/Data/Images/Cursors/1.png b/Demo/Data/Images/Cursors/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..e338b58a4bc7d63f6ce1c12c87c2ee165a414f89
Binary files /dev/null and b/Demo/Data/Images/Cursors/1.png differ
diff --git a/Demo/Data/Images/Cursors/2.png b/Demo/Data/Images/Cursors/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..59ee69537accd8ece438d9ef2951aa6d0ecb7e22
Binary files /dev/null and b/Demo/Data/Images/Cursors/2.png differ
diff --git a/Demo/Data/Images/Cursors/3.png b/Demo/Data/Images/Cursors/3.png
new file mode 100644
index 0000000000000000000000000000000000000000..b86d752d6a1606e96344e2cc424fa324e752824e
Binary files /dev/null and b/Demo/Data/Images/Cursors/3.png differ
diff --git a/Demo/Data/Images/Cursors/4.png b/Demo/Data/Images/Cursors/4.png
new file mode 100644
index 0000000000000000000000000000000000000000..61374dba987517b981cec9da52c80f67892b45c4
Binary files /dev/null and b/Demo/Data/Images/Cursors/4.png differ
diff --git a/Demo/Data/Images/Cursors/5.png b/Demo/Data/Images/Cursors/5.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c2bc104c56305084a32743f3d276ba8cea69c83
Binary files /dev/null and b/Demo/Data/Images/Cursors/5.png differ
diff --git a/Demo/Data/Images/Cursors/6.png b/Demo/Data/Images/Cursors/6.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab3996a8cc00a67421bfd548986ef275a79dabac
Binary files /dev/null and b/Demo/Data/Images/Cursors/6.png differ
diff --git a/Demo/Data/Images/Cursors/7.png b/Demo/Data/Images/Cursors/7.png
new file mode 100644
index 0000000000000000000000000000000000000000..0962be4759d758cec9af80859d00e93f0233ce3e
Binary files /dev/null and b/Demo/Data/Images/Cursors/7.png differ
diff --git a/Demo/Data/frameless.ui b/Demo/Data/frameless.ui
new file mode 100644
index 0000000000000000000000000000000000000000..a7919b0292608d448cd4160ae2706bcf31273134
--- /dev/null
+++ b/Demo/Data/frameless.ui
@@ -0,0 +1,197 @@
+
+
+ FormFrameless
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ Symbola
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 253
+ 20
+
+
+
+
+ -
+
+
+
+ 36
+ 36
+
+
+
+
+ 36
+ 36
+
+
+
+
+ webdings
+
+
+
+ Minimum
+
+
+ 0
+
+
+
+ -
+
+
+
+ 36
+ 36
+
+
+
+
+ 36
+ 36
+
+
+
+
+ webdings
+
+
+
+ Maximum
+
+
+ 1
+
+
+
+ -
+
+
+
+ 36
+ 36
+
+
+
+
+ 36
+ 36
+
+
+
+
+ webdings
+
+
+
+ Normal
+
+
+ 2
+
+
+
+ -
+
+
+
+ 36
+ 36
+
+
+
+
+ 36
+ 36
+
+
+
+
+ webdings
+
+
+
+ Close
+
+
+ r
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">frameless window with move and resize</p></body></html>
+
+
+
+
+
+
+
+
diff --git "a/Demo/Data/\350\203\214\346\231\257\350\277\236\347\272\277\345\212\250\347\224\273.html" "b/Demo/Data/\350\203\214\346\231\257\350\277\236\347\272\277\345\212\250\347\224\273.html"
new file mode 100644
index 0000000000000000000000000000000000000000..e6d25d784e3dc59f1603441d7ab8e125e4bcdeaf
--- /dev/null
+++ "b/Demo/Data/\350\203\214\346\231\257\350\277\236\347\272\277\345\212\250\347\224\273.html"
@@ -0,0 +1,208 @@
+
+
+
+
+ 背景连线动画
+
+
+
+
+
+
+
+
+
diff --git a/Demo/EmbedWindow.py b/Demo/EmbedWindow.py
index c3360d760fe484753cf44783426a85226ddf92ee..b6ffe04ff94881262bf9b0478d8288058fc413a5 100644
--- a/Demo/EmbedWindow.py
+++ b/Demo/EmbedWindow.py
@@ -4,22 +4,26 @@
"""
Created on 2018年3月1日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: EmbedWindow
@description: 嵌入外部窗口
"""
-__Author__ = 'By: Irony\nQQ: 892768447\nEmail: 892768447@qq.com'
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
-
-from PyQt5.QtGui import QWindow
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QListWidget,\
- QLabel
import win32con
import win32gui
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QWindow
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QListWidget, \
+ QLabel, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QWindow
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QPushButton, QListWidget, \
+ QLabel, QApplication
+
class Window(QWidget):
@@ -32,16 +36,22 @@ class Window(QWidget):
layout.addWidget(QPushButton('获取所有可用、可视窗口', self,
clicked=self._getWindowList, maximumHeight=30))
+ layout.addWidget(QPushButton('释放窗口', clicked=self.releaseWidget, maximumHeight=30))
layout.addWidget(
QLabel('双击列表中的项目则进行嵌入目标窗口到下方\n格式为:句柄|父句柄|标题|类名', self, maximumHeight=30))
self.windowList = QListWidget(
self, itemDoubleClicked=self.onItemDoubleClicked, maximumHeight=200)
layout.addWidget(self.windowList)
+ def releaseWidget(self):
+ """释放窗口"""
+ if self.layout().count() == 5:
+ self.restore()
+ self._getWindowList()
+
def closeEvent(self, event):
"""窗口关闭"""
- if self.layout().count() == 4:
- self.restore()
+ self.releaseWidget()
super(Window, self).closeEvent(event)
def _getWindowList(self):
@@ -55,39 +65,41 @@ class Window(QWidget):
self.windowList.takeItem(self.windowList.indexFromItem(item).row())
hwnd, phwnd, _, _ = item.text().split('|')
# 开始嵌入
-
- if self.layout().count() == 4:
- # 如果数量等于4说明之前已经嵌入了一个窗口,现在需要把它释放出来
- self.restore()
+ self.releaseWidget()
hwnd, phwnd = int(hwnd), int(phwnd)
# 嵌入之前的属性
style = win32gui.GetWindowLong(hwnd, win32con.GWL_STYLE)
exstyle = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
- print('save', hwnd, style, exstyle)
+ wrect = win32gui.GetWindowRect(hwnd)[:2] + win32gui.GetClientRect(hwnd)[2:]
+ print('save', hwnd, style, exstyle, wrect)
widget = QWidget.createWindowContainer(QWindow.fromWinId(hwnd))
widget.hwnd = hwnd # 窗口句柄
widget.phwnd = phwnd # 父窗口句柄
widget.style = style # 窗口样式
widget.exstyle = exstyle # 窗口额外样式
+ widget.wrect = wrect # 窗口位置
self.layout().addWidget(widget)
+ widget.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
+ win32gui.SetParent(hwnd, int(self.winId()))
+
def restore(self):
"""归还窗口"""
# 有bug,归还后窗口没有了WS_VISIBLE样式,不可见
- widget = self.layout().itemAt(3).widget()
- print('restore', widget.hwnd, widget.style, widget.exstyle)
- win32gui.SetParent(widget.hwnd, widget.phwnd) # 让它返回它的父窗口
- win32gui.SetWindowLong(
- widget.hwnd, win32con.GWL_STYLE, widget.style | win32con.WS_VISIBLE) # 恢复样式
- win32gui.SetWindowLong(
- widget.hwnd, win32con.GWL_EXSTYLE, widget.exstyle) # 恢复样式
- win32gui.ShowWindow(
- widget.hwnd, win32con.SW_SHOW) # 显示窗口
+ widget = self.layout().itemAt(4).widget()
+ hwnd, phwnd, style, exstyle, wrect = widget.hwnd, widget.phwnd, widget.style, widget.exstyle, widget.wrect
+ print('restore', hwnd, phwnd, style, exstyle, wrect)
widget.close()
self.layout().removeWidget(widget) # 从布局中移出
widget.deleteLater()
+ win32gui.SetParent(hwnd, phwnd) # 让它返回它的父窗口
+ win32gui.SetWindowLong(hwnd, win32con.GWL_STYLE, style | win32con.WS_VISIBLE) # 恢复样式
+ win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, exstyle) # 恢复样式
+ win32gui.ShowWindow(hwnd, win32con.SW_SHOW) # 显示窗口
+ win32gui.SetWindowPos(hwnd, 0, wrect[0], wrect[1], wrect[2], wrect[3], win32con.SWP_NOACTIVATE)
+
def _enumWindows(self, hwnd, _):
"""遍历回调函数"""
if hwnd == self.myhwnd:
@@ -102,7 +114,10 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+ import cgitb
+
+ cgitb.enable(format='text')
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/Demo/FacePoints.py b/Demo/FacePoints.py
index 7ebea2b859bd4c2fd16fc1df7839750fd41e34c1..98b6165ca2069cac93df4a56a4593d2b2a4ae405 100644
--- a/Demo/FacePoints.py
+++ b/Demo/FacePoints.py
@@ -1,31 +1,33 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月29日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: FacePoints
@description: 人脸特征点
-'''
-from bz2 import BZ2Decompressor
+"""
import cgitb
import os
import sys
+from bz2 import BZ2Decompressor
-from PyQt5.QtCore import QTimer, QUrl, QFile, QIODevice
-from PyQt5.QtGui import QImage, QPixmap
-from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
-from PyQt5.QtWidgets import QLabel, QMessageBox, QApplication
import cv2 # @UnresolvedImport
import dlib
import numpy
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QTimer, QUrl, QFile, QIODevice
+ from PyQt5.QtGui import QImage, QPixmap
+ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
+ from PyQt5.QtWidgets import QLabel, QMessageBox, QApplication
+except ImportError:
+ from PySide2.QtCore import QTimer, QUrl, QFile, QIODevice
+ from PySide2.QtGui import QImage, QPixmap
+ from PySide2.QtNetwork import QNetworkAccessManager, QNetworkRequest
+ from PySide2.QtWidgets import QLabel, QMessageBox, QApplication
DOWNSCALE = 4
URL = 'http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2'
@@ -163,7 +165,7 @@ class OpencvWidget(QLabel):
if __name__ == "__main__":
- sys.excepthook = cgitb.enable(1, None, 5, '')
+ cgitb.enable(format='text')
app = QApplication(sys.argv)
w = OpencvWidget()
w.show()
diff --git a/Demo/FollowWindow.py b/Demo/FollowWindow.py
index 5e54287c36ca8ea01db6b003d5ae569edc611447..41ad74c71fbd0c07b16598844b7f7fba97708422 100644
--- a/Demo/FollowWindow.py
+++ b/Demo/FollowWindow.py
@@ -4,23 +4,21 @@
"""
Created on 2018年10月22日
@author: Irony
-@site: https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: FollowWindow
@description: 跟随外部窗口
"""
import os
-from PyQt5.QtCore import QTimer
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton
import win32gui
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QTimer
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication
+except ImportError:
+ from PySide2.QtCore import QTimer
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication
class Window(QWidget):
@@ -53,7 +51,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
# 先检测是否已有记事本打开
hwnd = win32gui.FindWindow('Notepad', None)
print('hwnd', hwnd)
diff --git a/Demo/FramelessDialog.py b/Demo/FramelessDialog.py
new file mode 100644
index 0000000000000000000000000000000000000000..b55bef3fe8f0ac34aaedb97a4f124e3dba0c697e
--- /dev/null
+++ b/Demo/FramelessDialog.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年4月19日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: FramelessDialog
+@description: 无边框圆角对话框
+"""
+
+try:
+ from PyQt5.QtCore import Qt, QSize, QTimer
+ from PyQt5.QtWidgets import QDialog, QVBoxLayout, QWidget, \
+ QGraphicsDropShadowEffect, QPushButton, QGridLayout, QSpacerItem, \
+ QSizePolicy, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt, QSize, QTimer
+ from PySide2.QtWidgets import QDialog, QVBoxLayout, QWidget, \
+ QGraphicsDropShadowEffect, QPushButton, QGridLayout, QSpacerItem, \
+ QSizePolicy, QApplication
+
+Stylesheet = """
+#Custom_Widget {
+ background: white;
+ border-radius: 10px;
+}
+
+#closeButton {
+ min-width: 36px;
+ min-height: 36px;
+ font-family: "Webdings";
+ qproperty-text: "r";
+ border-radius: 10px;
+}
+#closeButton:hover {
+ color: white;
+ background: red;
+}
+"""
+
+
+class Dialog(QDialog):
+
+ def __init__(self, *args, **kwargs):
+ super(Dialog, self).__init__(*args, **kwargs)
+ self.setObjectName('Custom_Dialog')
+ self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)
+ self.setAttribute(Qt.WA_TranslucentBackground, True)
+ self.setStyleSheet(Stylesheet)
+ self.initUi()
+ # 添加阴影
+ effect = QGraphicsDropShadowEffect(self)
+ effect.setBlurRadius(12)
+ effect.setOffset(0, 0)
+ effect.setColor(Qt.gray)
+ self.setGraphicsEffect(effect)
+
+ def initUi(self):
+ layout = QVBoxLayout(self)
+ # 重点: 这个widget作为背景和圆角
+ self.widget = QWidget(self)
+ self.widget.setObjectName('Custom_Widget')
+ layout.addWidget(self.widget)
+
+ # 在widget中添加ui
+ layout = QGridLayout(self.widget)
+ layout.addItem(QSpacerItem(
+ 40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, 0)
+ layout.addWidget(QPushButton(
+ 'r', self, clicked=self.accept, objectName='closeButton'), 0, 1)
+ layout.addItem(QSpacerItem(20, 40, QSizePolicy.Minimum,
+ QSizePolicy.Expanding), 1, 0)
+
+ def sizeHint(self):
+ return QSize(600, 400)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Dialog()
+ w.exec_()
+ QTimer.singleShot(200, app.quit)
+ sys.exit(app.exec_())
diff --git a/Demo/FramelessWindow.py b/Demo/FramelessWindow.py
index aef6c261ba390945df3e02029b5df0b7bf227f92..7e1608e2d95500e563feb2a43674096260836622 100644
--- a/Demo/FramelessWindow.py
+++ b/Demo/FramelessWindow.py
@@ -1,22 +1,24 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QTextEdit
-from Lib.FramelessWindow import FramelessWindow # @UnresolvedImport
+"""
+Created on 2018年4月30日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: Test
+@description:
+"""
+
+try:
+ from PyQt5.QtGui import QIcon
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QTextEdit, QApplication
+except ImportError:
+ from PySide2.QtGui import QIcon
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QPushButton, QTextEdit, QApplication
-# Created on 2018年4月30日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: Test
-# description:
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+from Lib.FramelessWindow import FramelessWindow # @UnresolvedImport
class MainWindow(QWidget):
@@ -63,7 +65,7 @@ TitleBar {
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
app.setStyleSheet(StyleSheet)
w = FramelessWindow()
diff --git a/Demo/GifCursor.py b/Demo/GifCursor.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b1ea8f193f39ea8929381a55cf83eeee6117048
--- /dev/null
+++ b/Demo/GifCursor.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020年3月13日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: Demo.GifCursor
+@description:
+"""
+
+try:
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication
+except ImportError:
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication
+
+from Lib.QCursorGif import QCursorGif
+
+
+class Window(QWidget, QCursorGif):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ # 设置忙碌光标图片数组
+ self.initCursor(['Data/Images/Cursors/%d.png' %
+ i for i in range(8)], self)
+ self.setCursorTimeout(100)
+
+ layout = QVBoxLayout(self)
+ layout.addWidget(QPushButton(
+ 'start busy', self, clicked=self.startBusy))
+ layout.addWidget(QPushButton(
+ 'stop busy', self, clicked=self.stopBusy))
+
+
+if __name__ == '__main__':
+ import sys
+ import cgitb
+
+ cgitb.enable(format='text')
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/Demo/IsSignalConnected.py b/Demo/IsSignalConnected.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d534f3fa1520a5d1d700f06591ea86032fd54f8
--- /dev/null
+++ b/Demo/IsSignalConnected.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年2月24日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: IsSignalConnected
+@description: 判断信号是否连接
+"""
+
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QTextBrowser
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+ self.button1 = QPushButton('已连接', self, clicked=self.doTest)
+ self.button2 = QPushButton('未连接', self)
+ self.retView = QTextBrowser(self)
+ layout.addWidget(self.button1)
+ layout.addWidget(self.button2)
+ layout.addWidget(self.retView)
+
+ def doTest(self):
+ self.retView.append("""
+ # button1 clicked 是否连接: %s, %s
+ # button2 clicked 是否连接: %s, %s
+ """ % (
+ self.isSignalConnected(self.button1, 'clicked()'),
+ self.button1.receivers(self.button1.clicked) > 0,
+ self.isSignalConnected(self.button2, 'clicked()'),
+ self.button2.receivers(self.button2.clicked) > 0,
+ ))
+
+ def isSignalConnected(self, obj, name):
+ """判断信号是否连接
+ :param obj: 对象
+ :param name: 信号名,如 clicked()
+ """
+ index = obj.metaObject().indexOfMethod(name)
+ if index > -1:
+ method = obj.metaObject().method(index)
+ if method:
+ return obj.isSignalConnected(method)
+ return False
+
+
+if __name__ == '__main__':
+ import sys
+ from PyQt5.QtWidgets import QApplication
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/Demo/Lib/Application.py b/Demo/Lib/Application.py
index c1216004ee9c4b40ed9e484d70e9028687a947ca..eed9376aa50acd03b7279921ac441e60e85ed91a 100644
--- a/Demo/Lib/Application.py
+++ b/Demo/Lib/Application.py
@@ -1,35 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年3月30日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: 单实例应用.Application
@description:
-'''
+"""
+
from PyQt5.QtCore import QSharedMemory, pyqtSignal, Qt
from PyQt5.QtNetwork import QLocalSocket, QLocalServer
from PyQt5.QtWidgets import QApplication
-__version__ = "0.0.1"
-
class SharedApplication(QApplication):
-
+
def __init__(self, *args, **kwargs):
super(SharedApplication, self).__init__(*args, **kwargs)
self._running = False
- key = "SharedApplication" + __version__
+ key = "SharedApplication"
self._memory = QSharedMemory(key, self)
-
+
isAttached = self._memory.isAttached()
print("isAttached", isAttached)
if isAttached: # 如果进程附加在共享内存上
detach = self._memory.detach() # 取消进程附加在共享内存上
print("detach", detach)
-
+
if self._memory.create(1) and self._memory.error() != QSharedMemory.AlreadyExists:
# 创建共享内存,如果创建失败,则说明已经创建,否则未创建
print("create ok")
@@ -37,14 +36,14 @@ class SharedApplication(QApplication):
print("create failed")
self._running = True
del self._memory
-
+
def isRunning(self):
return self._running
+
class QSingleApplication(QApplication):
-
messageReceived = pyqtSignal(str)
-
+
def __init__(self, *args, **kwargs):
super(QSingleApplication, self).__init__(*args, **kwargs)
appid = QApplication.applicationFilePath().lower().split("/")[-1]
@@ -56,13 +55,13 @@ class QSingleApplication(QApplication):
self._socketIn = None
self._socketOut = None
self._running = False
-
+
# 先尝试连接
self._socketOut = QLocalSocket(self)
self._socketOut.connectToServer(self._socketName)
self._socketOut.error.connect(self.handleError)
self._running = self._socketOut.waitForConnected()
-
+
if not self._running: # 程序未运行
self._socketOut.close()
del self._socketOut
@@ -70,20 +69,20 @@ class QSingleApplication(QApplication):
self._socketServer.listen(self._socketName)
self._socketServer.newConnection.connect(self._onNewConnection)
self.aboutToQuit.connect(self.removeServer)
-
+
def handleError(self, message):
print("handleError message: ", message)
-
+
def isRunning(self):
return self._running
-
+
def activationWindow(self):
return self._activationWindow
-
+
def setActivationWindow(self, activationWindow, activateOnMessage=True):
self._activationWindow = activationWindow
self._activateOnMessage = activateOnMessage
-
+
def activateWindow(self):
if not self._activationWindow:
return
@@ -91,7 +90,7 @@ class QSingleApplication(QApplication):
self._activationWindow.windowState() & ~Qt.WindowMinimized)
self._activationWindow.raise_()
self._activationWindow.activateWindow()
-
+
def sendMessage(self, message, msecs=5000):
if not self._socketOut:
return False
@@ -99,10 +98,10 @@ class QSingleApplication(QApplication):
message = str(message).encode()
self._socketOut.write(message)
if not self._socketOut.waitForBytesWritten(msecs):
- raise RuntimeError("Bytes not written within %ss" %
+ raise RuntimeError("Bytes not written within %ss" %
(msecs / 1000.))
return True
-
+
def _onNewConnection(self):
if self._socketIn:
self._socketIn.readyRead.disconnect(self._onReadyRead)
@@ -112,7 +111,7 @@ class QSingleApplication(QApplication):
self._socketIn.readyRead.connect(self._onReadyRead)
if self._activateOnMessage:
self.activateWindow()
-
+
def _onReadyRead(self):
while 1:
message = self._socketIn.readLine()
@@ -123,4 +122,4 @@ class QSingleApplication(QApplication):
def removeServer(self):
self._socketServer.close()
- self._socketServer.removeServer(self._socketName)
\ No newline at end of file
+ self._socketServer.removeServer(self._socketName)
diff --git a/Demo/Lib/FramelessWindow.py b/Demo/Lib/FramelessWindow.py
index 04afccb92dc796b24753668dab9b6aa9efa1de2b..b4fa3e0ef195f60ad690e2799245b02abbdf5a8a 100644
--- a/Demo/Lib/FramelessWindow.py
+++ b/Demo/Lib/FramelessWindow.py
@@ -1,26 +1,29 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from PyQt5.QtCore import Qt, pyqtSignal, QPoint
-from PyQt5.QtGui import QFont, QEnterEvent, QPainter, QColor, QPen
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel,\
- QSpacerItem, QSizePolicy, QPushButton
-# Created on 2018年4月30日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: FramelessWindow
-# description:
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+"""
+Created on 2018年4月30日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: FramelessWindow
+@description:
+"""
+try:
+ from PyQt5.QtCore import Qt, pyqtSignal, QPoint
+ from PyQt5.QtGui import QFont, QEnterEvent, QPainter, QColor, QPen
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, \
+ QSpacerItem, QSizePolicy, QPushButton
+except ImportError:
+ from PySide2.QtCore import Qt, Signal as pyqtSignal, QPoint
+ from PySide2.QtGui import QFont, QEnterEvent, QPainter, QColor, QPen
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, \
+ QSpacerItem, QSizePolicy, QPushButton
-class TitleBar(QWidget):
+class TitleBar(QWidget):
# 窗口最小化信号
windowMinimumed = pyqtSignal()
# 窗口最大化信号
@@ -48,7 +51,7 @@ class TitleBar(QWidget):
layout.setContentsMargins(0, 0, 0, 0)
# 窗口图标
self.iconLabel = QLabel(self)
-# self.iconLabel.setScaledContents(True)
+ # self.iconLabel.setScaledContents(True)
layout.addWidget(self.iconLabel)
# 窗口标题
self.titleLabel = QLabel(self)
@@ -138,7 +141,6 @@ Left, Top, Right, Bottom, LeftTop, RightTop, LeftBottom, RightBottom = range(8)
class FramelessWindow(QWidget):
-
# 四周边距
Margins = 5
diff --git a/Demo/Lib/QCursorGif.py b/Demo/Lib/QCursorGif.py
new file mode 100644
index 0000000000000000000000000000000000000000..707e7479f99b846bbd829d97263e7be33fb96ad0
--- /dev/null
+++ b/Demo/Lib/QCursorGif.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020年3月13日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: Demo.Lib.QCursorGif
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import QTimer, Qt
+ from PyQt5.QtGui import QCursor, QPixmap
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtCore import QTimer, Qt
+ from PySide2.QtGui import QCursor, QPixmap
+ from PySide2.QtWidgets import QApplication
+
+
+class QCursorGif:
+
+ def initCursor(self, cursors, parent=None):
+ # 记录默认的光标
+ self._oldCursor = Qt.ArrowCursor
+ self.setOldCursor(parent)
+ # 加载光标图片
+ self._cursorImages = [
+ QCursor(QPixmap(cursor)) for cursor in cursors]
+ self._cursorIndex = 0
+ self._cursorCount = len(self._cursorImages) - 1
+ # 创建刷新定时器
+ self._cursorTimeout = 200
+ self._cursorTimer = QTimer(parent)
+ self._cursorTimer.timeout.connect(self._doBusy)
+
+ def _doBusy(self):
+ if self._cursorIndex > self._cursorCount:
+ self._cursorIndex = 0
+ QApplication.instance().setOverrideCursor(
+ self._cursorImages[self._cursorIndex])
+ self._cursorIndex += 1
+
+ def startBusy(self):
+ if not self._cursorTimer.isActive():
+ self._cursorTimer.start(self._cursorTimeout)
+
+ def stopBusy(self):
+ self._cursorTimer.stop()
+ QApplication.instance().setOverrideCursor(self._oldCursor)
+
+ def setCursorTimeout(self, timeout):
+ self._cursorTimeout = timeout
+
+ def setOldCursor(self, parent=None):
+ self._oldCursor = (parent.cursor() or Qt.ArrowCursor) if parent else (
+ QApplication.instance().overrideCursor() or Qt.ArrowCursor)
diff --git a/Demo/Lib/UiNotify.py b/Demo/Lib/UiNotify.py
index 20d26aa4a1059a7f404cb6ba03ddb997ade8850c..38f1ad92036394663bb54d58692dc0f5cac072bb 100644
--- a/Demo/Lib/UiNotify.py
+++ b/Demo/Lib/UiNotify.py
@@ -6,46 +6,50 @@
#
# WARNING! All changes made in this file will be lost!
-from PyQt5 import QtCore, QtGui, QtWidgets
+try:
+ from PyQt5 import QtCore, QtGui, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtGui, QtWidgets
+
class Ui_NotifyForm(object):
def setupUi(self, NotifyForm):
NotifyForm.setObjectName("NotifyForm")
NotifyForm.resize(300, 200)
NotifyForm.setStyleSheet("QWidget#widgetTitle {\n"
-" background-color: rgb(76, 169, 106);\n"
-"}\n"
-"QWidget#widgetBottom {\n"
-" border-top-style: solid;\n"
-" border-top-width: 2px;\n"
-" border-top-color: rgb(185, 218, 201);\n"
-"}\n"
-"QLabel#labelTitle {\n"
-" color: rgb(255, 255, 255);\n"
-"}\n"
-"QLabel#labelContent {\n"
-" padding: 5px;\n"
-"}\n"
-"QPushButton {\n"
-" border: none;\n"
-" background: transparent;\n"
-"}\n"
-"QPushButton#buttonClose {\n"
-" font-family: \"webdings\";\n"
-" color: rgb(255, 255, 255);\n"
-"}\n"
-"QPushButton#buttonClose:hover {\n"
-" background-color: rgb(212, 64, 39);\n"
-"}\n"
-"QPushButton#buttonView {\n"
-" color: rgb(255, 255, 255);\n"
-" border-radius: 5px;\n"
-" border: solid 1px rgb(76, 169, 106);\n"
-" background-color: rgb(76, 169, 106);\n"
-"}\n"
-"QPushButton#buttonView:hover {\n"
-" color: rgb(0, 0, 0);\n"
-"}")
+ " background-color: rgb(76, 169, 106);\n"
+ "}\n"
+ "QWidget#widgetBottom {\n"
+ " border-top-style: solid;\n"
+ " border-top-width: 2px;\n"
+ " border-top-color: rgb(185, 218, 201);\n"
+ "}\n"
+ "QLabel#labelTitle {\n"
+ " color: rgb(255, 255, 255);\n"
+ "}\n"
+ "QLabel#labelContent {\n"
+ " padding: 5px;\n"
+ "}\n"
+ "QPushButton {\n"
+ " border: none;\n"
+ " background: transparent;\n"
+ "}\n"
+ "QPushButton#buttonClose {\n"
+ " font-family: \"webdings\";\n"
+ " color: rgb(255, 255, 255);\n"
+ "}\n"
+ "QPushButton#buttonClose:hover {\n"
+ " background-color: rgb(212, 64, 39);\n"
+ "}\n"
+ "QPushButton#buttonView {\n"
+ " color: rgb(255, 255, 255);\n"
+ " border-radius: 5px;\n"
+ " border: solid 1px rgb(76, 169, 106);\n"
+ " background-color: rgb(76, 169, 106);\n"
+ "}\n"
+ "QPushButton#buttonView:hover {\n"
+ " color: rgb(0, 0, 0);\n"
+ "}")
self.verticalLayout = QtWidgets.QVBoxLayout(NotifyForm)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setSpacing(6)
@@ -61,7 +65,8 @@ class Ui_NotifyForm(object):
self.labelTitle.setText("")
self.labelTitle.setObjectName("labelTitle")
self.horizontalLayout_3.addWidget(self.labelTitle)
- spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding,
+ QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem)
self.buttonClose = QtWidgets.QPushButton(self.widgetTitle)
self.buttonClose.setMinimumSize(QtCore.QSize(26, 26))
@@ -80,7 +85,8 @@ class Ui_NotifyForm(object):
self.horizontalLayout.setContentsMargins(0, 5, 5, 5)
self.horizontalLayout.setSpacing(0)
self.horizontalLayout.setObjectName("horizontalLayout")
- spacerItem1 = QtWidgets.QSpacerItem(170, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ spacerItem1 = QtWidgets.QSpacerItem(170, 20, QtWidgets.QSizePolicy.Expanding,
+ QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.buttonView = QtWidgets.QPushButton(self.widgetBottom)
self.buttonView.setMinimumSize(QtCore.QSize(75, 25))
@@ -102,10 +108,10 @@ class Ui_NotifyForm(object):
if __name__ == "__main__":
import sys
+
app = QtWidgets.QApplication(sys.argv)
NotifyForm = QtWidgets.QWidget()
ui = Ui_NotifyForm()
ui.setupUi(NotifyForm)
NotifyForm.show()
sys.exit(app.exec_())
-
diff --git a/Demo/Lib/ui_frameless.py b/Demo/Lib/ui_frameless.py
new file mode 100644
index 0000000000000000000000000000000000000000..94bc7b7a853991576fe74af45a5ec8ec2a2843e5
--- /dev/null
+++ b/Demo/Lib/ui_frameless.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'frameless.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.2
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+try:
+ from PyQt5 import QtCore, QtGui, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtGui, QtWidgets
+
+
+class Ui_FormFrameless(object):
+ def setupUi(self, FormFrameless):
+ FormFrameless.setObjectName("FormFrameless")
+ FormFrameless.resize(400, 300)
+ self.verticalLayout = QtWidgets.QVBoxLayout(FormFrameless)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.widgetTitleBar = QtWidgets.QWidget(FormFrameless)
+ font = QtGui.QFont()
+ font.setFamily("Symbola")
+ self.widgetTitleBar.setFont(font)
+ self.widgetTitleBar.setObjectName("widgetTitleBar")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.widgetTitleBar)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ spacerItem = QtWidgets.QSpacerItem(253, 20, QtWidgets.QSizePolicy.Expanding,
+ QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.buttonMinimum = QtWidgets.QPushButton(self.widgetTitleBar)
+ self.buttonMinimum.setMinimumSize(QtCore.QSize(36, 36))
+ self.buttonMinimum.setMaximumSize(QtCore.QSize(36, 36))
+ font = QtGui.QFont()
+ font.setFamily("webdings")
+ self.buttonMinimum.setFont(font)
+ self.buttonMinimum.setObjectName("buttonMinimum")
+ self.horizontalLayout.addWidget(self.buttonMinimum)
+ self.buttonMaximum = QtWidgets.QPushButton(self.widgetTitleBar)
+ self.buttonMaximum.setMinimumSize(QtCore.QSize(36, 36))
+ self.buttonMaximum.setMaximumSize(QtCore.QSize(36, 36))
+ font = QtGui.QFont()
+ font.setFamily("webdings")
+ self.buttonMaximum.setFont(font)
+ self.buttonMaximum.setObjectName("buttonMaximum")
+ self.horizontalLayout.addWidget(self.buttonMaximum)
+ self.buttonNormal = QtWidgets.QPushButton(self.widgetTitleBar)
+ self.buttonNormal.setMinimumSize(QtCore.QSize(36, 36))
+ self.buttonNormal.setMaximumSize(QtCore.QSize(36, 36))
+ font = QtGui.QFont()
+ font.setFamily("webdings")
+ self.buttonNormal.setFont(font)
+ self.buttonNormal.setObjectName("buttonNormal")
+ self.horizontalLayout.addWidget(self.buttonNormal)
+ self.buttonClose = QtWidgets.QPushButton(self.widgetTitleBar)
+ self.buttonClose.setMinimumSize(QtCore.QSize(36, 36))
+ self.buttonClose.setMaximumSize(QtCore.QSize(36, 36))
+ font = QtGui.QFont()
+ font.setFamily("webdings")
+ self.buttonClose.setFont(font)
+ self.buttonClose.setObjectName("buttonClose")
+ self.horizontalLayout.addWidget(self.buttonClose)
+ self.verticalLayout.addWidget(self.widgetTitleBar)
+ self.textEdit = QtWidgets.QTextEdit(FormFrameless)
+ self.textEdit.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.textEdit.setObjectName("textEdit")
+ self.verticalLayout.addWidget(self.textEdit)
+ self.verticalLayout.setStretch(1, 1)
+
+ self.retranslateUi(FormFrameless)
+ QtCore.QMetaObject.connectSlotsByName(FormFrameless)
+
+ def retranslateUi(self, FormFrameless):
+ _translate = QtCore.QCoreApplication.translate
+ FormFrameless.setWindowTitle(_translate("FormFrameless", "Form"))
+ self.buttonMinimum.setToolTip(_translate("FormFrameless", "Minimum"))
+ self.buttonMinimum.setText(_translate("FormFrameless", "0"))
+ self.buttonMaximum.setToolTip(_translate("FormFrameless", "Maximum"))
+ self.buttonMaximum.setText(_translate("FormFrameless", "1"))
+ self.buttonNormal.setToolTip(_translate("FormFrameless", "Normal"))
+ self.buttonNormal.setText(_translate("FormFrameless", "2"))
+ self.buttonClose.setToolTip(_translate("FormFrameless", "Close"))
+ self.buttonClose.setText(_translate("FormFrameless", "r"))
+ self.textEdit.setHtml(_translate("FormFrameless",
+ "\n"
+ " \n"
+ "frameless window with move and resize
"))
+
+
+if __name__ == "__main__":
+ import sys
+
+ app = QtWidgets.QApplication(sys.argv)
+ FormFrameless = QtWidgets.QWidget()
+ ui = Ui_FormFrameless()
+ ui.setupUi(FormFrameless)
+ FormFrameless.show()
+ sys.exit(app.exec_())
diff --git a/Demo/NativeEvent.py b/Demo/NativeEvent.py
index 2ccf1693736a45440f661f8c38bd5b75baeb0a75..173124050882ab7cbdb18892169bfb44f8ed4dc8 100644
--- a/Demo/NativeEvent.py
+++ b/Demo/NativeEvent.py
@@ -2,42 +2,42 @@
# -*- coding: utf-8 -*-
"""Created on 2018年8月2日
author: Irony
-site: https://pyqt5.com , https://github.com/892768447
+site: https://pyqt.site , https://github.com/PyQt5
email: 892768447@qq.com
file: win无边框调整大小
description:
"""
-from ctypes.wintypes import POINT
import ctypes.wintypes
+from ctypes.wintypes import POINT
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QWidget, QPushButton
-from PyQt5.QtWinExtras import QtWin
import win32api
import win32con
import win32gui
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QCursor
+ from PyQt5.QtWidgets import QApplication, QPushButton, QWidget
+ from PyQt5.QtWinExtras import QtWin
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QCursor
+ from PySide2.QtWidgets import QApplication, QPushButton, QWidget
+ from PySide2.QtWinExtras import QtWin
class MINMAXINFO(ctypes.Structure):
_fields_ = [
- ("ptReserved", POINT),
- ("ptMaxSize", POINT),
- ("ptMaxPosition", POINT),
- ("ptMinTrackSize", POINT),
- ("ptMaxTrackSize", POINT),
+ ("ptReserved", POINT),
+ ("ptMaxSize", POINT),
+ ("ptMaxPosition", POINT),
+ ("ptMinTrackSize", POINT),
+ ("ptMaxTrackSize", POINT),
]
class Window(QWidget):
-
BorderWidth = 5
def __init__(self, *args, **kwargs):
@@ -45,16 +45,15 @@ class Window(QWidget):
# 主屏幕的可用大小(去掉任务栏)
self._rect = QApplication.instance().desktop().availableGeometry(self)
self.resize(800, 600)
- self.setWindowFlags(Qt.Window
- | Qt.FramelessWindowHint
- | Qt.WindowSystemMenuHint
- | Qt.WindowMinimizeButtonHint
- | Qt.WindowMaximizeButtonHint
- | Qt.WindowCloseButtonHint)
+ self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint |
+ Qt.WindowSystemMenuHint |
+ Qt.WindowMinimizeButtonHint |
+ Qt.WindowMaximizeButtonHint |
+ Qt.WindowCloseButtonHint)
# 增加薄边框
style = win32gui.GetWindowLong(int(self.winId()), win32con.GWL_STYLE)
- win32gui.SetWindowLong(
- int(self.winId()), win32con.GWL_STYLE, style | win32con.WS_THICKFRAME)
+ win32gui.SetWindowLong(int(self.winId()), win32con.GWL_STYLE,
+ style | win32con.WS_THICKFRAME)
if QtWin.isCompositionEnabled():
# 加上 Aero 边框阴影
@@ -67,8 +66,9 @@ class Window(QWidget):
if eventType == "windows_generic_MSG":
msg = ctypes.wintypes.MSG.from_address(message.__int__())
# 获取鼠标移动经过时的坐标
- x = win32api.LOWORD(msg.lParam) - self.frameGeometry().x()
- y = win32api.HIWORD(msg.lParam) - self.frameGeometry().y()
+ pos = QCursor.pos()
+ x = pos.x() - self.frameGeometry().x()
+ y = pos.y() - self.frameGeometry().y()
# 判断鼠标位置是否有其它控件
if self.childAt(x, y) != None:
return retval, result
@@ -77,8 +77,8 @@ class Window(QWidget):
return True, 0
if msg.message == win32con.WM_GETMINMAXINFO:
# 当窗口位置改变或者大小改变时会触发该消息
- info = ctypes.cast(
- msg.lParam, ctypes.POINTER(MINMAXINFO)).contents
+ info = ctypes.cast(msg.lParam,
+ ctypes.POINTER(MINMAXINFO)).contents
# 修改最大化的窗口大小为主屏幕的可用大小
info.ptMaxSize.x = self._rect.width()
info.ptMaxSize.y = self._rect.height()
@@ -121,7 +121,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
btn = QPushButton('exit', w, clicked=app.quit)
diff --git a/Demo/NewFramelessWindow.py b/Demo/NewFramelessWindow.py
new file mode 100644
index 0000000000000000000000000000000000000000..75dcc742feb20f0a1a5191445756fe8f47bb775d
--- /dev/null
+++ b/Demo/NewFramelessWindow.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2018年4月30日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: NewFramelessWindow
+@description:
+"""
+
+import sys
+
+try:
+ from PyQt5.QtCore import QEvent, QObject, QPoint, Qt, QTimer
+ from PyQt5.QtGui import QColor, QMouseEvent, QPainter, QWindow
+ from PyQt5.QtWidgets import QApplication, QMessageBox, QWidget
+except ImportError:
+ from PySide2.QtCore import QTimer, Qt, QEvent, QObject, QPoint
+ from PySide2.QtGui import QWindow, QPainter, QColor, QMouseEvent
+ from PySide2.QtWidgets import QApplication, QWidget, QMessageBox
+
+from Lib.ui_frameless import Ui_FormFrameless
+
+
+class FramelessObject(QObject):
+ Margins = 3 # 边缘边距
+ TitleHeight = 36 # 标题栏高度
+ Widgets = set() # 无边框窗口集合
+
+ @classmethod
+ def set_margins(cls, margins):
+ cls.Margins = margins
+
+ @classmethod
+ def set_title_height(cls, height):
+ cls.TitleHeight = height
+
+ @classmethod
+ def add_widget(cls, widget):
+ cls.Widgets.add(widget)
+
+ @classmethod
+ def del_widget(cls, widget):
+ if widget in cls.Widgets:
+ cls.Widgets.remove(widget)
+
+ def _get_edges(self, pos, width, height):
+ """根据坐标获取方向
+ :param pos: QPoint
+ :param width: int
+ :param height: int
+ :return: Qt.Edges
+ """
+ edge = 0
+ x, y = pos.x(), pos.y()
+
+ if y <= self.Margins:
+ edge |= Qt.TopEdge
+ if x <= self.Margins:
+ edge |= Qt.LeftEdge
+ if x >= width - self.Margins:
+ edge |= Qt.RightEdge
+ if y >= height - self.Margins:
+ edge |= Qt.BottomEdge
+
+ return edge
+
+ def _get_cursor(self, edges):
+ """调整鼠标样式
+ :param edges: int or None
+ :return: Qt.CursorShape
+ """
+ if edges == Qt.LeftEdge | Qt.TopEdge or edges == Qt.RightEdge | Qt.BottomEdge:
+ return Qt.SizeFDiagCursor
+ elif edges == Qt.RightEdge | Qt.TopEdge or edges == Qt.LeftEdge | Qt.BottomEdge:
+ return Qt.SizeBDiagCursor
+ elif edges == Qt.LeftEdge or edges == Qt.RightEdge:
+ return Qt.SizeHorCursor
+ elif edges == Qt.TopEdge or edges == Qt.BottomEdge:
+ return Qt.SizeVerCursor
+
+ return Qt.ArrowCursor
+
+ def is_titlebar(self, pos):
+ """判断是否是标题栏
+ :param pos: QPoint
+ :return: bool
+ """
+ return pos.y() <= self.TitleHeight
+
+ def moveOrResize(self, window, pos, width, height):
+ edges = self._get_edges(pos, width, height)
+ if edges:
+ if window.windowState() == Qt.WindowNoState:
+ window.startSystemResize(edges)
+ else:
+ if self.is_titlebar(pos):
+ window.startSystemMove()
+ # Fixed #172 主动触发一次鼠标释放事件,否则会导致鼠标悬停出问题
+ QApplication.instance().postEvent(
+ window,
+ QMouseEvent(QEvent.MouseButtonRelease, QPoint(-1, -1),
+ Qt.LeftButton, Qt.NoButton, Qt.NoModifier))
+
+ def eventFilter(self, obj, event):
+ if obj.isWindowType():
+ # top window 处理光标样式
+ if event.type() == QEvent.MouseMove and obj.windowState(
+ ) == Qt.WindowNoState:
+ obj.setCursor(
+ self._get_cursor(
+ self._get_edges(event.pos(), obj.width(),
+ obj.height())))
+ elif event.type() == QEvent.TouchUpdate:
+ self.moveOrResize(obj, event.pos(), obj.width(), obj.height())
+ elif obj in self.Widgets and isinstance(
+ event, QMouseEvent) and event.button() == Qt.LeftButton:
+ if event.type() == QEvent.MouseButtonDblClick:
+ # 双击最大化还原
+ if self.is_titlebar(event.pos()):
+ if obj.windowState() == Qt.WindowFullScreen:
+ pass
+ elif obj.windowState() == Qt.WindowMaximized:
+ obj.showNormal()
+ else:
+ obj.showMaximized()
+ elif event.type() == QEvent.MouseButtonPress:
+ self.moveOrResize(obj.windowHandle(), event.pos(), obj.width(),
+ obj.height())
+
+ return False
+
+
+class FramelessWindow(QWidget, Ui_FormFrameless):
+
+ def __init__(self, *args, **kwargs):
+ super(FramelessWindow, self).__init__(*args, **kwargs)
+ self.setupUi(self)
+ # 无边框
+ self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)
+ self.setAttribute(Qt.WA_TranslucentBackground, True)
+ self.setMouseTracking(True)
+ # 隐藏还原按钮
+ self.buttonNormal.setVisible(False)
+ # 标题栏按钮信号
+ self.buttonMinimum.clicked.connect(self.showMinimized)
+ self.buttonMaximum.clicked.connect(self.showMaximized)
+ self.buttonNormal.clicked.connect(self.showNormal)
+ self.buttonClose.clicked.connect(self.close)
+ self.setStyleSheet('#widgetTitleBar{background: rgb(232, 232, 232);}')
+
+ def showMinimized(self):
+ flags = self.windowFlags()
+ if sys.platform == 'darwin':
+ # fix mac 最小化失效问题
+ self.setWindowFlags((self.windowFlags() | Qt.CustomizeWindowHint) &
+ (~Qt.WindowTitleHint))
+ super(FramelessWindow, self).showMinimized()
+ if sys.platform == 'darwin':
+ # fix mac 最小化失效问题
+ self.setWindowFlags(flags)
+ self.show()
+
+ def changeEvent(self, event):
+ """窗口状态改变
+ :param event:
+ """
+ super(FramelessWindow, self).changeEvent(event)
+ # 窗口状态改变时修改标题栏控制按钮
+ visible = self.isMaximized()
+ self.buttonMaximum.setVisible(not visible)
+ self.buttonNormal.setVisible(visible)
+ if visible:
+ self.layout().setContentsMargins(0, 0, 0, 0)
+ else:
+ # TODO 与UI文件中的布局边距一致
+ m = FramelessObject.Margins
+ self.layout().setContentsMargins(m, m, m, m)
+
+ def paintEvent(self, event):
+ # 透明背景但是需要留下一个透明度用于鼠标捕获
+ painter = QPainter(self)
+ painter.fillRect(self.rect(), QColor(255, 255, 255, 1))
+
+
+if __name__ == '__main__':
+ import cgitb
+ import sys
+
+ cgitb.enable(format='text')
+
+ app = QApplication(sys.argv)
+ if not hasattr(QWindow, 'startSystemMove'):
+ QWindow.startSystemResize()
+ # 不支持
+ QMessageBox.critical(None, '错误', '当前Qt版本不支持该例子')
+ QTimer.singleShot(100, app.quit)
+ else:
+ # 安装全局事件过滤器
+ fo = FramelessObject()
+ app.installEventFilter(fo)
+
+ w1 = FramelessWindow()
+ fo.add_widget(w1)
+ w1.show()
+
+ w2 = FramelessWindow()
+ fo.add_widget(w2)
+ w2.show()
+ sys.exit(app.exec_())
diff --git a/Demo/Notification.py b/Demo/Notification.py
index f7a0ab15e777cb0221c987728af0cc0f13c99ad3..889c103fb3f32b19aaa52f0fbbf58be7106ddc7d 100644
--- a/Demo/Notification.py
+++ b/Demo/Notification.py
@@ -4,30 +4,30 @@
"""
Created on 2018年9月9日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: Notification
@description:
"""
import base64
-from PyQt5.QtCore import Qt, QRectF, QSize, pyqtSignal, QTimer
-from PyQt5.QtGui import QPixmap, QImage, QPainter, QPainterPath,\
- QColor
-from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout,\
- QGridLayout, QSpacerItem, QSizePolicy, QGraphicsDropShadowEffect,\
- QListWidget, QListWidgetItem
-
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import Qt, QRectF, QSize, pyqtSignal, QTimer
+ from PyQt5.QtGui import QPixmap, QImage, QPainter, QPainterPath, \
+ QColor
+ from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, \
+ QGridLayout, QSpacerItem, QSizePolicy, QGraphicsDropShadowEffect, \
+ QListWidget, QListWidgetItem, QApplication, QPushButton
+except ImportError:
+ from PySide2.QtCore import Qt, QRectF, QSize, Signal as pyqtSignal, QTimer
+ from PySide2.QtGui import QPixmap, QImage, QPainter, QPainterPath, \
+ QColor
+ from PySide2.QtWidgets import QWidget, QLabel, QHBoxLayout, \
+ QGridLayout, QSpacerItem, QSizePolicy, QGraphicsDropShadowEffect, \
+ QListWidget, QListWidgetItem, QApplication, QPushButton
class NotificationIcon:
-
Info, Success, Warning, Error, Close = range(5)
Types = {
Info: None,
@@ -39,10 +39,14 @@ class NotificationIcon:
@classmethod
def init(cls):
- cls.Types[cls.Info] = QPixmap(QImage.fromData(base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAC5ElEQVRYR8VX0VHbQBB9e/bkN3QQU0FMBSEVYFcQ8xPBJLJ1FWAqOMcaxogfTAWQCiAVRKkgTgfmM4zRZu6QhGzL0p0nDPr17e7bt7tv14RX/uiV48MJgAon+8TiAMRtMFogaqUJxADPwRRzg67kl8+xbWJWANR40iPQSSFgtX/mGQkaDr56V3VAKgGos4s2JXwJoF3naMPvMS+SrpTHs032GwGkdF+DsFMVnJm/oyGGeHico0EjIjpYes+YMyVd6R/flfkpBWCCQ9zaZM2LZDfLMGXsZ5kdI/lYBmINgHHyyLd1mWdBbAFAM/GY7K2WYx1AeB4T6L1N9umbGxZ0qktATaEAdCps48D39oq/LwEw3U5CN92LfczJoewfT7MAywDCaEbAuxeLrh0zz4L+0e4aAJfGy+sP3IMxlH1vpMJoSMCJDXgWtJeJVc6ACs9HBBrYODCJAFdYvAmkPJxnNqMwYht7Bn+T/lGg3z4DGEd3RPhQ54DBvwAOVkeqagRXfTLjh+x7+8sALOtfHLuiYzWOAiLoKbD58mnIGbCmLxUepS6NQmYlUGE0JeCTTXT9JvA9E9sZgO5iIpoyc6/YzcqSwQzgGgBXB7oXpH9klpRSkxY1xW/b7Iu2zk34PILPnazCqEPAtTWA8iZ0HsOu9L0bw4DzCJeNocMGNDpQ3IKO+6NUiJ4ysZNiBv5I3zPnmJmG5oM+wbS+9+qkvGi7NAXGmeUy0ioofa+XA0jH0UaMKpdRWs/adcwMqfV/tenqpqHY/Znt+j2gJi00RUzA201dXaxh9iZdZloJS+9H1otrkbRrD5InFqpPskxEshJQ468CkSmJC+i1HigaaxCAuCljgoDhwPdOjf7rFVxxuJrMkXScjtKc1rOLNpJk6nii5XmYzbngzlZn+RIb40kPJPTBYXUt6VEDJ8Pi6bWpNFb/jFYY6YGpDeKdjBmTKdMcxDGEmP73v2a2Gr/NOycGtglQZ/MPzEqCMLGckJEAAAAASUVORK5CYII=')))
- cls.Types[cls.Success] = QPixmap(QImage.fromData(base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACZUlEQVRYR8VXS3LTQBDtVsDbcAPMCbB3limkcAKSG4QFdnaYE2BOQLKzxSLJCeAGSUQheSnfwLmB2VJhXmpExpFHI2sk2RWv5FJPv9evP9NieuIfPzE+VSJw8qt3IMDvmahDoDYxt2UAACXMWIIowR5ffn8TJbaBWRE4CXvHAH9RgKXOgQUI48CfXZbZbiTw8Xe/w3d0zkydMkem91IZpyWOJu5sUXS+kEAqt3B+MNOLOuDqDEBLxxFHk7eza5MfIwEJDjhXTYD1s8zinYlEjsCD7FdNI9cJpEq0RFdPR47AMOzLCn69zegz6UgCP+pmfa8RSKudnPNdgCufTOLDxJtdPP7PoA1Cd8HEL5sSUCCD0B0x8bc1f8Bi6sevcgS2VXh6hMOwDz0gsUddNaxWKRjeuKfE/KlJ9Dq4UYH/o/Ns6scj+bgiMAjdayb26xLQwTfVEwg3gRcf6ARq578KuLo7VDc8psCQqwfjr4EfjYvkrAquFJ56UYpdSkAZSmNd1rrg0leOQFELgvA58OJTxVyRaAJORPOpF6UXnFUR5sDiXjs7UqsOMGMRlrWhTkJXpFL3mNrQZhA1lH3F0TiI5FurUQyMpn58VjhkSqQA4Tbw4nSVW6sBU5VXktXSeONlJH3s8jrOVr9RgVSFuNcWfzlh5n3LoKzMAPxxWuiULiQpiR2sZNnCyzIuWUr5Z1Ml0sgdHFZaShVDuR86/0huL3VXtDk/F4e11vKsTHLSCeKx7bYkW80hjLOrV1GhWH0ZrSlyh2MwdZhYfi8oZeYgLBmUiGd8sfVPM6syr2lUSYGaGBuP3QN6rVUwYV/egwAAAABJRU5ErkJggg==')))
- cls.Types[cls.Warning] = QPixmap(QImage.fromData(base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACmElEQVRYR8VXTW7TUBD+xjYSXZFukOIsSE9AskNJJMoJmq4r7OYEwAkabhBOkB/Emt4gVIojdpgbpIumEitX6gKB7UHPkauXxLHfc4F6Z3l+vvnmm/fGhAd+6IHzQwvA9cfOITMfAdQAcx1EdVEAM/tEFADsWyaPn57MfdXClABcT1qnzHSWJiwMzrwgoF91vXGRbS6AH59ajd8hDYmoURQo67tgxoij42rv62KX/04Agu44xmciVMokT32YERgGjquvZ1+y4mQCWPUa0/sk3vQlwqssEFsAVrQbU4XKL/ai2+5PPK6waQ4AOsoDnDARh83NdmwBuJq0fQI9L6p+L7rd3+/5gbAToMPI+FbkIzRRc72mbLcGIFE7jGFRIPHddmZrvstJh1X8CHGv6sxHqe1GkPYCoGcqgcoCAPPCdr2DLQC6wqMoPEj7qdqCNKllxs30sLpjYDluDUDGG5XqhY2sal3w4PiD7c7fJnHShMtJR8zpy/8CALiwndnhBgD1/t+XAXkaZAaUVHwnHulg0W6BNEWlAQD8zna8gQB0Ne70iXCm2j55jCUAei1gxvuaO+uXAcDg7zXHSy640iKUAehOEDJFqDmGQkiPLO5Fv+KADXOqvCuIsrPGsIyQdHou22YeRMJgOdHTQTkAfGk7XrLKrWlAvOhcRgBfWiZ3RQti0zxXuUFXCXMuo0TRitfxugjbIxC5RYzI6s9kIGFh+KLOpiW22id5AUuI8IaisFG4kCQg/sFKJgtPLix3KWXGeRETRbQDuCFCV2spTYMm+2FEI1WBbYIRPTeiqFtqLZeDraaD+qrbkpgQAvfl1WsXU0p/RjIjYYhTkNFgcCVlRlRKoAAc+5aF0V//NVPoc2kTLQZKZ8lx/AMXBmMwuXUwOAAAAABJRU5ErkJggg==')))
- cls.Types[cls.Error] = QPixmap(QImage.fromData(base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACrklEQVRYR82XW27aQBSG/4PtiNhIpStouoImKwjZAV1B07coWCpZQcgK6kh2lLeSFZSsIOwgdAdkBaUSEBQDpxpjU9vM+EJR03nDzJz/mzm3GcIrD3plfZQCeD47O1ho2jERNRmoE9AQG2BgBGBAwIiZe5Zh3JPjiG+5oxCAEF5q2iWITnMtRhOYu5XF4mr/9naYtSYXYGLbHQCXhYVTEwlom657rVqvBOB2uz71/a+ldq1SYe6ahnEhc4sSYGzbfQKOt915eh0D/ZrrnqS/SwEmrVYXRJ92Jb4OC+C65rrtuN0NgIltNwF837V4zN5Hy3V70e9NgFZrCKJ3CQDmJ9MwDsW36XzeB/AhA/CHqeuN2WxWX2paX2JraHneeynA+Pz8lCqVbxLjV5brimxAEJxqiEA8CjZVBvFy+bl2c9MV9hInoAw85qFpGEeRYQVEQjzMokcQHWxsiPne8jzh6j8AodGfyqNlHpiGcaKAkIk/gChwm2yYuv5W2FqfwLNtN5bAQ2bwySB83zENo50A8/1McaFRAU72XVek+mpk+D/JlIKI/xkee654uCbIhjVAqZIrgSgpLhiCwN4OAEj4vEB2yDybBCjsAol4ZD0nRdMQSRcUCsKUeNSw4o2mKMRGEOamoVx8FXDZKVosDYNMUHXAsBRnppo8RQcbpTgIGEkhykpFjnWxzGhPQYxt2yHgS/oIlKVYTJxImpG482nz+VG1Wh1N84pMCCGa0ULXHwmoJwCYnyzPW5fn/68dh7EgPbrMMl3gz7gro+n/7EoWD7w4a96l1NnJ1Yz5Lt6wCgFEk0r1CIkbiPnC9DxH5aHcd4FYGD5MOqVOg/muslh0/vphkm63k5eXZvA0I6qD+ZCI3jDzLxANiHn1NNvb6+30aVYgwLeeUsgFW1svsPA3Ncq4MHzVeO8AAAAASUVORK5CYII=')))
+ cls.Types[cls.Info] = QPixmap(QImage.fromData(base64.b64decode(
+ 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAC5ElEQVRYR8VX0VHbQBB9e/bkN3QQU0FMBSEVYFcQ8xPBJLJ1FWAqOMcaxogfTAWQCiAVRKkgTgfmM4zRZu6QhGzL0p0nDPr17e7bt7tv14RX/uiV48MJgAon+8TiAMRtMFogaqUJxADPwRRzg67kl8+xbWJWANR40iPQSSFgtX/mGQkaDr56V3VAKgGos4s2JXwJoF3naMPvMS+SrpTHs032GwGkdF+DsFMVnJm/oyGGeHico0EjIjpYes+YMyVd6R/flfkpBWCCQ9zaZM2LZDfLMGXsZ5kdI/lYBmINgHHyyLd1mWdBbAFAM/GY7K2WYx1AeB4T6L1N9umbGxZ0qktATaEAdCps48D39oq/LwEw3U5CN92LfczJoewfT7MAywDCaEbAuxeLrh0zz4L+0e4aAJfGy+sP3IMxlH1vpMJoSMCJDXgWtJeJVc6ACs9HBBrYODCJAFdYvAmkPJxnNqMwYht7Bn+T/lGg3z4DGEd3RPhQ54DBvwAOVkeqagRXfTLjh+x7+8sALOtfHLuiYzWOAiLoKbD58mnIGbCmLxUepS6NQmYlUGE0JeCTTXT9JvA9E9sZgO5iIpoyc6/YzcqSwQzgGgBXB7oXpH9klpRSkxY1xW/b7Iu2zk34PILPnazCqEPAtTWA8iZ0HsOu9L0bw4DzCJeNocMGNDpQ3IKO+6NUiJ4ysZNiBv5I3zPnmJmG5oM+wbS+9+qkvGi7NAXGmeUy0ioofa+XA0jH0UaMKpdRWs/adcwMqfV/tenqpqHY/Znt+j2gJi00RUzA201dXaxh9iZdZloJS+9H1otrkbRrD5InFqpPskxEshJQ468CkSmJC+i1HigaaxCAuCljgoDhwPdOjf7rFVxxuJrMkXScjtKc1rOLNpJk6nii5XmYzbngzlZn+RIb40kPJPTBYXUt6VEDJ8Pi6bWpNFb/jFYY6YGpDeKdjBmTKdMcxDGEmP73v2a2Gr/NOycGtglQZ/MPzEqCMLGckJEAAAAASUVORK5CYII=')))
+ cls.Types[cls.Success] = QPixmap(QImage.fromData(base64.b64decode(
+ 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACZUlEQVRYR8VXS3LTQBDtVsDbcAPMCbB3limkcAKSG4QFdnaYE2BOQLKzxSLJCeAGSUQheSnfwLmB2VJhXmpExpFHI2sk2RWv5FJPv9evP9NieuIfPzE+VSJw8qt3IMDvmahDoDYxt2UAACXMWIIowR5ffn8TJbaBWRE4CXvHAH9RgKXOgQUI48CfXZbZbiTw8Xe/w3d0zkydMkem91IZpyWOJu5sUXS+kEAqt3B+MNOLOuDqDEBLxxFHk7eza5MfIwEJDjhXTYD1s8zinYlEjsCD7FdNI9cJpEq0RFdPR47AMOzLCn69zegz6UgCP+pmfa8RSKudnPNdgCufTOLDxJtdPP7PoA1Cd8HEL5sSUCCD0B0x8bc1f8Bi6sevcgS2VXh6hMOwDz0gsUddNaxWKRjeuKfE/KlJ9Dq4UYH/o/Ns6scj+bgiMAjdayb26xLQwTfVEwg3gRcf6ARq578KuLo7VDc8psCQqwfjr4EfjYvkrAquFJ56UYpdSkAZSmNd1rrg0leOQFELgvA58OJTxVyRaAJORPOpF6UXnFUR5sDiXjs7UqsOMGMRlrWhTkJXpFL3mNrQZhA1lH3F0TiI5FurUQyMpn58VjhkSqQA4Tbw4nSVW6sBU5VXktXSeONlJH3s8jrOVr9RgVSFuNcWfzlh5n3LoKzMAPxxWuiULiQpiR2sZNnCyzIuWUr5Z1Ml0sgdHFZaShVDuR86/0huL3VXtDk/F4e11vKsTHLSCeKx7bYkW80hjLOrV1GhWH0ZrSlyh2MwdZhYfi8oZeYgLBmUiGd8sfVPM6syr2lUSYGaGBuP3QN6rVUwYV/egwAAAABJRU5ErkJggg==')))
+ cls.Types[cls.Warning] = QPixmap(QImage.fromData(base64.b64decode(
+ 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACmElEQVRYR8VXTW7TUBD+xjYSXZFukOIsSE9AskNJJMoJmq4r7OYEwAkabhBOkB/Emt4gVIojdpgbpIumEitX6gKB7UHPkauXxLHfc4F6Z3l+vvnmm/fGhAd+6IHzQwvA9cfOITMfAdQAcx1EdVEAM/tEFADsWyaPn57MfdXClABcT1qnzHSWJiwMzrwgoF91vXGRbS6AH59ajd8hDYmoURQo67tgxoij42rv62KX/04Agu44xmciVMokT32YERgGjquvZ1+y4mQCWPUa0/sk3vQlwqssEFsAVrQbU4XKL/ai2+5PPK6waQ4AOsoDnDARh83NdmwBuJq0fQI9L6p+L7rd3+/5gbAToMPI+FbkIzRRc72mbLcGIFE7jGFRIPHddmZrvstJh1X8CHGv6sxHqe1GkPYCoGcqgcoCAPPCdr2DLQC6wqMoPEj7qdqCNKllxs30sLpjYDluDUDGG5XqhY2sal3w4PiD7c7fJnHShMtJR8zpy/8CALiwndnhBgD1/t+XAXkaZAaUVHwnHulg0W6BNEWlAQD8zna8gQB0Ne70iXCm2j55jCUAei1gxvuaO+uXAcDg7zXHSy640iKUAehOEDJFqDmGQkiPLO5Fv+KADXOqvCuIsrPGsIyQdHou22YeRMJgOdHTQTkAfGk7XrLKrWlAvOhcRgBfWiZ3RQti0zxXuUFXCXMuo0TRitfxugjbIxC5RYzI6s9kIGFh+KLOpiW22id5AUuI8IaisFG4kCQg/sFKJgtPLix3KWXGeRETRbQDuCFCV2spTYMm+2FEI1WBbYIRPTeiqFtqLZeDraaD+qrbkpgQAvfl1WsXU0p/RjIjYYhTkNFgcCVlRlRKoAAc+5aF0V//NVPoc2kTLQZKZ8lx/AMXBmMwuXUwOAAAAABJRU5ErkJggg==')))
+ cls.Types[cls.Error] = QPixmap(QImage.fromData(base64.b64decode(
+ 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACrklEQVRYR82XW27aQBSG/4PtiNhIpStouoImKwjZAV1B07coWCpZQcgK6kh2lLeSFZSsIOwgdAdkBaUSEBQDpxpjU9vM+EJR03nDzJz/mzm3GcIrD3plfZQCeD47O1ho2jERNRmoE9AQG2BgBGBAwIiZe5Zh3JPjiG+5oxCAEF5q2iWITnMtRhOYu5XF4mr/9naYtSYXYGLbHQCXhYVTEwlom657rVqvBOB2uz71/a+ldq1SYe6ahnEhc4sSYGzbfQKOt915eh0D/ZrrnqS/SwEmrVYXRJ92Jb4OC+C65rrtuN0NgIltNwF837V4zN5Hy3V70e9NgFZrCKJ3CQDmJ9MwDsW36XzeB/AhA/CHqeuN2WxWX2paX2JraHneeynA+Pz8lCqVbxLjV5brimxAEJxqiEA8CjZVBvFy+bl2c9MV9hInoAw85qFpGEeRYQVEQjzMokcQHWxsiPne8jzh6j8AodGfyqNlHpiGcaKAkIk/gChwm2yYuv5W2FqfwLNtN5bAQ2bwySB83zENo50A8/1McaFRAU72XVek+mpk+D/JlIKI/xkee654uCbIhjVAqZIrgSgpLhiCwN4OAEj4vEB2yDybBCjsAol4ZD0nRdMQSRcUCsKUeNSw4o2mKMRGEOamoVx8FXDZKVosDYNMUHXAsBRnppo8RQcbpTgIGEkhykpFjnWxzGhPQYxt2yHgS/oIlKVYTJxImpG482nz+VG1Wh1N84pMCCGa0ULXHwmoJwCYnyzPW5fn/68dh7EgPbrMMl3gz7gro+n/7EoWD7w4a96l1NnJ1Yz5Lt6wCgFEk0r1CIkbiPnC9DxH5aHcd4FYGD5MOqVOg/muslh0/vphkm63k5eXZvA0I6qD+ZCI3jDzLxANiHn1NNvb6+30aVYgwLeeUsgFW1svsPA3Ncq4MHzVeO8AAAAASUVORK5CYII=')))
cls.Types[cls.Close] = QPixmap(QImage.fromData(base64.b64decode(
'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAeElEQVQ4T2NkoBAwUqifgboGzJy76AIjE3NCWmL0BWwumzV/qcH/f38XpCfHGcDkUVwAUsDw9+8GBmbmAHRDcMlheAGbQnwGYw0DZA1gp+JwFUgKZyDCDQGpwuIlrGGAHHAUGUCRFygKRIqjkeKERE6+oG5eIMcFAOqSchGwiKKAAAAAAElFTkSuQmCC')))
@@ -52,7 +56,6 @@ class NotificationIcon:
class NotificationItem(QWidget):
-
closed = pyqtSignal(QListWidgetItem)
def __init__(self, title, message, item, *args, ntype=0, callback=None, **kwargs):
@@ -152,7 +155,6 @@ class NotificationItem(QWidget):
class NotificationWindow(QListWidget):
-
_instance = None
def __init__(self, *args, **kwargs):
@@ -243,15 +245,18 @@ class NotificationWindow(QListWidget):
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.Hook(1, None, 5, sys.stderr, 'text')
- from PyQt5.QtWidgets import QApplication, QPushButton
+
+ cgitb.enable(format='text')
+
app = QApplication(sys.argv)
w = QWidget()
layout = QHBoxLayout(w)
+
def callback():
print('回调点击')
+
layout.addWidget(QPushButton(
'Info', w, clicked=lambda: NotificationWindow.info('提示', '这是一条会自动关闭的消息', callback=callback)))
layout.addWidget(QPushButton(
@@ -268,9 +273,9 @@ if __name__ == '__main__':
callback=callback)))
w.show()
-# NotificationIcon.init()
-# ww = NotificationItem('提示', '这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案
', None,
-# ntype=NotificationIcon.Error)
-# ww.bgWidget.setVisible(True)
-# ww.show()
+ # NotificationIcon.init()
+ # ww = NotificationItem('提示', '这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案
', None,
+ # ntype=NotificationIcon.Error)
+ # ww.bgWidget.setVisible(True)
+ # ww.show()
sys.exit(app.exec_())
diff --git a/Demo/ProbeWindow.py b/Demo/ProbeWindow.py
index 7bc4b0b08aa2a63c4803d13cc429c84c07438fa8..e023359cfe423d2dd38d62c1933aa2e397fe8f32 100644
--- a/Demo/ProbeWindow.py
+++ b/Demo/ProbeWindow.py
@@ -3,23 +3,22 @@
"""
Created on 2018年6月8日
author: Irony
-site: https://pyqt5.com , https://github.com/892768447
+site: https://pyqt.site , https://github.com/PyQt5
email: 892768447@qq.com
file: ProbeWindow
description: 简单探测窗口和放大截图
"""
-from PyQt5.QtCore import Qt, QRect
-from PyQt5.QtGui import QPainter, QPen, QCursor, QColor
-from PyQt5.QtWidgets import QLabel, QWidget, QApplication
import win32gui
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import Qt, QRect
+ from PyQt5.QtGui import QPainter, QPen, QCursor, QColor
+ from PyQt5.QtWidgets import QLabel, QWidget, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt, QRect
+ from PySide2.QtGui import QPainter, QPen, QCursor, QColor
+ from PySide2.QtWidgets import QLabel, QWidget, QApplication
class FrameWidget(QWidget):
@@ -111,11 +110,12 @@ class Label(QLabel):
painter.setPen(Qt.white)
painter.drawText(self.rect(), Qt.AlignLeft |
Qt.AlignBottom, '({}, {})\nRGB: ({}, {}, {})\n{}'.format(
- pos.x(), pos.y(), r, g, b, QColor(r, g, b).name()))
+ pos.x(), pos.y(), r, g, b, QColor(r, g, b).name()))
if __name__ == '__main__':
import sys
+
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(True)
w = Label()
diff --git a/Demo/QtThreading.py b/Demo/QtThreading.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0533850879b848e702eb1dcec465409e738db93
--- /dev/null
+++ b/Demo/QtThreading.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年3月8日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: Threading.QtThreading
+@description:
+"""
+from threading import Thread
+from time import sleep
+
+try:
+ from PyQt5.QtCore import QObject, pyqtSignal, QTimer, Qt
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QProgressBar, QApplication
+except ImportError:
+ from PySide2.QtCore import QObject, Signal as pyqtSignal, QTimer, Qt
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QProgressBar, QApplication
+
+
+class _Signals(QObject):
+ updateProgress = pyqtSignal(int)
+
+
+Signals = _Signals()
+
+
+class UpdateThread(Thread):
+
+ def run(self):
+ self.i = 0
+ for i in range(101):
+ self.i += 1
+ Signals.updateProgress.emit(i)
+ sleep(1)
+ self.i = 0
+ Signals.updateProgress.emit(i)
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 400)
+ layout = QVBoxLayout(self)
+ self.progressBar = QProgressBar(self)
+ layout.addWidget(self.progressBar)
+ Signals.updateProgress.connect(
+ self.progressBar.setValue, type=Qt.QueuedConnection)
+
+ QTimer.singleShot(2000, self.doStart)
+
+ def doStart(self):
+ self.updateThread = UpdateThread(daemon=True)
+ self.updateThread.start()
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/Demo/README.md b/Demo/README.md
index 9f3afa955b7868dc7080f3f72f33904bc3051532..175dd969a444f3f1cff8c58bcb8600ed3b1ca10d 100644
--- a/Demo/README.md
+++ b/Demo/README.md
@@ -1,5 +1,31 @@
# Demo
+- 目录
+ - [重启窗口Widget](#1重启窗口Widget)
+ - [简单的窗口贴边隐藏](#2简单的窗口贴边隐藏)
+ - [嵌入外部窗口](#3嵌入外部窗口)
+ - [简单跟随其它窗口](#4简单跟随其它窗口)
+ - [简单探测窗口和放大截图](#5简单探测窗口和放大截图)
+ - [无边框自定义标题栏窗口](#6无边框自定义标题栏窗口)
+ - [右下角弹出框](#7右下角弹出框)
+ - [程序重启](#8程序重启)
+ - [自定义属性](#9自定义属性)
+ - [调用截图DLL](#10调用截图DLL)
+ - [单实例应用](#11单实例应用)
+ - [简单的右下角气泡提示](#12简单的右下角气泡提示)
+ - [右侧消息通知栏](#13右侧消息通知栏)
+ - [验证码控件](#14验证码控件)
+ - [人脸特征点](#15人脸特征点)
+ - [使用Threading](#16使用Threading)
+ - [背景连线动画](#17背景连线动画)
+ - [无边框圆角对话框](#18无边框圆角对话框)
+ - [调整窗口显示边框](#19调整窗口显示边框)
+ - [判断信号是否连接](#20判断信号是否连接)
+ - [调用虚拟键盘](#21调用虚拟键盘)
+ - [动态忙碌光标](#22动态忙碌光标)
+ - [屏幕变动监听](#23屏幕变动监听)
+ - [无边框窗口](#24无边框窗口)
+
## 1、重启窗口Widget
[运行 RestartWindow.py](RestartWindow.py)
@@ -74,14 +100,14 @@

## 7、右下角弹出框
-[运行 WindowNotify.py](WindowNotify.py)
+[运行 WindowNotify.py](WindowNotify.py) | [查看 notify.ui](Data/notify.ui)

## 8、程序重启
[运行 AutoRestart.py](AutoRestart.py)
-
+
## 9、自定义属性
[运行 CustomProperties.py](CustomProperties.py)
@@ -113,7 +139,7 @@

-## 14、验证码
+## 14、验证码控件
[运行 VerificationCode.py](VerificationCode.py)
1. 更新为paintEvent方式,采用上下跳动
@@ -138,4 +164,81 @@ PyQt 结合 Opencv 进行人脸检测;
3. [dlib-19.4.0.win32-py3.5.exe](Data/dlib-19.4.0.win32-py3.5.exe)
4. [shape-predictor-68-face-landmarks.dat.bz2](http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2)
-
\ No newline at end of file
+
+
+## 16、使用Threading
+[运行 QtThreading.py](QtThreading.py)
+
+在PyQt中使用Theading线程
+
+
+
+## 17、背景连线动画
+[运行 CircleLine.py](CircleLine.py)
+
+主要参考 [背景连线动画.html](Data/背景连线动画.html)
+
+
+
+
+## 18、无边框圆角对话框
+[运行 FramelessDialog.py](FramelessDialog.py)
+
+1. 通过设置 `self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)` 和 `self.setAttribute(Qt.WA_TranslucentBackground, True)` 达到无边框和背景透明
+2. 在`QDialog`中放置一个`QWidget`作为背景和圆角
+3. 在`QWidget`中放置其他内容
+
+
+
+
+## 19、调整窗口显示边框
+[运行 ShowFrameWhenDrag.py](ShowFrameWhenDrag.py)
+
+1. 全局设置是【】在控制面板中->调整Windows的外观和性能->去掉勾选 拖动时显示窗口内容】
+2. 但是为了不影响其它应用,可以在窗口处理函数wndproc中对其进行判断处理
+3. 必须先要替换wndproc为自己的函数
+4. 当消息事件==WM_NCLBUTTONDOWN的时候, 先强制开启,然后处理完成后再还原
+
+好处在于可以减少窗口更新的次数(用途有频繁渲染的界面)
+
+
+
+## 20、判断信号是否连接
+[运行 IsSignalConnected.py](IsSignalConnected.py)
+
+1. 通过 `isSignalConnected` 判断是否连接
+2. 通过对象的 `receivers` 获取连接的数量来判断
+
+
+
+## 21、调用虚拟键盘
+[运行 CallVirtualKeyboard.py](CallVirtualKeyboard.py)
+
+1. Windows上调用的是`osk.exe`
+2. Linux上调用的是`florence`,`onboard`,`kvkbd`,这三种屏幕键盘需要自行安装
+
+
+
+
+## 22、动态忙碌光标
+[运行 GifCursor.py](GifCursor.py)
+
+通过定时器不停的修改光标图片来实现动态效果
+
+
+
+## 23、屏幕变动监听
+[运行 ScreenNotify.py](ScreenNotify.py)
+
+通过定时器减少不同的变化信号,尽量保证只调用一次槽函数来获取信息
+
+
+
+## 24、无边框窗口
+[运行 NewFramelessWindow.py](NewFramelessWindow.py)
+
+1. 该方法只针对 `Qt5.15` 以上版本有效
+2. 通过事件过滤器判断边缘设置鼠标样式
+3. 处理点击事件交通过 `QWindow.startSystemMove` 和 `QWindow.startSystemResize` 传递给系统处理
+
+
\ No newline at end of file
diff --git a/Demo/RestartWindow.py b/Demo/RestartWindow.py
index 9eec22425fd5604d99316fb93feaeb9ed6b6bb31..bd9709b31322afe0140c84ad0c28d27d74e0940a 100644
--- a/Demo/RestartWindow.py
+++ b/Demo/RestartWindow.py
@@ -1,26 +1,26 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月17日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: RestartWindow
@description: 窗口重启
-'''
-from PyQt5.QtCore import pyqtSignal
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit,\
- QMessageBox
+"""
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import pyqtSignal
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit, \
+ QMessageBox, QApplication
+except ImportError:
+ from PySide2.QtCore import Signal as pyqtSignal
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit, \
+ QMessageBox, QApplication
class RestartWindow(QWidget):
-
restarted = pyqtSignal(QWidget, str)
_Self = None # 很重要,保留窗口引用
@@ -55,7 +55,7 @@ class RestartWindow(QWidget):
if __name__ == "__main__":
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = RestartWindow("test")
w.show()
diff --git a/Demo/ScreenNotify.py b/Demo/ScreenNotify.py
new file mode 100644
index 0000000000000000000000000000000000000000..f2c9f9c58f9b3319473abc2e9e2abc96fbdb017c
--- /dev/null
+++ b/Demo/ScreenNotify.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2021/4/13
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ScreenNotify
+@description: 屏幕、分辨率、DPI变化通知
+"""
+import sys
+
+try:
+ from PyQt5.QtCore import QTimer, QRect
+ from PyQt5.QtWidgets import QApplication, QPlainTextEdit
+except ImportError:
+ from PySide2.QtCore import QTimer, QRect
+ from PySide2.QtWidgets import QApplication, QPlainTextEdit
+
+
+class Window(QPlainTextEdit):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.appendPlainText('修改分辨率后查看')
+ # 记录最后一次的值(减少槽调用)
+ self.m_rect = QRect()
+ # 使用定时器来延迟触发最后一次变化
+ self.m_timer = QTimer(self, timeout=self.onSolutionChanged)
+ self.m_timer.setSingleShot(True) # **重要** 保证多次信号尽量少的调用函数
+
+ # 主要是多屏幕->无屏幕->有屏幕
+ QApplication.instance().primaryScreenChanged.connect(lambda _: self.m_timer.start(1000))
+ # 其它信号最终基本上都会调用该信号
+ QApplication.instance().primaryScreen().virtualGeometryChanged.connect(
+ lambda _: self.m_timer.start(1000))
+ # DPI变化
+ QApplication.instance().primaryScreen().logicalDotsPerInchChanged.connect(
+ lambda _: self.m_timer.start(1000))
+
+ def onSolutionChanged(self):
+ # 获取主屏幕
+ screen = QApplication.instance().primaryScreen()
+ if self.m_rect == screen.availableVirtualGeometry():
+ return
+ self.m_rect = screen.availableVirtualGeometry()
+ # 所有屏幕可用大小
+ self.appendPlainText('\navailableVirtualGeometry: {0}'.format(str(screen.availableVirtualGeometry())))
+ # 获取所有屏幕
+ screens = QApplication.instance().screens()
+ for screen in screens:
+ self.appendPlainText(
+ 'screen: {0}, geometry({1}), availableGeometry({2}), logicalDotsPerInch({3}), '
+ 'physicalDotsPerInch({4}), refreshRate({5})'.format(
+ screen.name(), screen.geometry(), screen.availableGeometry(), screen.logicalDotsPerInch(),
+ screen.physicalDotsPerInch(), screen.refreshRate()))
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/Demo/ScreenShot/AutoRestart.gif b/Demo/ScreenShot/AutoRestart.gif
new file mode 100644
index 0000000000000000000000000000000000000000..ba12918d7bb4bdbfc196c534fc78879f3cdab4d4
Binary files /dev/null and b/Demo/ScreenShot/AutoRestart.gif differ
diff --git a/Demo/ScreenShot/AutoRestart.png b/Demo/ScreenShot/AutoRestart.png
deleted file mode 100644
index 18667c7d37fb21094c8e3b3f765b089ac7a8f4c4..0000000000000000000000000000000000000000
Binary files a/Demo/ScreenShot/AutoRestart.png and /dev/null differ
diff --git a/Demo/ScreenShot/CallVirtualKeyboard1.png b/Demo/ScreenShot/CallVirtualKeyboard1.png
new file mode 100644
index 0000000000000000000000000000000000000000..90bb7bf04b0dc31a1855d4a8dc131bb6424c5969
Binary files /dev/null and b/Demo/ScreenShot/CallVirtualKeyboard1.png differ
diff --git a/Demo/ScreenShot/CallVirtualKeyboard2.png b/Demo/ScreenShot/CallVirtualKeyboard2.png
new file mode 100644
index 0000000000000000000000000000000000000000..b3556cf1930dddeb35bee628965cddb07449c63e
Binary files /dev/null and b/Demo/ScreenShot/CallVirtualKeyboard2.png differ
diff --git a/Demo/ScreenShot/CircleLine.gif b/Demo/ScreenShot/CircleLine.gif
new file mode 100644
index 0000000000000000000000000000000000000000..46729734ac6786ec626d850bfa11091fada3e388
Binary files /dev/null and b/Demo/ScreenShot/CircleLine.gif differ
diff --git a/Demo/ScreenShot/FramelessDialog.png b/Demo/ScreenShot/FramelessDialog.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5d24a3c1ed7e6eb0f4f2a6567dd78292935c2ac
Binary files /dev/null and b/Demo/ScreenShot/FramelessDialog.png differ
diff --git a/Demo/ScreenShot/FramelessDialog1.png b/Demo/ScreenShot/FramelessDialog1.png
new file mode 100644
index 0000000000000000000000000000000000000000..7f89494a24055aff7f03c20435d207a6e85dcd5d
Binary files /dev/null and b/Demo/ScreenShot/FramelessDialog1.png differ
diff --git a/Demo/ScreenShot/GifCursor.gif b/Demo/ScreenShot/GifCursor.gif
new file mode 100644
index 0000000000000000000000000000000000000000..ed42f619d02d76e6598d28feffb12df802179f36
Binary files /dev/null and b/Demo/ScreenShot/GifCursor.gif differ
diff --git a/Demo/ScreenShot/IsSignalConnected.png b/Demo/ScreenShot/IsSignalConnected.png
new file mode 100644
index 0000000000000000000000000000000000000000..829976f96ba2101097f1cf028fb9d190c5bcc1e2
Binary files /dev/null and b/Demo/ScreenShot/IsSignalConnected.png differ
diff --git a/Demo/ScreenShot/NewFramelessWindow.gif b/Demo/ScreenShot/NewFramelessWindow.gif
new file mode 100644
index 0000000000000000000000000000000000000000..88f4333d8cd458dedbc0b3e45808aa50e90eacdc
Binary files /dev/null and b/Demo/ScreenShot/NewFramelessWindow.gif differ
diff --git a/Demo/ScreenShot/QtThreading.gif b/Demo/ScreenShot/QtThreading.gif
new file mode 100644
index 0000000000000000000000000000000000000000..c832a19da62c58dd23494525b0fddd5770c8194e
Binary files /dev/null and b/Demo/ScreenShot/QtThreading.gif differ
diff --git a/Demo/ScreenShot/ScreenNotify.png b/Demo/ScreenShot/ScreenNotify.png
new file mode 100644
index 0000000000000000000000000000000000000000..e95330cca5fbf6d60fd5e77cc7cf60b4e7d975da
Binary files /dev/null and b/Demo/ScreenShot/ScreenNotify.png differ
diff --git a/Demo/ScreenShot/ShowFrameWhenDrag.gif b/Demo/ScreenShot/ShowFrameWhenDrag.gif
new file mode 100644
index 0000000000000000000000000000000000000000..b06c3e4fa3509c77ec9d4371120709814deea434
Binary files /dev/null and b/Demo/ScreenShot/ShowFrameWhenDrag.gif differ
diff --git a/Demo/ScreenShotDll.py b/Demo/ScreenShotDll.py
index 964df60e2ddfb9dad74bef200ba1984e4bf8b64f..0def9e7cb2e1773240866bb12d381496a47f32ff 100644
--- a/Demo/ScreenShotDll.py
+++ b/Demo/ScreenShotDll.py
@@ -1,3 +1,4 @@
from ctypes import CDLL
+
dll = CDLL('Data/ScreenShot.dll')
dll.PrScrn()
diff --git a/Demo/SharedMemory.py b/Demo/SharedMemory.py
index 0ec5919a566ca85fa226d2d14fe9232c5ce0dc6e..f654ad1e8f05f78cd16f55d945cf53fd64ca2e0c 100644
--- a/Demo/SharedMemory.py
+++ b/Demo/SharedMemory.py
@@ -1,28 +1,29 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年3月30日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: TestQSharedMemory
@description:
-'''
-from PyQt5.QtWidgets import QWidget
+"""
+from PyQt5.QtWidgets import QWidget
from Lib.Application import SharedApplication # @UnresolvedImport
-__version__ = "0.0.1"
class Widget(QWidget):
-
- def __init__(self,*args,**kwargs):
- super(Widget, self).__init__(*args,**kwargs)
+
+ def __init__(self, *args, **kwargs):
+ super(Widget, self).__init__(*args, **kwargs)
+
if __name__ == "__main__":
- import sys,os
+ import sys, os
+
print(os.getpid())
app = SharedApplication(sys.argv)
if app.isRunning():
@@ -30,4 +31,4 @@ if __name__ == "__main__":
sys.exit(0)
w = Widget()
w.show()
- sys.exit(app.exec_())
\ No newline at end of file
+ sys.exit(app.exec_())
diff --git a/Demo/ShowFrameWhenDrag.py b/Demo/ShowFrameWhenDrag.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f0993fb46848c237da7fa7fd9dcbbf3ea7ca0ed
--- /dev/null
+++ b/Demo/ShowFrameWhenDrag.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年4月23日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ShowFrameWhenDrag
+@description: 调整窗口显示边框
+"""
+from ctypes import sizeof, windll, c_int, byref, c_long, c_void_p, c_ulong, c_longlong, \
+ c_ulonglong, WINFUNCTYPE, c_uint
+
+try:
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QApplication
+except ImportError:
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QLabel, QApplication
+
+if sizeof(c_long) == sizeof(c_void_p):
+ WPARAM = c_ulong
+ LPARAM = c_long
+elif sizeof(c_longlong) == sizeof(c_void_p):
+ WPARAM = c_ulonglong
+ LPARAM = c_longlong
+
+WM_NCLBUTTONDOWN = 0x00a1
+GWL_WNDPROC = -4
+SPI_GETDRAGFULLWINDOWS = 38
+SPI_SETDRAGFULLWINDOWS = 37
+WNDPROC = WINFUNCTYPE(c_long, c_void_p, c_uint, WPARAM, LPARAM)
+
+try:
+ CallWindowProc = windll.user32.CallWindowProcW
+ SetWindowLong = windll.user32.SetWindowLongW
+ SystemParametersInfo = windll.user32.SystemParametersInfoW
+except:
+ CallWindowProc = windll.user32.CallWindowProcA
+ SetWindowLong = windll.user32.SetWindowLongA
+ SystemParametersInfo = windll.user32.SystemParametersInfoA
+
+
+def GetDragFullwindows():
+ rv = c_int()
+ SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, byref(rv), 0)
+ return rv.value
+
+
+def SetDragFullwindows(value):
+ SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, value, 0, 0)
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+ layout.addWidget(QLabel('拖动或者调整窗口试试看'))
+
+ # 重点替换窗口处理过程
+ self._newwndproc = WNDPROC(self._wndproc)
+ self._oldwndproc = SetWindowLong(
+ int(self.winId()), GWL_WNDPROC, self._newwndproc)
+
+ def _wndproc(self, hwnd, msg, wparam, lparam):
+ if msg == WM_NCLBUTTONDOWN:
+ # 获取系统本身是否已经开启
+ isDragFullWindow = GetDragFullwindows()
+ if isDragFullWindow != 0:
+ # 开启虚线框
+ SetDragFullwindows(0)
+ # 系统本身处理
+ ret = CallWindowProc(
+ self._oldwndproc, hwnd, msg, wparam, lparam)
+ # 关闭虚线框
+ SetDragFullwindows(1)
+ return ret
+ return CallWindowProc(self._oldwndproc, hwnd, msg, wparam, lparam)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/Demo/SingleApplication.py b/Demo/SingleApplication.py
index d452e1670402083e0fe6bdbe9693c6d126b13ccc..1f5f74ee90ef2e4a3633fc226cab25f751d218e5 100644
--- a/Demo/SingleApplication.py
+++ b/Demo/SingleApplication.py
@@ -1,28 +1,29 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年3月30日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: TestQSingleApplication
@description:
-'''
+"""
+
from PyQt5.QtWidgets import QTextEdit
from Lib.Application import QSingleApplication # @UnresolvedImport
-__version__ = "0.0.1"
-
class Widget(QTextEdit):
-
+
def __init__(self, *args, **kwargs):
super(Widget, self).__init__(*args, **kwargs)
+
if __name__ == "__main__":
import sys
+
app = QSingleApplication(sys.argv)
if app.isRunning():
app.sendMessage("app is running")
diff --git a/Demo/VerificationCode.py b/Demo/VerificationCode.py
index df25ba7f97a97819702df406f7063a9aa8b21ff2..9490d8eff429f9104216af99d5dd724773a3efee 100644
--- a/Demo/VerificationCode.py
+++ b/Demo/VerificationCode.py
@@ -1,23 +1,25 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年4月5日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: widgets.WidgetCode
@description:
-'''
-from random import sample
+"""
import string
+from random import sample
-from PyQt5.QtCore import Qt, qrand, QPointF, QPoint, QBasicTimer
-from PyQt5.QtGui import QPainter, QBrush, QPen, QPalette, QFontMetrics
-from PyQt5.QtWidgets import QLabel
-
-
-__version__ = "0.0.1"
+try:
+ from PyQt5.QtCore import Qt, qrand, QPointF, QPoint, QBasicTimer
+ from PyQt5.QtGui import QPainter, QBrush, QPen, QPalette, QFontMetrics, QFontDatabase
+ from PyQt5.QtWidgets import QLabel, QApplication, QWidget, QHBoxLayout, QLineEdit
+except ImportError:
+ from PySide2.QtCore import Qt, qrand, QPointF, QPoint, QBasicTimer
+ from PySide2.QtGui import QPainter, QBrush, QPen, QPalette, QFontMetrics, QFontDatabase
+ from PySide2.QtWidgets import QLabel, QApplication, QWidget, QHBoxLayout, QLineEdit
DEF_NOISYPOINTCOUNT = 60 # 噪点数量
COLORLIST = ("black", "gray", "red", "green", "blue", "magenta")
@@ -28,8 +30,9 @@ FONT = "{word} "
WORDS = list(string.ascii_letters + string.digits)
SINETABLE = (0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38)
+
class WidgetCode(QLabel):
-
+
def __init__(self, *args, **kwargs):
super(WidgetCode, self).__init__(*args, **kwargs)
self._sensitive = False # 是否大小写敏感
@@ -47,33 +50,33 @@ class WidgetCode(QLabel):
self.step = 0
self.timer = QBasicTimer()
self.timer.start(60, self)
-
+
def reset(self):
self._code = "".join(sample(WORDS, 4)) # 随机4个字符
self.setText(self._code)
-
+
def check(self, code):
return self._code == str(code) if self._sensitive else self._code.lower() == str(code).lower()
-
+
def setSensitive(self, sensitive):
self._sensitive = sensitive
-
-# def setText(self, text):
-# text = text if (text and len(text) == 4) else "".join(sample(WORDS, 4)) # 随机4个字符
-# self._code = str(text)
-# html = "".join([FONT.format(color=COLORLIST[qrand() % 6], word=t) for t in text])
-# super(WidgetCode, self).setText(HTML.format(html=html))
-
+
+ # def setText(self, text):
+ # text = text if (text and len(text) == 4) else "".join(sample(WORDS, 4)) # 随机4个字符
+ # self._code = str(text)
+ # html = "".join([FONT.format(color=COLORLIST[qrand() % 6], word=t) for t in text])
+ # super(WidgetCode, self).setText(HTML.format(html=html))
+
def mouseReleaseEvent(self, event):
super(WidgetCode, self).mouseReleaseEvent(event)
self.reset()
-
+
def timerEvent(self, event):
if event.timerId() == self.timer.timerId():
self.step += 1
return self.update()
return super(WidgetCode, self).timerEvent(event)
-
+
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
@@ -107,21 +110,20 @@ class WidgetCode(QLabel):
painter.drawText(x, y - ((SINETABLE[index] * metrics.height()) / 400), ch)
x += metrics.width(ch)
+
if __name__ == "__main__":
import sys
- from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout
- from PyQt5.QtGui import QFontDatabase
- from PyQt5.QtWidgets import QLineEdit
+
app = QApplication(sys.argv)
app.setApplicationName("Validate Code")
QFontDatabase.addApplicationFont("Data/itckrist.ttf")
w = QWidget()
layout = QHBoxLayout(w)
-
+
cwidget = WidgetCode(w, minimumHeight=35, minimumWidth=80)
layout.addWidget(cwidget)
lineEdit = QLineEdit(w, maxLength=4, placeholderText="请输入验证码并按回车验证",
- returnPressed=lambda:print(cwidget.check(lineEdit.text())))
+ returnPressed=lambda: print(cwidget.check(lineEdit.text())))
layout.addWidget(lineEdit)
w.show()
sys.exit(app.exec_())
diff --git a/Demo/WeltHideWindow.py b/Demo/WeltHideWindow.py
index 73ba7b0d361204b710ace71d12ca729e8b36a80d..5faa20bd02023fe627f723ae7e004a5abc8c1783 100644
--- a/Demo/WeltHideWindow.py
+++ b/Demo/WeltHideWindow.py
@@ -4,18 +4,18 @@
"""
Created on 2018年3月1日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: WeltHideWindow
@description: 简单的窗口贴边隐藏
"""
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton
-
-__Author__ = 'By: Irony\nQQ: 892768447\nEmail: 892768447@qq.com'
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication
class WeltHideWindow(QWidget):
@@ -88,7 +88,7 @@ class WeltHideWindow(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = WeltHideWindow()
w.show()
diff --git a/Demo/WindowNotify.py b/Demo/WindowNotify.py
index 408ff5e9afc30de2b21a1dc8db311612bd74346b..490dbd4ee4c56602752de2c1c083dfd6dae833ca 100644
--- a/Demo/WindowNotify.py
+++ b/Demo/WindowNotify.py
@@ -1,27 +1,27 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年3月30日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: WindowNotify
@description: 右下角弹窗
-'''
+"""
import webbrowser
-from PyQt5.QtCore import Qt, QPropertyAnimation, QPoint, QTimer, pyqtSignal
-from PyQt5.QtWidgets import QWidget, QPushButton
+try:
+ from PyQt5.QtCore import Qt, QPropertyAnimation, QPoint, QTimer, pyqtSignal
+ from PyQt5.QtWidgets import QWidget, QPushButton, QApplication, QHBoxLayout
+except ImportError:
+ from PySide2.QtCore import Qt, QPropertyAnimation, QPoint, QTimer, Signal as pyqtSignal
+ from PySide2.QtWidgets import QWidget, QPushButton, QApplication, QHBoxLayout
from Lib.UiNotify import Ui_NotifyForm # @UnresolvedImport
-__version__ = "0.0.1"
-
-
class WindowNotify(QWidget, Ui_NotifyForm):
-
SignalClosed = pyqtSignal() # 弹窗关闭信号
def __init__(self, title="", content="", timeout=5000, *args, **kwargs):
@@ -60,10 +60,10 @@ class WindowNotify(QWidget, Ui_NotifyForm):
webbrowser.open_new_tab("http://alyl.vip")
def onClose(self):
- #点击关闭按钮时
+ # 点击关闭按钮时
print("onClose")
self.isShow = False
- QTimer.singleShot(100, self.closeAnimation)#启动弹回动画
+ QTimer.singleShot(100, self.closeAnimation) # 启动弹回动画
def _init(self):
# 隐藏任务栏|去掉边框|顶层显示
@@ -112,13 +112,14 @@ class WindowNotify(QWidget, Ui_NotifyForm):
print("showAnimation isShow = True")
# 显示动画
self.isShow = True
- self.animation.stop()#先停止之前的动画,重新开始
+ self.animation.stop() # 先停止之前的动画,重新开始
self.animation.setStartValue(self.pos())
self.animation.setEndValue(self._endPos)
self.animation.start()
# 弹出5秒后,如果没有焦点则弹回去
self._timer.start(self._timeout)
-# QTimer.singleShot(self._timeout, self.closeAnimation)
+
+ # QTimer.singleShot(self._timeout, self.closeAnimation)
def closeAnimation(self):
print("closeAnimation hasFocus", self.hasFocus())
@@ -158,9 +159,10 @@ class WindowNotify(QWidget, Ui_NotifyForm):
if self._timeouted:
QTimer.singleShot(1000, self.closeAnimation)
+
if __name__ == "__main__":
import sys
- from PyQt5.QtWidgets import QApplication, QHBoxLayout
+
app = QApplication(sys.argv)
window = QWidget()
diff --git a/Donate/PyQt_Group.png b/Donate/PyQt_Group.png
new file mode 100644
index 0000000000000000000000000000000000000000..4be38ded7c3ee3ea7eac2fe04b801d1e200ef3de
Binary files /dev/null and b/Donate/PyQt_Group.png differ
diff --git a/Donate/PyQt_Guild.png b/Donate/PyQt_Guild.png
new file mode 100644
index 0000000000000000000000000000000000000000..68ffa8e61096a26773ea526caf260d6fb437e9e3
Binary files /dev/null and b/Donate/PyQt_Guild.png differ
diff --git a/Donate/weixin.png b/Donate/weixin.png
index 0e7fe9d30b42ea5eb1ebb2f166c18aa7ccce0e57..ac0a728497a05717787c910660ee96468a64de5e 100644
Binary files a/Donate/weixin.png and b/Donate/weixin.png differ
diff --git a/Donate/weixin2.png b/Donate/weixin2.png
new file mode 100644
index 0000000000000000000000000000000000000000..0e7fe9d30b42ea5eb1ebb2f166c18aa7ccce0e57
Binary files /dev/null and b/Donate/weixin2.png differ
diff --git a/Donate/wxblog.jpg b/Donate/wxblog.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..95fd81a6e3e8defe47d154148bb9b8274067c03d
Binary files /dev/null and b/Donate/wxblog.jpg differ
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 94a9ed024d3859793618152ea559a168bbcbb5e2..0000000000000000000000000000000000000000
--- a/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- 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 .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
- .
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
diff --git a/PyQtGraph/Data/__init__.py b/PyQtGraph/Data/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce54303d2303812b15c2db2fe2b7e8d8a6497a41
--- /dev/null
+++ b/PyQtGraph/Data/__init__.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+@author: wxj
+@license: (C) Hefei tongzhi electromechanical control technology co.LTD
+@contact:
+@software: garner
+@file: __init__.py.py
+@time: 2019/5/21 18:07
+@desc:
+"""
diff --git a/PyQtGraph/Data/graphAnalysis.py b/PyQtGraph/Data/graphAnalysis.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f30b3a60e26c1d5a518a6d46f00c5191244b203
--- /dev/null
+++ b/PyQtGraph/Data/graphAnalysis.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'graphAnalysis.ui'
+#
+# Created by: PyQt5 UI code generator 5.11.3
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class graph_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(1024, 768)
+ self.gridLayout_3 = QtWidgets.QGridLayout(Form)
+ self.gridLayout_3.setContentsMargins(20, 20, 20, 20)
+ self.gridLayout_3.setSpacing(20)
+ self.gridLayout_3.setObjectName("gridLayout_3")
+ self.pushButton_7 = QtWidgets.QPushButton(Form)
+ self.pushButton_7.setMinimumSize(QtCore.QSize(0, 80))
+ font = QtGui.QFont()
+ font.setPointSize(15)
+ self.pushButton_7.setFont(font)
+ self.pushButton_7.setObjectName("pushButton_7")
+ self.gridLayout_3.addWidget(self.pushButton_7, 2, 2, 1, 2)
+ self.label = QtWidgets.QLabel(Form)
+ font = QtGui.QFont()
+ font.setPointSize(15)
+ self.label.setFont(font)
+ self.label.setAlignment(QtCore.Qt.AlignCenter)
+ self.label.setObjectName("label")
+ self.gridLayout_3.addWidget(self.label, 0, 0, 1, 4)
+ self.tabWidget = QtWidgets.QTabWidget(Form)
+ self.tabWidget.setObjectName("tabWidget")
+ self.tab = QtWidgets.QWidget()
+ self.tab.setObjectName("tab")
+ self.tabWidget.addTab(self.tab, "")
+ self.tab_2 = QtWidgets.QWidget()
+ self.tab_2.setObjectName("tab_2")
+ self.tabWidget.addTab(self.tab_2, "")
+ self.gridLayout_3.addWidget(self.tabWidget, 1, 1, 1, 4)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.pushButton_7.setText(_translate("Form", "分析"))
+ self.label.setText(_translate("Form", "图形分析"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Form", "折线图"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Form", "Tab 2"))
diff --git a/PyQtGraph/Data/graphAnalysis.ui b/PyQtGraph/Data/graphAnalysis.ui
new file mode 100644
index 0000000000000000000000000000000000000000..627d66ba656276658e185011e04469b2b6fdd46f
--- /dev/null
+++ b/PyQtGraph/Data/graphAnalysis.ui
@@ -0,0 +1,83 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 1024
+ 768
+
+
+
+ Form
+
+
+
+ 20
+
+
+ 20
+
+
+ 20
+
+
+ 20
+
+
+ 20
+
+ -
+
+
+
+ 0
+ 80
+
+
+
+
+ 15
+
+
+
+ 分析
+
+
+
+ -
+
+
+
+ 15
+
+
+
+ 图形分析
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 折线图
+
+
+
+
+ Tab 2
+
+
+
+
+
+
+
+
+
diff --git a/PyQtGraph/Data/graphTest.py b/PyQtGraph/Data/graphTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..06ffde6d12aba73d86e78c85371235060c3b7ef1
--- /dev/null
+++ b/PyQtGraph/Data/graphTest.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'graphTest.ui'
+#
+# Created by: PyQt5 UI code generator 5.11.3
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class graph_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(1024, 768)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Form)
+ self.verticalLayout.setContentsMargins(20, 20, 20, 20)
+ self.verticalLayout.setSpacing(20)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.label = QtWidgets.QLabel(Form)
+ font = QtGui.QFont()
+ font.setPointSize(15)
+ self.label.setFont(font)
+ self.label.setAlignment(QtCore.Qt.AlignCenter)
+ self.label.setObjectName("label")
+ self.verticalLayout.addWidget(self.label)
+ self.tabWidget = QtWidgets.QTabWidget(Form)
+ self.tabWidget.setObjectName("tabWidget")
+ self.tab = QtWidgets.QWidget()
+ self.tab.setObjectName("tab")
+ self.tabWidget.addTab(self.tab, "")
+ self.tab_2 = QtWidgets.QWidget()
+ self.tab_2.setObjectName("tab_2")
+ self.tabWidget.addTab(self.tab_2, "")
+ self.verticalLayout.addWidget(self.tabWidget)
+ self.pushButton_7 = QtWidgets.QPushButton(Form)
+ self.pushButton_7.setMinimumSize(QtCore.QSize(0, 80))
+ font = QtGui.QFont()
+ font.setPointSize(15)
+ self.pushButton_7.setFont(font)
+ self.pushButton_7.setObjectName("pushButton_7")
+ self.verticalLayout.addWidget(self.pushButton_7)
+
+ self.retranslateUi(Form)
+ self.tabWidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.label.setText(_translate("Form", "图形分析"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Form", "折线图"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Form", "Tab 2"))
+ self.pushButton_7.setText(_translate("Form", "分析"))
diff --git a/PyQtGraph/Data/graphTest.ui b/PyQtGraph/Data/graphTest.ui
new file mode 100644
index 0000000000000000000000000000000000000000..2dbb64ff05c6162394abad5f474b70c43f8b7ab1
--- /dev/null
+++ b/PyQtGraph/Data/graphTest.ui
@@ -0,0 +1,86 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 1024
+ 768
+
+
+
+ Form
+
+
+
+ 20
+
+
+ 20
+
+
+ 20
+
+
+ 20
+
+
+ 20
+
+ -
+
+
+
+ 15
+
+
+
+ 图形分析
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ 0
+
+
+
+ 折线图
+
+
+
+
+ Tab 2
+
+
+
+
+ -
+
+
+
+ 0
+ 80
+
+
+
+
+ 15
+
+
+
+ 分析
+
+
+
+
+
+
+
+
diff --git a/PyQtGraph/README.en.md b/PyQtGraph/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/PyQtGraph/README.md b/PyQtGraph/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3ac9f29d0e0f1b014aeac8d7152e089b8d423722
--- /dev/null
+++ b/PyQtGraph/README.md
@@ -0,0 +1,36 @@
+# PyQtGraph
+
+## 说明
+
+正在汇总Demo中,Be continued
+
+本章节预告,实际使用中的包`PyQtGraph`相关BUG,以及解决方案,如有精力会添加相应解决方案的作者
+
+1. `PyQtGraph`右键保存图片功能异常,以及解决防范
+2. 依据`PyQtGraph` examples例子的使用心得
+3. `PyQtGraph`如何禁止左键,右键,滑轮事件功能
+4. 使用`QScrollArea`后添加`pg.PlotWidget()`不能选择图片尺寸问题。
+5. 多图共享轴(Difficult)
+6. `pg.PlotWidget()`鼠标获取X轴坐标
+
+## 目录
+- [鼠标获取X轴坐标](#1鼠标获取X轴坐标)
+- [禁止右键点击功能、鼠标滚轮,添加滚动条等功能](#2禁止右键点击功能、鼠标滚轮,添加滚动条等功能)
+
+## 1、鼠标获取X轴坐标
+[运行 mouseFlow.py](mouseFlow.py)
+
+
+
+## 2、禁止右键点击功能、鼠标滚轮,添加滚动条等功能
+[运行 graph1.py](graph1.py) | [查看 graphTest.ui](Data/graphTest.ui)
+
+
+
+## 3、不用修改源码,重加载,解决右键保存图片异常;解决自定义坐标轴密集显示;禁止鼠标事件;
+[加载 tools.py](tools.py)
+
+## 4、QScrollArea添加和修改大小例子;
+[运行 testGraphAnalysis.py](testGraphAnalysis.py) | [查看 graphAnalysis.ui](Data/graphAnalysis.ui)
+
+
diff --git a/PyQtGraph/ScreenShot/GraphAnalysis.gif b/PyQtGraph/ScreenShot/GraphAnalysis.gif
new file mode 100644
index 0000000000000000000000000000000000000000..006265ef10eab7cd020dd892af72518e8e6335e4
Binary files /dev/null and b/PyQtGraph/ScreenShot/GraphAnalysis.gif differ
diff --git a/PyQtGraph/ScreenShot/function.gif b/PyQtGraph/ScreenShot/function.gif
new file mode 100644
index 0000000000000000000000000000000000000000..708e0f096030077fcfeb42cf9fe3f250f0d49a86
Binary files /dev/null and b/PyQtGraph/ScreenShot/function.gif differ
diff --git a/PyQtGraph/ScreenShot/mouseFlow.gif b/PyQtGraph/ScreenShot/mouseFlow.gif
new file mode 100644
index 0000000000000000000000000000000000000000..646ab999db20088b4017a7145fb2fa38b1f5b24a
Binary files /dev/null and b/PyQtGraph/ScreenShot/mouseFlow.gif differ
diff --git a/PyQtGraph/graph1.py b/PyQtGraph/graph1.py
new file mode 100644
index 0000000000000000000000000000000000000000..b37b8ade66f72cbf01263bfc67158b2fdfa79e3a
--- /dev/null
+++ b/PyQtGraph/graph1.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+Created on 2019年5月21日
+@author: weike32
+@site: https://pyqt.site ,https://github.com/weike32
+@email: 394967319@qq.com
+@file: CopyContent
+@description: 禁止右键,添加滑动窗口,点击按钮生成图片,自定义Y轴坐标,背景颜色调整
+"""
+import sys
+
+import pyqtgraph as pg
+from PyQt5.QtGui import QSpacerItem, QSizePolicy
+from PyQt5.QtWidgets import QDialog, QApplication, QWidget, QScrollArea, QVBoxLayout
+
+from PyQtGraph.Data.graphTest import graph_Form
+
+
+class CustomViewBox(pg.ViewBox):
+ def __init__(self, *args, **kwds):
+ pg.ViewBox.__init__(self, *args, **kwds)
+ self.RectMode = 3
+ self.setMouseMode(self.RectMode)
+
+ def mouseClickEvent(self, ev):
+ if ev.button() == pg.QtCore.Qt.RightButton:
+ self.autoRange()
+
+ def mouseDragEvent(self, ev):
+ pg.ViewBox.mouseDragEvent(self, ev)
+
+ def wheelEvent(self, ev, axis=None):
+ # pg.ViewBox.wheelEvent(self, ev, axis)
+ ev.ignore()
+
+
+class graphAnalysis(QDialog, graph_Form):
+ def __init__(self):
+ super(graphAnalysis, self).__init__()
+ self.setupUi(self)
+ self.pushButton_7.clicked.connect(self.test)
+ self.tabWidget.clear()
+
+ def test(self):
+ tab1 = QWidget()
+ scrollArea = QScrollArea(tab1)
+ scrollArea.setMinimumSize(984, 550)
+ scrollArea.setWidgetResizable(True)
+ labelsContainer = QWidget()
+ labelsContainer.setMinimumSize(0, 1500)
+ scrollArea.setWidget(labelsContainer)
+ layout = QVBoxLayout(labelsContainer)
+ time = ['2019-04-20 08:09:00', '2019-04-20 08:09:00', '2019-04-20 08:09:00', '2019-04-20 08:09:00']
+ value = [1.2, 2, 1, 4]
+ xdict = dict(enumerate(time))
+ ticks = [list(zip(range(4), tuple(time)))]
+ vb = CustomViewBox()
+ plt = pg.PlotWidget(title="标题这里填写", viewBox=vb)
+ plt.setBackground(background=None)
+ plt.plot(list(xdict.keys()), value)
+ plt.getPlotItem().getAxis("bottom").setTicks(ticks)
+ temp = QWidget()
+ temp.setMinimumSize(900, 300)
+ temp.setMaximumSize(900, 300)
+ layout1 = QVBoxLayout(temp)
+ layout1.addWidget(plt)
+ layout.addWidget(temp)
+ spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum,
+ QSizePolicy.Expanding)
+ layout.addItem(spacerItem)
+ self.tabWidget.addTab(tab1, '这里tabWidget修改标签')
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ w = graphAnalysis()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/PyQtGraph/mouseFlow.py b/PyQtGraph/mouseFlow.py
new file mode 100644
index 0000000000000000000000000000000000000000..1d3503b7f34ae59c7077072ceed429e32082fc59
--- /dev/null
+++ b/PyQtGraph/mouseFlow.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+Created on 2019年5月2日
+@author: weike32
+@site: https://pyqt.site ,https://github.com/weike32
+@email: 394967319@qq.com
+@file: CopyContent
+@description: 查阅了很多博客,如果有异,可以联系作者邮箱。本Demo仅作学习参考用,保有后续相关权益。
+"""
+import sys
+
+import numpy as np
+import pyqtgraph as pg
+from PyQt5 import QtCore
+from PyQt5.QtWidgets import QApplication, QMainWindow
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(726, 595)
+ self.graphicsView = pg.PlotWidget(Form)
+ self.graphicsView.setGeometry(QtCore.QRect(75, 131, 621, 441))
+ self.graphicsView.setObjectName("graphicsView")
+
+
+class MyWindow(QMainWindow, Ui_Form):
+ def __init__(self, parent=None):
+ super(MyWindow, self).__init__(parent)
+ self.setupUi(self)
+ x = np.linspace(-100, 100, 1000)
+ data = np.sin(x) / x
+ self.graphicsView.plot(data, pen=(255, 255, 255, 200))
+ self.label = pg.TextItem(text="横坐标:{}".format(0))
+ self.graphicsView.addItem(self.label)
+ self.setMouseTracking(True)
+ self.graphicsView.scene().sigMouseMoved.connect(self.onMouseMoved)
+
+ def onMouseMoved(self, evt):
+ if self.graphicsView.plotItem.vb.mapSceneToView(evt):
+ point = self.graphicsView.plotItem.vb.mapSceneToView(evt)
+ self.label.setHtml("横坐标:{0}
".format(point.x()))
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ myWin = MyWindow()
+ myWin.show()
+ sys.exit(app.exec_())
diff --git a/PyQtGraph/requirements.txt b/PyQtGraph/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0e9e4ca01da4d5e4bf4196642d17b4e16063f2b7
--- /dev/null
+++ b/PyQtGraph/requirements.txt
@@ -0,0 +1 @@
+pyqtgraph
diff --git a/PyQtGraph/testGraphAnalysis.py b/PyQtGraph/testGraphAnalysis.py
new file mode 100644
index 0000000000000000000000000000000000000000..23f23a4c27b62b1562e5f9b670844b7d7891b243
--- /dev/null
+++ b/PyQtGraph/testGraphAnalysis.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+Created on 2019年8月17日
+@author: weike32
+@site: https://pyqt.site ,https://github.com/weike32
+@email: 394967319@qq.com
+@file: CopyContent
+@description:
+"""
+import sys
+
+import pyqtgraph as pg
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QSpacerItem, QSizePolicy
+from PyQt5.QtWidgets import QDialog, QApplication, QWidget, QScrollArea, QVBoxLayout
+
+from PyQtGraph.Data.graphAnalysis import graph_Form
+
+
+class CustomViewBox(pg.ViewBox):
+ def __init__(self, *args, **kwds):
+ pg.ViewBox.__init__(self, *args, **kwds)
+ self.RectMode = 3
+ self.setMouseMode(self.RectMode)
+
+ def mouseClickEvent(self, ev):
+ if ev.button() == pg.QtCore.Qt.RightButton:
+ self.autoRange()
+
+ def mouseDragEvent(self, ev):
+ pg.ViewBox.mouseDragEvent(self, ev)
+
+ def wheelEvent(self, ev, axis=None):
+ ev.ignore()
+
+
+class graphAnalysis(QDialog, graph_Form):
+ def __init__(self):
+ super(graphAnalysis, self).__init__()
+ self.setupUi(self)
+ self.pushButton_7.clicked.connect(self.test)
+ self.tabWidget.clear()
+
+ def handleChanged(self, item, column):
+ count = item.childCount()
+ if item.checkState(column) == Qt.Checked:
+ for index in range(count):
+ item.child(index).setCheckState(0, Qt.Checked)
+ if item.checkState(column) == Qt.Unchecked:
+ for index in range(count):
+ item.child(index).setCheckState(0, Qt.Unchecked)
+
+ def test(self):
+
+ tab1 = QWidget()
+ scrollArea = QScrollArea(tab1)
+ scrollArea.setMinimumSize(650, 550)
+ scrollArea.setWidgetResizable(True)
+
+ labelsContainer = QWidget()
+ labelsContainer.setMinimumSize(0, 3000 + 200)
+ scrollArea.setWidget(labelsContainer)
+ layout = QVBoxLayout(labelsContainer)
+
+ time = ['2019-04-20 08:09:00', '2019-04-20 08:09:00', '2019-04-20 08:09:00', '2019-04-20 08:09:00']
+ value = [1.2, 2, 1, 4]
+ xdict = dict(enumerate(time))
+ ticks = [list(zip(range(4), tuple(time)))]
+ for i in range(11):
+ vb1 = CustomViewBox()
+ plt1 = pg.PlotWidget(title="Basic array plotting%s" % i, viewBox=vb1)
+ plt1.resize(500, 500)
+ plt1.setBackground(background=None)
+ plt1.plot(list(xdict.keys()), value)
+ plt1.getPlotItem().getAxis("bottom").setTicks(ticks)
+ temp1 = QWidget()
+ temp1.setMinimumSize(600, 300)
+ temp1.setMaximumSize(600, 300)
+ layout2 = QVBoxLayout(temp1)
+ layout2.addWidget(plt1)
+ layout.addWidget(temp1)
+ spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum,
+ QSizePolicy.Expanding)
+ layout.addItem(spacerItem)
+ # print(layout.count())
+ self.tabWidget.addTab(tab1, '12')
+ for i in range(self.tabWidget.count()):
+ self.tabWidget.widget(i)
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ w = graphAnalysis()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/PyQtGraph/tools.py b/PyQtGraph/tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a867de382c4fc3ab21963a1f3aaed9008d2b3d3
--- /dev/null
+++ b/PyQtGraph/tools.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+Created on 2019年5月21日
+@author: weike32
+@site: https://pyqt.site ,https://github.com/weike32
+@email: 394967319@qq.com
+@file: CopyContent
+@description: 工具类
+"""
+import pyqtgraph as pg
+from pyqtgraph.exporters.ImageExporter import ImageExporter, Exporter
+from pyqtgraph.parametertree import Parameter
+
+
+# 不用修改源码,重加载,解决右键保存图片异常
+def widthChanged(self):
+ sr = self.getSourceRect()
+ ar = float(sr.height()) / sr.width()
+ self.params.param('height').setValue(int(self.params['width'] * ar), blockSignal=self.heightChanged)
+
+
+def heightChanged(self):
+ sr = self.getSourceRect()
+ ar = float(sr.width()) / sr.height()
+ self.params.param('width').setValue(int(self.params['height'] * ar), blockSignal=self.widthChanged)
+
+
+def New__init__(self, item):
+ Exporter.__init__(self, item)
+ tr = self.getTargetRect()
+ if isinstance(item, pg.Qt.QtGui.QGraphicsItem):
+ scene = item.scene()
+ else:
+ scene = item
+ bgbrush = scene.views()[0].backgroundBrush()
+ bg = bgbrush.color()
+ if bgbrush.style() == pg.Qt.QtCore.Qt.NoBrush:
+ bg.setAlpha(0)
+
+ self.params = Parameter(name='params', type='group', children=[
+ {'name': 'width', 'type': 'int', 'value': int(tr.width()), 'limits': (0, None)},
+ {'name': 'height', 'type': 'int', 'value': int(tr.height()), 'limits': (0, None)},
+ {'name': 'antialias', 'type': 'bool', 'value': True},
+ {'name': 'background', 'type': 'color', 'value': bg},
+ ])
+ self.params.param('width').sigValueChanged.connect(self.widthChanged)
+ self.params.param('height').sigValueChanged.connect(self.heightChanged)
+
+
+ImageExporter.heightChanged = heightChanged
+ImageExporter.widthChanged = widthChanged
+ImageExporter.__init__ = New__init__
+
+
+# 解决自定义坐标轴密集显示
+class MyStringAxis(pg.AxisItem):
+ def __init__(self, xdict, *args, **kwargs):
+ pg.AxisItem.__init__(self, *args, **kwargs)
+ self.xdict = xdict
+
+ def tickStrings(self, values, scale, spacing):
+ strings = []
+ for v in values:
+ vs = v * scale
+ if vs in self.xdict.keys():
+ vstr = self.xdict[vs]
+ else:
+ vstr = ""
+ strings.append(vstr)
+ return strings
+
+
+# 禁止鼠标事件
+class CustomViewBox(pg.ViewBox):
+ def __init__(self, *args, **kwds):
+ pg.ViewBox.__init__(self, *args, **kwds)
+ self.RectMode = 3
+ self.setMouseMode(self.RectMode)
+
+ def mouseClickEvent(self, ev):
+ if ev.button() == pg.QtCore.Qt.RightButton:
+ self.autoRange()
+
+ def mouseDragEvent(self, ev):
+ pg.ViewBox.mouseDragEvent(self, ev)
+
+ def wheelEvent(self, ev, axis=None):
+ pg.ViewBox.wheelEvent(self, ev, axis)
diff --git a/QAxWidget/README.en.md b/QAxWidget/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QAxWidget/README.md b/QAxWidget/README.md
index 56835bb8faa054480149812cf0b31a10e02d7b83..8811751d7fc1ab5c6f9c6f21e40a9f351757d591 100644
--- a/QAxWidget/README.md
+++ b/QAxWidget/README.md
@@ -1,5 +1,8 @@
# QAxWidget
+- 目录
+ - [显示Word、Excel、PDF文件](#1显示WordExcelPDF文件)
+
## 1、显示Word、Excel、PDF文件
[运行 ViewOffice.py](ViewOffice.py)
diff --git a/QAxWidget/ViewOffice.py b/QAxWidget/ViewOffice.py
index f30d753fa81a42d16a92809b523e5593400161bb..3c3318097ab665fe1ec15e43ffb33680471555a1 100644
--- a/QAxWidget/ViewOffice.py
+++ b/QAxWidget/ViewOffice.py
@@ -1,24 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年4月6日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ViewOffice
@description:
-'''
+"""
+
from PyQt5.QAxContainer import QAxWidget
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QFileDialog,\
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QFileDialog, \
QMessageBox
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
-
-
class AxWidget(QWidget):
def __init__(self, *args, **kwargs):
@@ -68,6 +64,7 @@ class AxWidget(QWidget):
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = AxWidget()
w.show()
diff --git a/QCalendarWidget/CalendarQssStyle.py b/QCalendarWidget/CalendarQssStyle.py
index 6a1ed681968f02ab27ca75073ba17aa23ff18614..9d2e0924f2fa5eb014df742a9eb42712209603df 100644
--- a/QCalendarWidget/CalendarQssStyle.py
+++ b/QCalendarWidget/CalendarQssStyle.py
@@ -1,17 +1,23 @@
"""
Created on 2018年1月30日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: CalendarQssStyle
@description: 日历美化样式
"""
import sys
-from PyQt5.QtWidgets import QApplication, QCalendarWidget
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QTextCharFormat, QBrush, QColor
+ from PyQt5.QtWidgets import QApplication, QCalendarWidget
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QTextCharFormat, QBrush, QColor
+ from PySide2.QtWidgets import QApplication, QCalendarWidget
-
-StyleSheet = '''
+StyleSheet = """
/*顶部导航区域*/
#qt_calendar_navigationbar {
background-color: rgb(0, 188, 212);
@@ -99,7 +105,7 @@ CalendarWidget QToolButton::menu-indicator {
outline: 0px;/*去掉选中后的虚线框*/
selection-background-color: rgb(0, 188, 212); /*选中背景颜色*/
}
-'''
+"""
class CalendarWidget(QCalendarWidget):
@@ -109,6 +115,17 @@ class CalendarWidget(QCalendarWidget):
# 隐藏左边的序号
self.setVerticalHeaderFormat(self.NoVerticalHeader)
+ # 修改周六周日颜色
+
+ fmtGreen = QTextCharFormat()
+ fmtGreen.setForeground(QBrush(Qt.green))
+ self.setWeekdayTextFormat(Qt.Saturday, fmtGreen)
+
+ fmtOrange = QTextCharFormat()
+ fmtOrange.setForeground(QBrush(QColor(252, 140, 28)))
+ self.setWeekdayTextFormat(Qt.Sunday, fmtOrange)
+
+
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setStyleSheet(StyleSheet)
diff --git a/QCalendarWidget/README.md b/QCalendarWidget/README.md
index 42ee21d5ebd5f751a38df850c637c96955b6d91f..62bd881536f06fe12eadfba943cbbfe1a8813ead 100644
--- a/QCalendarWidget/README.md
+++ b/QCalendarWidget/README.md
@@ -1,5 +1,8 @@
# QCalendarWidget
+- 目录
+ - [QSS美化日历样式](#1QSS美化日历样式)
+
## 1、QSS美化日历样式
[运行 CalendarQssStyle.py](CalendarQssStyle.py)
diff --git a/QChart/README.md b/QChart/README.md
deleted file mode 100644
index fb1ca58663c4d3c216a5d2b0b2746bf07933e4c4..0000000000000000000000000000000000000000
--- a/QChart/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# QChart
-
-## 1、折线图
-[运行 LineChart.py](LineChart.py)
-
-
-
-## 2、折线堆叠图
-[运行 LineStack.py](LineStack.py)
-
-仿照 [line-stack](http://echarts.baidu.com/demo.html#line-stack)
-
-
-
-## 3、柱状堆叠图
-[运行 BarStack.py](BarStack.py)
-
-仿照 [bar-stack](http://echarts.baidu.com/demo.html#bar-stack)
-
-
-
-## 4、LineChart自定义xy轴
-[运行 CustomXYaxis.py](CustomXYaxis.py)
-
-
-
-## 5、ToolTip提示
-[运行 ToolTip.py](ToolTip.py) | [运行 ToolTip2.py](ToolTip2.py)
-
- 
\ No newline at end of file
diff --git a/QComboBox/CenterText.py b/QComboBox/CenterText.py
new file mode 100644
index 0000000000000000000000000000000000000000..30eef32a7c11a71ec0822dfe5a3bda078afa13e9
--- /dev/null
+++ b/QComboBox/CenterText.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2022/09/03
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CenterText.py
+@description: 文字居中对齐
+"""
+
+import sys
+
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtWidgets import QApplication, QStyle, QVBoxLayout, QWidget
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QApplication, QStyle, QVBoxLayout, QWidget
+
+from Lib.CtComboBox import CtComboBox
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+
+ c1 = CtComboBox(self)
+ c1.addItems(['item-%s' % i for i in range(10)])
+ layout.addWidget(c1)
+
+ # 可编辑
+ c2 = CtComboBox(self)
+ c2.setEditable(True)
+ c2.lineEdit().setAlignment(Qt.AlignCenter)
+ c2.addItems(['item-%s' % i for i in range(10)])
+ layout.addWidget(c2)
+
+ # 带图标
+ c3 = CtComboBox(self)
+ for i in range(10):
+ c3.addItem(c3.style().standardIcon(QStyle.SP_ComputerIcon),
+ 'item-%s' % i)
+ layout.addWidget(c3)
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QComboBox/CityLinkage.py b/QComboBox/CityLinkage.py
index 603f342d3b69bd2185e78fab4475cd415091487b..203888f49bec45524b48dfe903205c049f0ce243 100644
--- a/QComboBox/CityLinkage.py
+++ b/QComboBox/CityLinkage.py
@@ -3,8 +3,8 @@
"""
Created on 2018年1月27日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: CityLinkage
@description: 下拉联动
@@ -12,16 +12,18 @@ Created on 2018年1月27日
import json
import sys
-from PyQt5.QtCore import Qt, QSortFilterProxyModel, QRegExp
-from PyQt5.QtGui import QStandardItemModel, QStandardItem
-from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout, QComboBox,\
- QLabel, QSpacerItem, QSizePolicy
import chardet
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt, QSortFilterProxyModel, QRegExp
+ from PyQt5.QtGui import QStandardItemModel, QStandardItem
+ from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout, QComboBox, \
+ QLabel, QSpacerItem, QSizePolicy
+except ImportError:
+ from PySide2.QtCore import Qt, QSortFilterProxyModel, QRegExp
+ from PySide2.QtGui import QStandardItemModel, QStandardItem
+ from PySide2.QtWidgets import QWidget, QApplication, QHBoxLayout, QComboBox, \
+ QLabel, QSpacerItem, QSizePolicy
class SortFilterProxyModel(QSortFilterProxyModel):
diff --git a/QComboBox/Lib/CtComboBox.py b/QComboBox/Lib/CtComboBox.py
new file mode 100644
index 0000000000000000000000000000000000000000..3539ff1117d04a1c2b1c2250ab65993c6cc75791
--- /dev/null
+++ b/QComboBox/Lib/CtComboBox.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2022/09/04
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CtComboBox.py
+@description: 文字居中对齐
+"""
+
+try:
+ from PyQt5.QtCore import QRect, Qt
+ from PyQt5.QtGui import QIcon, QPalette
+ from PyQt5.QtWidgets import QComboBox, QProxyStyle
+except ImportError:
+ from PySide2.QtCore import QRect, Qt
+ from PySide2.QtGui import QIcon, QPalette
+ from PySide2.QtWidgets import QComboBox, QProxyStyle
+
+
+class ComboBoxStyle(QProxyStyle):
+
+ def drawControl(self, element, option, painter, widget=None):
+ if element == QProxyStyle.CE_ComboBoxLabel:
+ # https://github.com/qt/qtbase/blob/5.15.2/src/widgets/styles/qcommonstyle.cpp#L2200
+ editRect = self.subControlRect(QProxyStyle.CC_ComboBox, option,
+ QProxyStyle.SC_ComboBoxEditField,
+ widget)
+ painter.save()
+ painter.setClipRect(editRect)
+ if not option.currentIcon.isNull():
+ # 绘制图标
+ mode = QIcon.Normal if (
+ option.state &
+ QProxyStyle.State_Enabled) else QIcon.Disabled
+ pixmap = option.currentIcon.pixmap(
+ widget.window().windowHandle() if widget else None,
+ option.iconSize, mode)
+ iconRect = QRect(editRect)
+ iconRect.setWidth(option.iconSize.width() + 4)
+ iconRect = self.alignedRect(option.direction,
+ Qt.AlignLeft | Qt.AlignVCenter,
+ iconRect.size(), editRect)
+ if option.editable:
+ painter.fillRect(iconRect,
+ option.palette.brush(QPalette.Base))
+ self.drawItemPixmap(painter, iconRect, Qt.AlignCenter, pixmap)
+
+ if option.direction == Qt.RightToLeft:
+ editRect.translate(-4 - option.iconSize.width(), 0)
+ else:
+ editRect.translate(option.iconSize.width() + 4, 0)
+ if option.currentText and not option.editable:
+ # 考虑右边箭头位置
+ arrowRect = self.subControlRect(QProxyStyle.CC_ComboBox, option,
+ QProxyStyle.SC_ComboBoxArrow,
+ widget)
+ editRect.setWidth(editRect.width() + arrowRect.width())
+ # 绘制居中文字
+ self.drawItemText(
+ painter, editRect.adjusted(1, 0, -1, 0),
+ self.visualAlignment(option.direction, Qt.AlignCenter),
+ option.palette, option.state & QProxyStyle.State_Enabled,
+ option.currentText)
+ painter.restore()
+ return
+ super(ComboBoxStyle, self).drawControl(element, option, painter, widget)
+
+
+class CtComboBox(QComboBox):
+
+ def __init__(self, *args, **kwargs):
+ super(CtComboBox, self).__init__(*args, **kwargs)
+ # 绑定每个元素添加信号,用于设置文本居中
+ self.model().rowsInserted.connect(self._onRowsInserted)
+ self.setStyle(ComboBoxStyle())
+
+ def _onRowsInserted(self, index, first, last):
+ if first < 0:
+ return
+ for i in range(first, last + 1):
+ self.view().model().item(i).setTextAlignment(Qt.AlignCenter)
diff --git a/QComboBox/Lib/__init__.py b/QComboBox/Lib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QComboBox/README.en.md b/QComboBox/README.en.md
index b3bd077727c362838ec07ba45a1bbea77a80ec06..9a4f5214accfa2a5987d6294d78520339a2b2f4a 100644
--- a/QComboBox/README.en.md
+++ b/QComboBox/README.en.md
@@ -1,5 +1,8 @@
# QComboBox
+- Catalog
+ - [Data Linkage](#1data-linkage)
+
## 1、Data Linkage
[Run CityLinkage.py](CityLinkage.py)
diff --git a/QComboBox/README.md b/QComboBox/README.md
index 22d75d385e62a0c31a410f86ca723ccbd15ee66d..8a914d23a5f5847f2cdb81457bedf93f8af3bf3e 100644
--- a/QComboBox/README.md
+++ b/QComboBox/README.md
@@ -1,6 +1,11 @@
# QComboBox
+- 目录
+ - [下拉数据关联](#1下拉数据关联)
+ - [文本居中显示](#2文本居中显示)
+
## 1、下拉数据关联
+
[运行 CityLinkage.py](CityLinkage.py)
一个省市区关联的三级联动,数据源在data.json中
@@ -9,4 +14,13 @@
2. 并根据唯一编码过滤,为了不影响内容显示,唯一编码的角色为`ToolTipRole`
3. 用`QColumnView`可以实现类似效果
-
\ No newline at end of file
+
+
+## 2、文本居中显示
+
+[运行 CenterText.py](CenterText.py)
+
+1. 使用`QProxyStyle`对文件居中显示
+2. 新增得item数据使用`setTextAlignment`对齐
+
+
\ No newline at end of file
diff --git a/QComboBox/ScreenShot/CenterText.png b/QComboBox/ScreenShot/CenterText.png
new file mode 100644
index 0000000000000000000000000000000000000000..a99d8fad3e34b135c6b8f691c995c9381a1b87e0
Binary files /dev/null and b/QComboBox/ScreenShot/CenterText.png differ
diff --git a/QFileSystemModel/CustomIcon.py b/QFileSystemModel/CustomIcon.py
index 9eb15b80c6c846f18429d13faa3f9a77c28f94cc..9b322288b70e91788337718e2a0a956991e1cec4 100644
--- a/QFileSystemModel/CustomIcon.py
+++ b/QFileSystemModel/CustomIcon.py
@@ -3,28 +3,29 @@
"""
Created on 2018年1月26日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
-@file: FileSystemModel
+@file: CustomIcon
@description:
"""
-import sys
-
-from PyQt5.QtCore import QFileInfo
-from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QFileSystemModel, QFileIconProvider, QApplication,\
- QTreeView
+import sys
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
-
-# 图标提供类
+try:
+ from PyQt5.QtCore import QFileInfo
+ from PyQt5.QtGui import QIcon
+ from PyQt5.QtWidgets import QFileSystemModel, QFileIconProvider, QApplication, \
+ QTreeView
+except ImportError:
+ from PySide2.QtCore import QFileInfo
+ from PySide2.QtGui import QIcon
+ from PySide2.QtWidgets import QFileSystemModel, QFileIconProvider, QApplication, \
+ QTreeView
class FileIconProvider(QFileIconProvider):
+ """图标提供类"""
def __init__(self, *args, **kwargs):
super(FileIconProvider, self).__init__(*args, **kwargs)
diff --git a/QFileSystemModel/README.md b/QFileSystemModel/README.md
index 0f95051e2f6d0c35e77afb6e4ba828c017b9d5b8..fc76971f0f8a451c9fbaea2b642d0284bf616bab 100644
--- a/QFileSystemModel/README.md
+++ b/QFileSystemModel/README.md
@@ -1,5 +1,8 @@
# QFileSystemModel
+- 目录
+ - [自定义图标](#1自定义图标)
+
## 1、自定义图标
[运行 CustomIcon.py](CustomIcon.py)
diff --git a/QFlowLayout/Data/CoverItemWidget.ui b/QFlowLayout/Data/CoverItemWidget.ui
new file mode 100644
index 0000000000000000000000000000000000000000..2f245e1344895af124b9ed3ea8b387c232077a79
--- /dev/null
+++ b/QFlowLayout/Data/CoverItemWidget.ui
@@ -0,0 +1,85 @@
+
+
+ CoverItemWidget
+
+
+
+ 200
+ 256
+
+
+
+
+ 200
+ 256
+
+
+
+
+
+
+
+ 12
+
+
+ 10
+
+
+ 10
+
+
+ 10
+
+
+ 10
+
+ -
+
+
+
+ 180
+ 180
+
+
+
+
+
+
+ true
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 10
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ CoverLabel
+ QLabel
+
+
+
+
+
+
diff --git a/QFlowLayout/Data/CoverLabel.ui b/QFlowLayout/Data/CoverLabel.ui
new file mode 100644
index 0000000000000000000000000000000000000000..2e7e037880e23bace4731623a4f1c25620fe267b
--- /dev/null
+++ b/QFlowLayout/Data/CoverLabel.ui
@@ -0,0 +1,109 @@
+
+
+ CoverLabel
+
+
+
+ 0
+ 0
+ 180
+ 180
+
+
+
+ PointingHandCursor
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 135
+
+
+
+
+ -
+
+
+ #widgetBottom {
+ background-color: rgba(0, 0, 0, 150);
+}
+
+
+ -
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/QFlowLayout/Data/Svg_icon_headset_sm.svg b/QFlowLayout/Data/Svg_icon_headset_sm.svg
new file mode 100644
index 0000000000000000000000000000000000000000..98a1a3591d4f0741b27d27862023bc75bddf4a8d
--- /dev/null
+++ b/QFlowLayout/Data/Svg_icon_headset_sm.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/QFlowLayout/Data/Svg_icon_loading.svg b/QFlowLayout/Data/Svg_icon_loading.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0b00b7993dc84f2a6221b942d35dc62421e70ac4
--- /dev/null
+++ b/QFlowLayout/Data/Svg_icon_loading.svg
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QFlowLayout/Data/Svg_icon_play_sm.svg b/QFlowLayout/Data/Svg_icon_play_sm.svg
new file mode 100644
index 0000000000000000000000000000000000000000..08a47f20e76885d4e77b0f31221ff4d9e8bacafd
--- /dev/null
+++ b/QFlowLayout/Data/Svg_icon_play_sm.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/QFlowLayout/HotPlaylist.py b/QFlowLayout/HotPlaylist.py
index 2fef16729a0027cd8fa2c74233e23bfe4072b5cc..6b276c841686310259203c46a0844ec9bd593971 100644
--- a/QFlowLayout/HotPlaylist.py
+++ b/QFlowLayout/HotPlaylist.py
@@ -1,194 +1,47 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
-Created on 2018年2月4日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+"""
+Created on 2023/02/22
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
@email: 892768447@qq.com
-@file: TencentMovieHotPlay_Flow
-@description:
-'''
+@file: HotPlaylist.py
+@description:
+"""
+
import os
import sys
-import webbrowser
-
-from PyQt5.QtCore import QSize, Qt, QUrl, QTimer, pyqtSignal
-from PyQt5.QtGui import QPainter, QFont, QLinearGradient, QGradient, QColor,\
- QBrush, QPaintEvent, QPixmap
-from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
-from PyQt5.QtSvg import QSvgWidget
-from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel,\
- QHBoxLayout, QSpacerItem, QSizePolicy, QScrollArea, QAbstractSlider
+from Lib.CoverItemWidget import CoverItemWidget
from Lib.flowlayout import FlowLayout # @UnresolvedImport
from lxml.etree import HTML # @UnresolvedImport
+try:
+ from PyQt5.QtCore import Qt, QTimer, QUrl, pyqtSignal
+ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
+ from PyQt5.QtSvg import QSvgWidget
+ from PyQt5.QtWidgets import (QAbstractSlider, QApplication, QScrollArea,
+ QWidget)
+except ImportError:
+ from PySide2.QtCore import Qt, QTimer, QUrl, pyqtSignal
+ from PySide2.QtNetwork import QNetworkAccessManager, QNetworkRequest
+ from PySide2.QtSvg import QSvgWidget
+ from PySide2.QtWidgets import (QAbstractSlider, QApplication, QScrollArea,
+ QWidget)
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
-
-# offset=0,30,60,90
-Url = "http://v.qq.com/x/list/movie?pay=-1&offset={0}"
+# offset=0,35,70,105
+Url = "https://music.163.com/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset={0}"
-# 播放量图标
-Svg_icon_play_sm = '''
-
-
-'''.encode()
+Agent = b"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.50"
-Svg_icon_loading = '''
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- '''.encode()
+Referer = b"https://music.163.com"
-# 主演
+# 作者
Actor = '''{title} '''
-class CoverLabel(QLabel):
-
- def __init__(self, cover_path, cover_title, video_url, *args, **kwargs):
- super(CoverLabel, self).__init__(*args, **kwargs)
-# super(CoverLabel, self).__init__(
-# ' '.format(os.path.abspath(cover_path)), *args, **kwargs)
- self.setCursor(Qt.PointingHandCursor)
- self.setScaledContents(True)
- self.setMinimumSize(220, 308)
- self.setMaximumSize(220, 308)
- self.cover_path = cover_path
- self.cover_title = cover_title
- self.video_url = video_url
- self.setPixmap(QPixmap(cover_path))
-
- def setCoverPath(self, path):
- self.cover_path = path
-
- def mouseReleaseEvent(self, event):
- super(CoverLabel, self).mouseReleaseEvent(event)
- webbrowser.open_new_tab(self.video_url)
-
- def paintEvent(self, event):
- super(CoverLabel, self).paintEvent(event)
- if hasattr(self, "cover_title") and self.cover_title != "":
- # 底部绘制文字
- painter = QPainter(self)
- rect = self.rect()
- # 粗略字体高度
- painter.save()
- fheight = self.fontMetrics().height()
- # 底部矩形框背景渐变颜色
- bottomRectColor = QLinearGradient(
- rect.width() / 2, rect.height() - 24 - fheight,
- rect.width() / 2, rect.height())
- bottomRectColor.setSpread(QGradient.PadSpread)
- bottomRectColor.setColorAt(0, QColor(255, 255, 255, 70))
- bottomRectColor.setColorAt(1, QColor(0, 0, 0, 50))
- # 画半透明渐变矩形框
- painter.setPen(Qt.NoPen)
- painter.setBrush(QBrush(bottomRectColor))
- painter.drawRect(rect.x(), rect.height() - 24 -
- fheight, rect.width(), 24 + fheight)
- painter.restore()
- # 距离底部一定高度画文字
- font = self.font() or QFont()
- font.setPointSize(8)
- painter.setFont(font)
- painter.setPen(Qt.white)
- rect.setHeight(rect.height() - 12) # 底部减去一定高度
- painter.drawText(rect, Qt.AlignHCenter |
- Qt.AlignBottom, self.cover_title)
-
-
-class ItemWidget(QWidget):
-
- def __init__(self, cover_path, figure_info, figure_title,
- figure_score, figure_desc, figure_count, video_url, cover_url, img_path, *args, **kwargs):
- super(ItemWidget, self).__init__(*args, **kwargs)
- self.setMaximumSize(220, 420)
- self.setMaximumSize(220, 420)
- self.img_path = img_path
- self.cover_url = cover_url
- layout = QVBoxLayout(self)
- layout.setContentsMargins(10, 20, 10, 0)
- # 图片label
- self.clabel = CoverLabel(cover_path, figure_info, video_url, self)
- layout.addWidget(self.clabel)
-
- # 片名和分数
- flayout = QHBoxLayout()
- flayout.addWidget(QLabel(figure_title, self))
- flayout.addItem(QSpacerItem(
- 20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
- flayout.addWidget(QLabel(figure_score, self, styleSheet="color: red;"))
- layout.addLayout(flayout)
-
- # 主演
- layout.addWidget(
- QLabel(figure_desc, self, styleSheet="color: #999999;", openExternalLinks=True))
-
- # 播放量
- blayout = QHBoxLayout()
- count_icon = QSvgWidget(self)
- count_icon.setMaximumSize(16, 16)
- count_icon.load(Svg_icon_play_sm)
- blayout.addWidget(count_icon)
- blayout.addWidget(
- QLabel(figure_count, self, styleSheet="color: #999999;"))
- layout.addLayout(blayout)
-
- def setCover(self, path):
- self.clabel.setCoverPath(path)
- self.clabel.setPixmap(QPixmap(path))
-# self.clabel.setText(' '.format(os.path.abspath(path)))
-
- def sizeHint(self):
- # 每个item控件的大小
- return QSize(220, 420)
-
- def event(self, event):
- if isinstance(event, QPaintEvent):
- if event.rect().height() > 20 and hasattr(self, "clabel"):
- if self.clabel.cover_path.find("pic_v.png") > -1: # 封面未加载
- # print("start download img:", self.cover_url)
- req = QNetworkRequest(QUrl(self.cover_url))
- # 设置两个自定义属性方便后期reply中处理
- req.setAttribute(QNetworkRequest.User + 1, self)
- req.setAttribute(QNetworkRequest.User + 2, self.img_path)
- self.parentWidget()._manager.get(req) # 调用父窗口中的下载器下载
- return super(ItemWidget, self).event(event)
-
-
class GridWidget(QWidget):
-
Page = 0
loadStarted = pyqtSignal(bool)
@@ -200,16 +53,18 @@ class GridWidget(QWidget):
self._manager.finished.connect(self.onFinished)
def load(self):
- if self.Page == -1:
+ if self.Page == -1 or self.Page > 10:
return
self.loadStarted.emit(True)
# 延迟一秒后调用目的在于显示进度条
QTimer.singleShot(1000, self._load)
def _load(self):
- print("load url:", Url.format(self.Page * 30))
- url = QUrl(Url.format(self.Page * 30))
- self._manager.get(QNetworkRequest(url))
+ print("load url:", Url.format(self.Page * 35))
+ url = QUrl(Url.format(self.Page * 35))
+ req = QNetworkRequest(url)
+ req.setRawHeader(b"User-Agent", Agent)
+ self._manager.get(req)
def onFinished(self, reply):
# 请求完成后会调用该函数
@@ -229,11 +84,13 @@ class GridWidget(QWidget):
self.loadStarted.emit(False)
def _parseHtml(self, html):
+ # print(html)
# encoding = chardet.detect(html) or {}
# html = html.decode(encoding.get("encoding","utf-8"))
html = HTML(html)
# 查找所有的li list_item
- lis = html.xpath("//li[@class='list_item']")
+ lis = html.xpath("//ul[@id='m-pl-container']/li")
+ # print(lis)
if not lis:
self.Page = -1 # 后面没有页面了
return
@@ -242,28 +99,30 @@ class GridWidget(QWidget):
def _makeItem(self, lis):
for li in lis:
- a = li.find("a")
- video_url = a.get("href") # 视频播放地址
- img = a.find("img")
- cover_url = "http:" + img.get("r-lazyload") # 封面图片
- figure_title = img.get("alt") # 电影名
- figure_info = a.find("div/span")
- figure_info = "" if figure_info is None else figure_info.text # 影片信息
- figure_score = "".join(li.xpath(".//em/text()")) # 评分
- # 主演
- figure_desc = "主演: " + \
- "".join([Actor.format(**dict(fd.items()))
- for fd in li.xpath(".//div[@class='figure_desc']/a")])
+ a = li.find('.//div/a')
+ play_url = "https://music.163.com" + a.get("href") # 歌单播放地址
+ img = li.find(".//div/img")
+ cover_url = img.get("src") # 封面图片
+ playlist_title = a.get("title") # 歌单名
+ # 歌手
+ author_info = li.xpath(".//p[2]/a")[0]
+ playlist_author = "".format(
+ Actor.format(href="https://music.163.com" +
+ author_info.get("href"),
+ title=author_info.get("title")))
# 播放数
- figure_count = (
- li.xpath(".//div[@class='figure_count']/span/text()") or [""])[0]
+ play_count = (li.xpath(".//div/div/span[2]/text()") or [""])[0]
path = "cache/{0}.jpg".format(
- os.path.splitext(os.path.basename(video_url))[0])
+ os.path.splitext(os.path.basename(cover_url).split('?')[0])[0])
cover_path = "Data/pic_v.png"
if os.path.isfile(path):
cover_path = path
- iwidget = ItemWidget(cover_path, figure_info, figure_title,
- figure_score, figure_desc, figure_count, video_url, cover_url, path, self)
+
+ # print(cover_path, playlist_title,
+ # playlist_author, play_count, play_url, cover_url, path)
+ iwidget = CoverItemWidget(self, manager=self._manager)
+ iwidget.init(cover_path, playlist_title, playlist_author,
+ play_count, play_url, cover_url, path)
self._layout.addWidget(iwidget)
@@ -283,9 +142,11 @@ class Window(QScrollArea):
# 连接竖着的滚动条滚动事件
self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered)
# 进度条
- self.loadWidget = QSvgWidget(
- self, minimumHeight=120, minimumWidth=120, visible=False)
- self.loadWidget.load(Svg_icon_loading)
+ self.loadWidget = QSvgWidget(self,
+ minimumHeight=120,
+ minimumWidth=120,
+ visible=False)
+ self.loadWidget.load('Data/Svg_icon_loading.svg')
def setLoadStarted(self, started):
self._loadStart = started
@@ -297,7 +158,8 @@ class Window(QScrollArea):
if action != QAbstractSlider.SliderMove or self._loadStart:
return
# 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断
- if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar().maximum():
+ if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar(
+ ).maximum():
# 可以下一页了
self._widget.load()
@@ -306,9 +168,7 @@ class Window(QScrollArea):
self.loadWidget.setGeometry(
int((self.width() - self.loadWidget.minimumWidth()) / 2),
int((self.height() - self.loadWidget.minimumHeight()) / 2),
- self.loadWidget.minimumWidth(),
- self.loadWidget.minimumHeight()
- )
+ self.loadWidget.minimumWidth(), self.loadWidget.minimumHeight())
if __name__ == "__main__":
diff --git a/QFlowLayout/Lib/CoverItemWidget.py b/QFlowLayout/Lib/CoverItemWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ef3ff00225af2269e33bd3c3ea4c47ba54c2adf
--- /dev/null
+++ b/QFlowLayout/Lib/CoverItemWidget.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/22
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CoverItemWidget.py
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import QSize, QUrl
+ from PyQt5.QtGui import QPaintEvent, QPixmap
+ from PyQt5.QtNetwork import QNetworkRequest
+ from PyQt5.QtWidgets import QWidget
+except ImportError:
+ from PySide2.QtCore import QSize, QUrl
+ from PySide2.QtGui import QPaintEvent, QPixmap
+ from PySide2.QtNetwork import QNetworkRequest
+ from PySide2.QtWidgets import QWidget
+
+from .Ui_CoverItemWidget import Ui_CoverItemWidget # @UnresolvedImport
+
+
+class CoverItemWidget(QWidget, Ui_CoverItemWidget):
+
+ def __init__(self, *args, **kwargs):
+ self._manager = kwargs.pop('manager', None)
+ super(CoverItemWidget, self).__init__(*args, **kwargs)
+ self.setupUi(self)
+
+ def init(self, cover_path, playlist_title, playlist_author, play_count,
+ play_url, cover_url, img_path):
+ self.img_path = img_path
+ self.cover_url = cover_url
+ # 图片label
+ self.labelCover.init(cover_path, play_url, play_count)
+
+ # 歌单
+ self.labelTitle.setText(playlist_title)
+
+ # 作者
+ self.labelAuthor.setText(playlist_author)
+
+ def setCover(self, path):
+ self.labelCover.setCoverPath(path)
+ self.labelCover.setPixmap(QPixmap(path))
+
+ def sizeHint(self):
+ # 每个item控件的大小
+ return QSize(200, 256)
+
+ def event(self, event):
+ if isinstance(event, QPaintEvent):
+ if event.rect().height() > 20 and hasattr(self, "labelCover"):
+ if self.labelCover.cover_path.find("pic_v.png") > -1: # 封面未加载
+ # print("start download img:", self.cover_url)
+ req = QNetworkRequest(QUrl(self.cover_url))
+ # 设置两个自定义属性方便后期reply中处理
+ req.setAttribute(QNetworkRequest.User + 1, self)
+ req.setAttribute(QNetworkRequest.User + 2, self.img_path)
+ if self._manager:
+ self._manager.get(req) # 调用父窗口中的下载器下载
+ return super(CoverItemWidget, self).event(event)
diff --git a/QFlowLayout/Lib/CoverLabel.py b/QFlowLayout/Lib/CoverLabel.py
new file mode 100644
index 0000000000000000000000000000000000000000..dbee15af4cf251de21d8d7d4194b18cb8a192bbc
--- /dev/null
+++ b/QFlowLayout/Lib/CoverLabel.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/22
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CoverLabel.py
+@description:
+"""
+import webbrowser
+
+try:
+ from PyQt5.QtGui import QPixmap
+ from PyQt5.QtWidgets import QLabel
+except ImportError:
+ from PySide2.QtGui import QPixmap
+ from PySide2.QtWidgets import QLabel
+
+from .Ui_CoverLabel import Ui_CoverLabel # @UnresolvedImport
+
+
+class CoverLabel(QLabel, Ui_CoverLabel):
+
+ def __init__(self, *args, **kwargs):
+ super(CoverLabel, self).__init__(*args, **kwargs)
+ self.setupUi(self)
+
+ def init(self, cover_path, play_url, play_count):
+ self.cover_path = cover_path
+ self.play_url = play_url
+ self.setPixmap(QPixmap(cover_path))
+ self.labelHeadset.setPixmap(QPixmap('Data/Svg_icon_headset_sm.svg'))
+ self.labelPlay.setPixmap(QPixmap('Data/Svg_icon_play_sm.svg'))
+ self.labelCount.setStyleSheet('color: #999999;')
+ self.labelCount.setText(play_count)
+
+ def setCoverPath(self, path):
+ self.cover_path = path
+
+ def mouseReleaseEvent(self, event):
+ super(CoverLabel, self).mouseReleaseEvent(event)
+ webbrowser.open_new_tab(self.play_url)
diff --git a/QFlowLayout/Lib/Ui_CoverItemWidget.py b/QFlowLayout/Lib/Ui_CoverItemWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..c048f686e4662268e855850c42d7da8a3182c91a
--- /dev/null
+++ b/QFlowLayout/Lib/Ui_CoverItemWidget.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'CoverItemWidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.2
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+try:
+ from PyQt5 import QtCore, QtGui, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtGui, QtWidgets
+
+
+class Ui_CoverItemWidget(object):
+ def setupUi(self, CoverItemWidget):
+ CoverItemWidget.setObjectName("CoverItemWidget")
+ CoverItemWidget.setMinimumSize(QtCore.QSize(200, 256))
+ CoverItemWidget.setMaximumSize(QtCore.QSize(200, 256))
+ CoverItemWidget.setWindowTitle("")
+ self.verticalLayout = QtWidgets.QVBoxLayout(CoverItemWidget)
+ self.verticalLayout.setContentsMargins(10, 10, 10, 10)
+ self.verticalLayout.setSpacing(12)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.labelCover = CoverLabel(CoverItemWidget)
+ self.labelCover.setMinimumSize(QtCore.QSize(180, 180))
+ self.labelCover.setText("")
+ self.labelCover.setScaledContents(True)
+ self.labelCover.setAlignment(QtCore.Qt.AlignCenter)
+ self.labelCover.setObjectName("labelCover")
+ self.verticalLayout.addWidget(self.labelCover)
+ self.labelTitle = QtWidgets.QLabel(CoverItemWidget)
+ font = QtGui.QFont()
+ font.setPointSize(10)
+ self.labelTitle.setFont(font)
+ self.labelTitle.setText("")
+ self.labelTitle.setObjectName("labelTitle")
+ self.verticalLayout.addWidget(self.labelTitle)
+ self.labelAuthor = QtWidgets.QLabel(CoverItemWidget)
+ self.labelAuthor.setText("")
+ self.labelAuthor.setObjectName("labelAuthor")
+ self.verticalLayout.addWidget(self.labelAuthor)
+
+ self.retranslateUi(CoverItemWidget)
+ QtCore.QMetaObject.connectSlotsByName(CoverItemWidget)
+
+ def retranslateUi(self, CoverItemWidget):
+ pass
+from .CoverLabel import CoverLabel
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ CoverItemWidget = QtWidgets.QWidget()
+ ui = Ui_CoverItemWidget()
+ ui.setupUi(CoverItemWidget)
+ CoverItemWidget.show()
+ sys.exit(app.exec_())
diff --git a/QFlowLayout/Lib/Ui_CoverLabel.py b/QFlowLayout/Lib/Ui_CoverLabel.py
new file mode 100644
index 0000000000000000000000000000000000000000..86e1526a548fbd165e68d695447c530166faaee1
--- /dev/null
+++ b/QFlowLayout/Lib/Ui_CoverLabel.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'CoverLabel.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.2
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+try:
+ from PyQt5 import QtCore, QtGui, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtGui, QtWidgets
+
+
+class Ui_CoverLabel(object):
+ def setupUi(self, CoverLabel):
+ CoverLabel.setObjectName("CoverLabel")
+ CoverLabel.resize(180, 180)
+ CoverLabel.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
+ CoverLabel.setWindowTitle("")
+ self.verticalLayout = QtWidgets.QVBoxLayout(CoverLabel)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ spacerItem = QtWidgets.QSpacerItem(20, 135, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem)
+ self.widgetBottom = QtWidgets.QWidget(CoverLabel)
+ self.widgetBottom.setStyleSheet("#widgetBottom {\n"
+" background-color: rgba(0, 0, 0, 150);\n"
+"}")
+ self.widgetBottom.setObjectName("widgetBottom")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.widgetBottom)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.labelHeadset = QtWidgets.QLabel(self.widgetBottom)
+ self.labelHeadset.setMinimumSize(QtCore.QSize(16, 16))
+ self.labelHeadset.setText("")
+ self.labelHeadset.setObjectName("labelHeadset")
+ self.horizontalLayout.addWidget(self.labelHeadset)
+ self.labelCount = QtWidgets.QLabel(self.widgetBottom)
+ self.labelCount.setText("")
+ self.labelCount.setObjectName("labelCount")
+ self.horizontalLayout.addWidget(self.labelCount)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem1)
+ self.labelPlay = QtWidgets.QLabel(self.widgetBottom)
+ self.labelPlay.setMinimumSize(QtCore.QSize(16, 16))
+ self.labelPlay.setText("")
+ self.labelPlay.setObjectName("labelPlay")
+ self.horizontalLayout.addWidget(self.labelPlay)
+ self.verticalLayout.addWidget(self.widgetBottom)
+
+ self.retranslateUi(CoverLabel)
+ QtCore.QMetaObject.connectSlotsByName(CoverLabel)
+
+ def retranslateUi(self, CoverLabel):
+ pass
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ CoverLabel = QtWidgets.QWidget()
+ ui = Ui_CoverLabel()
+ ui.setupUi(CoverLabel)
+ CoverLabel.show()
+ sys.exit(app.exec_())
diff --git a/QFlowLayout/Lib/flowlayout.py b/QFlowLayout/Lib/flowlayout.py
index ef1d54e3e5785c926aaa9e785815b5f5a56af43e..3cc9a9a382f1a42d0b1734bb56e60b15b10798bb 100644
--- a/QFlowLayout/Lib/flowlayout.py
+++ b/QFlowLayout/Lib/flowlayout.py
@@ -41,10 +41,14 @@
##
#############################################################################
-
-from PyQt5.QtCore import QPoint, QRect, QSize, Qt
-from PyQt5.QtWidgets import (QApplication, QLayout, QPushButton, QSizePolicy,
- QWidget)
+try:
+ from PyQt5.QtCore import QPoint, QRect, QSize, Qt
+ from PyQt5.QtWidgets import (QApplication, QLayout, QPushButton,
+ QSizePolicy, QWidget)
+except ImportError:
+ from PySide2.QtCore import QPoint, QRect, QSize, Qt
+ from PySide2.QtWidgets import (QApplication, QLayout, QPushButton,
+ QSizePolicy, QWidget)
class Window(QWidget):
@@ -152,7 +156,6 @@ class FlowLayout(QLayout):
if __name__ == '__main__':
-
import sys
app = QApplication(sys.argv)
diff --git a/QFlowLayout/README.md b/QFlowLayout/README.md
index 3b6e82015498ce398f832f3f4b7008fab105b9dc..9fa52ee63fd14ca8d94629f349e481643982a39c 100644
--- a/QFlowLayout/README.md
+++ b/QFlowLayout/README.md
@@ -1,6 +1,9 @@
# QListView
-## 1、腾讯视频热播列表
+- 目录
+ - [音乐热歌列表](#1音乐热歌列表)
+
+## 1、音乐热歌列表
[运行 HotPlaylist.py](HotPlaylist.py)
简单思路说明:
diff --git a/QFlowLayout/testxpath.py b/QFlowLayout/testxpath.py
new file mode 100644
index 0000000000000000000000000000000000000000..e167f2315f054d9b872e799e5ca59b7f55dca546
--- /dev/null
+++ b/QFlowLayout/testxpath.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/21
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: testxpath.py
+@description:
+"""
+
+import os
+
+from lxml.etree import HTML # @UnresolvedImport
+
+# 作者
+Actor = '''{title} '''
+
+
+def _makeItem(lis):
+ for li in lis:
+ a = li.find('.//div/a')
+ play_url = "https://music.163.com" + a.get("href") # 视频播放地址
+ img = li.find(".//div/img")
+ cover_url = img.get("src") # 封面图片
+ playlist_title = a.get("title") # 歌单名
+ # figure_info = a.find("div/span")
+ figure_info = "aaa" #if figure_info is None else figure_info.text # 影片信息
+ figure_score = "" # 评分
+ # 歌手
+ figure = li.xpath(".//p[2]/a")[0]
+ playlist_author = "".format(
+ Actor.format(href="https://music.163.com" +figure.get("href"),title=figure.get("title")))
+ # 播放数
+ play_count = (li.xpath(".//div/div/span[2]/text()") or [""])[0]
+ path = "cache/{0}.jpg".format(
+ os.path.splitext(os.path.basename(cover_url).split('?')[0])[0])
+ cover_path = "Data/pic_v.png"
+ if os.path.isfile(path):
+ cover_path = path
+
+ print(cover_path, playlist_title, playlist_author,
+ play_count, play_url, cover_url, path)
+ # iwidget = ItemWidget(cover_path, playlist_title,
+ # playlist_author, play_count, play_url,
+ # cover_url, path, self)
+ # self._layout.addWidget(iwidget)
+
+
+def _parseHtml(html):
+ html = HTML(html)
+ # 查找所有的li
+ lis = html.xpath("//ul[@id='m-pl-container']/li")
+ # if not lis:
+ # self.Page = -1 # 后面没有页面了
+ # return
+ # self.Page += 1
+ _makeItem(lis)
+
+
+if __name__ == '__main__':
+ data = open(r'D:\Computer\Desktop\163.html', 'rb').read()
+ _parseHtml(data)
\ No newline at end of file
diff --git a/QFont/AwesomeFont.py b/QFont/AwesomeFont.py
index 7d8098dee7cac51f39bb48ac57a40f5e989d6f2b..58b1baf23851439325acccadc6721028602d1cb7 100644
--- a/QFont/AwesomeFont.py
+++ b/QFont/AwesomeFont.py
@@ -1,24 +1,25 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年3月30日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: AwesomeFont
@description:
-'''
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+"""
import sys
-from PyQt5.QtGui import QFontDatabase, QFont
-from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout,\
- QScrollArea, QPushButton
+try:
+ from PyQt5.QtGui import QFontDatabase, QFont
+ from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, \
+ QScrollArea, QPushButton
+except ImportError:
+ from PySide2.QtGui import QFontDatabase, QFont
+ from PySide2.QtWidgets import QApplication, QWidget, QGridLayout, \
+ QScrollArea, QPushButton
from Lib.FontAwesome import FontAwesomes # @UnresolvedImport
@@ -44,7 +45,7 @@ class ScrollArea(QScrollArea):
minimumHeight=33,
font=QFont("FontAwesome", 14)),
row, col, 1, 1)
-
+
self.showMaximized()
def resizeEvent(self, event):
diff --git "a/QFont/Data/\346\217\220\345\217\226\345\255\227\347\254\246/get.py" "b/QFont/Data/\346\217\220\345\217\226\345\255\227\347\254\246/get.py"
index 624999c63527b542fe4b016caee34ba33072cc08..216d2e359cbc99b582fbfb277e9ef490c725ee6c 100644
--- "a/QFont/Data/\346\217\220\345\217\226\345\255\227\347\254\246/get.py"
+++ "b/QFont/Data/\346\217\220\345\217\226\345\255\227\347\254\246/get.py"
@@ -1,5 +1,6 @@
# from bs4 import BeautifulSoup
import re
+
cheatsheet = open("cheatsheet.txt", "rb").read().decode()
re_fa = re.compile(" fa(.*)")
diff --git a/QFont/Lib/FontAwesome.py b/QFont/Lib/FontAwesome.py
index 60ca9cae1425ef54eab234f0ebe7345fc1fea6b5..3527c2ea500f1c5cb8e3f27373c3f6b730ebf8d4 100644
--- a/QFont/Lib/FontAwesome.py
+++ b/QFont/Lib/FontAwesome.py
@@ -1,21 +1,22 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年3月30日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: FontAwesome
@description:
-'''
+"""
+
+
# FontAwesome 版本: 4.7.0
# 字体图标地址: http://fontawesome.io/icons/
# 字体字符地址: http://fontawesome.io/cheatsheet/
class FontAwesomes:
-
FA = None
@classmethod
diff --git a/QFont/README.md b/QFont/README.md
index 6cbd4933c963ad9468f0b186c6aa9c6311eec7dc..21a1296923c340bb45bcf123276ca248abce47a5 100644
--- a/QFont/README.md
+++ b/QFont/README.md
@@ -1,9 +1,11 @@
-# 字体测试
+# QFont
-### [Python3.4.4 or Python3.5][PyQt5]
+- 目录
+ - [加载自定义字体](#1加载自定义字体)
-### 其中Roboto字体通过TTF编辑器修改了family,方便QFont加载
+## 1、加载自定义字体
+[运行 AwesomeFont.py](AwesomeFont.py)
-# 截图
-
-
\ No newline at end of file
+通过`QFontDatabase.addApplicationFont`加载字体文件
+
+
\ No newline at end of file
diff --git a/QFont/ScreenShot/2.png b/QFont/ScreenShot/2.png
deleted file mode 100644
index c5f4b9db6c0fe69c87766b4cb7a5a82d4bd257d9..0000000000000000000000000000000000000000
Binary files a/QFont/ScreenShot/2.png and /dev/null differ
diff --git a/QFont/ScreenShot/1.png b/QFont/ScreenShot/AwesomeFont.png
similarity index 100%
rename from QFont/ScreenShot/1.png
rename to QFont/ScreenShot/AwesomeFont.png
diff --git a/QGraphicsDropShadowEffect/Lib/AnimationShadowEffect.py b/QGraphicsDropShadowEffect/Lib/AnimationShadowEffect.py
index 9cbbd75920e7f30a177ad3806757fc7c13afb5ed..4551bd4f924543330c2f3cd9c41b15a5ce6d79cf 100644
--- a/QGraphicsDropShadowEffect/Lib/AnimationShadowEffect.py
+++ b/QGraphicsDropShadowEffect/Lib/AnimationShadowEffect.py
@@ -4,20 +4,18 @@
"""
Created on 2018年9月25日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: AnimationShadowEffect
@description: 边框动画阴影动画
"""
-from PyQt5.QtCore import QPropertyAnimation, pyqtProperty
-from PyQt5.QtWidgets import QGraphicsDropShadowEffect
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import QPropertyAnimation, pyqtProperty
+ from PyQt5.QtWidgets import QGraphicsDropShadowEffect
+except ImportError:
+ from PySide2.QtCore import QPropertyAnimation, Property as pyqtProperty
+ from PySide2.QtWidgets import QGraphicsDropShadowEffect
class AnimationShadowEffect(QGraphicsDropShadowEffect):
diff --git a/QGraphicsDropShadowEffect/README.md b/QGraphicsDropShadowEffect/README.md
index bfb723f1556e3170e4e8eb75083f972eb7f6e064..6e5be6fbf66bbbbfaafb18244d948da5f10044df 100644
--- a/QGraphicsDropShadowEffect/README.md
+++ b/QGraphicsDropShadowEffect/README.md
@@ -1,5 +1,8 @@
# QGraphicsDropShadowEffect
+- 目录
+ - [边框阴影动画](#1边框阴影动画)
+
## 1、边框阴影动画
[运行 ShadowEffect.py](ShadowEffect.py)
diff --git a/QGraphicsDropShadowEffect/ShadowEffect.py b/QGraphicsDropShadowEffect/ShadowEffect.py
index 9c03cc6effc4e3b38052ed1722928b6cdb30cea3..b121e852fbc4765b69d24e157196375412378219 100644
--- a/QGraphicsDropShadowEffect/ShadowEffect.py
+++ b/QGraphicsDropShadowEffect/ShadowEffect.py
@@ -4,23 +4,22 @@
"""
Created on 2018年9月25日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ShadowEffect
@description:
"""
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QPixmap
-from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QPushButton, QLineEdit
-
-from Lib.AnimationShadowEffect import AnimationShadowEffect # @UnresolvedImport
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QPixmap
+ from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QPushButton, QLineEdit, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QPixmap
+ from PySide2.QtWidgets import QWidget, QHBoxLayout, QLabel, QPushButton, QLineEdit, QApplication
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+from Lib.AnimationShadowEffect import AnimationShadowEffect # @UnresolvedImport
class Window(QWidget):
@@ -64,7 +63,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QGraphicsView/AddQWidget.py b/QGraphicsView/AddQWidget.py
index 1689972cd2418aefbe000234b35768480ae3deb0..ff8026c93258e5406f651ae4d3b75b78cd171deb 100644
--- a/QGraphicsView/AddQWidget.py
+++ b/QGraphicsView/AddQWidget.py
@@ -3,22 +3,22 @@
"""
Created on 2017年12月23日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: AddQWidget
@description:
"""
import sys
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QVBoxLayout,\
- QApplication, QGraphicsView, QGraphicsScene
-
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QVBoxLayout, \
+ QApplication, QGraphicsView, QGraphicsScene
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QWidget, QHBoxLayout, QLabel, QVBoxLayout, \
+ QApplication, QGraphicsView, QGraphicsScene
class ToolTipItem(QWidget):
diff --git a/QGraphicsView/Data/bg.jpg b/QGraphicsView/Data/bg.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..db164572913a0c85bb9f0c8687e097dd82dcf19a
Binary files /dev/null and b/QGraphicsView/Data/bg.jpg differ
diff --git a/QGraphicsView/Data/icons/basic.png b/QGraphicsView/Data/icons/basic.png
new file mode 100644
index 0000000000000000000000000000000000000000..7627b2449bb89276ee65afbf55cbf2ef74adf4c0
Binary files /dev/null and b/QGraphicsView/Data/icons/basic.png differ
diff --git a/QGraphicsView/Data/icons/business.png b/QGraphicsView/Data/icons/business.png
new file mode 100644
index 0000000000000000000000000000000000000000..479d2e0dd330a6bdfca8657c637aa377b12dbd31
Binary files /dev/null and b/QGraphicsView/Data/icons/business.png differ
diff --git a/QGraphicsView/Data/icons/cloudService.png b/QGraphicsView/Data/icons/cloudService.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c3579f1d2b1a1777a78084285cef0ccb0a68e67
Binary files /dev/null and b/QGraphicsView/Data/icons/cloudService.png differ
diff --git a/QGraphicsView/Data/icons/dataBase.png b/QGraphicsView/Data/icons/dataBase.png
new file mode 100644
index 0000000000000000000000000000000000000000..716e0cff90aae747505c2bcf777cb0d6f62f4a1a
Binary files /dev/null and b/QGraphicsView/Data/icons/dataBase.png differ
diff --git a/QGraphicsView/Data/icons/engineering.png b/QGraphicsView/Data/icons/engineering.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c68bd454db1912a2dbe70b7f3361ddde6d43f59
Binary files /dev/null and b/QGraphicsView/Data/icons/engineering.png differ
diff --git a/QGraphicsView/Data/icons/network.png b/QGraphicsView/Data/icons/network.png
new file mode 100644
index 0000000000000000000000000000000000000000..dde507a2d3d4a14ce57ec6cb4d13c42a14b5b6a1
Binary files /dev/null and b/QGraphicsView/Data/icons/network.png differ
diff --git a/QGraphicsView/Data/icons/science.png b/QGraphicsView/Data/icons/science.png
new file mode 100644
index 0000000000000000000000000000000000000000..01c377119005818e41dfa1c6c5aa1ac6b9afae7a
Binary files /dev/null and b/QGraphicsView/Data/icons/science.png differ
diff --git a/QGraphicsView/Data/icons/softwareEngineering.png b/QGraphicsView/Data/icons/softwareEngineering.png
new file mode 100644
index 0000000000000000000000000000000000000000..51be32213141501a87f919e75f12b19f51385d1a
Binary files /dev/null and b/QGraphicsView/Data/icons/softwareEngineering.png differ
diff --git a/QGraphicsView/Data/icons/wireframe.png b/QGraphicsView/Data/icons/wireframe.png
new file mode 100644
index 0000000000000000000000000000000000000000..040e0f422be16187190555da374452244779abec
Binary files /dev/null and b/QGraphicsView/Data/icons/wireframe.png differ
diff --git a/QGraphicsView/DragGraphics.py b/QGraphicsView/DragGraphics.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ba5606080c0c83104b53d3d9ed1a5f5476ec862
--- /dev/null
+++ b/QGraphicsView/DragGraphics.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/09
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: DragGraphics.py
+@description:
+"""
+
+import json
+import os
+
+try:
+ from PyQt5.QtCore import QMimeData, Qt
+ from PyQt5.QtGui import QDrag, QIcon, QPixmap
+ from PyQt5.QtWidgets import (QApplication, QGraphicsPixmapItem,
+ QGraphicsScene, QGraphicsView, QHBoxLayout,
+ QListWidget, QListWidgetItem, QTreeWidget,
+ QTreeWidgetItem, QWidget)
+except ImportError:
+ from PySide2.QtCore import QMimeData, Qt
+ from PySide2.QtGui import QDrag, QIcon, QPixmap
+ from PySide2.QtWidgets import (QApplication, QGraphicsPixmapItem,
+ QGraphicsScene, QGraphicsView, QHBoxLayout,
+ QListWidget, QListWidgetItem, QTreeWidget,
+ QTreeWidgetItem, QWidget)
+
+
+class ListWidget(QListWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(ListWidget, self).__init__(*args, **kwargs)
+ self.setDragEnabled(True)
+ self.setDragDropMode(QListWidget.DragOnly)
+ self.setDefaultDropAction(Qt.IgnoreAction)
+ self.setEditTriggers(QListWidget.NoEditTriggers)
+ self.setResizeMode(QListWidget.Adjust)
+ self.setViewMode(QListWidget.IconMode)
+
+ def startDrag(self, supportedActions):
+ items = self.selectedItems()
+ if not items:
+ return
+ # 这里就简单的根据名字提示来传递数据了,实际上可以传递任意数据
+ data = QMimeData()
+ data.setData('application/node-items',
+ json.dumps([item.toolTip() for item in items]).encode())
+ # 这里简单显示第一个缩略图
+ pixmap = items[0].icon().pixmap(36, 36)
+ drag = QDrag(self)
+ drag.setMimeData(data)
+ drag.setPixmap(pixmap)
+ drag.setHotSpot(pixmap.rect().center())
+ drag.exec_(supportedActions)
+
+
+class GraphicsView(QGraphicsView):
+
+ def __init__(self, *args, **kwargs):
+ super(GraphicsView, self).__init__(*args, **kwargs)
+ self.setAcceptDrops(True)
+ self._scene = QGraphicsScene(self) # 场景
+ self.setScene(self._scene)
+
+ def dragEnterEvent(self, event):
+ """判断拖入的数据是否支持"""
+ mimeData = event.mimeData()
+ if not mimeData.hasFormat('application/node-items'):
+ event.ignore()
+ return
+
+ event.acceptProposedAction()
+
+ dragMoveEvent = dragEnterEvent
+
+ def dropEvent(self, event):
+ """获取拖拽的数据并绘制对于的图形"""
+ datas = event.mimeData().data('application/node-items')
+ datas = json.loads(datas.data().decode())
+ print('datas:', datas)
+
+ path = os.path.join(os.path.dirname(__file__), 'Data/icons')
+ for name in datas:
+ item = QGraphicsPixmapItem(QPixmap(os.path.join(path, name)))
+ item.setFlags(QGraphicsPixmapItem.ItemIsFocusable |
+ QGraphicsPixmapItem.ItemIsMovable)
+ self._scene.addItem(item)
+ pos = self.mapToScene(event.pos())
+ item.moveBy(pos.x(), pos.y())
+
+
+class DragGraphics(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(DragGraphics, self).__init__(*args, **kwargs)
+ self.resize(800, 600)
+ layout = QHBoxLayout(self)
+
+ # 左侧树形控制
+ self.treeWidget = QTreeWidget(self)
+ self.treeWidget.header().setVisible(False)
+ self.treeWidget.setMaximumWidth(300)
+ layout.addWidget(self.treeWidget)
+
+ # 右侧图形显示
+ self.graphicsView = GraphicsView(self)
+ layout.addWidget(self.graphicsView)
+
+ self._init_trees()
+
+ def _init_trees(self):
+ """初始化树形控件中的图形节点列表"""
+ # 1. 获取所有图标
+ path = os.path.join(os.path.dirname(__file__), 'Data/icons')
+ icons = [os.path.join(path, name) for name in os.listdir(path)]
+
+ # 2. 添加根节点
+ for i in range(2):
+ item = QTreeWidgetItem(self.treeWidget)
+ item.setText(0, 'View %d' % i)
+
+ # 3. 添加子节点作为容器用于存放图标
+ itemc = QTreeWidgetItem(item)
+ child = ListWidget(self.treeWidget)
+ self.treeWidget.setItemWidget(itemc, 0, child)
+
+ # 4. 添加图标
+ for icon in icons:
+ item = QListWidgetItem(child)
+ item.setIcon(QIcon(icon))
+ item.setToolTip(os.path.basename(icon))
+
+ self.treeWidget.expandAll()
+
+
+if __name__ == '__main__':
+ import cgitb
+ import sys
+
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = DragGraphics()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QGraphicsView/ImageView.py b/QGraphicsView/ImageView.py
new file mode 100644
index 0000000000000000000000000000000000000000..f6789c54ba7002654fb810b715dff05a7d93d808
--- /dev/null
+++ b/QGraphicsView/ImageView.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020/11/12
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ImageView
+@description: 图片查看控件,支持移动、放大、缩小
+"""
+
+import os
+
+try:
+ from PyQt5.QtCore import QPointF, Qt, QRectF, QSizeF
+ from PyQt5.QtGui import QPainter, QColor, QImage, QPixmap
+ from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsPixmapItem, QGraphicsScene
+except ImportError:
+ from PySide2.QtCore import QPointF, Qt, QRectF, QSizeF
+ from PySide2.QtGui import QPainter, QColor, QImage, QPixmap
+ from PySide2.QtWidgets import QApplication, QGraphicsView, QGraphicsPixmapItem, QGraphicsScene
+
+
+class ImageView(QGraphicsView):
+ """图片查看控件"""
+
+ def __init__(self, *args, **kwargs):
+ image = kwargs.pop('image', None)
+ background = kwargs.pop('background', None)
+ super(ImageView, self).__init__(*args, **kwargs)
+ self.setCursor(Qt.OpenHandCursor)
+ self.setBackground(background)
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing |
+ QPainter.SmoothPixmapTransform)
+ self.setCacheMode(self.CacheBackground)
+ self.setViewportUpdateMode(self.SmartViewportUpdate)
+ self._item = QGraphicsPixmapItem() # 放置图像
+ self._item.setFlags(QGraphicsPixmapItem.ItemIsFocusable |
+ QGraphicsPixmapItem.ItemIsMovable)
+ self._scene = QGraphicsScene(self) # 场景
+ self.setScene(self._scene)
+ self._scene.addItem(self._item)
+ rect = QApplication.instance().desktop().availableGeometry(self)
+ self.resize(int(rect.width() * 2 / 3), int(rect.height() * 2 / 3))
+
+ self.pixmap = None
+ self._delta = 0.1 # 缩放
+ self.setPixmap(image)
+
+ def setBackground(self, color):
+ """设置背景颜色
+ :param color: 背景颜色
+ :type color: QColor or str or GlobalColor
+ """
+ if isinstance(color, QColor):
+ self.setBackgroundBrush(color)
+ elif isinstance(color, (str, Qt.GlobalColor)):
+ color = QColor(color)
+ if color.isValid():
+ self.setBackgroundBrush(color)
+
+ def setPixmap(self, pixmap, fitIn=True):
+ """加载图片
+ :param pixmap: 图片或者图片路径
+ :param fitIn: 是否适应
+ :type pixmap: QPixmap or QImage or str
+ :type fitIn: bool
+ """
+ if isinstance(pixmap, QPixmap):
+ self.pixmap = pixmap
+ elif isinstance(pixmap, QImage):
+ self.pixmap = QPixmap.fromImage(pixmap)
+ elif isinstance(pixmap, str) and os.path.isfile(pixmap):
+ self.pixmap = QPixmap(pixmap)
+ else:
+ return
+ self._item.setPixmap(self.pixmap)
+ self._item.update()
+ self.setSceneDims()
+ if fitIn:
+ self.fitInView(QRectF(self._item.pos(), QSizeF(
+ self.pixmap.size())), Qt.KeepAspectRatio)
+ self.update()
+
+ def setSceneDims(self):
+ if not self.pixmap:
+ return
+ self.setSceneRect(QRectF(QPointF(0, 0), QPointF(self.pixmap.width(), self.pixmap.height())))
+
+ def fitInView(self, rect, flags=Qt.IgnoreAspectRatio):
+ """剧中适应
+ :param rect: 矩形范围
+ :param flags:
+ :return:
+ """
+ if not self.scene() or rect.isNull():
+ return
+ unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
+ self.scale(1 / unity.width(), 1 / unity.height())
+ viewRect = self.viewport().rect()
+ sceneRect = self.transform().mapRect(rect)
+ x_ratio = viewRect.width() / sceneRect.width()
+ y_ratio = viewRect.height() / sceneRect.height()
+ if flags == Qt.KeepAspectRatio:
+ x_ratio = y_ratio = min(x_ratio, y_ratio)
+ elif flags == Qt.KeepAspectRatioByExpanding:
+ x_ratio = y_ratio = max(x_ratio, y_ratio)
+ self.scale(x_ratio, y_ratio)
+ self.centerOn(rect.center())
+
+ def wheelEvent(self, event):
+ if event.angleDelta().y() > 0:
+ self.zoomIn()
+ else:
+ self.zoomOut()
+
+ def zoomIn(self):
+ """放大"""
+ self.zoom(1 + self._delta)
+
+ def zoomOut(self):
+ """缩小"""
+ self.zoom(1 - self._delta)
+
+ def zoom(self, factor):
+ """缩放
+ :param factor: 缩放的比例因子
+ """
+ _factor = self.transform().scale(
+ factor, factor).mapRect(QRectF(0, 0, 1, 1)).width()
+ if _factor < 0.07 or _factor > 100:
+ # 防止过大过小
+ return
+ self.scale(factor, factor)
+
+
+if __name__ == '__main__':
+ import sys
+ import cgitb
+
+ cgitb.enable(format='text')
+
+ app = QApplication(sys.argv)
+ w = ImageView(image='Data/bg.jpg', background=Qt.black)
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QGraphicsView/README.md b/QGraphicsView/README.md
index 52901d4ad28e96adce0b49b2893ce9894f7ee86b..ef66543d52f69139236c896b928c91a6c22631a3 100644
--- a/QGraphicsView/README.md
+++ b/QGraphicsView/README.md
@@ -1,5 +1,11 @@
# QGraphicsView
+- 目录
+ - [绘制世界地图](#1绘制世界地图)
+ - [添加QWidget](#2添加QWidget)
+ - [图片查看器](#3图片查看器)
+ - [图标拖拽](#4图标拖拽)
+
## 1、绘制世界地图
[运行 WorldMap.py](WorldMap.py)
@@ -13,4 +19,21 @@
通过 `QGraphicsScene.addWidget` 添加自定义QWidget
-
\ No newline at end of file
+
+
+## 3、图片查看器
+[运行 ImageView.py](ImageView.py)
+
+支持放大缩小和移动
+
+
+
+## 3、图标拖拽
+[运行 DragGraphics.py](DragGraphics.py)
+
+该示例主要是包含左侧树状图标列表和右侧视图显示,从左侧拖拽到右侧
+
+1. 重写`QListWidget`的`startDrag`函数用来封装拖拽数据
+2. 重写`QGraphicsView`的`dragEnterEvent`、`dragMoveEvent`、`dropEvent`函数用来处理拖拽事件
+
+
\ No newline at end of file
diff --git a/QGraphicsView/ScreenShot/DragGraphics.gif b/QGraphicsView/ScreenShot/DragGraphics.gif
new file mode 100644
index 0000000000000000000000000000000000000000..65f0c4ba67b67f97b7aec7081f8617f5b7382f59
Binary files /dev/null and b/QGraphicsView/ScreenShot/DragGraphics.gif differ
diff --git a/QGraphicsView/ScreenShot/ImageView.gif b/QGraphicsView/ScreenShot/ImageView.gif
new file mode 100644
index 0000000000000000000000000000000000000000..150634c25f24d0a74e01e9e43004f1ad973232ef
Binary files /dev/null and b/QGraphicsView/ScreenShot/ImageView.gif differ
diff --git a/QGraphicsView/WorldMap.py b/QGraphicsView/WorldMap.py
index 3efc7b17479ca1d818235ee4ba78ab3539703667..32428dfba457683e39a36092710885ce681e99c1 100644
--- a/QGraphicsView/WorldMap.py
+++ b/QGraphicsView/WorldMap.py
@@ -3,8 +3,8 @@
"""
Created on 2017年12月17日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: WorldMap
@description:
@@ -12,19 +12,19 @@ Created on 2017年12月17日
import json
import math
-from PyQt5.QtCore import Qt, QPointF, QRectF
-from PyQt5.QtGui import QColor, QPainter, QPolygonF, QPen, QBrush
-from PyQt5.QtOpenGL import QGLFormat
-from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPolygonItem
-
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt, QPointF, QRectF
+ from PyQt5.QtGui import QColor, QPainter, QPolygonF, QPen, QBrush
+ from PyQt5.QtOpenGL import QGLFormat
+ from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsPolygonItem
+except ImportError:
+ from PySide2.QtCore import Qt, QPointF, QRectF
+ from PySide2.QtGui import QColor, QPainter, QPolygonF, QPen, QBrush
+ from PySide2.QtOpenGL import QGLFormat
+ from PySide2.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsPolygonItem
class GraphicsView(QGraphicsView):
-
# 背景区域颜色
backgroundColor = QColor(31, 31, 47)
# 边框颜色
@@ -94,8 +94,8 @@ class GraphicsView(QGraphicsView):
AnchorUnderMouse 鼠标当前位置被用作锚点
'''
self.setTransformationAnchor(self.AnchorUnderMouse)
-# if QGLFormat.hasOpenGL(): # 如果开启了OpenGL则使用OpenGL Widget
-# self.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers)))
+ # if QGLFormat.hasOpenGL(): # 如果开启了OpenGL则使用OpenGL Widget
+ # self.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers)))
'''
#参考 http://doc.qt.io/qt-5/qgraphicsview.html#ViewportUpdateMode-enum
FullViewportUpdate 当场景的任何可见部分改变或重新显示时,QGraphicsView将更新整个视口。 当QGraphicsView花费更多的时间来计算绘制的内容(比如重复更新很多小项目)时,这种方法是最快的。 这是不支持部分更新(如QGLWidget)的视口以及需要禁用滚动优化的视口的首选更新模式。
@@ -157,7 +157,7 @@ class GraphicsView(QGraphicsView):
if __name__ == "__main__":
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
print("OpenGL Status:", QGLFormat.hasOpenGL())
view = GraphicsView()
diff --git a/QGridLayout/Data/CoverItemWidget.ui b/QGridLayout/Data/CoverItemWidget.ui
new file mode 100644
index 0000000000000000000000000000000000000000..2f245e1344895af124b9ed3ea8b387c232077a79
--- /dev/null
+++ b/QGridLayout/Data/CoverItemWidget.ui
@@ -0,0 +1,85 @@
+
+
+ CoverItemWidget
+
+
+
+ 200
+ 256
+
+
+
+
+ 200
+ 256
+
+
+
+
+
+
+
+ 12
+
+
+ 10
+
+
+ 10
+
+
+ 10
+
+
+ 10
+
+ -
+
+
+
+ 180
+ 180
+
+
+
+
+
+
+ true
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 10
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ CoverLabel
+ QLabel
+
+
+
+
+
+
diff --git a/QGridLayout/Data/CoverLabel.ui b/QGridLayout/Data/CoverLabel.ui
new file mode 100644
index 0000000000000000000000000000000000000000..2e7e037880e23bace4731623a4f1c25620fe267b
--- /dev/null
+++ b/QGridLayout/Data/CoverLabel.ui
@@ -0,0 +1,109 @@
+
+
+ CoverLabel
+
+
+
+ 0
+ 0
+ 180
+ 180
+
+
+
+ PointingHandCursor
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 135
+
+
+
+
+ -
+
+
+ #widgetBottom {
+ background-color: rgba(0, 0, 0, 150);
+}
+
+
+ -
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/QGridLayout/Data/Svg_icon_headset_sm.svg b/QGridLayout/Data/Svg_icon_headset_sm.svg
new file mode 100644
index 0000000000000000000000000000000000000000..98a1a3591d4f0741b27d27862023bc75bddf4a8d
--- /dev/null
+++ b/QGridLayout/Data/Svg_icon_headset_sm.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/QGridLayout/Data/Svg_icon_loading.svg b/QGridLayout/Data/Svg_icon_loading.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0b00b7993dc84f2a6221b942d35dc62421e70ac4
--- /dev/null
+++ b/QGridLayout/Data/Svg_icon_loading.svg
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QGridLayout/Data/Svg_icon_play_sm.svg b/QGridLayout/Data/Svg_icon_play_sm.svg
new file mode 100644
index 0000000000000000000000000000000000000000..08a47f20e76885d4e77b0f31221ff4d9e8bacafd
--- /dev/null
+++ b/QGridLayout/Data/Svg_icon_play_sm.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/QGridLayout/HotPlaylist.py b/QGridLayout/HotPlaylist.py
index a2022c2ca5be6bc0a9fca01c867db6ebe03a3999..ff5500ad0412d47b7ddf8530063f971219341c0a 100644
--- a/QGridLayout/HotPlaylist.py
+++ b/QGridLayout/HotPlaylist.py
@@ -1,192 +1,45 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-
-'''
-Created on 2018年2月4日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+"""
+Created on 2023/02/22
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
@email: 892768447@qq.com
-@file: TencentMovieHotPlay
-@description:
-'''
+@file: HotPlaylist.py
+@description:
+"""
+
import os
import sys
-import webbrowser
-
-from PyQt5.QtCore import QSize, Qt, QUrl, QTimer, pyqtSignal
-from PyQt5.QtGui import QPainter, QFont, QLinearGradient, QGradient, QColor,\
- QBrush, QPaintEvent, QPixmap
-from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
-from PyQt5.QtSvg import QSvgWidget
-from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel,\
- QHBoxLayout, QSpacerItem, QSizePolicy, QScrollArea, QGridLayout,\
- QAbstractSlider
+from Lib.CoverItemWidget import CoverItemWidget
from lxml.etree import HTML # @UnresolvedImport
+try:
+ from PyQt5.QtCore import Qt, QTimer, QUrl, pyqtSignal
+ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
+ from PyQt5.QtSvg import QSvgWidget
+ from PyQt5.QtWidgets import (QAbstractSlider, QApplication, QGridLayout,
+ QScrollArea, QWidget)
+except ImportError:
+ from PySide2.QtCore import Qt, QTimer, QUrl, pyqtSignal
+ from PySide2.QtNetwork import QNetworkAccessManager, QNetworkRequest
+ from PySide2.QtSvg import QSvgWidget
+ from PySide2.QtWidgets import (QAbstractSlider, QApplication, QGridLayout,
+ QScrollArea, QWidget)
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+# offset=0,35,70,105
+Url = "https://music.163.com/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset={0}"
-# offset=0,30,60,90
-Url = "http://v.qq.com/x/list/movie?pay=-1&offset={0}"
+Agent = b"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.50"
-# 播放量图标
-Svg_icon_play_sm = '''
-
-
-'''.encode()
+Referer = b"https://music.163.com"
-Svg_icon_loading = '''
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- '''.encode()
-
-# 主演
+# 作者
Actor = '''{title} '''
-class CoverLabel(QLabel):
-
- def __init__(self, cover_path, cover_title, video_url, *args, **kwargs):
- super(CoverLabel, self).__init__(*args, **kwargs)
- self.setCursor(Qt.PointingHandCursor)
- self.setScaledContents(True)
- self.setMinimumSize(220, 308)
- self.setMaximumSize(220, 308)
- self.cover_path = cover_path
- self.cover_title = cover_title
- self.video_url = video_url
- self.setPixmap(QPixmap(cover_path))
-
- def setCoverPath(self, path):
- self.cover_path = path
-
- def mouseReleaseEvent(self, event):
- super(CoverLabel, self).mouseReleaseEvent(event)
- webbrowser.open_new_tab(self.video_url)
-
- def paintEvent(self, event):
- super(CoverLabel, self).paintEvent(event)
- if hasattr(self, "cover_title") and self.cover_title != "":
- # 底部绘制文字
- painter = QPainter(self)
- rect = self.rect()
- # 粗略字体高度
- painter.save()
- fheight = self.fontMetrics().height()
- # 底部矩形框背景渐变颜色
- bottomRectColor = QLinearGradient(
- rect.width() / 2, rect.height() - 24 - fheight,
- rect.width() / 2, rect.height())
- bottomRectColor.setSpread(QGradient.PadSpread)
- bottomRectColor.setColorAt(0, QColor(255, 255, 255, 70))
- bottomRectColor.setColorAt(1, QColor(0, 0, 0, 50))
- # 画半透明渐变矩形框
- painter.setPen(Qt.NoPen)
- painter.setBrush(QBrush(bottomRectColor))
- painter.drawRect(rect.x(), rect.height() - 24 -
- fheight, rect.width(), 24 + fheight)
- painter.restore()
- # 距离底部一定高度画文字
- font = self.font() or QFont()
- font.setPointSize(8)
- painter.setFont(font)
- painter.setPen(Qt.white)
- rect.setHeight(rect.height() - 12) # 底部减去一定高度
- painter.drawText(rect, Qt.AlignHCenter |
- Qt.AlignBottom, self.cover_title)
-
-
-class ItemWidget(QWidget):
-
- def __init__(self, cover_path, figure_info, figure_title,
- figure_score, figure_desc, figure_count, video_url, cover_url, img_path, *args, **kwargs):
- super(ItemWidget, self).__init__(*args, **kwargs)
- self.setMaximumSize(220, 380)
- self.setMaximumSize(220, 380)
- self.img_path = img_path
- self.cover_url = cover_url
- layout = QVBoxLayout(self)
- layout.setContentsMargins(0, 0, 0, 0)
- # 图片label
- self.clabel = CoverLabel(cover_path, figure_info, video_url, self)
- layout.addWidget(self.clabel)
-
- # 片名和分数
- flayout = QHBoxLayout()
- flayout.addWidget(QLabel(figure_title, self))
- flayout.addItem(QSpacerItem(
- 20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
- flayout.addWidget(QLabel(figure_score, self, styleSheet="color: red;"))
- layout.addLayout(flayout)
-
- # 主演
- layout.addWidget(
- QLabel(figure_desc, self, styleSheet="color: #999999;", openExternalLinks=True))
-
- # 播放量
- blayout = QHBoxLayout()
- count_icon = QSvgWidget(self)
- count_icon.setMaximumSize(16, 16)
- count_icon.load(Svg_icon_play_sm)
- blayout.addWidget(count_icon)
- blayout.addWidget(
- QLabel(figure_count, self, styleSheet="color: #999999;"))
- layout.addLayout(blayout)
-
- def setCover(self, path):
- self.clabel.setCoverPath(path)
- self.clabel.setPixmap(QPixmap(path))
-# self.clabel.setText(' '.format(os.path.abspath(path)))
-
- def sizeHint(self):
- # 每个item控件的大小
- return QSize(220, 380)
-
- def event(self, event):
- if isinstance(event, QPaintEvent):
- if event.rect().height() > 20 and hasattr(self, "clabel"):
- if self.clabel.cover_path.find("pic_v.png") > -1: # 封面未加载
- # print("start download img:", self.cover_url)
- req = QNetworkRequest(QUrl(self.cover_url))
- # 设置两个自定义属性方便后期reply中处理
- req.setAttribute(QNetworkRequest.User + 1, self)
- req.setAttribute(QNetworkRequest.User + 2, self.img_path)
- self.parentWidget()._manager.get(req) # 调用父窗口中的下载器下载
- return super(ItemWidget, self).event(event)
-
-
class GridWidget(QWidget):
-
Page = 0
loadStarted = pyqtSignal(bool)
@@ -199,16 +52,18 @@ class GridWidget(QWidget):
self._manager.finished.connect(self.onFinished)
def load(self):
- if self.Page == -1:
+ if self.Page == -1 or self.Page > 10:
return
self.loadStarted.emit(True)
# 延迟一秒后调用目的在于显示进度条
QTimer.singleShot(1000, self._load)
def _load(self):
- print("load url:", Url.format(self.Page * 30))
- url = QUrl(Url.format(self.Page * 30))
- self._manager.get(QNetworkRequest(url))
+ print("load url:", Url.format(self.Page * 35))
+ url = QUrl(Url.format(self.Page * 35))
+ req = QNetworkRequest(url)
+ req.setRawHeader(b"User-Agent", Agent)
+ self._manager.get(req)
def onFinished(self, reply):
# 请求完成后会调用该函数
@@ -232,19 +87,21 @@ class GridWidget(QWidget):
return (src[i:i + length] for i in range(len(src)) if i % length == 0)
def _parseHtml(self, html):
+ # print(html)
# encoding = chardet.detect(html) or {}
# html = html.decode(encoding.get("encoding","utf-8"))
html = HTML(html)
# 查找所有的li list_item
- lis = html.xpath("//li[@class='list_item']")
+ lis = html.xpath("//ul[@id='m-pl-container']/li")
+ # print(lis)
if not lis:
self.Page = -1 # 后面没有页面了
return
- lack_count = self._layout.count() % 30 # 获取布局中上次还缺几个5行*6列的标准
- row_count = int(self._layout.count() / 6) # 行数
+ lack_count = self._layout.count() % 35 # 获取布局中上次还缺几个5行*6列的标准
+ row_count = int(self._layout.count() / 5) # 行数
print("lack_count:", lack_count)
self.Page += 1 # 自增+1
- if lack_count != 0: # 上一次没有满足一行6个,需要补齐
+ if lack_count != 0: # 上一次没有满足一行5个,需要补齐
lack_li = lis[:lack_count]
lis = lis[lack_count:]
self._makeItem(lack_li, row_count) # 补齐
@@ -255,31 +112,34 @@ class GridWidget(QWidget):
self._makeItem(lis, row_count)
def _makeItem(self, li_s, row_count):
- li_s = self.splist(li_s, 6)
+ li_s = self.splist(li_s, 5)
for row, lis in enumerate(li_s):
for col, li in enumerate(lis):
- a = li.find("a")
- video_url = a.get("href") # 视频播放地址
- img = a.find("img")
- cover_url = "http:" + img.get("r-lazyload") # 封面图片
- figure_title = img.get("alt") # 电影名
- figure_info = a.find("div/span")
- figure_info = "" if figure_info is None else figure_info.text # 影片信息
- figure_score = "".join(li.xpath(".//em/text()")) # 评分
- # 主演
- figure_desc = "主演: " + \
- "".join([Actor.format(**dict(fd.items()))
- for fd in li.xpath(".//div[@class='figure_desc']/a")])
+ a = li.find('.//div/a')
+ play_url = "https://music.163.com" + a.get("href") # 歌单播放地址
+ img = li.find(".//div/img")
+ cover_url = img.get("src") # 封面图片
+ playlist_title = a.get("title") # 歌单名
+ # 歌手
+ author_info = li.xpath(".//p[2]/a")[0]
+ playlist_author = "".format(
+ Actor.format(href="https://music.163.com" +
+ author_info.get("href"),
+ title=author_info.get("title")))
# 播放数
- figure_count = (
- li.xpath(".//div[@class='figure_count']/span/text()") or [""])[0]
+ play_count = (li.xpath(".//div/div/span[2]/text()") or [""])[0]
path = "cache/{0}.jpg".format(
- os.path.splitext(os.path.basename(video_url))[0])
+ os.path.splitext(
+ os.path.basename(cover_url).split('?')[0])[0])
cover_path = "Data/pic_v.png"
if os.path.isfile(path):
cover_path = path
- iwidget = ItemWidget(cover_path, figure_info, figure_title,
- figure_score, figure_desc, figure_count, video_url, cover_url, path, self)
+
+ # print(cover_path, playlist_title,
+ # playlist_author, play_count, play_url, cover_url, path)
+ iwidget = CoverItemWidget(self, manager=self._manager)
+ iwidget.init(cover_path, playlist_title, playlist_author,
+ play_count, play_url, cover_url, path)
self._layout.addWidget(iwidget, row_count + row, col)
@@ -299,9 +159,11 @@ class Window(QScrollArea):
# 连接竖着的滚动条滚动事件
self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered)
# 进度条
- self.loadWidget = QSvgWidget(
- self, minimumHeight=120, minimumWidth=120, visible=False)
- self.loadWidget.load(Svg_icon_loading)
+ self.loadWidget = QSvgWidget(self,
+ minimumHeight=120,
+ minimumWidth=120,
+ visible=False)
+ self.loadWidget.load('Data/Svg_icon_loading.svg')
def setLoadStarted(self, started):
self._loadStart = started
@@ -313,7 +175,8 @@ class Window(QScrollArea):
if action != QAbstractSlider.SliderMove or self._loadStart:
return
# 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断
- if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar().maximum():
+ if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar(
+ ).maximum():
# 可以下一页了
self._widget.load()
@@ -322,9 +185,7 @@ class Window(QScrollArea):
self.loadWidget.setGeometry(
int((self.width() - self.loadWidget.minimumWidth()) / 2),
int((self.height() - self.loadWidget.minimumHeight()) / 2),
- self.loadWidget.minimumWidth(),
- self.loadWidget.minimumHeight()
- )
+ self.loadWidget.minimumWidth(), self.loadWidget.minimumHeight())
if __name__ == "__main__":
diff --git a/QGridLayout/Lib/CoverItemWidget.py b/QGridLayout/Lib/CoverItemWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ef3ff00225af2269e33bd3c3ea4c47ba54c2adf
--- /dev/null
+++ b/QGridLayout/Lib/CoverItemWidget.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/22
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CoverItemWidget.py
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import QSize, QUrl
+ from PyQt5.QtGui import QPaintEvent, QPixmap
+ from PyQt5.QtNetwork import QNetworkRequest
+ from PyQt5.QtWidgets import QWidget
+except ImportError:
+ from PySide2.QtCore import QSize, QUrl
+ from PySide2.QtGui import QPaintEvent, QPixmap
+ from PySide2.QtNetwork import QNetworkRequest
+ from PySide2.QtWidgets import QWidget
+
+from .Ui_CoverItemWidget import Ui_CoverItemWidget # @UnresolvedImport
+
+
+class CoverItemWidget(QWidget, Ui_CoverItemWidget):
+
+ def __init__(self, *args, **kwargs):
+ self._manager = kwargs.pop('manager', None)
+ super(CoverItemWidget, self).__init__(*args, **kwargs)
+ self.setupUi(self)
+
+ def init(self, cover_path, playlist_title, playlist_author, play_count,
+ play_url, cover_url, img_path):
+ self.img_path = img_path
+ self.cover_url = cover_url
+ # 图片label
+ self.labelCover.init(cover_path, play_url, play_count)
+
+ # 歌单
+ self.labelTitle.setText(playlist_title)
+
+ # 作者
+ self.labelAuthor.setText(playlist_author)
+
+ def setCover(self, path):
+ self.labelCover.setCoverPath(path)
+ self.labelCover.setPixmap(QPixmap(path))
+
+ def sizeHint(self):
+ # 每个item控件的大小
+ return QSize(200, 256)
+
+ def event(self, event):
+ if isinstance(event, QPaintEvent):
+ if event.rect().height() > 20 and hasattr(self, "labelCover"):
+ if self.labelCover.cover_path.find("pic_v.png") > -1: # 封面未加载
+ # print("start download img:", self.cover_url)
+ req = QNetworkRequest(QUrl(self.cover_url))
+ # 设置两个自定义属性方便后期reply中处理
+ req.setAttribute(QNetworkRequest.User + 1, self)
+ req.setAttribute(QNetworkRequest.User + 2, self.img_path)
+ if self._manager:
+ self._manager.get(req) # 调用父窗口中的下载器下载
+ return super(CoverItemWidget, self).event(event)
diff --git a/QGridLayout/Lib/CoverLabel.py b/QGridLayout/Lib/CoverLabel.py
new file mode 100644
index 0000000000000000000000000000000000000000..dbee15af4cf251de21d8d7d4194b18cb8a192bbc
--- /dev/null
+++ b/QGridLayout/Lib/CoverLabel.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/22
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CoverLabel.py
+@description:
+"""
+import webbrowser
+
+try:
+ from PyQt5.QtGui import QPixmap
+ from PyQt5.QtWidgets import QLabel
+except ImportError:
+ from PySide2.QtGui import QPixmap
+ from PySide2.QtWidgets import QLabel
+
+from .Ui_CoverLabel import Ui_CoverLabel # @UnresolvedImport
+
+
+class CoverLabel(QLabel, Ui_CoverLabel):
+
+ def __init__(self, *args, **kwargs):
+ super(CoverLabel, self).__init__(*args, **kwargs)
+ self.setupUi(self)
+
+ def init(self, cover_path, play_url, play_count):
+ self.cover_path = cover_path
+ self.play_url = play_url
+ self.setPixmap(QPixmap(cover_path))
+ self.labelHeadset.setPixmap(QPixmap('Data/Svg_icon_headset_sm.svg'))
+ self.labelPlay.setPixmap(QPixmap('Data/Svg_icon_play_sm.svg'))
+ self.labelCount.setStyleSheet('color: #999999;')
+ self.labelCount.setText(play_count)
+
+ def setCoverPath(self, path):
+ self.cover_path = path
+
+ def mouseReleaseEvent(self, event):
+ super(CoverLabel, self).mouseReleaseEvent(event)
+ webbrowser.open_new_tab(self.play_url)
diff --git a/QGridLayout/Lib/Ui_CoverItemWidget.py b/QGridLayout/Lib/Ui_CoverItemWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..c048f686e4662268e855850c42d7da8a3182c91a
--- /dev/null
+++ b/QGridLayout/Lib/Ui_CoverItemWidget.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'CoverItemWidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.2
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+try:
+ from PyQt5 import QtCore, QtGui, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtGui, QtWidgets
+
+
+class Ui_CoverItemWidget(object):
+ def setupUi(self, CoverItemWidget):
+ CoverItemWidget.setObjectName("CoverItemWidget")
+ CoverItemWidget.setMinimumSize(QtCore.QSize(200, 256))
+ CoverItemWidget.setMaximumSize(QtCore.QSize(200, 256))
+ CoverItemWidget.setWindowTitle("")
+ self.verticalLayout = QtWidgets.QVBoxLayout(CoverItemWidget)
+ self.verticalLayout.setContentsMargins(10, 10, 10, 10)
+ self.verticalLayout.setSpacing(12)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.labelCover = CoverLabel(CoverItemWidget)
+ self.labelCover.setMinimumSize(QtCore.QSize(180, 180))
+ self.labelCover.setText("")
+ self.labelCover.setScaledContents(True)
+ self.labelCover.setAlignment(QtCore.Qt.AlignCenter)
+ self.labelCover.setObjectName("labelCover")
+ self.verticalLayout.addWidget(self.labelCover)
+ self.labelTitle = QtWidgets.QLabel(CoverItemWidget)
+ font = QtGui.QFont()
+ font.setPointSize(10)
+ self.labelTitle.setFont(font)
+ self.labelTitle.setText("")
+ self.labelTitle.setObjectName("labelTitle")
+ self.verticalLayout.addWidget(self.labelTitle)
+ self.labelAuthor = QtWidgets.QLabel(CoverItemWidget)
+ self.labelAuthor.setText("")
+ self.labelAuthor.setObjectName("labelAuthor")
+ self.verticalLayout.addWidget(self.labelAuthor)
+
+ self.retranslateUi(CoverItemWidget)
+ QtCore.QMetaObject.connectSlotsByName(CoverItemWidget)
+
+ def retranslateUi(self, CoverItemWidget):
+ pass
+from .CoverLabel import CoverLabel
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ CoverItemWidget = QtWidgets.QWidget()
+ ui = Ui_CoverItemWidget()
+ ui.setupUi(CoverItemWidget)
+ CoverItemWidget.show()
+ sys.exit(app.exec_())
diff --git a/QGridLayout/Lib/Ui_CoverLabel.py b/QGridLayout/Lib/Ui_CoverLabel.py
new file mode 100644
index 0000000000000000000000000000000000000000..86e1526a548fbd165e68d695447c530166faaee1
--- /dev/null
+++ b/QGridLayout/Lib/Ui_CoverLabel.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'CoverLabel.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.2
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+try:
+ from PyQt5 import QtCore, QtGui, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtGui, QtWidgets
+
+
+class Ui_CoverLabel(object):
+ def setupUi(self, CoverLabel):
+ CoverLabel.setObjectName("CoverLabel")
+ CoverLabel.resize(180, 180)
+ CoverLabel.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
+ CoverLabel.setWindowTitle("")
+ self.verticalLayout = QtWidgets.QVBoxLayout(CoverLabel)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ spacerItem = QtWidgets.QSpacerItem(20, 135, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem)
+ self.widgetBottom = QtWidgets.QWidget(CoverLabel)
+ self.widgetBottom.setStyleSheet("#widgetBottom {\n"
+" background-color: rgba(0, 0, 0, 150);\n"
+"}")
+ self.widgetBottom.setObjectName("widgetBottom")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.widgetBottom)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.labelHeadset = QtWidgets.QLabel(self.widgetBottom)
+ self.labelHeadset.setMinimumSize(QtCore.QSize(16, 16))
+ self.labelHeadset.setText("")
+ self.labelHeadset.setObjectName("labelHeadset")
+ self.horizontalLayout.addWidget(self.labelHeadset)
+ self.labelCount = QtWidgets.QLabel(self.widgetBottom)
+ self.labelCount.setText("")
+ self.labelCount.setObjectName("labelCount")
+ self.horizontalLayout.addWidget(self.labelCount)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem1)
+ self.labelPlay = QtWidgets.QLabel(self.widgetBottom)
+ self.labelPlay.setMinimumSize(QtCore.QSize(16, 16))
+ self.labelPlay.setText("")
+ self.labelPlay.setObjectName("labelPlay")
+ self.horizontalLayout.addWidget(self.labelPlay)
+ self.verticalLayout.addWidget(self.widgetBottom)
+
+ self.retranslateUi(CoverLabel)
+ QtCore.QMetaObject.connectSlotsByName(CoverLabel)
+
+ def retranslateUi(self, CoverLabel):
+ pass
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ CoverLabel = QtWidgets.QWidget()
+ ui = Ui_CoverLabel()
+ ui.setupUi(CoverLabel)
+ CoverLabel.show()
+ sys.exit(app.exec_())
diff --git a/QGridLayout/Lib/__init__.py b/QGridLayout/Lib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QGridLayout/README.md b/QGridLayout/README.md
index 624bc1c6e0630cb6af5ef8cacd16eef61c6cc6b0..8c851274764f94cfa004849ac82a5940ccb7412f 100644
--- a/QGridLayout/README.md
+++ b/QGridLayout/README.md
@@ -1,6 +1,9 @@
# QListView
-## 1、腾讯视频热播列表
+- 目录
+ - [音乐热歌列表](#1音乐热歌列表)
+
+## 1、音乐热歌列表
[运行 HotPlaylist.py](HotPlaylist.py)
简单思路说明:
diff --git a/QHBoxLayout/Data/BaseHorizontalLayout.ui b/QHBoxLayout/Data/BaseHorizontalLayout.ui
new file mode 100644
index 0000000000000000000000000000000000000000..0d0410ecbf27df083a9dc7db11e909cdcf5187d4
--- /dev/null
+++ b/QHBoxLayout/Data/BaseHorizontalLayout.ui
@@ -0,0 +1,38 @@
+
+
+ BaseHorizontalLayout
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+ -
+
+
+ 通过 右键 -> 布局 -> 水平布局
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ 右侧按钮
+
+
+
+
+
+
+
+
diff --git a/QHBoxLayout/Data/HorizontalLayoutMargin.ui b/QHBoxLayout/Data/HorizontalLayoutMargin.ui
new file mode 100644
index 0000000000000000000000000000000000000000..89673b3418b8ed37fab3890f2810e6e094375a16
--- /dev/null
+++ b/QHBoxLayout/Data/HorizontalLayoutMargin.ui
@@ -0,0 +1,60 @@
+
+
+ HorizontalLayoutMargin
+
+
+
+ 0
+ 0
+ 457
+ 216
+
+
+
+ Form
+
+
+ #VerticalLayoutMargin {
+ background: #5aaadb;
+}
+#label {
+ background: #85c440;
+}
+
+
+
+ 20
+
+
+ 20
+
+ -
+
+
+ 通过设置Margin和Spacing设置边距
+以及两个控件之间的间隔距离
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Spcasing 为 20
+
+
+
+ -
+
+
+ Spcasing 为 20
+
+
+
+
+
+
+
+
diff --git a/QHBoxLayout/Data/HorizontalLayoutStretch.ui b/QHBoxLayout/Data/HorizontalLayoutStretch.ui
new file mode 100644
index 0000000000000000000000000000000000000000..a45f50cb7e3d285167f9863dd27b2aaddb2ec1f4
--- /dev/null
+++ b/QHBoxLayout/Data/HorizontalLayoutStretch.ui
@@ -0,0 +1,62 @@
+
+
+ HorizontalLayoutStretch
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+ #label {
+ background: #5aaadb;
+}
+#label_2 {
+ background: #85c440;
+}
+#label_3 {
+ background: #f2b63c;
+}
+
+
+ -
+
+
+ 1/6
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ 2/6
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ 3/6
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+
+
diff --git a/QHBoxLayout/README.md b/QHBoxLayout/README.md
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a20a92b51fd102762ab55e3ffcdaef0e45e82c0a 100644
--- a/QHBoxLayout/README.md
+++ b/QHBoxLayout/README.md
@@ -0,0 +1,32 @@
+# QHBoxLayout
+
+- 目录
+ - [水平布局](#1水平布局)
+ - [边距和间隔](#2边距和间隔)
+ - [比例分配](#3比例分配)
+
+## 1、水平布局
+[查看 BaseHorizontalLayout.ui](Data/BaseHorizontalLayout.ui)
+
+
+
+## 2、边距和间隔
+[查看 HorizontalLayoutMargin.ui](Data/HorizontalLayoutMargin.ui)
+
+1. 通过`setContentsMargins(-1, -1, 20, -1)`设置左上右下的边距,-1表示默认值
+2. 通过`setSpacing`设置控件之间的间隔
+
+
+
+## 3、比例分配
+[查看 HorizontalLayoutStretch.ui](Data/HorizontalLayoutStretch.ui)
+
+通过`setStretch`设置各个部分的占比 分别为:1/6 2/6 3/6
+
+```python
+self.horizontalLayout.setStretch(0, 1)
+self.horizontalLayout.setStretch(1, 2)
+self.horizontalLayout.setStretch(2, 3)
+```
+
+
\ No newline at end of file
diff --git a/QHBoxLayout/ScreenShot/BaseHorizontalLayout.png b/QHBoxLayout/ScreenShot/BaseHorizontalLayout.png
new file mode 100644
index 0000000000000000000000000000000000000000..37f0c3f8e163bd0ea616a97a7a2ce38162299155
Binary files /dev/null and b/QHBoxLayout/ScreenShot/BaseHorizontalLayout.png differ
diff --git a/QHBoxLayout/ScreenShot/HorizontalLayoutMargin.png b/QHBoxLayout/ScreenShot/HorizontalLayoutMargin.png
new file mode 100644
index 0000000000000000000000000000000000000000..32dc9ec4956753618808a6bb983ae4c9e0502e8a
Binary files /dev/null and b/QHBoxLayout/ScreenShot/HorizontalLayoutMargin.png differ
diff --git a/QHBoxLayout/ScreenShot/HorizontalLayoutStretch.png b/QHBoxLayout/ScreenShot/HorizontalLayoutStretch.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c568f9a6c757dd4d9395ce144184121b8ad5abe
Binary files /dev/null and b/QHBoxLayout/ScreenShot/HorizontalLayoutStretch.png differ
diff --git a/QLabel/CircleImage.py b/QLabel/CircleImage.py
index 0c805cb06effcfcb09401ed2df35c1524ae0356a..7b59420eb35742b3acb032ba07b7d5049756f061 100644
--- a/QLabel/CircleImage.py
+++ b/QLabel/CircleImage.py
@@ -3,19 +3,21 @@
"""
Created on 2018年1月20日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: CircleImage
@description: 圆形图片
"""
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QPixmap, QPainter, QPainterPath
-from PyQt5.QtWidgets import QLabel, QWidget, QHBoxLayout
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QPixmap, QPainter, QPainterPath
+ from PyQt5.QtWidgets import QLabel, QWidget, QHBoxLayout, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QPixmap, QPainter, QPainterPath
+ from PySide2.QtWidgets import QLabel, QWidget, QHBoxLayout, QApplication
class Label(QLabel):
@@ -67,7 +69,6 @@ class Window(QWidget):
if __name__ == "__main__":
import sys
- from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
w = Window()
diff --git a/QLabel/ImageRotate.py b/QLabel/ImageRotate.py
index f0dd48d635faf2f95d08237d2e351be5b3c0064e..142299b77ca534ec9aad32a8a4a87d707b609041 100644
--- a/QLabel/ImageRotate.py
+++ b/QLabel/ImageRotate.py
@@ -4,22 +4,22 @@
"""
Created on 2018年11月19日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file:
@description:
"""
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QPixmap, QPainter, QImage
-from PyQt5.QtWidgets import QWidget, QLabel, QPushButton,\
- QVBoxLayout, QHBoxLayout, QSpacerItem, QSizePolicy
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QPixmap, QPainter, QImage
+ from PyQt5.QtWidgets import QWidget, QLabel, QPushButton, \
+ QVBoxLayout, QHBoxLayout, QSpacerItem, QSizePolicy, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QPixmap, QPainter, QImage
+ from PySide2.QtWidgets import QWidget, QLabel, QPushButton, \
+ QVBoxLayout, QHBoxLayout, QSpacerItem, QSizePolicy, QApplication
class Window(QWidget):
@@ -73,12 +73,12 @@ class Window(QWidget):
self.srcImage = image # 替换
self.imageLabel.setPixmap(QPixmap.fromImage(self.srcImage))
-# # 下面这个旋转方法针对90度的倍数,否则图片会变大
-# trans = QTransform()
-# trans.rotate(90)
-# self.srcImage = self.srcImage.transformed(
-# trans, Qt.SmoothTransformation)
-# self.imageLabel.setPixmap(QPixmap.fromImage(self.srcImage))
+ # # 下面这个旋转方法针对90度的倍数,否则图片会变大
+ # trans = QTransform()
+ # trans.rotate(90)
+ # self.srcImage = self.srcImage.transformed(
+ # trans, Qt.SmoothTransformation)
+ # self.imageLabel.setPixmap(QPixmap.fromImage(self.srcImage))
def doAnticlockwise(self):
# 逆时针45度
@@ -96,6 +96,7 @@ class Window(QWidget):
self.srcImage = image # 替换
self.imageLabel.setPixmap(QPixmap.fromImage(self.srcImage))
+
# # 下面这个旋转方法针对90度的倍数,否则图片会变大
# trans = QTransform()
# trans.rotate(90)
@@ -106,7 +107,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QLabel/ImageSlipped.py b/QLabel/ImageSlipped.py
index dfcac07d4236ba5499d744df04f8fffbb35c5dd1..1ea7818ba198ea0ac38a37e1bad4a25949dfe6ab 100644
--- a/QLabel/ImageSlipped.py
+++ b/QLabel/ImageSlipped.py
@@ -4,20 +4,18 @@
"""
Created on 2018年10月18日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ImageSlipped
@description:
"""
-from PyQt5.QtGui import QPixmap, QPainter
-from PyQt5.QtWidgets import QWidget
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtGui import QPixmap, QPainter
+ from PyQt5.QtWidgets import QWidget, QApplication
+except ImportError:
+ from PySide2.QtGui import QPixmap, QPainter
+ from PySide2.QtWidgets import QWidget, QApplication
class SlippedImgWidget(QWidget):
@@ -73,7 +71,7 @@ class SlippedImgWidget(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = SlippedImgWidget('Data/bg1.jpg', 'Data/fg1.png')
w.show()
diff --git a/QLabel/Lib/NinePatch.py b/QLabel/Lib/NinePatch.py
index 966903ae84201f51d4641bb16a0274781cbce0f9..e157f1bf24e76c235be2933078be2e194b7a285c 100644
--- a/QLabel/Lib/NinePatch.py
+++ b/QLabel/Lib/NinePatch.py
@@ -4,22 +4,19 @@
"""
Created on 2018年10月25日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: NinePatch
@description:
"""
from math import fabs
-from PyQt5.QtCore import QRect
-from PyQt5.QtGui import QImage, QColor, QPainter, qRed, qGreen, qBlue, qAlpha
-
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QRect
+ from PyQt5.QtGui import QImage, QColor, QPainter, qRed, qGreen, qBlue, qAlpha
+except ImportError:
+ from PySide2.QtCore import QRect
+ from PySide2.QtGui import QImage, QColor, QPainter, qRed, qGreen, qBlue, qAlpha
class _Exception(Exception):
@@ -44,7 +41,8 @@ class ExceptionIncorrectWidth(_Exception):
class ExceptionIncorrectWidthAndHeight(_Exception):
def __str__(self):
- return "Input incorrect width width and height. Minimum width = :{imgW} . Minimum height = :{imgH}".format(imgW=self.imgW, imgH=self.imgH)
+ return "Input incorrect width width and height. Minimum width = :{imgW} . Minimum height = :{imgH}".format(
+ imgW=self.imgW, imgH=self.imgH)
class ExceptionIncorrectHeight(_Exception):
@@ -62,11 +60,11 @@ class ExceptionNot9Patch(Exception):
class NinePatch:
def __init__(self, fileName):
- self.CachedImage = None # 缓存图片
+ self.CachedImage = None # 缓存图片
self.OldWidth = -1
self.OldHeight = -1
self.ResizeDistancesX = []
- self.ResizeDistancesY = [] # [(int,int)]数组
+ self.ResizeDistancesY = [] # [(int,int)]数组
self.setImage(fileName)
def width(self):
@@ -103,7 +101,8 @@ class NinePatch:
for i in range(len(self.ResizeDistancesY)):
resizeHeight += self.ResizeDistancesY[i][1]
- if (width < (self.Image.width() - 2 - resizeWidth) and height < (self.Image.height() - 2 - resizeHeight)):
+ if (width < (self.Image.width() - 2 - resizeWidth) and height < (
+ self.Image.height() - 2 - resizeHeight)):
raise ExceptionIncorrectWidthAndHeight(
self.Image.width() - 2, self.Image.height() - 2)
@@ -123,7 +122,8 @@ class NinePatch:
@classmethod
def GetContentAreaRect(self, width, height):
# print("GetContentAreaRect : width:%d height:%d" % (width, height))
- return (QRect(self.ContentArea.x(), self.ContentArea.y(), (width - (self.Image.width() - 2 - self.ContentArea.width())),
+ return (QRect(self.ContentArea.x(), self.ContentArea.y(),
+ (width - (self.Image.width() - 2 - self.ContentArea.width())),
(height - (self.Image.height() - 2 - self.ContentArea.height()))))
def DrawScaledPart(self, oldRect, newRect, painter):
@@ -189,7 +189,8 @@ class NinePatch:
for i in range(self.Image.width()):
if (self.IsColorBlack(self.Image.pixel(i, j)) and left == 0):
left = i
- if (left and self.IsColorBlack(self.Image.pixel(i, j)) and not self.IsColorBlack(self.Image.pixel(i + 1, j))):
+ if (left and self.IsColorBlack(self.Image.pixel(i, j)) and not self.IsColorBlack(
+ self.Image.pixel(i + 1, j))):
right = i
left -= 1
# print("ResizeDistancesX.append ", left, " ", right - left)
@@ -204,7 +205,8 @@ class NinePatch:
if (self.IsColorBlack(self.Image.pixel(i, j)) and top == 0):
top = j
- if (top and self.IsColorBlack(self.Image.pixel(i, j)) and not self.IsColorBlack(self.Image.pixel(i, j + 1))):
+ if (top and self.IsColorBlack(self.Image.pixel(i, j)) and not self.IsColorBlack(
+ self.Image.pixel(i, j + 1))):
bot = j
top -= 1
# print("ResizeDistancesY.append ", top, " ", bot - top)
@@ -241,10 +243,10 @@ class NinePatch:
# print("after GetFactor: ", width, height, factorX, factorY)
lostX = 0.0
lostY = 0.0
- x1 = 0 # for image parts X
- y1 = 0 # for image parts Y
-# widthResize # width for image parts
-# heightResize # height for image parts
+ x1 = 0 # for image parts X
+ y1 = 0 # for image parts Y
+ # widthResize # width for image parts
+ # heightResize # height for image parts
resizeX = 0
resizeY = 0
offsetX = 0
@@ -310,7 +312,8 @@ class NinePatch:
offsetY = 0
for i in range(len(self.ResizeDistancesY)):
self.DrawConstPart(QRect(x1 + 1, y1 + 1, widthResize, self.ResizeDistancesY[i][0] - y1),
- QRect(x1 + offsetX, y1 + offsetY, widthResize, self.ResizeDistancesY[i][0] - y1), painter)
+ QRect(x1 + offsetX, y1 + offsetY, widthResize,
+ self.ResizeDistancesY[i][0] - y1), painter)
y1 = self.ResizeDistancesY[i][0]
resizeY = round(float(self.ResizeDistancesY[i][1]) * factorY)
lostY += resizeY - (float(self.ResizeDistancesY[i][1]) * factorY)
@@ -334,7 +337,8 @@ class NinePatch:
offsetX = 0
for i in range(len(self.ResizeDistancesX)):
self.DrawConstPart(QRect(x1 + 1, y1 + 1, self.ResizeDistancesX[i][0] - x1, heightResize),
- QRect(x1 + offsetX, y1 + offsetY, self.ResizeDistancesX[i][0] - x1, heightResize), painter)
+ QRect(x1 + offsetX, y1 + offsetY, self.ResizeDistancesX[i][0] - x1,
+ heightResize), painter)
x1 = self.ResizeDistancesX[i][0]
resizeX = round(float(self.ResizeDistancesX[i][1]) * factorX)
lostX += resizeX - (float(self.ResizeDistancesX[i][1]) * factorX)
diff --git a/QLabel/Lib/QtNinePatch/sip/configure.py b/QLabel/Lib/QtNinePatch/sip/configure.py
index eae22a59927d790f3380a11b7063111b7470a823..a3cc255c76fd76375145e48c98d211bde4334106 100644
--- a/QLabel/Lib/QtNinePatch/sip/configure.py
+++ b/QLabel/Lib/QtNinePatch/sip/configure.py
@@ -5,9 +5,8 @@ import os
import shutil
import PyQt5
-from PyQt5.QtCore import PYQT_CONFIGURATION
import sipconfig
-
+from PyQt5.QtCore import PYQT_CONFIGURATION
# 模块名
moduleName = 'QtNinePatch'
@@ -35,14 +34,13 @@ sip_cmd = ' '.join([
'-b', "build/" + build_file,
'-I', config.default_sip_dir + '/PyQt5',
PYQT_CONFIGURATION.get('sip_flags', ''),
- '%s.sip' % moduleName,
+ '%s.sip' % moduleName,
])
os.makedirs('build', exist_ok=True)
print(sip_cmd)
os.system(sip_cmd)
-
# Create the Makefile.
makefile = sipconfig.SIPModuleMakefile(
config, build_file, dir='build'
diff --git a/QLabel/Lib/QtNinePatch2.py b/QLabel/Lib/QtNinePatch2.py
index b0a84354cde6dbe6819ac670006f6cb5cd3dce32..7697cc69920a2107c29c064d565f582bfc8a9145 100644
--- a/QLabel/Lib/QtNinePatch2.py
+++ b/QLabel/Lib/QtNinePatch2.py
@@ -4,22 +4,19 @@
"""
Created on 2018年10月25日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: QtNinePatch
@description:
"""
from math import floor
-from PyQt5.QtCore import Qt, QRect
-from PyQt5.QtGui import qAlpha, QPixmap, QPainter
-
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt, QRect
+ from PyQt5.QtGui import qAlpha, QPixmap, QPainter
+except ImportError:
+ from PySide2.QtCore import Qt, QRect
+ from PySide2.QtGui import qAlpha, QPixmap, QPainter
class Part:
diff --git a/QLabel/Lib/res_rc.py b/QLabel/Lib/res_rc.py
index baab0b942aeed3f63a1f90df7428ecdaa636a830..0bebdc4c7d8003496a1268f642a3b4ca29389098 100644
--- a/QLabel/Lib/res_rc.py
+++ b/QLabel/Lib/res_rc.py
@@ -6,7 +6,10 @@
#
# WARNING! All changes made in this file will be lost!
-from PyQt5 import QtCore
+try:
+ from PyQt5 import QtCore
+except ImportError:
+ from PySide2 import QtCore
qt_resource_data = b"\
\x00\x00\x19\xf0\
@@ -462,10 +465,13 @@ else:
rcc_version = 2
qt_resource_struct = qt_resource_struct_v2
+
def qInitResources():
QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
+
def qCleanupResources():
QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
+
qInitResources()
diff --git a/QLabel/Lib/xpmres.py b/QLabel/Lib/xpmres.py
index fea8f797da6f03a5206bbf3b201a1cb9bce2e81f..b589c8d1497afd50600004e3a3533236569e75a7 100644
--- a/QLabel/Lib/xpmres.py
+++ b/QLabel/Lib/xpmres.py
@@ -1,18 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月23日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: xpmres
@description:
-'''
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+"""
# 这里把转换的xpm数组直接放到py文件中当做一个变量
diff --git a/QLabel/NinePatch.py b/QLabel/NinePatch.py
index 936faf7e2fff7ffdd0660c5acd9c5cf59cb1c011..f072dc10ca5f4bec095b3045f0ab326f128e53df 100644
--- a/QLabel/NinePatch.py
+++ b/QLabel/NinePatch.py
@@ -4,23 +4,20 @@
"""
Created on 2018年10月25日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: NinePatch
@description:
"""
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
-
import sys
-from PyQt5.QtGui import QImage, QPainter
-from PyQt5.QtWidgets import QApplication, QLabel, QWidget
+try:
+ from PyQt5.QtGui import QPainter
+ from PyQt5.QtWidgets import QApplication, QWidget
+except ImportError:
+ from PySide2.QtGui import QPainter
+ from PySide2.QtWidgets import QApplication, QWidget
from Lib.NinePatch import NinePatch
@@ -29,7 +26,7 @@ class Label(QWidget):
def __init__(self, *args, **kwargs):
super(Label, self).__init__(*args, **kwargs)
- #.9 格式的图片
+ # .9 格式的图片
self.image = NinePatch('Data/skin_aio_friend_bubble_pressed.9.png')
def paintEvent(self, event):
diff --git a/QLabel/QtNinePatch.py b/QLabel/QtNinePatch.py
index 8fbdbc9c03886affcd92e54f1f4d9b5ba0bb4774..497b7bfa7132b1d4072cdc0ce9709cdb52046da3 100644
--- a/QLabel/QtNinePatch.py
+++ b/QLabel/QtNinePatch.py
@@ -4,22 +4,15 @@
"""
Created on 2018年10月25日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: testQtNinePatch
@description:
"""
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
-
import sys
-
from ctypes import CDLL
+
from PyQt5.QtGui import QImage
from PyQt5.QtWidgets import QApplication, QLabel
@@ -32,7 +25,7 @@ class Label(QLabel):
def __init__(self, *args, **kwargs):
super(Label, self).__init__(*args, **kwargs)
- #.9 格式的图片
+ # .9 格式的图片
self.image = QImage('Data/skin_aio_friend_bubble_pressed.9.png')
def showEvent(self, event):
diff --git a/QLabel/QtNinePatch2.py b/QLabel/QtNinePatch2.py
index 187b15ce90455d2763a1a198a2888d955692f624..f861461202910e350997206582347888d7efcb7e 100644
--- a/QLabel/QtNinePatch2.py
+++ b/QLabel/QtNinePatch2.py
@@ -4,23 +4,20 @@
"""
Created on 2018年10月25日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: QtNinePatch2
@description:
"""
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
-
import sys
-from PyQt5.QtGui import QImage
-from PyQt5.QtWidgets import QApplication, QLabel
+try:
+ from PyQt5.QtGui import QImage
+ from PyQt5.QtWidgets import QApplication, QLabel
+except ImportError:
+ from PySide2.QtGui import QImage
+ from PySide2.QtWidgets import QApplication, QLabel
from Lib import QtNinePatch2
@@ -29,7 +26,7 @@ class Label(QLabel):
def __init__(self, *args, **kwargs):
super(Label, self).__init__(*args, **kwargs)
- #.9 格式的图片
+ # .9 格式的图片
self.image = QImage('Data/skin_aio_friend_bubble_pressed.9.png')
def showEvent(self, event):
diff --git a/QLabel/README.md b/QLabel/README.md
index 2d068acffbb75348d8ac76cf1a7d5afcbdb7aeb3..acfc3d535ac5eca6b075889155ca20aee40c2f8b 100644
--- a/QLabel/README.md
+++ b/QLabel/README.md
@@ -1,5 +1,12 @@
# QLabel
+- 目录
+ - [图片加载显示](#1图片加载显示)
+ - [图片旋转](#2图片旋转)
+ - [仿网页图片错位显示](#3仿网页图片错位显示)
+ - [显示.9格式图片(气泡)](#4显示9格式图片气泡)
+ - [圆形图片](#5圆形图片)
+
## 1、图片加载显示
[运行 ShowImage.py](ShowImage.py)
diff --git a/QLabel/ShowImage.py b/QLabel/ShowImage.py
index ac3fb9f6e82dc23fc874120d1ee5ef2bf8e1a951..41ca08ead5aa359faa973babd02f9dae7a212ef5 100644
--- a/QLabel/ShowImage.py
+++ b/QLabel/ShowImage.py
@@ -1,29 +1,28 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月23日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ShowImage
@description:
-'''
+"""
import sys
-from PyQt5.QtCore import QResource
-from PyQt5.QtGui import QPixmap, QMovie
-from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout, QLabel
+try:
+ from PyQt5.QtCore import QResource
+ from PyQt5.QtGui import QPixmap, QMovie
+ from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout, QLabel
+except ImportError:
+ from PySide2.QtCore import QResource
+ from PySide2.QtGui import QPixmap, QMovie
+ from PySide2.QtWidgets import QWidget, QApplication, QHBoxLayout, QLabel
-from Lib import res_rc # @UnresolvedImport @UnusedImport
from Lib.xpmres import image_head # @UnresolvedImport
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
-
-
class ImageView(QWidget):
def __init__(self, *args, **kwargs):
diff --git a/QListView/CustomWidgetItem.py b/QListView/CustomWidgetItem.py
index 6a8a1be81bf261bc1ed64c0ee125ac6f5c746022..3bffcac13242fc713509cbd40067d91438905c1b 100644
--- a/QListView/CustomWidgetItem.py
+++ b/QListView/CustomWidgetItem.py
@@ -1,22 +1,26 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from PyQt5.QtCore import QSize
-from PyQt5.QtGui import QStandardItemModel, QStandardItem
-from PyQt5.QtWidgets import QListView, QWidget, QHBoxLayout, QLineEdit,\
- QPushButton
-# Created on 2018年8月4日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: QListView.显示自定义Widget
-# description:
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+"""
+Created on 2018年8月4日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QListView.显示自定义Widget
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import QSize
+ from PyQt5.QtGui import QStandardItemModel, QStandardItem
+ from PyQt5.QtWidgets import QListView, QWidget, QHBoxLayout, QLineEdit, \
+ QPushButton, QApplication
+except ImportError:
+ from PySide2.QtCore import QSize
+ from PySide2.QtGui import QStandardItemModel, QStandardItem
+ from PySide2.QtWidgets import QListView, QWidget, QHBoxLayout, QLineEdit, \
+ QPushButton, QApplication
class CustomWidget(QWidget):
@@ -56,7 +60,7 @@ class ListView(QListView):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = ListView()
w.show()
diff --git a/QListView/CustomWidgetSortItem.py b/QListView/CustomWidgetSortItem.py
index 09fe903614e0282c476caee9a012f44c2ac4fcd9..3a68ef20f82ab66e9645e4393ab3504c380c00bf 100644
--- a/QListView/CustomWidgetSortItem.py
+++ b/QListView/CustomWidgetSortItem.py
@@ -1,26 +1,29 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from random import choice, randint
-import string
-from time import time
-from PyQt5.QtCore import QSortFilterProxyModel, Qt, QSize
-from PyQt5.QtGui import QStandardItem, QStandardItemModel
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QListView,\
- QHBoxLayout, QLineEdit
+"""
+Created on 2018年8月4日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QListView.显示自定义Widget并排序
+@description:
+"""
+import string
+from random import choice, randint
+from time import time
-# Created on 2018年8月4日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: QListView.显示自定义Widget并排序
-# description:
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import QSortFilterProxyModel, Qt, QSize
+ from PyQt5.QtGui import QStandardItem, QStandardItemModel
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QListView, \
+ QHBoxLayout, QLineEdit, QApplication
+except ImportError:
+ from PySide2.QtCore import QSortFilterProxyModel, Qt, QSize
+ from PySide2.QtGui import QStandardItem, QStandardItemModel
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QPushButton, QListView, \
+ QHBoxLayout, QLineEdit, QApplication
def randomChar(y):
@@ -55,11 +58,11 @@ class SortFilterProxyModel(QSortFilterProxyModel):
leftData = leftData.split('-')[-1]
rightData = rightData.split('-')[-1]
return leftData < rightData
-# elif self.sortOrder() == Qt.AscendingOrder:
-# #按照名字升序排序
-# leftData = leftData.split('-')[0]
-# rightData = rightData.split('-')[0]
-# return leftData < rightData
+ # elif self.sortOrder() == Qt.AscendingOrder:
+ # #按照名字升序排序
+ # leftData = leftData.split('-')[0]
+ # rightData = rightData.split('-')[0]
+ # return leftData < rightData
return super(SortFilterProxyModel, self).lessThan(source_left, source_right)
@@ -89,7 +92,7 @@ class Window(QWidget):
times = time() + randint(0, 30) # 当前时间随机+
value = '{}-{}'.format(name, times) # 内容用-分开
item = QStandardItem(value)
-# item.setData(value, Qt.UserRole + 2)
+ # item.setData(value, Qt.UserRole + 2)
self.dmodel.appendRow(item)
# 索引
index = self.fmodel.mapFromSource(item.index())
@@ -109,7 +112,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QListView/ImageView.py b/QListView/ImageView.py
new file mode 100644
index 0000000000000000000000000000000000000000..b030806c14dd16017263a0b2e94e910083e8f1e2
--- /dev/null
+++ b/QListView/ImageView.py
@@ -0,0 +1,240 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2021/4/15
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ImageView
+@description:
+"""
+import os
+
+try:
+ from PyQt5.QtCore import QPointF, Qt, QRectF, QSizeF
+ from PyQt5.QtGui import QStandardItem, QStandardItemModel, QPainter, QColor, QImage, QPixmap
+ from PyQt5.QtWidgets import QApplication, QListView, QGraphicsView, QGraphicsPixmapItem, QGraphicsScene
+except ImportError:
+ from PySide2.QtCore import QPointF, Qt, QRectF, QSizeF
+ from PySide2.QtGui import QStandardItem, QStandardItemModel, QPainter, QColor, QImage, QPixmap
+ from PySide2.QtWidgets import QApplication, QListView, QGraphicsView, QGraphicsPixmapItem, QGraphicsScene
+
+ScrollPixel = 40
+
+
+class BigImageView(QGraphicsView):
+ """图片查看控件"""
+
+ def __init__(self, *args, **kwargs):
+ image = kwargs.pop('image', None)
+ background = kwargs.pop('background', None)
+ super(BigImageView, self).__init__(*args, **kwargs)
+ self.setCursor(Qt.OpenHandCursor)
+ self.setBackground(background)
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing |
+ QPainter.SmoothPixmapTransform)
+ self.setCacheMode(self.CacheBackground)
+ self.setViewportUpdateMode(self.SmartViewportUpdate)
+ self._item = QGraphicsPixmapItem() # 放置图像
+ self._item.setFlags(QGraphicsPixmapItem.ItemIsFocusable |
+ QGraphicsPixmapItem.ItemIsMovable)
+ self._scene = QGraphicsScene(self) # 场景
+ self.setScene(self._scene)
+ self._scene.addItem(self._item)
+ rect = QApplication.instance().desktop().availableGeometry()
+ self.resize(int(rect.width() * 2 / 3), int(rect.height() * 2 / 3))
+
+ self.pixmap = None
+ self._delta = 0.1 # 缩放
+ self.setPixmap(image)
+
+ def setBackground(self, color):
+ """设置背景颜色
+ :param color: 背景颜色
+ :type color: QColor or str or GlobalColor
+ """
+ if isinstance(color, QColor):
+ self.setBackgroundBrush(color)
+ elif isinstance(color, (str, Qt.GlobalColor)):
+ color = QColor(color)
+ if color.isValid():
+ self.setBackgroundBrush(color)
+
+ def setPixmap(self, pixmap, fitIn=True):
+ """加载图片
+ :param pixmap: 图片或者图片路径
+ :param fitIn: 是否适应
+ :type pixmap: QPixmap or QImage or str
+ :type fitIn: bool
+ """
+ if isinstance(pixmap, QPixmap):
+ self.pixmap = pixmap
+ elif isinstance(pixmap, QImage):
+ self.pixmap = QPixmap.fromImage(pixmap)
+ elif isinstance(pixmap, str) and os.path.isfile(pixmap):
+ self.pixmap = QPixmap(pixmap)
+ else:
+ return
+ self._item.setPixmap(self.pixmap)
+ self._item.update()
+ self.setSceneDims()
+ if fitIn:
+ self.fitInView(QRectF(self._item.pos(), QSizeF(
+ self.pixmap.size())), Qt.KeepAspectRatio)
+ self.update()
+
+ def setSceneDims(self):
+ if not self.pixmap:
+ return
+ self.setSceneRect(QRectF(QPointF(0, 0), QPointF(self.pixmap.width(), self.pixmap.height())))
+
+ def fitInView(self, rect, flags=Qt.IgnoreAspectRatio):
+ """剧中适应
+ :param rect: 矩形范围
+ :param flags:
+ :return:
+ """
+ if not self.scene() or rect.isNull():
+ return
+ unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
+ self.scale(1 / unity.width(), 1 / unity.height())
+ viewRect = self.viewport().rect()
+ sceneRect = self.transform().mapRect(rect)
+ x_ratio = viewRect.width() / sceneRect.width()
+ y_ratio = viewRect.height() / sceneRect.height()
+ if flags == Qt.KeepAspectRatio:
+ x_ratio = y_ratio = min(x_ratio, y_ratio)
+ elif flags == Qt.KeepAspectRatioByExpanding:
+ x_ratio = y_ratio = max(x_ratio, y_ratio)
+ self.scale(x_ratio, y_ratio)
+ self.centerOn(rect.center())
+
+ def wheelEvent(self, event):
+ if event.angleDelta().y() > 0:
+ self.zoomIn()
+ else:
+ self.zoomOut()
+
+ def zoomIn(self):
+ """放大"""
+ self.zoom(1 + self._delta)
+
+ def zoomOut(self):
+ """缩小"""
+ self.zoom(1 - self._delta)
+
+ def zoom(self, factor):
+ """缩放
+ :param factor: 缩放的比例因子
+ """
+ _factor = self.transform().scale(
+ factor, factor).mapRect(QRectF(0, 0, 1, 1)).width()
+ if _factor < 0.07 or _factor > 100:
+ # 防止过大过小
+ return
+ self.scale(factor, factor)
+
+
+class ImageView(QListView):
+
+ def __init__(self, *args, **kwargs):
+ super(ImageView, self).__init__(*args, **kwargs)
+ self.setFrameShape(self.NoFrame)
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.setEditTriggers(self.NoEditTriggers)
+ self.setDropIndicatorShown(True)
+ self.setDragDropMode(self.DragDrop)
+ self.setDefaultDropAction(Qt.IgnoreAction)
+ self.setSelectionMode(self.ExtendedSelection)
+ self.setVerticalScrollMode(self.ScrollPerPixel)
+ self.setHorizontalScrollMode(self.ScrollPerPixel)
+ self.setFlow(self.LeftToRight)
+ self.setWrapping(True)
+ self.setResizeMode(self.Adjust)
+ self.setSpacing(6)
+ self.setViewMode(self.IconMode)
+ self.setWordWrap(True)
+ self.setSelectionRectVisible(True)
+ self.setContextMenuPolicy(Qt.CustomContextMenu)
+ # 解决拖动到顶部或者底部自动滚动
+ self.setAutoScrollMargin(150)
+ self.verticalScrollBar().setSingleStep(ScrollPixel)
+ # 设置model
+ self.dmodel = QStandardItemModel(self)
+ self.setModel(self.dmodel)
+
+ # 大图控件
+ self.bigView = BigImageView(background='#323232')
+
+ def addItem(self, image):
+ if isinstance(image, str):
+ image = QPixmap(image)
+ # 添加一个item
+ item = QStandardItem()
+ # 记录原始图片
+ item.setData(image, Qt.UserRole + 1) # 用于双击的时候取出来
+ # 缩放成小图并显示
+ item.setData(image.scaled(60, 60, Qt.IgnoreAspectRatio, Qt.SmoothTransformation), Qt.DecorationRole)
+ # 添加item到界面中
+ self.dmodel.appendRow(item)
+
+ def count(self):
+ return self.dmodel.rowCount()
+
+ def setCurrentRow(self, row):
+ self.setCurrentIndex(self.dmodel.index(row, 0))
+
+ def currentRow(self):
+ return self.currentIndex().row()
+
+ def updateGeometries(self):
+ # 一次滑动20px
+ super(ImageView, self).updateGeometries()
+ self.verticalScrollBar().setSingleStep(ScrollPixel)
+
+ def closeEvent(self, event):
+ # 关闭预览窗口
+ self.bigView.close()
+ super(ImageView, self).closeEvent(event)
+
+ def wheelEvent(self, event):
+ # 修复滑动bug
+ if self.flow() == QListView.LeftToRight:
+ bar = self.horizontalScrollBar()
+ value = ScrollPixel if event.angleDelta().y() < 0 else (0 - ScrollPixel)
+ bar.setSliderPosition(bar.value() + value)
+ else:
+ super(ImageView, self).wheelEvent(event)
+
+ def mouseDoubleClickEvent(self, event):
+ # 列表双击,如果有item则进入item处理流程,否则调用打开图片功能
+ index = self.indexAt(event.pos())
+ if index and index.isValid():
+ item = self.dmodel.itemFromIndex(index)
+ if item:
+ # 取出原图用来新窗口显示
+ image = item.data(Qt.UserRole + 1)
+ self.bigView.setPixmap(image)
+ self.bigView.show()
+ return
+ super(ImageView, self).mouseDoubleClickEvent(event)
+
+
+if __name__ == '__main__':
+ import sys
+ import cgitb
+
+ cgitb.enable(format='text')
+
+ app = QApplication(sys.argv)
+ w = ImageView()
+ w.show()
+
+ # 添加模拟图片
+ for i in range(3):
+ for name in os.listdir('ScreenShot'):
+ w.addItem(os.path.join('ScreenShot', name))
+ sys.exit(app.exec_())
diff --git a/QListView/README.md b/QListView/README.md
index 74d7644fab32aa29f453a25e6a130baef56c7653..95a16ef94b466be79c72e03d851edc236680eddf 100644
--- a/QListView/README.md
+++ b/QListView/README.md
@@ -1,5 +1,10 @@
# QListView
+- 目录
+ - [显示自定义Widget](#1显示自定义Widget)
+ - [显示自定义Widget并排序](#2显示自定义Widget并排序)
+ - [自定义角色排序](#3自定义角色排序)
+
## 1、显示自定义Widget
[运行 CustomWidgetItem.py](CustomWidgetItem.py)
diff --git a/QListView/SortItemByRole.py b/QListView/SortItemByRole.py
index 269bd6f876694b0f09bcf834015dbed8b4c3bb05..d4ccd876945878126e6831e9cf0fcb57afac982a 100644
--- a/QListView/SortItemByRole.py
+++ b/QListView/SortItemByRole.py
@@ -4,23 +4,21 @@
"""
Created on 2018年12月27日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: QListView.SortItemByRole
@description:
"""
from random import choice
-from PyQt5.QtCore import QSortFilterProxyModel, Qt
-from PyQt5.QtGui import QStandardItem, QStandardItemModel
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QListView, QPushButton
-
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QSortFilterProxyModel, Qt
+ from PyQt5.QtGui import QStandardItem, QStandardItemModel
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QListView, QPushButton
+except ImportError:
+ from PySide2.QtCore import QSortFilterProxyModel, Qt
+ from PySide2.QtGui import QStandardItem, QStandardItemModel
+ from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QListView, QPushButton
class SortFilterProxyModel(QSortFilterProxyModel):
@@ -72,8 +70,8 @@ IndexDict = {
4: '清',
}
-IdRole = Qt.UserRole + 1 # 用于恢复排序
-ClassifyRole = Qt.UserRole + 2 # 用于按照分类序号排序
+IdRole = Qt.UserRole + 1 # 用于恢复排序
+ClassifyRole = Qt.UserRole + 2 # 用于按照分类序号排序
class Window(QWidget):
@@ -96,8 +94,8 @@ class Window(QWidget):
def restoreSort(self):
# 恢复默认排序
- self.fmodel.setSortRole(IdRole) # 必须设置排序角色为ID
- self.fmodel.sort(0) # 排序第一列按照ID升序
+ self.fmodel.setSortRole(IdRole) # 必须设置排序角色为ID
+ self.fmodel.sort(0) # 排序第一列按照ID升序
def sortByClassify(self):
self.fmodel.setSortIndex(NameDict.get(
@@ -141,8 +139,9 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.enable(1, None, 5, '')
- from PyQt5.QtWidgets import QApplication
+
+ cgitb.enable(format='text')
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QListWidget/Data/CoverItemWidget.ui b/QListWidget/Data/CoverItemWidget.ui
new file mode 100644
index 0000000000000000000000000000000000000000..2f245e1344895af124b9ed3ea8b387c232077a79
--- /dev/null
+++ b/QListWidget/Data/CoverItemWidget.ui
@@ -0,0 +1,85 @@
+
+
+ CoverItemWidget
+
+
+
+ 200
+ 256
+
+
+
+
+ 200
+ 256
+
+
+
+
+
+
+
+ 12
+
+
+ 10
+
+
+ 10
+
+
+ 10
+
+
+ 10
+
+ -
+
+
+
+ 180
+ 180
+
+
+
+
+
+
+ true
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 10
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ CoverLabel
+ QLabel
+
+
+
+
+
+
diff --git a/QListWidget/Data/CoverLabel.ui b/QListWidget/Data/CoverLabel.ui
new file mode 100644
index 0000000000000000000000000000000000000000..2e7e037880e23bace4731623a4f1c25620fe267b
--- /dev/null
+++ b/QListWidget/Data/CoverLabel.ui
@@ -0,0 +1,109 @@
+
+
+ CoverLabel
+
+
+
+ 0
+ 0
+ 180
+ 180
+
+
+
+ PointingHandCursor
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 135
+
+
+
+
+ -
+
+
+ #widgetBottom {
+ background-color: rgba(0, 0, 0, 150);
+}
+
+
+ -
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/QListWidget/Data/Svg_icon_headset_sm.svg b/QListWidget/Data/Svg_icon_headset_sm.svg
new file mode 100644
index 0000000000000000000000000000000000000000..98a1a3591d4f0741b27d27862023bc75bddf4a8d
--- /dev/null
+++ b/QListWidget/Data/Svg_icon_headset_sm.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/QListWidget/Data/Svg_icon_loading.svg b/QListWidget/Data/Svg_icon_loading.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0b00b7993dc84f2a6221b942d35dc62421e70ac4
--- /dev/null
+++ b/QListWidget/Data/Svg_icon_loading.svg
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QListWidget/Data/Svg_icon_play_sm.svg b/QListWidget/Data/Svg_icon_play_sm.svg
new file mode 100644
index 0000000000000000000000000000000000000000..08a47f20e76885d4e77b0f31221ff4d9e8bacafd
--- /dev/null
+++ b/QListWidget/Data/Svg_icon_play_sm.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/QListWidget/DeleteCustomItem.py b/QListWidget/DeleteCustomItem.py
index a2d493304491e8633507d586b024883aaa92c658..ab8516e06bc10bb2cfd97a97137dc332c5542029 100644
--- a/QListWidget/DeleteCustomItem.py
+++ b/QListWidget/DeleteCustomItem.py
@@ -4,25 +4,23 @@
"""
Created on 2018年11月4日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: 删除Item
@description:
"""
-from PyQt5.QtCore import QSize, pyqtSignal
-from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLineEdit, QPushButton,\
- QListWidgetItem, QVBoxLayout, QListWidget
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import QSize, pyqtSignal
+ from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLineEdit, QPushButton, \
+ QListWidgetItem, QVBoxLayout, QListWidget, QApplication
+except ImportError:
+ from PySide2.QtCore import QSize, Signal as pyqtSignal
+ from PySide2.QtWidgets import QWidget, QHBoxLayout, QLineEdit, QPushButton, \
+ QListWidgetItem, QVBoxLayout, QListWidget, QApplication
class ItemWidget(QWidget):
-
itemDeleted = pyqtSignal(QListWidgetItem)
def __init__(self, text, item, *args, **kwargs):
@@ -91,8 +89,9 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.enable(1, None, 5, 'text')
- from PyQt5.QtWidgets import QApplication
+
+ cgitb.enable(format='text')
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QListWidget/DragDrop.py b/QListWidget/DragDrop.py
index dcc8818c9368516af568e14636bce1321f13bde8..0df4479e16c06dc115ce826737577d6c1bcda2d8 100644
--- a/QListWidget/DragDrop.py
+++ b/QListWidget/DragDrop.py
@@ -4,21 +4,20 @@
"""
Created on 2018年9月14日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: DragListWidget
@description:
"""
-from PyQt5.QtCore import Qt, QSize, QRect, QPoint
-from PyQt5.QtGui import QColor, QPixmap, QDrag, QPainter, QCursor
-from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QLabel, QRubberBand
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt, QSize, QRect, QPoint
+ from PyQt5.QtGui import QColor, QPixmap, QDrag, QPainter, QCursor
+ from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QLabel, QRubberBand, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt, QSize, QRect, QPoint
+ from PySide2.QtGui import QColor, QPixmap, QDrag, QPainter, QCursor
+ from PySide2.QtWidgets import QListWidget, QListWidgetItem, QLabel, QRubberBand, QApplication
class DropListWidget(QListWidget):
@@ -162,7 +161,7 @@ class DragListWidget(QListWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
app.setStyleSheet("""QListWidget {
outline: 0px;
diff --git a/QListWidget/FoldWidget.py b/QListWidget/FoldWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..7f75a8d800f7774c0abc8368f69661c54ad5ee5b
--- /dev/null
+++ b/QListWidget/FoldWidget.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年5月27日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: FoldWidget
+@description: 自定义item折叠控件仿QTreeWidget
+"""
+
+from PyQt5.QtCore import QSize
+from PyQt5.QtWidgets import QWidget, QPushButton, QFormLayout, \
+ QLineEdit, QListWidget, QListWidgetItem, QCheckBox
+
+
+class CustomWidget(QWidget):
+
+ def __init__(self, item, *args, **kwargs):
+ super(CustomWidget, self).__init__(*args, **kwargs)
+ self.oldSize = None
+ self.item = item
+ layout = QFormLayout(self)
+ layout.addRow('我是label', QLineEdit(self))
+ layout.addRow('点击', QCheckBox(
+ '隐藏下面的按钮', self, toggled=self.hideChild))
+ self.button = QPushButton('我是被隐藏的', self)
+ layout.addRow(self.button)
+
+ def hideChild(self, v):
+ self.button.setVisible(not v)
+ # 这里很重要 当隐藏内部子控件时 需要重新计算高度
+ self.adjustSize()
+
+ def resizeEvent(self, event):
+ # 解决item的高度问题
+ super(CustomWidget, self).resizeEvent(event)
+ self.item.setSizeHint(QSize(self.minimumWidth(), self.height()))
+
+
+class CustomButton(QPushButton):
+ # 按钮作为开关
+
+ def __init__(self, item, *args, **kwargs):
+ super(CustomButton, self).__init__(*args, **kwargs)
+ self.item = item
+ self.setCheckable(True) # 设置可选中
+
+ def resizeEvent(self, event):
+ # 解决item的高度问题
+ super(CustomButton, self).resizeEvent(event)
+ self.item.setSizeHint(QSize(self.minimumWidth(), self.height()))
+
+
+class Window(QListWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+
+ for _ in range(3):
+ # 开关
+ item = QListWidgetItem(self)
+ btn = CustomButton(item, '折叠', self, objectName='testBtn')
+ self.setItemWidget(item, btn)
+
+ # 被折叠控件
+ item = QListWidgetItem(self)
+ # 通过按钮的选中来隐藏下面的item
+ btn.toggled.connect(item.setHidden)
+ self.setItemWidget(item, CustomWidget(item, self))
+
+
+if __name__ == '__main__':
+ import sys
+ import cgitb
+
+ cgitb.enable(format='text')
+ from PyQt5.QtWidgets import QApplication
+
+ app = QApplication(sys.argv)
+ # 通过qss改变按钮的高度
+ app.setStyleSheet('#testBtn{min-height:40px;}')
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QListWidget/HotPlaylist.py b/QListWidget/HotPlaylist.py
index bcba0d65a5d4ba08431f2c51516d1a1ca72749e9..e457b5cbc78b9567984b7ca8e224ba713cdeb1d3 100644
--- a/QListWidget/HotPlaylist.py
+++ b/QListWidget/HotPlaylist.py
@@ -1,195 +1,45 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-
-'''
-Created on 2018年2月4日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+"""
+Created on 2023/02/22
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
@email: 892768447@qq.com
-@file: TencentMovieHotPlay_ListWidget
-@description:
-'''
+@file: HotPlaylist.py
+@description:
+"""
+
import os
import sys
-import webbrowser
-
-from PyQt5.QtCore import QSize, Qt, QUrl, QTimer
-from PyQt5.QtGui import QPainter, QFont, QLinearGradient, QGradient, QColor,\
- QBrush, QPaintEvent, QPixmap
-from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
-from PyQt5.QtSvg import QSvgWidget
-from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel,\
- QHBoxLayout, QSpacerItem, QSizePolicy, QAbstractSlider,\
- QListWidget, QListWidgetItem
+from Lib.CoverItemWidget import CoverItemWidget
from lxml.etree import HTML # @UnresolvedImport
+try:
+ from PyQt5.QtCore import QTimer, QUrl
+ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
+ from PyQt5.QtSvg import QSvgWidget
+ from PyQt5.QtWidgets import (QAbstractSlider, QApplication, QListWidget,
+ QListWidgetItem)
+except ImportError:
+ from PySide2.QtCore import QTimer, QUrl
+ from PySide2.QtNetwork import QNetworkAccessManager, QNetworkRequest
+ from PySide2.QtSvg import QSvgWidget
+ from PySide2.QtWidgets import (QAbstractSlider, QApplication, QListWidget,
+ QListWidgetItem)
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+# offset=0,35,70,105
+Url = "https://music.163.com/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset={0}"
-# offset=0,30,60,90
-Url = "http://v.qq.com/x/list/movie?pay=-1&offset={0}"
+Agent = b"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.50"
-# 播放量图标
-Svg_icon_play_sm = '''
-
-
-'''.encode()
+Referer = b"https://music.163.com"
-Svg_icon_loading = '''
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- '''.encode()
-
-# 主演
+# 作者
Actor = '''{title} '''
-class CoverLabel(QLabel):
-
- def __init__(self, cover_path, cover_title, video_url, *args, **kwargs):
- super(CoverLabel, self).__init__(*args, **kwargs)
-# super(CoverLabel, self).__init__(
-# ' '.format(os.path.abspath(cover_path)), *args, **kwargs)
- self.setCursor(Qt.PointingHandCursor)
- self.setScaledContents(True)
- self.setMinimumSize(220, 308)
- self.setMaximumSize(220, 308)
- self.cover_path = cover_path
- self.cover_title = cover_title
- self.video_url = video_url
- self.setPixmap(QPixmap(cover_path))
-
- def setCoverPath(self, path):
- self.cover_path = path
-
- def mouseReleaseEvent(self, event):
- super(CoverLabel, self).mouseReleaseEvent(event)
- webbrowser.open_new_tab(self.video_url)
-
- def paintEvent(self, event):
- super(CoverLabel, self).paintEvent(event)
- if hasattr(self, "cover_title") and self.cover_title != "":
- # 底部绘制文字
- painter = QPainter(self)
- rect = self.rect()
- # 粗略字体高度
- painter.save()
- fheight = self.fontMetrics().height()
- # 底部矩形框背景渐变颜色
- bottomRectColor = QLinearGradient(
- rect.width() / 2, rect.height() - 24 - fheight,
- rect.width() / 2, rect.height())
- bottomRectColor.setSpread(QGradient.PadSpread)
- bottomRectColor.setColorAt(0, QColor(255, 255, 255, 70))
- bottomRectColor.setColorAt(1, QColor(0, 0, 0, 50))
- # 画半透明渐变矩形框
- painter.setPen(Qt.NoPen)
- painter.setBrush(QBrush(bottomRectColor))
- painter.drawRect(rect.x(), rect.height() - 24 -
- fheight, rect.width(), 24 + fheight)
- painter.restore()
- # 距离底部一定高度画文字
- font = self.font() or QFont()
- font.setPointSize(8)
- painter.setFont(font)
- painter.setPen(Qt.white)
- rect.setHeight(rect.height() - 12) # 底部减去一定高度
- painter.drawText(rect, Qt.AlignHCenter |
- Qt.AlignBottom, self.cover_title)
-
-
-class ItemWidget(QWidget):
-
- def __init__(self, cover_path, figure_info, figure_title,
- figure_score, figure_desc, figure_count, video_url, cover_url, img_path, manager, *args, **kwargs):
- super(ItemWidget, self).__init__(*args, **kwargs)
- self.setMaximumSize(220, 420)
- self.setMaximumSize(220, 420)
- self.img_path = img_path
- self.cover_url = cover_url
- self._manager = manager
- layout = QVBoxLayout(self)
- layout.setContentsMargins(10, 20, 10, 0)
- # 图片label
- self.clabel = CoverLabel(cover_path, figure_info, video_url, self)
- layout.addWidget(self.clabel)
-
- # 片名和分数
- flayout = QHBoxLayout()
- flayout.addWidget(QLabel(figure_title, self))
- flayout.addItem(QSpacerItem(
- 20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
- flayout.addWidget(QLabel(figure_score, self, styleSheet="color: red;"))
- layout.addLayout(flayout)
-
- # 主演
- layout.addWidget(
- QLabel(figure_desc, self, styleSheet="color: #999999;", openExternalLinks=True))
-
- # 播放量
- blayout = QHBoxLayout()
- count_icon = QSvgWidget(self)
- count_icon.setMaximumSize(16, 16)
- count_icon.load(Svg_icon_play_sm)
- blayout.addWidget(count_icon)
- blayout.addWidget(
- QLabel(figure_count, self, styleSheet="color: #999999;"))
- layout.addLayout(blayout)
-
- def setCover(self, path):
- self.clabel.setCoverPath(path)
- self.clabel.setPixmap(QPixmap(path))
-# self.clabel.setText(' '.format(os.path.abspath(path)))
-
- def sizeHint(self):
- # 每个item控件的大小
- return QSize(220, 420)
-
- def event(self, event):
- if isinstance(event, QPaintEvent):
- if event.rect().height() > 20 and hasattr(self, "clabel"):
- if self.clabel.cover_path.find("pic_v.png") > -1: # 封面未加载
- # print("start download img:", self.cover_url)
- req = QNetworkRequest(QUrl(self.cover_url))
- # 设置两个自定义属性方便后期reply中处理
- req.setAttribute(QNetworkRequest.User + 1, self)
- req.setAttribute(QNetworkRequest.User + 2, self.img_path)
- self._manager.get(req) # 调用父窗口中的下载器下载
- return super(ItemWidget, self).event(event)
-
-
class Window(QListWidget):
-
Page = 0
def __init__(self, *args, **kwargs):
@@ -204,16 +54,18 @@ class Window(QListWidget):
# 连接竖着的滚动条滚动事件
self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered)
# 进度条
- self.loadWidget = QSvgWidget(
- self, minimumHeight=120, minimumWidth=120, visible=False)
- self.loadWidget.load(Svg_icon_loading)
+ self.loadWidget = QSvgWidget(self,
+ minimumHeight=120,
+ minimumWidth=120,
+ visible=False)
+ self.loadWidget.load('Data/Svg_icon_loading.svg')
# 异步网络下载管理器
self._manager = QNetworkAccessManager(self)
self._manager.finished.connect(self.onFinished)
def load(self):
- if self.Page == -1:
+ if self.Page == -1 or self.Page > 10:
return
self._loadStart = True
self.loadWidget.setVisible(True)
@@ -221,9 +73,11 @@ class Window(QListWidget):
QTimer.singleShot(1000, self._load)
def _load(self):
- print("load url:", Url.format(self.Page * 30))
- url = QUrl(Url.format(self.Page * 30))
- self._manager.get(QNetworkRequest(url))
+ print("load url:", Url.format(self.Page * 35))
+ url = QUrl(Url.format(self.Page * 35))
+ req = QNetworkRequest(url)
+ req.setRawHeader(b"User-Agent", Agent)
+ self._manager.get(req)
def onFinished(self, reply):
# 请求完成后会调用该函数
@@ -244,11 +98,13 @@ class Window(QListWidget):
self.loadWidget.setVisible(False)
def _parseHtml(self, html):
+ # print(html)
# encoding = chardet.detect(html) or {}
# html = html.decode(encoding.get("encoding","utf-8"))
html = HTML(html)
# 查找所有的li list_item
- lis = html.xpath("//li[@class='list_item']")
+ lis = html.xpath("//ul[@id='m-pl-container']/li")
+ # print(lis)
if not lis:
self.Page = -1 # 后面没有页面了
return
@@ -257,28 +113,30 @@ class Window(QListWidget):
def _makeItem(self, lis):
for li in lis:
- a = li.find("a")
- video_url = a.get("href") # 视频播放地址
- img = a.find("img")
- cover_url = "http:" + img.get("r-lazyload") # 封面图片
- figure_title = img.get("alt") # 电影名
- figure_info = a.find("div/span")
- figure_info = "" if figure_info is None else figure_info.text # 影片信息
- figure_score = "".join(li.xpath(".//em/text()")) # 评分
- # 主演
- figure_desc = "主演: " + \
- "".join([Actor.format(**dict(fd.items()))
- for fd in li.xpath(".//div[@class='figure_desc']/a")])
+ a = li.find('.//div/a')
+ play_url = "https://music.163.com" + a.get("href") # 歌单播放地址
+ img = li.find(".//div/img")
+ cover_url = img.get("src") # 封面图片
+ playlist_title = a.get("title") # 歌单名
+ # 歌手
+ author_info = li.xpath(".//p[2]/a")[0]
+ playlist_author = "".format(
+ Actor.format(href="https://music.163.com" +
+ author_info.get("href"),
+ title=author_info.get("title")))
# 播放数
- figure_count = (
- li.xpath(".//div[@class='figure_count']/span/text()") or [""])[0]
+ play_count = (li.xpath(".//div/div/span[2]/text()") or [""])[0]
path = "cache/{0}.jpg".format(
- os.path.splitext(os.path.basename(video_url))[0])
+ os.path.splitext(os.path.basename(cover_url).split('?')[0])[0])
cover_path = "Data/pic_v.png"
if os.path.isfile(path):
cover_path = path
- iwidget = ItemWidget(cover_path, figure_info, figure_title,
- figure_score, figure_desc, figure_count, video_url, cover_url, path, self._manager, self)
+
+ # print(cover_path, playlist_title,
+ # playlist_author, play_count, play_url, cover_url, path)
+ iwidget = CoverItemWidget(self, manager=self._manager)
+ iwidget.init(cover_path, playlist_title, playlist_author,
+ play_count, play_url, cover_url, path)
item = QListWidgetItem(self)
item.setSizeHint(iwidget.sizeHint())
self.setItemWidget(item, iwidget)
@@ -289,7 +147,8 @@ class Window(QListWidget):
if action != QAbstractSlider.SliderMove or self._loadStart:
return
# 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断
- if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar().maximum():
+ if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar(
+ ).maximum():
# 可以下一页了
self.load()
@@ -298,9 +157,7 @@ class Window(QListWidget):
self.loadWidget.setGeometry(
int((self.width() - self.loadWidget.minimumWidth()) / 2),
int((self.height() - self.loadWidget.minimumHeight()) / 2),
- self.loadWidget.minimumWidth(),
- self.loadWidget.minimumHeight()
- )
+ self.loadWidget.minimumWidth(), self.loadWidget.minimumHeight())
if __name__ == "__main__":
diff --git a/QListWidget/Lib/CoverItemWidget.py b/QListWidget/Lib/CoverItemWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ef3ff00225af2269e33bd3c3ea4c47ba54c2adf
--- /dev/null
+++ b/QListWidget/Lib/CoverItemWidget.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/22
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CoverItemWidget.py
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import QSize, QUrl
+ from PyQt5.QtGui import QPaintEvent, QPixmap
+ from PyQt5.QtNetwork import QNetworkRequest
+ from PyQt5.QtWidgets import QWidget
+except ImportError:
+ from PySide2.QtCore import QSize, QUrl
+ from PySide2.QtGui import QPaintEvent, QPixmap
+ from PySide2.QtNetwork import QNetworkRequest
+ from PySide2.QtWidgets import QWidget
+
+from .Ui_CoverItemWidget import Ui_CoverItemWidget # @UnresolvedImport
+
+
+class CoverItemWidget(QWidget, Ui_CoverItemWidget):
+
+ def __init__(self, *args, **kwargs):
+ self._manager = kwargs.pop('manager', None)
+ super(CoverItemWidget, self).__init__(*args, **kwargs)
+ self.setupUi(self)
+
+ def init(self, cover_path, playlist_title, playlist_author, play_count,
+ play_url, cover_url, img_path):
+ self.img_path = img_path
+ self.cover_url = cover_url
+ # 图片label
+ self.labelCover.init(cover_path, play_url, play_count)
+
+ # 歌单
+ self.labelTitle.setText(playlist_title)
+
+ # 作者
+ self.labelAuthor.setText(playlist_author)
+
+ def setCover(self, path):
+ self.labelCover.setCoverPath(path)
+ self.labelCover.setPixmap(QPixmap(path))
+
+ def sizeHint(self):
+ # 每个item控件的大小
+ return QSize(200, 256)
+
+ def event(self, event):
+ if isinstance(event, QPaintEvent):
+ if event.rect().height() > 20 and hasattr(self, "labelCover"):
+ if self.labelCover.cover_path.find("pic_v.png") > -1: # 封面未加载
+ # print("start download img:", self.cover_url)
+ req = QNetworkRequest(QUrl(self.cover_url))
+ # 设置两个自定义属性方便后期reply中处理
+ req.setAttribute(QNetworkRequest.User + 1, self)
+ req.setAttribute(QNetworkRequest.User + 2, self.img_path)
+ if self._manager:
+ self._manager.get(req) # 调用父窗口中的下载器下载
+ return super(CoverItemWidget, self).event(event)
diff --git a/QListWidget/Lib/CoverLabel.py b/QListWidget/Lib/CoverLabel.py
new file mode 100644
index 0000000000000000000000000000000000000000..dbee15af4cf251de21d8d7d4194b18cb8a192bbc
--- /dev/null
+++ b/QListWidget/Lib/CoverLabel.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/22
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CoverLabel.py
+@description:
+"""
+import webbrowser
+
+try:
+ from PyQt5.QtGui import QPixmap
+ from PyQt5.QtWidgets import QLabel
+except ImportError:
+ from PySide2.QtGui import QPixmap
+ from PySide2.QtWidgets import QLabel
+
+from .Ui_CoverLabel import Ui_CoverLabel # @UnresolvedImport
+
+
+class CoverLabel(QLabel, Ui_CoverLabel):
+
+ def __init__(self, *args, **kwargs):
+ super(CoverLabel, self).__init__(*args, **kwargs)
+ self.setupUi(self)
+
+ def init(self, cover_path, play_url, play_count):
+ self.cover_path = cover_path
+ self.play_url = play_url
+ self.setPixmap(QPixmap(cover_path))
+ self.labelHeadset.setPixmap(QPixmap('Data/Svg_icon_headset_sm.svg'))
+ self.labelPlay.setPixmap(QPixmap('Data/Svg_icon_play_sm.svg'))
+ self.labelCount.setStyleSheet('color: #999999;')
+ self.labelCount.setText(play_count)
+
+ def setCoverPath(self, path):
+ self.cover_path = path
+
+ def mouseReleaseEvent(self, event):
+ super(CoverLabel, self).mouseReleaseEvent(event)
+ webbrowser.open_new_tab(self.play_url)
diff --git a/QListWidget/Lib/Ui_CoverItemWidget.py b/QListWidget/Lib/Ui_CoverItemWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..c048f686e4662268e855850c42d7da8a3182c91a
--- /dev/null
+++ b/QListWidget/Lib/Ui_CoverItemWidget.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'CoverItemWidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.2
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+try:
+ from PyQt5 import QtCore, QtGui, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtGui, QtWidgets
+
+
+class Ui_CoverItemWidget(object):
+ def setupUi(self, CoverItemWidget):
+ CoverItemWidget.setObjectName("CoverItemWidget")
+ CoverItemWidget.setMinimumSize(QtCore.QSize(200, 256))
+ CoverItemWidget.setMaximumSize(QtCore.QSize(200, 256))
+ CoverItemWidget.setWindowTitle("")
+ self.verticalLayout = QtWidgets.QVBoxLayout(CoverItemWidget)
+ self.verticalLayout.setContentsMargins(10, 10, 10, 10)
+ self.verticalLayout.setSpacing(12)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.labelCover = CoverLabel(CoverItemWidget)
+ self.labelCover.setMinimumSize(QtCore.QSize(180, 180))
+ self.labelCover.setText("")
+ self.labelCover.setScaledContents(True)
+ self.labelCover.setAlignment(QtCore.Qt.AlignCenter)
+ self.labelCover.setObjectName("labelCover")
+ self.verticalLayout.addWidget(self.labelCover)
+ self.labelTitle = QtWidgets.QLabel(CoverItemWidget)
+ font = QtGui.QFont()
+ font.setPointSize(10)
+ self.labelTitle.setFont(font)
+ self.labelTitle.setText("")
+ self.labelTitle.setObjectName("labelTitle")
+ self.verticalLayout.addWidget(self.labelTitle)
+ self.labelAuthor = QtWidgets.QLabel(CoverItemWidget)
+ self.labelAuthor.setText("")
+ self.labelAuthor.setObjectName("labelAuthor")
+ self.verticalLayout.addWidget(self.labelAuthor)
+
+ self.retranslateUi(CoverItemWidget)
+ QtCore.QMetaObject.connectSlotsByName(CoverItemWidget)
+
+ def retranslateUi(self, CoverItemWidget):
+ pass
+from .CoverLabel import CoverLabel
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ CoverItemWidget = QtWidgets.QWidget()
+ ui = Ui_CoverItemWidget()
+ ui.setupUi(CoverItemWidget)
+ CoverItemWidget.show()
+ sys.exit(app.exec_())
diff --git a/QListWidget/Lib/Ui_CoverLabel.py b/QListWidget/Lib/Ui_CoverLabel.py
new file mode 100644
index 0000000000000000000000000000000000000000..86e1526a548fbd165e68d695447c530166faaee1
--- /dev/null
+++ b/QListWidget/Lib/Ui_CoverLabel.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'CoverLabel.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.2
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+try:
+ from PyQt5 import QtCore, QtGui, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtGui, QtWidgets
+
+
+class Ui_CoverLabel(object):
+ def setupUi(self, CoverLabel):
+ CoverLabel.setObjectName("CoverLabel")
+ CoverLabel.resize(180, 180)
+ CoverLabel.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
+ CoverLabel.setWindowTitle("")
+ self.verticalLayout = QtWidgets.QVBoxLayout(CoverLabel)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ spacerItem = QtWidgets.QSpacerItem(20, 135, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem)
+ self.widgetBottom = QtWidgets.QWidget(CoverLabel)
+ self.widgetBottom.setStyleSheet("#widgetBottom {\n"
+" background-color: rgba(0, 0, 0, 150);\n"
+"}")
+ self.widgetBottom.setObjectName("widgetBottom")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.widgetBottom)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.labelHeadset = QtWidgets.QLabel(self.widgetBottom)
+ self.labelHeadset.setMinimumSize(QtCore.QSize(16, 16))
+ self.labelHeadset.setText("")
+ self.labelHeadset.setObjectName("labelHeadset")
+ self.horizontalLayout.addWidget(self.labelHeadset)
+ self.labelCount = QtWidgets.QLabel(self.widgetBottom)
+ self.labelCount.setText("")
+ self.labelCount.setObjectName("labelCount")
+ self.horizontalLayout.addWidget(self.labelCount)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem1)
+ self.labelPlay = QtWidgets.QLabel(self.widgetBottom)
+ self.labelPlay.setMinimumSize(QtCore.QSize(16, 16))
+ self.labelPlay.setText("")
+ self.labelPlay.setObjectName("labelPlay")
+ self.horizontalLayout.addWidget(self.labelPlay)
+ self.verticalLayout.addWidget(self.widgetBottom)
+
+ self.retranslateUi(CoverLabel)
+ QtCore.QMetaObject.connectSlotsByName(CoverLabel)
+
+ def retranslateUi(self, CoverLabel):
+ pass
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ CoverLabel = QtWidgets.QWidget()
+ ui = Ui_CoverLabel()
+ ui.setupUi(CoverLabel)
+ CoverLabel.show()
+ sys.exit(app.exec_())
diff --git a/QListWidget/Lib/__init__.py b/QListWidget/Lib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QListWidget/README.md b/QListWidget/README.md
index 0a25cfae4899492195c0d5222c0385846801a4a2..69610a6f4b5adc142f263e05ef4fd240515488f7 100644
--- a/QListWidget/README.md
+++ b/QListWidget/README.md
@@ -1,5 +1,12 @@
# QListView
+- 目录
+ - [删除自定义Item](#1删除自定义Item)
+ - [自定义可拖拽Item](#2自定义可拖拽Item)
+ - [音乐热歌列表](#3音乐热歌列表)
+ - [仿折叠控件效果](#4仿折叠控件效果)
+ - [列表常用信号](#5列表常用信号)
+
## 1、删除自定义Item
[运行 DeleteCustomItem.py](DeleteCustomItem.py)
@@ -15,7 +22,7 @@

-## 3、腾讯视频热播列表
+## 3、音乐热歌列表
[运行 HotPlaylist.py](HotPlaylist.py)
简单思路说明:
@@ -36,4 +43,21 @@
2. `setWrapping(True)`
3. `setResizeMode(QListWidget.Adjust)`
-
\ No newline at end of file
+
+
+## 4、仿折叠控件效果
+[运行 FoldWidget.py](FoldWidget.py)
+
+1. 利用`QListWidget`设置Item的自定义控件
+2. `QListWidget`通过间隔设置`QPushButton`和`CustomWidget`来添加
+3. 绑定按钮的选中状态通过`setHidden`设置Item的隐藏和显示
+4. 自定义控件中尺寸发生变化后需要调用`adjustSize()`来同步
+
+
+
+## 5、列表常用信号
+[运行 SignalsExample.py](SignalsExample.py)
+
+根据官网文档 https://doc.qt.io/qt-5/qlistwidget.html#signals 中的信号介绍编写
+
+
\ No newline at end of file
diff --git a/QListWidget/ScreenShot/FoldWidget.gif b/QListWidget/ScreenShot/FoldWidget.gif
new file mode 100644
index 0000000000000000000000000000000000000000..bf9a2ab06082c6b74a4c93c270c98f3ff5aa5980
Binary files /dev/null and b/QListWidget/ScreenShot/FoldWidget.gif differ
diff --git a/QListWidget/ScreenShot/SignalsExample.gif b/QListWidget/ScreenShot/SignalsExample.gif
new file mode 100644
index 0000000000000000000000000000000000000000..37357e61948e224beb27ee3a985bcef3848dba8a
Binary files /dev/null and b/QListWidget/ScreenShot/SignalsExample.gif differ
diff --git a/QListWidget/SignalsExample.py b/QListWidget/SignalsExample.py
new file mode 100644
index 0000000000000000000000000000000000000000..38f20c2bf3159df53f5d4baaec78abd86f2cab45
--- /dev/null
+++ b/QListWidget/SignalsExample.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年7月3日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QListWidget.SignalsExample
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QColor
+ from PyQt5.QtWidgets import QWidget, QHBoxLayout, QListWidget, QPlainTextEdit, \
+ QListWidgetItem, QAbstractItemView, QListView, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QColor
+ from PySide2.QtWidgets import QWidget, QHBoxLayout, QListWidget, QPlainTextEdit, \
+ QListWidgetItem, QAbstractItemView, QListView, QApplication
+
+
+def formatColor(text, color):
+ return '{1} '.format(color.name(), text)
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QHBoxLayout(self)
+
+ self.listWidget = QListWidget(self)
+ self.listWidget.setAlternatingRowColors(True)
+ self.listWidget.setSelectionMode(QAbstractItemView.ExtendedSelection)
+ self.listWidget.setMovement(QListView.Free)
+ self.listWidget.setMouseTracking(True) # 用于itemEntered信号
+
+ self.resultView = QPlainTextEdit(self)
+ self.resultView.setReadOnly(True)
+
+ layout.addWidget(self.listWidget)
+ layout.addWidget(self.resultView)
+
+ self.initData()
+ self.initSignals()
+
+ def initData(self):
+ # 初始化模拟数据
+ for i in range(100):
+ item = QListWidgetItem('Item {0}'.format(i), self.listWidget)
+ if i % 3 == 0:
+ item.setFlags(item.flags() | Qt.ItemIsEditable)
+
+ def initSignals(self):
+ # 初始化信号
+ self.listWidget.currentItemChanged.connect(self.onCurrentItemChanged)
+ self.listWidget.currentRowChanged.connect(self.onCurrentRowChanged)
+ self.listWidget.currentTextChanged.connect(self.onCurrentTextChanged)
+ self.listWidget.itemActivated.connect(self.onItemActivated)
+ self.listWidget.itemChanged.connect(self.onItemChanged)
+ self.listWidget.itemClicked.connect(self.onItemClicked)
+ self.listWidget.itemDoubleClicked.connect(self.onItemDoubleClicked)
+ self.listWidget.itemEntered.connect(self.onItemEntered)
+ self.listWidget.itemPressed.connect(self.onItemPressed)
+ self.listWidget.itemSelectionChanged.connect(
+ self.onItemSelectionChanged)
+
+ def onCurrentItemChanged(self, current, previous):
+ current = current.text() if current else ''
+ previous = previous.text() if previous else ''
+ self.resultView.appendHtml(
+ '{0}: [{1}] -> [{2}]'.format(
+ formatColor('currentItemChanged', QColor(Qt.red)),
+ current, previous))
+
+ def onCurrentRowChanged(self, currentRow):
+ self.resultView.appendHtml(
+ '{0}: {1}'.format(
+ formatColor('currentRowChanged', QColor(Qt.green)),
+ currentRow))
+
+ def onCurrentTextChanged(self, currentText):
+ self.resultView.appendHtml(
+ '{0}: {1}'.format(
+ formatColor('currentTextChanged', QColor(Qt.yellow)), currentText))
+
+ def onItemActivated(self, item):
+ self.resultView.appendHtml(
+ '{0}: {1}'.format(
+ formatColor('itemActivated', QColor(Qt.blue)), item.text()))
+
+ def onItemChanged(self, item):
+ self.resultView.appendHtml(
+ '{0}: {1}'.format(
+ formatColor('itemChanged', QColor(Qt.cyan)), item.text()))
+
+ def onItemClicked(self, item):
+ self.resultView.appendHtml(
+ '{0}: {1}'.format(formatColor('itemClicked', QColor(Qt.magenta)), item.text()))
+
+ def onItemDoubleClicked(self, item):
+ self.resultView.appendHtml(
+ '{0}: {1}'.format(formatColor('itemDoubleClicked', QColor(Qt.darkGreen)), item.text()))
+
+ def onItemEntered(self, item):
+ self.resultView.appendHtml(
+ '{0}: {1}'.format(formatColor('itemEntered', QColor(Qt.darkCyan)), item.text()))
+
+ def onItemPressed(self, item):
+ print(item)
+ self.resultView.appendHtml(
+ '{0}: {1}'.format(formatColor('itemPressed', QColor(Qt.darkYellow)), item.text()))
+
+ def onItemSelectionChanged(self):
+ self.resultView.appendPlainText('itemSelectionChanged')
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QMenu/MultiSelect.py b/QMenu/MultiSelect.py
index 17bf9bd57240df2424dc7b9487e60968f764c636..31a78c8ae4c25a3b6630882486ba695091aaa949 100644
--- a/QMenu/MultiSelect.py
+++ b/QMenu/MultiSelect.py
@@ -4,20 +4,16 @@
"""
Created on 2018年10月24日
@author: Irony
-@site: https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: MultiSelect
@description:
"""
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QMenu,\
- QAction
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QMenu, QAction
+except ImportError:
+ from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QMenu, QAction
class Window(QWidget):
@@ -70,8 +66,9 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.enable(1, None, 5, 'text')
- from PyQt5.QtWidgets import QApplication
+
+ cgitb.enable(format='text')
+
app = QApplication(sys.argv)
w = Window()
w.resize(400, 400)
diff --git a/QMenu/QQMenu.py b/QMenu/QQMenu.py
new file mode 100644
index 0000000000000000000000000000000000000000..a561eb4727080b9217718bb714497fe372dcd610
--- /dev/null
+++ b/QMenu/QQMenu.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2021/4/7
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QQMenu
+@description:
+"""
+import string
+from random import choice, randint
+
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QPixmap, QPainter, QFont, QIcon
+ from PyQt5.QtWidgets import QLabel, QMenu, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QPixmap, QPainter, QFont, QIcon
+ from PySide2.QtWidgets import QLabel, QMenu, QApplication
+
+Style = """
+QMenu {
+ /* 半透明效果 */
+ background-color: rgba(255, 255, 255, 230);
+ border: none;
+ border-radius: 4px;
+}
+
+QMenu::item {
+ border-radius: 4px;
+ /* 这个距离很麻烦需要根据菜单的长度和图标等因素微调 */
+ padding: 8px 48px 8px 36px; /* 36px是文字距离左侧距离*/
+ background-color: transparent;
+}
+
+/* 鼠标悬停和按下效果 */
+QMenu::item:selected {
+ border-radius: 0px;
+ /* 半透明效果 */
+ background-color: rgba(232, 232, 232, 232);
+}
+
+/* 禁用效果 */
+QMenu::item:disabled {
+ background-color: transparent;
+}
+
+/* 图标距离左侧距离 */
+QMenu::icon {
+ left: 15px;
+}
+
+/* 分割线效果 */
+QMenu::separator {
+ height: 1px;
+ background-color: rgb(232, 236, 243);
+}
+"""
+
+
+def get_icon():
+ # 测试模拟图标
+ pixmap = QPixmap(16, 16)
+ pixmap.fill(Qt.transparent)
+ painter = QPainter()
+ painter.begin(pixmap)
+ painter.setFont(QFont('Webdings', 11))
+ painter.setPen(Qt.GlobalColor(randint(4, 18)))
+ painter.drawText(0, 0, 16, 16, Qt.AlignCenter,
+ choice(string.ascii_letters))
+ painter.end()
+ return QIcon(pixmap)
+
+
+def about_qt():
+ # 关于Qt
+ QApplication.instance().aboutQt()
+
+
+class Window(QLabel):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 400)
+ self.setAlignment(Qt.AlignCenter)
+ self.setText('右键弹出菜单')
+ self.context_menu = QMenu(self)
+ self.init_menu()
+
+ def contextMenuEvent(self, event):
+ self.context_menu.exec_(event.globalPos())
+
+ def init_menu(self):
+ # 背景透明
+ self.context_menu.setAttribute(Qt.WA_TranslucentBackground)
+ # 无边框、去掉自带阴影
+ self.context_menu.setWindowFlags(
+ self.context_menu.windowFlags() | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint)
+
+ # 模拟菜单项
+ for i in range(10):
+ if i % 2 == 0:
+ action = self.context_menu.addAction('菜单 %d' % i, about_qt)
+ action.setEnabled(i % 4)
+ elif i % 3 == 0:
+ self.context_menu.addAction(get_icon(), '菜单 %d' % i, about_qt)
+ if i % 4 == 0:
+ self.context_menu.addSeparator()
+ if i % 5 == 0:
+ # 二级菜单
+ # 二级菜单
+ menu = QMenu('二级菜单 %d' % i, self.context_menu)
+ # 背景透明
+ menu.setAttribute(Qt.WA_TranslucentBackground)
+ # 无边框、去掉自带阴影
+ menu.setWindowFlags(menu.windowFlags() | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint)
+ for j in range(3):
+ menu.addAction(get_icon(), '子菜单 %d' % j)
+ self.context_menu.addMenu(menu)
+
+
+if __name__ == '__main__':
+ import sys
+ import cgitb
+
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ app.setStyleSheet(Style)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QMenu/README.en.md b/QMenu/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QMenu/README.md b/QMenu/README.md
index 7895d4e7a60901e58e346f6710af3f8442fd4139..33271cab8d4bd25973d51ee2bd5be0b4654bdfc3 100644
--- a/QMenu/README.md
+++ b/QMenu/README.md
@@ -1,5 +1,9 @@
# QMenu
+- 目录
+ - [菜单设置多选并且不关闭](#1菜单设置多选并且不关闭)
+ - [仿QQ右键菜单](#2仿QQ右键菜单)
+
## 1、菜单设置多选并且不关闭
[运行 MultiSelect.py](MultiSelect.py)
@@ -32,4 +36,9 @@ def _menu_mouseReleaseEvent(self, event):
action.activate(action.Trigger)
```
-
\ No newline at end of file
+
+
+## 2、仿QQ右键菜单
+[运行 QQMenu.py](QQMenu.py)
+
+
\ No newline at end of file
diff --git a/QMenu/ScreenShot/QQMenu.gif b/QMenu/ScreenShot/QQMenu.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e989d9dd36c392396759ff3b19a6c12e7cb7c109
Binary files /dev/null and b/QMenu/ScreenShot/QQMenu.gif differ
diff --git a/QMessageBox/ChineseText.py b/QMessageBox/ChineseText.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ba727789bcebd66de9d9f6ac8b0910938bc78e5
--- /dev/null
+++ b/QMessageBox/ChineseText.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年7月10日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ChineseText
+@description: 修改消息对话框文字汉化
+"""
+import sys
+
+try:
+ from PyQt5.QtWidgets import QApplication, QMessageBox
+except ImportError:
+ from PySide2.QtWidgets import QApplication, QMessageBox
+
+TextStyle = """
+QMessageBox QPushButton[text="OK"] {
+ qproperty-text: "好的";
+}
+QMessageBox QPushButton[text="Open"] {
+ qproperty-text: "打开";
+}
+QMessageBox QPushButton[text="Save"] {
+ qproperty-text: "保存";
+}
+QMessageBox QPushButton[text="Cancel"] {
+ qproperty-text: "取消";
+}
+QMessageBox QPushButton[text="Close"] {
+ qproperty-text: "关闭";
+}
+QMessageBox QPushButton[text="Discard"] {
+ qproperty-text: "不保存";
+}
+QMessageBox QPushButton[text="Don't Save"] {
+ qproperty-text: "不保存";
+}
+QMessageBox QPushButton[text="Apply"] {
+ qproperty-text: "应用";
+}
+QMessageBox QPushButton[text="Reset"] {
+ qproperty-text: "重置";
+}
+QMessageBox QPushButton[text="Restore Defaults"] {
+ qproperty-text: "恢复默认";
+}
+QMessageBox QPushButton[text="Help"] {
+ qproperty-text: "帮助";
+}
+QMessageBox QPushButton[text="Save All"] {
+ qproperty-text: "保存全部";
+}
+QMessageBox QPushButton[text="&Yes"] {
+ qproperty-text: "是";
+}
+QMessageBox QPushButton[text="Yes to &All"] {
+ qproperty-text: "全部都是";
+}
+QMessageBox QPushButton[text="&No"] {
+ qproperty-text: "不";
+}
+QMessageBox QPushButton[text="N&o to All"] {
+ qproperty-text: "全部都不";
+}
+QMessageBox QPushButton[text="Abort"] {
+ qproperty-text: "终止";
+}
+QMessageBox QPushButton[text="Retry"] {
+ qproperty-text: "重试";
+}
+QMessageBox QPushButton[text="Ignore"] {
+ qproperty-text: "忽略";
+}
+"""
+
+app = QApplication(sys.argv)
+
+# 通过QSS样式的方式设置按钮文字
+app.setStyleSheet(TextStyle)
+
+# 由于年代久远,Qt5的翻译功能没有更新,还是用的旧的结构导致无法翻译
+# 这里不使用(需要修改ts源码重新编译成qm)
+# translator = QTranslator()
+# print(translator.load(QLocale(), 'qt', '_', QLibraryInfo.location(
+# QLibraryInfo.TranslationsPath)))
+# app.installTranslator(translator)
+
+QMessageBox.information(
+ None, 'information', '消息',
+ QMessageBox.Ok |
+ QMessageBox.Open |
+ QMessageBox.Save |
+ QMessageBox.Cancel |
+ QMessageBox.Close |
+ QMessageBox.Discard |
+ QMessageBox.Apply |
+ QMessageBox.Reset |
+ QMessageBox.RestoreDefaults |
+ QMessageBox.Help |
+ QMessageBox.SaveAll |
+ QMessageBox.Yes |
+ QMessageBox.YesToAll |
+ QMessageBox.No |
+ QMessageBox.NoToAll |
+ QMessageBox.Abort |
+ QMessageBox.Retry |
+ QMessageBox.Ignore
+)
+sys.exit()
diff --git a/QMessageBox/CountDownClose.py b/QMessageBox/CountDownClose.py
index 2252ce00c9dbc6b0c56e12768b594cdc10636807..e582bb58057d83d28917736210a650073baca27b 100644
--- a/QMessageBox/CountDownClose.py
+++ b/QMessageBox/CountDownClose.py
@@ -4,22 +4,19 @@
"""
Created on 2018年6月22日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: MessageBox
@description:
"""
from random import randrange
-from PyQt5.QtCore import QTimer
-from PyQt5.QtWidgets import QMessageBox
-
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QTimer
+ from PyQt5.QtWidgets import QApplication, QMessageBox, QPushButton
+except ImportError:
+ from PySide2.QtCore import QTimer
+ from PySide2.QtWidgets import QApplication, QMessageBox, QPushButton
class MessageBox(QMessageBox):
@@ -53,7 +50,7 @@ class MessageBox(QMessageBox):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication, QPushButton
+
app = QApplication(sys.argv)
w = QPushButton('点击弹出对话框')
w.resize(200, 200)
diff --git a/QMessageBox/CustomColorIcon.py b/QMessageBox/CustomColorIcon.py
index 53d95c135f89187e20a4fd94d4936f9940514489..e41748c3271a46f27c7f98d20d0307a99167c3d1 100644
--- a/QMessageBox/CustomColorIcon.py
+++ b/QMessageBox/CustomColorIcon.py
@@ -1,23 +1,21 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月17日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: CustomBtnIcon
@description:
-'''
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+"""
import sys
-from PyQt5.QtWidgets import QApplication, QMessageBox
-
+try:
+ from PyQt5.QtWidgets import QApplication, QMessageBox
+except ImportError:
+ from PySide2.QtWidgets import QApplication, QMessageBox
app = QApplication(sys.argv)
app.setStyleSheet('''QDialogButtonBox {
@@ -62,15 +60,15 @@ QMessageBox QPushButton[text="Apply"] {
}
''')
QMessageBox.information(None, "information", "消息",
- QMessageBox.Apply |
- QMessageBox.Cancel |
- QMessageBox.Close |
- QMessageBox.Discard |
- QMessageBox.Help |
- QMessageBox.No |
- QMessageBox.Ok |
- QMessageBox.Open |
- QMessageBox.Reset |
- QMessageBox.Save |
- QMessageBox.Yes)
+ QMessageBox.Apply |
+ QMessageBox.Cancel |
+ QMessageBox.Close |
+ QMessageBox.Discard |
+ QMessageBox.Help |
+ QMessageBox.No |
+ QMessageBox.Ok |
+ QMessageBox.Open |
+ QMessageBox.Reset |
+ QMessageBox.Save |
+ QMessageBox.Yes)
sys.exit()
diff --git a/QMessageBox/README.md b/QMessageBox/README.md
index db685931097e8cebdf61ac484f050a82a507a8df..84a1e91419a5696342c0fce5560086b51718bbba 100644
--- a/QMessageBox/README.md
+++ b/QMessageBox/README.md
@@ -1,5 +1,10 @@
# QMessageBox
+- 目录
+ - [消息对话框倒计时关闭](#1消息对话框倒计时关闭)
+ - [自定义图标等](#2自定义图标等)
+ - [消息框按钮文字汉化](#3消息框按钮文字汉化)
+
## 1、消息对话框倒计时关闭
[运行 CountDownClose.py](CountDownClose.py)
@@ -11,4 +16,13 @@
## 2、自定义图标等
[运行 CustomColorIcon.py](CustomColorIcon.py)
-
\ No newline at end of file
+
+
+## 3、消息框按钮文字汉化
+[运行 ChineseText.py](ChineseText.py)
+
+1. 因为Qt5的翻译文件还是沿用旧的Qt4的结构导致部分地方无法翻译
+2. 可以通过手动重新编译翻译文件解决问题
+3. 这里可以通过QSS特性修改按钮文字,详细见代码
+
+
\ No newline at end of file
diff --git a/QMessageBox/ScreenShot/ChineseText.png b/QMessageBox/ScreenShot/ChineseText.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2cc9b97e1773d15bba50187dcc981b8b0076bb2
Binary files /dev/null and b/QMessageBox/ScreenShot/ChineseText.png differ
diff --git a/QMetaObject/CallInThread.py b/QMetaObject/CallInThread.py
new file mode 100644
index 0000000000000000000000000000000000000000..f5fdf824b958ad44cf701623837622db193a9724
--- /dev/null
+++ b/QMetaObject/CallInThread.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/23
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CallInThread.py
+@description:
+"""
+import time
+from datetime import datetime
+from threading import Thread
+
+from PyQt5.QtCore import (Q_ARG, Q_RETURN_ARG, QMetaObject, Qt, QThread,
+ pyqtSignal, pyqtSlot)
+from PyQt5.QtWidgets import QApplication, QHBoxLayout, QTextBrowser, QWidget
+
+
+class ThreadQt(QThread):
+
+ def __init__(self, textBrowser, *args, **kwargs):
+ super(ThreadQt, self).__init__(*args, **kwargs)
+ self._textBrowser = textBrowser
+
+ def stop(self):
+ self.requestInterruption()
+
+ def run(self):
+ while not self.isInterruptionRequested():
+ text = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ # 通过`invokeMethod`直接调用对应的槽函数
+ # 1. 获取函数`isReadOnly1`返回值, self.parent() 是Window窗口对象
+ # NOTE:注意这里获取返回值要用 `Qt.DirectConnection` 方式
+ retValue = QMetaObject.invokeMethod(self.parent(), 'isReadOnly1',
+ Qt.DirectConnection,
+ Q_RETURN_ARG(bool))
+ # 2. 通过`invokeMethod`队列调用对应控件槽函数`append`
+ argValue = Q_ARG(str, text + ' readOnly: ' + str(retValue))
+ QMetaObject.invokeMethod(self._textBrowser, 'append',
+ Qt.QueuedConnection, argValue)
+ self.sleep(1)
+
+
+class ThreadPy(Thread):
+
+ def __init__(self, textBrowser, parent, *args, **kwargs):
+ super(ThreadPy, self).__init__(*args, **kwargs)
+ self._running = True
+ self._textBrowser = textBrowser
+ self._parent = parent
+
+ def stop(self):
+ self._running = False
+
+ def run(self):
+ while self._running:
+ text = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ # 通过`invokeMethod`队列调用对应控件信号,`self._parent`是Window窗口对象
+ QMetaObject.invokeMethod(self._parent, 'appendText',
+ Qt.QueuedConnection,
+ Q_ARG(str, text + ' from Signal'))
+ # 通过`invokeMethod`队列调用对应控件槽函数`append`
+ QMetaObject.invokeMethod(self._textBrowser, 'append',
+ Qt.QueuedConnection,
+ Q_ARG(str, text + ' to Slot'))
+ time.sleep(1)
+
+
+class Window(QWidget):
+
+ # 更新信号
+ appendText = pyqtSignal(str)
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QHBoxLayout(self)
+ self.textBrowser1 = QTextBrowser(self)
+ self.textBrowser2 = QTextBrowser(self)
+ layout.addWidget(self.textBrowser1)
+ layout.addWidget(self.textBrowser2)
+
+ self.appendText.connect(self.textBrowser2.append)
+
+ # Qt线程
+ self.thread1 = ThreadQt(self.textBrowser1, self)
+ self.thread1.start()
+
+ # PY线程
+ self.thread2 = ThreadPy(self.textBrowser2, self)
+ self.thread2.start()
+
+ @pyqtSlot(result=bool)
+ def isReadOnly1(self):
+ # 线程中直接调用该槽函数获取UI中的内容
+ return self.textBrowser1.isReadOnly()
+
+ def closeEvent(self, event):
+ self.thread1.stop()
+ self.thread2.stop()
+ self.thread1.wait()
+ self.thread2.join()
+ super(Window, self).closeEvent(event)
+
+
+if __name__ == '__main__':
+ import cgitb
+ import sys
+
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QMetaObject/README.en.md b/QMetaObject/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QMetaObject/README.md b/QMetaObject/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..06f2560dd49ee77a1c0a423b51cda0ab32f41743
--- /dev/null
+++ b/QMetaObject/README.md
@@ -0,0 +1,24 @@
+# QMetaObject
+
+- 目录
+ - [在线程中操作UI](#1在线程中操作UI)
+
+## 1、在线程中操作UI
+[运行 CallInThread.py](CallInThread.py)
+
+如果想在`QThread`或者`threading.Thread`中不通过信号直接操作UI,则可以使用`QMetaObject.invokeMethod`调用。
+
+该函数一般有常用的几种调用方法:
+
+1. 直接调用槽函数:`QMetaObject.invokeMethod(uiobj, 'slot_method', Qt.QueuedConnection)`
+2. 直接调用信号:`QMetaObject.invokeMethod(uiobj, 'signal_method', Qt.QueuedConnection)`
+3. 调用信号或槽函数并传递参数:`QMetaObject.invokeMethod(uiobj, 'method', Qt.QueuedConnection, Q_ARG(str, 'text'))`
+4. 调用槽函数得到返回值:`QMetaObject.invokeMethod(uiobj, 'slot_method', Qt.DirectConnection, Q_RETURN_ARG(str))`
+5. 调用带参数的槽函数得到返回值:`QMetaObject.invokeMethod(uiobj, 'slot_method', Qt.DirectConnection, Q_RETURN_ARG(int), Q_ARG(bool, False))`, 传入bool类型的参数,获取int类型返回值
+
+这里需要注意:
+
+1. 调用函数都是异步队列方式,需要使用`Qt.QueuedConnection`
+2. 而要得到返回值则必须使用同步方式, 即`Qt.DirectConnection`
+
+
\ No newline at end of file
diff --git a/QMetaObject/ScreenShot/CallInThread.png b/QMetaObject/ScreenShot/CallInThread.png
new file mode 100644
index 0000000000000000000000000000000000000000..62ed7f62b8990d7528747891ff5ad13981ad208d
Binary files /dev/null and b/QMetaObject/ScreenShot/CallInThread.png differ
diff --git a/QPainter/Data/qt-logo.png b/QPainter/Data/qt-logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..90e6f905aeec1eaf412616c829329dde84ba2f33
Binary files /dev/null and b/QPainter/Data/qt-logo.png differ
diff --git a/QPainter/Draw.py b/QPainter/Draw.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1e8f8089fe76ad3c84be6bcd29aee85d6efc7e0
--- /dev/null
+++ b/QPainter/Draw.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2022年12月12日
+@site: https://pyqt.site , https://github.com/PyQt5
+@description: QPainter画图
+"""
+import sys
+try:
+ from PyQt5.QtWidgets import QApplication, QWidget, qApp
+ from PyQt5.QtGui import QPainter, QFont, QColor, QPixmap
+ from PyQt5.QtCore import Qt, pyqtSignal
+ from PyQt5.Qt import QPoint, QPolygon
+except ImportError:
+ from PySide2.QtWidgets import QApplication, QWidget, qApp
+ from PySide2.QtGui import QPainter, QFont, QColor, QPixmap
+ from PySide2.QtCore import Qt, pyqtSignal
+ from PySide2.Qt import QPoint, QPolygon
+
+
+
+class draw(QWidget):
+
+
+ drawsig = pyqtSignal(bool)
+
+ def __init__(self):
+ super(draw, self).__init__()
+ self.setWindowTitle("QPainter画图")
+ self._painter = QPainter()
+ self.scale = 1.0
+ self.pixmap = QPixmap()
+ self.setMouseTracking(True)
+ self.setFocusPolicy(Qt.WheelFocus)
+ self.drawEnable = False
+ self.points = []
+ self.current_points = []
+ self.drawsig.connect(self.setDrawEnable)
+
+
+ def setDrawEnable(self, enable=False):
+ self.drawEnable = enable
+ self.update()
+
+ def mouseMoveEvent(self, ev):
+ if self.drawEnable:
+ def in_end_range(curr, first):
+ return first.x() - 5 <= curr.x() <= first.x() + 5 and first.y() - 5 <= curr.y() <= first.y() + 5
+
+ if len(self.current_points) > 0 and in_end_range(ev.pos(), self.current_points[0]):
+ self.current_points.append(self.current_points[0])
+ self.points.append(self.current_points)
+ self.current_points = []
+ else:
+ self.current_points.append(ev.pos())
+ elif len(self.current_points) > 0:
+ self.current_points.append(ev.pos())
+ self.points.append(self.current_points)
+ self.current_points = []
+
+ self.update()
+
+ def mousePressEvent(self, ev):
+ if Qt.LeftButton & ev.button():
+ self.drawsig.emit(True)
+ def mouseReleaseEvent(self, ev):
+ if Qt.LeftButton & ev.button():
+ self.drawsig.emit(False)
+
+ def paintEvent(self, ev):
+ if len(self.points) <= 0 and len(self.current_points) <= 0 : return
+ p = self._painter
+ p.begin(self)
+ p.setRenderHint(QPainter.Antialiasing)
+ p.setRenderHint(QPainter.HighQualityAntialiasing)
+ p.setRenderHint(QPainter.SmoothPixmapTransform)
+ p.scale(self.scale, self.scale)
+ p.setPen(QColor(0, 0, 0))
+ for pts in self.points:
+ p.drawPolyline(QPolygon(pts))
+ if len(self.current_points) > 0:
+ p.drawPolyline(QPolygon(self.current_points))
+ p.end()
+
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ mainWin = draw()
+ mainWin.show()
+ sys.exit(app.exec_())
+
diff --git a/QPainter/README.en.md b/QPainter/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..42512411c5309df459e310d53d74207b307f1f60
--- /dev/null
+++ b/QPainter/README.en.md
@@ -0,0 +1 @@
+# QPainter
\ No newline at end of file
diff --git a/QPainter/README.md b/QPainter/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..81206483e3fc76c1876b6765c8451e2f1425676a
--- /dev/null
+++ b/QPainter/README.md
@@ -0,0 +1,16 @@
+# QPainter
+
+
+- 目录
+ - [利用QPainter绘制各种图形](#QPainter绘制各种图形)
+ - [简易画板](#简易画板)
+
+## 1、QPainter绘制各种图形
+[运行 StockDialog.py](StockDialog.py)
+
+
+
+## 2、简易画板
+[运行 Draw.py](Draw.py)
+
+
\ No newline at end of file
diff --git a/QPainter/ScreenShot/Draw.gif b/QPainter/ScreenShot/Draw.gif
new file mode 100644
index 0000000000000000000000000000000000000000..65f85619a3ced54ff87b514283d1205b37e8a81d
Binary files /dev/null and b/QPainter/ScreenShot/Draw.gif differ
diff --git a/QPainter/ScreenShot/StockDialog.gif b/QPainter/ScreenShot/StockDialog.gif
new file mode 100644
index 0000000000000000000000000000000000000000..97d964e7fc2f30067dbe8c14f7daf1363ec0c8a9
Binary files /dev/null and b/QPainter/ScreenShot/StockDialog.gif differ
diff --git a/QPainter/StockDialog.py b/QPainter/StockDialog.py
new file mode 100644
index 0000000000000000000000000000000000000000..996dbe1fd59391ebd1e6246214105790c672dbaa
--- /dev/null
+++ b/QPainter/StockDialog.py
@@ -0,0 +1,298 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2022年12月12日
+@site: https://pyqt.site , https://github.com/PyQt5
+@description: QPainter绘制各种图形
+"""
+import sys
+
+try:
+ from PyQt5.QtWidgets import QApplication, QWidget, qApp
+ from PyQt5.QtGui import QPainter, QFont, QColor, QPixmap
+ from PyQt5.QtCore import Qt, pyqtSignal, QPointF
+ from PyQt5.Qt import QPoint, QPolygon, QSplitter, QFrame, QGridLayout, QLabel,\
+ QComboBox, QSpinBox, QPalette, QStackedWidget, QVBoxLayout,\
+ QPushButton, QColorDialog, QPen,QLinearGradient, QConicalGradient,\
+ QRadialGradient, QBrush, QRect, QPainterPath, QFileDialog
+except ImportError:
+ from PySide2.QtWidgets import QApplication, QWidget, qApp
+ from PySide2.QtGui import QPainter, QFont, QColor, QPixmap
+ from PySide2.QtCore import Qt, pyqtSignal, QPointF
+ from PySide2.Qt import QPoint, QPolygon, QSplitter, QFrame, QGridLayout, QLabel,\
+ QComboBox, QSpinBox, QPalette, QStackedWidget, QVBoxLayout,\
+ QPushButton, QColorDialog, QPen, QLinearGradient, QConicalGradient,\
+ QRadialGradient, QBrush, QRect , QPainterPath, QFileDialog
+
+
+class StockDialog(QWidget):
+ def __init__(self, parent=None):
+ super(StockDialog, self).__init__(parent)
+ self.setWindowTitle("QPainter绘制各种图形")
+
+ mainSplitter = QSplitter(Qt.Horizontal)
+ mainSplitter.setOpaqueResize(True)
+ frame = QFrame(mainSplitter)
+ mainLayout = QGridLayout(frame)
+ mainLayout.setSpacing(6)
+
+ label1 = QLabel("形状:")
+ label2 = QLabel("画笔线宽:")
+ label3 = QLabel("画笔颜色:")
+ label4 = QLabel("画笔风格:")
+ label5 = QLabel("画笔顶端:")
+ label6 = QLabel("画笔连接点:")
+ label7 = QLabel("画刷风格:")
+ label8 = QLabel("画刷颜色:")
+
+ self.shapeComboBox = QComboBox()
+ self.shapeComboBox.addItem("Line", "Line")
+ self.shapeComboBox.addItem("Rectangle", "Rectangle")
+ self.shapeComboBox.addItem('Rounded Rectangle', 'Rounded Rectangle')
+ self.shapeComboBox.addItem('Ellipse', 'Ellipse')
+ self.shapeComboBox.addItem('Pie', 'Pie')
+ self.shapeComboBox.addItem('Chord', 'Chord')
+ self.shapeComboBox.addItem('Path', 'Path')
+ self.shapeComboBox.addItem('Polygon', 'Polygon')
+ self.shapeComboBox.addItem('Polyline', 'Polyline')
+ self.shapeComboBox.addItem('Arc', 'Arc')
+ self.shapeComboBox.addItem('Points', 'Points')
+ self.shapeComboBox.addItem('Text', 'Text')
+ self.shapeComboBox.addItem('Pixmap', 'Pixmap')
+
+ self.widthSpinBox = QSpinBox()
+ self.widthSpinBox.setRange(0, 20)
+
+ self.penColorFrame = QFrame()
+ self.penColorFrame.setAutoFillBackground(True)
+ self.penColorFrame.setPalette(QPalette(Qt.blue))
+ self.penColorPushButton = QPushButton("更改")
+
+ self.penStyleComboBox = QComboBox()
+ self.penStyleComboBox.addItem("Solid", Qt.SolidLine)
+ self.penStyleComboBox.addItem('Dash', Qt.DashLine)
+ self.penStyleComboBox.addItem('Dot', Qt.DotLine)
+ self.penStyleComboBox.addItem('Dash Dot', Qt.DashDotLine)
+ self.penStyleComboBox.addItem('Dash Dot Dot', Qt.DashDotDotLine)
+ self.penStyleComboBox.addItem('None', Qt.NoPen)
+
+ self.penCapComboBox = QComboBox()
+ self.penCapComboBox.addItem("Flat", Qt.FlatCap)
+ self.penCapComboBox.addItem('Square', Qt.SquareCap)
+ self.penCapComboBox.addItem('Round', Qt.RoundCap)
+
+ self.penJoinComboBox = QComboBox()
+ self.penJoinComboBox.addItem("Miter", Qt.MiterJoin)
+ self.penJoinComboBox.addItem('Bebel', Qt.BevelJoin)
+ self.penJoinComboBox.addItem('Round', Qt.RoundJoin)
+
+ self.brushStyleComboBox = QComboBox()
+ self.brushStyleComboBox.addItem("Linear Gradient", Qt.LinearGradientPattern)
+ self.brushStyleComboBox.addItem('Radial Gradient', Qt.RadialGradientPattern)
+ self.brushStyleComboBox.addItem('Conical Gradient', Qt.ConicalGradientPattern)
+ self.brushStyleComboBox.addItem('Texture', Qt.TexturePattern)
+ self.brushStyleComboBox.addItem('Solid', Qt.SolidPattern)
+ self.brushStyleComboBox.addItem('Horizontal', Qt.HorPattern)
+ self.brushStyleComboBox.addItem('Vertical', Qt.VerPattern)
+ self.brushStyleComboBox.addItem('Cross', Qt.CrossPattern)
+ self.brushStyleComboBox.addItem('Backward Diagonal', Qt.BDiagPattern)
+ self.brushStyleComboBox.addItem('Forward Diagonal', Qt.FDiagPattern)
+ self.brushStyleComboBox.addItem('Diagonal Cross', Qt.DiagCrossPattern)
+ self.brushStyleComboBox.addItem('Dense 1', Qt.Dense1Pattern)
+ self.brushStyleComboBox.addItem('Dense 2', Qt.Dense2Pattern)
+ self.brushStyleComboBox.addItem('Dense 3', Qt.Dense3Pattern)
+ self.brushStyleComboBox.addItem('Dense 4', Qt.Dense4Pattern)
+ self.brushStyleComboBox.addItem('Dense 5', Qt.Dense5Pattern)
+ self.brushStyleComboBox.addItem('Dense 6', Qt.Dense6Pattern)
+ self.brushStyleComboBox.addItem('Dense 7', Qt.Dense7Pattern)
+ self.brushStyleComboBox.addItem('None', Qt.NoBrush)
+
+ self.brushColorFrame = QFrame()
+ self.brushColorFrame.setAutoFillBackground(True)
+ self.brushColorFrame.setPalette(QPalette(Qt.green))
+ self.brushColorPushButton = QPushButton("更改")
+
+ labelCol = 0
+ contentCol = 1
+
+ # 建立布局
+ mainLayout.addWidget(label1, 1, labelCol)
+ mainLayout.addWidget(self.shapeComboBox, 1, contentCol)
+ mainLayout.addWidget(label2, 2, labelCol)
+ mainLayout.addWidget(self.widthSpinBox, 2, contentCol)
+ mainLayout.addWidget(label3, 4, labelCol)
+ mainLayout.addWidget(self.penColorFrame, 4, contentCol)
+ mainLayout.addWidget(self.penColorPushButton, 4, 3)
+ mainLayout.addWidget(label4, 6, labelCol)
+ mainLayout.addWidget(self.penStyleComboBox, 6, contentCol)
+ mainLayout.addWidget(label5, 8, labelCol)
+ mainLayout.addWidget(self.penCapComboBox, 8, contentCol)
+ mainLayout.addWidget(label6, 10, labelCol)
+ mainLayout.addWidget(self.penJoinComboBox, 10, contentCol)
+ mainLayout.addWidget(label7, 12, labelCol)
+ mainLayout.addWidget(self.brushStyleComboBox, 12, contentCol)
+ mainLayout.addWidget(label8, 14, labelCol)
+ mainLayout.addWidget(self.brushColorFrame, 14, contentCol)
+ mainLayout.addWidget(self.brushColorPushButton, 14, 3)
+ mainSplitter1 = QSplitter(Qt.Horizontal)
+ mainSplitter1.setOpaqueResize(True)
+
+ stack1 = QStackedWidget()
+ stack1.setFrameStyle(QFrame.Panel | QFrame.Raised)
+ self.area = PaintArea()
+ stack1.addWidget(self.area)
+ frame1 = QFrame(mainSplitter1)
+ mainLayout1 = QVBoxLayout(frame1)
+ mainLayout1.setSpacing(6)
+ mainLayout1.addWidget(stack1)
+
+ layout = QGridLayout(self)
+ layout.addWidget(mainSplitter1, 0, 0)
+ layout.addWidget(mainSplitter, 0, 1)
+ self.setLayout(layout)
+
+ # 信号和槽函数
+ self.shapeComboBox.activated.connect(self.slotShape)
+ self.widthSpinBox.valueChanged.connect(self.slotPenWidth)
+ self.penColorPushButton.clicked.connect(self.slotPenColor)
+ self.penStyleComboBox.activated.connect(self.slotPenStyle)
+ self.penCapComboBox.activated.connect(self.slotPenCap)
+ self.penJoinComboBox.activated.connect(self.slotPenJoin)
+ self.brushStyleComboBox.activated.connect(self.slotBrush)
+ self.brushColorPushButton.clicked.connect(self.slotBrushColor)
+
+ self.slotShape(self.shapeComboBox.currentIndex())
+ self.slotPenWidth(self.widthSpinBox.value())
+ self.slotBrush(self.brushStyleComboBox.currentIndex())
+
+ def slotShape(self, value):
+ shape = self.area.Shape[value]
+ self.area.setShape(shape)
+
+ def slotPenWidth(self, value):
+ color = self.penColorFrame.palette().color(QPalette.Window)
+ style = Qt.PenStyle(self.penStyleComboBox.itemData(self.penStyleComboBox.currentIndex(), Qt.UserRole))
+ cap = Qt.PenCapStyle(self.penCapComboBox.itemData(self.penCapComboBox.currentIndex(), Qt.UserRole))
+ join = Qt.PenJoinStyle(self.penJoinComboBox.itemData(self.penJoinComboBox.currentIndex(), Qt.UserRole))
+ self.area.setPen(QPen(color, value, style, cap, join))
+
+ def slotPenStyle(self, value):
+ self.slotPenWidth(value)
+
+ def slotPenCap(self, value):
+ self.slotPenWidth(value)
+
+ def slotPenJoin(self, value):
+ self.slotPenWidth(value)
+
+ def slotPenColor(self):
+ color = QColorDialog.getColor(Qt.blue)
+ self.penColorFrame.setPalette(QPalette(color))
+ self.area.setPen(QPen(color))
+
+ def slotBrushColor(self):
+ color = QColorDialog.getColor(Qt.blue)
+ self.brushColorFrame.setPalette(QPalette(color))
+ self.slotBrush(self.brushStyleComboBox.currentIndex())
+
+ def slotBrush(self, value):
+ color = self.brushColorFrame.palette().color(QPalette.Window)
+ style = Qt.BrushStyle(self.brushStyleComboBox.itemData(value, Qt.UserRole))
+
+ if (style == Qt.LinearGradientPattern):
+ linearGradient = QLinearGradient(0, 0, 400, 400)
+ linearGradient.setColorAt(0.0, Qt.white)
+ linearGradient.setColorAt(0.2, color)
+ linearGradient.setColorAt(1.0, Qt.black)
+ self.area.setBrush(linearGradient)
+ elif style == Qt.RadialGradientPattern:
+ radialGradient = QRadialGradient(200, 200, 80, 70, 70);
+ radialGradient.setColorAt(0.0, Qt.white)
+ radialGradient.setColorAt(0.2, Qt.green)
+ radialGradient.setColorAt(1.0, Qt.black)
+ self.area.setBrush(radialGradient)
+ elif (style == Qt.ConicalGradientPattern):
+ conicalGradient = QConicalGradient(200, 200, 30)
+ conicalGradient.setColorAt(0.0, Qt.white)
+ conicalGradient.setColorAt(0.2, color)
+ conicalGradient.setColorAt(1.0, Qt.black)
+ self.area.setBrush(conicalGradient)
+ elif (style == Qt.TexturePattern):
+ self.area.setBrush(QBrush(QPixmap("./Data/qt-logo.png")))
+ else:
+ self.area.setBrush(QBrush(color, style))
+
+
+class PaintArea(QWidget):
+ def __init__(self):
+ super(PaintArea, self).__init__()
+ self.Shape = ["Line", "Rectangle", 'Rounded Rectangle', "Ellipse", "Pie", 'Chord',
+ "Path", "Polygon", "Polyline", "Arc", "Points", "Text", "Pixmap"]
+ self.setPalette(QPalette(Qt.white))
+ self.setAutoFillBackground(True)
+ self.setMinimumSize(500, 500)
+ self.pen = QPen()
+ self.brush = QBrush()
+
+ def setShape(self, s):
+ self.shape = s
+ self.update()
+
+ def setPen(self, p):
+ self.pen = p
+ self.update()
+
+ def setBrush(self, b):
+ self.brush = b
+ self.update()
+
+ def paintEvent(self, QPaintEvent):
+ p = QPainter(self)
+ p.setPen(self.pen)
+ p.setBrush(self.brush)
+
+ rect = QRect(50, 100, 300, 200)
+ points = [QPoint(150, 100), QPoint(300, 150), QPoint(350, 250), QPoint(100, 300)]
+ startAngle = 30 * 16
+ spanAngle = 120 * 16
+
+ if self.shape == "Line":
+ p.drawLine(rect.topLeft(), rect.bottomRight())
+ elif self.shape == "Rectangle":
+ p.drawRect(rect)
+ elif self.shape == 'Rounded Rectangle':
+ p.drawRoundedRect(rect, 25, 25, Qt.RelativeSize)
+ elif self.shape == "Ellipse":
+ p.drawEllipse(rect)
+ elif self.shape == "Polygon":
+ p.drawPolygon(QPolygon(points), Qt.WindingFill)
+ elif self.shape == "Polyline":
+ p.drawPolyline(QPolygon(points))
+ elif self.shape == "Points":
+ p.drawPoints(QPolygon(points))
+ elif self.shape == "Pie":
+ p.drawPie(rect, startAngle, spanAngle)
+ elif self.shape == "Arc":
+ p.drawArc(rect, startAngle, spanAngle)
+ elif self.shape == "Chord":
+ p.drawChord(rect, startAngle, spanAngle)
+ elif self.shape == "Path":
+ path = QPainterPath()
+ path.addRect(150, 150, 100, 100)
+ path.moveTo(100, 100)
+ path.cubicTo(300, 100, 200, 200, 300, 300)
+ path.cubicTo(100, 300, 200, 200, 100, 100)
+ p.drawPath(path)
+ elif self.shape == "Text":
+ p.drawText(rect, Qt.AlignCenter, "Hello Qt!")
+ elif self.shape == "Pixmap":
+ p.drawPixmap(150, 150, QPixmap("./Data/qt-logo.png"))
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ form = StockDialog()
+ form.show()
+ app.exec_()
+
diff --git a/QProcess/GetCmdResult.py b/QProcess/GetCmdResult.py
new file mode 100644
index 0000000000000000000000000000000000000000..f7ea5469421025d44d260af5315d31aa43025418
--- /dev/null
+++ b/QProcess/GetCmdResult.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/01
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: GetCmdResult.py
+@description:
+"""
+
+import sys
+
+try:
+ from PyQt5.QtCore import QProcess
+ from PyQt5.QtWidgets import (QApplication, QLabel, QPushButton,
+ QTextBrowser, QVBoxLayout, QWidget)
+except ImportError:
+ from PySide2.QtCore import QProcess
+ from PySide2.QtWidgets import (QApplication, QLabel, QPushButton,
+ QTextBrowser, QVBoxLayout, QWidget)
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.setWindowTitle('执行命令得到结果')
+ layout = QVBoxLayout(self)
+ layout.addWidget(QLabel('点击执行 ping www.baidu.com', self))
+
+ self.buttonRunSync = QPushButton('同步执行', self)
+ layout.addWidget(self.buttonRunSync)
+ self.buttonRunSync.clicked.connect(self.run_ping)
+
+ self.buttonRunASync = QPushButton('异步执行', self)
+ layout.addWidget(self.buttonRunASync)
+ self.buttonRunASync.clicked.connect(self.run_ping)
+
+ self.resultView = QTextBrowser(self)
+ layout.addWidget(self.resultView)
+
+ self._pingProcess = None
+
+ def run_ping(self):
+ sender = self.sender() # 同步或者异步按钮
+ self.buttonRunSync.setEnabled(False)
+ self.buttonRunASync.setEnabled(False)
+
+ if self._pingProcess:
+ self._pingProcess.terminate()
+
+ self._pingProcess = QProcess(self)
+ self._pingProcess.setProgram('ping')
+ if sys.platform.startswith('win'):
+ self._pingProcess.setArguments(['-n', '5', 'www.baidu.com'])
+ self._pingProcess.setArguments(['-n', '5', 'www.baidu.com'])
+ elif sys.platform.startswith('darwin') or sys.platform.startswith(
+ 'linux'):
+ self._pingProcess.setArguments(['-c', '5', 'www.baidu.com'])
+ # 合并输出流和错误流,执行完毕后通过readAll可以一次性读取所有结果
+ self._pingProcess.setProcessChannelMode(QProcess.MergedChannels)
+ self._pingProcess.started.connect(self.on_started)
+
+ if sender == self.buttonRunASync:
+ # 异步执行
+ self._pingProcess.finished.connect(self.on_finished)
+ self._pingProcess.errorOccurred.connect(self.on_error)
+ self._pingProcess.start()
+ elif sender == self.buttonRunSync:
+ # 同步执行
+ self._pingProcess.start()
+ if self._pingProcess.waitForFinished():
+ self.on_finished(self._pingProcess.exitCode(),
+ self._pingProcess.exitStatus())
+ else:
+ self.resultView.append('ping process read timeout')
+ self.on_error(self._pingProcess.error())
+
+ def on_started(self):
+ self.resultView.append('ping process started')
+
+ def on_finished(self, exitCode, exitStatus):
+ self.resultView.append(
+ 'ping process finished, exitCode: %s, exitStatus: %s' %
+ (exitCode, exitStatus))
+ # 读取所有结果
+ result = self._pingProcess.readAll().data()
+ try:
+ import chardet
+ encoding = chardet.detect(result)
+ self.resultView.append(result.decode(encoding['encoding']))
+ except Exception:
+ self.resultView.append(result.decode('utf-8', errors='ignore'))
+ self._pingProcess.kill()
+ self._pingProcess = None
+ self.buttonRunSync.setEnabled(True)
+ self.buttonRunASync.setEnabled(True)
+
+ def on_error(self, error):
+ self.resultView.append('ping process error: %s, message: %s' %
+ (error, self._pingProcess.errorString()))
+ self._pingProcess.kill()
+ self._pingProcess = None
+ self.buttonRunSync.setEnabled(True)
+ self.buttonRunASync.setEnabled(True)
+
+
+if __name__ == '__main__':
+ import cgitb
+
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QProcess/InteractiveRun.py b/QProcess/InteractiveRun.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c538c1ca7d24d8fd8cb61307427b6b9fcf428df
--- /dev/null
+++ b/QProcess/InteractiveRun.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2023/02/01
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: InteractiveRun.py
+@description:
+"""
+
+import os
+import sys
+
+try:
+ import chardet
+except ImportError:
+ print('chardet not found')
+
+try:
+ from PyQt5.QtCore import QProcess
+ from PyQt5.QtWidgets import (QApplication, QLineEdit, QPushButton,
+ QTextBrowser, QVBoxLayout, QWidget)
+except ImportError:
+ from PySide2.QtCore import QProcess
+ from PySide2.QtWidgets import (QApplication, QLineEdit, QPushButton,
+ QTextBrowser, QVBoxLayout, QWidget)
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+
+ command = 'ping www.baidu.com'
+ if sys.platform.startswith('win'):
+ command = 'ping -n 5 www.baidu.com'
+ elif sys.platform.startswith('darwin') or sys.platform.startswith(
+ 'linux'):
+ command = 'ping -c 5 www.baidu.com'
+ else:
+ raise RuntimeError('Unsupported platform: %s' % sys.platform)
+
+ self.cmdEdit = QLineEdit(command, self)
+ layout.addWidget(self.cmdEdit)
+
+ self.buttonRun = QPushButton('执行命令', self)
+ layout.addWidget(self.buttonRun)
+ self.buttonRun.clicked.connect(self.run_command)
+
+ self.resultView = QTextBrowser(self)
+ layout.addWidget(self.resultView)
+
+ self._cmdProcess = None
+ self._init()
+
+ def closeEvent(self, event):
+ if self._cmdProcess:
+ self._cmdProcess.writeData('exit'.encode() + os.linesep.encode())
+ self._cmdProcess.waitForFinished()
+ if self._cmdProcess:
+ self._cmdProcess.terminate()
+ super(Window, self).closeEvent(event)
+
+ def _init(self):
+ if self._cmdProcess:
+ return
+ # 打开终端shell
+ self._cmdProcess = QProcess(self)
+ self._cmdProcess.setProgram(
+ 'cmd' if sys.platform.startswith('win') else 'bash')
+ # 合并输出流和错误流,只从标准输出流读取数据
+ self._cmdProcess.setProcessChannelMode(QProcess.MergedChannels)
+ self._cmdProcess.started.connect(self.on_started)
+ self._cmdProcess.finished.connect(self.on_finished)
+ self._cmdProcess.errorOccurred.connect(self.on_error)
+ self._cmdProcess.readyReadStandardOutput.connect(
+ self.on_readyReadStandardOutput)
+ self._cmdProcess.start()
+
+ def run_command(self):
+ self._init()
+ command = self.cmdEdit.text().strip()
+ if not command:
+ return
+ command = command.encode(sys.getdefaultencoding()) + os.linesep.encode(
+ sys.getdefaultencoding())
+ self._cmdProcess.writeData(command)
+
+ def on_started(self):
+ self.resultView.append('ping process started, pid: %s' %
+ self._cmdProcess.processId())
+
+ def on_finished(self, exitCode, exitStatus):
+ print('ping process finished, exitCode: %s, exitStatus: %s' %
+ (exitCode, exitStatus))
+ self._cmdProcess.kill()
+ self._cmdProcess = None
+
+ def on_error(self, error):
+ self.resultView.append('ping process error: %s, message: %s' %
+ (error, self._cmdProcess.errorString()))
+ self._cmdProcess.kill()
+ self._cmdProcess = None
+
+ def on_readyReadStandardOutput(self):
+ # 读取已有结果
+ result = self._cmdProcess.readAllStandardOutput().data()
+ try:
+ encoding = chardet.detect(result)
+ self.resultView.append(result.decode(encoding['encoding']))
+ except Exception:
+ self.resultView.append(result.decode('utf-8', errors='ignore'))
+
+
+if __name__ == '__main__':
+ import cgitb
+
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QProcess/README.en.md b/QProcess/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QProcess/README.md b/QProcess/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0136bd7bdcab620957991f7adb02ea8167655be4
--- /dev/null
+++ b/QProcess/README.md
@@ -0,0 +1,33 @@
+# QProcess
+
+- 目录
+ - [执行命令得到结果](#1执行命令得到结果)
+ - [交互执行命令](#2交互执行命令)
+
+## 1、执行命令得到结果
+[运行 GetCmdResult.py](GetCmdResult.py)
+
+`QProcess` 常用执行命令方式有以下几种:
+1. `QProcess.execute('ping', ['www.baidu.com'])`:同步执行,返回值为进程退出码
+2. `QProcess.startDetached('ping', ['www.baidu.com'], '工作路径')`:返回值为是否启动成功,该命令一般用于启动某个程序后就不管了
+3. 通过构造`QProcess`对象,然后通过`QProcess.start()`启动进程,并分为同步和异步两种方式获取输出
+
+示例代码为第3种方式:
+
+1. 通过`setProcessChannelMode(QProcess.MergedChannels)`合并标准输出和错误输出
+2. `waitForFinished`为同步方式,然后调用`readAll`读取所有输出
+3. 也可以绑定`finished`信号,然后通过`readAll`读取所有输出
+
+
+
+## 2、交互执行命令
+[运行 InteractiveRun.py](InteractiveRun.py)
+
+`QProcess` 也可以用于交互式执行命令,具体需要如下几步:
+
+1. 通过`setProcessChannelMode(QProcess.MergedChannels)`合并标准输出和错误输出
+2. 通过`start`启动进程
+3. 通过`readyReadStandardOutput`信号读取进程输出
+4. 通过`writeData`向进程写入数据
+
+
\ No newline at end of file
diff --git a/QProcess/ScreenShot/GetCmdResult.gif b/QProcess/ScreenShot/GetCmdResult.gif
new file mode 100644
index 0000000000000000000000000000000000000000..bdf059608b36c0ba9111480fc84f057e561b32f7
Binary files /dev/null and b/QProcess/ScreenShot/GetCmdResult.gif differ
diff --git a/QProcess/ScreenShot/InteractiveRun.gif b/QProcess/ScreenShot/InteractiveRun.gif
new file mode 100644
index 0000000000000000000000000000000000000000..225144da81f60e539254439a8c7e4e3bb075dcaf
Binary files /dev/null and b/QProcess/ScreenShot/InteractiveRun.gif differ
diff --git a/QProgressBar/ColourfulProgress.py b/QProgressBar/ColourfulProgress.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c629d0cbb7cea62a7635797e176d4882bc50443
--- /dev/null
+++ b/QProgressBar/ColourfulProgress.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2022/02/25
+@author: Irony
+@site: https://pyqt.site, https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ColourfulProgress.py
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import QLineF, QRect, QRectF, Qt
+ from PyQt5.QtGui import QColor, QPainter, QPainterPath, QPen, QTransform
+ from PyQt5.QtWidgets import (QApplication, QGridLayout, QProgressBar,
+ QSlider, QStyleOptionProgressBar, QWidget)
+except ImportError:
+ from PySide2.QtCore import QRect, Qt, QRectF, QLineF
+ from PySide2.QtGui import QColor, QPainter, QPen, QTransform, QPainterPath
+ from PySide2.QtWidgets import (QApplication, QProgressBar,
+ QStyleOptionProgressBar, QWidget, QSlider,
+ QGridLayout)
+
+from Lib.QStyleAnimation import QProgressStyleAnimation
+
+
+class ColourfulProgress(QProgressBar):
+
+ def __init__(self, *args, **kwargs):
+ self._color = kwargs.pop('color', QColor(43, 194, 83))
+ self._fps = kwargs.pop('fps', 60)
+ self._lineWidth = kwargs.pop('lineWidth', 50) # 线条宽度
+ self._radius = kwargs.pop('radius', None) # None为自动计算圆角
+ self._animation = None
+ super(ColourfulProgress, self).__init__(*args, **kwargs)
+ self.setColor(self._color)
+ self.setFps(self._fps)
+ self.setLineWidth(self._lineWidth)
+ self.setRadius(self._radius)
+
+ def setColor(self, color):
+ """
+ :type color: QColor
+ :param color: 颜色
+ """
+ self._color = QColor(color) if isinstance(
+ color, (QColor, Qt.GlobalColor)) else QColor(43, 194, 83)
+
+ def setFps(self, fps):
+ """
+ :type fps: int
+ :param fps: 帧率
+ """
+ self._fps = max(int(fps), 1) if isinstance(fps, (int, float)) else 60
+
+ def setLineWidth(self, width):
+ """
+ :type width: int
+ :param width: 线条宽度
+ """
+ self._lineWidth = max(int(width), 0) if isinstance(width,
+ (int, float)) else 50
+
+ def setRadius(self, radius):
+ """
+ :type radius: int
+ :param radius: 半径
+ """
+ self._radius = max(int(radius), 1) if isinstance(radius,
+ (int, float)) else None
+
+ def paintEvent(self, _):
+ """
+ 重写绘制事件,参考 qfusionstyle.cpp 中的 CE_ProgressBarContents 绘制方法
+ """
+ option = QStyleOptionProgressBar()
+ self.initStyleOption(option)
+
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.Antialiasing)
+ painter.translate(0.5, 0.5)
+
+ vertical = option.orientation == Qt.Vertical # 是否垂直
+ inverted = option.invertedAppearance # 是否反转
+ # 是否显示动画
+ indeterminate = (option.minimum == option.maximum) or (
+ option.minimum < option.progress < option.maximum)
+ rect = option.rect
+
+ if vertical:
+ rect = QRect(rect.left(), rect.top(), rect.height(),
+ rect.width()) # 翻转宽度和高度
+ m = QTransform.fromTranslate(rect.height(), 0)
+ m.rotate(90.0)
+ painter.setTransform(m, True)
+
+ maxWidth = rect.width()
+ progress = max(option.progress, option.minimum)
+ totalSteps = max(1, option.maximum - option.minimum)
+ progressSteps = progress - option.minimum
+ progressBarWidth = int(progressSteps * maxWidth / totalSteps)
+ width = progressBarWidth # 已进行的进度宽度
+ radius = max(1, (min(width,
+ self.width() if vertical else self.height()) //
+ 4) if self._radius is None else self._radius)
+
+ reverse = (not vertical and
+ option.direction == Qt.RightToLeft) or vertical
+ if inverted:
+ reverse = not reverse
+
+ # 绘制范围
+ path = QPainterPath()
+ if not reverse:
+ progressBar = QRectF(rect.left(), rect.top(), width, rect.height())
+ else:
+ progressBar = QRectF(rect.right() - width, rect.top(), width,
+ rect.height())
+
+ # 切割范围
+ path.addRoundedRect(progressBar, radius, radius)
+ painter.setClipPath(path)
+
+ # 绘制背景颜色
+ painter.setPen(Qt.NoPen)
+ painter.setBrush(self._color)
+ painter.drawRoundedRect(progressBar, radius, radius)
+
+ if not indeterminate:
+ if self._animation:
+ self._animation.stop()
+ self._animation = None
+ else:
+ # 叠加颜色覆盖后出现类似线条间隔的效果
+ color = self._color.lighter(320)
+ color.setAlpha(80)
+ painter.setPen(QPen(color, self._lineWidth))
+
+ if self._animation:
+ if self._animation.state() == QProgressStyleAnimation.Stopped:
+ # FIXME: 最小化后动画会停止
+ self._animation.start()
+ step = int(self._animation.animationStep() % self._lineWidth)
+ else:
+ step = 0
+ self._animation = QProgressStyleAnimation(self._fps, self)
+ self._animation.start()
+
+ # 动画斜线绘制
+ startX = int(progressBar.left() - rect.height() - self._lineWidth)
+ endX = int(rect.right() + self._lineWidth)
+
+ if (not inverted and not vertical) or (inverted and vertical):
+ lines = [
+ QLineF(x + step, progressBar.bottom(),
+ x + rect.height() + step, progressBar.top())
+ for x in range(startX, endX, self._lineWidth)
+ ]
+ else:
+ lines = [
+ QLineF(x - step, progressBar.bottom(),
+ x + rect.height() - step, progressBar.top())
+ for x in range(startX, endX, self._lineWidth)
+ ]
+ painter.drawLines(lines)
+
+
+if __name__ == '__main__':
+ import cgitb
+ import sys
+
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+
+ w = QWidget()
+ layout = QGridLayout(w)
+
+ w1 = ColourfulProgress(color=QColor('#85c440'))
+ w1.setMinimumWidth(300)
+ w1.setMaximumWidth(300)
+ w1.setRange(0, 100)
+ layout.addWidget(w1, 0, 0, 1, 1)
+
+ w2 = ColourfulProgress(color=QColor('#f2b63c'))
+ w2.setMinimumWidth(300)
+ w2.setMaximumWidth(300)
+ w2.setInvertedAppearance(True)
+ w2.setRange(0, 100)
+ layout.addWidget(w2, 1, 0, 1, 1)
+
+ w3 = ColourfulProgress(color=QColor('#db3a27'))
+ w3.setMinimumHeight(300)
+ w3.setMaximumHeight(300)
+ w3.setOrientation(Qt.Vertical)
+ w3.setRange(0, 100)
+ layout.addWidget(w3, 0, 1, 2, 1)
+
+ w4 = ColourfulProgress(color=QColor('#5aaadb'))
+ w4.setMinimumHeight(300)
+ w4.setMaximumHeight(300)
+ w4.setInvertedAppearance(True)
+ w4.setOrientation(Qt.Vertical)
+ w4.setRange(0, 100)
+ layout.addWidget(w4, 0, 2, 2, 1)
+
+ slider = QSlider(Qt.Horizontal)
+ slider.setRange(0, 100)
+ slider.valueChanged.connect(w1.setValue)
+ slider.valueChanged.connect(w2.setValue)
+ slider.valueChanged.connect(w3.setValue)
+ slider.valueChanged.connect(w4.setValue)
+ slider.setValue(50)
+ layout.addWidget(slider, 2, 0, 1, 3)
+
+ w.show()
+
+ sys.exit(app.exec_())
diff --git a/QProgressBar/Lib/DWaterProgress.py b/QProgressBar/Lib/DWaterProgress.py
new file mode 100644
index 0000000000000000000000000000000000000000..9242ada269702f48344074cd409079ea348ab2f6
--- /dev/null
+++ b/QProgressBar/Lib/DWaterProgress.py
@@ -0,0 +1,253 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2021/1/1
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: DWaterProgress
+@see https://github.com/linuxdeepin/dtkwidget/blob/master/src/widgets/dwaterprogress.cpp
+@description:
+"""
+import math
+
+try:
+ from PyQt5.QtCore import pyqtSlot, QTimer, QSizeF, Qt, QRectF, QPointF, QRect, QPoint, QSize
+ from PyQt5.QtGui import QImage, QColor, QPainter, QLinearGradient, QGradient, QPainterPath, QPixmap, \
+ QBrush, QPen
+ from PyQt5.QtSvg import QSvgRenderer
+ from PyQt5.QtWidgets import QProgressBar, QGraphicsDropShadowEffect
+except ImportError:
+ from PySide2.QtCore import Slot as pyqtSlot, QTimer, QSizeF, Qt, QRectF, QPointF, QRect, QPoint, QSize
+ from PySide2.QtGui import QImage, QColor, QPainter, QLinearGradient, QGradient, QPainterPath, QPixmap, \
+ QBrush, QPen
+ from PySide2.QtSvg import QSvgRenderer
+ from PySide2.QtWidgets import QProgressBar, QGraphicsDropShadowEffect
+
+WATER_FRONT = """
+
+
+"""
+WATER_BACK = """
+
+
+"""
+
+
+class Pop:
+ # https://github.com/linuxdeepin/dtkwidget/blob/master/src/widgets/dwaterprogress.cpp#L36
+
+ def __init__(self, size, xs, ys, xo=0, yo=0):
+ self.size = size
+ self.xSpeed = xs
+ self.ySpeed = ys
+ self.xOffset = xo
+ self.yOffset = yo
+
+
+class DWaterProgress(QProgressBar):
+
+ def __init__(self, *args, **kwargs):
+ super(DWaterProgress, self).__init__(*args, **kwargs)
+ self.waterFrontImage = QImage()
+ self.waterBackImage = QImage()
+ self.waterFrontSvg = QSvgRenderer(WATER_FRONT.encode())
+ self.waterBackSvg = QSvgRenderer(WATER_BACK.encode())
+ self.pops = []
+ self.initPops()
+ self.setTextVisible(True)
+ self.interval = 33
+ self.timer = QTimer(self)
+ self.timer.setInterval(self.interval)
+ self.timer.timeout.connect(self.onTimerOut)
+ self.resizePixmap(self.size())
+ self.frontXOffset = self.width()
+ self.backXOffset = 0
+ effect = QGraphicsDropShadowEffect(self)
+ effect.setOffset(0, 6)
+ effect.setColor(QColor(1, 153, 248, 255 * 5 / 20))
+ effect.setBlurRadius(12)
+ self.setGraphicsEffect(effect)
+
+ def initPops(self):
+ self.pops = [Pop(7, -1.8, 0.6), Pop(8, 1.2, 1.0), Pop(11, 0.8, 1.6)]
+
+ @pyqtSlot()
+ def start(self):
+ self.timer.start()
+
+ @pyqtSlot()
+ def stop(self):
+ self.timer.stop()
+
+ def resizePixmap(self, sz):
+ # https://github.com/linuxdeepin/dtkwidget/blob/master/src/widgets/dwaterprogress.cpp#L192
+ # resize water
+ waterWidth = 500 * sz.width() / 100
+ waterHeight = 110 * sz.height() / 100
+ waterSize = QSizeF(waterWidth, waterHeight).toSize()
+
+ if self.waterFrontImage.size() != waterSize:
+ image = QImage(waterWidth, waterHeight, QImage.Format_ARGB32)
+ image.fill(Qt.transparent)
+ waterPainter = QPainter(image)
+ self.waterFrontSvg.render(waterPainter)
+ self.waterFrontImage = image
+
+ if self.waterBackImage.size() != waterSize:
+ image = QImage(waterWidth, waterHeight, QImage.Format_ARGB32)
+ image.fill(Qt.transparent) # partly transparent red-ish background
+ waterPainter = QPainter(image)
+ self.waterBackSvg.render(waterPainter)
+ self.waterBackImage = image
+
+ def onTimerOut(self):
+ # interval can not be zero, and limit to 1
+ self.interval = max(1, self.interval)
+ # move 60% per second
+ frontXDeta = 40.0 / (1000.0 / self.interval)
+ # move 90% per second
+ backXDeta = 60.0 / (1000.0 / self.interval)
+
+ canvasWidth = int(self.width() * self.devicePixelRatioF())
+ self.frontXOffset -= frontXDeta * canvasWidth / 100
+ self.backXOffset += backXDeta * canvasWidth / 100
+
+ if self.frontXOffset > canvasWidth:
+ self.frontXOffset = canvasWidth
+
+ if self.frontXOffset < - (self.waterFrontImage.width() - canvasWidth):
+ self.frontXOffset = canvasWidth
+
+ if self.backXOffset > self.waterBackImage.width():
+ self.backXOffset = 0
+
+ # update pop
+ # move 25% per second default
+ speed = 25 / (1000.0 / self.interval) # 100 / self.height()
+ for pop in self.pops:
+ # yOffset 0 ~ 100
+ pop.yOffset += speed * pop.ySpeed
+ if pop.yOffset < 0:
+ pass
+ if pop.yOffset > self.value():
+ pop.yOffset = 0
+ pop.xOffset = math.sin((pop.yOffset / 100) * 2 * 3.14) * 18 * pop.xSpeed + 50
+ self.update()
+
+ def paint(self, painter):
+ painter.setRenderHint(QPainter.Antialiasing)
+
+ pixelRatio = self.devicePixelRatioF()
+ rect = QRectF(0, 0, self.width() * pixelRatio, self.height() * pixelRatio)
+ sz = QSizeF(self.width() * pixelRatio, self.height() * pixelRatio).toSize()
+
+ self.resizePixmap(sz)
+
+ yOffset = rect.toRect().topLeft().y() + (100 - self.value() - 10) * sz.height() / 100
+
+ # draw water
+ waterImage = QImage(sz, QImage.Format_ARGB32_Premultiplied)
+ waterPainter = QPainter()
+ waterPainter.begin(waterImage)
+ waterPainter.setRenderHint(QPainter.Antialiasing)
+ waterPainter.setCompositionMode(QPainter.CompositionMode_Source)
+
+ pointStart = QPointF(sz.width() / 2, 0)
+ pointEnd = QPointF(sz.width() / 2, sz.height())
+ linear = QLinearGradient(pointStart, pointEnd)
+ startColor = QColor('#1F08FF')
+ startColor.setAlphaF(1)
+ endColor = QColor('#50FFF7')
+ endColor.setAlphaF(0.28)
+ linear.setColorAt(0, startColor)
+ linear.setColorAt(1, endColor)
+ linear.setSpread(QGradient.PadSpread)
+ waterPainter.setPen(Qt.NoPen)
+ waterPainter.setBrush(linear)
+ waterPainter.drawEllipse(waterImage.rect().center(), sz.width() / 2 + 1, sz.height() / 2 + 1)
+
+ waterPainter.setCompositionMode(QPainter.CompositionMode_SourceOver)
+ waterPainter.drawImage(int(self.backXOffset), yOffset, self.waterBackImage)
+ waterPainter.drawImage(int(self.backXOffset) - self.waterBackImage.width(), yOffset,
+ self.waterBackImage)
+ waterPainter.drawImage(int(self.frontXOffset), yOffset, self.waterFrontImage)
+ waterPainter.drawImage(int(self.frontXOffset) - self.waterFrontImage.width(), yOffset,
+ self.waterFrontImage)
+
+ # draw pop
+ if self.value() > 30:
+ for pop in self.pops:
+ popPath = QPainterPath()
+ popPath.addEllipse(pop.xOffset * sz.width() / 100, (100 - pop.yOffset) * sz.height() / 100,
+ pop.size * sz.width() / 100, pop.size * sz.height() / 100)
+ waterPainter.fillPath(popPath, QColor(255, 255, 255, 255 * 0.3))
+
+ if self.isTextVisible():
+ font = waterPainter.font()
+ rectValue = QRect()
+ progressText = self.text().strip('%')
+
+ if progressText == '100':
+ font.setPixelSize(sz.height() * 35 / 100)
+ waterPainter.setFont(font)
+
+ rectValue.setWidth(sz.width() * 60 / 100)
+ rectValue.setHeight(sz.height() * 35 / 100)
+ rectValue.moveCenter(rect.center().toPoint())
+ waterPainter.setPen(Qt.white)
+ waterPainter.drawText(rectValue, Qt.AlignCenter, progressText)
+ else:
+ font.setPixelSize(sz.height() * 40 / 100)
+ waterPainter.setFont(font)
+
+ rectValue.setWidth(sz.width() * 45 / 100)
+ rectValue.setHeight(sz.height() * 40 / 100)
+ rectValue.moveCenter(rect.center().toPoint())
+ rectValue.moveLeft(rect.left() + rect.width() * 0.45 * 0.5)
+
+ waterPainter.setPen(Qt.white)
+ waterPainter.drawText(rectValue, Qt.AlignCenter, progressText)
+ font.setPixelSize(font.pixelSize() / 2)
+ waterPainter.setFont(font)
+ rectPerent = QRect(QPoint(rectValue.right(), rectValue.bottom() - rect.height() * 20 / 100),
+ QPoint(rectValue.right() + rect.width() * 20 / 100, rectValue.bottom()))
+
+ waterPainter.drawText(rectPerent, Qt.AlignCenter, '%')
+
+ waterPainter.end()
+
+ maskPixmap = QPixmap(sz)
+ maskPixmap.fill(Qt.transparent)
+ path = QPainterPath()
+ path.addEllipse(QRectF(0, 0, sz.width(), sz.height()))
+ maskPainter = QPainter()
+ maskPainter.begin(maskPixmap)
+ maskPainter.setRenderHint(QPainter.Antialiasing)
+ maskPainter.setPen(QPen(Qt.white, 1))
+ maskPainter.fillPath(path, QBrush(Qt.white))
+ maskPainter.end()
+
+ mode = QPainter.CompositionMode_SourceIn
+ contentImage = QImage(sz, QImage.Format_ARGB32_Premultiplied)
+ contentPainter = QPainter()
+ contentPainter.begin(contentImage)
+ contentPainter.setCompositionMode(QPainter.CompositionMode_Source)
+ contentPainter.fillRect(contentImage.rect(), Qt.transparent)
+ contentPainter.setCompositionMode(QPainter.CompositionMode_SourceOver)
+ contentPainter.drawImage(0, 0, maskPixmap.toImage())
+ contentPainter.setCompositionMode(mode)
+ contentPainter.drawImage(0, 0, waterImage)
+ contentPainter.setCompositionMode(QPainter.CompositionMode_DestinationOver)
+ contentPainter.end()
+
+ contentImage.setDevicePixelRatio(pixelRatio)
+ painter.drawImage(self.rect(), contentImage)
+
+ def paintEvent(self, event):
+ painter = QPainter(self)
+ self.paint(painter)
+
+ def sizeHint(self):
+ return QSize(100, 100)
diff --git a/QProgressBar/Lib/QStyleAnimation.py b/QProgressBar/Lib/QStyleAnimation.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2c91657a77b2f6964cda89f04f5231f9210a45a
--- /dev/null
+++ b/QProgressBar/Lib/QStyleAnimation.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2022/02/26
+@author: Irony
+@site: https://pyqt.site, https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QStyleAnimation.py
+@description:
+"""
+from enum import IntEnum
+
+try:
+ from PyQt5.QtCore import (QAbstractAnimation, QCoreApplication, QEvent,
+ QTime)
+except ImportError:
+ from PySide2.QtCore import (QAbstractAnimation, QCoreApplication, QEvent,
+ QTime)
+
+ScrollBarFadeOutDuration = 200.0
+ScrollBarFadeOutDelay = 450.0
+
+StyleAnimationUpdate = 213
+
+
+class QStyleAnimation(QAbstractAnimation):
+ FrameRate = IntEnum(
+ 'FrameRate',
+ ['DefaultFps', 'SixtyFps', 'ThirtyFps', 'TwentyFps', 'FifteenFps'])
+
+ def __init__(self, *args, **kwargs):
+ super(QStyleAnimation, self).__init__(*args, **kwargs)
+ self._delay = 0
+ self._duration = -1
+ self._startTime = QTime.currentTime()
+ self._fps = self.FrameRate.ThirtyFps
+ self._skip = 0
+
+ def target(self):
+ return self.parent()
+
+ def duration(self):
+ return self._duration
+
+ def setDuration(self, duration):
+ self._duration = duration
+
+ def delay(self):
+ return self._delay
+
+ def setDelay(self, delay):
+ self._delay = delay
+
+ def startTime(self):
+ return self._startTime
+
+ def setStartTime(self, time):
+ self._startTime = time
+
+ def frameRate(self):
+ return self._fps
+
+ def setFrameRate(self, fps):
+ self._fps = fps
+
+ def updateTarget(self):
+ event = QEvent(QEvent.Type(StyleAnimationUpdate))
+ event.setAccepted(False)
+ QCoreApplication.sendEvent(self.target(), event)
+ if not event.isAccepted():
+ self.stop()
+
+ def start(self):
+ self._skip = 0
+ super(QStyleAnimation, self).start(QAbstractAnimation.KeepWhenStopped)
+
+ def isUpdateNeeded(self):
+ return self.currentTime() > self._delay
+
+ def updateCurrentTime(self, _):
+ self._skip += 1
+ if self._skip >= self._fps:
+ self._skip = 0
+ if self.parent() and self.isUpdateNeeded():
+ self.updateTarget()
+
+
+class QProgressStyleAnimation(QStyleAnimation):
+
+ def __init__(self, speed, *args, **kwargs):
+ super(QProgressStyleAnimation, self).__init__(*args, **kwargs)
+ self._speed = speed
+ self._step = -1
+
+ def animationStep(self):
+ return self.currentTime() / (1000.0 / self._speed)
+
+ def progressStep(self, width):
+ step = self.animationStep()
+ progress = (step * width / self._speed) % width
+ if ((step * width / self._speed) % (2 * width)) >= width:
+ progress = width - progress
+ return progress
+
+ def speed(self):
+ return self._speed
+
+ def setSpeed(self, speed):
+ self._speed = speed
+
+ def isUpdateNeeded(self):
+ if super(QProgressStyleAnimation, self).isUpdateNeeded():
+ current = self.animationStep()
+ if self._step == -1 or self._step != current:
+ self._step = current
+ return True
+ return False
diff --git a/QProgressBar/Lib/WaterRippleProgressBar.py b/QProgressBar/Lib/WaterRippleProgressBar.py
index 3673a712ce59e7ab0d3e3274a69da0587913249d..605e56dd865eb31a8b18ae6e862c704536f95e7d 100644
--- a/QProgressBar/Lib/WaterRippleProgressBar.py
+++ b/QProgressBar/Lib/WaterRippleProgressBar.py
@@ -1,28 +1,28 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# Created on 2018年4月1日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: WaterRippleProgressBar
-# description:
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+"""
+Created on 2018年4月1日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: WaterRippleProgressBar
+@description:
+"""
import math
-from PyQt5.QtCore import QTimer, Qt, QRectF, QSize
-from PyQt5.QtGui import QPainter, QPainterPath, QColor, QFont
-from PyQt5.QtWidgets import QProgressBar
+try:
+ from PyQt5.QtCore import QTimer, Qt, QRectF, QSize
+ from PyQt5.QtGui import QPainter, QPainterPath, QColor, QFont
+ from PyQt5.QtWidgets import QProgressBar
+except ImportError:
+ from PySide2.QtCore import QTimer, Qt, QRectF, QSize
+ from PySide2.QtGui import QPainter, QPainterPath, QColor, QFont
+ from PySide2.QtWidgets import QProgressBar
class WaterRippleProgressBar(QProgressBar):
-
# 浪高百分比
waterHeight = 1
# 密度
@@ -84,7 +84,7 @@ class WaterRippleProgressBar(QProgressBar):
# 正弦曲线公式 y = A * sin(ωx + φ) + k
# 当前值所占百分比
percent = 1 - (self.value() - self.minimum()) / \
- (self.maximum() - self.minimum())
+ (self.maximum() - self.minimum())
# w表示周期,6为人为定义
w = 6 * self.waterDensity * math.pi / self.width()
# A振幅 高度百分比,1/26为人为定义
diff --git a/QProgressBar/MetroCircleProgress.py b/QProgressBar/MetroCircleProgress.py
index 3b1fe1f64657e22a5be0b5806eab14421d78b94f..58f4f4649aa5781d97770cdcb82cda190959b26b 100644
--- a/QProgressBar/MetroCircleProgress.py
+++ b/QProgressBar/MetroCircleProgress.py
@@ -4,27 +4,25 @@
"""
Created on 2018年9月日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: MetroCircleProgress
@description:
"""
-from PyQt5.QtCore import QSequentialAnimationGroup, pyqtProperty,\
- QPauseAnimation, QPropertyAnimation, QParallelAnimationGroup,\
- QObject, QSize, Qt, pyqtSignal, QRectF
-from PyQt5.QtGui import QPainter, QColor
-from PyQt5.QtWidgets import QWidget, QVBoxLayout
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import QSequentialAnimationGroup, QPauseAnimation, QPropertyAnimation, \
+ QParallelAnimationGroup, QObject, QSize, Qt, QRectF, pyqtSignal, pyqtProperty
+ from PyQt5.QtGui import QPainter, QColor
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout
+except ImportError:
+ from PySide2.QtCore import QSequentialAnimationGroup, QPauseAnimation, QPropertyAnimation, \
+ QParallelAnimationGroup, QObject, QSize, Qt, QRectF, Signal as pyqtSignal, Property as pyqtProperty
+ from PySide2.QtGui import QPainter, QColor
+ from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout
class CircleItem(QObject):
-
X = 0 # x坐标
Opacity = 1 # 透明度0~1
valueChanged = pyqtSignal()
@@ -52,7 +50,6 @@ def qBound(miv, cv, mxv):
class MetroCircleProgress(QWidget):
-
Radius = 5 # 半径
Color = QColor(24, 189, 155) # 圆圈颜色
BackgroundColor = QColor(Qt.transparent) # 背景颜色
@@ -189,7 +186,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QProgressBar/PercentProgressBar.py b/QProgressBar/PercentProgressBar.py
index 80f5d6c7ab5ad910d9f22478685f91c58d3ec2c1..8e469103682f078d55875b2e420383f05a911b17 100644
--- a/QProgressBar/PercentProgressBar.py
+++ b/QProgressBar/PercentProgressBar.py
@@ -4,25 +4,23 @@
"""
Created on 2018年9月4日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: PercentProgressBar
@description:
"""
-from PyQt5.QtCore import pyqtProperty, QSize, Qt, QRectF, QTimer
-from PyQt5.QtGui import QColor, QPainter, QFont
-from PyQt5.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QSlider
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import pyqtProperty, QSize, Qt, QRectF, QTimer
+ from PyQt5.QtGui import QColor, QPainter, QFont
+ from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QSlider
+except ImportError:
+ from PySide2.QtCore import Property as pyqtProperty, QSize, Qt, QRectF, QTimer
+ from PySide2.QtGui import QColor, QPainter, QFont
+ from PySide2.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QSlider
class PercentProgressBar(QWidget):
-
MinValue = 0
MaxValue = 100
Value = 0
@@ -300,8 +298,10 @@ class Window(QWidget):
self.staticPercentProgressBar.showFreeArea = True
self.staticPercentProgressBar.ShowSmallCircle = True
vlayout.addWidget(self.staticPercentProgressBar)
- vlayout.addWidget(QSlider(self, minimum=0, maximum=100, orientation=Qt.Horizontal,
- valueChanged=self.staticPercentProgressBar.setValue))
+
+ self.slider = QSlider(self, minimum=0, maximum=100, orientation=Qt.Horizontal)
+ self.slider.valueChanged.connect(self.staticPercentProgressBar.setValue)
+ vlayout.addWidget(self.slider)
self._timer.start(100)
@@ -316,8 +316,9 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.Hook(1, None, 5, sys.stderr, 'text')
- from PyQt5.QtWidgets import QApplication
+
+ cgitb.enable(format='text')
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QProgressBar/README.md b/QProgressBar/README.md
index 2c64728bc8924adfaef100cf91d10059ddbb8606..b7197dc8688a0b4bd6a6c65aac174d73d802e938 100644
--- a/QProgressBar/README.md
+++ b/QProgressBar/README.md
@@ -1,5 +1,14 @@
# QProgressBar
+- 目录
+ - [常规样式美化](#1常规样式美化)
+ - [圆圈进度条](#2圆圈进度条)
+ - [百分比进度条](#3百分比进度条)
+ - [Metro进度条](#4Metro进度条)
+ - [水波纹进度条](#5水波纹进度条)
+ - [圆形水位进度条](#6圆形水位进度条)
+ - [多彩动画进度条](#7多彩动画进度条)
+
## 1、常规样式美化
[运行 SimpleStyle.py](SimpleStyle.py)
@@ -29,4 +38,18 @@
2. 利用 `QPainterPath` 矩形或者圆形作为背景
3. 用 `QPainterPath` 把y坐标用 `lineTo` 连接起来形成一个U字形+上方波浪的闭合区间
-
\ No newline at end of file
+
+
+## 6、圆形水位进度条
+[运行 WaterProgress.py](WaterProgress.py)
+
+参考 https://github.com/linuxdeepin/dtkwidget/blob/master/src/widgets/dwaterprogress.cpp
+
+
+
+## 7、多彩动画进度条
+[运行 ColourfulProgress.py](ColourfulProgress.py)
+
+动画实现参考 qfusionstyle.cpp 中的 CE_ProgressBarContents 绘制方法
+
+
diff --git a/QProgressBar/RoundProgressBar.py b/QProgressBar/RoundProgressBar.py
index d406a6672134a304fa0b9393008352259595d36e..a52cef198697f28261753fb94d36d29749fd4ed6 100644
--- a/QProgressBar/RoundProgressBar.py
+++ b/QProgressBar/RoundProgressBar.py
@@ -4,25 +4,23 @@
"""
Created on 2018年9月4日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: 界面美化.圆形进度条.CircleProgressBar
@description:
"""
-from PyQt5.QtCore import QSize, pyqtProperty, QTimer, Qt
-from PyQt5.QtGui import QColor, QPainter
-from PyQt5.QtWidgets import QWidget, QHBoxLayout
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QSize, pyqtProperty, QTimer, Qt
+ from PyQt5.QtGui import QColor, QPainter
+ from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout
+except ImportError:
+ from PySide2.QtCore import QSize, Property as pyqtProperty, QTimer, Qt
+ from PySide2.QtGui import QColor, QPainter
+ from PySide2.QtWidgets import QApplication, QWidget, QHBoxLayout
class CircleProgressBar(QWidget):
-
Color = QColor(24, 189, 155) # 圆圈颜色
Clockwise = True # 顺时针还是逆时针
Delta = 36
@@ -105,7 +103,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QProgressBar/ScreenShot/ColourfulProgress.gif b/QProgressBar/ScreenShot/ColourfulProgress.gif
new file mode 100644
index 0000000000000000000000000000000000000000..3489cd96cd5419930e2a78db5dc95fd99ddd1784
Binary files /dev/null and b/QProgressBar/ScreenShot/ColourfulProgress.gif differ
diff --git a/QProgressBar/ScreenShot/WaterProgress.gif b/QProgressBar/ScreenShot/WaterProgress.gif
new file mode 100644
index 0000000000000000000000000000000000000000..f434c17290089053933c5c9c397c53f22f42d0ee
Binary files /dev/null and b/QProgressBar/ScreenShot/WaterProgress.gif differ
diff --git a/QProgressBar/SimpleStyle.py b/QProgressBar/SimpleStyle.py
index 2ad17c8e7e4e18804c7bc433f820ed9cb60f2a98..b8aa5b73b92b843c7674b2caab12b8b0fa03bc65 100644
--- a/QProgressBar/SimpleStyle.py
+++ b/QProgressBar/SimpleStyle.py
@@ -1,26 +1,26 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月30日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: SimpleStyle
@description:
-'''
-from random import randint
-import sys
-
-from PyQt5.QtCore import QTimer
-from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QProgressBar
+"""
+import sys
+from random import randint
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QTimer
+ from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QProgressBar
+except ImportError:
+ from PySide2.QtCore import QTimer
+ from PySide2.QtWidgets import QWidget, QApplication, QVBoxLayout, QProgressBar
-StyleSheet = '''
+StyleSheet = """
/*设置红色进度条*/
#RedProgressBar {
text-align: center; /*进度值居中*/
@@ -50,7 +50,7 @@ StyleSheet = '''
width: 10px; /*区块宽度*/
margin: 0.5px;
}
-'''
+"""
class ProgressBar(QProgressBar):
diff --git a/QProgressBar/WaterProgress.py b/QProgressBar/WaterProgress.py
new file mode 100644
index 0000000000000000000000000000000000000000..97bbc20178a005846bd017715fcdf9ab9e8d8e54
--- /dev/null
+++ b/QProgressBar/WaterProgress.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2021/1/1
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: WaterProgress
+@description:
+"""
+import sys
+
+try:
+ from PyQt5.QtCore import QTimer
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout
+except ImportError:
+ from PySide2.QtCore import QTimer
+ from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout
+
+from Lib.DWaterProgress import DWaterProgress
+
+
+class WaterProgressWindow(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(WaterProgressWindow, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+
+ self.progress = DWaterProgress(self)
+ self.progress.setFixedSize(100, 100)
+ self.progress.setValue(0)
+ self.progress.start()
+
+ layout.addWidget(self.progress)
+
+ self.timer = QTimer(self, timeout=self.updateProgress)
+ self.timer.start(50)
+
+ def updateProgress(self):
+ value = self.progress.value()
+ if value == 100:
+ self.progress.setValue(0)
+ else:
+ self.progress.setValue(value + 1)
+
+
+if __name__ == '__main__':
+ import cgitb
+
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = WaterProgressWindow()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QProgressBar/WaterProgressBar.py b/QProgressBar/WaterProgressBar.py
index 5704fe06839bdfdaac65424a38113a6dee144958..30aa6d43f49427e008f3ca0e8805e96c12abe79d 100644
--- a/QProgressBar/WaterProgressBar.py
+++ b/QProgressBar/WaterProgressBar.py
@@ -1,27 +1,27 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# Created on 2018年4月1日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: WaterProgressBar
-# description:
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
-
+"""
+Created on 2018年4月1日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: WaterProgressBar
+@description:
+"""
from random import randint
-from PyQt5.Qt import QSpinBox
-from PyQt5.QtCore import QTimer
-from PyQt5.QtGui import QPixmap, QIcon
-from PyQt5.QtWidgets import QWidget, QFormLayout, QRadioButton, QPushButton,\
- QColorDialog
+try:
+ from PyQt5.QtCore import QTimer
+ from PyQt5.QtGui import QPixmap, QIcon
+ from PyQt5.QtWidgets import QApplication, QWidget, QFormLayout, QRadioButton, QPushButton, QColorDialog, \
+ QSpinBox
+except ImportError:
+ from PySide2.QtCore import QTimer
+ from PySide2.QtGui import QPixmap, QIcon
+ from PySide2.QtWidgets import QApplication, QWidget, QFormLayout, QRadioButton, QPushButton, QColorDialog, \
+ QSpinBox
from Lib.WaterRippleProgressBar import WaterRippleProgressBar # @UnresolvedImport
@@ -53,11 +53,13 @@ class Window(QWidget):
layout.addWidget(
QPushButton('设置随机0-100固定值', self, clicked=self.setRandomValue))
- layout.addRow('振幅(浪高)',
- QSpinBox(self, value=1, valueChanged=self.bar.setWaterHeight))
+ spb1 = QSpinBox(self, value=1)
+ spb1.valueChanged.connect(self.bar.setWaterHeight)
+ layout.addRow('振幅(浪高)', spb1)
- layout.addRow('周期(密度)',
- QSpinBox(self, value=1, valueChanged=self.bar.setWaterDensity))
+ spb2 = QSpinBox(self, value=1)
+ spb2.valueChanged.connect(self.bar.setWaterDensity)
+ layout.addRow('周期(密度)', spb2)
layout.addWidget(self.bar)
@@ -116,9 +118,10 @@ class Window(QWidget):
value = 0
self.bar.setValue(value)
+
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QPropertyAnimation/Data/1.png b/QPropertyAnimation/Data/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..f53f32a98d5f00dd28c05c42c2a5c7599b2a7108
Binary files /dev/null and b/QPropertyAnimation/Data/1.png differ
diff --git a/QPropertyAnimation/Data/2.png b/QPropertyAnimation/Data/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..b6a92262815c100b59ed38d45bc65ebc326d9510
Binary files /dev/null and b/QPropertyAnimation/Data/2.png differ
diff --git a/QPropertyAnimation/Data/pointtool.pyx b/QPropertyAnimation/Data/pointtool.pyx
index bd47b6678072cc784648eb7b662f1caefc36280a..3300cc6679144116ddbdf29f3aaa625b123d9908 100644
--- a/QPropertyAnimation/Data/pointtool.pyx
+++ b/QPropertyAnimation/Data/pointtool.pyx
@@ -1,10 +1,8 @@
from libc.math cimport pow
-
def getDistance(p1, p2):
return pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2)
-
def findClose(points):
cdef int plen = len(points)
cdef int i = 0
diff --git a/QPropertyAnimation/Data/setup.py b/QPropertyAnimation/Data/setup.py
index 09839f6ee37088b4c389cc97e751fe7e305d304b..3112b44c03ec2be395ba690ad17528ea53a03a06 100644
--- a/QPropertyAnimation/Data/setup.py
+++ b/QPropertyAnimation/Data/setup.py
@@ -10,8 +10,9 @@
# )
from distutils.core import setup
+
from Cython.Build import cythonize
setup(
ext_modules=cythonize("pointtool.pyx"),
-)
\ No newline at end of file
+)
diff --git a/QPropertyAnimation/FadeInOut.py b/QPropertyAnimation/FadeInOut.py
index edf30eae42a743fac1e0f7b535d1ef189d1389ce..024c65c27346d703081aaa408219b0652270b90e 100644
--- a/QPropertyAnimation/FadeInOut.py
+++ b/QPropertyAnimation/FadeInOut.py
@@ -1,20 +1,22 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from PyQt5.QtCore import QPropertyAnimation
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton
-# Created on 2018年6月14日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: FadeInOut
-# description:
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+"""
+Created on 2018年6月14日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: FadeInOut
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import QPropertyAnimation
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
+except ImportError:
+ from PySide2.QtCore import QPropertyAnimation
+ from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
class Window(QWidget):
@@ -55,7 +57,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QPropertyAnimation/FlipWidgetAnimation.py b/QPropertyAnimation/FlipWidgetAnimation.py
new file mode 100644
index 0000000000000000000000000000000000000000..1adc2648fe282f9de3ed7dc587bc965568ab7816
--- /dev/null
+++ b/QPropertyAnimation/FlipWidgetAnimation.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年5月15日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: 翻转动画
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import Qt, pyqtSignal, QTimer
+ from PyQt5.QtGui import QPixmap
+ from PyQt5.QtWidgets import QApplication, QStackedWidget, QLabel
+except ImportError:
+ from PySide2.QtCore import Qt, Signal as pyqtSignal, QTimer
+ from PySide2.QtGui import QPixmap
+ from PySide2.QtWidgets import QApplication, QStackedWidget, QLabel
+
+from Lib.FlipWidget import FlipWidget
+
+
+class LoginWidget(QLabel):
+ # 只是显示登录界面截图
+
+ windowClosed = pyqtSignal()
+ windowChanged = pyqtSignal()
+
+ def __init__(self, *args, **kwargs):
+ super(LoginWidget, self).__init__(*args, **kwargs)
+ self.setPixmap(QPixmap('Data/1.png'))
+
+ def mousePressEvent(self, event):
+ super(LoginWidget, self).mousePressEvent(event)
+ pos = event.pos()
+ if pos.y() <= 40:
+ if pos.x() > self.width() - 30:
+ # 点击关闭按钮的地方
+ self.windowClosed.emit()
+ elif self.width() - 90 <= pos.x() <= self.width() - 60:
+ # 点击切换按钮
+ self.windowChanged.emit()
+
+
+class SettingWidget(QLabel):
+ # 只是显示设置界面截图
+
+ windowClosed = pyqtSignal()
+ windowChanged = pyqtSignal()
+
+ def __init__(self, *args, **kwargs):
+ super(SettingWidget, self).__init__(*args, **kwargs)
+ self.setPixmap(QPixmap('Data/2.png'))
+
+ def mousePressEvent(self, event):
+ super(SettingWidget, self).mousePressEvent(event)
+ pos = event.pos()
+ if pos.y() >= self.height() - 30:
+ if self.width() - 95 <= pos.x() <= self.width() - 10:
+ # 点击切换按钮
+ self.windowChanged.emit()
+ elif pos.y() <= 40:
+ if pos.x() > self.width() - 30:
+ # 点击关闭按钮的地方
+ self.windowClosed.emit()
+
+
+class Window(QStackedWidget):
+ # 主窗口
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(428, 329)
+ self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)
+
+ # 这个是动画窗口,先创建不显示
+ self.flipWidget = FlipWidget()
+ self.flipWidget.finished.connect(self.showWidget)
+
+ # 登录窗口
+ self.loginWidget = LoginWidget(self)
+ self.loginWidget.windowClosed.connect(self.close)
+ self.loginWidget.windowChanged.connect(self.jumpSettingWidget)
+ self.addWidget(self.loginWidget)
+
+ # 设置窗口
+ self.settingWidget = SettingWidget(self)
+ self.settingWidget.windowClosed.connect(self.close)
+ self.settingWidget.windowChanged.connect(self.jumpLoginWidget)
+ self.addWidget(self.settingWidget)
+
+ def showWidget(self):
+ # 显示主窗口隐藏动画窗口
+ self.setWindowOpacity(1)
+ QTimer.singleShot(100, self.flipWidget.hide)
+
+ def jumpLoginWidget(self):
+ # 翻转到登录界面
+ self.setWindowOpacity(0) # 类似隐藏,但是保留了任务栏
+ self.setCurrentWidget(self.loginWidget) # 很重要,一定要先切换过去,不然会导致第一次截图有误
+ image1 = self.loginWidget.grab() # 截图1
+ image2 = self.settingWidget.grab() # 截图2
+ padding = 100 # 扩大边距 @UnusedVariable
+ self.flipWidget.setGeometry(self.geometry())
+ # .adjusted(-padding, -padding, padding, padding))
+ self.flipWidget.updateImages(FlipWidget.Right, image2, image1)
+
+ def jumpSettingWidget(self):
+ # 翻转到设置界面
+ self.setWindowOpacity(0) # 类似隐藏,但是保留了任务栏
+ self.setCurrentWidget(self.settingWidget) # 很重要,一定要先切换过去,不然会导致第一次截图有误
+ image1 = self.loginWidget.grab() # 截图1
+ image2 = self.settingWidget.grab() # 截图2
+ padding = 100 # 扩大边距 @UnusedVariable
+ self.flipWidget.setGeometry(self.geometry())
+ # .adjusted(-padding, -padding, padding, padding))
+ self.flipWidget.updateImages(FlipWidget.Left, image1, image2)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QPropertyAnimation/Lib/FlipWidget.py b/QPropertyAnimation/Lib/FlipWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..6878e632626a86af096b7e780cbd259eb900a4c6
--- /dev/null
+++ b/QPropertyAnimation/Lib/FlipWidget.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年5月15日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: FlipWidget
+@description: 动画翻转窗口
+"""
+
+try:
+ from PyQt5.QtCore import pyqtSignal, pyqtProperty, Qt, QPropertyAnimation, QEasingCurve, QPointF
+ from PyQt5.QtGui import QPainter, QTransform
+ from PyQt5.QtWidgets import QWidget
+except ImportError:
+ from PySide2.QtCore import Signal as pyqtSignal, Property as pyqtProperty, Qt, QPropertyAnimation, \
+ QEasingCurve, QPointF
+ from PySide2.QtGui import QPainter, QTransform
+ from PySide2.QtWidgets import QWidget
+
+
+class FlipWidget(QWidget):
+ Left = 0 # 从右往左
+ Right = 1 # 从左往右
+ Scale = 3 # 图片缩放比例
+ finished = pyqtSignal()
+
+ def __init__(self, *args, **kwargs):
+ super(FlipWidget, self).__init__(*args, **kwargs)
+ # 无边框无任务栏
+ self.setWindowFlags(self.windowFlags() |
+ Qt.FramelessWindowHint | Qt.SubWindow)
+ # 背景透明
+ self.setAttribute(Qt.WA_TranslucentBackground, True)
+ # 翻转角度
+ self._angle = 0
+ # 属性动画针对自定义属性`angle`
+ self._animation = QPropertyAnimation(self, b'angle', self)
+ self._animation.setDuration(550)
+ self._animation.setEasingCurve(QEasingCurve.OutInQuad)
+ self._animation.finished.connect(self.finished.emit)
+
+ @pyqtProperty(int)
+ def angle(self):
+ return self._angle
+
+ @angle.setter
+ def angle(self, angle):
+ self._angle = angle
+ self.update()
+
+ def updateImages(self, direction, image1, image2):
+ """设置两张切换图
+ :param direction: 方向
+ :param image1: 图片1
+ :param image2: 图片2
+ """
+ self.image1 = image1
+ self.image2 = image2
+ self.show()
+ self._angle = 0
+ # 根据方向设置动画的初始和结束值
+ if direction == self.Right:
+ self._animation.setStartValue(1)
+ self._animation.setEndValue(-180)
+ elif direction == self.Left:
+ self._animation.setStartValue(1)
+ self._animation.setEndValue(180)
+ self._animation.start()
+
+ def paintEvent(self, event):
+ super(FlipWidget, self).paintEvent(event)
+
+ if hasattr(self, 'image1') and hasattr(self, 'image2') and self.isVisible():
+
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.Antialiasing, True)
+ painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
+
+ # 变换
+ transform = QTransform()
+ # 把圆心设置为矩形中心
+ transform.translate(self.width() / 2, self.height() / 2)
+
+ if self._angle >= -90 and self._angle <= 90:
+ # 当翻转角度在90范围内显示第一张图,且从大图缩放到小图的过程
+ painter.save()
+ # 设置翻转角度
+ transform.rotate(self._angle, Qt.YAxis)
+ painter.setTransform(transform)
+ # 缩放图片高度
+ width = self.image1.width() / 2
+ height = int(self.image1.height() *
+ (1 - abs(self._angle / self.Scale) / 100))
+ image = self.image1.scaled(
+ self.image1.width(), height,
+ Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
+ painter.drawPixmap(
+ QPointF(-width, -height / 2), image)
+ painter.restore()
+ else:
+ # 当翻转角度在90范围内显示第二张图,且从小图缩放到原图的过程
+ painter.save()
+ if self._angle > 0:
+ angle = 180 + self._angle
+ else:
+ angle = self._angle - 180
+ # 设置翻转角度, 注意这里角度有差异
+ transform.rotate(angle, Qt.YAxis)
+ painter.setTransform(transform)
+ # 缩放图片高度
+ width = self.image2.width() / 2
+ height = int(self.image2.height() *
+ (1 - ((360 - abs(angle)) / self.Scale / 100)))
+ image = self.image2.scaled(
+ self.image2.width(), height,
+ Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
+ painter.drawPixmap(
+ QPointF(-width, -height / 2), image)
+ painter.restore()
diff --git a/QPropertyAnimation/Lib/SlidingStackedWidget.py b/QPropertyAnimation/Lib/SlidingStackedWidget.py
index aae37a147668b41e40ee3c707b6ca881f6d456db..b10bb341dde439061a8c01eb0a92fd4a4a43a1fe 100644
--- a/QPropertyAnimation/Lib/SlidingStackedWidget.py
+++ b/QPropertyAnimation/Lib/SlidingStackedWidget.py
@@ -4,25 +4,23 @@
"""
Created on 2018年11月24日
author: Irony
-site: https://pyqt5.com , https://github.com/892768447
+site: https://pyqt.site , https://github.com/PyQt5
email: 892768447@qq.com
file:
description: 参考 http://qt.shoutwiki.com/wiki/Extending_QStackedWidget_for_sliding_page_animations_in_Qt
"""
-from PyQt5.QtCore import Qt, pyqtProperty, QEasingCurve, QPoint, \
- QPropertyAnimation, QParallelAnimationGroup, QTimer
-from PyQt5.QtWidgets import QStackedWidget
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import Qt, pyqtProperty, QEasingCurve, QPoint, QPropertyAnimation, \
+ QParallelAnimationGroup, QTimer
+ from PyQt5.QtWidgets import QStackedWidget
+except ImportError:
+ from PySide2.QtCore import Qt, Property as pyqtProperty, QEasingCurve, QPoint, QPropertyAnimation, \
+ QParallelAnimationGroup, QTimer
+ from PySide2.QtWidgets import QStackedWidget
class SlidingStackedWidget(QStackedWidget):
-
LEFT2RIGHT, RIGHT2LEFT, TOP2BOTTOM, BOTTOM2TOP, AUTOMATIC = range(5)
def __init__(self, *args, **kwargs):
@@ -119,7 +117,7 @@ class SlidingStackedWidget(QStackedWidget):
self._active = 1
_now = self.currentIndex()
_next = self.indexOf(widget)
- if _now == next:
+ if _now == _next:
self._active = 0
return
@@ -210,7 +208,7 @@ class SlidingStackedWidget(QStackedWidget):
def animationDoneSlot(self):
"""动画结束处理函数"""
# 由于重写了setCurrentIndex方法所以这里要用父类本身的方法
-# self.setCurrentIndex(self._next)
+ # self.setCurrentIndex(self._next)
QStackedWidget.setCurrentIndex(self, self._next)
w = self.widget(self._now)
w.hide()
diff --git a/QPropertyAnimation/Lib/UiImageSlider.py b/QPropertyAnimation/Lib/UiImageSlider.py
index a47fb819e19702287b366dd9ffd08a863cfba1d7..78be1306ca98d90d8e283000bb1135f6a0f2da6a 100644
--- a/QPropertyAnimation/Lib/UiImageSlider.py
+++ b/QPropertyAnimation/Lib/UiImageSlider.py
@@ -6,7 +6,11 @@
#
# WARNING! All changes made in this file will be lost!
-from PyQt5 import QtCore, QtGui, QtWidgets
+try:
+ from PyQt5 import QtCore, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtWidgets
+
class Ui_Form(object):
def setupUi(self, Form):
@@ -89,14 +93,15 @@ class Ui_Form(object):
self.pushButtonStart.setText(_translate("Form", "轮播开始"))
self.pushButtonStop.setText(_translate("Form", "轮播停止"))
+
from Lib.SlidingStackedWidget import SlidingStackedWidget
if __name__ == "__main__":
import sys
+
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
-
diff --git a/QPropertyAnimation/MenuAnimation.py b/QPropertyAnimation/MenuAnimation.py
index e8a84c5629776b6b17194c779aea193cc01da47d..4e8c7cdc1af9cfb592609ad8d38018128d8d4695 100644
--- a/QPropertyAnimation/MenuAnimation.py
+++ b/QPropertyAnimation/MenuAnimation.py
@@ -4,20 +4,18 @@
"""
Created on 2018年8月22日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: MenuAnimation
@description:
"""
-from PyQt5.QtCore import QPropertyAnimation, QEasingCurve, QRect
-from PyQt5.QtWidgets import QWidget, QMenu, QApplication
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QPropertyAnimation, QEasingCurve, QRect
+ from PyQt5.QtWidgets import QWidget, QMenu, QApplication
+except ImportError:
+ from PySide2.QtCore import QPropertyAnimation, QEasingCurve, QRect
+ from PySide2.QtWidgets import QWidget, QMenu, QApplication
class Window(QWidget):
@@ -61,7 +59,8 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.Hook(1, None, 5, sys.stderr, 'text')
+
+ cgitb.enable(format='text')
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QPropertyAnimation/PageSwitching.py b/QPropertyAnimation/PageSwitching.py
index 739f4040f93c9abe87d2e9b6d3ff9647321dddcd..9477eb4edff849e29cb86825615b123ef05dfa64 100644
--- a/QPropertyAnimation/PageSwitching.py
+++ b/QPropertyAnimation/PageSwitching.py
@@ -4,27 +4,25 @@
"""
Created on 2018年11月24日
author: Irony
-site: https://pyqt5.com , https://github.com/892768447
+site: https://pyqt.site , https://github.com/PyQt5
email: 892768447@qq.com
file: PageSwitching
description:
"""
import os
-from PyQt5.QtCore import QEasingCurve, Qt
-from PyQt5.QtGui import QPixmap
-from PyQt5.QtWidgets import QWidget, QLabel
+try:
+ from PyQt5.QtCore import QEasingCurve, Qt
+ from PyQt5.QtGui import QPixmap
+ from PyQt5.QtWidgets import QWidget, QLabel, QApplication
+except ImportError:
+ from PySide2.QtCore import QEasingCurve, Qt
+ from PySide2.QtGui import QPixmap
+ from PySide2.QtWidgets import QWidget, QLabel, QApplication
from Lib.UiImageSlider import Ui_Form # @UnresolvedImport
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
-
-
class ImageSliderWidget(QWidget, Ui_Form):
def __init__(self, *args, **kwargs):
@@ -76,7 +74,7 @@ class ImageSliderWidget(QWidget, Ui_Form):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = ImageSliderWidget()
w.show()
diff --git a/QPropertyAnimation/README.md b/QPropertyAnimation/README.md
index 6becc907869ade4bb2300223138474b3e506d962..25ecc4df4389e885a29e2fef532dc22eec8401c4 100644
--- a/QPropertyAnimation/README.md
+++ b/QPropertyAnimation/README.md
@@ -1,6 +1,14 @@
# QPropertyAnimation
-# 1、窗口淡入淡出
+- 目录
+ - [窗口淡入淡出](#1窗口淡入淡出)
+ - [右键菜单动画](#2右键菜单动画)
+ - [点阵特效](#3点阵特效)
+ - [页面切换/图片轮播动画](#4页面切换图片轮播动画)
+ - [窗口抖动](#5窗口抖动)
+ - [窗口翻转动画(仿QQ)](#6窗口翻转动画仿QQ)
+
+## 1、窗口淡入淡出
[运行 FadeInOut.py](FadeInOut.py)
1. 使用`QPropertyAnimation`对窗口的`windowOpacity`透明度属性进行修改
@@ -13,7 +21,7 @@

-# 2、右键菜单动画
+## 2、右键菜单动画
[运行 MenuAnimation.py](MenuAnimation.py)
1. 使用`QPropertyAnimation`对菜单控件的`geometry`属性进行修改
@@ -96,8 +104,8 @@ def findClose(points):

-## 5、页面切换/图片轮播动画
-[运行 PageSwitching.py](PageSwitching.py)
+## 4、页面切换/图片轮播动画
+[运行 PageSwitching.py](PageSwitching.py) | [查看 UiImageSlider.ui](Data/UiImageSlider.ui)
1. 使用`QPropertyAnimation`对`QStackedWidget`中的子控件进行pos位移操作实现动画切换特效
1. 主要代码参考http://qt.shoutwiki.com/wiki/Extending_QStackedWidget_for_sliding_page_animations_in_Qt
@@ -110,4 +118,22 @@ def findClose(points):
1. `setCurrentIndex` 切换到指定页
1. `autoStart(msec)` 轮播模式, 默认是3000毫秒
-
\ No newline at end of file
+
+
+## 5、窗口抖动
+[运行 ShakeWindow.py](ShakeWindow.py)
+
+通过`QPropertyAnimation`对控件的pos属性进行死去活来的修改
+
+
+
+## 6、窗口翻转动画(仿QQ)
+[运行 FlipWidgetAnimation.py](FlipWidgetAnimation.py)
+
+1. 用了两个`QLabel`来显示模拟的图片界面,并实现鼠标点击模拟真实的窗口对应位置点击
+2. 用了`QStackedWidget`来存放上面的两个界面`QLabel`
+3. 点击切换时主要是对上面的两个界面进行截图并传递给翻转动画窗口
+4. 通过`setWindowOpacity`控制主窗口的显示隐藏(保留任务栏),当然也可以用`hide`
+5. 动画窗口`FlipWidget.py`主要实现两张图片的翻转显示,考虑到0-90和90-180之前的情况,以及图片的缩放动画
+
+
\ No newline at end of file
diff --git a/QPropertyAnimation/RlatticeEffect.py b/QPropertyAnimation/RlatticeEffect.py
index 7c4433740ed71176f192b546b6c53bbce940071b..a57c37b1c2a2f69dc1a9ca38d21b6e69a3449c47 100644
--- a/QPropertyAnimation/RlatticeEffect.py
+++ b/QPropertyAnimation/RlatticeEffect.py
@@ -4,7 +4,7 @@
"""
Created on 2018年11月22日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: RlatticeEffect
@description:
@@ -12,29 +12,29 @@ Created on 2018年11月22日
from random import random
from time import time
-from PyQt5.QtCore import QPropertyAnimation, QObject, pyqtProperty, QEasingCurve,\
- Qt, QRectF, pyqtSignal
-from PyQt5.QtGui import QColor, QPainterPath, QPainter
-from PyQt5.QtWidgets import QWidget
-
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
-
+try:
+ from PyQt5.QtCore import QPropertyAnimation, QObject, QEasingCurve, Qt, QRectF, pyqtSignal, pyqtProperty
+ from PyQt5.QtGui import QColor, QPainterPath, QPainter
+ from PyQt5.QtWidgets import QApplication, QWidget
+except ImportError:
+ from PySide2.QtCore import QPropertyAnimation, QObject, QEasingCurve, Qt, QRectF, Signal as pyqtSignal, \
+ Property as pyqtProperty
+ from PySide2.QtGui import QColor, QPainterPath, QPainter
+ from PySide2.QtWidgets import QApplication, QWidget
try:
from Lib import pointtool # @UnusedImport @UnresolvedImport
+
getDistance = pointtool.getDistance
findClose = pointtool.findClose
except:
import math
+
def getDistance(p1, p2):
return math.pow(p1.x - p2.x, 2) + math.pow(p1.y - p2.y, 2)
+
def findClose(points):
plen = len(points)
for i in range(plen):
@@ -66,8 +66,7 @@ class Target:
class Point(QObject):
-
- valueChanged = pyqtSignal()
+ valueChanged = pyqtSignal(int)
def __init__(self, x, ox, y, oy, *args, **kwargs):
super(Point, self).__init__(*args, **kwargs)
@@ -90,12 +89,12 @@ class Point(QObject):
# 属性动画
if not hasattr(self, 'xanimation'):
self.xanimation = QPropertyAnimation(
- self, b'x', self, valueChanged=self.valueChanged.emit,
- easingCurve=QEasingCurve.InOutSine)
+ self, b'x', self, easingCurve=QEasingCurve.InOutSine)
+ self.xanimation.valueChanged.connect(self.valueChanged.emit)
self.yanimation = QPropertyAnimation(
- self, b'y', self, valueChanged=self.valueChanged.emit,
- easingCurve=QEasingCurve.InOutSine,
- finished=self.updateAnimation)
+ self, b'y', self, easingCurve=QEasingCurve.InOutSine)
+ self.yanimation.valueChanged.connect(self.valueChanged.emit)
+ self.yanimation.finished.connect(self.updateAnimation)
self.updateAnimation()
def updateAnimation(self):
@@ -138,6 +137,9 @@ class Window(QWidget):
self.target = Target(self.width() / 2, self.height() / 2)
self.initPoints()
+ def update(self, *args):
+ super(Window, self).update()
+
def paintEvent(self, event):
super(Window, self).paintEvent(event)
painter = QPainter()
@@ -220,8 +222,9 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.enable(1, None, 5, '')
- from PyQt5.QtWidgets import QApplication
+
+ cgitb.enable(format='text')
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QPropertyAnimation/ScreenShot/FlipWidgetAnimation.gif b/QPropertyAnimation/ScreenShot/FlipWidgetAnimation.gif
new file mode 100644
index 0000000000000000000000000000000000000000..106f90106fb77bb34d0fd8ac98f9d7b30b829795
Binary files /dev/null and b/QPropertyAnimation/ScreenShot/FlipWidgetAnimation.gif differ
diff --git a/QPropertyAnimation/ScreenShot/ShakeWindow.gif b/QPropertyAnimation/ScreenShot/ShakeWindow.gif
new file mode 100644
index 0000000000000000000000000000000000000000..3100a51a450b8493eb6b3403c0993f2ebe2a1185
Binary files /dev/null and b/QPropertyAnimation/ScreenShot/ShakeWindow.gif differ
diff --git a/QPropertyAnimation/ShakeWindow.py b/QPropertyAnimation/ShakeWindow.py
new file mode 100644
index 0000000000000000000000000000000000000000..6c7d9c95325dbb7889d307a29cd3b64c1b2b5223
--- /dev/null
+++ b/QPropertyAnimation/ShakeWindow.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年5月8日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ShakeWindow
+@description: 抖动动画
+"""
+
+try:
+ from PyQt5.QtCore import QPropertyAnimation, QPoint
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
+except ImportError:
+ from PySide2.QtCore import QPropertyAnimation, QPoint
+ from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 400)
+ layout = QVBoxLayout(self)
+ layout.addWidget(QPushButton('抖动', self, clicked=self.doShake))
+
+ def doShake(self):
+ self.doShakeWindow(self)
+
+ # 下面这个方法可以做成这样的封装给任何控件
+ def doShakeWindow(self, target):
+ """窗口抖动动画
+ :param target: 目标控件
+ """
+ if hasattr(target, '_shake_animation'):
+ # 如果已经有该对象则跳过
+ return
+
+ animation = QPropertyAnimation(target, b'pos', target)
+ target._shake_animation = animation
+ animation.finished.connect(lambda: delattr(target, '_shake_animation'))
+
+ pos = target.pos()
+ x, y = pos.x(), pos.y()
+
+ animation.setDuration(200)
+ animation.setLoopCount(2)
+ animation.setKeyValueAt(0, QPoint(x, y))
+ animation.setKeyValueAt(0.09, QPoint(x + 2, y - 2))
+ animation.setKeyValueAt(0.18, QPoint(x + 4, y - 4))
+ animation.setKeyValueAt(0.27, QPoint(x + 2, y - 6))
+ animation.setKeyValueAt(0.36, QPoint(x + 0, y - 8))
+ animation.setKeyValueAt(0.45, QPoint(x - 2, y - 10))
+ animation.setKeyValueAt(0.54, QPoint(x - 4, y - 8))
+ animation.setKeyValueAt(0.63, QPoint(x - 6, y - 6))
+ animation.setKeyValueAt(0.72, QPoint(x - 8, y - 4))
+ animation.setKeyValueAt(0.81, QPoint(x - 6, y - 2))
+ animation.setKeyValueAt(0.90, QPoint(x - 4, y - 0))
+ animation.setKeyValueAt(0.99, QPoint(x - 2, y + 2))
+ animation.setEndValue(QPoint(x, y))
+
+ animation.start(animation.DeleteWhenStopped)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QProxyStyle/Lib/TabBarStyle.py b/QProxyStyle/Lib/TabBarStyle.py
index 12d7c8ab2bd2e9ad8db89d4e29ab2ff83bf26f47..59406ccfb4e204beb27453f7d7c4ce2c69280cf6 100644
--- a/QProxyStyle/Lib/TabBarStyle.py
+++ b/QProxyStyle/Lib/TabBarStyle.py
@@ -4,7 +4,7 @@
"""
Created on 2018年12月27日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: TabBarStyle
@description:
@@ -13,13 +13,6 @@ from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QProxyStyle
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
-
-
class TabBarStyle(QProxyStyle):
def sizeFromContents(self, types, option, size, widget):
diff --git a/QProxyStyle/Lib/TabCornerStyle.py b/QProxyStyle/Lib/TabCornerStyle.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe7935402fcac4bc0253b6205ab87d4fc09e4395
--- /dev/null
+++ b/QProxyStyle/Lib/TabCornerStyle.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2021年06月23日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: TabCornerStyle
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import QRect
+ from PyQt5.QtWidgets import QProxyStyle, QStyle
+except ImportError:
+ from PySide2.QtCore import QRect
+ from PySide2.QtWidgets import QProxyStyle, QStyle
+
+
+class TabCornerStyle(QProxyStyle):
+
+ def subElementRect(self, element, option, widget):
+ try:
+ rect = super(TabCornerStyle, self).subElementRect(element, option, widget)
+ if element == QStyle.SE_TabWidgetRightCorner and rect.isValid():
+ # 标签tab的矩形范围
+ tab_rect = self.subElementRect(QStyle.SE_TabWidgetTabBar, option, widget)
+ # 内容面板的矩形范围
+ panel_rect = self.subElementRect(QStyle.SE_TabWidgetTabPane, option, widget)
+ ext_height = 2 * self.pixelMetric(QStyle.PM_TabBarBaseHeight, option, widget)
+ # 修正过后填充的矩形范围
+ cor_rect = QRect(tab_rect.x() + tab_rect.width() + ext_height, tab_rect.y() + ext_height,
+ panel_rect.width() - tab_rect.width() - 2 * ext_height,
+ tab_rect.height() - 2 * ext_height)
+ return cor_rect
+ return rect
+ except Exception as e:
+ print(e)
+ return QRect()
diff --git a/QProxyStyle/README.md b/QProxyStyle/README.md
index bf6f59812d69d1fca9f1d45a1acf44a6cbc63349..d64072bf2d4ac26e040d0e3954acbf89ab14b883 100644
--- a/QProxyStyle/README.md
+++ b/QProxyStyle/README.md
@@ -1,5 +1,9 @@
# QProxyStyle
+- 目录
+ - [QTabWidget Tab文字方向](#1qtabwidget-tab文字方向)
+ - [QTabWidget 角落控件位置](#2qtabwidget-角落控件位置)
+
## 1、QTabWidget Tab文字方向
[运行 TabTextDirection.py](TabTextDirection.py)
@@ -7,4 +11,15 @@
2. `sizeFromContents` 转置size
3. `drawControl` 绘制文字
-
\ No newline at end of file
+
+
+## 2、QTabWidget 角落控件位置
+[运行 TabCornerWidget.py](TabCornerWidget.py)
+
+1. 通过 `app.setStyle(TabCornerStyle())` 设置代理样式
+2. `setCornerWidget` 设置自定义角落控件
+
+原理是通过代理样式中对 `SE_TabWidgetRightCorner` 计算的结果进行校正,使得角落控件占满右边空白位置,
+然后再配合自定义控件中使用 `QSpacerItem` 占据右边位置使得 + 号按钮居左,表现效果为 + 号按钮跟随标签的增加和减少
+
+
\ No newline at end of file
diff --git a/QProxyStyle/ScreenShot/TabCornerStyle.png b/QProxyStyle/ScreenShot/TabCornerStyle.png
new file mode 100644
index 0000000000000000000000000000000000000000..4133f625cd29132ec3625750e75220a6d8280323
Binary files /dev/null and b/QProxyStyle/ScreenShot/TabCornerStyle.png differ
diff --git a/QProxyStyle/TabCornerWidget.py b/QProxyStyle/TabCornerWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..39d71462158256136e1a2e62e6b2f2d30cd4dbcc
--- /dev/null
+++ b/QProxyStyle/TabCornerWidget.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2021年06月23日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: TabCornerWidget
+@description:
+"""
+
+try:
+ from PyQt5.QtCore import pyqtSignal, Qt
+ from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QSpacerItem, QSizePolicy, \
+ QTabWidget
+except ImportError:
+ from PySide2.QtCore import Signal as pyqtSignal, Qt
+ from PySide2.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QSpacerItem, QSizePolicy, \
+ QTabWidget
+
+from QProxyStyle.Lib.TabCornerStyle import TabCornerStyle
+
+
+class TabCornerWidget(QWidget):
+ signalTabAdd = pyqtSignal()
+
+ def __init__(self, *args, **kwargs):
+ super(TabCornerWidget, self).__init__(*args, **kwargs)
+ layout = QHBoxLayout(self)
+ layout.setContentsMargins(0, 0, 0, 0)
+ self.buttonAdd = QPushButton('+', self, toolTip='添加新标签页', clicked=self.signalTabAdd.emit)
+ layout.addWidget(self.buttonAdd)
+ layout.addItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
+
+ def resizeEvent(self, event):
+ super(TabCornerWidget, self).resizeEvent(event)
+ # 更新按钮高度
+ if hasattr(self, 'buttonAdd'):
+ self.buttonAdd.setFixedSize(self.height(), self.height())
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ app.setStyle(TabCornerStyle())
+
+ tab1 = QTabWidget()
+ cor1 = TabCornerWidget(tab1)
+ cor1.signalTabAdd.connect(lambda: tab1.addTab(QWidget(tab1), 'tab' + str(tab1.count() + 1)))
+ tab1.setCornerWidget(cor1, Qt.TopRightCorner)
+ tab1.show()
+
+ tab2 = QTabWidget()
+ tab2.setTabPosition(QTabWidget.South) # tab 标签方向
+ cor2 = TabCornerWidget(tab2)
+ cor2.signalTabAdd.connect(lambda: tab2.addTab(QWidget(tab2), 'tab' + str(tab2.count() + 1)))
+ tab2.setCornerWidget(cor2, Qt.BottomRightCorner)
+ tab2.show()
+
+ for i in range(10):
+ tab1.addTab(QWidget(tab1), 'tab' + str(i + 1))
+ tab2.addTab(QWidget(tab1), 'tab' + str(i + 1))
+
+ sys.exit(app.exec_())
diff --git a/QProxyStyle/TabTextDirection.py b/QProxyStyle/TabTextDirection.py
index 22dc4d869180c0e8b3c42c8d2115b07cc95a3760..52ba56872a17268f11f010c1f00708b87e364861 100644
--- a/QProxyStyle/TabTextDirection.py
+++ b/QProxyStyle/TabTextDirection.py
@@ -4,7 +4,7 @@
"""
Created on 2018年12月27日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: TestTabWidget
@description:
@@ -14,13 +14,6 @@ from PyQt5.QtWidgets import QTabWidget, QLabel, QWidget, QGridLayout
from Lib.TabBarStyle import TabBarStyle
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
-
-
class TabWidget(QTabWidget):
def __init__(self, *args, **kwargs):
@@ -44,6 +37,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
app.setStyle(TabBarStyle())
w = Window()
diff --git a/QPushButton/BottomLineProgress.py b/QPushButton/BottomLineProgress.py
index 491124f90634d738ec917ce6028b6ac6f5d5f5f4..00352f5447cea143f60f17ee040964ddeaf97d5f 100644
--- a/QPushButton/BottomLineProgress.py
+++ b/QPushButton/BottomLineProgress.py
@@ -1,25 +1,26 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年2月1日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: PushButtonLine
@description:
-'''
-from random import randint
-import sys
-
-from PyQt5.QtCore import QTimer, QThread, pyqtSignal
-from PyQt5.QtGui import QPainter, QColor, QPen
-from PyQt5.QtWidgets import QPushButton, QApplication, QWidget, QVBoxLayout
+"""
+import sys
+from random import randint
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QTimer, QThread, pyqtSignal
+ from PyQt5.QtGui import QPainter, QColor, QPen
+ from PyQt5.QtWidgets import QPushButton, QApplication, QWidget, QVBoxLayout
+except ImportError:
+ from PySide2.QtCore import QTimer, QThread, Signal as pyqtSignal
+ from PySide2.QtGui import QPainter, QColor, QPen
+ from PySide2.QtWidgets import QPushButton, QApplication, QWidget, QVBoxLayout
StyleSheet = '''
PushButtonLine {
@@ -32,7 +33,6 @@ PushButtonLine {
class LoadingThread(QThread):
-
valueChanged = pyqtSignal(float) # 当前值/最大值
def __init__(self, *args, **kwargs):
@@ -41,12 +41,13 @@ class LoadingThread(QThread):
def run(self):
for i in range(self.totalValue + 1):
+ if self.isInterruptionRequested():
+ break
self.valueChanged.emit(i / self.totalValue)
- QThread.msleep(randint(300, 600))
+ QThread.msleep(randint(50, 100))
class PushButtonLine(QPushButton):
-
lineColor = QColor(0, 150, 136)
def __init__(self, *args, **kwargs):
@@ -57,6 +58,9 @@ class PushButtonLine(QPushButton):
self._timer = QTimer(self, timeout=self.update)
self.clicked.connect(self.start)
+ def __del__(self):
+ self.stop()
+
def paintEvent(self, event):
super(PushButtonLine, self).paintEvent(event)
if not self._timer.isActive():
@@ -79,13 +83,21 @@ class PushButtonLine(QPushButton):
self.setText(self._waitText)
def stop(self):
- self.loadingThread.valueChanged.disconnect(self.setPercent)
- self.loadingThread.terminate()
- self.loadingThread.deleteLater()
- del self.loadingThread
- self._percent = 0
- self._timer.stop()
- self.setText(self._text)
+ try:
+ if hasattr(self, "loadingThread"):
+ if self.loadingThread.isRunning():
+ self.loadingThread.requestInterruption()
+ self.loadingThread.quit()
+ self.loadingThread.wait(2000)
+ del self.loadingThread
+ except RuntimeError:
+ pass
+ try:
+ self._percent = 0
+ self._timer.stop()
+ self.setText(self._text)
+ except RuntimeError:
+ pass
def setPercent(self, v):
self._percent = v
diff --git a/QPushButton/FontRotate.py b/QPushButton/FontRotate.py
index c9cae277512dc9c4fabfe6d322ff9aac41da5c62..e8a728a2c7594793bb8ce6a8b3a8cd409a3266e8 100644
--- a/QPushButton/FontRotate.py
+++ b/QPushButton/FontRotate.py
@@ -1,31 +1,30 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年2月1日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: PushButtonFont
@description:
-'''
-
+"""
import sys
-from PyQt5.QtCore import QPropertyAnimation, Qt, QRectF
-from PyQt5.QtGui import QFontDatabase
-from PyQt5.QtWidgets import QPushButton, QApplication, QStyleOptionButton,\
- QStylePainter, QStyle
-
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QPropertyAnimation, Qt, QRectF
+ from PyQt5.QtGui import QFontDatabase
+ from PyQt5.QtWidgets import QPushButton, QApplication, QStyleOptionButton, \
+ QStylePainter, QStyle
+except ImportError:
+ from PySide2.QtCore import QPropertyAnimation, Qt, QRectF
+ from PySide2.QtGui import QFontDatabase
+ from PySide2.QtWidgets import QPushButton, QApplication, QStyleOptionButton, \
+ QStylePainter, QStyle
class PushButtonFont(QPushButton):
-
LoadingText = "\uf110"
def __init__(self, *args, **kwargs):
diff --git a/QPushButton/NormalStyle.py b/QPushButton/NormalStyle.py
index 831064b48110ce9caa5fc0a9310ee26ba8dca2be..ec36a89fbdea7e2b88eb127f0494ba81e0cd6817 100644
--- a/QPushButton/NormalStyle.py
+++ b/QPushButton/NormalStyle.py
@@ -1,23 +1,23 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月29日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: NormalStyle
@description:
-'''
-import sys
-from PyQt5.QtWidgets import QWidget, QHBoxLayout, QPushButton, QApplication
+"""
+import sys
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtWidgets import QWidget, QHBoxLayout, QPushButton, QApplication
+except ImportError:
+ from PySide2.QtWidgets import QWidget, QHBoxLayout, QPushButton, QApplication
-StyleSheet = '''
+StyleSheet = """
/*这里是通用设置,所有按钮都有效,不过后面的可以覆盖这个*/
QPushButton {
border: none; /*去掉边框*/
@@ -85,7 +85,7 @@ QPushButton[text="purple button"] {
color: white; /*文字颜色*/
background-color: #9c27b0;
}
-'''
+"""
class Window(QWidget):
diff --git a/QPushButton/README.md b/QPushButton/README.md
index 0697824e8aabaaac501acefffbab737a8841bb03..2328a51eacd564e9a4aa7007952d90a205e66e7e 100644
--- a/QPushButton/README.md
+++ b/QPushButton/README.md
@@ -1,5 +1,11 @@
# QPushButton
+- 目录
+ - [普通样式](#1普通样式)
+ - [按钮底部线条进度](#2按钮底部线条进度)
+ - [按钮文字旋转进度](#3按钮文字旋转进度)
+ - [按钮常用信号](#4按钮常用信号)
+
## 1、普通样式
[运行 NormalStyle.py](NormalStyle.py)
@@ -19,4 +25,12 @@
利用字体,使用FontAwesome字体来显示一个圆形进度条,然后利用旋转动画
-
\ No newline at end of file
+
+
+## 4、按钮常用信号
+[运行 SignalsExample.py](SignalsExample.py)
+
+根据官网文档 https://doc.qt.io/qt-5/qabstractbutton.html#signals 中的信号介绍编写
+按钮的点击、按下、释放、选中信号演示
+
+
\ No newline at end of file
diff --git a/QPushButton/ScreenShot/SignalsExample.gif b/QPushButton/ScreenShot/SignalsExample.gif
new file mode 100644
index 0000000000000000000000000000000000000000..49420031b01fbfb2af5a926c8bfcfa342d63d533
Binary files /dev/null and b/QPushButton/ScreenShot/SignalsExample.gif differ
diff --git a/QPushButton/SignalsExample.py b/QPushButton/SignalsExample.py
new file mode 100644
index 0000000000000000000000000000000000000000..35163e814e3e07a58a406e0da7cd6279a9961da1
--- /dev/null
+++ b/QPushButton/SignalsExample.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年7月2日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QPushButton.SignalsExample
+@description: 按钮信号例子
+"""
+
+try:
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QPlainTextEdit
+except ImportError:
+ from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QPlainTextEdit
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+
+ btn1 = QPushButton('按钮点击信号', self)
+ btn1.setObjectName('ClickBtn')
+ btn1.clicked.connect(self.onClicked)
+ layout.addWidget(btn1)
+
+ btn2 = QPushButton('按钮按下信号', self)
+ btn2.setObjectName('PressBtn')
+ btn2.pressed.connect(self.onPressed)
+ layout.addWidget(btn2)
+
+ btn3 = QPushButton('按钮释放信号', self)
+ btn3.setObjectName('ReleaseBtn')
+ btn3.released.connect(self.onReleased)
+ layout.addWidget(btn3)
+
+ btn4 = QPushButton('按钮释放信号', self)
+ btn4.setObjectName('ToggleBtn')
+ btn4.setCheckable(True)
+ btn4.toggled.connect(self.onToggled)
+ layout.addWidget(btn4)
+
+ self.resultView = QPlainTextEdit(self)
+ self.resultView.setReadOnly(True)
+ layout.addWidget(self.resultView)
+
+ def onClicked(self):
+ self.resultView.appendPlainText(
+ '按钮{0}被点击'.format(self.sender().objectName()))
+
+ def onPressed(self):
+ self.resultView.appendPlainText(
+ '按钮{0}被按下'.format(self.sender().objectName()))
+
+ def onReleased(self):
+ self.resultView.appendPlainText(
+ '按钮{0}被释放'.format(self.sender().objectName()))
+
+ def onToggled(self, checked):
+ self.resultView.appendPlainText(
+ '按钮{0}被选中:{1}'.format(self.sender().objectName(), checked))
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QScrollArea/Lib/SettingUi.py b/QScrollArea/Lib/SettingUi.py
index a9bfdffc653bc1922cc3a9e416bff3a1d128a228..d25f3ef2a9e6561a16c99f934e82373ea6da9805 100644
--- a/QScrollArea/Lib/SettingUi.py
+++ b/QScrollArea/Lib/SettingUi.py
@@ -6,7 +6,11 @@
#
# WARNING! All changes made in this file will be lost!
-from PyQt5 import QtCore, QtGui, QtWidgets
+try:
+ from PyQt5 import QtCore, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtWidgets
+
class Ui_Setting(object):
def setupUi(self, Setting):
@@ -151,7 +155,8 @@ class Ui_Setting(object):
self.comboBox.addItem("")
self.comboBox.addItem("")
self.horizontalLayout_2.addWidget(self.comboBox)
- spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding,
+ QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem)
self.formLayout_9.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_2)
self.checkBox_30 = QtWidgets.QCheckBox(self.widget_2)
@@ -171,7 +176,8 @@ class Ui_Setting(object):
self.pushButton_4 = QtWidgets.QPushButton(self.widget_2)
self.pushButton_4.setObjectName("pushButton_4")
self.horizontalLayout_3.addWidget(self.pushButton_4)
- spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding,
+ QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem1)
self.formLayout_9.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3)
self.verticalLayout.addWidget(self.widget_2)
@@ -469,7 +475,8 @@ class Ui_Setting(object):
self.checkBox_23.setText(_translate("Setting", "启用一声问候消息"))
self.checkBox_24.setText(_translate("Setting", "启用设备连接提醒"))
self.right5.setText(_translate("Setting", "当插入安卓设备时,提示安装或者更新QQ手机版"))
- self.label_3.setText(_translate("Setting", " 您可以设置是否在屏幕右下角收到来自QQ空间的通知,进入设置 。
"))
+ self.label_3.setText(_translate("Setting",
+ " 您可以设置是否在屏幕右下角收到来自QQ空间的通知,进入设置 。
"))
self.label_4.setText(_translate("Setting", "好友上线提醒"))
self.radioButton.setText(_translate("Setting", "关闭好友上线提醒"))
self.radioButton_2.setText(_translate("Setting", "全部好友上线提醒"))
@@ -485,10 +492,10 @@ class Ui_Setting(object):
if __name__ == "__main__":
import sys
+
app = QtWidgets.QApplication(sys.argv)
Setting = QtWidgets.QWidget()
ui = Ui_Setting()
ui.setupUi(Setting)
Setting.show()
sys.exit(app.exec_())
-
diff --git a/QScrollArea/QQSettingPanel.py b/QScrollArea/QQSettingPanel.py
index 6dccd7c00044f1b84b082d5183f85745bff8c4da..2a5236aa32acf0370b9e340988b61e545aaddead 100644
--- a/QScrollArea/QQSettingPanel.py
+++ b/QScrollArea/QQSettingPanel.py
@@ -1,22 +1,21 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from PyQt5.QtWidgets import QWidget
-
-from Lib.SettingUi import Ui_Setting # @UnresolvedImport
+"""
+Created on 2018年3月28日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QQSettingPanel
+@description:
+"""
-# Created on 2018年3月28日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: QQSettingPanel
-# description:
+try:
+ from PyQt5.QtWidgets import QApplication, QWidget
+except ImportError:
+ from PySide2.QtWidgets import QApplication, QWidget
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+from Lib.SettingUi import Ui_Setting # @UnresolvedImport
class Window(QWidget, Ui_Setting):
@@ -59,9 +58,9 @@ class Window(QWidget, Ui_Setting):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
- app.setStyleSheet(open("Data/style.qss", "rb").read().decode("utf-8"))
+ app.setStyleSheet(open('Data/style.qss', 'rb').read().decode('utf-8'))
w = Window()
w.show()
sys.exit(app.exec_())
diff --git a/QScrollArea/README.md b/QScrollArea/README.md
index 7591a342d23ebcb016e35dca7726768074be63b1..810730955f529f1300705a54ace8c9b1b5bccd81 100644
--- a/QScrollArea/README.md
+++ b/QScrollArea/README.md
@@ -1,7 +1,10 @@
# QScrollArea
+- 目录
+ - [仿QQ设置面板](#1仿QQ设置面板)
+
## 1、仿QQ设置面板
-[运行 QQSettingPanel.py](QQSettingPanel.py)
+[运行 QQSettingPanel.py](QQSettingPanel.py) | [查看 setting.ui](Data/setting.ui)
1. 左侧为`QListWidget`,右侧使用`QScrollArea`设置`QVBoxLayout`,然后依次往里面添加QWidget
2. 右侧添加`QWidget`的时候有两种方案
diff --git a/QScrollBar/README.md b/QScrollBar/README.md
index 190ff2a809426abe65c95f05434b668ad6d0e35b..1de1b8b0148fb6016f93a71520d186c00496717b 100644
--- a/QScrollBar/README.md
+++ b/QScrollBar/README.md
@@ -1,6 +1,10 @@
# QScrollBar
+- 目录
+ - [滚动条样式美化](#1滚动条样式美化)
+
## 1、滚动条样式美化
+[运行 StyleScrollBar.py](StyleScrollBar.py)
使用QSS和图片对滚动条进行美化(horizontal 横向、vertical 纵向)
diff --git a/QScrollBar/StyleScrollBar.py b/QScrollBar/StyleScrollBar.py
index 96c591d201d7f29b94b55126609af3157f4e3a6c..f5d4d347f30723158b01d371a378d1728640fd71 100644
--- a/QScrollBar/StyleScrollBar.py
+++ b/QScrollBar/StyleScrollBar.py
@@ -1,22 +1,22 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月20日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ScrollBar
@description:
-'''
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QTextEdit, QApplication
+"""
import chardet
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtWidgets import QTextEdit, QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QTextEdit, QApplication
class Window(QTextEdit):
@@ -38,6 +38,7 @@ class Window(QTextEdit):
if __name__ == "__main__":
import sys
+
app = QApplication(sys.argv)
app.setApplicationName("滚动条样式")
app.setApplicationDisplayName("滚动条样式")
diff --git a/QSerialPort/Lib/UiSerialPort.py b/QSerialPort/Lib/UiSerialPort.py
index 0da8c3512e604936a945aecc477da3b242461d6b..934e2156c1e9d5d1d7ded463b717bf1ba65965a5 100644
--- a/QSerialPort/Lib/UiSerialPort.py
+++ b/QSerialPort/Lib/UiSerialPort.py
@@ -6,19 +6,20 @@
#
# WARNING! All changes made in this file will be lost!
-from PyQt5 import QtCore, QtGui, QtWidgets
+from PyQt5 import QtCore, QtWidgets
+
class Ui_FormSerialPort(object):
def setupUi(self, FormSerialPort):
FormSerialPort.setObjectName("FormSerialPort")
FormSerialPort.resize(721, 597)
FormSerialPort.setStyleSheet("#labelStatus {\n"
-" border-radius: 13px;\n"
-" background-color: gray;\n"
-"}\n"
-"#labelStatus[isOn=\"true\"] {\n"
-" background-color: green;\n"
-"}")
+ " border-radius: 13px;\n"
+ " background-color: gray;\n"
+ "}\n"
+ "#labelStatus[isOn=\"true\"] {\n"
+ " background-color: green;\n"
+ "}")
self.gridLayout = QtWidgets.QGridLayout(FormSerialPort)
self.gridLayout.setObjectName("gridLayout")
self.groupBox = QtWidgets.QGroupBox(FormSerialPort)
@@ -86,7 +87,8 @@ class Ui_FormSerialPort(object):
self.labelStatus.setProperty("isOn", False)
self.labelStatus.setObjectName("labelStatus")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.labelStatus)
- spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum,
+ QtWidgets.QSizePolicy.Expanding)
self.formLayout.setItem(6, QtWidgets.QFormLayout.FieldRole, spacerItem)
self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1)
self.textBrowser = QtWidgets.QTextBrowser(FormSerialPort)
@@ -166,10 +168,10 @@ class Ui_FormSerialPort(object):
if __name__ == "__main__":
import sys
+
app = QtWidgets.QApplication(sys.argv)
FormSerialPort = QtWidgets.QWidget()
ui = Ui_FormSerialPort()
ui.setupUi(FormSerialPort)
FormSerialPort.show()
sys.exit(app.exec_())
-
diff --git a/QSerialPort/README.md b/QSerialPort/README.md
index 6e97a66f141772cc25f74c196acfd57e3019efbe..a0418b793119fbcb12978701921ae9cbe8ed0f71 100644
--- a/QSerialPort/README.md
+++ b/QSerialPort/README.md
@@ -1,7 +1,10 @@
-#
+# QSerialPort
+
+- 目录
+ - [串口调试小助手](#1串口调试小助手)
## 1、串口调试小助手
-[运行 SerialDebugAssistant.py](SerialDebugAssistant.py)
+[运行 SerialDebugAssistant.py](SerialDebugAssistant.py) | [查看 UiSerialPort.ui](Data/UiSerialPort.ui)
用`QSerialPort`写了个类似串口调试小助手的工具, 这个类的官方资料: http://doc.qt.io/qt-5/qserialport.html
diff --git a/QSerialPort/SerialDebugAssistant.py b/QSerialPort/SerialDebugAssistant.py
index 0f87638f79eb085fd6e7ec8ea7d8e3d6ea2677bb..5f5c8e415300aa687d74f0f04db2a576bae3906e 100644
--- a/QSerialPort/SerialDebugAssistant.py
+++ b/QSerialPort/SerialDebugAssistant.py
@@ -4,7 +4,7 @@
"""
Created on 2018年11月6日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: SerialDebugAssistant
@description: 串口调试小助手
@@ -16,13 +16,6 @@ from PyQt5.QtWidgets import QWidget, QMessageBox
from Lib.UiSerialPort import Ui_FormSerialPort # @UnresolvedImport
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
-
-
class Window(QWidget, Ui_FormSerialPort):
def __init__(self, *args, **kwargs):
@@ -51,7 +44,7 @@ class Window(QWidget, Ui_FormSerialPort):
QMessageBox.critical(self, '错误', '没有选择串口')
return
port = self._ports[name]
-# self._serial.setPort(port)
+ # self._serial.setPort(port)
# 根据名字设置串口(也可以用上面的函数)
self._serial.setPortName(port.systemLocation())
# 设置波特率
@@ -139,8 +132,10 @@ class Window(QWidget, Ui_FormSerialPort):
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.enable(1, None, 5, '')
+
+ cgitb.enable(format='text')
from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QSlider/ClickJumpSlider.py b/QSlider/ClickJumpSlider.py
index 3e7f00a330ecd8dbe50f884807e51cf640c7975d..d12f97a55117f4586a0dd0f9d2eb5a1b9dc0b29d 100644
--- a/QSlider/ClickJumpSlider.py
+++ b/QSlider/ClickJumpSlider.py
@@ -4,21 +4,20 @@
"""
Created on 2018年11月5日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ClickJumpSlider
@description:
"""
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QSlider, QStyleOptionSlider, QStyle, QWidget,\
- QFormLayout, QLabel
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtWidgets import QApplication, QSlider, QStyleOptionSlider, QStyle, QWidget, QFormLayout, \
+ QLabel
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QApplication, QSlider, QStyleOptionSlider, QStyle, QWidget, QFormLayout, \
+ QLabel
class ClickJumpSlider(QSlider):
@@ -55,31 +54,34 @@ class DemoWindow(QWidget):
layout = QFormLayout(self)
self.label1 = QLabel('0', self)
- layout.addRow(self.label1, ClickJumpSlider(
- Qt.Horizontal, valueChanged=lambda v: self.label1.setText(str(v))))
+ self.slider1 = ClickJumpSlider(Qt.Horizontal)
+ self.slider1.valueChanged.connect(lambda v: self.label1.setText(str(v)))
+ layout.addRow(self.label1, self.slider1)
# 横向-反向显示
self.label2 = QLabel('0', self)
- layout.addRow(self.label2, ClickJumpSlider(
- Qt.Horizontal, invertedAppearance=True,
- valueChanged=lambda v: self.label2.setText(str(v))))
+ self.slider2 = ClickJumpSlider(Qt.Horizontal, invertedAppearance=True)
+ self.slider2.valueChanged.connect(lambda v: self.label2.setText(str(v)))
+ layout.addRow(self.label2, self.slider2)
self.label3 = QLabel('0', self)
- layout.addRow(self.label3, ClickJumpSlider(
- Qt.Vertical, minimumHeight=200, valueChanged=lambda v: self.label3.setText(str(v))))
+ self.slider3 = ClickJumpSlider(Qt.Vertical, minimumHeight=200)
+ self.slider3.valueChanged.connect(lambda v: self.label3.setText(str(v)))
+ layout.addRow(self.label3, self.slider3)
# 纵向反向显示
self.label4 = QLabel('0', self)
- layout.addRow(self.label4, ClickJumpSlider(
- Qt.Vertical, invertedAppearance=True,
- minimumHeight=200, valueChanged=lambda v: self.label4.setText(str(v))))
+ self.slider4 = ClickJumpSlider(Qt.Vertical, invertedAppearance=True, minimumHeight=200)
+ self.slider4.valueChanged.connect(lambda v: self.label4.setText(str(v)))
+ layout.addRow(self.label4, self.slider4)
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.enable(1, None, 5, '')
- from PyQt5.QtWidgets import QApplication
+
+ cgitb.enable(format='text')
+
app = QApplication(sys.argv)
w = DemoWindow()
w.show()
diff --git a/QSlider/LfSlider.py b/QSlider/LfSlider.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c900cfa9d29598fbd1ef0a76e738d1d1039a27b
--- /dev/null
+++ b/QSlider/LfSlider.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2021/4/9
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: LfSlider
+@description: 降低值变化频率
+"""
+from datetime import datetime
+
+try:
+ from PyQt5.QtCore import pyqtSignal, QTimer, Qt
+ from PyQt5.QtWidgets import QApplication, QSlider, QWidget, QVBoxLayout, QPlainTextEdit, QHBoxLayout, \
+ QGroupBox
+except ImportError:
+ from PySide2.QtCore import Signal as pyqtSignal, QTimer, Qt
+ from PySide2.QtWidgets import QApplication, QSlider, QWidget, QVBoxLayout, QPlainTextEdit, QHBoxLayout, \
+ QGroupBox
+
+
+class LfSlider(QSlider):
+ valueChanged = pyqtSignal(int)
+
+ def __init__(self, *args, **kwargs):
+ delay = kwargs.pop('delay', 500)
+ super(LfSlider, self).__init__(*args, **kwargs)
+ self.lastValue = self.value()
+ self.uTimer = QTimer(self)
+ self.uTimer.timeout.connect(self.onValueChanged)
+ self.uTimer.start(delay)
+
+ def onValueChanged(self):
+ if self.lastValue != self.value():
+ self.lastValue = self.value()
+ self.valueChanged.emit(self.lastValue)
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QHBoxLayout(self)
+
+ # 左侧原始
+ left_group = QGroupBox('原始QSlider', self)
+ left_layout = QVBoxLayout(left_group)
+ self.leftLabel = QPlainTextEdit(self)
+ left_layout.addWidget(self.leftLabel)
+
+ self.leftSlider = QSlider(Qt.Horizontal, self)
+ self.leftSlider.valueChanged.connect(self.onLeftChanged)
+ left_layout.addWidget(self.leftSlider)
+
+ layout.addWidget(left_group)
+
+ # 右侧低频率变化
+ right_group = QGroupBox('LfSlider', self)
+ right_layout = QVBoxLayout(right_group)
+ self.rightLabel = QPlainTextEdit(self)
+ right_layout.addWidget(self.rightLabel)
+
+ self.rightSlider = LfSlider(Qt.Horizontal, self)
+ self.rightSlider.valueChanged.connect(self.onRightChanged)
+ right_layout.addWidget(self.rightSlider)
+
+ layout.addWidget(right_group)
+
+ def onLeftChanged(self, value):
+ self.leftLabel.appendPlainText(datetime.now().strftime("[%H:%M:%S.%f] ") + str(value))
+
+ def onRightChanged(self, value):
+ self.rightLabel.appendPlainText(datetime.now().strftime("[%H:%M:%S.%f] ") + str(value))
+
+
+if __name__ == '__main__':
+ import sys
+ import cgitb
+
+ cgitb.enable(format='text')
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QSlider/PaintQSlider.py b/QSlider/PaintQSlider.py
index 0d154e8b51faf7c751ea2028b53c3894e8951682..d72a757484c96cb1f46a3a88a0e1f94a2790bdd0 100644
--- a/QSlider/PaintQSlider.py
+++ b/QSlider/PaintQSlider.py
@@ -4,24 +4,18 @@
"""
Created on 2018年5月15日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: PaintQSlider
@description:
"""
+
from PyQt5.QtCore import Qt, QRect, QPointF
from PyQt5.QtGui import QPainter, QColor
-from PyQt5.QtWidgets import QSlider, QWidget, QVBoxLayout, QProxyStyle, QStyle,\
+from PyQt5.QtWidgets import QSlider, QWidget, QVBoxLayout, QProxyStyle, QStyle, \
QStyleOptionSlider
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
-
-
class SliderStyle(QProxyStyle):
def subControlRect(self, control, option, subControl, widget=None):
@@ -124,6 +118,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.setStyleSheet('QWidget {background: gray;}')
diff --git a/QSlider/QssQSlider.py b/QSlider/QssQSlider.py
index af65e4916134998d9a770621b3b3a94a7fc34576..7b3a0f33958701edcdeee33630de29b3f1dce3c4 100644
--- a/QSlider/QssQSlider.py
+++ b/QSlider/QssQSlider.py
@@ -4,20 +4,18 @@
"""
Created on 2018年5月15日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: QssQSlider
@description: 通过QSS美化QSlider
"""
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QSlider
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QSlider
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QSlider
StyleSheet = """
QWidget {
@@ -76,7 +74,7 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
app.setStyleSheet(StyleSheet)
w = Window()
diff --git a/QSlider/README.md b/QSlider/README.md
index b03d8a0c32993cbd87ddb1c91b414659c75406b3..1dc8803a37d8fabb70ffe0cdecfc7b513b8486e8 100644
--- a/QSlider/README.md
+++ b/QSlider/README.md
@@ -1,5 +1,10 @@
# QSlider
+- 目录
+ - [滑动条点击定位](#1滑动条点击定位)
+ - [双层圆环样式](#2双层圆环样式)
+ - [低频率值变化](#3低频率值变化)
+
## 1、滑动条点击定位
[运行 ClickJumpSlider.py](ClickJumpSlider.py)
@@ -39,4 +44,11 @@ def mousePressEvent(self, event):
[运行 QssQSlider.py](QssQSlider.py) | [运行 PaintQSlider.py](PaintQSlider.py)

-
\ No newline at end of file
+
+
+## 3、低频率值变化
+[运行 LfSlider.py](LfSlider.py)
+
+覆盖了`valueChanged`信号,通过使用定时器来延迟发送值变化,如果无法覆盖信号则可以自定义一个新的信号
+
+
diff --git a/QSlider/ScreenShot/LfSlider.gif b/QSlider/ScreenShot/LfSlider.gif
new file mode 100644
index 0000000000000000000000000000000000000000..035f31184212d02da3b6be6ebaef6f7148be9772
Binary files /dev/null and b/QSlider/ScreenShot/LfSlider.gif differ
diff --git a/QSplashScreen/Data/splash.gif b/QSplashScreen/Data/splash.gif
new file mode 100644
index 0000000000000000000000000000000000000000..8553388e94d3a15dc09b0f1390d7b02006d2d1a8
Binary files /dev/null and b/QSplashScreen/Data/splash.gif differ
diff --git a/QSplashScreen/GifSplashScreen.py b/QSplashScreen/GifSplashScreen.py
new file mode 100644
index 0000000000000000000000000000000000000000..d409dd04fac34a5817dc1a13348980d67777568c
--- /dev/null
+++ b/QSplashScreen/GifSplashScreen.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020/6/11
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file:
+@description:
+"""
+
+from time import sleep
+
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QMovie
+ from PyQt5.QtWidgets import QApplication, QSplashScreen, QWidget
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QMovie
+ from PySide2.QtWidgets import QApplication, QSplashScreen, QWidget
+
+
+class GifSplashScreen(QSplashScreen):
+
+ def __init__(self, *args, **kwargs):
+ super(GifSplashScreen, self).__init__(*args, **kwargs)
+ self.movie = QMovie('Data/splash.gif')
+ self.movie.frameChanged.connect(self.onFrameChanged)
+ self.movie.start()
+
+ def onFrameChanged(self, _):
+ self.setPixmap(self.movie.currentPixmap())
+
+ def finish(self, widget):
+ self.movie.stop()
+ super(GifSplashScreen, self).finish(widget)
+
+
+class BusyWindow(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(BusyWindow, self).__init__(*args, **kwargs)
+ # 模拟耗时操作,一般来说耗时的加载数据应该放到线程
+ for i in range(5):
+ sleep(1)
+ splash.showMessage('加载进度: %d' % i, Qt.AlignHCenter | Qt.AlignBottom, Qt.white)
+ QApplication.instance().processEvents()
+
+ splash.showMessage('初始化完成', Qt.AlignHCenter | Qt.AlignBottom, Qt.white)
+ splash.finish(self)
+
+
+if __name__ == '__main__':
+ import sys
+ import cgitb
+
+ cgitb.enable(format='text')
+
+ app = QApplication(sys.argv)
+
+ global splash
+ splash = GifSplashScreen()
+ splash.show()
+
+ w = BusyWindow()
+ w.show()
+
+ # 测试二
+ # def createWindow():
+ # app.w = QWidget()
+ # # 模拟初始5秒后再显示
+ # splash.showMessage('等待界面显示', Qt.AlignHCenter | Qt.AlignBottom, Qt.white)
+ # QTimer.singleShot(3000, lambda: (
+ # splash.showMessage('初始化完成', Qt.AlignHCenter | Qt.AlignBottom, Qt.white), app.w.show(),
+ # splash.finish(app.w)))
+
+ # 模拟耗时5秒。但是不能用sleep
+ # 可以使用子线程加载耗时的数据
+ # 主线程中循环设置UI可以配合QApplication.instance().processEvents()
+ # QTimer.singleShot(3000, createWindow)
+
+ splash.showMessage('等待创建界面', Qt.AlignHCenter | Qt.AlignBottom, Qt.white)
+
+ sys.exit(app.exec_())
diff --git a/QSplashScreen/README.en.md b/QSplashScreen/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QSplashScreen/README.md b/QSplashScreen/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..51da420e76ac818e4576db305fa77707eca745b4
--- /dev/null
+++ b/QSplashScreen/README.md
@@ -0,0 +1,11 @@
+# QSplashScreen
+
+- 目录
+ - [启动画面动画](#1启动画面动画)
+
+## 1、启动画面动画
+[运行 GifSplashScreen.py](GifSplashScreen.py)
+
+结合 `QMovie` 的 `frameChanged` 信号 不停地设置新的pixmap图片
+
+
\ No newline at end of file
diff --git a/QSplashScreen/ScreenShot/GifSplashScreen.gif b/QSplashScreen/ScreenShot/GifSplashScreen.gif
new file mode 100644
index 0000000000000000000000000000000000000000..0c22635058ffac4df5a38e558df1b72463866744
Binary files /dev/null and b/QSplashScreen/ScreenShot/GifSplashScreen.gif differ
diff --git a/QSplitter/README.en.md b/QSplitter/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QSplitter/README.md b/QSplitter/README.md
index b19695b2476502d7c02a7da19bc31ab9723d3964..f52a7ca3bfca45797d4b9636e2e7c2031ab1fa4b 100644
--- a/QSplitter/README.md
+++ b/QSplitter/README.md
@@ -1,5 +1,8 @@
# QSplitter
+- 目录
+ - [分割窗口的分割条重绘](#1分割窗口的分割条重绘)
+
## 1、分割窗口的分割条重绘
[运行 RewriteHandle.py](RewriteHandle.py)
diff --git a/QSplitter/RewriteHandle.py b/QSplitter/RewriteHandle.py
index 241f6c28e01bb5a12d977b57027980c499c0692f..d69ab630d92d080ee250c3531a5f3a11b0c57a04 100644
--- a/QSplitter/RewriteHandle.py
+++ b/QSplitter/RewriteHandle.py
@@ -4,25 +4,23 @@
"""
Created on 2018年3月21日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: Splitter
@description:
"""
import sys
-from PyQt5.QtCore import Qt, QPointF, pyqtSignal
-from PyQt5.QtGui import QPainter, QPolygonF
-from PyQt5.QtWidgets import QTextEdit, QListWidget,\
- QTreeWidget, QSplitter, QApplication, QMainWindow, QSplitterHandle
-
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"\
-
+try:
+ from PyQt5.QtCore import Qt, QPointF, pyqtSignal
+ from PyQt5.QtGui import QPainter, QPolygonF
+ from PyQt5.QtWidgets import QTextEdit, QListWidget, \
+ QTreeWidget, QSplitter, QApplication, QMainWindow, QSplitterHandle
+except ImportError:
+ from PySide2.QtCore import Qt, QPointF, Signal as pyqtSignal
+ from PySide2.QtGui import QPainter, QPolygonF
+ from PySide2.QtWidgets import QTextEdit, QListWidget, \
+ QTreeWidget, QSplitter, QApplication, QMainWindow, QSplitterHandle
class SplitterHandle(QSplitterHandle):
@@ -49,7 +47,7 @@ class SplitterHandle(QSplitterHandle):
else:
# 设置默认的鼠标样式并可以移动
self.setCursor(Qt.SplitHCursor if self.orientation()
- == Qt.Horizontal else Qt.SplitVCursor)
+ == Qt.Horizontal else Qt.SplitVCursor)
super(SplitterHandle, self).mouseMoveEvent(event)
def paintEvent(self, event):
diff --git a/QStackedWidget/LeftTabStacked.py b/QStackedWidget/LeftTabStacked.py
index 298b8494319e762bb4e04508a1214b6e5c2559c9..6c8257d06ed7a63fad4b0ac61a56b9b8dd12875c 100644
--- a/QStackedWidget/LeftTabStacked.py
+++ b/QStackedWidget/LeftTabStacked.py
@@ -1,24 +1,27 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from random import randint
-from PyQt5.QtCore import Qt, QSize
-from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QWidget, QListWidget, QStackedWidget, QHBoxLayout,\
- QListWidgetItem, QLabel
+"""
+Created on 2018年5月29日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: LeftTabWidget
+@description:
+"""
+from random import randint
-# Created on 2018年5月29日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: LeftTabWidget
-# description:
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import Qt, QSize
+ from PyQt5.QtGui import QIcon
+ from PyQt5.QtWidgets import QApplication, QWidget, QListWidget, QStackedWidget, QHBoxLayout, \
+ QListWidgetItem, QLabel
+except ImportError:
+ from PySide2.QtCore import Qt, QSize
+ from PySide2.QtGui import QIcon
+ from PySide2.QtWidgets import QApplication, QWidget, QListWidget, QStackedWidget, QHBoxLayout, \
+ QListWidgetItem, QLabel
class LeftTabWidget(QWidget):
@@ -26,7 +29,7 @@ class LeftTabWidget(QWidget):
def __init__(self, *args, **kwargs):
super(LeftTabWidget, self).__init__(*args, **kwargs)
self.resize(800, 600)
- #左右布局(左边一个QListWidget + 右边QStackedWidget)
+ # 左右布局(左边一个QListWidget + 右边QStackedWidget)
layout = QHBoxLayout(self, spacing=0)
layout.setContentsMargins(0, 0, 0, 0)
# 左侧列表
@@ -102,7 +105,7 @@ QLabel {
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplicationr
+
app = QApplication(sys.argv)
app.setStyleSheet(Stylesheet)
w = LeftTabWidget()
diff --git a/QStackedWidget/README.md b/QStackedWidget/README.md
index 0ad655eac70f4bd5ffd09c269e100f6927b0e589..2329fc3ed460075577e8d04f63039c1118b460b8 100644
--- a/QStackedWidget/README.md
+++ b/QStackedWidget/README.md
@@ -1,5 +1,8 @@
# QStackedWidget
+- 目录
+ - [左侧选项卡](#1左侧选项卡)
+
## 1、左侧选项卡
[运行 LeftTabStacked.py](LeftTabStacked.py)
@@ -11,5 +14,7 @@
2. 右侧添加`QWidget`的时候有两种方案
1. 左侧list根据序号来索引,右侧添加widget时给定带序号的变量名,如widget_0,widget_1,widget_2之类的,这样可以直接根据`QListWidget`的序号关联起来
2. 左侧list添加item时给定右侧对应的widget变量值
+
+PS: 用设计设的做法 : https://www.jianshu.com/p/dac62b5c225c
-
\ No newline at end of file
+
diff --git a/QSystemTrayIcon/MinimizeToTray.py b/QSystemTrayIcon/MinimizeToTray.py
new file mode 100755
index 0000000000000000000000000000000000000000..f4325838511199f7a4057b5d834d182ff64eecc4
--- /dev/null
+++ b/QSystemTrayIcon/MinimizeToTray.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+
+try:
+ from PyQt5.QtCore import QSize
+ from PyQt5.QtWidgets import (
+ QApplication, QMainWindow,
+ QLabel, QGridLayout, QWidget,
+ QCheckBox, QSystemTrayIcon,
+ QSpacerItem, QSizePolicy, QMenu, QAction, QStyle)
+except ImportError:
+ from PySide2.QtCore import QSize
+ from PySide2.QtWidgets import (
+ QApplication, QMainWindow,
+ QLabel, QGridLayout, QWidget,
+ QCheckBox, QSystemTrayIcon,
+ QSpacerItem, QSizePolicy, QMenu, QAction, QStyle)
+
+
+class MainWindow(QMainWindow):
+ """
+ Сheckbox and system tray icons.
+ Will initialize in the constructor.
+ """
+ check_box = None
+ tray_icon = None
+
+ # Override the class constructor
+ def __init__(self):
+ # Be sure to call the super class method
+ QMainWindow.__init__(self)
+
+ self.setMinimumSize(QSize(480, 80)) # Set sizes
+ self.setWindowTitle("System Tray Application") # Set a title
+ # Create a central widget
+ central_widget = QWidget(self)
+ # Set the central widget
+ self.setCentralWidget(central_widget)
+
+ grid_layout = QGridLayout(self) # Create a QGridLayout
+ # Set the layout into the central widget
+ central_widget.setLayout(grid_layout)
+ grid_layout.addWidget(
+ QLabel("Application, which can minimize to Tray", self), 0, 0)
+
+ # Add a checkbox, which will depend on the behavior of the program when the window is closed
+ self.check_box = QCheckBox('Minimize to Tray')
+ grid_layout.addWidget(self.check_box, 1, 0)
+ grid_layout.addItem(QSpacerItem(
+ 0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding), 2, 0)
+
+ # Init QSystemTrayIcon
+ self.tray_icon = QSystemTrayIcon(self)
+ self.tray_icon.setIcon(
+ self.style().standardIcon(QStyle.SP_ComputerIcon))
+
+ '''
+ Define and add steps to work with the system tray icon
+ show - show window
+ hide - hide window
+ exit - exit from application
+ '''
+ show_action = QAction("Show", self)
+ quit_action = QAction("Exit", self)
+ hide_action = QAction("Hide", self)
+ show_action.triggered.connect(self.show)
+ hide_action.triggered.connect(self.hide)
+ quit_action.triggered.connect(QApplication.instance().quit)
+ tray_menu = QMenu()
+ tray_menu.addAction(show_action)
+ tray_menu.addAction(hide_action)
+ tray_menu.addAction(quit_action)
+ self.tray_icon.setContextMenu(tray_menu)
+ self.tray_icon.show()
+
+ # Override closeEvent, to intercept the window closing event
+ # The window will be closed only if there is no check mark in the check box
+ def closeEvent(self, event):
+ if self.check_box.isChecked():
+ event.ignore()
+ self.hide()
+ self.tray_icon.showMessage(
+ "Tray Program",
+ "Application was minimized to Tray",
+ QSystemTrayIcon.Information,
+ 2000
+ )
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ mw = MainWindow()
+ mw.show()
+ sys.exit(app.exec_())
diff --git a/QSystemTrayIcon/README.md b/QSystemTrayIcon/README.md
new file mode 100755
index 0000000000000000000000000000000000000000..b333ea5030aa50635efdaf38ee8af1167423a45e
--- /dev/null
+++ b/QSystemTrayIcon/README.md
@@ -0,0 +1,21 @@
+# QSystemTrayIcon
+
+- 目录
+ - [最小化到系统托盘](#1最小化到系统托盘)
+ - [系统托盘闪烁](#2系统托盘闪烁)
+
+## 1、最小化到系统托盘
+
+[运行 MinimizeToTray.py](MinimizeToTray.py)
+
+选择 Minimize to Tray 在关闭窗口时最小化到系统托盘。
+
+> Reference: https://evileg.com/en/post/68/
+
+
+
+## 2、系统托盘闪烁
+
+[运行 TrayNotify.py](TrayNotify.py)
+
+通过定时器设置不同图标来实现闪烁。
\ No newline at end of file
diff --git a/QSystemTrayIcon/ScreenShot/MinimizeToTray.gif b/QSystemTrayIcon/ScreenShot/MinimizeToTray.gif
new file mode 100755
index 0000000000000000000000000000000000000000..29d322bf1d6ba46b57debe83de88668d4b6600a7
Binary files /dev/null and b/QSystemTrayIcon/ScreenShot/MinimizeToTray.gif differ
diff --git a/QSystemTrayIcon/TrayNotify.py b/QSystemTrayIcon/TrayNotify.py
new file mode 100644
index 0000000000000000000000000000000000000000..914a064defcb624eebda2bed6415a0e8084ead80
--- /dev/null
+++ b/QSystemTrayIcon/TrayNotify.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2021年12月09日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: TrayNotify
+@description: 托盘闪烁
+"""
+
+try:
+ from PyQt5.QtCore import QTimer
+ from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QPushButton,
+ QStyle, QSystemTrayIcon, QWidget)
+except ImportError:
+ from PySide2.QtCore import QTimer
+ from PySide2.QtWidgets import (QApplication, QHBoxLayout, QPushButton,
+ QStyle, QSystemTrayIcon, QWidget)
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QHBoxLayout(self)
+ layout.addWidget(QPushButton('开始闪烁', self, clicked=self.start_flash))
+ layout.addWidget(QPushButton('停止闪烁', self, clicked=self.stop_flash))
+ # 创建托盘图标
+ self.tray_icon = QSystemTrayIcon(self)
+ self.tray_icon.setIcon(self.style().standardIcon(
+ QStyle.SP_ComputerIcon))
+ self.tray_icon.show()
+ # 图标闪烁定时器
+ self.tray_visible = True
+ self.flash_timer = QTimer(self, timeout=self.flash_icon)
+
+ def closeEvent(self, event):
+ self.stop_flash()
+ self.tray_icon.hide()
+ super(Window, self).closeEvent(event)
+
+ def start_flash(self):
+ """开始闪烁"""
+ if not self.flash_timer.isActive():
+ self.flash_timer.start(500)
+
+ def stop_flash(self):
+ """停止闪烁后需要显示图标"""
+ if self.flash_timer.isActive():
+ self.flash_timer.stop()
+ self.tray_icon.setIcon(self.style().standardIcon(
+ QStyle.SP_ComputerIcon))
+
+ def flash_icon(self):
+ """根据当前图标是否可见切换图标"""
+ if self.tray_visible:
+ self.tray_icon.setIcon(self.style().standardIcon(
+ QStyle.SP_TrashIcon))
+ else:
+ self.tray_icon.setIcon(self.style().standardIcon(
+ QStyle.SP_ComputerIcon))
+ self.tray_visible = not self.tray_visible
+
+
+if __name__ == '__main__':
+ import sys
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QTableView/CopyContent.py b/QTableView/CopyContent.py
index b5c82943fd416c6c6a0cf5986ee712e45273c1f7..abeabe436cf4dbd181ef821cecb7f05f7e2b798e 100644
--- a/QTableView/CopyContent.py
+++ b/QTableView/CopyContent.py
@@ -1,22 +1,23 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年4月6日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: CopyContent
@description:
-'''
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QStandardItemModel, QStandardItem
-from PyQt5.QtWidgets import QTableView, QApplication, QAction, QMessageBox
+"""
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QStandardItemModel, QStandardItem
+ from PyQt5.QtWidgets import QTableView, QApplication, QAction, QMessageBox
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QStandardItemModel, QStandardItem
+ from PySide2.QtWidgets import QTableView, QApplication, QAction, QMessageBox
class TableView(QTableView):
@@ -98,8 +99,10 @@ class TableView(QTableView):
self.myModel.setItem(
row, col, QStandardItem("row: {row},col: {col}".format(row=row + 1, col=col + 1)))
+
if __name__ == "__main__":
import sys
+
app = QApplication(sys.argv)
app.setApplicationName("TableView")
w = TableView()
diff --git a/QTableView/README.md b/QTableView/README.md
index 171ad4a6ccdf89090b2f93f58db08ee6449e2123..1d79c7a79960aa6cafeec0096b6c0dc218ee7e86 100644
--- a/QTableView/README.md
+++ b/QTableView/README.md
@@ -1,5 +1,8 @@
# QTableView
+- 目录
+ - [表格内容复制](#1表格内容复制)
+
## 1、表格内容复制
[运行 CopyContent.py](CopyContent.py)
diff --git a/QTableWidget/Lib/mainui.py b/QTableWidget/Lib/mainui.py
index d5f7041a0f575c420c1fe74e59658bd5044265a5..c8f46651c71dcf79fdbc451a91c2723f9567939f 100644
--- a/QTableWidget/Lib/mainui.py
+++ b/QTableWidget/Lib/mainui.py
@@ -6,7 +6,11 @@
#
# WARNING! All changes made in this file will be lost!
-from PyQt5 import QtCore, QtGui, QtWidgets
+try:
+ from PyQt5 import QtCore, QtWidgets
+except ImportError:
+ from PySide2 import QtCore, QtWidgets
+
class Ui_Form(object):
def setupUi(self, Form):
@@ -102,10 +106,10 @@ class Ui_Form(object):
if __name__ == "__main__":
import sys
+
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
-
diff --git a/QTableWidget/README.md b/QTableWidget/README.md
index a8bcbb901aaec7793360d62f94a4bfea880356aa..7ed8033751f4bc10488ccb2895244835b712b668 100644
--- a/QTableWidget/README.md
+++ b/QTableWidget/README.md
@@ -1,8 +1,17 @@
# QTableWidget
+- 目录
+ - [Sqlalchemy动态拼接字段查询显示表格](#1Sqlalchemy动态拼接字段查询显示表格)
+ - [表格嵌入日历,下拉框,进度条,按钮](#2表格嵌入)
+
## 1、Sqlalchemy动态拼接字段查询显示表格
-[运行 SqlQuery.py](SqlQuery.py)
+[运行 SqlQuery.py](SqlQuery.py) | [查看 mainui.ui](Data/mainui.ui)
通过判断界面中选择的条件对`Sqlalchemy`的`model`进行字段拼接从而实现按条件查询
-
\ No newline at end of file
+
+
+## 2、TableWidget嵌入部件
+[运行 TableWidget.py](TableWidget.py)
+点击开始按钮,进度条开始
+
diff --git a/QTableWidget/ScreenShot/table.png b/QTableWidget/ScreenShot/table.png
new file mode 100644
index 0000000000000000000000000000000000000000..f050ee7330a7761cc497b35aae507c218c6ed861
Binary files /dev/null and b/QTableWidget/ScreenShot/table.png differ
diff --git a/QTableWidget/SqlQuery.py b/QTableWidget/SqlQuery.py
index 0e417f759c2d91c32000850e4e8f801de1943975..c40581e0aeb301292df20ebdf5c4e1e751c4006e 100644
--- a/QTableWidget/SqlQuery.py
+++ b/QTableWidget/SqlQuery.py
@@ -4,26 +4,27 @@
"""
Created on 2018年5月15日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: SqlQuery
@description:
"""
-from PyQt5.QtCore import pyqtSlot
-from PyQt5.QtWidgets import QWidget, QMessageBox, QTableWidgetItem
+
+try:
+ from PyQt5.QtCore import pyqtSlot
+ from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QTableWidgetItem
+except ImportError:
+ from PySide2.QtCore import Slot as pyqtSlot
+ from PySide2.QtWidgets import QApplication, QWidget, QMessageBox, QTableWidgetItem
+
from sqlalchemy.engine import create_engine
from sqlalchemy.ext.declarative.api import declarative_base
from sqlalchemy.orm.session import sessionmaker
from sqlalchemy.sql.expression import and_
from sqlalchemy.sql.schema import Column
from sqlalchemy.sql.sqltypes import Integer, Text
-from Lib.mainui import Ui_Form
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+from Lib.mainui import Ui_Form
# engine = create_engine('mysql+mysqldb://root@localhost:3306/tourist?charset=utf8')
engine = create_engine('sqlite:///Data/data.sqlite3', echo=True) # echo 表示开启命令显示
@@ -31,7 +32,6 @@ Base = declarative_base()
class Tourist(Base):
-
__tablename__ = 'tourist'
id = Column(Integer, primary_key=True)
@@ -141,8 +141,9 @@ class Window(QWidget, Ui_Form):
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.Hook(1, None, 5, sys.stderr, 'text')
- from PyQt5.QtWidgets import QApplication
+
+ cgitb.enable(format='text')
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QTableWidget/TableWidget.py b/QTableWidget/TableWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..a052566b03d2382f99d36da70eb48833f94c46e6
--- /dev/null
+++ b/QTableWidget/TableWidget.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+Created on 2017年4月21日
+@author: weike32
+@site: https://pyqt.site ,https://github.com/weike32
+@email: 394967319@qq.com
+@file: CopyContent
+@description: 查阅了很多博客,如果有异,可以联系作者邮箱。本Demo仅作学习参考用,保有后续相关权益。
+"""
+import sys
+
+try:
+ from PyQt5 import QtWidgets
+ from PyQt5.QtCore import *
+ from PyQt5.QtGui import *
+ from PyQt5.QtWidgets import *
+except ImportError:
+ from PySide2 import QtWidgets
+ from PySide2.QtCore import *
+ from PySide2.QtGui import *
+ from PySide2.QtWidgets import *
+
+
+class MyTable(QTableWidget):
+ def __init__(self, parent=None):
+ super(MyTable, self).__init__(parent)
+ self.setWindowTitle("我是一个表格")
+ self.setWindowIcon(QIcon("male.png"))
+ self.resize(920, 240)
+ self.setColumnCount(6)
+ self.setRowCount(2)
+ # 设置表格有两行五列。
+ self.setColumnWidth(0, 200)
+ self.setColumnWidth(4, 200)
+ self.setRowHeight(0, 100)
+ # 设置第一行高度为100px,第一列宽度为200px。
+
+ self.table()
+
+ def table(self):
+ self.setItem(0, 0, QTableWidgetItem("你的名字"))
+ self.setItem(0, 1, QTableWidgetItem("性别"))
+ self.setItem(0, 2, QTableWidgetItem("出生日期"))
+ self.setItem(0, 3, QTableWidgetItem("职业"))
+ self.setItem(0, 4, QTableWidgetItem("收入"))
+ self.setItem(0, 5, QTableWidgetItem("进度条"))
+ # 添加表格的文字内容.
+ self.setHorizontalHeaderLabels(["第一行", "第二行", "第三行", "第四行", "第五行", "第六行"])
+ self.setVerticalHeaderLabels(["第一列", "第二列"])
+ # 设置表头
+ lbp = QLabel()
+ lbp.setPixmap(QPixmap("youPicture.png"))
+ self.setCellWidget(1, 1, lbp)
+ # 在表中添加一张图片
+ twi = QTableWidgetItem("Graph")
+ twi.setFont(QFont("Times", 10, ))
+ self.setItem(1, 0, twi)
+
+ # 添加一个自己设置了大小和类型的文字。
+ dte = QDateTimeEdit()
+ dte.setDateTime(QDateTime.currentDateTime())
+ dte.setDisplayFormat("yyyy/MM/dd")
+ dte.setCalendarPopup(True)
+ self.setCellWidget(1, 2, dte)
+ # 添加一个弹出的日期选择,设置默认值为当前日期,显示格式为年月日。
+ cbw = QComboBox()
+ cbw.addItem("医生")
+ cbw.addItem("老师")
+ cbw.addItem("律师")
+ self.setCellWidget(1, 3, cbw)
+ # 添加了一个下拉选择框
+ sb = QSpinBox()
+ sb.setRange(1000, 10000)
+ sb.setValue(5000) # 设置最开始显示的数字
+ sb.setDisplayIntegerBase(10) # 这个是显示数字的进制,默认是十进制。
+ sb.setSuffix("元") # 设置后辍
+ sb.setPrefix("RMB: ") # 设置前辍
+ sb.setSingleStep(100)
+ self.setCellWidget(1, 4, sb)
+ # 添加一个进度条
+
+ self.progressBar = QtWidgets.QProgressBar(self)
+ self.progressBar.setProperty("value", 0)
+ self.progressBar.setObjectName("progressBar")
+ self.setCellWidget(1, 5, self.progressBar)
+ self.step = 0
+ self.timer = QTimer()
+ self.timer.setInterval(1000)
+ self.timer.start()
+ # 信号连接到槽
+ self.timer.timeout.connect(self.onTimerOut)
+ self.count = 0
+
+ def onTimerOut(self): # 重写timerEvent
+ self.count += 1
+ if self.count >= 100: # value >= 100时,停止计时器
+ self.timer.stop()
+ print("结束")
+ # self.progressBar.setValue(self.step)
+ else:
+ print(self.count)
+ self.progressBar.setValue(self.count)
+ # return
+ # self.step += 1
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ myTable = MyTable()
+ myTable.show()
+ app.exit(app.exec_())
diff --git a/QTextBrowser/DynamicRes.py b/QTextBrowser/DynamicRes.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c229fe5ddddc39c6fb550e9e1a9947f67724c81
--- /dev/null
+++ b/QTextBrowser/DynamicRes.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020/6/3
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: DynamicRes
+@description:
+"""
+from threading import Thread
+
+import requests
+
+try:
+ from PyQt5.QtCore import QUrl, QByteArray
+ from PyQt5.QtGui import QImage, QTextDocument
+ from PyQt5.QtWidgets import QApplication, QTextBrowser, QWidget, QVBoxLayout, QPushButton
+except ImportError:
+ from PySide2.QtCore import QUrl, QByteArray
+ from PySide2.QtGui import QImage, QTextDocument
+ from PySide2.QtWidgets import QApplication, QTextBrowser, QWidget, QVBoxLayout, QPushButton
+
+
+class TextBrowser(QTextBrowser):
+ NetImages = {}
+
+ def __init__(self, *args, **kwargs):
+ super(TextBrowser, self).__init__(*args, **kwargs)
+ self.setOpenLinks(False) # 禁止打开URL
+
+ def downloadImage(self, url):
+ try:
+ self.NetImages[url] = [QByteArray(requests.get(url.toString()).content), 1]
+ print('下载完成', url)
+ except Exception as e:
+ print('下载失败', url, e)
+ self.NetImages[url] = [QByteArray(), 1]
+
+ def loadResource(self, rtype, url):
+ ret = super(TextBrowser, self).loadResource(rtype, url)
+ # 加载图片资源
+ if rtype == QTextDocument.ImageResource:
+ if ret:
+ return ret
+ if url.toString().startswith('irony'): # 自定义的协议头
+ print('加载本地', '../Donate/zhifubao.png', url)
+ return QImage(
+ '../Donate/zhifubao.png') # 或者 QByteArray(open('../Donate/zhifubao.png', 'rb').read())
+ elif url.toString().startswith('http'): # 加载网络图片
+ img, status = self.NetImages.get(url, [None, None])
+ if url not in self.NetImages or status is None:
+ # 子线程下载
+ self.NetImages[url] = [None, 1]
+ print('download ', url)
+ Thread(target=self.downloadImage, args=(url,), daemon=True).start()
+ elif img:
+ return img
+ return ret
+
+ def mouseDoubleClickEvent(self, event):
+ # 双击图片得到图片的URL,也可以用来放大显示
+ super(TextBrowser, self).mouseDoubleClickEvent(event)
+ url = self.anchorAt(event.pos())
+ if url:
+ print('url:', url, self.document().resource(QTextDocument.ImageResource, QUrl(url)))
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+
+ self.textBrowser = TextBrowser(self)
+ self.downButton = QPushButton('加载网络图片', self)
+
+ layout.addWidget(self.textBrowser)
+ layout.addWidget(self.downButton)
+
+ # 加载本地图片
+ img = QImage('../Donate/weixin.png')
+ # 第二个参数为任意唯一的url类似于qrc方式
+ self.textBrowser.document().addResource(QTextDocument.ImageResource,
+ QUrl('dynamic:/images/weixin.png'), img)
+
+ # 设置html
+ # 需要注意里面的图片地址
+ self.textBrowser.setHtml(
+ '
' # 方式一直接加载本地图片
+ '
' # 方式二通过addResource添加资源
+ '
' # 方式三定义自定义的协议头通过loadResource动态加载
+ '
')
+
+
+if __name__ == '__main__':
+ import sys
+ import cgitb
+
+ cgitb.enable(format='text')
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QTextBrowser/README.md b/QTextBrowser/README.md
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..dee5984cc93cd54cb9bf16f686efd0ad065b1075 100644
--- a/QTextBrowser/README.md
+++ b/QTextBrowser/README.md
@@ -0,0 +1,14 @@
+# QTextBrowser
+
+- 目录
+ - [动态加载图片](#1动态加载图片)
+
+## 1、动态加载图片
+[运行 DynamicRes.py](DynamicRes.py)
+
+动态加载资源有多种方式,这里主要介绍 [addResource](https://doc.qt.io/qt-5/qtextdocument.html#addResource) 和 [loadResource](https://doc.qt.io/qt-5/qtextbrowser.html#loadResource) 函数
+
+1、通过 `self.textBrowser.document().addResource(QTextDocument.ImageResource, QUrl('dynamic:/images/weixin.png'), img)` 向文档中注册新的资源索引,类似QRC
+2、通过重载 `loadResource` 函数可以监听到所有的资源加载,然后动态返回内容
+
+
\ No newline at end of file
diff --git a/QTextBrowser/ScreenShot/DynamicRes.gif b/QTextBrowser/ScreenShot/DynamicRes.gif
new file mode 100644
index 0000000000000000000000000000000000000000..9c923b58a50c56707b092957dbfd3d74c8f090af
Binary files /dev/null and b/QTextBrowser/ScreenShot/DynamicRes.gif differ
diff --git a/QTextEdit/HighlightText.py b/QTextEdit/HighlightText.py
index d1c8c485d75b24ef4bd15bf1cd4b033b6b7e1867..ca7398fdfaef271b745c28173cccef83feccc30c 100644
--- a/QTextEdit/HighlightText.py
+++ b/QTextEdit/HighlightText.py
@@ -1,7 +1,27 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年5月22日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file:
+@description:
+"""
+
import sys
-from PyQt5.QtGui import QTextCharFormat, QTextDocument, QTextCursor
-from PyQt5.QtWidgets import (QApplication, QMainWindow, QTextEdit,
- QToolBar, QLineEdit, QPushButton, QColorDialog, QHBoxLayout, QWidget)
+
+try:
+ from PyQt5.QtCore import QRegExp
+ from PyQt5.QtGui import QTextCharFormat, QTextCursor
+ from PyQt5.QtWidgets import (QApplication, QMainWindow, QTextEdit,
+ QToolBar, QLineEdit, QPushButton, QColorDialog, QHBoxLayout, QWidget)
+except ImportError:
+ from PySide2.QtCore import QRegExp
+ from PySide2.QtGui import QTextCharFormat, QTextCursor
+ from PySide2.QtWidgets import (QApplication, QMainWindow, QTextEdit,
+ QToolBar, QLineEdit, QPushButton, QColorDialog, QHBoxLayout, QWidget)
class TextEdit(QMainWindow):
@@ -22,30 +42,46 @@ class TextEdit(QMainWindow):
tb = QToolBar(self)
tb.addWidget(widget)
+ self.addToolBar(tb)
def setText(self, text):
self.textEdit.setPlainText(text)
- def mergeFormatOnWordOrSelection(self, format):
- cursor = self.textEdit.textCursor()
- if not cursor.hasSelection():
- cursor.select(QTextCursor.WordUnderCursor)
- cursor.mergeCharFormat(format)
- self.textEdit.mergeCurrentCharFormat(format)
-
def highlight(self):
text = self.findText.text() # 输入框中的文字
if not text:
return
+
col = QColorDialog.getColor(self.textEdit.textColor(), self)
if not col.isValid():
return
+
+ # 恢复默认的颜色
+ cursor = self.textEdit.textCursor()
+ cursor.select(QTextCursor.Document)
+ cursor.setCharFormat(QTextCharFormat())
+ cursor.clearSelection()
+ self.textEdit.setTextCursor(cursor)
+
+ # 文字颜色
fmt = QTextCharFormat()
fmt.setForeground(col)
- # 先把光标移动到开头
+
+ # 正则
+ expression = QRegExp(text)
self.textEdit.moveCursor(QTextCursor.Start)
- while self.textEdit.find(text, QTextDocument.FindWholeWords): # 查找所有文字
- self.mergeFormatOnWordOrSelection(fmt)
+ cursor = self.textEdit.textCursor()
+
+ # 循环查找设置颜色
+ pos = 0
+ index = expression.indexIn(self.textEdit.toPlainText(), pos)
+ while index >= 0:
+ cursor.setPosition(index)
+ cursor.movePosition(QTextCursor.Right,
+ QTextCursor.KeepAnchor, len(text))
+ cursor.mergeCharFormat(fmt)
+ pos = index + expression.matchedLength()
+ index = expression.indexIn(self.textEdit.toPlainText(), pos)
if __name__ == '__main__':
diff --git a/QTextEdit/README.md b/QTextEdit/README.md
index 68780a4cdeb78883cff0a264c5e75f5d70997d76..98bec9d8f1dc1533c0d45b8c1cef8049cffc5a40 100644
--- a/QTextEdit/README.md
+++ b/QTextEdit/README.md
@@ -1,5 +1,8 @@
# QTextEdit
+- 目录
+ - [文本查找高亮](#1文本查找高亮)
+
## 1、文本查找高亮
[运行 HighlightText.py](HighlightText.py)
diff --git a/QThread/InheritQThread.py b/QThread/InheritQThread.py
index ea10801818974cc3067d1182b7524b8295446ec9..051f5f6b2b5e4014c54386f95f20e83211410449 100644
--- a/QThread/InheritQThread.py
+++ b/QThread/InheritQThread.py
@@ -4,27 +4,28 @@
"""
Created on 2018年3月9日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: InheritQThread
@description: 继承QThread
"""
-from PyQt5.QtCore import QThread, pyqtSignal
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QProgressBar, QPushButton
-
-__Author__ = 'By: Irony\nQQ: 892768447\nEmail: 892768447@qq.com'
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import QThread, pyqtSignal
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QProgressBar, QPushButton
+except ImportError:
+ from PySide2.QtCore import QThread, Signal as pyqtSignal
+ from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QProgressBar, QPushButton
class Worker(QThread):
-
valueChanged = pyqtSignal(int) # 值变化信号
def run(self):
- print('thread id', int(QThread.currentThreadId()))
+ print('thread id', QThread.currentThread())
for i in range(1, 101):
+ if self.isInterruptionRequested():
+ break
print('value', i)
self.valueChanged.emit(i)
QThread.sleep(1)
@@ -41,7 +42,7 @@ class Window(QWidget):
layout.addWidget(QPushButton('开启线程', self, clicked=self.onStart))
# 当前线程id
- print('main id', int(QThread.currentThreadId()))
+ print('main id', QThread.currentThread())
# 子线程
self._thread = Worker(self)
@@ -49,21 +50,26 @@ class Window(QWidget):
self._thread.valueChanged.connect(self.progressBar.setValue)
def onStart(self):
- print('main id', int(QThread.currentThreadId()))
- self._thread.start() # 启动线程
+ if not self._thread.isRunning():
+ print('main id', QThread.currentThread())
+ self._thread.start() # 启动线程
def closeEvent(self, event):
if self._thread.isRunning():
+ self._thread.requestInterruption()
self._thread.quit()
+ self._thread.wait()
# 强制
# self._thread.terminate()
- del self._thread
+ self._thread.deleteLater()
super(Window, self).closeEvent(event)
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+ import cgitb
+
+ cgitb.enable(format='text')
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QThread/QuitThread.py b/QThread/QuitThread.py
new file mode 100644
index 0000000000000000000000000000000000000000..35b7a8e9eb331471f785922ac8a6c07436d7a9d7
--- /dev/null
+++ b/QThread/QuitThread.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020/11/27
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QuitThread
+@description:
+"""
+
+import sys
+from time import time
+
+try:
+ from PyQt5.QtCore import QThread, QCoreApplication, QTimer
+except ImportError:
+ from PySide2.QtCore import QThread, QCoreApplication, QTimer
+
+
+class Thread(QThread):
+
+ def run(self):
+ print('thread id', QThread.currentThread())
+ i = 0
+ while i < 101 and not self.isInterruptionRequested():
+ print('value', i, time())
+ i += 1
+ QThread.msleep(500)
+ print('thread quit')
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+ t = Thread()
+ t.finished.connect(app.quit)
+ t.start()
+ # 3秒后退出
+ print('will quit 3s latter')
+ QTimer.singleShot(3000, t.requestInterruption)
+ sys.exit(app.exec_())
diff --git a/QThread/README.md b/QThread/README.md
index 6f52c7c94b4736c75af090bbb16114f3d5bc10ad..b9661c21c64559ea041b749eea945ea1c51a9cb9 100644
--- a/QThread/README.md
+++ b/QThread/README.md
@@ -1,6 +1,11 @@
# QThread
-PyQt多线程的简单使用例子
+- 目录
+ - [继承QThread](#1继承QThread)
+ - [moveToThread](#2moveToThread)
+ - [线程挂起恢复](#3线程挂起恢复)
+ - [线程休眠唤醒](#4线程休眠唤醒)
+ - [线程退出](#5线程退出)
## 1、继承QThread
[运行 InheritQThread.py](InheritQThread.py)
@@ -30,4 +35,11 @@ PyQt多线程的简单使用例子
使用 `QWaitCondition` 的 `wait` 和 `wakeAll` 方法
-
\ No newline at end of file
+
+
+## 5、线程退出
+[运行 QuitThread.py](QuitThread.py)
+
+`isInterruptionRequested` 和 `requestInterruption` 函数作为退出标识调用
+
+
\ No newline at end of file
diff --git a/QThread/ScreenShot/QuitThread.jpg b/QThread/ScreenShot/QuitThread.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2c96dee2508aea759382d501574cf5583c55357e
Binary files /dev/null and b/QThread/ScreenShot/QuitThread.jpg differ
diff --git a/QThread/SuspendThread.py b/QThread/SuspendThread.py
index 707324e629a69e981c5815c75537fbac78550ccc..32c3601835114feb1a2c6ef40bc85cab5eb64165 100644
--- a/QThread/SuspendThread.py
+++ b/QThread/SuspendThread.py
@@ -1,26 +1,24 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+
+"""
+Created on 2018年3月13日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file:
+@description:
+"""
+
import ctypes
+import win32con
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QProgressBar, QPushButton
-import win32con
from win32process import SuspendThread, ResumeThread
-# Created on 2018年3月13日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: 多线程使用.a
-# description:
-__Author__ = 'By: Irony\nQQ: 892768447\nEmail: 892768447@qq.com'
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
-
-
class Worker(QThread):
-
valueChanged = pyqtSignal(int) # 值变化信号
handle = -1
@@ -109,8 +107,10 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
import os
+
print('pid', os.getpid())
from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QThread/WakeupThread.py b/QThread/WakeupThread.py
index 2f11a8065ed07a6d4de06fd0416021792c761565..e9350371fee85a759a54c3bf23d30903e933ff8c 100644
--- a/QThread/WakeupThread.py
+++ b/QThread/WakeupThread.py
@@ -4,7 +4,7 @@
"""
Created on 2018年11月11日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file:
@description:
@@ -13,15 +13,7 @@ from PyQt5.QtCore import QThread, QWaitCondition, QMutex, pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QProgressBar
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
-
-
class Thread(QThread):
-
valueChange = pyqtSignal(int)
def __init__(self, *args, **kwargs):
@@ -75,8 +67,10 @@ class Window(QWidget):
if __name__ == '__main__':
import sys
import cgitb
- sys.excepthook = cgitb.enable(1, None, 5, '')
+
+ cgitb.enable(format='text')
from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QThread/moveToThread.py b/QThread/moveToThread.py
index ea1602de6269c2f7a08b3b5b7d93b50e0dd4ea52..77214df1f7823b70e33c64a867416e25814f035a 100644
--- a/QThread/moveToThread.py
+++ b/QThread/moveToThread.py
@@ -4,27 +4,28 @@
"""
Created on 2018年3月9日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: moveToThread
@description: moveToThread
"""
-from PyQt5.QtCore import QObject, pyqtSignal, QThread
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QProgressBar, QPushButton
-
-__Author__ = 'By: Irony\nQQ: 892768447\nEmail: 892768447@qq.com'
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+try:
+ from PyQt5.QtCore import QObject, pyqtSignal, QThread
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QProgressBar, QPushButton
+except ImportError:
+ from PySide2.QtCore import QObject, Signal as pyqtSignal, QThread
+ from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QProgressBar, QPushButton
class Worker(QObject):
-
valueChanged = pyqtSignal(int) # 值变化信号
def run(self):
- print('thread id', int(QThread.currentThreadId()))
+ print('thread id', )
for i in range(1, 101):
+ if QThread.currentThread().isInterruptionRequested():
+ break
print('value', i)
self.valueChanged.emit(i)
QThread.sleep(1)
@@ -41,33 +42,38 @@ class Window(QWidget):
layout.addWidget(QPushButton('开启线程', self, clicked=self.onStart))
# 当前线程id
- print('main id', int(QThread.currentThreadId()))
+ print('main id', QThread.currentThread())
# 启动线程更新进度条值
self._thread = QThread(self)
self._worker = Worker()
self._worker.moveToThread(self._thread) # 移动到线程中执行
self._thread.finished.connect(self._worker.deleteLater)
+ self._thread.started.connect(self._worker.run)
self._worker.valueChanged.connect(self.progressBar.setValue)
def onStart(self):
- print('main id', int(QThread.currentThreadId()))
- self._thread.started.connect(self._worker.run)
- self._thread.start() # 启动线程
+ if not self._thread.isRunning():
+ print('main id', QThread.currentThread())
+ self._thread.start() # 启动线程
def closeEvent(self, event):
if self._thread.isRunning():
+ self._thread.requestInterruption()
self._thread.quit()
+ self._thread.wait()
# 强制
# self._thread.terminate()
- del self._thread
- del self._worker
+ self._thread.deleteLater()
super(Window, self).closeEvent(event)
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+ import cgitb
+
+ cgitb.enable(format='text')
+
app = QApplication(sys.argv)
w = Window()
w.show()
diff --git a/QTreeWidget/Data/testTree.ui b/QTreeWidget/Data/testTree.ui
new file mode 100644
index 0000000000000000000000000000000000000000..8ceab90f82dd962cac0a365f01407211c2ee8a48
--- /dev/null
+++ b/QTreeWidget/Data/testTree.ui
@@ -0,0 +1,82 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 719
+ 544
+
+
+
+ Form
+
+
+
+
+ 80
+ 80
+ 256
+ 192
+
+
+
+
+ 测试
+
+
+ -
+
+ 测试1
+
+
+ Unchecked
+
+ -
+
+ 子节点1
+
+
+ Unchecked
+
+
+ -
+
+ 字节点2
+
+
+ Unchecked
+
+
+ -
+
+ 字节点3
+
+
+ Unchecked
+
+
+ -
+
+ 字节点4
+
+
+ Unchecked
+
+
+ -
+
+ 字节点5
+
+
+ Unchecked
+
+
+
+
+
+
+
+
diff --git a/QTreeWidget/Lib/__init__.py b/QTreeWidget/Lib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QTreeWidget/Lib/testTree.py b/QTreeWidget/Lib/testTree.py
new file mode 100644
index 0000000000000000000000000000000000000000..2745b729f1d4c48b0e6829d3e0ae58198ebb01a9
--- /dev/null
+++ b/QTreeWidget/Lib/testTree.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'testTree.ui'
+#
+# Created by: PyQt5 UI code generator 5.10.1
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(719, 544)
+ self.treeWidget = QtWidgets.QTreeWidget(Form)
+ self.treeWidget.setGeometry(QtCore.QRect(80, 80, 256, 192))
+ self.treeWidget.setObjectName("treeWidget")
+ item_0 = QtWidgets.QTreeWidgetItem(self.treeWidget)
+ item_0.setCheckState(0, QtCore.Qt.Unchecked)
+ item_1 = QtWidgets.QTreeWidgetItem(item_0)
+ item_1.setCheckState(0, QtCore.Qt.Unchecked)
+ item_1 = QtWidgets.QTreeWidgetItem(item_0)
+ item_1.setCheckState(0, QtCore.Qt.Unchecked)
+ item_1 = QtWidgets.QTreeWidgetItem(item_0)
+ item_1.setCheckState(0, QtCore.Qt.Unchecked)
+ item_1 = QtWidgets.QTreeWidgetItem(item_0)
+ item_1.setCheckState(0, QtCore.Qt.Unchecked)
+ item_1 = QtWidgets.QTreeWidgetItem(item_0)
+ item_1.setCheckState(0, QtCore.Qt.Unchecked)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.treeWidget.headerItem().setText(0, _translate("Form", "测试"))
+ __sortingEnabled = self.treeWidget.isSortingEnabled()
+ self.treeWidget.setSortingEnabled(False)
+ self.treeWidget.topLevelItem(0).setText(0, _translate("Form", "测试1"))
+ self.treeWidget.topLevelItem(0).child(0).setText(0, _translate("Form", "子节点1"))
+ self.treeWidget.topLevelItem(0).child(1).setText(0, _translate("Form", "字节点2"))
+ self.treeWidget.topLevelItem(0).child(2).setText(0, _translate("Form", "字节点3"))
+ self.treeWidget.topLevelItem(0).child(3).setText(0, _translate("Form", "字节点4"))
+ self.treeWidget.topLevelItem(0).child(4).setText(0, _translate("Form", "字节点5"))
+ self.treeWidget.setSortingEnabled(__sortingEnabled)
+
+
+if __name__ == "__main__":
+ import sys
+
+ app = QtWidgets.QApplication(sys.argv)
+ Form = QtWidgets.QWidget()
+ ui = Ui_Form()
+ ui.setupUi(Form)
+ Form.show()
+ sys.exit(app.exec_())
diff --git a/QTreeWidget/ParentNodeForbid.py b/QTreeWidget/ParentNodeForbid.py
new file mode 100644
index 0000000000000000000000000000000000000000..1a139e7bacc39d326936c437132f79d3b06ef2ad
--- /dev/null
+++ b/QTreeWidget/ParentNodeForbid.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年11月8日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QTreeWidget.ParentNodeForbid
+@description: 父节点不可选中
+"""
+
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem, QStyledItemDelegate, \
+ QStyle
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem, QStyledItemDelegate, \
+ QStyle
+
+
+class NoColorItemDelegate(QStyledItemDelegate):
+
+ def paint(self, painter, option, index):
+ if option.state & QStyle.State_HasFocus:
+ # 取消虚线框
+ option.state = option.state & ~ QStyle.State_HasFocus
+ if option.state & QStyle.State_MouseOver and index.data(Qt.UserRole + 1):
+ # 不显示鼠标悬停颜色
+ option.state = option.state & ~ QStyle.State_MouseOver
+ super(NoColorItemDelegate, self).paint(painter, option, index)
+
+
+class Window(QTreeWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.setItemDelegateForColumn(0, NoColorItemDelegate(self))
+
+ # 父节点(不可选中)
+ pitem1 = QTreeWidgetItem(self, ['parent item 1'])
+ # 设置不可选中
+ pitem1.setFlags(pitem1.flags() & ~Qt.ItemIsSelectable)
+ # 设置一个标识用于屏蔽鼠标事件
+ pitem1.setData(0, Qt.UserRole + 1, True)
+
+ pitem2 = QTreeWidgetItem(self, ['parent item 2'])
+ pitem2.setFlags(pitem2.flags() & ~Qt.ItemIsSelectable)
+ pitem2.setData(0, Qt.UserRole + 1, True)
+
+ # 子节点(可选)
+ citem1 = QTreeWidgetItem(pitem1, ['child item 1'])
+ citem2 = QTreeWidgetItem(pitem2, ['child item 2'])
+
+ self.expandAll()
+
+ # 信号槽
+ self.itemActivated.connect(self.onItemActivated)
+ self.itemClicked.connect(self.onItemClicked)
+ self.itemDoubleClicked.connect(self.onItemDoubleClicked)
+ self.itemPressed.connect(self.onItemPressed)
+
+ def mousePressEvent(self, event):
+ # 鼠标点击事件,判断当前点击位置是否有item且满足标志则拦截鼠标事件
+ item = self.itemAt(event.pos())
+ if item and item.data(0, Qt.UserRole + 1):
+ event.accept()
+ return
+ super(Window, self).mousePressEvent(event)
+
+ def onItemActivated(self, item, column):
+ print('Activated', item.text(0), item, column)
+
+ def onItemClicked(self, item, column):
+ print('Clicked', item.text(0), item, column)
+
+ def onItemDoubleClicked(self, item, column):
+ print('DoubleClicked', item.text(0), item, column)
+
+ def onItemPressed(self, item, column):
+ print('Pressed', item.text(0), item, column)
+
+
+if __name__ == '__main__':
+ import sys
+ import cgitb
+
+ cgitb.enable(format='text')
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QTreeWidget/ParsingJson.py b/QTreeWidget/ParsingJson.py
index dbbc088c368bca6b84669349a15652ca95753538..c32480ce840bca2c8d5b21a714f78ef40bbb052c 100644
--- a/QTreeWidget/ParsingJson.py
+++ b/QTreeWidget/ParsingJson.py
@@ -4,7 +4,7 @@
"""
Created on 2018年4月8日
@author: Irony
-@site: https://pyqt5.com , https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ParsingJson
@description:
@@ -12,18 +12,18 @@ Created on 2018年4月8日
import json
import webbrowser
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QWidget,\
- QLabel, QSpacerItem, QSizePolicy, QHBoxLayout
import chardet
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QIcon
+ from PyQt5.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem, QWidget, \
+ QLabel, QSpacerItem, QSizePolicy, QHBoxLayout
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QIcon
+ from PySide2.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem, QWidget, \
+ PySide2, QSpacerItem, QSizePolicy, QHBoxLayout
class ItemWidget(QWidget):
@@ -94,7 +94,7 @@ class JsonTreeWidget(QTreeWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
app.setStyleSheet("""QTreeView {
outline: 0px;
diff --git a/QTreeWidget/README.md b/QTreeWidget/README.md
index 5317d970b9a301ded042fea7b462adc70743f71d..5194650a93e39d71b2682af9db917a2821a9d39f 100644
--- a/QTreeWidget/README.md
+++ b/QTreeWidget/README.md
@@ -1,8 +1,29 @@
# QTreeWidget
+- 目录
+ - [通过json数据生成树形结构](#1通过json数据生成树形结构)
+ - [点击父节点全选/取消全选子节点](#2点击父节点全选取消全选子节点)
+ - [禁止父节点/禁止父节点](#3禁止父节点)
+
## 1、通过json数据生成树形结构
[运行 ParsingJson.py](ParsingJson.py)
解析每一层json数据中的list
-
\ No newline at end of file
+
+
+
+## 2、点击父节点全选/取消全选子节点
+[运行 testTreeWidget.py](testTreeWidget.py) | [查看 testTree.ui](Data/testTree.ui)
+
+点击父节点全选/取消全选子节点
+
+
+
+## 3、禁止父节点
+[运行 ParentNodeForbid.py](ParentNodeForbid.py)
+
+ 1. 父节点通过设置`pitem1.setFlags(pitem1.flags() & ~Qt.ItemIsSelectable)`为不可选
+ 2. 完全禁用点击等需要重写`mousePressEvent`事件并结合item的标志来判断
+
+
\ No newline at end of file
diff --git a/QTreeWidget/ScreenShot/ParentNodeForbid.gif b/QTreeWidget/ScreenShot/ParentNodeForbid.gif
new file mode 100644
index 0000000000000000000000000000000000000000..272309932ba2136e3c1778115cd5eaf099344f20
Binary files /dev/null and b/QTreeWidget/ScreenShot/ParentNodeForbid.gif differ
diff --git a/QTreeWidget/ScreenShot/allSelectNode.png b/QTreeWidget/ScreenShot/allSelectNode.png
new file mode 100644
index 0000000000000000000000000000000000000000..fac534f0e25fa4902508c9a9f52ad2942c83da78
Binary files /dev/null and b/QTreeWidget/ScreenShot/allSelectNode.png differ
diff --git a/QTreeWidget/__init__.py b/QTreeWidget/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QTreeWidget/testTreeWidget.py b/QTreeWidget/testTreeWidget.py
new file mode 100644
index 0000000000000000000000000000000000000000..3bdb95682210c22ed5f6d2db9c02da1407d25d0a
--- /dev/null
+++ b/QTreeWidget/testTreeWidget.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+Created on 2017年4月20日
+@author: weike32
+@site: https://pyqt.site , https://github.com/weike32
+@email: 394967319@qq.com
+@file: CopyContent
+@description:
+"""
+import sys
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QDialog, QApplication
+
+from Lib.testTree import Ui_Form # @UnresolvedImport
+
+
+class graphAnalysis(QDialog, Ui_Form):
+ def __init__(self):
+ super(graphAnalysis, self).__init__()
+ self.setupUi(self)
+ # 点击父节点
+ self.treeWidget.itemChanged.connect(self.handleChanged)
+
+ def handleChanged(self, item, column):
+ count = item.childCount()
+ if item.checkState(column) == Qt.Checked:
+ for index in range(count):
+ item.child(index).setCheckState(0, Qt.Checked)
+ if item.checkState(column) == Qt.Unchecked:
+ for index in range(count):
+ item.child(index).setCheckState(0, Qt.Unchecked)
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ w = graphAnalysis()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QVBoxLayout/Data/BaseVerticalLayout.ui b/QVBoxLayout/Data/BaseVerticalLayout.ui
new file mode 100644
index 0000000000000000000000000000000000000000..77c5b148ad2500287ca8c1003bb80bd1ed1e7239
--- /dev/null
+++ b/QVBoxLayout/Data/BaseVerticalLayout.ui
@@ -0,0 +1,38 @@
+
+
+ BaseVerticalLayout
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+ -
+
+
+ 通过 右键 -> 布局 -> 垂直布局
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ 底部按钮
+
+
+
+
+
+
+
+
diff --git a/QVBoxLayout/Data/VerticalLayoutMargin.ui b/QVBoxLayout/Data/VerticalLayoutMargin.ui
new file mode 100644
index 0000000000000000000000000000000000000000..6045d80906bf7be1493d06efc615d73c4469c88a
--- /dev/null
+++ b/QVBoxLayout/Data/VerticalLayoutMargin.ui
@@ -0,0 +1,62 @@
+
+
+ VerticalLayoutMargin
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+ #VerticalLayoutMargin {
+ background: #5aaadb;
+}
+#label {
+ background: #85c440;
+}
+
+
+
+ 50
+
+
+ 20
+
+
+ 20
+
+ -
+
+
+ 通过设置Margin和Spacing设置边距以及两个控件之间的间隔距离
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Spcasing 为 50
+
+
+
+ -
+
+
+ Spcasing 为 50
+
+
+
+
+
+
+
+
diff --git a/QVBoxLayout/Data/VerticalLayoutStretch.ui b/QVBoxLayout/Data/VerticalLayoutStretch.ui
new file mode 100644
index 0000000000000000000000000000000000000000..bf198c9f3649bf70580b9721006b3ec62607f596
--- /dev/null
+++ b/QVBoxLayout/Data/VerticalLayoutStretch.ui
@@ -0,0 +1,62 @@
+
+
+ VerticalLayoutStretch
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+ #label {
+ background: #5aaadb;
+}
+#label_2 {
+ background: #85c440;
+}
+#label_3 {
+ background: #f2b63c;
+}
+
+
+ -
+
+
+ 通过设置Stretch设置每部分的占比(1,2,3) 1/6
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ 2/6
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ 3/6
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+
+
diff --git a/QVBoxLayout/README.md b/QVBoxLayout/README.md
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0067952222ef89db5dab91c5ed7f9a79a6ec5772 100644
--- a/QVBoxLayout/README.md
+++ b/QVBoxLayout/README.md
@@ -0,0 +1,32 @@
+# QVBoxLayout
+
+- 目录
+ - [垂直布局](#1垂直布局)
+ - [边距和间隔](#2边距和间隔)
+ - [比例分配](#3比例分配)
+
+## 1、垂直布局
+[查看 BaseVerticalLayout.ui](Data/BaseVerticalLayout.ui)
+
+
+
+## 2、边距和间隔
+[查看 VerticalLayoutMargin.ui](Data/VerticalLayoutMargin.ui)
+
+1. 通过`setContentsMargins(20, 20, -1, -1)`设置左上右下的边距,-1表示默认值
+2. 通过`setSpacing`设置控件之间的间隔
+
+
+
+## 3、比例分配
+[查看 VerticalLayoutStretch.ui](Data/VerticalLayoutStretch.ui)
+
+通过`setStretch`设置各个部分的占比 分别为:1/6 2/6 3/6
+
+```python
+self.verticalLayout.setStretch(0, 1)
+self.verticalLayout.setStretch(1, 2)
+self.verticalLayout.setStretch(2, 3)
+```
+
+
\ No newline at end of file
diff --git a/QVBoxLayout/ScreenShot/BaseVerticalLayout.png b/QVBoxLayout/ScreenShot/BaseVerticalLayout.png
new file mode 100644
index 0000000000000000000000000000000000000000..944a73a7a32c9522603f8c1fe0957074b11fdfd3
Binary files /dev/null and b/QVBoxLayout/ScreenShot/BaseVerticalLayout.png differ
diff --git a/QVBoxLayout/ScreenShot/VerticalLayoutMargin.png b/QVBoxLayout/ScreenShot/VerticalLayoutMargin.png
new file mode 100644
index 0000000000000000000000000000000000000000..2208490cea21175507f6259c4b266b93e387e5ce
Binary files /dev/null and b/QVBoxLayout/ScreenShot/VerticalLayoutMargin.png differ
diff --git a/QVBoxLayout/ScreenShot/VerticalLayoutStretch.png b/QVBoxLayout/ScreenShot/VerticalLayoutStretch.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff3573234678233e4afef7ba0c8edaa5102c8819
Binary files /dev/null and b/QVBoxLayout/ScreenShot/VerticalLayoutStretch.png differ
diff --git a/QWebChannel/CallEachWithJs.py b/QWebChannel/CallEachWithJs.py
new file mode 100644
index 0000000000000000000000000000000000000000..238b0625f7e0a34ce32e20ea7b52ce16a6e1cc5a
--- /dev/null
+++ b/QWebChannel/CallEachWithJs.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2021/12/15
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CallEachWithJs.py
+@description: 与JS之间的互相调用
+"""
+
+import os
+
+from PyQt5.QtCore import QUrl, pyqtSlot
+from PyQt5.QtGui import QDesktopServices
+from PyQt5.QtWidgets import (QApplication, QLineEdit, QPushButton, QVBoxLayout,
+ QWidget)
+
+from Lib.WebChannelObject import WebChannelObject
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.m_obj = WebChannelObject(self)
+ # 注册该窗口,可以访问该窗口的属性,槽函数,信号
+ # https://doc.qt.io/qt-5/qwidget.html#properties
+ # https://doc.qt.io/qt-5/qwidget.html#signals
+ # https://doc.qt.io/qt-5/qwidget.html#public-slots
+ self.m_obj.registerObject('qtwindow', self)
+ self.m_obj.start()
+
+ layout = QVBoxLayout(self)
+ self.editTitle = QLineEdit(self, placeholderText='输入标题')
+ layout.addWidget(self.editTitle)
+ layout.addWidget(QPushButton('修改标题', self, clicked=self.onChangeTitle))
+
+ QDesktopServices.openUrl(
+ QUrl.fromLocalFile(
+ os.path.join(os.path.dirname(sys.argv[0] or __file__),
+ 'Data/CallEachWithJs.html')))
+
+ def onChangeTitle(self):
+ self.setWindowTitle(self.editTitle.text())
+
+ # ------- 把非槽函数通过pyqtSlot重新暴露 -------
+ @pyqtSlot(int, int)
+ def resize(self, width, height):
+ super().resize(width, height)
+
+
+if __name__ == '__main__':
+ import cgitb
+ import sys
+
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QWebChannel/Data/CallEachWithJs.html b/QWebChannel/Data/CallEachWithJs.html
new file mode 100644
index 0000000000000000000000000000000000000000..fb5752be5aab70b9c55e8826515902f5f0aa9797
--- /dev/null
+++ b/QWebChannel/Data/CallEachWithJs.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+ 输出:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QWebChannel/Data/qwebchannel.js b/QWebChannel/Data/qwebchannel.js
new file mode 100644
index 0000000000000000000000000000000000000000..32ad51eb7de524d5b7221e32711882d6d3d4957e
--- /dev/null
+++ b/QWebChannel/Data/qwebchannel.js
@@ -0,0 +1,448 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+"use strict";
+
+var QWebChannelMessageTypes = {
+ signal: 1,
+ propertyUpdate: 2,
+ init: 3,
+ idle: 4,
+ debug: 5,
+ invokeMethod: 6,
+ connectToSignal: 7,
+ disconnectFromSignal: 8,
+ setProperty: 9,
+ response: 10,
+};
+
+var QWebChannel = function(transport, initCallback)
+{
+ if (typeof transport !== "object" || typeof transport.send !== "function") {
+ console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." +
+ " Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send));
+ return;
+ }
+
+ var channel = this;
+ this.transport = transport;
+
+ this.send = function(data)
+ {
+ if (typeof(data) !== "string") {
+ data = JSON.stringify(data);
+ }
+ channel.transport.send(data);
+ }
+
+ this.transport.onmessage = function(message)
+ {
+ var data = message.data;
+ if (typeof data === "string") {
+ data = JSON.parse(data);
+ }
+ switch (data.type) {
+ case QWebChannelMessageTypes.signal:
+ channel.handleSignal(data);
+ break;
+ case QWebChannelMessageTypes.response:
+ channel.handleResponse(data);
+ break;
+ case QWebChannelMessageTypes.propertyUpdate:
+ channel.handlePropertyUpdate(data);
+ break;
+ default:
+ console.error("invalid message received:", message.data);
+ break;
+ }
+ }
+
+ this.execCallbacks = {};
+ this.execId = 0;
+ this.exec = function(data, callback)
+ {
+ if (!callback) {
+ // if no callback is given, send directly
+ channel.send(data);
+ return;
+ }
+ if (channel.execId === Number.MAX_VALUE) {
+ // wrap
+ channel.execId = Number.MIN_VALUE;
+ }
+ if (data.hasOwnProperty("id")) {
+ console.error("Cannot exec message with property id: " + JSON.stringify(data));
+ return;
+ }
+ data.id = channel.execId++;
+ channel.execCallbacks[data.id] = callback;
+ channel.send(data);
+ };
+
+ this.objects = {};
+
+ this.handleSignal = function(message)
+ {
+ var object = channel.objects[message.object];
+ if (object) {
+ object.signalEmitted(message.signal, message.args);
+ } else {
+ console.warn("Unhandled signal: " + message.object + "::" + message.signal);
+ }
+ }
+
+ this.handleResponse = function(message)
+ {
+ if (!message.hasOwnProperty("id")) {
+ console.error("Invalid response message received: ", JSON.stringify(message));
+ return;
+ }
+ channel.execCallbacks[message.id](message.data);
+ delete channel.execCallbacks[message.id];
+ }
+
+ this.handlePropertyUpdate = function(message)
+ {
+ message.data.forEach(data => {
+ var object = channel.objects[data.object];
+ if (object) {
+ object.propertyUpdate(data.signals, data.properties);
+ } else {
+ console.warn("Unhandled property update: " + data.object + "::" + data.signal);
+ }
+ });
+ channel.exec({type: QWebChannelMessageTypes.idle});
+ }
+
+ this.debug = function(message)
+ {
+ channel.send({type: QWebChannelMessageTypes.debug, data: message});
+ };
+
+ channel.exec({type: QWebChannelMessageTypes.init}, function(data) {
+ for (const objectName of Object.keys(data)) {
+ new QObject(objectName, data[objectName], channel);
+ }
+
+ // now unwrap properties, which might reference other registered objects
+ for (const objectName of Object.keys(channel.objects)) {
+ channel.objects[objectName].unwrapProperties();
+ }
+
+ if (initCallback) {
+ initCallback(channel);
+ }
+ channel.exec({type: QWebChannelMessageTypes.idle});
+ });
+};
+
+function QObject(name, data, webChannel)
+{
+ this.__id__ = name;
+ webChannel.objects[name] = this;
+
+ // List of callbacks that get invoked upon signal emission
+ this.__objectSignals__ = {};
+
+ // Cache of all properties, updated when a notify signal is emitted
+ this.__propertyCache__ = {};
+
+ var object = this;
+
+ // ----------------------------------------------------------------------
+
+ this.unwrapQObject = function(response)
+ {
+ if (response instanceof Array) {
+ // support list of objects
+ return response.map(qobj => object.unwrapQObject(qobj))
+ }
+ if (!(response instanceof Object))
+ return response;
+
+ if (!response["__QObject*__"] || response.id === undefined) {
+ var jObj = {};
+ for (const propName of Object.keys(response)) {
+ jObj[propName] = object.unwrapQObject(response[propName]);
+ }
+ return jObj;
+ }
+
+ var objectId = response.id;
+ if (webChannel.objects[objectId])
+ return webChannel.objects[objectId];
+
+ if (!response.data) {
+ console.error("Cannot unwrap unknown QObject " + objectId + " without data.");
+ return;
+ }
+
+ var qObject = new QObject( objectId, response.data, webChannel );
+ qObject.destroyed.connect(function() {
+ if (webChannel.objects[objectId] === qObject) {
+ delete webChannel.objects[objectId];
+ // reset the now deleted QObject to an empty {} object
+ // just assigning {} though would not have the desired effect, but the
+ // below also ensures all external references will see the empty map
+ // NOTE: this detour is necessary to workaround QTBUG-40021
+ Object.keys(qObject).forEach(name => delete qObject[name]);
+ }
+ });
+ // here we are already initialized, and thus must directly unwrap the properties
+ qObject.unwrapProperties();
+ return qObject;
+ }
+
+ this.unwrapProperties = function()
+ {
+ for (const propertyIdx of Object.keys(object.__propertyCache__)) {
+ object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]);
+ }
+ }
+
+ function addSignal(signalData, isPropertyNotifySignal)
+ {
+ var signalName = signalData[0];
+ var signalIndex = signalData[1];
+ object[signalName] = {
+ connect: function(callback) {
+ if (typeof(callback) !== "function") {
+ console.error("Bad callback given to connect to signal " + signalName);
+ return;
+ }
+
+ object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
+ object.__objectSignals__[signalIndex].push(callback);
+
+ // only required for "pure" signals, handled separately for properties in propertyUpdate
+ if (isPropertyNotifySignal)
+ return;
+
+ // also note that we always get notified about the destroyed signal
+ if (signalName === "destroyed" || signalName === "destroyed()" || signalName === "destroyed(QObject*)")
+ return;
+
+ // and otherwise we only need to be connected only once
+ if (object.__objectSignals__[signalIndex].length == 1) {
+ webChannel.exec({
+ type: QWebChannelMessageTypes.connectToSignal,
+ object: object.__id__,
+ signal: signalIndex
+ });
+ }
+ },
+ disconnect: function(callback) {
+ if (typeof(callback) !== "function") {
+ console.error("Bad callback given to disconnect from signal " + signalName);
+ return;
+ }
+ object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
+ var idx = object.__objectSignals__[signalIndex].indexOf(callback);
+ if (idx === -1) {
+ console.error("Cannot find connection of signal " + signalName + " to " + callback.name);
+ return;
+ }
+ object.__objectSignals__[signalIndex].splice(idx, 1);
+ if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) {
+ // only required for "pure" signals, handled separately for properties in propertyUpdate
+ webChannel.exec({
+ type: QWebChannelMessageTypes.disconnectFromSignal,
+ object: object.__id__,
+ signal: signalIndex
+ });
+ }
+ }
+ };
+ }
+
+ /**
+ * Invokes all callbacks for the given signalname. Also works for property notify callbacks.
+ */
+ function invokeSignalCallbacks(signalName, signalArgs)
+ {
+ var connections = object.__objectSignals__[signalName];
+ if (connections) {
+ connections.forEach(function(callback) {
+ callback.apply(callback, signalArgs);
+ });
+ }
+ }
+
+ this.propertyUpdate = function(signals, propertyMap)
+ {
+ // update property cache
+ for (const propertyIndex of Object.keys(propertyMap)) {
+ var propertyValue = propertyMap[propertyIndex];
+ object.__propertyCache__[propertyIndex] = this.unwrapQObject(propertyValue);
+ }
+
+ for (const signalName of Object.keys(signals)) {
+ // Invoke all callbacks, as signalEmitted() does not. This ensures the
+ // property cache is updated before the callbacks are invoked.
+ invokeSignalCallbacks(signalName, signals[signalName]);
+ }
+ }
+
+ this.signalEmitted = function(signalName, signalArgs)
+ {
+ invokeSignalCallbacks(signalName, this.unwrapQObject(signalArgs));
+ }
+
+ function addMethod(methodData)
+ {
+ var methodName = methodData[0];
+ var methodIdx = methodData[1];
+
+ // Fully specified methods are invoked by id, others by name for host-side overload resolution
+ var invokedMethod = methodName[methodName.length - 1] === ')' ? methodIdx : methodName
+
+ object[methodName] = function() {
+ var args = [];
+ var callback;
+ var errCallback;
+ for (var i = 0; i < arguments.length; ++i) {
+ var argument = arguments[i];
+ if (typeof argument === "function")
+ callback = argument;
+ else if (argument instanceof QObject && webChannel.objects[argument.__id__] !== undefined)
+ args.push({
+ "id": argument.__id__
+ });
+ else
+ args.push(argument);
+ }
+
+ var result;
+ // during test, webChannel.exec synchronously calls the callback
+ // therefore, the promise must be constucted before calling
+ // webChannel.exec to ensure the callback is set up
+ if (!callback && (typeof(Promise) === 'function')) {
+ result = new Promise(function(resolve, reject) {
+ callback = resolve;
+ errCallback = reject;
+ });
+ }
+
+ webChannel.exec({
+ "type": QWebChannelMessageTypes.invokeMethod,
+ "object": object.__id__,
+ "method": invokedMethod,
+ "args": args
+ }, function(response) {
+ if (response !== undefined) {
+ var result = object.unwrapQObject(response);
+ if (callback) {
+ (callback)(result);
+ }
+ } else if (errCallback) {
+ (errCallback)();
+ }
+ });
+
+ return result;
+ };
+ }
+
+ function bindGetterSetter(propertyInfo)
+ {
+ var propertyIndex = propertyInfo[0];
+ var propertyName = propertyInfo[1];
+ var notifySignalData = propertyInfo[2];
+ // initialize property cache with current value
+ // NOTE: if this is an object, it is not directly unwrapped as it might
+ // reference other QObject that we do not know yet
+ object.__propertyCache__[propertyIndex] = propertyInfo[3];
+
+ if (notifySignalData) {
+ if (notifySignalData[0] === 1) {
+ // signal name is optimized away, reconstruct the actual name
+ notifySignalData[0] = propertyName + "Changed";
+ }
+ addSignal(notifySignalData, true);
+ }
+
+ Object.defineProperty(object, propertyName, {
+ configurable: true,
+ get: function () {
+ var propertyValue = object.__propertyCache__[propertyIndex];
+ if (propertyValue === undefined) {
+ // This shouldn't happen
+ console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__);
+ }
+
+ return propertyValue;
+ },
+ set: function(value) {
+ if (value === undefined) {
+ console.warn("Property setter for " + propertyName + " called with undefined value!");
+ return;
+ }
+ object.__propertyCache__[propertyIndex] = value;
+ var valueToSend = value;
+ if (valueToSend instanceof QObject && webChannel.objects[valueToSend.__id__] !== undefined)
+ valueToSend = { "id": valueToSend.__id__ };
+ webChannel.exec({
+ "type": QWebChannelMessageTypes.setProperty,
+ "object": object.__id__,
+ "property": propertyIndex,
+ "value": valueToSend
+ });
+ }
+ });
+
+ }
+
+ // ----------------------------------------------------------------------
+
+ data.methods.forEach(addMethod);
+
+ data.properties.forEach(bindGetterSetter);
+
+ data.signals.forEach(function(signal) { addSignal(signal, false); });
+
+ Object.assign(object, data.enums);
+}
+
+//required for use with nodejs
+if (typeof module === 'object') {
+ module.exports = {
+ QWebChannel: QWebChannel
+ };
+}
diff --git a/QWebChannel/Lib/WebChannelObject.py b/QWebChannel/Lib/WebChannelObject.py
new file mode 100644
index 0000000000000000000000000000000000000000..7fe5e9edb3d9936000cd6ce4bdab048ecc56771c
--- /dev/null
+++ b/QWebChannel/Lib/WebChannelObject.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2021/12/15
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: WebChannelObject.py
+@description: 交互对象,需要继承QObject并暴露接口
+"""
+
+from PyQt5.QtCore import (QJsonDocument, QJsonParseError, QObject,
+ pyqtProperty, pyqtSlot)
+from PyQt5.QtNetwork import QHostAddress
+from PyQt5.QtWebChannel import QWebChannel, QWebChannelAbstractTransport
+from PyQt5.QtWebSockets import QWebSocketServer
+
+
+class WebSocketTransport(QWebChannelAbstractTransport):
+
+ def __init__(self, socket, *args, **kwargs):
+ super(WebSocketTransport, self).__init__(*args, **kwargs)
+ self.m_socket = socket
+ self.m_socket.textMessageReceived.connect(self.textMessageReceived)
+ self.m_socket.disconnected.connect(self.deleteLater)
+
+ def sendMessage(self, message):
+ print('sendMessage:', message)
+ self.m_socket.sendTextMessage(
+ QJsonDocument(message).toJson(QJsonDocument.Compact).data().decode(
+ 'utf-8', errors='ignore'))
+
+ def textMessageReceived(self, message):
+ print('textMessageReceived:', message)
+ error = QJsonParseError()
+ json = QJsonDocument.fromJson(message.encode('utf-8', errors='ignore'),
+ error)
+ if error.error:
+ print('Failed to parse message:{}, Error is:{}'.format(
+ message, error.errorString()))
+ return
+ if not json.isObject():
+ print('Received JSON message that is not an object:{}'.format(
+ message))
+ return
+ self.messageReceived.emit(json.object(), self)
+
+
+class WebChannelObject(QObject):
+
+ def __init__(self, *args, **kwargs):
+ super(WebChannelObject, self).__init__(*args, **kwargs)
+ # 内部属性供外部调用
+ self._intValue = 0
+ self._floatValue = 0.0
+ self._boolValue = False
+ self._strValue = ''
+ # 设置数组或者字典有一定问题
+ # self._listValue = []
+ # self._mapValue = {}
+
+ # webchannel对象
+ self.m_webchannel = QWebChannel(self)
+ # 这里默认注册自己,这里使用了类名作为名称
+ self.registerObject(self.__class__.__name__, self)
+ # websocket服务
+ self.m_clients = {}
+ self.m_server = QWebSocketServer(self.__class__.__name__,
+ QWebSocketServer.NonSecureMode, self)
+
+ def registerObject(self, name, obj):
+ """注册对象
+ @param name: 名称
+ @type name: str
+ @param obj: 对象
+ @type obj: QObject
+ """
+ self.m_webchannel.registerObject(name, obj)
+
+ def registerObjects(self, objects):
+ """注册多个对象
+ @param objects: 对象列表
+ @type objects: list
+ """
+ for name, obj in objects:
+ self.registerObject(name, obj)
+
+ def deregisterObject(self, obj):
+ """注销对象
+ @param obj: 对象
+ @type obj: QObject
+ """
+ self.m_webchannel.deregisterObject(obj)
+
+ def deregisterObjects(self, objects):
+ """注销多个对象
+ @param objects: 对象列表
+ @type objects: list
+ """
+ for obj in objects:
+ self.deregisterObject(obj)
+
+ def start(self, port=12345):
+ """启动服务
+ @param port: 端口
+ @type port: int
+ """
+ if not self.m_server.listen(QHostAddress.Any, port):
+ raise Exception(
+ 'Failed to create WebSocket server on port {}'.format(port))
+
+ print('WebSocket server listening on port {}'.format(port))
+ # 新连接信号
+ self.m_server.newConnection.connect(self._handleNewConnection)
+
+ def stop(self):
+ """停止服务"""
+ self.m_server.close()
+
+ def _handleNewConnection(self):
+ """新连接"""
+ socket = self.m_server.nextPendingConnection()
+ print('New WebSocket connection from {}'.format(
+ socket.peerAddress().toString()))
+ # 连接关闭信号
+ socket.disconnected.connect(self._handleDisconnected)
+ transport = WebSocketTransport(socket)
+ self.m_clients[socket] = transport
+ self.m_webchannel.connectTo(transport)
+
+ def _handleDisconnected(self):
+ """连接关闭"""
+ socket = self.sender()
+ print('WebSocket connection from {} closed'.format(
+ socket.peerAddress()))
+ if socket in self.m_clients:
+ self.m_clients.pop(socket)
+ socket.deleteLater()
+
+ # ------- 下面是注册属性的方法 -------
+
+ @pyqtProperty(int)
+ def intValue(self):
+ return self._intValue
+
+ @intValue.setter
+ def intValue(self, value):
+ self._intValue = value
+
+ @pyqtProperty(float)
+ def floatValue(self):
+ return self._floatValue
+
+ @floatValue.setter
+ def floatValue(self, value):
+ self._floatValue = value
+
+ @pyqtProperty(bool)
+ def boolValue(self):
+ return self._boolValue
+
+ @boolValue.setter
+ def boolValue(self, value):
+ self._boolValue = value
+
+ @pyqtProperty(str)
+ def strValue(self):
+ return self._strValue
+
+ @strValue.setter
+ def strValue(self, value):
+ self._strValue = value
+
+ # @pyqtProperty(list)
+ # def listValue(self):
+ # return self._listValue
+
+ # @listValue.setter
+ # def listValue(self, value):
+ # self._listValue = value
+
+ # @pyqtProperty(dict)
+ # def mapValue(self):
+ # return self._mapValue
+
+ # @mapValue.setter
+ # def mapValue(self, value):
+ # self._mapValue = value
+
+ # ------- 下面是注册函数的方法 -------
+ # ------- 如果有返回值一定要注明 result=返回类型 -------
+
+ @pyqtSlot(int, int, result=int)
+ def testAdd(self, a, b):
+ return a + b
diff --git a/QWebChannel/Lib/__init__.py b/QWebChannel/Lib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QWebChannel/README.en.md b/QWebChannel/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QWebChannel/README.md b/QWebChannel/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..51618de783a868a6e0aeba5a3953a19a18fb88fd
--- /dev/null
+++ b/QWebChannel/README.md
@@ -0,0 +1,12 @@
+# QWebChannel
+
+- 目录
+ - [和Js互相调用](#1和Js互相调用)
+
+## 1、和Js互相调用
+[运行 CallEachWithJs.py](CallEachWithJs.py)
+
+通过`qwebchannel.js`和`QWebChannel.registerObject`通过中间件`WebSocket`进行对象和Javascript的交互(类似于json rpc)
+该方法类似与`QWebEngineView`中的例子,同时该demo也适用与nodejs。
+
+
\ No newline at end of file
diff --git a/QWebChannel/ScreenShot/CallEachWithJs.gif b/QWebChannel/ScreenShot/CallEachWithJs.gif
new file mode 100644
index 0000000000000000000000000000000000000000..8ff3a88e3c72509139b877dbdb047d597c5fe090
Binary files /dev/null and b/QWebChannel/ScreenShot/CallEachWithJs.gif differ
diff --git a/QWebEngineView/BlockRequest.py b/QWebEngineView/BlockRequest.py
new file mode 100644
index 0000000000000000000000000000000000000000..27b86d786a582bf91877919cea6b5f4cf1dcf834
--- /dev/null
+++ b/QWebEngineView/BlockRequest.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年9月24日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: BlockRequest
+@description: 拦截请求
+"""
+
+try:
+ from PyQt5.QtCore import QUrl
+ from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
+ from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtCore import QUrl
+ from PySide2.QtWebEngineCore import QWebEngineUrlRequestInterceptor
+ from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
+ from PySide2.QtWidgets import QApplication
+
+
+class RequestInterceptor(QWebEngineUrlRequestInterceptor):
+
+ def interceptRequest(self, info):
+ url = info.requestUrl().toString()
+ if url.find('pos.baidu.com') > -1 and url.find('ltu=') > -1:
+ # 拦截百度联盟的广告
+ print('block:', url)
+ info.block(True)
+
+
+class Window(QWebEngineView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(800, 600)
+ QWebEngineProfile.defaultProfile().setRequestInterceptor(RequestInterceptor(self))
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ w.load(QUrl('https://so.csdn.net/so/search/s.do?q=Qt&t=blog'))
+ sys.exit(app.exec_())
diff --git a/QWebEngineView/BlockRequestData.py b/QWebEngineView/BlockRequestData.py
new file mode 100644
index 0000000000000000000000000000000000000000..eea7a02413b82992e8436fb9cde068f7db3f40e1
--- /dev/null
+++ b/QWebEngineView/BlockRequestData.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020年2月18日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: BlockRequestData
+@description: 拦截请求内容
+"""
+
+try:
+ from PyQt5.QtCore import QUrl, QFile, QIODevice, QByteArray
+ from PyQt5.QtWidgets import QApplication
+ from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler, \
+ QWebEngineUrlRequestInterceptor, QWebEngineUrlScheme # @UnresolvedImport
+ from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
+except ImportError:
+ from PySide2.QtCore import QUrl, QFile, QIODevice, QByteArray
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtWebEngineCore import QWebEngineUrlSchemeHandler, \
+ QWebEngineUrlRequestInterceptor, QWebEngineUrlScheme # @UnresolvedImport
+ from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
+
+
+# 自定义url协议头
+class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
+
+ def requestStarted(self, job):
+ url = job.requestUrl().toString()
+ if url == 'myurl://png':
+ file = QFile('Data/app.png', job)
+ file.open(QIODevice.ReadOnly)
+ job.reply(b'image/png', file)
+
+
+# 请求拦截器
+
+
+class RequestInterceptor(QWebEngineUrlRequestInterceptor):
+
+ def interceptRequest(self, info):
+ url = info.requestUrl().toString()
+ # 这里演示只是拦截所有png图片,可自由发挥比如拦截js文件,修改后再返回
+ if url.endswith('.png'):
+ # 原理在于重定向到自己的url协议里
+ info.redirect(QUrl('myurl://png'))
+
+
+class Window(QWebEngineView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(800, 600)
+
+ # 首先获取默认的url协议
+ h1 = QWebEngineUrlScheme.schemeByName(QByteArray(b'http'))
+ h2 = QWebEngineUrlScheme.schemeByName(QByteArray(b'https'))
+
+ # 这里需要修改增加本地文件和跨域支持
+ CorsEnabled = 0x80 # 5.14才增加
+ h1.setFlags(h1.flags() |
+ QWebEngineUrlScheme.SecureScheme |
+ QWebEngineUrlScheme.LocalScheme |
+ QWebEngineUrlScheme.LocalAccessAllowed |
+ CorsEnabled)
+ h2.setFlags(h2.flags() |
+ QWebEngineUrlScheme.SecureScheme |
+ QWebEngineUrlScheme.LocalScheme |
+ QWebEngineUrlScheme.LocalAccessAllowed |
+ CorsEnabled)
+
+ # 安装url拦截器和自定义url协议处理
+ de = QWebEngineProfile.defaultProfile() # @UndefinedVariable
+ de.setRequestInterceptor(RequestInterceptor(self))
+ de.installUrlSchemeHandler(QByteArray(b'myurl'), UrlSchemeHandler(self))
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ w.load(QUrl('https://www.baidu.com/'))
+ sys.exit(app.exec_())
diff --git a/QWebEngineView/Data/JsSignals.html b/QWebEngineView/Data/JsSignals.html
new file mode 100644
index 0000000000000000000000000000000000000000..9fb0bb32a6ed918c89ae666cdbcf855f1025d460
--- /dev/null
+++ b/QWebEngineView/Data/JsSignals.html
@@ -0,0 +1,46 @@
+
+
+
+ 测试
+
+
+
+
+ 1、测试修改窗口属性
+ windowTitle 是Qt窗口本身的属性, 当标题被修改后会触发本身的windowTitleChanged信号
+
+
+ 测试修改标题
+
+ 2、调用Python中的方法callFromJs
+ callFromJs(str) 函数是由Python代码中定义, 通过@pyqtSlot(str)装饰器暴露出来
+ Bridge.callFromJs('test')
+
+ 3、发送自定义信号
+ 点击底部的按钮将会发送customSignal信号出来,网页中收到该信号将会在下面日志框输出内容
+
+ 日志
+
+
+
diff --git a/QWebEngineView/Data/app.png b/QWebEngineView/Data/app.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f50561f192ef269b0bd0b8d270ca89802937118
Binary files /dev/null and b/QWebEngineView/Data/app.png differ
diff --git a/QWebEngineView/Data/html2canvas.min.js b/QWebEngineView/Data/html2canvas.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..c1b8cc998eb989ed8e9c3c561ae9c04c43f5f1a8
--- /dev/null
+++ b/QWebEngineView/Data/html2canvas.min.js
@@ -0,0 +1,20 @@
+/*!
+ * html2canvas 1.0.0-rc.3
+ * Copyright (c) 2019 Niklas von Hertzen
+ * Released under MIT License
+ */
+!function(A,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(A=A||self).html2canvas=e();window.html2canvas = e()}(this,function(){"use strict";
+/*! *****************************************************************************
+ Copyright (c) Microsoft Corporation. All rights reserved.
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+ this file except in compliance with the License. You may obtain a copy of the
+ License at http://www.apache.org/licenses/LICENSE-2.0
+
+ THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+ WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+ MERCHANTABLITY OR NON-INFRINGEMENT.
+
+ See the Apache Version 2.0 License for specific language governing permissions
+ and limitations under the License.
+ ***************************************************************************** */var r=function(A,e){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(A,e){A.__proto__=e}||function(A,e){for(var t in e)e.hasOwnProperty(t)&&(A[t]=e[t])})(A,e)};function A(A,e){function t(){this.constructor=A}r(A,e),A.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}var K=function(){return(K=Object.assign||function(A){for(var e,t=1,r=arguments.length;ts[0]&&e[1]>10),s%1024+56320)),(B+1===t||16384>5])<<2)+(31&A),this.data[e];if(A<=65535)return e=((e=this.index[2048+(A-55296>>5)])<<2)+(31&A),this.data[e];if(A>11),e=this.index[e],e+=A>>5&63,e=((e=this.index[e])<<2)+(31&A),this.data[e];if(A<=1114111)return this.data[this.highValueIndex]}return this.errorValue},i);function i(A,e,t,r,B,n){this.initialValue=A,this.errorValue=e,this.highStart=t,this.highValueIndex=r,this.index=B,this.data=n}function C(A,e,t,r){var B=r[t];if(Array.isArray(A)?-1!==A.indexOf(B):A===B)for(var n=t;n<=r.length;){if((i=r[++n])===e)return!0;if(i!==H)break}if(B===H)for(n=t;0>4,c[i++]=(15&r)<<4|B>>2,c[i++]=(3&B)<<6|63&n;return a}("KwAAAAAAAAAACA4AIDoAAPAfAAACAAAAAAAIABAAGABAAEgAUABYAF4AZgBeAGYAYABoAHAAeABeAGYAfACEAIAAiACQAJgAoACoAK0AtQC9AMUAXgBmAF4AZgBeAGYAzQDVAF4AZgDRANkA3gDmAOwA9AD8AAQBDAEUARoBIgGAAIgAJwEvATcBPwFFAU0BTAFUAVwBZAFsAXMBewGDATAAiwGTAZsBogGkAawBtAG8AcIBygHSAdoB4AHoAfAB+AH+AQYCDgIWAv4BHgImAi4CNgI+AkUCTQJTAlsCYwJrAnECeQKBAk0CiQKRApkCoQKoArACuALAAsQCzAIwANQC3ALkAjAA7AL0AvwCAQMJAxADGAMwACADJgMuAzYDPgOAAEYDSgNSA1IDUgNaA1oDYANiA2IDgACAAGoDgAByA3YDfgOAAIQDgACKA5IDmgOAAIAAogOqA4AAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAK8DtwOAAIAAvwPHA88D1wPfAyAD5wPsA/QD/AOAAIAABAQMBBIEgAAWBB4EJgQuBDMEIAM7BEEEXgBJBCADUQRZBGEEaQQwADAAcQQ+AXkEgQSJBJEEgACYBIAAoASoBK8EtwQwAL8ExQSAAIAAgACAAIAAgACgAM0EXgBeAF4AXgBeAF4AXgBeANUEXgDZBOEEXgDpBPEE+QQBBQkFEQUZBSEFKQUxBTUFPQVFBUwFVAVcBV4AYwVeAGsFcwV7BYMFiwWSBV4AmgWgBacFXgBeAF4AXgBeAKsFXgCyBbEFugW7BcIFwgXIBcIFwgXQBdQF3AXkBesF8wX7BQMGCwYTBhsGIwYrBjMGOwZeAD8GRwZNBl4AVAZbBl4AXgBeAF4AXgBeAF4AXgBeAF4AXgBeAGMGXgBqBnEGXgBeAF4AXgBeAF4AXgBeAF4AXgB5BoAG4wSGBo4GkwaAAIADHgR5AF4AXgBeAJsGgABGA4AAowarBrMGswagALsGwwbLBjAA0wbaBtoG3QbaBtoG2gbaBtoG2gblBusG8wb7BgMHCwcTBxsHCwcjBysHMAc1BzUHOgdCB9oGSgdSB1oHYAfaBloHaAfaBlIH2gbaBtoG2gbaBtoG2gbaBjUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHbQdeAF4ANQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQd1B30HNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1B4MH2gaKB68EgACAAIAAgACAAIAAgACAAI8HlwdeAJ8HpweAAIAArwe3B14AXgC/B8UHygcwANAH2AfgB4AA6AfwBz4B+AcACFwBCAgPCBcIogEYAR8IJwiAAC8INwg/CCADRwhPCFcIXwhnCEoDGgSAAIAAgABvCHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIhAiLCI4IMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwAJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlggwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAANQc1BzUHNQc1BzUHNQc1BzUHNQc1B54INQc1B6II2gaqCLIIugiAAIAAvgjGCIAAgACAAIAAgACAAIAAgACAAIAAywiHAYAA0wiAANkI3QjlCO0I9Aj8CIAAgACAAAIJCgkSCRoJIgknCTYHLwk3CZYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiAAIAAAAFAAXgBeAGAAcABeAHwAQACQAKAArQC9AJ4AXgBeAE0A3gBRAN4A7AD8AMwBGgEAAKcBNwEFAUwBXAF4QkhCmEKnArcCgAHHAsABz4LAAcABwAHAAd+C6ABoAG+C/4LAAcABwAHAAc+DF4MAAcAB54M3gweDV4Nng3eDaABoAGgAaABoAGgAaABoAGgAaABoAGgAaABoAGgAaABoAGgAaABoAEeDqABVg6WDqABoQ6gAaABoAHXDvcONw/3DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DncPAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcAB7cPPwlGCU4JMACAAIAAgABWCV4JYQmAAGkJcAl4CXwJgAkwADAAMAAwAIgJgACLCZMJgACZCZ8JowmrCYAAswkwAF4AXgB8AIAAuwkABMMJyQmAAM4JgADVCTAAMAAwADAAgACAAIAAgACAAIAAgACAAIAAqwYWBNkIMAAwADAAMADdCeAJ6AnuCR4E9gkwAP4JBQoNCjAAMACAABUK0wiAAB0KJAosCjQKgAAwADwKQwqAAEsKvQmdCVMKWwowADAAgACAALcEMACAAGMKgABrCjAAMAAwADAAMAAwADAAMAAwADAAMAAeBDAAMAAwADAAMAAwADAAMAAwADAAMAAwAIkEPQFzCnoKiQSCCooKkAqJBJgKoAqkCokEGAGsCrQKvArBCjAAMADJCtEKFQHZCuEK/gHpCvEKMAAwADAAMACAAIwE+QowAIAAPwEBCzAAMAAwADAAMACAAAkLEQswAIAAPwEZCyELgAAOCCkLMAAxCzkLMAAwADAAMAAwADAAXgBeAEELMAAwADAAMAAwADAAMAAwAEkLTQtVC4AAXAtkC4AAiQkwADAAMAAwADAAMAAwADAAbAtxC3kLgAuFC4sLMAAwAJMLlwufCzAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAApwswADAAMACAAIAAgACvC4AAgACAAIAAgACAALcLMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAvwuAAMcLgACAAIAAgACAAIAAyguAAIAAgACAAIAA0QswADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAANkLgACAAIAA4AswADAAMAAwADAAMAAwADAAMAAwADAAMAAwAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACJCR4E6AswADAAhwHwC4AA+AsADAgMEAwwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMACAAIAAGAwdDCUMMAAwAC0MNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQw1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHPQwwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADUHNQc1BzUHNQc1BzUHNQc2BzAAMAA5DDUHNQc1BzUHNQc1BzUHNQc1BzUHNQdFDDAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAgACAAIAATQxSDFoMMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwAF4AXgBeAF4AXgBeAF4AYgxeAGoMXgBxDHkMfwxeAIUMXgBeAI0MMAAwADAAMAAwAF4AXgCVDJ0MMAAwADAAMABeAF4ApQxeAKsMswy7DF4Awgy9DMoMXgBeAF4AXgBeAF4AXgBeAF4AXgDRDNkMeQBqCeAM3Ax8AOYM7Az0DPgMXgBeAF4AXgBeAF4AXgBeAF4AXgBeAF4AXgBeAF4AXgCgAAANoAAHDQ4NFg0wADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAeDSYNMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwAIAAgACAAIAAgACAAC4NMABeAF4ANg0wADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwAD4NRg1ODVYNXg1mDTAAbQ0wADAAMAAwADAAMAAwADAA2gbaBtoG2gbaBtoG2gbaBnUNeg3CBYANwgWFDdoGjA3aBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gaUDZwNpA2oDdoG2gawDbcNvw3HDdoG2gbPDdYN3A3fDeYN2gbsDfMN2gbaBvoN/g3aBgYODg7aBl4AXgBeABYOXgBeACUG2gYeDl4AJA5eACwO2w3aBtoGMQ45DtoG2gbaBtoGQQ7aBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gZJDjUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1B1EO2gY1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQdZDjUHNQc1BzUHNQc1B2EONQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHaA41BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1B3AO2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gY1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1B2EO2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gZJDtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBkkOeA6gAKAAoAAwADAAMAAwAKAAoACgAKAAoACgAKAAgA4wADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAD//wQABAAEAAQABAAEAAQABAAEAA0AAwABAAEAAgAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAKABMAFwAeABsAGgAeABcAFgASAB4AGwAYAA8AGAAcAEsASwBLAEsASwBLAEsASwBLAEsAGAAYAB4AHgAeABMAHgBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAFgAbABIAHgAeAB4AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQABYADQARAB4ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsABAAEAAQABAAEAAUABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAkAFgAaABsAGwAbAB4AHQAdAB4ATwAXAB4ADQAeAB4AGgAbAE8ATwAOAFAAHQAdAB0ATwBPABcATwBPAE8AFgBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB0AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgBQAB4AHgAeAB4AUABQAFAAUAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAB4AHgAeAFAATwBAAE8ATwBPAEAATwBQAFAATwBQAB4AHgAeAB4AHgAeAB0AHQAdAB0AHgAdAB4ADgBQAFAAUABQAFAAHgAeAB4AHgAeAB4AHgBQAB4AUAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4ABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAJAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAkACQAJAAkACQAJAAkABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAeAB4AHgAeAFAAHgAeAB4AKwArAFAAUABQAFAAGABQACsAKwArACsAHgAeAFAAHgBQAFAAUAArAFAAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4ABAAEAAQABAAEAAQABAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAUAAeAB4AHgAeAB4AHgArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwAYAA0AKwArAB4AHgAbACsABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQADQAEAB4ABAAEAB4ABAAEABMABAArACsAKwArACsAKwArACsAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAKwArACsAKwArAFYAVgBWAB4AHgArACsAKwArACsAKwArACsAKwArACsAHgAeAB4AHgAeAB4AHgAeAB4AGgAaABoAGAAYAB4AHgAEAAQABAAEAAQABAAEAAQABAAEAAQAEwAEACsAEwATAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABABLAEsASwBLAEsASwBLAEsASwBLABoAGQAZAB4AUABQAAQAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQABMAUAAEAAQABAAEAAQABAAEAB4AHgAEAAQABAAEAAQABABQAFAABAAEAB4ABAAEAAQABABQAFAASwBLAEsASwBLAEsASwBLAEsASwBQAFAAUAAeAB4AUAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwAeAFAABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAAQABAAEAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAAQAUABQAB4AHgAYABMAUAArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAFAABAAEAAQABAAEAFAABAAEAAQAUAAEAAQABAAEAAQAKwArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAArACsAHgArAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAeAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABABQAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAFAABAAEAAQABAAEAAQABABQAFAAUABQAFAAUABQAFAAUABQAAQABAANAA0ASwBLAEsASwBLAEsASwBLAEsASwAeAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQAKwBQAFAAUABQAFAAUABQAFAAKwArAFAAUAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUAArAFAAKwArACsAUABQAFAAUAArACsABABQAAQABAAEAAQABAAEAAQAKwArAAQABAArACsABAAEAAQAUAArACsAKwArACsAKwArACsABAArACsAKwArAFAAUAArAFAAUABQAAQABAArACsASwBLAEsASwBLAEsASwBLAEsASwBQAFAAGgAaAFAAUABQAFAAUABMAB4AGwBQAB4AKwArACsABAAEAAQAKwBQAFAAUABQAFAAUAArACsAKwArAFAAUAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUAArAFAAUAArAFAAUAArAFAAUAArACsABAArAAQABAAEAAQABAArACsAKwArAAQABAArACsABAAEAAQAKwArACsABAArACsAKwArACsAKwArAFAAUABQAFAAKwBQACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwAEAAQAUABQAFAABAArACsAKwArACsAKwArACsAKwArACsABAAEAAQAKwBQAFAAUABQAFAAUABQAFAAUAArAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUAArAFAAUAArAFAAUABQAFAAUAArACsABABQAAQABAAEAAQABAAEAAQABAArAAQABAAEACsABAAEAAQAKwArAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAUABQAAQABAArACsASwBLAEsASwBLAEsASwBLAEsASwAeABsAKwArACsAKwArACsAKwBQAAQABAAEAAQABAAEACsABAAEAAQAKwBQAFAAUABQAFAAUABQAFAAKwArAFAAUAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQAKwArAAQABAArACsABAAEAAQAKwArACsAKwArACsAKwArAAQABAArACsAKwArAFAAUAArAFAAUABQAAQABAArACsASwBLAEsASwBLAEsASwBLAEsASwAeAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwAEAFAAKwBQAFAAUABQAFAAUAArACsAKwBQAFAAUAArAFAAUABQAFAAKwArACsAUABQACsAUAArAFAAUAArACsAKwBQAFAAKwArACsAUABQAFAAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwAEAAQABAAEAAQAKwArACsABAAEAAQAKwAEAAQABAAEACsAKwBQACsAKwArACsAKwArAAQAKwArACsAKwArACsAKwArACsAKwBLAEsASwBLAEsASwBLAEsASwBLAFAAUABQAB4AHgAeAB4AHgAeABsAHgArACsAKwArACsABAAEAAQABAArAFAAUABQAFAAUABQAFAAUAArAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArAFAABAAEAAQABAAEAAQABAArAAQABAAEACsABAAEAAQABAArACsAKwArACsAKwArAAQABAArAFAAUABQACsAKwArACsAKwBQAFAABAAEACsAKwBLAEsASwBLAEsASwBLAEsASwBLACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAB4AUAAEAAQABAArAFAAUABQAFAAUABQAFAAUAArAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQACsAKwAEAFAABAAEAAQABAAEAAQABAArAAQABAAEACsABAAEAAQABAArACsAKwArACsAKwArAAQABAArACsAKwArACsAKwArAFAAKwBQAFAABAAEACsAKwBLAEsASwBLAEsASwBLAEsASwBLACsAUABQACsAKwArACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAFAABAAEAAQABAAEAAQABAArAAQABAAEACsABAAEAAQABABQAB4AKwArACsAKwBQAFAAUAAEAFAAUABQAFAAUABQAFAAUABQAFAABAAEACsAKwBLAEsASwBLAEsASwBLAEsASwBLAFAAUABQAFAAUABQAFAAUABQABoAUABQAFAAUABQAFAAKwArAAQABAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQACsAUAArACsAUABQAFAAUABQAFAAUAArACsAKwAEACsAKwArACsABAAEAAQABAAEAAQAKwAEACsABAAEAAQABAAEAAQABAAEACsAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArAAQABAAeACsAKwArACsAKwArACsAKwArACsAKwArAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXAAqAFwAXAAqACoAKgAqACoAKgAqACsAKwArACsAGwBcAFwAXABcAFwAXABcACoAKgAqACoAKgAqACoAKgAeAEsASwBLAEsASwBLAEsASwBLAEsADQANACsAKwArACsAKwBcAFwAKwBcACsAKwBcAFwAKwBcACsAKwBcACsAKwArACsAKwArAFwAXABcAFwAKwBcAFwAXABcAFwAXABcACsAXABcAFwAKwBcACsAXAArACsAXABcACsAXABcAFwAXAAqAFwAXAAqACoAKgAqACoAKgArACoAKgBcACsAKwBcAFwAXABcAFwAKwBcACsAKgAqACoAKgAqACoAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArAFwAXABcAFwAUAAOAA4ADgAOAB4ADgAOAAkADgAOAA0ACQATABMAEwATABMACQAeABMAHgAeAB4ABAAEAB4AHgAeAB4AHgAeAEsASwBLAEsASwBLAEsASwBLAEsAUABQAFAAUABQAFAAUABQAFAAUAANAAQAHgAEAB4ABAAWABEAFgARAAQABABQAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAANAAQABAAEAAQABAANAAQABABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAAQABAAEACsABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsADQANAB4AHgAeAB4AHgAeAAQAHgAeAB4AHgAeAB4AKwAeAB4ADgAOAA0ADgAeAB4AHgAeAB4ACQAJACsAKwArACsAKwBcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqAFwASwBLAEsASwBLAEsASwBLAEsASwANAA0AHgAeAB4AHgBcAFwAXABcAFwAXAAqACoAKgAqAFwAXABcAFwAKgAqACoAXAAqACoAKgBcAFwAKgAqACoAKgAqACoAKgBcAFwAXAAqACoAKgAqAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAKgAqACoAKgAqACoAKgAqACoAKgAqACoAXAAqAEsASwBLAEsASwBLAEsASwBLAEsAKgAqACoAKgAqACoAUABQAFAAUABQAFAAKwBQACsAKwArACsAKwBQACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAFAAUABQAFAAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAUABQAFAAUABQAFAAUABQAFAAKwBQAFAAUABQACsAKwBQAFAAUABQAFAAUABQACsAUAArAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUAArACsAUABQAFAAUABQAFAAUAArAFAAKwBQAFAAUABQACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwAEAAQABAAeAA0AHgAeAB4AHgAeAB4AHgBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAB4AHgAeAB4AHgAeAB4AHgAeACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAFAAUABQAFAAUABQACsAKwANAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAB4AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAA0AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQABYAEQArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAADQANAA0AUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAABAAEAAQAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAA0ADQArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQAKwArACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQACsABAAEACsAKwArACsAKwArACsAKwArACsAKwArAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXAAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoADQANABUAXAANAB4ADQAbAFwAKgArACsASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArAB4AHgATABMADQANAA4AHgATABMAHgAEAAQABAAJACsASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAUABQAFAAUABQAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABABQACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwArACsABAAEAAQABAAEAAQABAAEAAQABAAEAAQAKwArACsAKwAeACsAKwArABMAEwBLAEsASwBLAEsASwBLAEsASwBLAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcACsAKwBcAFwAXABcAFwAKwArACsAKwArACsAKwArACsAKwArAFwAXABcAFwAXABcAFwAXABcAFwAXABcACsAKwArACsAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwBcACsAKwArACoAKgBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEACsAKwAeAB4AXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAKgAqACoAKgAqACoAKgAqACoAKgArACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgArACsABABLAEsASwBLAEsASwBLAEsASwBLACsAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwArACsAKgAqACoAKgAqACoAKgBcACoAKgAqACoAKgAqACsAKwAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArAAQABAAEAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQAUABQAFAAUABQAFAAUAArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsADQANAB4ADQANAA0ADQAeAB4AHgAeAB4AHgAeAB4AHgAeAAQABAAEAAQABAAEAAQABAAEAB4AHgAeAB4AHgAeAB4AHgAeACsAKwArAAQABAAEAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAUABQAEsASwBLAEsASwBLAEsASwBLAEsAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArACsAKwArACsAKwArACsAHgAeAB4AHgBQAFAAUABQAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArACsAKwANAA0ADQANAA0ASwBLAEsASwBLAEsASwBLAEsASwArACsAKwBQAFAAUABLAEsASwBLAEsASwBLAEsASwBLAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAANAA0AUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgAeAB4AHgAeAB4AHgArACsAKwArACsAKwArACsABAAEAAQAHgAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAFAAUABQAFAABABQAFAAUABQAAQABAAEAFAAUAAEAAQABAArACsAKwArACsAKwAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAKwAEAAQABAAEAAQAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArACsAUABQAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUAArAFAAKwBQACsAUAArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACsAHgAeAB4AHgAeAB4AHgAeAFAAHgAeAB4AUABQAFAAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAFAAUABQAFAAKwArAB4AHgAeAB4AHgAeACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArACsAUABQAFAAKwAeAB4AHgAeAB4AHgAeAA4AHgArAA0ADQANAA0ADQANAA0ACQANAA0ADQAIAAQACwAEAAQADQAJAA0ADQAMAB0AHQAeABcAFwAWABcAFwAXABYAFwAdAB0AHgAeABQAFAAUAA0AAQABAAQABAAEAAQABAAJABoAGgAaABoAGgAaABoAGgAeABcAFwAdABUAFQAeAB4AHgAeAB4AHgAYABYAEQAVABUAFQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgANAB4ADQANAA0ADQAeAA0ADQANAAcAHgAeAB4AHgArAAQABAAEAAQABAAEAAQABAAEAAQAUABQACsAKwBPAFAAUABQAFAAUAAeAB4AHgAWABEATwBQAE8ATwBPAE8AUABQAFAAUABQAB4AHgAeABYAEQArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAGwAbABsAGwAbABsAGwAaABsAGwAbABsAGwAbABsAGwAbABsAGwAbABsAGwAaABsAGwAbABsAGgAbABsAGgAbABsAGwAbABsAGwAbABsAGwAbABsAGwAbABsAGwAbABsABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgBQABoAHgAdAB4AUAAeABoAHgAeAB4AHgAeAB4AHgAeAB4ATwAeAFAAGwAeAB4AUABQAFAAUABQAB4AHgAeAB0AHQAeAFAAHgBQAB4AUAAeAFAATwBQAFAAHgAeAB4AHgAeAB4AHgBQAFAAUABQAFAAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgBQAB4AUABQAFAAUABPAE8AUABQAFAAUABQAE8AUABQAE8AUABPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBQAFAAUABQAE8ATwBPAE8ATwBPAE8ATwBPAE8AUABQAFAAUABQAFAAUABQAFAAHgAeAFAAUABQAFAATwAeAB4AKwArACsAKwAdAB0AHQAdAB0AHQAdAB0AHQAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAdAB4AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAeAB0AHQAeAB4AHgAdAB0AHgAeAB0AHgAeAB4AHQAeAB0AGwAbAB4AHQAeAB4AHgAeAB0AHgAeAB0AHQAdAB0AHgAeAB0AHgAdAB4AHQAdAB0AHQAdAB0AHgAdAB4AHgAeAB4AHgAdAB0AHQAdAB4AHgAeAB4AHQAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAeAB4AHgAdAB4AHgAeAB4AHgAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAdAB4AHgAdAB0AHQAdAB4AHgAdAB0AHgAeAB0AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAdAB0AHgAeAB0AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB0AHgAeAB4AHQAeAB4AHgAeAB4AHgAeAB0AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeABQAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAWABEAFgARAB4AHgAeAB4AHgAeAB0AHgAeAB4AHgAeAB4AHgAlACUAHgAeAB4AHgAeAB4AHgAeAB4AFgARAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACUAJQAlACUAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBQAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB4AHgAeAB4AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHgAeAB0AHQAdAB0AHgAeAB4AHgAeAB4AHgAeAB4AHgAdAB0AHgAdAB0AHQAdAB0AHQAdAB4AHgAeAB4AHgAeAB4AHgAdAB0AHgAeAB0AHQAeAB4AHgAeAB0AHQAeAB4AHgAeAB0AHQAdAB4AHgAdAB4AHgAdAB0AHQAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAdAB0AHQAeAB4AHgAeAB4AHgAeAB4AHgAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAeAB0AHQAeAB4AHQAeAB4AHgAeAB0AHQAeAB4AHgAeACUAJQAdAB0AJQAeACUAJQAlACAAJQAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAHgAeAB4AHgAdAB4AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAdAB4AHQAdAB0AHgAdACUAHQAdAB4AHQAdAB4AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB0AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAlACUAJQAlACUAJQAlACUAHQAdAB0AHQAlAB4AJQAlACUAHQAlACUAHQAdAB0AJQAlAB0AHQAlAB0AHQAlACUAJQAeAB0AHgAeAB4AHgAdAB0AJQAdAB0AHQAdAB0AHQAlACUAJQAlACUAHQAlACUAIAAlAB0AHQAlACUAJQAlACUAJQAlACUAHgAeAB4AJQAlACAAIAAgACAAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAdAB4AHgAeABcAFwAXABcAFwAXAB4AEwATACUAHgAeAB4AFgARABYAEQAWABEAFgARABYAEQAWABEAFgARAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAWABEAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AFgARABYAEQAWABEAFgARABYAEQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeABYAEQAWABEAFgARABYAEQAWABEAFgARABYAEQAWABEAFgARABYAEQAWABEAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AFgARABYAEQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeABYAEQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAdAB0AHQAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwAeAB4AHgAeAB4AHgAeAB4AHgArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAEAAQABAAeAB4AKwArACsAKwArABMADQANAA0AUAATAA0AUABQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAUAANACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAEAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQACsAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXAA0ADQANAA0ADQANAA0ADQAeAA0AFgANAB4AHgAXABcAHgAeABcAFwAWABEAFgARABYAEQAWABEADQANAA0ADQATAFAADQANAB4ADQANAB4AHgAeAB4AHgAMAAwADQANAA0AHgANAA0AFgANAA0ADQANAA0ADQANACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACsAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAKwArACsAKwArACsAKwArACsAKwArACsAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAlACUAJQAlACUAJQAlACUAJQAlACUAJQArACsAKwArAA0AEQARACUAJQBHAFcAVwAWABEAFgARABYAEQAWABEAFgARACUAJQAWABEAFgARABYAEQAWABEAFQAWABEAEQAlAFcAVwBXAFcAVwBXAFcAVwBXAAQABAAEAAQABAAEACUAVwBXAFcAVwA2ACUAJQBXAFcAVwBHAEcAJQAlACUAKwBRAFcAUQBXAFEAVwBRAFcAUQBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFEAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBRAFcAUQBXAFEAVwBXAFcAVwBXAFcAUQBXAFcAVwBXAFcAVwBRAFEAKwArAAQABAAVABUARwBHAFcAFQBRAFcAUQBXAFEAVwBRAFcAUQBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFEAVwBRAFcAUQBXAFcAVwBXAFcAVwBRAFcAVwBXAFcAVwBXAFEAUQBXAFcAVwBXABUAUQBHAEcAVwArACsAKwArACsAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAKwArAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwArACUAJQBXAFcAVwBXACUAJQAlACUAJQAlACUAJQAlACUAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAKwArACsAKwArACUAJQAlACUAKwArACsAKwArACsAKwArACsAKwArACsAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACsAVwBXAFcAVwBXAFcAVwBXAFcAVwAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAE8ATwBPAE8ATwBPAE8ATwAlAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACUAJQAlACUAJQAlACUAJQAlACUAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAEcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAKwArACsAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAADQATAA0AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABLAEsASwBLAEsASwBLAEsASwBLAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAFAABAAEAAQABAAeAAQABAAEAAQABAAEAAQABAAEAAQAHgBQAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AUABQAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAeAA0ADQANAA0ADQArACsAKwArACsAKwArACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAFAAUABQAFAAUABQAFAAUABQAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AUAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgBQAB4AHgAeAB4AHgAeAFAAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArAB4AHgAeAB4AHgAeAB4AHgArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAAQAUABQAFAABABQAFAAUABQAAQAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAeAB4AHgAeACsAKwArACsAUABQAFAAUABQAFAAHgAeABoAHgArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAADgAOABMAEwArACsAKwArACsAKwArACsABAAEAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEACsAKwArACsAKwArACsAKwANAA0ASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABABQAFAAUABQAFAAUAAeAB4AHgBQAA4AUAArACsAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAA0ADQBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAKwArACsAKwArACsAKwArACsAKwArAB4AWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYACsAKwArAAQAHgAeAB4AHgAeAB4ADQANAA0AHgAeAB4AHgArAFAASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArAB4AHgBcAFwAXABcAFwAKgBcAFwAXABcAFwAXABcAFwAXABcAEsASwBLAEsASwBLAEsASwBLAEsAXABcAFwAXABcACsAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwArACsAKwArACsAKwArAFAAUABQAAQAUABQAFAAUABQAFAAUABQAAQABAArACsASwBLAEsASwBLAEsASwBLAEsASwArACsAHgANAA0ADQBcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAKgAqACoAXAAqACoAKgBcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXAAqAFwAKgAqACoAXABcACoAKgBcAFwAXABcAFwAKgAqAFwAKgBcACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAFwAXABcACoAKgBQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEAA0ADQBQAFAAUAAEAAQAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUAArACsAUABQAFAAUABQAFAAKwArAFAAUABQAFAAUABQACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAAQADQAEAAQAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwArACsAVABVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBUAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVACsAKwArACsAKwArACsAKwArACsAKwArAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAKwArACsAKwBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAKwArACsAKwAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACUAJQBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAJQAlACUAJQAlACUAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAKwArACsAKwArAFYABABWAFYAVgBWAFYAVgBWAFYAVgBWAB4AVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgArAFYAVgBWAFYAVgArAFYAKwBWAFYAKwBWAFYAKwBWAFYAVgBWAFYAVgBWAFYAVgBWAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAEQAWAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUAAaAB4AKwArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAGAARABEAGAAYABMAEwAWABEAFAArACsAKwArACsAKwAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACUAJQAlACUAJQAWABEAFgARABYAEQAWABEAFgARABYAEQAlACUAFgARACUAJQAlACUAJQAlACUAEQAlABEAKwAVABUAEwATACUAFgARABYAEQAWABEAJQAlACUAJQAlACUAJQAlACsAJQAbABoAJQArACsAKwArAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAAcAKwATACUAJQAbABoAJQAlABYAEQAlACUAEQAlABEAJQBXAFcAVwBXAFcAVwBXAFcAVwBXABUAFQAlACUAJQATACUAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXABYAJQARACUAJQAlAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwAWACUAEQAlABYAEQARABYAEQARABUAVwBRAFEAUQBRAFEAUQBRAFEAUQBRAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAEcARwArACsAVwBXAFcAVwBXAFcAKwArAFcAVwBXAFcAVwBXACsAKwBXAFcAVwBXAFcAVwArACsAVwBXAFcAKwArACsAGgAbACUAJQAlABsAGwArAB4AHgAeAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAKwAEAAQABAAQAB0AKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwBQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsADQANAA0AKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArAB4AHgAeAB4AHgAeAB4AHgAeAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgBQAFAAHgAeAB4AKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArACsAKwArAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4ABAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAAQAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsADQBQAFAAUABQACsAKwArACsAUABQAFAAUABQAFAAUABQAA0AUABQAFAAUABQACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUAArACsAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQACsAKwArAFAAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAA0AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAB4AHgBQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsADQBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArAB4AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwBQAFAAUABQAFAABAAEAAQAKwAEAAQAKwArACsAKwArAAQABAAEAAQAUABQAFAAUAArAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsABAAEAAQAKwArACsAKwAEAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsADQANAA0ADQANAA0ADQANAB4AKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAB4AUABQAFAAUABQAFAAUABQAB4AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEACsAKwArACsAUABQAFAAUABQAA0ADQANAA0ADQANABQAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwANAA0ADQANAA0ADQANAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAHgAeAB4AHgArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwBQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAA0ADQAeAB4AHgAeAB4AKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEAAQABAAEAAQABAAeAB4AHgANAA0ADQANACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwBLAEsASwBLAEsASwBLAEsASwBLACsAKwArACsAKwArAFAAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsASwBLAEsASwBLAEsASwBLAEsASwANAA0ADQANACsAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAeAA4AUAArACsAKwArACsAKwArACsAKwAEAFAAUABQAFAADQANAB4ADQAeAAQABAAEAB4AKwArAEsASwBLAEsASwBLAEsASwBLAEsAUAAOAFAADQANAA0AKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAAQABAAEAAQABAANAA0AHgANAA0AHgAEACsAUABQAFAAUABQAFAAUAArAFAAKwBQAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAA0AKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAAQABAAEAAQAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwArACsABAAEAAQABAArAFAAUABQAFAAUABQAFAAUAArACsAUABQACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAArACsABAAEACsAKwAEAAQABAArACsAUAArACsAKwArACsAKwAEACsAKwArACsAKwBQAFAAUABQAFAABAAEACsAKwAEAAQABAAEAAQABAAEACsAKwArAAQABAAEAAQABAArACsAKwArACsAKwArACsAKwArACsABAAEAAQABAAEAAQABABQAFAAUABQAA0ADQANAA0AHgBLAEsASwBLAEsASwBLAEsASwBLACsADQArAB4AKwArAAQABAAEAAQAUABQAB4AUAArACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEACsAKwAEAAQABAAEAAQABAAEAAQABAAOAA0ADQATABMAHgAeAB4ADQANAA0ADQANAA0ADQANAA0ADQANAA0ADQANAA0AUABQAFAAUAAEAAQAKwArAAQADQANAB4AUAArACsAKwArACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwAOAA4ADgAOAA4ADgAOAA4ADgAOAA4ADgAOACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXAArACsAKwAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAXABcAA0ADQANACoASwBLAEsASwBLAEsASwBLAEsASwBQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwBQAFAABAAEAAQABAAEAAQABAAEAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAFAABAAEAAQABAAOAB4ADQANAA0ADQAOAB4ABAArACsAKwArACsAKwArACsAUAAEAAQABAAEAAQABAAEAAQABAAEAAQAUABQAFAAUAArACsAUABQAFAAUAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAA0ADQANACsADgAOAA4ADQANACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEACsABAAEAAQABAAEAAQABAAEAFAADQANAA0ADQANACsAKwArACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwAOABMAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQACsAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAArACsAKwAEACsABAAEACsABAAEAAQABAAEAAQABABQAAQAKwArACsAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsADQANAA0ADQANACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAASABIAEgAQwBDAEMAUABQAFAAUABDAFAAUABQAEgAQwBIAEMAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAASABDAEMAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABIAEMAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwANAA0AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAAQABAAEAAQABAANACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAA0ADQANAB4AHgAeAB4AHgAeAFAAUABQAFAADQAeACsAKwArACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwArAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAUAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsABAAEAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAEcARwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwArACsAKwArACsAKwArACsAKwArACsAKwArAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQACsAKwAeAAQABAANAAQABAAEAAQAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACsAKwArACsAKwArACsAKwArACsAHgAeAB4AHgAeAB4AHgArACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4ABAAEAAQABAAEAB4AHgAeAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAHgAeAAQABAAEAAQABAAEAAQAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAEAAQABAAEAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgAEAAQABAAeACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArAFAAUAArACsAUAArACsAUABQACsAKwBQAFAAUABQACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwBQACsAUABQAFAAUABQAFAAUAArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAKwAeAB4AUABQAFAAUABQACsAUAArACsAKwBQAFAAUABQAFAAUABQACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAHgAeAB4AHgAeAB4AHgAeAB4AKwArAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAB4AHgAeAB4ABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAB4AHgAeAB4AHgAeAB4AHgAEAB4AHgAeAB4AHgAeAB4AHgAeAB4ABAAeAB4ADQANAA0ADQAeACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAAQABAAEAAQABAArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsABAAEAAQABAAEAAQABAArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArACsABAAEAAQABAAEAAQABAArAAQABAArAAQABAAEAAQABAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEAAQAKwArACsAKwArACsAKwArACsAHgAeAB4AHgAEAAQABAAEAAQABAAEACsAKwArACsAKwBLAEsASwBLAEsASwBLAEsASwBLACsAKwArACsAFgAWAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUAArAFAAKwArAFAAKwBQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUAArAFAAKwBQACsAKwArACsAKwArAFAAKwArACsAKwBQACsAUAArAFAAKwBQAFAAUAArAFAAUAArAFAAKwArAFAAKwBQACsAUAArAFAAKwBQACsAUABQACsAUAArACsAUABQAFAAUAArAFAAUABQAFAAUABQAFAAKwBQAFAAUABQACsAUABQAFAAUAArAFAAKwBQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwBQAFAAUAArAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgArACsAKwArACsAKwArACsAKwArACsAKwArACsATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwAlACUAJQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAeACUAHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHgAeACUAJQAlACUAHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACkAKQApACkAKQApACkAKQApACkAKQApACkAKQApACkAKQApACkAKQApACkAKQApACkAKQAlACUAJQAlACUAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAB4AHgAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAHgAeACUAJQAlACUAJQAeACUAJQAlACUAJQAgACAAIAAlACUAIAAlACUAIAAgACAAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAIQAhACEAIQAhACUAJQAgACAAJQAlACAAIAAgACAAIAAgACAAIAAgACAAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAIAAgACAAIAAlACUAJQAlACAAJQAgACAAIAAgACAAIAAgACAAIAAlACUAJQAgACUAJQAlACUAIAAgACAAJQAgACAAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAeACUAHgAlAB4AJQAlACUAJQAlACAAJQAlACUAJQAeACUAHgAeACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAHgAeAB4AHgAeAB4AHgAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAIAAgACUAJQAlACUAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAIAAlACUAJQAlACAAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAB4AHgAeAB4AHgAeACUAJQAlACUAJQAlACUAIAAgACAAJQAlACUAIAAgACAAIAAgAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AFwAXABcAFQAVABUAHgAeAB4AHgAlACUAJQAgACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAIAAgACAAJQAlACUAJQAlACUAJQAlACUAIAAlACUAJQAlACUAJQAlACUAJQAlACUAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAlACUAJQAlACUAJQAlACUAJQAlACUAJQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAlACUAJQAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAlACUAJQAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAlACUAHgAeAB4AHgAeAB4AHgAeACUAJQAlACUAJQAlACUAJQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACUAJQAlACUAJQAlACUAJQAlACUAJQAlACAAIAAgACAAIAAlACAAIAAlACUAJQAlACUAJQAgACUAJQAlACUAJQAlACUAJQAlACAAIAAgACAAIAAgACAAIAAgACAAJQAlACUAIAAgACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACsAKwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAJQAlACUAJQAlACUAJQAlACUAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAJQAlACUAJQAlACUAJQAlACUAJQAlAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQArAAQAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsA"),U=Array.isArray(a)?function(A){for(var e=A.length,t=[],r=0;r=this._value.length?-1:this._value[A]},yA.prototype.consumeUnicodeRangeToken=function(){for(var A=[],e=this.consumeCodePoint();aA(e)&&A.length<6;)A.push(e),e=this.consumeCodePoint();for(var t=!1;63===e&&A.length<6;)A.push(e),e=this.consumeCodePoint(),t=!0;if(t){var r=parseInt(l.apply(void 0,A.map(function(A){return 63===A?48:A})),16),B=parseInt(l.apply(void 0,A.map(function(A){return 63===A?70:A})),16);return{type:sA.UNICODE_RANGE_TOKEN,start:r,end:B}}var n=parseInt(l.apply(void 0,A),16);if(45===this.peekCodePoint(0)&&aA(this.peekCodePoint(1))){this.consumeCodePoint(),e=this.consumeCodePoint();for(var s=[];aA(e)&&s.length<6;)s.push(e),e=this.consumeCodePoint();return B=parseInt(l.apply(void 0,s),16),{type:sA.UNICODE_RANGE_TOKEN,start:n,end:B}}return{type:sA.UNICODE_RANGE_TOKEN,start:n,end:n}},yA.prototype.consumeIdentLikeToken=function(){var A=this.consumeName();return"url"===A.toLowerCase()&&40===this.peekCodePoint(0)?(this.consumeCodePoint(),this.consumeUrlToken()):40===this.peekCodePoint(0)?(this.consumeCodePoint(),{type:sA.FUNCTION_TOKEN,value:A}):{type:sA.IDENT_TOKEN,value:A}},yA.prototype.consumeUrlToken=function(){var A=[];if(this.consumeWhiteSpace(),-1===this.peekCodePoint(0))return{type:sA.URL_TOKEN,value:""};var e,t=this.peekCodePoint(0);if(39===t||34===t){var r=this.consumeStringToken(this.consumeCodePoint());return r.type===sA.STRING_TOKEN&&(this.consumeWhiteSpace(),-1===this.peekCodePoint(0)||41===this.peekCodePoint(0))?(this.consumeCodePoint(),{type:sA.URL_TOKEN,value:r.value}):(this.consumeBadUrlRemnants(),IA)}for(;;){var B=this.consumeCodePoint();if(-1===B||41===B)return{type:sA.URL_TOKEN,value:l.apply(void 0,A)};if(cA(B))return this.consumeWhiteSpace(),-1===this.peekCodePoint(0)||41===this.peekCodePoint(0)?(this.consumeCodePoint(),{type:sA.URL_TOKEN,value:l.apply(void 0,A)}):(this.consumeBadUrlRemnants(),IA);if(34===B||39===B||40===B||0<=(e=B)&&e<=8||11===e||14<=e&&e<=31||127===e)return this.consumeBadUrlRemnants(),IA;if(92===B){if(!uA(B,this.peekCodePoint(0)))return this.consumeBadUrlRemnants(),IA;A.push(this.consumeEscapedCodePoint())}else A.push(B)}},yA.prototype.consumeWhiteSpace=function(){for(;cA(this.peekCodePoint(0));)this.consumeCodePoint()},yA.prototype.consumeBadUrlRemnants=function(){for(;;){var A=this.consumeCodePoint();if(41===A||-1===A)return;uA(A,this.peekCodePoint(0))&&this.consumeEscapedCodePoint()}},yA.prototype.consumeStringSlice=function(A){for(var e="";0>8,r=255&A>>16,B=255&A>>24;return e<255?"rgba("+B+","+r+","+t+","+e/255+")":"rgb("+B+","+r+","+t+")"}function re(A,e){if(A.type===sA.NUMBER_TOKEN)return A.number;if(A.type!==sA.PERCENTAGE_TOKEN)return 0;var t=3===e?1:255;return 3===e?A.number/100*t:Math.round(A.number/100*t)}function Be(A){var e=A.filter(kA);if(3===e.length){var t=e.map(re),r=t[0],B=t[1],n=t[2];return ue(r,B,n,1)}if(4!==e.length)return 0;var s=e.map(re),o=(r=s[0],B=s[1],n=s[2],s[3]);return ue(r,B,n,o)}var ne=function(A,e){return e===sA.LEFT_CURLY_BRACKET_TOKEN&&A.type===sA.RIGHT_CURLY_BRACKET_TOKEN||(e===sA.LEFT_SQUARE_BRACKET_TOKEN&&A.type===sA.RIGHT_SQUARE_BRACKET_TOKEN||e===sA.LEFT_PARENTHESIS_TOKEN&&A.type===sA.RIGHT_PARENTHESIS_TOKEN)},se={type:sA.NUMBER_TOKEN,number:0,flags:4},oe={type:sA.PERCENTAGE_TOKEN,number:50,flags:4},ie={type:sA.PERCENTAGE_TOKEN,number:100,flags:4},ae=function(A,e){if(A.type===sA.PERCENTAGE_TOKEN)return A.number/100*e;if(xA(A))switch(A.unit){case"rem":case"em":return 16*A.number;case"px":default:return A.number}return A.number},ce=function(A){if(A.type===sA.DIMENSION_TOKEN)switch(A.unit){case"deg":return Math.PI*A.number/180;case"grad":return Math.PI/200*A.number;case"rad":return A.number;case"turn":return 2*Math.PI*A.number}throw new Error("Unsupported angle type")},Qe=function(A){return Math.PI*A/180},we=function(A){if(A.type===sA.FUNCTION){var e=he[A.name];if(void 0===e)throw new Error('Attempting to parse an unsupported color function "'+A.name+'"');return e(A.values)}if(A.type===sA.HASH_TOKEN){if(3===A.value.length){var t=A.value.substring(0,1),r=A.value.substring(1,2),B=A.value.substring(2,3);return ue(parseInt(t+t,16),parseInt(r+r,16),parseInt(B+B,16),1)}if(4===A.value.length){t=A.value.substring(0,1),r=A.value.substring(1,2),B=A.value.substring(2,3);var n=A.value.substring(3,4);return ue(parseInt(t+t,16),parseInt(r+r,16),parseInt(B+B,16),parseInt(n+n,16)/255)}if(6===A.value.length){t=A.value.substring(0,2),r=A.value.substring(2,4),B=A.value.substring(4,6);return ue(parseInt(t,16),parseInt(r,16),parseInt(B,16),1)}if(8===A.value.length){t=A.value.substring(0,2),r=A.value.substring(2,4),B=A.value.substring(4,6),n=A.value.substring(6,8);return ue(parseInt(t,16),parseInt(r,16),parseInt(B,16),parseInt(n,16)/255)}}if(A.type===sA.IDENT_TOKEN){var s=He[A.value.toUpperCase()];if(void 0!==s)return s}return He.TRANSPARENT},ue=function(A,e,t,r){return(A<<24|e<<16|t<<8|Math.round(255*r)<<0)>>>0};function Ue(A,e,t){return t<0&&(t+=1),1<=t&&(t-=1),t<1/6?(e-A)*t*6+A:t<.5?e:t<2/3?6*(e-A)*(2/3-t)+A:A}function le(A){var e=A.filter(kA),t=e[0],r=e[1],B=e[2],n=e[3],s=(t.type===sA.NUMBER_TOKEN?Qe(t.number):ce(t))/(2*Math.PI),o=qA(r)?r.number/100:0,i=qA(B)?B.number/100:0,a=void 0!==n&&qA(n)?ae(n,1):1;if(0==o)return ue(255*i,255*i,255*i,1);var c=i<=.5?i*(1+o):i+o-i*o,Q=2*i-c,w=Ue(Q,c,s+1/3),u=Ue(Q,c,s),U=Ue(Q,c,s-1/3);return ue(255*w,255*u,255*U,a)}var Ce,ge,Ee,Fe,he={hsl:le,hsla:le,rgb:Be,rgba:Be},He={ALICEBLUE:4042850303,ANTIQUEWHITE:4209760255,AQUA:16777215,AQUAMARINE:2147472639,AZURE:4043309055,BEIGE:4126530815,BISQUE:4293182719,BLACK:255,BLANCHEDALMOND:4293643775,BLUE:65535,BLUEVIOLET:2318131967,BROWN:2771004159,BURLYWOOD:3736635391,CADETBLUE:1604231423,CHARTREUSE:2147418367,CHOCOLATE:3530104575,CORAL:4286533887,CORNFLOWERBLUE:1687547391,CORNSILK:4294499583,CRIMSON:3692313855,CYAN:16777215,DARKBLUE:35839,DARKCYAN:9145343,DARKGOLDENROD:3095837695,DARKGRAY:2846468607,DARKGREEN:6553855,DARKGREY:2846468607,DARKKHAKI:3182914559,DARKMAGENTA:2332068863,DARKOLIVEGREEN:1433087999,DARKORANGE:4287365375,DARKORCHID:2570243327,DARKRED:2332033279,DARKSALMON:3918953215,DARKSEAGREEN:2411499519,DARKSLATEBLUE:1211993087,DARKSLATEGRAY:793726975,DARKSLATEGREY:793726975,DARKTURQUOISE:13554175,DARKVIOLET:2483082239,DEEPPINK:4279538687,DEEPSKYBLUE:12582911,DIMGRAY:1768516095,DIMGREY:1768516095,DODGERBLUE:512819199,FIREBRICK:2988581631,FLORALWHITE:4294635775,FORESTGREEN:579543807,FUCHSIA:4278255615,GAINSBORO:3705462015,GHOSTWHITE:4177068031,GOLD:4292280575,GOLDENROD:3668254975,GRAY:2155905279,GREEN:8388863,GREENYELLOW:2919182335,GREY:2155905279,HONEYDEW:4043305215,HOTPINK:4285117695,INDIANRED:3445382399,INDIGO:1258324735,IVORY:4294963455,KHAKI:4041641215,LAVENDER:3873897215,LAVENDERBLUSH:4293981695,LAWNGREEN:2096890111,LEMONCHIFFON:4294626815,LIGHTBLUE:2916673279,LIGHTCORAL:4034953471,LIGHTCYAN:3774873599,LIGHTGOLDENRODYELLOW:4210742015,LIGHTGRAY:3553874943,LIGHTGREEN:2431553791,LIGHTGREY:3553874943,LIGHTPINK:4290167295,LIGHTSALMON:4288707327,LIGHTSEAGREEN:548580095,LIGHTSKYBLUE:2278488831,LIGHTSLATEGRAY:2005441023,LIGHTSLATEGREY:2005441023,LIGHTSTEELBLUE:2965692159,LIGHTYELLOW:4294959359,LIME:16711935,LIMEGREEN:852308735,LINEN:4210091775,MAGENTA:4278255615,MAROON:2147483903,MEDIUMAQUAMARINE:1724754687,MEDIUMBLUE:52735,MEDIUMORCHID:3126187007,MEDIUMPURPLE:2473647103,MEDIUMSEAGREEN:1018393087,MEDIUMSLATEBLUE:2070474495,MEDIUMSPRINGGREEN:16423679,MEDIUMTURQUOISE:1221709055,MEDIUMVIOLETRED:3340076543,MIDNIGHTBLUE:421097727,MINTCREAM:4127193855,MISTYROSE:4293190143,MOCCASIN:4293178879,NAVAJOWHITE:4292783615,NAVY:33023,OLDLACE:4260751103,OLIVE:2155872511,OLIVEDRAB:1804477439,ORANGE:4289003775,ORANGERED:4282712319,ORCHID:3664828159,PALEGOLDENROD:4008225535,PALEGREEN:2566625535,PALETURQUOISE:2951671551,PALEVIOLETRED:3681588223,PAPAYAWHIP:4293907967,PEACHPUFF:4292524543,PERU:3448061951,PINK:4290825215,PLUM:3718307327,POWDERBLUE:2967529215,PURPLE:2147516671,REBECCAPURPLE:1714657791,RED:4278190335,ROSYBROWN:3163525119,ROYALBLUE:1097458175,SADDLEBROWN:2336560127,SALMON:4202722047,SANDYBROWN:4104413439,SEAGREEN:780883967,SEASHELL:4294307583,SIENNA:2689740287,SILVER:3233857791,SKYBLUE:2278484991,SLATEBLUE:1784335871,SLATEGRAY:1887473919,SLATEGREY:1887473919,SNOW:4294638335,SPRINGGREEN:16744447,STEELBLUE:1182971135,TAN:3535047935,TEAL:8421631,THISTLE:3636451583,TOMATO:4284696575,TRANSPARENT:0,TURQUOISE:1088475391,VIOLET:4001558271,WHEAT:4125012991,WHITE:4294967295,WHITESMOKE:4126537215,YELLOW:4294902015,YELLOWGREEN:2597139199};(ge=Ce||(Ce={}))[ge.VALUE=0]="VALUE",ge[ge.LIST=1]="LIST",ge[ge.IDENT_VALUE=2]="IDENT_VALUE",ge[ge.TYPE_VALUE=3]="TYPE_VALUE",ge[ge.TOKEN_VALUE=4]="TOKEN_VALUE",(Fe=Ee||(Ee={}))[Fe.BORDER_BOX=0]="BORDER_BOX",Fe[Fe.PADDING_BOX=1]="PADDING_BOX";function de(A){var e=we(A[0]),t=A[1];return t&&qA(t)?{color:e,stop:t}:{color:e,stop:null}}function fe(A,t){var e=A[0],r=A[A.length-1];null===e.stop&&(e.stop=se),null===r.stop&&(r.stop=ie);for(var B=[],n=0,s=0;s A.optimumDistance)?{optimumCorner:e,optimumDistance:B}:A},{optimumDistance:o?1/0:-1/0,optimumCorner:null}).optimumCorner}function Ie(A){var B=Qe(180),n=[];return WA(A).forEach(function(A,e){if(0===e){var t=A[0];if(t.type===sA.IDENT_TOKEN&&-1!==["top","left","right","bottom"].indexOf(t.value))return void(B=Ae(A));if($A(t))return void(B=(ce(t)+Qe(270))%Qe(360))}var r=de(A);n.push(r)}),{angle:B,stops:n,type:xe.LINEAR_GRADIENT}}function Te(A){return 0===A[0]&&255===A[1]&&0===A[2]&&255===A[3]}var me={name:"background-clip",initialValue:"border-box",prefix:!(Fe[Fe.CONTENT_BOX=2]="CONTENT_BOX"),type:Ce.LIST,parse:function(A){return A.map(function(A){if(zA(A))switch(A.value){case"padding-box":return Ee.PADDING_BOX;case"content-box":return Ee.CONTENT_BOX}return Ee.BORDER_BOX})}},Re={name:"background-color",initialValue:"transparent",prefix:!1,type:Ce.TYPE_VALUE,format:"color"},Le=function(A,e,t,r,B){var n="http://www.w3.org/2000/svg",s=document.createElementNS(n,"svg"),o=document.createElementNS(n,"foreignObject");return s.setAttributeNS(null,"width",A.toString()),s.setAttributeNS(null,"height",e.toString()),o.setAttributeNS(null,"width","100%"),o.setAttributeNS(null,"height","100%"),o.setAttributeNS(null,"x",t.toString()),o.setAttributeNS(null,"y",r.toString()),o.setAttributeNS(null,"externalResourcesRequired","true"),s.appendChild(o),o.appendChild(B),s},Oe=function(r){return new Promise(function(A,e){var t=new Image;t.onload=function(){return A(t)},t.onerror=e,t.src="data:image/svg+xml;charset=utf-8,"+encodeURIComponent((new XMLSerializer).serializeToString(r))})},ve={get SUPPORT_RANGE_BOUNDS(){var A=function(A){if(A.createRange){var e=A.createRange();if(e.getBoundingClientRect){var t=A.createElement("boundtest");t.style.height="123px",t.style.display="block",A.body.appendChild(t),e.selectNode(t);var r=e.getBoundingClientRect(),B=Math.round(r.height);if(A.body.removeChild(t),123===B)return!0}}return!1}(document);return Object.defineProperty(ve,"SUPPORT_RANGE_BOUNDS",{value:A}),A},get SUPPORT_SVG_DRAWING(){var A=function(A){var e=new Image,t=A.createElement("canvas"),r=t.getContext("2d");if(!r)return!1;e.src="data:image/svg+xml, ";try{r.drawImage(e,0,0),t.toDataURL()}catch(A){return!1}return!0}(document);return Object.defineProperty(ve,"SUPPORT_SVG_DRAWING",{value:A}),A},get SUPPORT_FOREIGNOBJECT_DRAWING(){var A="function"==typeof Array.from&&"function"==typeof window.fetch?function(r){var A=r.createElement("canvas"),B=100;A.width=B,A.height=B;var n=A.getContext("2d");if(!n)return Promise.reject(!1);n.fillStyle="rgb(0, 255, 0)",n.fillRect(0,0,B,B);var e=new Image,s=A.toDataURL();e.src=s;var t=Le(B,B,0,0,e);return n.fillStyle="red",n.fillRect(0,0,B,B),Oe(t).then(function(A){n.drawImage(A,0,0);var e=n.getImageData(0,0,B,B).data;n.fillStyle="red",n.fillRect(0,0,B,B);var t=r.createElement("div");return t.style.backgroundImage="url("+s+")",t.style.height="100px",Te(e)?Oe(Le(B,B,0,0,t)):Promise.reject(!1)}).then(function(A){return n.drawImage(A,0,0),Te(n.getImageData(0,0,B,B).data)}).catch(function(){return!1})}(document):Promise.resolve(!1);return Object.defineProperty(ve,"SUPPORT_FOREIGNOBJECT_DRAWING",{value:A}),A},get SUPPORT_CORS_IMAGES(){var A=void 0!==(new Image).crossOrigin;return Object.defineProperty(ve,"SUPPORT_CORS_IMAGES",{value:A}),A},get SUPPORT_RESPONSE_TYPE(){var A="string"==typeof(new XMLHttpRequest).responseType;return Object.defineProperty(ve,"SUPPORT_RESPONSE_TYPE",{value:A}),A},get SUPPORT_CORS_XHR(){var A="withCredentials"in new XMLHttpRequest;return Object.defineProperty(ve,"SUPPORT_CORS_XHR",{value:A}),A}},De=(Se.prototype.debug=function(){for(var A=[],e=0;eA.height?new I(A.left+(A.width-A.height)/2,A.top,A.height,A.height):A.width"),Wn(this.referenceElement.ownerDocument,B,n),o.replaceChild(o.adoptNode(this.documentElement),o.documentElement),o.close(),i},xn.prototype.createElementClone=function(A){return Fn(A)?this.createCanvasClone(A):rn(A)?this.createStyleClone(A):A.cloneNode(!1)},xn.prototype.createStyleClone=function(A){try{var e=A.sheet;if(e&&e.cssRules){var t=[].slice.call(e.cssRules,0).reduce(function(A,e){return e&&"string"==typeof e.cssText?A+e.cssText:A},""),r=A.cloneNode(!1);return r.textContent=t,r}}catch(A){if(De.getInstance(this.options.id).error("Unable to access cssRules property",A),"SecurityError"!==A.name)throw A}return A.cloneNode(!1)},xn.prototype.createCanvasClone=function(A){if(this.options.inlineImages&&A.ownerDocument){var e=A.ownerDocument.createElement("img");try{return e.src=A.toDataURL(),e}catch(A){De.getInstance(this.options.id).info("Unable to clone canvas contents, canvas is tainted")}}var t=A.cloneNode(!1);try{t.width=A.width,t.height=A.height;var r=A.getContext("2d"),B=t.getContext("2d");return B&&(r?B.putImageData(r.getImageData(0,0,A.width,A.height),0,0):B.drawImage(A,0,0)),t}catch(A){}return t},xn.prototype.cloneNode=function(A){if(Qn(A))return document.createTextNode(A.data);if(!A.ownerDocument)return A.cloneNode(!1);var e=A.ownerDocument.defaultView;if(un(A)&&e){var t=this.createElementClone(A),r=e.getComputedStyle(A),B=e.getComputedStyle(A,":before"),n=e.getComputedStyle(A,":after");this.referenceElement===A&&(this.clonedReferenceElement=t),En(t)&&$n(t);for(var s=this.counters.parse(new wB(r)),o=this.resolvePseudoContent(A,t,B,Ln.BEFORE),i=A.firstChild;i;i=i.nextSibling)wn(i)&&("SCRIPT"===i.tagName||i.hasAttribute(_n)||"function"==typeof this.options.ignoreElements&&this.options.ignoreElements(i))||this.options.copyStyles&&wn(i)&&rn(i)||t.appendChild(this.cloneNode(i));o&&t.insertBefore(o,t.firstChild);var a=this.resolvePseudoContent(A,t,n,Ln.AFTER);return a&&t.appendChild(a),this.counters.pop(s),r&&this.options.copyStyles&&!Hn(A)&&Gn(r,t),0===A.scrollTop&&0===A.scrollLeft||this.scrolledElements.push([t,A.scrollLeft,A.scrollTop]),(dn(A)||fn(A))&&(dn(t)||fn(t))&&(t.value=A.value),t}return A.cloneNode(!1)},xn.prototype.resolvePseudoContent=function(U,A,e,t){var l=this;if(e){var r=e.content,C=A.ownerDocument;if(C&&r&&"none"!==r&&"-moz-alt-content"!==r&&"none"!==e.display){this.counters.parse(new wB(e));var g=new QB(e),E=C.createElement("html2canvaspseudoelement");return Gn(e,E),g.content.forEach(function(A){if(A.type===sA.STRING_TOKEN)E.appendChild(C.createTextNode(A.value));else if(A.type===sA.URL_TOKEN){var e=C.createElement("img");e.src=A.value,e.style.opacity="1",E.appendChild(e)}else if(A.type===sA.FUNCTION){if("attr"===A.name){var t=A.values.filter(zA);t.length&&E.appendChild(C.createTextNode(U.getAttribute(t[0].value)||""))}else if("counter"===A.name){var r=A.values.filter(kA),B=r[0],n=r[1];if(B&&zA(B)){var s=l.counters.getCounterValue(B.value),o=n&&zA(n)?ir.parse(n.value):tr.DECIMAL;E.appendChild(C.createTextNode(yn(s,o,!1)))}}else if("counters"===A.name){var i=A.values.filter(kA),a=(B=i[0],i[1]);if(n=i[2],B&&zA(B)){var c=l.counters.getCounterValues(B.value),Q=n&&zA(n)?ir.parse(n.value):tr.DECIMAL,w=a&&a.type===sA.STRING_TOKEN?a.value:"",u=c.map(function(A){return yn(A,Q,!1)}).join(w);E.appendChild(C.createTextNode(u))}}}else if(A.type===sA.IDENT_TOKEN)switch(A.value){case"open-quote":E.appendChild(C.createTextNode(eB(g.quotes,l.quoteDepth++,!0)));break;case"close-quote":E.appendChild(C.createTextNode(eB(g.quotes,--l.quoteDepth,!1)))}}),E.className=qn+" "+Zn,A.className+=t===Ln.BEFORE?" "+qn:" "+Zn,E}}},xn);function xn(A,e){if(this.options=e,this.scrolledElements=[],this.referenceElement=A,this.counters=new pn,this.quoteDepth=0,!A.ownerDocument)throw new Error("Cloned element does not have an owner document");this.documentElement=this.cloneNode(A.ownerDocument.documentElement)}(On=Ln||(Ln={}))[On.BEFORE=0]="BEFORE",On[On.AFTER=1]="AFTER";var Vn,zn,Xn=function(A,e){var t=A.createElement("iframe");return t.className="html2canvas-container",t.style.visibility="hidden",t.style.position="fixed",t.style.left="-10000px",t.style.top="0px",t.style.border="0",t.width=e.width.toString(),t.height=e.height.toString(),t.scrolling="no",t.setAttribute(_n,"true"),A.body.appendChild(t),t},Jn=function(B){return new Promise(function(e,A){var t=B.contentWindow;if(!t)return A("No window assigned for iframe");var r=t.document;t.onload=B.onload=r.onreadystatechange=function(){t.onload=B.onload=r.onreadystatechange=null;var A=setInterval(function(){0"),e},Wn=function(A,e,t){A&&A.defaultView&&(e!==A.defaultView.pageXOffset||t!==A.defaultView.pageYOffset)&&A.defaultView.scrollTo(e,t)},Yn=function(A){var e=A[0],t=A[1],r=A[2];e.scrollLeft=t,e.scrollTop=r},qn="___html2canvas___pseudoelement_before",Zn="___html2canvas___pseudoelement_after",jn='{\n content: "" !important;\n display: none !important;\n}',$n=function(A){As(A,"."+qn+":before"+jn+"\n ."+Zn+":after"+jn)},As=function(A,e){var t=A.ownerDocument;if(t){var r=t.createElement("style");r.textContent=e,A.appendChild(r)}};(zn=Vn||(Vn={}))[zn.VECTOR=0]="VECTOR",zn[zn.BEZIER_CURVE=1]="BEZIER_CURVE";function es(A,t){return A.length===t.length&&A.some(function(A,e){return A===t[e]})}var ts=(rs.prototype.add=function(A,e){return new rs(this.x+A,this.y+e)},rs);function rs(A,e){this.type=Vn.VECTOR,this.x=A,this.y=e}function Bs(A,e,t){return new ts(A.x+(e.x-A.x)*t,A.y+(e.y-A.y)*t)}var ns=(ss.prototype.subdivide=function(A,e){var t=Bs(this.start,this.startControl,A),r=Bs(this.startControl,this.endControl,A),B=Bs(this.endControl,this.end,A),n=Bs(t,r,A),s=Bs(r,B,A),o=Bs(n,s,A);return e?new ss(this.start,t,n,o):new ss(o,s,B,this.end)},ss.prototype.add=function(A,e){return new ss(this.start.add(A,e),this.startControl.add(A,e),this.endControl.add(A,e),this.end.add(A,e))},ss.prototype.reverse=function(){return new ss(this.end,this.endControl,this.startControl,this.start)},ss);function ss(A,e,t,r){this.type=Vn.BEZIER_CURVE,this.start=A,this.startControl=e,this.endControl=t,this.end=r}function os(A){return A.type===Vn.BEZIER_CURVE}var is,as,cs=function(A){var e=A.styles,t=A.bounds,r=jA(e.borderTopLeftRadius,t.width,t.height),B=r[0],n=r[1],s=jA(e.borderTopRightRadius,t.width,t.height),o=s[0],i=s[1],a=jA(e.borderBottomRightRadius,t.width,t.height),c=a[0],Q=a[1],w=jA(e.borderBottomLeftRadius,t.width,t.height),u=w[0],U=w[1],l=[];l.push((B+o)/t.width),l.push((u+c)/t.width),l.push((n+U)/t.height),l.push((i+Q)/t.height);var C=Math.max.apply(Math,l);1t.width+p?0:o-p,i-H,is.TOP_RIGHT):new ts(t.left+t.width-d,t.top+H),this.bottomRightPaddingBox=0t.width+p+T?0:o-p+T,i-(H+N),is.TOP_RIGHT):new ts(t.left+t.width-(d+K),t.top+H+N),this.bottomRightContentBox=0A.element.container.styles.zIndex.order&&(i=e,!0)}),n.negativeZIndex.splice(i,0,s)}else if(0A.element.container.styles.zIndex.order&&(a=e+1,!0)}),n.positiveZIndex.splice(a,0,s)}else n.zeroOrAutoZIndexOrTransformedOrOpacity.push(s)}else A.styles.isFloating()?n.nonPositionedFloats.push(s):n.nonPositionedInlineLevel.push(s);ps(r,s,e?s:w,B)}else A.styles.isInlineLevel()?Q.inlineLevel.push(r):Q.nonInlineLevel.push(r),ps(r,Q,w,B);AB(A.flags,8)&&Ns(A,B)})},Ns=function(A,e){for(var t=A instanceof bB?A.start:1,r=A instanceof bB&&A.reversed,B=0;Ba?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(l.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:k&&!k.call("\ufeff\xa0")?function(a){return null==a?"":k.call(a)}:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||n.guid++,e):void 0},now:function(){return+new Date},support:l}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s="sizzle"+-new Date,t=a.document,u=0,v=0,w=eb(),x=eb(),y=eb(),z=function(a,b){return a===b&&(j=!0),0},A="undefined",B=1<<31,C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=D.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",M=L.replace("w","w#"),N="\\["+K+"*("+L+")"+K+"*(?:([*^$|!~]?=)"+K+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+M+")|)|)"+K+"*\\]",O=":("+L+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+N.replace(3,8)+")*)|.*)\\)|)",P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(O),U=new RegExp("^"+M+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L.replace("w","w*")+")"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=/'|\\/g,ab=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),bb=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{G.apply(D=H.call(t.childNodes),t.childNodes),D[t.childNodes.length].nodeType}catch(cb){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function db(a,b,d,e){var f,g,h,i,j,m,p,q,u,v;if((b?b.ownerDocument||b:t)!==l&&k(b),b=b||l,d=d||[],!a||"string"!=typeof a)return d;if(1!==(i=b.nodeType)&&9!==i)return[];if(n&&!e){if(f=Z.exec(a))if(h=f[1]){if(9===i){if(g=b.getElementById(h),!g||!g.parentNode)return d;if(g.id===h)return d.push(g),d}else if(b.ownerDocument&&(g=b.ownerDocument.getElementById(h))&&r(b,g)&&g.id===h)return d.push(g),d}else{if(f[2])return G.apply(d,b.getElementsByTagName(a)),d;if((h=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(h)),d}if(c.qsa&&(!o||!o.test(a))){if(q=p=s,u=b,v=9===i&&a,1===i&&"object"!==b.nodeName.toLowerCase()){m=ob(a),(p=b.getAttribute("id"))?q=p.replace(_,"\\$&"):b.setAttribute("id",q),q="[id='"+q+"'] ",j=m.length;while(j--)m[j]=q+pb(m[j]);u=$.test(a)&&mb(b.parentNode)||b,v=m.join(",")}if(v)try{return G.apply(d,u.querySelectorAll(v)),d}catch(w){}finally{p||b.removeAttribute("id")}}}return xb(a.replace(P,"$1"),b,d,e)}function eb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function fb(a){return a[s]=!0,a}function gb(a){var b=l.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function hb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function ib(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||B)-(~a.sourceIndex||B);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function jb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function kb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function lb(a){return fb(function(b){return b=+b,fb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function mb(a){return a&&typeof a.getElementsByTagName!==A&&a}c=db.support={},f=db.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},k=db.setDocument=function(a){var b,e=a?a.ownerDocument||a:t,g=e.defaultView;return e!==l&&9===e.nodeType&&e.documentElement?(l=e,m=e.documentElement,n=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){k()},!1):g.attachEvent&&g.attachEvent("onunload",function(){k()})),c.attributes=gb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=gb(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(e.getElementsByClassName)&&gb(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=gb(function(a){return m.appendChild(a).id=s,!e.getElementsByName||!e.getElementsByName(s).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==A&&n){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){var c=typeof a.getAttributeNode!==A&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==A?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==A&&n?b.getElementsByClassName(a):void 0},p=[],o=[],(c.qsa=Y.test(e.querySelectorAll))&&(gb(function(a){a.innerHTML=" ",a.querySelectorAll("[t^='']").length&&o.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||o.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll(":checked").length||o.push(":checked")}),gb(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&o.push("name"+K+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||o.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),o.push(",.*:")})),(c.matchesSelector=Y.test(q=m.webkitMatchesSelector||m.mozMatchesSelector||m.oMatchesSelector||m.msMatchesSelector))&&gb(function(a){c.disconnectedMatch=q.call(a,"div"),q.call(a,"[s!='']:x"),p.push("!=",O)}),o=o.length&&new RegExp(o.join("|")),p=p.length&&new RegExp(p.join("|")),b=Y.test(m.compareDocumentPosition),r=b||Y.test(m.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},z=b?function(a,b){if(a===b)return j=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===t&&r(t,a)?-1:b===e||b.ownerDocument===t&&r(t,b)?1:i?I.call(i,a)-I.call(i,b):0:4&d?-1:1)}:function(a,b){if(a===b)return j=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],k=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:i?I.call(i,a)-I.call(i,b):0;if(f===g)return ib(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)k.unshift(c);while(h[d]===k[d])d++;return d?ib(h[d],k[d]):h[d]===t?-1:k[d]===t?1:0},e):l},db.matches=function(a,b){return db(a,null,null,b)},db.matchesSelector=function(a,b){if((a.ownerDocument||a)!==l&&k(a),b=b.replace(S,"='$1']"),!(!c.matchesSelector||!n||p&&p.test(b)||o&&o.test(b)))try{var d=q.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return db(b,l,null,[a]).length>0},db.contains=function(a,b){return(a.ownerDocument||a)!==l&&k(a),r(a,b)},db.attr=function(a,b){(a.ownerDocument||a)!==l&&k(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!n):void 0;return void 0!==f?f:c.attributes||!n?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},db.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},db.uniqueSort=function(a){var b,d=[],e=0,f=0;if(j=!c.detectDuplicates,i=!c.sortStable&&a.slice(0),a.sort(z),j){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return i=null,a},e=db.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=db.selectors={cacheLength:50,createPseudo:fb,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ab,bb),a[3]=(a[4]||a[5]||"").replace(ab,bb),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||db.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&db.error(a[0]),a},PSEUDO:function(a){var b,c=!a[5]&&a[2];return V.CHILD.test(a[0])?null:(a[3]&&void 0!==a[4]?a[2]=a[4]:c&&T.test(c)&&(b=ob(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ab,bb).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=w[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&w(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==A&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=db.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),t=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&t){k=q[s]||(q[s]={}),j=k[a]||[],n=j[0]===u&&j[1],m=j[0]===u&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[u,n,m];break}}else if(t&&(j=(b[s]||(b[s]={}))[a])&&j[0]===u)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(t&&((l[s]||(l[s]={}))[a]=[u,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||db.error("unsupported pseudo: "+a);return e[s]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?fb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:fb(function(a){var b=[],c=[],d=g(a.replace(P,"$1"));return d[s]?fb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:fb(function(a){return function(b){return db(a,b).length>0}}),contains:fb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:fb(function(a){return U.test(a||"")||db.error("unsupported lang: "+a),a=a.replace(ab,bb).toLowerCase(),function(b){var c;do if(c=n?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===m},focus:function(a){return a===l.activeElement&&(!l.hasFocus||l.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:lb(function(){return[0]}),last:lb(function(a,b){return[b-1]}),eq:lb(function(a,b,c){return[0>c?c+b:c]}),even:lb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:lb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:lb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:lb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function qb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=v++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[u,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[s]||(b[s]={}),(h=i[d])&&h[0]===u&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function rb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function sb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function tb(a,b,c,d,e,f){return d&&!d[s]&&(d=tb(d)),e&&!e[s]&&(e=tb(e,f)),fb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||wb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:sb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=sb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=sb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ub(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],i=g||d.relative[" "],j=g?1:0,k=qb(function(a){return a===b},i,!0),l=qb(function(a){return I.call(b,a)>-1},i,!0),m=[function(a,c,d){return!g&&(d||c!==h)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>j;j++)if(c=d.relative[a[j].type])m=[qb(rb(m),c)];else{if(c=d.filter[a[j].type].apply(null,a[j].matches),c[s]){for(e=++j;f>e;e++)if(d.relative[a[e].type])break;return tb(j>1&&rb(m),j>1&&pb(a.slice(0,j-1).concat({value:" "===a[j-2].type?"*":""})).replace(P,"$1"),c,e>j&&ub(a.slice(j,e)),f>e&&ub(a=a.slice(e)),f>e&&pb(a))}m.push(c)}return rb(m)}function vb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,i,j,k){var m,n,o,p=0,q="0",r=f&&[],s=[],t=h,v=f||e&&d.find.TAG("*",k),w=u+=null==t?1:Math.random()||.1,x=v.length;for(k&&(h=g!==l&&g);q!==x&&null!=(m=v[q]);q++){if(e&&m){n=0;while(o=a[n++])if(o(m,g,i)){j.push(m);break}k&&(u=w)}c&&((m=!o&&m)&&p--,f&&r.push(m))}if(p+=q,c&&q!==p){n=0;while(o=b[n++])o(r,s,g,i);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=E.call(j));s=sb(s)}G.apply(j,s),k&&!f&&s.length>0&&p+b.length>1&&db.uniqueSort(j)}return k&&(u=w,h=t),r};return c?fb(f):f}g=db.compile=function(a,b){var c,d=[],e=[],f=y[a+" "];if(!f){b||(b=ob(a)),c=b.length;while(c--)f=ub(b[c]),f[s]?d.push(f):e.push(f);f=y(a,vb(e,d))}return f};function wb(a,b,c){for(var d=0,e=b.length;e>d;d++)db(a,b[d],c);return c}function xb(a,b,e,f){var h,i,j,k,l,m=ob(a);if(!f&&1===m.length){if(i=m[0]=m[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&c.getById&&9===b.nodeType&&n&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(ab,bb),b)||[])[0],!b)return e;a=a.slice(i.shift().value.length)}h=V.needsContext.test(a)?0:i.length;while(h--){if(j=i[h],d.relative[k=j.type])break;if((l=d.find[k])&&(f=l(j.matches[0].replace(ab,bb),$.test(i[0].type)&&mb(b.parentNode)||b))){if(i.splice(h,1),a=f.length&&pb(i),!a)return G.apply(e,f),e;break}}}return g(a,m)(f,b,!n,e,$.test(a)&&mb(b.parentNode)||b),e}return c.sortStable=s.split("").sort(z).join("")===s,c.detectDuplicates=!!j,k(),c.sortDetached=gb(function(a){return 1&a.compareDocumentPosition(l.createElement("div"))}),gb(function(a){return a.innerHTML=" ","#"===a.firstChild.getAttribute("href")})||hb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&gb(function(a){return a.innerHTML=" ",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||hb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),gb(function(a){return null==a.getAttribute("disabled")})||hb(J,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),db}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=a.document,A=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,B=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:A.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:z,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=z.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return y.find(a);this.length=1,this[0]=d}return this.context=z,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};B.prototype=n.fn,y=n(z);var C=/^(?:parents|prev(?:Until|All))/,D={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!n(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function E(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return E(a,"nextSibling")},prev:function(a){return E(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(D[a]||(e=n.unique(e)),C.test(a)&&(e=e.reverse())),this.pushStack(e)}});var F=/\S+/g,G={};function H(a){var b=G[a]={};return n.each(a.match(F)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?G[a]||H(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&n.each(arguments,function(a,c){var d;while((d=n.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){if(a===!0?!--n.readyWait:!n.isReady){if(!z.body)return setTimeout(n.ready);n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(z,[n]),n.fn.trigger&&n(z).trigger("ready").off("ready"))}}});function J(){z.addEventListener?(z.removeEventListener("DOMContentLoaded",K,!1),a.removeEventListener("load",K,!1)):(z.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(z.addEventListener||"load"===event.type||"complete"===z.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===z.readyState)setTimeout(n.ready);else if(z.addEventListener)z.addEventListener("DOMContentLoaded",K,!1),a.addEventListener("load",K,!1);else{z.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&z.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!n.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}J(),n.ready()}}()}return I.promise(b)};var L="undefined",M;for(M in n(l))break;l.ownLast="0"!==M,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c=z.getElementsByTagName("body")[0];c&&(a=z.createElement("div"),a.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",b=z.createElement("div"),c.appendChild(a).appendChild(b),typeof b.style.zoom!==L&&(b.style.cssText="border:0;margin:0;width:1px;padding:1px;display:inline;zoom:1",(l.inlineBlockNeedsLayout=3===b.offsetWidth)&&(c.style.zoom=1)),c.removeChild(a),a=b=null)}),function(){var a=z.createElement("div");if(null==l.deleteExpando){l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}}a=null}(),n.acceptData=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(n.acceptData(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f
+}}function S(a,b,c){if(n.acceptData(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d]));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},X=/^(?:checkbox|radio)$/i;!function(){var a=z.createDocumentFragment(),b=z.createElement("div"),c=z.createElement("input");if(b.setAttribute("className","t"),b.innerHTML=" a ",l.leadingWhitespace=3===b.firstChild.nodeType,l.tbody=!b.getElementsByTagName("tbody").length,l.htmlSerialize=!!b.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==z.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,a.appendChild(c),l.appendChecked=c.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,a.appendChild(b),b.innerHTML=" ",l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){l.noCloneEvent=!1}),b.cloneNode(!0).click()),null==l.deleteExpando){l.deleteExpando=!0;try{delete b.test}catch(d){l.deleteExpando=!1}}a=b=c=null}(),function(){var b,c,d=z.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),l[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var Y=/^(?:input|select|textarea)$/i,Z=/^key/,$=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,ab=/^([^.]*)(?:\.(.+)|)$/;function bb(){return!0}function cb(){return!1}function db(){try{return z.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof n===L||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(F)||[""],h=b.length;while(h--)f=ab.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(F)||[""],j=b.length;while(j--)if(h=ab.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,m,o=[d||z],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||z,3!==d.nodeType&&8!==d.nodeType&&!_.test(p+n.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[n.expando]?b:new n.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),k=n.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!n.isWindow(d)){for(i=k.delegateType||p,_.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||z)&&o.push(l.defaultView||l.parentWindow||a)}m=0;while((h=o[m++])&&!b.isPropagationStopped())b.type=m>1?i:k.bindType||p,f=(n._data(h,"events")||{})[b.type]&&n._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&n.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&n.acceptData(d)&&g&&d[p]&&!n.isWindow(d)){l=d[g],l&&(d[g]=null),n.event.triggered=p;try{d[p]()}catch(r){}n.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((n.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?n(c,this).index(i)>=0:n.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h ]","i"),ib=/^\s+/,jb=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,kb=/<([\w:]+)/,lb=/\s*$/g,sb={option:[1,""," "],legend:[1,""," "],area:[1,""," "],param:[1,""," "],thead:[1,""],tr:[2,""],col:[2,""],td:[3,""],_default:l.htmlSerialize?[0,"",""]:[1,"X","
"]},tb=eb(z),ub=tb.appendChild(z.createElement("div"));sb.optgroup=sb.option,sb.tbody=sb.tfoot=sb.colgroup=sb.caption=sb.thead,sb.th=sb.td;function vb(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==L?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==L?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,vb(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function wb(a){X.test(a.type)&&(a.defaultChecked=a.checked)}function xb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function yb(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function zb(a){var b=qb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ab(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}function Bb(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Cb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(yb(b).text=a.text,zb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&X.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}n.extend({clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!hb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ub.innerHTML=a.outerHTML,ub.removeChild(f=ub.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=vb(f),h=vb(a),g=0;null!=(e=h[g]);++g)d[g]&&Cb(e,d[g]);if(b)if(c)for(h=h||vb(a),d=d||vb(f),g=0;null!=(e=h[g]);g++)Bb(e,d[g]);else Bb(a,f);return d=vb(f,"script"),d.length>0&&Ab(d,!i&&vb(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k,m=a.length,o=eb(b),p=[],q=0;m>q;q++)if(f=a[q],f||0===f)if("object"===n.type(f))n.merge(p,f.nodeType?[f]:f);else if(mb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(kb.exec(f)||["",""])[1].toLowerCase(),k=sb[i]||sb._default,h.innerHTML=k[1]+f.replace(jb,"<$1>$2>")+k[2],e=k[0];while(e--)h=h.lastChild;if(!l.leadingWhitespace&&ib.test(f)&&p.push(b.createTextNode(ib.exec(f)[0])),!l.tbody){f="table"!==i||lb.test(f)?""!==k[1]||lb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)n.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}n.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),l.appendChecked||n.grep(vb(p,"input"),wb),q=0;while(f=p[q++])if((!d||-1===n.inArray(f,d))&&(g=n.contains(f.ownerDocument,f),h=vb(o.appendChild(f),"script"),g&&Ab(h),c)){e=0;while(f=h[e++])pb.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.deleteExpando,m=n.event.special;null!=(d=a[h]);h++)if((b||n.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k?delete d[i]:typeof d.removeAttribute!==L?d.removeAttribute(i):d[i]=null,c.push(f))}}}),n.fn.extend({text:function(a){return W(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||z).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=xb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=xb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(vb(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&Ab(vb(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(vb(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return W(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(gb,""):void 0;if(!("string"!=typeof a||nb.test(a)||!l.htmlSerialize&&hb.test(a)||!l.leadingWhitespace&&ib.test(a)||sb[(kb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(jb,"<$1>$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(vb(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(vb(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,k=this.length,m=this,o=k-1,p=a[0],q=n.isFunction(p);if(q||k>1&&"string"==typeof p&&!l.checkClone&&ob.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(k&&(i=n.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=n.map(vb(i,"script"),yb),f=g.length;k>j;j++)d=i,j!==o&&(d=n.clone(d,!0,!0),f&&n.merge(g,vb(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,n.map(g,zb),j=0;f>j;j++)d=g[j],pb.test(d.type||"")&&!n._data(d,"globalEval")&&n.contains(h,d)&&(d.src?n._evalUrl&&n._evalUrl(d.src):n.globalEval((d.text||d.textContent||d.innerHTML||"").replace(rb,"")));i=c=null}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],g=n(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Db,Eb={};function Fb(b,c){var d=n(c.createElement(b)).appendTo(c.body),e=a.getDefaultComputedStyle?a.getDefaultComputedStyle(d[0]).display:n.css(d[0],"display");return d.detach(),e}function Gb(a){var b=z,c=Eb[a];return c||(c=Fb(a,b),"none"!==c&&c||(Db=(Db||n("")).appendTo(b.documentElement),b=(Db[0].contentWindow||Db[0].contentDocument).document,b.write(),b.close(),c=Fb(a,b),Db.detach()),Eb[a]=c),c}!function(){var a,b,c=z.createElement("div"),d="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;padding:0;margin:0;border:0";c.innerHTML=" a ",a=c.getElementsByTagName("a")[0],a.style.cssText="float:left;opacity:.5",l.opacity=/^0.5/.test(a.style.opacity),l.cssFloat=!!a.style.cssFloat,c.style.backgroundClip="content-box",c.cloneNode(!0).style.backgroundClip="",l.clearCloneStyle="content-box"===c.style.backgroundClip,a=c=null,l.shrinkWrapBlocks=function(){var a,c,e,f;if(null==b){if(a=z.getElementsByTagName("body")[0],!a)return;f="border:0;width:0;height:0;position:absolute;top:0;left:-9999px",c=z.createElement("div"),e=z.createElement("div"),a.appendChild(c).appendChild(e),b=!1,typeof e.style.zoom!==L&&(e.style.cssText=d+";width:1px;padding:1px;zoom:1",e.innerHTML="
",e.firstChild.style.width="5px",b=3!==e.offsetWidth),a.removeChild(c),a=c=e=null}return b}}();var Hb=/^margin/,Ib=new RegExp("^("+T+")(?!px)[a-z%]+$","i"),Jb,Kb,Lb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Jb=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Kb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Jb(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),Ib.test(g)&&Hb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):z.documentElement.currentStyle&&(Jb=function(a){return a.currentStyle},Kb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Jb(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Ib.test(g)&&!Lb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Mb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h=z.createElement("div"),i="border:0;width:0;height:0;position:absolute;top:0;left:-9999px",j="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;padding:0;margin:0;border:0";h.innerHTML=" a ",b=h.getElementsByTagName("a")[0],b.style.cssText="float:left;opacity:.5",l.opacity=/^0.5/.test(b.style.opacity),l.cssFloat=!!b.style.cssFloat,h.style.backgroundClip="content-box",h.cloneNode(!0).style.backgroundClip="",l.clearCloneStyle="content-box"===h.style.backgroundClip,b=h=null,n.extend(l,{reliableHiddenOffsets:function(){if(null!=c)return c;var a,b,d,e=z.createElement("div"),f=z.getElementsByTagName("body")[0];if(f)return e.setAttribute("className","t"),e.innerHTML=" a ",a=z.createElement("div"),a.style.cssText=i,f.appendChild(a).appendChild(e),e.innerHTML="",b=e.getElementsByTagName("td"),b[0].style.cssText="padding:0;margin:0;border:0;display:none",d=0===b[0].offsetHeight,b[0].style.display="",b[1].style.display="none",c=d&&0===b[0].offsetHeight,f.removeChild(a),e=f=null,c},boxSizing:function(){return null==d&&k(),d},boxSizingReliable:function(){return null==e&&k(),e},pixelPosition:function(){return null==f&&k(),f},reliableMarginRight:function(){var b,c,d,e;if(null==g&&a.getComputedStyle){if(b=z.getElementsByTagName("body")[0],!b)return;c=z.createElement("div"),d=z.createElement("div"),c.style.cssText=i,b.appendChild(c).appendChild(d),e=d.appendChild(z.createElement("div")),e.style.cssText=d.style.cssText=j,e.style.marginRight=e.style.width="0",d.style.width="1px",g=!parseFloat((a.getComputedStyle(e,null)||{}).marginRight),b.removeChild(c)}return g}});function k(){var b,c,h=z.getElementsByTagName("body")[0];h&&(b=z.createElement("div"),c=z.createElement("div"),b.style.cssText=i,h.appendChild(b).appendChild(c),c.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;display:block;padding:1px;border:1px;width:4px;margin-top:1%;top:1%",n.swap(h,null!=h.style.zoom?{zoom:1}:{},function(){d=4===c.offsetWidth}),e=!0,f=!1,g=!0,a.getComputedStyle&&(f="1%"!==(a.getComputedStyle(c,null)||{}).top,e="4px"===(a.getComputedStyle(c,null)||{width:"4px"}).width),h.removeChild(b),c=h=null)}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Nb=/alpha\([^)]*\)/i,Ob=/opacity\s*=\s*([^)]*)/,Pb=/^(none|table(?!-c[ea]).+)/,Qb=new RegExp("^("+T+")(.*)$","i"),Rb=new RegExp("^([+-])=("+T+")","i"),Sb={position:"absolute",visibility:"hidden",display:"block"},Tb={letterSpacing:0,fontWeight:400},Ub=["Webkit","O","Moz","ms"];function Vb(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Ub.length;while(e--)if(b=Ub[e]+c,b in a)return b;return d}function Wb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=n._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&V(d)&&(f[g]=n._data(d,"olddisplay",Gb(d.nodeName)))):f[g]||(e=V(d),(c&&"none"!==c||!e)&&n._data(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Xb(a,b,c){var d=Qb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Yb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+U[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+U[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+U[f]+"Width",!0,e))):(g+=n.css(a,"padding"+U[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+U[f]+"Width",!0,e)));return g}function Zb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Jb(a),g=l.boxSizing()&&"border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Kb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Ib.test(e))return e;d=g&&(l.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Yb(a,b,c||(g?"border":"content"),d,f)+"px"}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Kb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":l.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;if(b=n.cssProps[h]||(n.cssProps[h]=Vb(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Rb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),l.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]="",i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Vb(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Kb(a,b,d)),"normal"===f&&b in Tb&&(f=Tb[b]),""===c||c?(e=parseFloat(f),c===!0||n.isNumeric(e)?e||0:f):f}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?0===a.offsetWidth&&Pb.test(n.css(a,"display"))?n.swap(a,Sb,function(){return Zb(a,b,d)}):Zb(a,b,d):void 0},set:function(a,c,d){var e=d&&Jb(a);return Xb(a,c,d?Yb(a,b,d,l.boxSizing()&&"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),l.opacity||(n.cssHooks.opacity={get:function(a,b){return Ob.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=n.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===n.trim(f.replace(Nb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Nb.test(f)?f.replace(Nb,e):f+" "+e)}}),n.cssHooks.marginRight=Mb(l.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},Kb,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+U[d]+b]=f[d]||f[d-2]||f[0];return e}},Hb.test(a)||(n.cssHooks[a+b].set=Xb)}),n.fn.extend({css:function(a,b){return W(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=Jb(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)
+},a,b,arguments.length>1)},show:function(){return Wb(this,!0)},hide:function(){return Wb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){V(this)?n(this).show():n(this).hide()})}});function $b(a,b,c,d,e){return new $b.prototype.init(a,b,c,d,e)}n.Tween=$b,$b.prototype={constructor:$b,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=$b.propHooks[this.prop];return a&&a.get?a.get(this):$b.propHooks._default.get(this)},run:function(a){var b,c=$b.propHooks[this.prop];return this.pos=b=this.options.duration?n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):$b.propHooks._default.set(this),this}},$b.prototype.init.prototype=$b.prototype,$b.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},$b.propHooks.scrollTop=$b.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=$b.prototype.init,n.fx.step={};var _b,ac,bc=/^(?:toggle|show|hide)$/,cc=new RegExp("^(?:([+-])=|)("+T+")([a-z%]*)$","i"),dc=/queueHooks$/,ec=[jc],fc={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=cc.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&cc.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function gc(){return setTimeout(function(){_b=void 0}),_b=n.now()}function hc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=U[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function ic(a,b,c){for(var d,e=(fc[b]||[]).concat(fc["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function jc(a,b,c){var d,e,f,g,h,i,j,k,m=this,o={},p=a.style,q=a.nodeType&&V(a),r=n._data(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,m.always(function(){m.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=n.css(a,"display"),k=Gb(a.nodeName),"none"===j&&(j=k),"inline"===j&&"none"===n.css(a,"float")&&(l.inlineBlockNeedsLayout&&"inline"!==k?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",l.shrinkWrapBlocks()||m.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],bc.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||n.style(a,d)}if(!n.isEmptyObject(o)){r?"hidden"in r&&(q=r.hidden):r=n._data(a,"fxshow",{}),f&&(r.hidden=!q),q?n(a).show():m.done(function(){n(a).hide()}),m.done(function(){var b;n._removeData(a,"fxshow");for(b in o)n.style(a,b,o[b])});for(d in o)g=ic(q?r[d]:0,d,m),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function kc(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function lc(a,b,c){var d,e,f=0,g=ec.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=_b||gc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:_b||gc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(kc(k,j.opts.specialEasing);g>f;f++)if(d=ec[f].call(j,a,k,j.opts))return d;return n.map(k,ic,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(lc,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],fc[c]=fc[c]||[],fc[c].unshift(b)},prefilter:function(a,b){b?ec.unshift(a):ec.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(V).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=lc(this,n.extend({},a),f);(e||n._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=n._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&dc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=n._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(hc(b,!0),a,d,e)}}),n.each({slideDown:hc("show"),slideUp:hc("hide"),slideToggle:hc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=n.timers,c=0;for(_b=n.now();ca ",a=e.getElementsByTagName("a")[0],c=z.createElement("select"),d=c.appendChild(z.createElement("option")),b=e.getElementsByTagName("input")[0],a.style.cssText="top:1px",l.getSetAttribute="t"!==e.className,l.style=/top/.test(a.getAttribute("style")),l.hrefNormalized="/a"===a.getAttribute("href"),l.checkOn=!!b.value,l.optSelected=d.selected,l.enctype=!!z.createElement("form").enctype,c.disabled=!0,l.optDisabled=!d.disabled,b=z.createElement("input"),b.setAttribute("value",""),l.input=""===b.getAttribute("value"),b.value="t",b.setAttribute("type","radio"),l.radioValue="t"===b.value,a=b=c=d=e=null}();var mc=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(mc,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.text(a)}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(l.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)if(d=e[g],n.inArray(n.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},l.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var nc,oc,pc=n.expr.attrHandle,qc=/^(?:checked|selected)$/i,rc=l.getSetAttribute,sc=l.input;n.fn.extend({attr:function(a,b){return W(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===L?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?oc:nc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(F);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)?sc&&rc||!qc.test(c)?a[d]=!1:a[n.camelCase("default-"+c)]=a[d]=!1:n.attr(a,c,""),a.removeAttribute(rc?c:d)},attrHooks:{type:{set:function(a,b){if(!l.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),oc={set:function(a,b,c){return b===!1?n.removeAttr(a,c):sc&&rc||!qc.test(c)?a.setAttribute(!rc&&n.propFix[c]||c,c):a[n.camelCase("default-"+c)]=a[c]=!0,c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=pc[b]||n.find.attr;pc[b]=sc&&rc||!qc.test(b)?function(a,b,d){var e,f;return d||(f=pc[b],pc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,pc[b]=f),e}:function(a,b,c){return c?void 0:a[n.camelCase("default-"+b)]?b.toLowerCase():null}}),sc&&rc||(n.attrHooks.value={set:function(a,b,c){return n.nodeName(a,"input")?void(a.defaultValue=b):nc&&nc.set(a,b,c)}}),rc||(nc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},pc.id=pc.name=pc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},n.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:nc.set},n.attrHooks.contenteditable={set:function(a,b,c){nc.set(a,""===b?!1:b,c)}},n.each(["width","height"],function(a,b){n.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),l.style||(n.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var tc=/^(?:input|select|textarea|button|object)$/i,uc=/^(?:a|area)$/i;n.fn.extend({prop:function(a,b){return W(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return a=n.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=n.find.attr(a,"tabindex");return b?parseInt(b,10):tc.test(a.nodeName)||uc.test(a.nodeName)&&a.href?0:-1}}}}),l.hrefNormalized||n.each(["href","src"],function(a,b){n.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),l.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this}),l.enctype||(n.propFix.enctype="encoding");var vc=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(F)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(vc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(F)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(vc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(F)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===L||"boolean"===c)&&(this.className&&n._data(this,"__className__",this.className),this.className=this.className||a===!1?"":n._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(vc," ").indexOf(b)>=0)return!0;return!1}}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var wc=n.now(),xc=/\?/,yc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;n.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=n.trim(b+"");return e&&!n.trim(e.replace(yc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():n.error("Invalid JSON: "+b)},n.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||n.error("Invalid XML: "+b),c};var zc,Ac,Bc=/#.*$/,Cc=/([?&])_=[^&]*/,Dc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Ec=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Fc=/^(?:GET|HEAD)$/,Gc=/^\/\//,Hc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Ic={},Jc={},Kc="*/".concat("*");try{Ac=location.href}catch(Lc){Ac=z.createElement("a"),Ac.href="",Ac=Ac.href}zc=Hc.exec(Ac.toLowerCase())||[];function Mc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(F)||[];if(n.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nc(a,b,c,d){var e={},f=a===Jc;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Oc(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&n.extend(!0,a,c),a}function Pc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Qc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ac,type:"GET",isLocal:Ec.test(zc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Oc(Oc(a,n.ajaxSettings),b):Oc(n.ajaxSettings,a)},ajaxPrefilter:Mc(Ic),ajaxTransport:Mc(Jc),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Dc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||Ac)+"").replace(Bc,"").replace(Gc,zc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(F)||[""],null==k.crossDomain&&(c=Hc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===zc[1]&&c[2]===zc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(zc[3]||("http:"===zc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),Nc(Ic,k,b,v),2===t)return v;h=k.global,h&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Fc.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(xc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Cc.test(e)?e.replace(Cc,"$1_="+wc++):e+(xc.test(e)?"&":"?")+"_="+wc++)),k.ifModified&&(n.lastModified[e]&&v.setRequestHeader("If-Modified-Since",n.lastModified[e]),n.etag[e]&&v.setRequestHeader("If-None-Match",n.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Kc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Nc(Jc,k,b,v)){v.readyState=1,h&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Pc(k,v,c)),u=Qc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(n.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){if(n.isFunction(a))return this.each(function(b){n(this).wrapAll(a.call(this,b))});if(this[0]){var b=n(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!l.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||n.css(a,"display"))},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var Rc=/%20/g,Sc=/\[\]$/,Tc=/\r?\n/g,Uc=/^(?:submit|button|image|reset|file)$/i,Vc=/^(?:input|select|textarea|keygen)/i;function Wc(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||Sc.test(a)?d(a,e):Wc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Wc(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Wc(c,a[c],b,e);return d.join("&").replace(Rc,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&Vc.test(this.nodeName)&&!Uc.test(a)&&(this.checked||!X.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(Tc,"\r\n")}}):{name:b.name,value:c.replace(Tc,"\r\n")}}).get()}}),n.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&$c()||_c()}:$c;var Xc=0,Yc={},Zc=n.ajaxSettings.xhr();a.ActiveXObject&&n(a).on("unload",function(){for(var a in Yc)Yc[a](void 0,!0)}),l.cors=!!Zc&&"withCredentials"in Zc,Zc=l.ajax=!!Zc,Zc&&n.ajaxTransport(function(a){if(!a.crossDomain||l.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Xc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Yc[g],b=void 0,f.onreadystatechange=n.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Yc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function $c(){try{return new a.XMLHttpRequest}catch(b){}}function _c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=z.head||n("head")[0]||z.documentElement;return{send:function(d,e){b=z.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var ad=[],bd=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=ad.pop()||n.expando+"_"+wc++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(bd.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&bd.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(bd,"$1"+e):b.jsonp!==!1&&(b.url+=(xc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,ad.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||z;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var cd=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&cd)return cd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=a.slice(h,a.length),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&n.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?n("").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var dd=a.document.documentElement;function ed(a){return n.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&n.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,n.contains(b,e)?(typeof e.getBoundingClientRect!==L&&(d=e.getBoundingClientRect()),c=ed(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===n.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(c=a.offset()),c.top+=n.css(a[0],"borderTopWidth",!0),c.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-n.css(d,"marginTop",!0),left:b.left-c.left-n.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||dd;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||dd})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);n.fn[a]=function(d){return W(this,function(a,d,e){var f=ed(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?n(f).scrollLeft():e,c?e:n(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=Mb(l.pixelPosition,function(a,c){return c?(c=Kb(a,b),Ib.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return W(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var fd=a.jQuery,gd=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=gd),b&&a.jQuery===n&&(a.jQuery=fd),n},typeof b===L&&(a.jQuery=a.$=n),n});
\ No newline at end of file
diff --git a/QWebEngineView/Data/promise-7.0.4.min.js b/QWebEngineView/Data/promise-7.0.4.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..9ba76826475ea79d0c3d4127fae36aa01d71923c
--- /dev/null
+++ b/QWebEngineView/Data/promise-7.0.4.min.js
@@ -0,0 +1,2 @@
+!function n(t,e,r){function o(u,f){if(!e[u]){if(!t[u]){var c="function"==typeof require&&require;if(!f&&c)return c(u,!0);if(i)return i(u,!0);var s=new Error("Cannot find module '"+u+"'");throw s.code="MODULE_NOT_FOUND",s}var l=e[u]={exports:{}};t[u][0].call(l.exports,function(n){var e=t[u][1][n];return o(e?e:n)},l,l.exports,n,t,e,r)}return e[u].exports}for(var i="function"==typeof require&&require,u=0;u
l){for(var t=0,e=f.length-s;e>t;t++)f[t]=f[t+s];f.length-=s,s=0}}f.length=0,s=0,c=!1}function o(n){var t=1,e=new a(n),r=document.createTextNode("");return e.observe(r,{characterData:!0}),function(){t=-t,r.data=t}}function i(n){return function(){function t(){clearTimeout(e),clearInterval(r),n()}var e=setTimeout(t,0),r=setInterval(t,50)}}t.exports=e;var u,f=[],c=!1,s=0,l=1024,a=n.MutationObserver||n.WebKitMutationObserver;u="function"==typeof a?o(r):i(r),e.requestFlush=u,e.makeRequestCallFromTimer=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],5:[function(n,t,e){"function"!=typeof Promise.prototype.done&&(Promise.prototype.done=function(n,t){var e=arguments.length?this.then.apply(this,arguments):this;e.then(null,function(n){setTimeout(function(){throw n},0)})})},{}],6:[function(n,t,e){n("asap");"undefined"==typeof Promise&&(Promise=n("./lib/core.js"),n("./lib/es6-extensions.js")),n("./polyfill-done.js")},{"./lib/core.js":1,"./lib/es6-extensions.js":2,"./polyfill-done.js":5,asap:3}]},{},[6]);
+//# sourceMappingURL=/polyfills/promise-7.0.4.min.js.map
\ No newline at end of file
diff --git a/QWebEngineView/Data/qwebchannel.js b/QWebEngineView/Data/qwebchannel.js
new file mode 100644
index 0000000000000000000000000000000000000000..5b047c27b45d08e5dd6cd47930f6ec7962f8db2f
--- /dev/null
+++ b/QWebEngineView/Data/qwebchannel.js
@@ -0,0 +1,427 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+"use strict";
+
+var QWebChannelMessageTypes = {
+ signal: 1,
+ propertyUpdate: 2,
+ init: 3,
+ idle: 4,
+ debug: 5,
+ invokeMethod: 6,
+ connectToSignal: 7,
+ disconnectFromSignal: 8,
+ setProperty: 9,
+ response: 10,
+};
+
+var QWebChannel = function(transport, initCallback)
+{
+ if (typeof transport !== "object" || typeof transport.send !== "function") {
+ console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." +
+ " Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send));
+ return;
+ }
+
+ var channel = this;
+ this.transport = transport;
+
+ this.send = function(data)
+ {
+ if (typeof(data) !== "string") {
+ data = JSON.stringify(data);
+ }
+ channel.transport.send(data);
+ }
+
+ this.transport.onmessage = function(message)
+ {
+ var data = message.data;
+ if (typeof data === "string") {
+ data = JSON.parse(data);
+ }
+ switch (data.type) {
+ case QWebChannelMessageTypes.signal:
+ channel.handleSignal(data);
+ break;
+ case QWebChannelMessageTypes.response:
+ channel.handleResponse(data);
+ break;
+ case QWebChannelMessageTypes.propertyUpdate:
+ channel.handlePropertyUpdate(data);
+ break;
+ default:
+ console.error("invalid message received:", message.data);
+ break;
+ }
+ }
+
+ this.execCallbacks = {};
+ this.execId = 0;
+ this.exec = function(data, callback)
+ {
+ if (!callback) {
+ // if no callback is given, send directly
+ channel.send(data);
+ return;
+ }
+ if (channel.execId === Number.MAX_VALUE) {
+ // wrap
+ channel.execId = Number.MIN_VALUE;
+ }
+ if (data.hasOwnProperty("id")) {
+ console.error("Cannot exec message with property id: " + JSON.stringify(data));
+ return;
+ }
+ data.id = channel.execId++;
+ channel.execCallbacks[data.id] = callback;
+ channel.send(data);
+ };
+
+ this.objects = {};
+
+ this.handleSignal = function(message)
+ {
+ var object = channel.objects[message.object];
+ if (object) {
+ object.signalEmitted(message.signal, message.args);
+ } else {
+ console.warn("Unhandled signal: " + message.object + "::" + message.signal);
+ }
+ }
+
+ this.handleResponse = function(message)
+ {
+ if (!message.hasOwnProperty("id")) {
+ console.error("Invalid response message received: ", JSON.stringify(message));
+ return;
+ }
+ channel.execCallbacks[message.id](message.data);
+ delete channel.execCallbacks[message.id];
+ }
+
+ this.handlePropertyUpdate = function(message)
+ {
+ for (var i in message.data) {
+ var data = message.data[i];
+ var object = channel.objects[data.object];
+ if (object) {
+ object.propertyUpdate(data.signals, data.properties);
+ } else {
+ console.warn("Unhandled property update: " + data.object + "::" + data.signal);
+ }
+ }
+ channel.exec({type: QWebChannelMessageTypes.idle});
+ }
+
+ this.debug = function(message)
+ {
+ channel.send({type: QWebChannelMessageTypes.debug, data: message});
+ };
+
+ channel.exec({type: QWebChannelMessageTypes.init}, function(data) {
+ for (var objectName in data) {
+ var object = new QObject(objectName, data[objectName], channel);
+ }
+ // now unwrap properties, which might reference other registered objects
+ for (var objectName in channel.objects) {
+ channel.objects[objectName].unwrapProperties();
+ }
+ if (initCallback) {
+ initCallback(channel);
+ }
+ channel.exec({type: QWebChannelMessageTypes.idle});
+ });
+};
+
+function QObject(name, data, webChannel)
+{
+ this.__id__ = name;
+ webChannel.objects[name] = this;
+
+ // List of callbacks that get invoked upon signal emission
+ this.__objectSignals__ = {};
+
+ // Cache of all properties, updated when a notify signal is emitted
+ this.__propertyCache__ = {};
+
+ var object = this;
+
+ // ----------------------------------------------------------------------
+
+ this.unwrapQObject = function(response)
+ {
+ if (response instanceof Array) {
+ // support list of objects
+ var ret = new Array(response.length);
+ for (var i = 0; i < response.length; ++i) {
+ ret[i] = object.unwrapQObject(response[i]);
+ }
+ return ret;
+ }
+ if (!response
+ || !response["__QObject*__"]
+ || response.id === undefined) {
+ return response;
+ }
+
+ var objectId = response.id;
+ if (webChannel.objects[objectId])
+ return webChannel.objects[objectId];
+
+ if (!response.data) {
+ console.error("Cannot unwrap unknown QObject " + objectId + " without data.");
+ return;
+ }
+
+ var qObject = new QObject( objectId, response.data, webChannel );
+ qObject.destroyed.connect(function() {
+ if (webChannel.objects[objectId] === qObject) {
+ delete webChannel.objects[objectId];
+ // reset the now deleted QObject to an empty {} object
+ // just assigning {} though would not have the desired effect, but the
+ // below also ensures all external references will see the empty map
+ // NOTE: this detour is necessary to workaround QTBUG-40021
+ var propertyNames = [];
+ for (var propertyName in qObject) {
+ propertyNames.push(propertyName);
+ }
+ for (var idx in propertyNames) {
+ delete qObject[propertyNames[idx]];
+ }
+ }
+ });
+ // here we are already initialized, and thus must directly unwrap the properties
+ qObject.unwrapProperties();
+ return qObject;
+ }
+
+ this.unwrapProperties = function()
+ {
+ for (var propertyIdx in object.__propertyCache__) {
+ object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]);
+ }
+ }
+
+ function addSignal(signalData, isPropertyNotifySignal)
+ {
+ var signalName = signalData[0];
+ var signalIndex = signalData[1];
+ object[signalName] = {
+ connect: function(callback) {
+ if (typeof(callback) !== "function") {
+ console.error("Bad callback given to connect to signal " + signalName);
+ return;
+ }
+
+ object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
+ object.__objectSignals__[signalIndex].push(callback);
+
+ if (!isPropertyNotifySignal && signalName !== "destroyed") {
+ // only required for "pure" signals, handled separately for properties in propertyUpdate
+ // also note that we always get notified about the destroyed signal
+ webChannel.exec({
+ type: QWebChannelMessageTypes.connectToSignal,
+ object: object.__id__,
+ signal: signalIndex
+ });
+ }
+ },
+ disconnect: function(callback) {
+ if (typeof(callback) !== "function") {
+ console.error("Bad callback given to disconnect from signal " + signalName);
+ return;
+ }
+ object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
+ var idx = object.__objectSignals__[signalIndex].indexOf(callback);
+ if (idx === -1) {
+ console.error("Cannot find connection of signal " + signalName + " to " + callback.name);
+ return;
+ }
+ object.__objectSignals__[signalIndex].splice(idx, 1);
+ if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) {
+ // only required for "pure" signals, handled separately for properties in propertyUpdate
+ webChannel.exec({
+ type: QWebChannelMessageTypes.disconnectFromSignal,
+ object: object.__id__,
+ signal: signalIndex
+ });
+ }
+ }
+ };
+ }
+
+ /**
+ * Invokes all callbacks for the given signalname. Also works for property notify callbacks.
+ */
+ function invokeSignalCallbacks(signalName, signalArgs)
+ {
+ var connections = object.__objectSignals__[signalName];
+ if (connections) {
+ connections.forEach(function(callback) {
+ callback.apply(callback, signalArgs);
+ });
+ }
+ }
+
+ this.propertyUpdate = function(signals, propertyMap)
+ {
+ // update property cache
+ for (var propertyIndex in propertyMap) {
+ var propertyValue = propertyMap[propertyIndex];
+ object.__propertyCache__[propertyIndex] = propertyValue;
+ }
+
+ for (var signalName in signals) {
+ // Invoke all callbacks, as signalEmitted() does not. This ensures the
+ // property cache is updated before the callbacks are invoked.
+ invokeSignalCallbacks(signalName, signals[signalName]);
+ }
+ }
+
+ this.signalEmitted = function(signalName, signalArgs)
+ {
+ invokeSignalCallbacks(signalName, this.unwrapQObject(signalArgs));
+ }
+
+ function addMethod(methodData)
+ {
+ var methodName = methodData[0];
+ var methodIdx = methodData[1];
+ object[methodName] = function() {
+ var args = [];
+ var callback;
+ for (var i = 0; i < arguments.length; ++i) {
+ var argument = arguments[i];
+ if (typeof argument === "function")
+ callback = argument;
+ else if (argument instanceof QObject && webChannel.objects[argument.__id__] !== undefined)
+ args.push({
+ "id": argument.__id__
+ });
+ else
+ args.push(argument);
+ }
+
+ webChannel.exec({
+ "type": QWebChannelMessageTypes.invokeMethod,
+ "object": object.__id__,
+ "method": methodIdx,
+ "args": args
+ }, function(response) {
+ if (response !== undefined) {
+ var result = object.unwrapQObject(response);
+ if (callback) {
+ (callback)(result);
+ }
+ }
+ });
+ };
+ }
+
+ function bindGetterSetter(propertyInfo)
+ {
+ var propertyIndex = propertyInfo[0];
+ var propertyName = propertyInfo[1];
+ var notifySignalData = propertyInfo[2];
+ // initialize property cache with current value
+ // NOTE: if this is an object, it is not directly unwrapped as it might
+ // reference other QObject that we do not know yet
+ object.__propertyCache__[propertyIndex] = propertyInfo[3];
+
+ if (notifySignalData) {
+ if (notifySignalData[0] === 1) {
+ // signal name is optimized away, reconstruct the actual name
+ notifySignalData[0] = propertyName + "Changed";
+ }
+ addSignal(notifySignalData, true);
+ }
+
+ Object.defineProperty(object, propertyName, {
+ configurable: true,
+ get: function () {
+ var propertyValue = object.__propertyCache__[propertyIndex];
+ if (propertyValue === undefined) {
+ // This shouldn't happen
+ console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__);
+ }
+
+ return propertyValue;
+ },
+ set: function(value) {
+ if (value === undefined) {
+ console.warn("Property setter for " + propertyName + " called with undefined value!");
+ return;
+ }
+ object.__propertyCache__[propertyIndex] = value;
+ var valueToSend = value;
+ if (valueToSend instanceof QObject && webChannel.objects[valueToSend.__id__] !== undefined)
+ valueToSend = { "id": valueToSend.__id__ };
+ webChannel.exec({
+ "type": QWebChannelMessageTypes.setProperty,
+ "object": object.__id__,
+ "property": propertyIndex,
+ "value": valueToSend
+ });
+ }
+ });
+
+ }
+
+ // ----------------------------------------------------------------------
+
+ data.methods.forEach(addMethod);
+
+ data.properties.forEach(bindGetterSetter);
+
+ data.signals.forEach(function(signal) { addSignal(signal, false); });
+
+ for (var name in data.enums) {
+ object[name] = data.enums[name];
+ }
+}
+
+//required for use with nodejs
+if (typeof module === 'object') {
+ module.exports = {
+ QWebChannel: QWebChannel
+ };
+}
diff --git a/QWebEngineView/GetCookie.py b/QWebEngineView/GetCookie.py
index 11a4755f610781424d26eeb00fa3a9a9b2c7f47e..f74eae8b0711c9c24f7289f3e1cb05c2edb7a535 100644
--- a/QWebEngineView/GetCookie.py
+++ b/QWebEngineView/GetCookie.py
@@ -1,44 +1,71 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月10日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: GetCookie
@description:
-'''
-import sys
-
-from PyQt5.QtCore import QUrl
-from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
-from PyQt5.QtWidgets import QApplication
+"""
+import sys
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QUrl, QByteArray
+ from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
+ from PyQt5.QtWidgets import QApplication, QTextEdit
+except ImportError:
+ from PySide2.QtCore import QUrl, QByteArray
+ from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
+ from PySide2.QtWidgets import QApplication, QTextEdit
class WebEngineView(QWebEngineView):
-
DomainCookies = {} # 存放domain的key-value
PathCookies = {} # 存放domain+path的key-value
def __init__(self, *args, **kwargs):
super(WebEngineView, self).__init__(*args, **kwargs)
+ self.cookieView = QTextEdit()
+ self.cookieView.resize(800, 400)
+ self.cookieView.move(400, 400)
+ self.cookieView.setWindowTitle('Cookies')
+ self.cookieView.show()
# 绑定cookie被添加的信号槽
QWebEngineProfile.defaultProfile().cookieStore(
).cookieAdded.connect(self.onCookieAdd)
self.loadFinished.connect(self.onLoadFinished)
+ def closeEvent(self, event):
+ self.cookieView.close()
+ super(WebEngineView, self).closeEvent(event)
+
+ def bytestostr(self, data):
+ if isinstance(data, str):
+ return data
+ if isinstance(data, QByteArray):
+ data = data.data()
+ if isinstance(data, bytes):
+ data = data.decode(errors='ignore')
+ else:
+ data = str(data)
+ return data
+
def onLoadFinished(self):
print("*****AllDomainCookies:", self.getAllDomainCookies())
print("*****AllPathCookies:", self.getAllPathCookies())
- print("*****pyqt5.com cookie:", self.getDomainCookies(".pyqt5.com"))
- print("*****pyqt5.com / path cookie:",
- self.getPathCookies(".pyqt5.com/"))
+ self.cookieView.append(
+ "AllDomainCookies: " + self.bytestostr(self.getAllDomainCookies()))
+ self.cookieView.append('')
+ self.cookieView.append(
+ "AllPathCookies: " + self.bytestostr(self.getAllPathCookies()))
+ self.cookieView.append('')
+
+ print("*****pyqt.site cookie:", self.getDomainCookies(".pyqt.site"))
+ print("*****pyqt.site / path cookie:",
+ self.getPathCookies(".pyqt.site/"))
def getAllDomainCookies(self):
return self.DomainCookies
@@ -53,9 +80,9 @@ class WebEngineView(QWebEngineView):
return self.PathCookies.get(dpath, {})
def onCookieAdd(self, cookie):
- '''
+ """
:param cookie: QNetworkCookie
- '''
+ """
domain = cookie.domain()
path = cookie.path()
name = cookie.name().data()
@@ -71,6 +98,8 @@ class WebEngineView(QWebEngineView):
_cookie[name] = value
else:
self.PathCookies[domain_path] = {name: value}
+
+
# print("add cookie:", cookie.domain(),
# cookie.path(), cookie.name(), cookie.value())
@@ -79,5 +108,5 @@ if __name__ == "__main__":
app = QApplication(sys.argv)
w = WebEngineView()
w.show()
- w.load(QUrl("https://pyqt5.com"))
+ w.load(QUrl("https://pyqt.site"))
sys.exit(app.exec_())
diff --git a/QWebEngineView/GetRequestInfo.py b/QWebEngineView/GetRequestInfo.py
new file mode 100644
index 0000000000000000000000000000000000000000..a86d810aac934c1a2995e28afb24f79c265ec47c
--- /dev/null
+++ b/QWebEngineView/GetRequestInfo.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年9月24日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QWebEngineView.BlockAds
+@description: 拦截请求
+"""
+
+try:
+ from PyQt5.QtCore import QUrl, QByteArray
+ from PyQt5.QtWidgets import QApplication
+ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
+ from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler, QWebEngineUrlScheme, \
+ QWebEngineUrlRequestInterceptor
+ from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
+except ImportError:
+ from PySide2.QtCore import QUrl, QByteArray
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtNetwork import QNetworkAccessManager, QNetworkRequest
+ from PySide2.QtWebEngineCore import QWebEngineUrlSchemeHandler, QWebEngineUrlScheme, \
+ QWebEngineUrlRequestInterceptor
+ from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
+
+
+class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
+ AttrType = QNetworkRequest.User + 1
+
+ def __init__(self, *args, **kwargs):
+ super(UrlSchemeHandler, self).__init__(*args, **kwargs)
+ self._manager = QNetworkAccessManager(self)
+ self._manager.finished.connect(self.onFinished)
+
+ def requestStarted(self, request):
+ # 拦截
+ # request.fail(QWebEngineUrlRequestJob.RequestDenied)
+ # print('initiator:', request.initiator())
+ print('requestMethod:', request.requestMethod())
+ print('requestHeaders:', request.requestHeaders())
+ url = request.requestUrl()
+ if url.scheme().startswith('myurl'):
+ url.setScheme(url.scheme().replace('myurl', 'http'))
+ print('requestUrl:', url)
+
+ # 构造真实请求
+ req = QNetworkRequest(url)
+ req.setAttribute(self.AttrType, request) # 记录
+ for headerName, headerValue in request.requestHeaders().items():
+ req.setRawHeader(headerName, headerValue)
+ method = request.requestMethod()
+
+ # TODO: 这里需要把浏览器内部的cookie获取出来重新设置
+ if method == b'GET':
+ self._manager.get(req)
+ # TODO: 这里貌似没法得到POST的数据,ajax的请求貌似也有问题
+ elif method == b'POST':
+ self._manager.post(req)
+
+ def onFinished(self, reply):
+ req = reply.request() # 获取请求
+ o_req = req.attribute(self.AttrType, None)
+ if o_req:
+ # Notice: 这里可以对数据做修改再返回
+ # TODO: 可能还存在 QNetworkAccessManager 与浏览器之间的 cookie 同步问题
+ o_req.reply(req.header(QNetworkRequest.ContentTypeHeader) or b'text/html', reply)
+ o_req.destroyed.connect(reply.deleteLater)
+
+
+# 把所有请求重定向到myurl
+class RequestInterceptor(QWebEngineUrlRequestInterceptor):
+
+ def interceptRequest(self, info):
+ url = info.requestUrl()
+ if url.scheme() == 'http':
+ # 重定向
+ url.setScheme('myurl')
+ info.redirect(url)
+ elif url.scheme() == 'https':
+ # 重定向
+ url.setScheme('myurls')
+ info.redirect(url)
+
+
+class Window(QWebEngineView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(800, 600)
+ profile = QWebEngineProfile.defaultProfile()
+
+ # 首先获取默认的url协议
+ o_http = QWebEngineUrlScheme.schemeByName(QByteArray(b'http'))
+ o_https = QWebEngineUrlScheme.schemeByName(QByteArray(b'https'))
+ print('scheme:', o_http, o_https)
+
+ # 这里需要修改增加本地文件和跨域支持
+ CorsEnabled = 0x80 # 5.14才增加
+ o_http.setFlags(o_http.flags() |
+ QWebEngineUrlScheme.SecureScheme |
+ QWebEngineUrlScheme.LocalScheme |
+ QWebEngineUrlScheme.LocalAccessAllowed |
+ CorsEnabled)
+ o_https.setFlags(o_https.flags() |
+ QWebEngineUrlScheme.SecureScheme |
+ QWebEngineUrlScheme.LocalScheme |
+ QWebEngineUrlScheme.LocalAccessAllowed |
+ CorsEnabled)
+
+ # 安装url拦截器和自定义url协议处理
+ de = QWebEngineProfile.defaultProfile() # @UndefinedVariable
+ de.setRequestInterceptor(RequestInterceptor(self))
+ self.urlSchemeHandler = UrlSchemeHandler(self)
+ de.installUrlSchemeHandler(QByteArray(b'myurl'), self.urlSchemeHandler) # for http
+ de.installUrlSchemeHandler(QByteArray(b'myurls'), self.urlSchemeHandler) # for https
+
+
+if __name__ == '__main__':
+ import sys
+ import os
+ import webbrowser
+ import cgitb
+
+ cgitb.enable(format='text')
+
+ app = QApplication(sys.argv)
+ # 开启F12 控制台功能,需要单独通过浏览器打开这个页面
+ # 这里可以做个保护, 发布软件,启动时把这个环境变量删掉。防止他人通过环境变量开启
+ os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = '9966'
+ # 打开调试页面
+ webbrowser.open_new_tab('http://127.0.0.1:9966')
+
+ w = Window()
+ w.show()
+ w.load(QUrl('https://pyqt.site'))
+ sys.exit(app.exec_())
diff --git a/QWebEngineView/JsSignals.py b/QWebEngineView/JsSignals.py
new file mode 100644
index 0000000000000000000000000000000000000000..aac940196d6fce8c2d56fcf7e84780233f9e44e6
--- /dev/null
+++ b/QWebEngineView/JsSignals.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年4月27日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QWebEngineView.JsSignals
+@description:
+"""
+import os
+from time import time
+
+try:
+ from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal
+ from PyQt5.QtWebChannel import QWebChannel
+ from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
+ from PyQt5.QtWidgets import QMessageBox, QWidget, QVBoxLayout, QPushButton
+except ImportError:
+ from PySide2.QtCore import QUrl, Slot as pyqtSlot, Signal as pyqtSignal
+ from PySide2.QtWebChannel import QWebChannel
+ from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
+ from PySide2.QtWidgets import QMessageBox, QWidget, QVBoxLayout, QPushButton
+
+
+class WebEngineView(QWebEngineView):
+ customSignal = pyqtSignal(str)
+
+ def __init__(self, *args, **kwargs):
+ super(WebEngineView, self).__init__(*args, **kwargs)
+ self.initSettings()
+ self.channel = QWebChannel(self)
+ # 把自身对象传递进去
+ self.channel.registerObject('Bridge', self)
+ # 设置交互接口
+ self.page().setWebChannel(self.channel)
+
+ # START #####以下代码可能是在5.6 QWebEngineView刚出来时的bug,必须在每次加载页面的时候手动注入
+ #### 也有可能是跳转页面后就失效了,需要手动注入,有没有修复具体未测试
+
+ # self.page().loadStarted.connect(self.onLoadStart)
+ # self._script = open('Data/qwebchannel.js', 'rb').read().decode()
+
+ # def onLoadStart(self):
+ # self.page().runJavaScript(self._script)
+
+ # END ###########################
+
+ # 注意pyqtSlot用于把该函数暴露给js可以调用
+ @pyqtSlot(str)
+ def callFromJs(self, text):
+ QMessageBox.information(self, "提示", "来自js调用:{}".format(text))
+
+ def sendCustomSignal(self):
+ # 发送自定义信号
+ self.customSignal.emit('当前时间: ' + str(time()))
+
+ @pyqtSlot(str)
+ @pyqtSlot(QUrl)
+ def load(self, url):
+ """
+ eg: load("https://pyqt.site")
+ :param url: 网址
+ """
+ return super(WebEngineView, self).load(QUrl(url))
+
+ def initSettings(self):
+ """
+ eg: 初始化设置
+ """
+ # 获取浏览器默认设置
+ settings = QWebEngineSettings.globalSettings()
+ # 设置默认编码utf8
+ settings.setDefaultTextEncoding("utf-8")
+ # 自动加载图片,默认开启
+ # settings.setAttribute(QWebEngineSettings.AutoLoadImages,True)
+ # 自动加载图标,默认开启
+ # settings.setAttribute(QWebEngineSettings.AutoLoadIconsForPage,True)
+ # 开启js,默认开启
+ # settings.setAttribute(QWebEngineSettings.JavascriptEnabled,True)
+ # js可以访问剪贴板
+ settings.setAttribute(
+ QWebEngineSettings.JavascriptCanAccessClipboard, True)
+ # js可以打开窗口,默认开启
+ # settings.setAttribute(QWebEngineSettings.JavascriptCanOpenWindows,True)
+ # 链接获取焦点时的状态,默认开启
+ # settings.setAttribute(QWebEngineSettings.LinksIncludedInFocusChain,True)
+ # 本地储存,默认开启
+ # settings.setAttribute(QWebEngineSettings.LocalStorageEnabled,True)
+ # 本地访问远程
+ settings.setAttribute(
+ QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)
+ # 本地加载,默认开启
+ # settings.setAttribute(QWebEngineSettings.LocalContentCanAccessFileUrls,True)
+ # 监控负载要求跨站点脚本,默认关闭
+ # settings.setAttribute(QWebEngineSettings.XSSAuditingEnabled,False)
+ # 空间导航特性,默认关闭
+ # settings.setAttribute(QWebEngineSettings.SpatialNavigationEnabled,False)
+ # 支持平超链接属性,默认关闭
+ # settings.setAttribute(QWebEngineSettings.HyperlinkAuditingEnabled,False)
+ # 使用滚动动画,默认关闭
+ settings.setAttribute(QWebEngineSettings.ScrollAnimatorEnabled, True)
+ # 支持错误页面,默认启用
+ # settings.setAttribute(QWebEngineSettings.ErrorPageEnabled, True)
+ # 支持插件,默认关闭
+ settings.setAttribute(QWebEngineSettings.PluginsEnabled, True)
+ # 支持全屏应用程序,默认关闭
+ settings.setAttribute(
+ QWebEngineSettings.FullScreenSupportEnabled, True)
+ # 支持屏幕截屏,默认关闭
+ settings.setAttribute(QWebEngineSettings.ScreenCaptureEnabled, True)
+ # 支持html5 WebGl,默认开启
+ settings.setAttribute(QWebEngineSettings.WebGLEnabled, True)
+ # 支持2d绘制,默认开启
+ settings.setAttribute(
+ QWebEngineSettings.Accelerated2dCanvasEnabled, True)
+ # 支持图标触摸,默认关闭
+ settings.setAttribute(QWebEngineSettings.TouchIconsEnabled, True)
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+ self.webview = WebEngineView(self)
+ layout.addWidget(self.webview)
+ layout.addWidget(QPushButton(
+ '发送自定义信号', self, clicked=self.webview.sendCustomSignal))
+
+ self.webview.windowTitleChanged.connect(self.setWindowTitle)
+ self.webview.load(QUrl.fromLocalFile(
+ os.path.abspath('Data/JsSignals.html')))
+
+
+if __name__ == '__main__':
+ from PyQt5.QtWidgets import QApplication
+ import sys
+
+ # 开启F12 控制台功能,需要单独通过浏览器打开这个页面
+ # 这里可以做个保护, 发布软件,启动时把这个环境变量删掉。防止他人通过环境变量开启
+ os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = '9966'
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ w.move(100, 100)
+
+ # 打开调试页面
+ dw = QWebEngineView()
+ dw.setWindowTitle('开发人员工具')
+ dw.load(QUrl('http://127.0.0.1:9966'))
+ dw.move(600, 100)
+ dw.show()
+ sys.exit(app.exec_())
diff --git a/QWebEngineView/README.md b/QWebEngineView/README.md
index 5650216974e5357bcced9ed042d023695ba5d1e8..c916f999cdbf58799415216476a8b635ac5d3172 100644
--- a/QWebEngineView/README.md
+++ b/QWebEngineView/README.md
@@ -1,8 +1,64 @@
# QWebEngineView
-## 1、QWebEngineView
+- 目录
+ - [获取Cookie](#1获取Cookie)
+ - [和Js交互操作](#2和Js交互操作)
+ - [网页整体截图](#3网页整体截图)
+ - [同网站不同用户](#4同网站不同用户)
+ - [拦截请求](#5拦截请求)
+ - [拦截请求内容](#6拦截请求内容)
+ - [设置Cookie](#7设置Cookie)
+
+## 1、获取Cookie
[运行 GetCookie.py](GetCookie.py)
通过`QWebEngineProfile`中得到的`cookieStore`并绑定它的`cookieAdded`信号来得到Cookie
-
\ No newline at end of file
+
+
+## 2、和Js交互操作
+[运行 JsSignals.py](JsSignals.py)
+
+通过`qwebchannel.js`和`QWebChannel.registerObject`进行Python对象和Javascript的交互
+
+具体看代码中的注释
+
+
+
+## 3、网页整体截图
+[运行 ScreenShotPage.py](ScreenShotPage.py)
+
+1. 方式1:目前通过不完美方法(先调整`QWebEngineView`的大小为`QWebEnginePage`的内容大小,等待一定时间后截图再还原大小)
+2. 方式2:通过js库`html2canvas`对指定元素截图,得到`base64`编码的数据并调用接口函数传递到py代码中
+
+
+
+## 4、同网站不同用户
+[运行 SiteDiffUser.py](SiteDiffUser.py)
+
+原理是为每个`QWebEngineView`创建一个`QWebEnginePage`,且使用独立的`QWebEngineProfile`,并配置`persistentStoragePath`不同路径
+
+
+
+## 5、拦截请求
+[运行 BlockRequest.py](BlockRequest.py)
+
+通过`QWebEngineUrlRequestInterceptor`中的`interceptRequest`方法对每个请求做拦截过滤
+
+
+
+## 6、拦截请求内容
+[运行 BlockRequestData.py](BlockRequestData.py)
+
+这里用了一个投巧的办法,原理是先通过`QWebEngineUrlRequestInterceptor`中的`interceptRequest`方法对每个请求做拦截过滤,
+发现目标url后重定向到`QWebEngineUrlSchemeHandler`实现的自定义协议头返回数据
+
+
+
+## 7、设置Cookie
+[运行 SetCookies.py](SetCookies.py)
+
+通过`QWebEngineProfile`中得到的`cookieStore`来添加`QNetworkCookie`对象实现,
+需要注意的是httpOnly=true时,通过js无法获取
+
+
\ No newline at end of file
diff --git a/QWebEngineView/ScreenShot/BlockRequest.png b/QWebEngineView/ScreenShot/BlockRequest.png
new file mode 100644
index 0000000000000000000000000000000000000000..657cfc5d168886f7682687164fdeef0a0781a30b
Binary files /dev/null and b/QWebEngineView/ScreenShot/BlockRequest.png differ
diff --git a/QWebEngineView/ScreenShot/BlockRequestData.png b/QWebEngineView/ScreenShot/BlockRequestData.png
new file mode 100644
index 0000000000000000000000000000000000000000..a2999d9c06f9dca9f36a590202c461383efb29c1
Binary files /dev/null and b/QWebEngineView/ScreenShot/BlockRequestData.png differ
diff --git a/QWebEngineView/ScreenShot/JsSignals.gif b/QWebEngineView/ScreenShot/JsSignals.gif
new file mode 100644
index 0000000000000000000000000000000000000000..1629e8e9eb2f8dcdb49f89d00cc9e73495c4ede5
Binary files /dev/null and b/QWebEngineView/ScreenShot/JsSignals.gif differ
diff --git a/QWebEngineView/ScreenShot/ScreenShotPage.gif b/QWebEngineView/ScreenShot/ScreenShotPage.gif
new file mode 100644
index 0000000000000000000000000000000000000000..1f514428ac2eadab0e23f364d6fbcc4ee07a5e26
Binary files /dev/null and b/QWebEngineView/ScreenShot/ScreenShotPage.gif differ
diff --git a/QWebEngineView/ScreenShot/SetCookies.png b/QWebEngineView/ScreenShot/SetCookies.png
new file mode 100644
index 0000000000000000000000000000000000000000..01c98c1dd385791bd7d11f9a26d36e663d0101e7
Binary files /dev/null and b/QWebEngineView/ScreenShot/SetCookies.png differ
diff --git a/QWebEngineView/ScreenShot/SiteDiffUser.gif b/QWebEngineView/ScreenShot/SiteDiffUser.gif
new file mode 100644
index 0000000000000000000000000000000000000000..dd135a0bb95c322113d0563fc10e80d7ac737814
Binary files /dev/null and b/QWebEngineView/ScreenShot/SiteDiffUser.gif differ
diff --git a/QWebEngineView/ScreenShotPage.py b/QWebEngineView/ScreenShotPage.py
new file mode 100644
index 0000000000000000000000000000000000000000..bef64383a8d8107a2d418cb13f031c20407d9d47
--- /dev/null
+++ b/QWebEngineView/ScreenShotPage.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年7月8日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ScreenShotPage
+@description: 网页整体截图
+"""
+import base64
+import cgitb
+import os
+import sys
+
+try:
+ from PyQt5.QtCore import QUrl, Qt, pyqtSlot, QSize, QTimer, QPoint
+ from PyQt5.QtGui import QImage, QPainter, QIcon, QPixmap
+ from PyQt5.QtWebChannel import QWebChannel
+ from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
+ from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButton, \
+ QGroupBox, QLineEdit, QHBoxLayout, QListWidget, QListWidgetItem, \
+ QProgressDialog
+except ImportError:
+ from PySide2.QtCore import QUrl, Qt, Slot as pyqtSlot, QSize, QTimer, QPoint
+ from PySide2.QtGui import QImage, QPainter, QIcon, QPixmap
+ from PySide2.QtWebChannel import QWebChannel
+ from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
+ from PySide2.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButton, \
+ QGroupBox, QLineEdit, QHBoxLayout, QListWidget, QListWidgetItem, \
+ QProgressDialog
+
+# 对部分内容进行截图
+CODE = """
+var el = $("%s");
+html2canvas(el[0], {
+ width: el.outerWidth(true),
+ windowWidth: el.outerWidth(true),
+}).then(function(canvas) {
+ window._self.saveImage(canvas.toDataURL());
+ canvas = null;
+});
+"""
+
+# 创建交互桥梁脚本
+CreateBridge = """
+new QWebChannel(qt.webChannelTransport,
+ function(channel) {
+ window._self = channel.objects._self;
+ }
+);
+"""
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(600, 400)
+ layout = QHBoxLayout(self)
+
+ # 左侧
+ widgetLeft = QWidget(self)
+ layoutLeft = QVBoxLayout(widgetLeft)
+ # 右侧
+ self.widgetRight = QListWidget(
+ self, minimumWidth=200, iconSize=QSize(150, 150))
+ self.widgetRight.setViewMode(QListWidget.IconMode)
+ layout.addWidget(widgetLeft)
+ layout.addWidget(self.widgetRight)
+
+ self.webView = QWebEngineView()
+ layoutLeft.addWidget(self.webView)
+
+ # 截图方式一
+ groupBox1 = QGroupBox('截图方式一', self)
+ layout1 = QVBoxLayout(groupBox1)
+ layout1.addWidget(QPushButton('截图1', self, clicked=self.onScreenShot1))
+ layoutLeft.addWidget(groupBox1)
+
+ # 截图方式二(采用js)
+ groupBox2 = QGroupBox('截图方式二', self)
+ layout2 = QVBoxLayout(groupBox2)
+ self.codeEdit = QLineEdit(
+ 'body', groupBox2, placeholderText='请输入需要截图的元素、ID或者class:如body、#id .class')
+ layout2.addWidget(self.codeEdit)
+ self.btnMethod2 = QPushButton(
+ '', self, clicked=self.onScreenShot2, enabled=False)
+ layout2.addWidget(self.btnMethod2)
+ layoutLeft.addWidget(groupBox2)
+
+ # 提供访问接口
+ self.channel = QWebChannel(self)
+ # 把自身对象传递进去
+ self.channel.registerObject('_self', self)
+ # 设置交互接口
+ self.webView.page().setWebChannel(self.channel)
+ # 支持截图
+ settings = QWebEngineSettings.globalSettings()
+ settings.setAttribute(QWebEngineSettings.ScreenCaptureEnabled, True)
+ self.webView.loadStarted.connect(self.onLoadStarted)
+ self.webView.loadFinished.connect(self.onLoadFinished)
+ self.webView.load(QUrl("https://pyqt.site"))
+
+ def onLoadStarted(self):
+ print('load started')
+ self.btnMethod2.setEnabled(False)
+ self.btnMethod2.setText('暂时无法使用(等待页面加载完成)')
+
+ @pyqtSlot(bool)
+ def onLoadFinished(self, finished):
+ if not finished:
+ return
+ print('load finished')
+ # 注入脚本
+ page = self.webView.page()
+ # 执行qwebchannel,jquery,promise,html2canvas
+ page.runJavaScript(
+ open('Data/qwebchannel.js', 'rb').read().decode())
+ page.runJavaScript(
+ open('Data/jquery.js', 'rb').read().decode())
+ # page.runJavaScript(
+ # open('Data/promise-7.0.4.min.js', 'rb').read().decode())
+ page.runJavaScript(
+ open('Data/html2canvas.min.js', 'rb').read().decode())
+ page.runJavaScript(CreateBridge)
+ print('inject js ok')
+ self.btnMethod2.setText('截图2')
+ self.btnMethod2.setEnabled(True)
+
+ def onScreenShot1(self):
+ # 截图方式1
+ page = self.webView.page()
+ oldSize = self.webView.size()
+ self.webView.resize(page.contentsSize().toSize())
+
+ def doScreenShot():
+ rect = self.webView.contentsRect()
+ size = rect.size()
+ image = QImage(size, QImage.Format_ARGB32_Premultiplied)
+ image.fill(Qt.transparent)
+
+ painter = QPainter()
+ painter.begin(image)
+ painter.setRenderHint(QPainter.Antialiasing, True)
+ painter.setRenderHint(QPainter.TextAntialiasing, True)
+ painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
+
+ self.webView.render(painter, QPoint())
+ painter.end()
+ self.webView.resize(oldSize)
+
+ # 添加到左侧list中
+ item = QListWidgetItem(self.widgetRight)
+ image = QPixmap.fromImage(image)
+ item.setIcon(QIcon(image))
+ item.setData(Qt.UserRole + 1, image)
+
+ # 先等一下再截图吧
+ QTimer.singleShot(2000, doScreenShot)
+
+ def onScreenShot2(self):
+ # 截图方式2
+ code = self.codeEdit.text().strip()
+ if not code:
+ return
+ self.progressdialog = QProgressDialog(self, windowTitle='正在截图中')
+ self.progressdialog.setRange(0, 0)
+ self.webView.page().runJavaScript(CODE % code)
+ self.progressdialog.exec_()
+
+ @pyqtSlot(str)
+ def saveImage(self, image):
+ self.progressdialog.close()
+ # data:image/png;base64,iVBORw0KG....
+ if not image.startswith('data:image'):
+ return
+ data = base64.b64decode(image.split(';base64,')[1])
+ image = QPixmap()
+ image.loadFromData(data)
+ # 添加到左侧list中
+ item = QListWidgetItem(self.widgetRight)
+ item.setIcon(QIcon(image))
+ item.setData(Qt.UserRole + 1, image)
+
+
+if __name__ == '__main__':
+ # 开启F12 控制台功能,需要单独通过浏览器打开这个页面
+ # 这里可以做个保护, 发布软件,启动时把这个环境变量删掉。防止他人通过环境变量开启
+ os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = '9966'
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+
+ # 打开调试页面
+ dw = QWebEngineView()
+ dw.setWindowTitle('开发人员工具')
+ dw.load(QUrl('http://127.0.0.1:9966'))
+ dw.show()
+ dw.move(100, 100)
+ sys.exit(app.exec_())
diff --git a/QWebEngineView/SetCookies.py b/QWebEngineView/SetCookies.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a85837812dcc8f09e5856b4ae27a8fbdb454f8d
--- /dev/null
+++ b/QWebEngineView/SetCookies.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2022/05/12
+@author: Irony
+@site: https://pyqt.site https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: SetCookies.py
+@description: 主动设置Cookie
+"""
+
+try:
+ from PyQt5.QtCore import QDateTime, Qt, QUrl
+ from PyQt5.QtWebEngineWidgets import QWebEngineProfile, QWebEngineView
+ from PyQt5.QtWidgets import QApplication
+ from PyQt5.QtNetwork import QNetworkCookie
+except ImportError:
+ from PySide2.QtCore import QDateTime, Qt, QUrl
+ from PySide2.QtWebEngineWidgets import QWebEngineProfile, QWebEngineView
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtNetwork import QNetworkCookie
+
+cookies = [{
+ "domain": "pyqt.site",
+ "expirationDate": 1714906174.734258,
+ "hostOnly": True,
+ "httpOnly": False,
+ "name": "snexid",
+ "path": "/",
+ "sameSite": None,
+ "secure": False,
+ "session": False,
+ "storeId": None,
+ "value": "1-22-333-4444-55555"
+}, {
+ "domain": "pyqt.site",
+ "expirationDate": 1714906174.734258,
+ "hostOnly": True,
+ "httpOnly": True,
+ "name": "testonly",
+ "path": "/",
+ "secure": True,
+ "value": "testonly"
+}, {
+ "domain": "pyqt.site",
+ "hostOnly": True,
+ "httpOnly": False,
+ "name": "test",
+ "path": "/",
+ "secure": False,
+ "value": "test"
+}]
+
+
+class Window(QWebEngineView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ # global
+ # self.cookieStore = QWebEngineProfile.defaultProfile().cookieStore()
+
+ # current
+ self.cookieStore = self.page().profile().cookieStore()
+ self.initCookies()
+ self.loadProgress.connect(self.onLoadProgress)
+ self.load(QUrl('https://pyqt.site'))
+
+ def onLoadProgress(self, progress):
+ if progress == 100:
+ # 测试获取cookie
+ self.page().runJavaScript('alert(document.cookie);')
+
+ def initCookies(self):
+ for cookie in cookies:
+ qcookie = QNetworkCookie()
+ qcookie.setName(cookie.get('name', '').encode())
+ qcookie.setValue(cookie.get('value', '').encode())
+ qcookie.setDomain(cookie.get('domain', ''))
+ qcookie.setPath(cookie.get('path', ''))
+ qcookie.setExpirationDate(
+ QDateTime.fromString(str(cookie.get('expirationDate', 0)),
+ Qt.ISODate))
+ qcookie.setHttpOnly(cookie.get('httpOnly', False))
+ qcookie.setSecure(cookie.get('secure', False))
+ # 注意可以设置具体的url
+ self.cookieStore.setCookie(qcookie, QUrl())
+
+
+if __name__ == '__main__':
+ import cgitb
+ import sys
+
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QWebEngineView/SiteDiffUser.py b/QWebEngineView/SiteDiffUser.py
new file mode 100644
index 0000000000000000000000000000000000000000..3b30b642d1b82051597d58de45bdf7a691980554
--- /dev/null
+++ b/QWebEngineView/SiteDiffUser.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Created on 2019年8月23日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QWebEngineView.SiteDiffUser
+@description: 同个网站不同用户
+"""
+
+import os
+
+try:
+ from PyQt5.QtCore import QUrl
+ from PyQt5.QtWebEngineWidgets import (QWebEnginePage, QWebEngineProfile,
+ QWebEngineView)
+ from PyQt5.QtWidgets import QApplication, QTabWidget
+except ImportError:
+ from PySide2.QtCore import QUrl
+ from PySide2.QtWebEngineWidgets import (QWebEnginePage, QWebEngineProfile,
+ QWebEngineView)
+ from PySide2.QtWidgets import QApplication, QTabWidget
+
+
+class Window(QTabWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+
+ # 用户1
+ self.webView1 = QWebEngineView(self)
+ profile1 = QWebEngineProfile('storage1', self.webView1)
+ # 要设置绝对路径,否则会出现部分问题
+ # 比如 reCaptcha 不能加载,见 https://github.com/PyQt5/PyQt/issues/125
+ profile1.setPersistentStoragePath(os.path.abspath('Tmp/Storage1'))
+ print(profile1.cookieStore())
+ # 如果要清除cookie
+ # cookieStore = profile1.cookieStore()
+ # cookieStore.deleteAllCookies()
+ # cookieStore.deleteSessionCookies()
+ page1 = QWebEnginePage(profile1, self.webView1)
+ self.webView1.setPage(page1)
+ self.addTab(self.webView1, '用户1')
+
+ # 用户2
+ self.webView2 = QWebEngineView(self)
+ profile2 = QWebEngineProfile('storage2', self.webView2)
+ profile2.setPersistentStoragePath(os.path.abspath('Tmp/Storage2'))
+ print(profile2.cookieStore())
+ page2 = QWebEnginePage(profile2, self.webView2)
+ self.webView2.setPage(page2)
+ self.addTab(self.webView2, '用户2')
+
+ self.webView1.load(QUrl('https://v.qq.com'))
+ self.webView2.load(QUrl('https://v.qq.com'))
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QWebView/BlockRequest.py b/QWebView/BlockRequest.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd50c6ec7719ff42dedacf6bdd930d4a6a288eb8
--- /dev/null
+++ b/QWebView/BlockRequest.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年9月24日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QWebView.BlockAds
+@description: 拦截请求
+"""
+from PyQt5.QtCore import QUrl, QBuffer
+from PyQt5.QtNetwork import QNetworkAccessManager
+from PyQt5.QtWebKitWidgets import QWebView
+
+
+class RequestInterceptor(QNetworkAccessManager):
+
+ def createRequest(self, op, originalReq, outgoingData):
+ """创建请求
+ :param op: 操作类型见http://doc.qt.io/qt-5/qnetworkaccessmanager.html#Operation-enum
+ :param originalReq: 原始请求
+ :param outgoingData: 输出数据
+ """
+ url = originalReq.url().toString()
+ if url.find('pos.baidu.com') > -1 and url.find('ltu=') > -1:
+ # 拦截百度联盟的广告
+ print('block:', url)
+ originalReq.setUrl(QUrl())
+ if op == self.PostOperation and outgoingData:
+ # 拦截或者修改post数据
+ # 读取后要重新设置,不然网站接收不到请求
+ data = outgoingData.readAll().data()
+ print('post data:', data)
+ # 修改data后重新设置
+ outgoingData = QBuffer(self)
+ outgoingData.setData(data)
+
+ return super(RequestInterceptor, self).createRequest(op, originalReq, outgoingData)
+
+
+class Window(QWebView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(800, 600)
+ self.page().setNetworkAccessManager(RequestInterceptor(self))
+
+
+if __name__ == '__main__':
+ import sys
+ from PyQt5.QtWidgets import QApplication
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ w.load(QUrl('https://so.csdn.net/so/search/s.do?q=Qt&t=blog'))
+ sys.exit(app.exec_())
diff --git a/QWebView/Data/JsSignals.html b/QWebView/Data/JsSignals.html
new file mode 100644
index 0000000000000000000000000000000000000000..2a9c1f10f244392b3aa44b61c1d87e1952827ca7
--- /dev/null
+++ b/QWebView/Data/JsSignals.html
@@ -0,0 +1,39 @@
+
+
+
+ 测试
+
+
+
+ 1、测试修改窗口属性
+ windowTitle 是Qt窗口本身的属性, 当标题被修改后会触发本身的windowTitleChanged信号
+
+
+ 测试修改标题
+
+ 2、调用Python中的方法callFromJs
+ callFromJs(str) 函数是由Python代码中定义, 通过@pyqtSlot(str)装饰器暴露出来
+ Bridge.callFromJs('test')
+
+ 3、发送自定义信号
+ 点击底部的按钮将会发送customSignal信号出来,网页中收到该信号将会在下面日志框输出内容
+
+ 日志
+
+
+
diff --git a/QWebView/Data/NPSWF32.dll b/QWebView/Data/NPSWF32.dll
new file mode 100644
index 0000000000000000000000000000000000000000..9f2417175af056347a663ebea3fe16e260b3f6c8
Binary files /dev/null and b/QWebView/Data/NPSWF32.dll differ
diff --git a/QWebView/Data/html2canvas.min.js b/QWebView/Data/html2canvas.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..c1b8cc998eb989ed8e9c3c561ae9c04c43f5f1a8
--- /dev/null
+++ b/QWebView/Data/html2canvas.min.js
@@ -0,0 +1,20 @@
+/*!
+ * html2canvas 1.0.0-rc.3
+ * Copyright (c) 2019 Niklas von Hertzen
+ * Released under MIT License
+ */
+!function(A,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(A=A||self).html2canvas=e();window.html2canvas = e()}(this,function(){"use strict";
+/*! *****************************************************************************
+ Copyright (c) Microsoft Corporation. All rights reserved.
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+ this file except in compliance with the License. You may obtain a copy of the
+ License at http://www.apache.org/licenses/LICENSE-2.0
+
+ THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+ WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+ MERCHANTABLITY OR NON-INFRINGEMENT.
+
+ See the Apache Version 2.0 License for specific language governing permissions
+ and limitations under the License.
+ ***************************************************************************** */var r=function(A,e){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(A,e){A.__proto__=e}||function(A,e){for(var t in e)e.hasOwnProperty(t)&&(A[t]=e[t])})(A,e)};function A(A,e){function t(){this.constructor=A}r(A,e),A.prototype=null===e?Object.create(e):(t.prototype=e.prototype,new t)}var K=function(){return(K=Object.assign||function(A){for(var e,t=1,r=arguments.length;ts[0]&&e[1]>10),s%1024+56320)),(B+1===t||16384>5])<<2)+(31&A),this.data[e];if(A<=65535)return e=((e=this.index[2048+(A-55296>>5)])<<2)+(31&A),this.data[e];if(A>11),e=this.index[e],e+=A>>5&63,e=((e=this.index[e])<<2)+(31&A),this.data[e];if(A<=1114111)return this.data[this.highValueIndex]}return this.errorValue},i);function i(A,e,t,r,B,n){this.initialValue=A,this.errorValue=e,this.highStart=t,this.highValueIndex=r,this.index=B,this.data=n}function C(A,e,t,r){var B=r[t];if(Array.isArray(A)?-1!==A.indexOf(B):A===B)for(var n=t;n<=r.length;){if((i=r[++n])===e)return!0;if(i!==H)break}if(B===H)for(n=t;0>4,c[i++]=(15&r)<<4|B>>2,c[i++]=(3&B)<<6|63&n;return a}("KwAAAAAAAAAACA4AIDoAAPAfAAACAAAAAAAIABAAGABAAEgAUABYAF4AZgBeAGYAYABoAHAAeABeAGYAfACEAIAAiACQAJgAoACoAK0AtQC9AMUAXgBmAF4AZgBeAGYAzQDVAF4AZgDRANkA3gDmAOwA9AD8AAQBDAEUARoBIgGAAIgAJwEvATcBPwFFAU0BTAFUAVwBZAFsAXMBewGDATAAiwGTAZsBogGkAawBtAG8AcIBygHSAdoB4AHoAfAB+AH+AQYCDgIWAv4BHgImAi4CNgI+AkUCTQJTAlsCYwJrAnECeQKBAk0CiQKRApkCoQKoArACuALAAsQCzAIwANQC3ALkAjAA7AL0AvwCAQMJAxADGAMwACADJgMuAzYDPgOAAEYDSgNSA1IDUgNaA1oDYANiA2IDgACAAGoDgAByA3YDfgOAAIQDgACKA5IDmgOAAIAAogOqA4AAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAK8DtwOAAIAAvwPHA88D1wPfAyAD5wPsA/QD/AOAAIAABAQMBBIEgAAWBB4EJgQuBDMEIAM7BEEEXgBJBCADUQRZBGEEaQQwADAAcQQ+AXkEgQSJBJEEgACYBIAAoASoBK8EtwQwAL8ExQSAAIAAgACAAIAAgACgAM0EXgBeAF4AXgBeAF4AXgBeANUEXgDZBOEEXgDpBPEE+QQBBQkFEQUZBSEFKQUxBTUFPQVFBUwFVAVcBV4AYwVeAGsFcwV7BYMFiwWSBV4AmgWgBacFXgBeAF4AXgBeAKsFXgCyBbEFugW7BcIFwgXIBcIFwgXQBdQF3AXkBesF8wX7BQMGCwYTBhsGIwYrBjMGOwZeAD8GRwZNBl4AVAZbBl4AXgBeAF4AXgBeAF4AXgBeAF4AXgBeAGMGXgBqBnEGXgBeAF4AXgBeAF4AXgBeAF4AXgB5BoAG4wSGBo4GkwaAAIADHgR5AF4AXgBeAJsGgABGA4AAowarBrMGswagALsGwwbLBjAA0wbaBtoG3QbaBtoG2gbaBtoG2gblBusG8wb7BgMHCwcTBxsHCwcjBysHMAc1BzUHOgdCB9oGSgdSB1oHYAfaBloHaAfaBlIH2gbaBtoG2gbaBtoG2gbaBjUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHbQdeAF4ANQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQd1B30HNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1B4MH2gaKB68EgACAAIAAgACAAIAAgACAAI8HlwdeAJ8HpweAAIAArwe3B14AXgC/B8UHygcwANAH2AfgB4AA6AfwBz4B+AcACFwBCAgPCBcIogEYAR8IJwiAAC8INwg/CCADRwhPCFcIXwhnCEoDGgSAAIAAgABvCHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIfQh3CHgIeQh6CHsIfAh9CHcIeAh5CHoIewh8CH0Idwh4CHkIegh7CHwIhAiLCI4IMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwAJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlggwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAANQc1BzUHNQc1BzUHNQc1BzUHNQc1B54INQc1B6II2gaqCLIIugiAAIAAvgjGCIAAgACAAIAAgACAAIAAgACAAIAAywiHAYAA0wiAANkI3QjlCO0I9Aj8CIAAgACAAAIJCgkSCRoJIgknCTYHLwk3CZYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiWCJYIlgiAAIAAAAFAAXgBeAGAAcABeAHwAQACQAKAArQC9AJ4AXgBeAE0A3gBRAN4A7AD8AMwBGgEAAKcBNwEFAUwBXAF4QkhCmEKnArcCgAHHAsABz4LAAcABwAHAAd+C6ABoAG+C/4LAAcABwAHAAc+DF4MAAcAB54M3gweDV4Nng3eDaABoAGgAaABoAGgAaABoAGgAaABoAGgAaABoAGgAaABoAGgAaABoAEeDqABVg6WDqABoQ6gAaABoAHXDvcONw/3DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DvcO9w73DncPAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcABwAHAAcAB7cPPwlGCU4JMACAAIAAgABWCV4JYQmAAGkJcAl4CXwJgAkwADAAMAAwAIgJgACLCZMJgACZCZ8JowmrCYAAswkwAF4AXgB8AIAAuwkABMMJyQmAAM4JgADVCTAAMAAwADAAgACAAIAAgACAAIAAgACAAIAAqwYWBNkIMAAwADAAMADdCeAJ6AnuCR4E9gkwAP4JBQoNCjAAMACAABUK0wiAAB0KJAosCjQKgAAwADwKQwqAAEsKvQmdCVMKWwowADAAgACAALcEMACAAGMKgABrCjAAMAAwADAAMAAwADAAMAAwADAAMAAeBDAAMAAwADAAMAAwADAAMAAwADAAMAAwAIkEPQFzCnoKiQSCCooKkAqJBJgKoAqkCokEGAGsCrQKvArBCjAAMADJCtEKFQHZCuEK/gHpCvEKMAAwADAAMACAAIwE+QowAIAAPwEBCzAAMAAwADAAMACAAAkLEQswAIAAPwEZCyELgAAOCCkLMAAxCzkLMAAwADAAMAAwADAAXgBeAEELMAAwADAAMAAwADAAMAAwAEkLTQtVC4AAXAtkC4AAiQkwADAAMAAwADAAMAAwADAAbAtxC3kLgAuFC4sLMAAwAJMLlwufCzAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAApwswADAAMACAAIAAgACvC4AAgACAAIAAgACAALcLMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAvwuAAMcLgACAAIAAgACAAIAAyguAAIAAgACAAIAA0QswADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAANkLgACAAIAA4AswADAAMAAwADAAMAAwADAAMAAwADAAMAAwAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACJCR4E6AswADAAhwHwC4AA+AsADAgMEAwwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMACAAIAAGAwdDCUMMAAwAC0MNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQw1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHPQwwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADUHNQc1BzUHNQc1BzUHNQc2BzAAMAA5DDUHNQc1BzUHNQc1BzUHNQc1BzUHNQdFDDAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAgACAAIAATQxSDFoMMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwAF4AXgBeAF4AXgBeAF4AYgxeAGoMXgBxDHkMfwxeAIUMXgBeAI0MMAAwADAAMAAwAF4AXgCVDJ0MMAAwADAAMABeAF4ApQxeAKsMswy7DF4Awgy9DMoMXgBeAF4AXgBeAF4AXgBeAF4AXgDRDNkMeQBqCeAM3Ax8AOYM7Az0DPgMXgBeAF4AXgBeAF4AXgBeAF4AXgBeAF4AXgBeAF4AXgCgAAANoAAHDQ4NFg0wADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAeDSYNMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwAIAAgACAAIAAgACAAC4NMABeAF4ANg0wADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwAD4NRg1ODVYNXg1mDTAAbQ0wADAAMAAwADAAMAAwADAA2gbaBtoG2gbaBtoG2gbaBnUNeg3CBYANwgWFDdoGjA3aBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gaUDZwNpA2oDdoG2gawDbcNvw3HDdoG2gbPDdYN3A3fDeYN2gbsDfMN2gbaBvoN/g3aBgYODg7aBl4AXgBeABYOXgBeACUG2gYeDl4AJA5eACwO2w3aBtoGMQ45DtoG2gbaBtoGQQ7aBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gZJDjUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1B1EO2gY1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQdZDjUHNQc1BzUHNQc1B2EONQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHaA41BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1B3AO2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gY1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1BzUHNQc1B2EO2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gZJDtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBtoG2gbaBkkOeA6gAKAAoAAwADAAMAAwAKAAoACgAKAAoACgAKAAgA4wADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAAwADAAMAD//wQABAAEAAQABAAEAAQABAAEAA0AAwABAAEAAgAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAKABMAFwAeABsAGgAeABcAFgASAB4AGwAYAA8AGAAcAEsASwBLAEsASwBLAEsASwBLAEsAGAAYAB4AHgAeABMAHgBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAFgAbABIAHgAeAB4AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQABYADQARAB4ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsABAAEAAQABAAEAAUABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAkAFgAaABsAGwAbAB4AHQAdAB4ATwAXAB4ADQAeAB4AGgAbAE8ATwAOAFAAHQAdAB0ATwBPABcATwBPAE8AFgBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB0AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgBQAB4AHgAeAB4AUABQAFAAUAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAB4AHgAeAFAATwBAAE8ATwBPAEAATwBQAFAATwBQAB4AHgAeAB4AHgAeAB0AHQAdAB0AHgAdAB4ADgBQAFAAUABQAFAAHgAeAB4AHgAeAB4AHgBQAB4AUAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4ABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAJAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAkACQAJAAkACQAJAAkABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAeAB4AHgAeAFAAHgAeAB4AKwArAFAAUABQAFAAGABQACsAKwArACsAHgAeAFAAHgBQAFAAUAArAFAAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4ABAAEAAQABAAEAAQABAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAUAAeAB4AHgAeAB4AHgArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwAYAA0AKwArAB4AHgAbACsABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQADQAEAB4ABAAEAB4ABAAEABMABAArACsAKwArACsAKwArACsAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAKwArACsAKwArAFYAVgBWAB4AHgArACsAKwArACsAKwArACsAKwArACsAHgAeAB4AHgAeAB4AHgAeAB4AGgAaABoAGAAYAB4AHgAEAAQABAAEAAQABAAEAAQABAAEAAQAEwAEACsAEwATAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABABLAEsASwBLAEsASwBLAEsASwBLABoAGQAZAB4AUABQAAQAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQABMAUAAEAAQABAAEAAQABAAEAB4AHgAEAAQABAAEAAQABABQAFAABAAEAB4ABAAEAAQABABQAFAASwBLAEsASwBLAEsASwBLAEsASwBQAFAAUAAeAB4AUAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwAeAFAABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAAQABAAEAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAAQAUABQAB4AHgAYABMAUAArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAFAABAAEAAQABAAEAFAABAAEAAQAUAAEAAQABAAEAAQAKwArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAArACsAHgArAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAeAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABABQAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAFAABAAEAAQABAAEAAQABABQAFAAUABQAFAAUABQAFAAUABQAAQABAANAA0ASwBLAEsASwBLAEsASwBLAEsASwAeAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQAKwBQAFAAUABQAFAAUABQAFAAKwArAFAAUAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUAArAFAAKwArACsAUABQAFAAUAArACsABABQAAQABAAEAAQABAAEAAQAKwArAAQABAArACsABAAEAAQAUAArACsAKwArACsAKwArACsABAArACsAKwArAFAAUAArAFAAUABQAAQABAArACsASwBLAEsASwBLAEsASwBLAEsASwBQAFAAGgAaAFAAUABQAFAAUABMAB4AGwBQAB4AKwArACsABAAEAAQAKwBQAFAAUABQAFAAUAArACsAKwArAFAAUAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUAArAFAAUAArAFAAUAArAFAAUAArACsABAArAAQABAAEAAQABAArACsAKwArAAQABAArACsABAAEAAQAKwArACsABAArACsAKwArACsAKwArAFAAUABQAFAAKwBQACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwAEAAQAUABQAFAABAArACsAKwArACsAKwArACsAKwArACsABAAEAAQAKwBQAFAAUABQAFAAUABQAFAAUAArAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUAArAFAAUAArAFAAUABQAFAAUAArACsABABQAAQABAAEAAQABAAEAAQABAArAAQABAAEACsABAAEAAQAKwArAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAUABQAAQABAArACsASwBLAEsASwBLAEsASwBLAEsASwAeABsAKwArACsAKwArACsAKwBQAAQABAAEAAQABAAEACsABAAEAAQAKwBQAFAAUABQAFAAUABQAFAAKwArAFAAUAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQAKwArAAQABAArACsABAAEAAQAKwArACsAKwArACsAKwArAAQABAArACsAKwArAFAAUAArAFAAUABQAAQABAArACsASwBLAEsASwBLAEsASwBLAEsASwAeAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwAEAFAAKwBQAFAAUABQAFAAUAArACsAKwBQAFAAUAArAFAAUABQAFAAKwArACsAUABQACsAUAArAFAAUAArACsAKwBQAFAAKwArACsAUABQAFAAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwAEAAQABAAEAAQAKwArACsABAAEAAQAKwAEAAQABAAEACsAKwBQACsAKwArACsAKwArAAQAKwArACsAKwArACsAKwArACsAKwBLAEsASwBLAEsASwBLAEsASwBLAFAAUABQAB4AHgAeAB4AHgAeABsAHgArACsAKwArACsABAAEAAQABAArAFAAUABQAFAAUABQAFAAUAArAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArAFAABAAEAAQABAAEAAQABAArAAQABAAEACsABAAEAAQABAArACsAKwArACsAKwArAAQABAArAFAAUABQACsAKwArACsAKwBQAFAABAAEACsAKwBLAEsASwBLAEsASwBLAEsASwBLACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAB4AUAAEAAQABAArAFAAUABQAFAAUABQAFAAUAArAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQACsAKwAEAFAABAAEAAQABAAEAAQABAArAAQABAAEACsABAAEAAQABAArACsAKwArACsAKwArAAQABAArACsAKwArACsAKwArAFAAKwBQAFAABAAEACsAKwBLAEsASwBLAEsASwBLAEsASwBLACsAUABQACsAKwArACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAFAABAAEAAQABAAEAAQABAArAAQABAAEACsABAAEAAQABABQAB4AKwArACsAKwBQAFAAUAAEAFAAUABQAFAAUABQAFAAUABQAFAABAAEACsAKwBLAEsASwBLAEsASwBLAEsASwBLAFAAUABQAFAAUABQAFAAUABQABoAUABQAFAAUABQAFAAKwArAAQABAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQACsAUAArACsAUABQAFAAUABQAFAAUAArACsAKwAEACsAKwArACsABAAEAAQABAAEAAQAKwAEACsABAAEAAQABAAEAAQABAAEACsAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArAAQABAAeACsAKwArACsAKwArACsAKwArACsAKwArAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXAAqAFwAXAAqACoAKgAqACoAKgAqACsAKwArACsAGwBcAFwAXABcAFwAXABcACoAKgAqACoAKgAqACoAKgAeAEsASwBLAEsASwBLAEsASwBLAEsADQANACsAKwArACsAKwBcAFwAKwBcACsAKwBcAFwAKwBcACsAKwBcACsAKwArACsAKwArAFwAXABcAFwAKwBcAFwAXABcAFwAXABcACsAXABcAFwAKwBcACsAXAArACsAXABcACsAXABcAFwAXAAqAFwAXAAqACoAKgAqACoAKgArACoAKgBcACsAKwBcAFwAXABcAFwAKwBcACsAKgAqACoAKgAqACoAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArAFwAXABcAFwAUAAOAA4ADgAOAB4ADgAOAAkADgAOAA0ACQATABMAEwATABMACQAeABMAHgAeAB4ABAAEAB4AHgAeAB4AHgAeAEsASwBLAEsASwBLAEsASwBLAEsAUABQAFAAUABQAFAAUABQAFAAUAANAAQAHgAEAB4ABAAWABEAFgARAAQABABQAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAANAAQABAAEAAQABAANAAQABABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAAQABAAEACsABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsADQANAB4AHgAeAB4AHgAeAAQAHgAeAB4AHgAeAB4AKwAeAB4ADgAOAA0ADgAeAB4AHgAeAB4ACQAJACsAKwArACsAKwBcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqAFwASwBLAEsASwBLAEsASwBLAEsASwANAA0AHgAeAB4AHgBcAFwAXABcAFwAXAAqACoAKgAqAFwAXABcAFwAKgAqACoAXAAqACoAKgBcAFwAKgAqACoAKgAqACoAKgBcAFwAXAAqACoAKgAqAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAKgAqACoAKgAqACoAKgAqACoAKgAqACoAXAAqAEsASwBLAEsASwBLAEsASwBLAEsAKgAqACoAKgAqACoAUABQAFAAUABQAFAAKwBQACsAKwArACsAKwBQACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAFAAUABQAFAAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAUABQAFAAUABQAFAAUABQAFAAKwBQAFAAUABQACsAKwBQAFAAUABQAFAAUABQACsAUAArAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUAArACsAUABQAFAAUABQAFAAUAArAFAAKwBQAFAAUABQACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwAEAAQABAAeAA0AHgAeAB4AHgAeAB4AHgBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAB4AHgAeAB4AHgAeAB4AHgAeACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAFAAUABQAFAAUABQACsAKwANAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAB4AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAA0AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQABYAEQArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAADQANAA0AUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAABAAEAAQAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAA0ADQArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQAKwArACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQACsABAAEACsAKwArACsAKwArACsAKwArACsAKwArAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXAAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoADQANABUAXAANAB4ADQAbAFwAKgArACsASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArAB4AHgATABMADQANAA4AHgATABMAHgAEAAQABAAJACsASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAUABQAFAAUABQAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABABQACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwArACsABAAEAAQABAAEAAQABAAEAAQABAAEAAQAKwArACsAKwAeACsAKwArABMAEwBLAEsASwBLAEsASwBLAEsASwBLAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcACsAKwBcAFwAXABcAFwAKwArACsAKwArACsAKwArACsAKwArAFwAXABcAFwAXABcAFwAXABcAFwAXABcACsAKwArACsAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwBcACsAKwArACoAKgBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEACsAKwAeAB4AXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAKgAqACoAKgAqACoAKgAqACoAKgArACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgArACsABABLAEsASwBLAEsASwBLAEsASwBLACsAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwArACsAKgAqACoAKgAqACoAKgBcACoAKgAqACoAKgAqACsAKwAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArAAQABAAEAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQAUABQAFAAUABQAFAAUAArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsADQANAB4ADQANAA0ADQAeAB4AHgAeAB4AHgAeAB4AHgAeAAQABAAEAAQABAAEAAQABAAEAB4AHgAeAB4AHgAeAB4AHgAeACsAKwArAAQABAAEAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAUABQAEsASwBLAEsASwBLAEsASwBLAEsAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArACsAKwArACsAKwArACsAHgAeAB4AHgBQAFAAUABQAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArACsAKwANAA0ADQANAA0ASwBLAEsASwBLAEsASwBLAEsASwArACsAKwBQAFAAUABLAEsASwBLAEsASwBLAEsASwBLAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAANAA0AUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgAeAB4AHgAeAB4AHgArACsAKwArACsAKwArACsABAAEAAQAHgAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAFAAUABQAFAABABQAFAAUABQAAQABAAEAFAAUAAEAAQABAArACsAKwArACsAKwAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAKwAEAAQABAAEAAQAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArACsAUABQAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUAArAFAAKwBQACsAUAArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACsAHgAeAB4AHgAeAB4AHgAeAFAAHgAeAB4AUABQAFAAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAFAAUABQAFAAKwArAB4AHgAeAB4AHgAeACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArACsAUABQAFAAKwAeAB4AHgAeAB4AHgAeAA4AHgArAA0ADQANAA0ADQANAA0ACQANAA0ADQAIAAQACwAEAAQADQAJAA0ADQAMAB0AHQAeABcAFwAWABcAFwAXABYAFwAdAB0AHgAeABQAFAAUAA0AAQABAAQABAAEAAQABAAJABoAGgAaABoAGgAaABoAGgAeABcAFwAdABUAFQAeAB4AHgAeAB4AHgAYABYAEQAVABUAFQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgANAB4ADQANAA0ADQAeAA0ADQANAAcAHgAeAB4AHgArAAQABAAEAAQABAAEAAQABAAEAAQAUABQACsAKwBPAFAAUABQAFAAUAAeAB4AHgAWABEATwBQAE8ATwBPAE8AUABQAFAAUABQAB4AHgAeABYAEQArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAGwAbABsAGwAbABsAGwAaABsAGwAbABsAGwAbABsAGwAbABsAGwAbABsAGwAaABsAGwAbABsAGgAbABsAGgAbABsAGwAbABsAGwAbABsAGwAbABsAGwAbABsAGwAbABsABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgBQABoAHgAdAB4AUAAeABoAHgAeAB4AHgAeAB4AHgAeAB4ATwAeAFAAGwAeAB4AUABQAFAAUABQAB4AHgAeAB0AHQAeAFAAHgBQAB4AUAAeAFAATwBQAFAAHgAeAB4AHgAeAB4AHgBQAFAAUABQAFAAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgBQAB4AUABQAFAAUABPAE8AUABQAFAAUABQAE8AUABQAE8AUABPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBQAFAAUABQAE8ATwBPAE8ATwBPAE8ATwBPAE8AUABQAFAAUABQAFAAUABQAFAAHgAeAFAAUABQAFAATwAeAB4AKwArACsAKwAdAB0AHQAdAB0AHQAdAB0AHQAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAdAB4AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAeAB0AHQAeAB4AHgAdAB0AHgAeAB0AHgAeAB4AHQAeAB0AGwAbAB4AHQAeAB4AHgAeAB0AHgAeAB0AHQAdAB0AHgAeAB0AHgAdAB4AHQAdAB0AHQAdAB0AHgAdAB4AHgAeAB4AHgAdAB0AHQAdAB4AHgAeAB4AHQAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAeAB4AHgAdAB4AHgAeAB4AHgAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAdAB4AHgAdAB0AHQAdAB4AHgAdAB0AHgAeAB0AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAdAB0AHgAeAB0AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB0AHgAeAB4AHQAeAB4AHgAeAB4AHgAeAB0AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeABQAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAWABEAFgARAB4AHgAeAB4AHgAeAB0AHgAeAB4AHgAeAB4AHgAlACUAHgAeAB4AHgAeAB4AHgAeAB4AFgARAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACUAJQAlACUAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBQAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB4AHgAeAB4AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHgAeAB0AHQAdAB0AHgAeAB4AHgAeAB4AHgAeAB4AHgAdAB0AHgAdAB0AHQAdAB0AHQAdAB4AHgAeAB4AHgAeAB4AHgAdAB0AHgAeAB0AHQAeAB4AHgAeAB0AHQAeAB4AHgAeAB0AHQAdAB4AHgAdAB4AHgAdAB0AHQAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAdAB0AHQAeAB4AHgAeAB4AHgAeAB4AHgAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAeAB0AHQAeAB4AHQAeAB4AHgAeAB0AHQAeAB4AHgAeACUAJQAdAB0AJQAeACUAJQAlACAAJQAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAHgAeAB4AHgAdAB4AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAdAB4AHQAdAB0AHgAdACUAHQAdAB4AHQAdAB4AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB0AHQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAlACUAJQAlACUAJQAlACUAHQAdAB0AHQAlAB4AJQAlACUAHQAlACUAHQAdAB0AJQAlAB0AHQAlAB0AHQAlACUAJQAeAB0AHgAeAB4AHgAdAB0AJQAdAB0AHQAdAB0AHQAlACUAJQAlACUAHQAlACUAIAAlAB0AHQAlACUAJQAlACUAJQAlACUAHgAeAB4AJQAlACAAIAAgACAAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAdAB4AHgAeABcAFwAXABcAFwAXAB4AEwATACUAHgAeAB4AFgARABYAEQAWABEAFgARABYAEQAWABEAFgARAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAWABEAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AFgARABYAEQAWABEAFgARABYAEQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeABYAEQAWABEAFgARABYAEQAWABEAFgARABYAEQAWABEAFgARABYAEQAWABEAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AFgARABYAEQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeABYAEQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHQAdAB0AHQAdAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwAeAB4AHgAeAB4AHgAeAB4AHgArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAEAAQABAAeAB4AKwArACsAKwArABMADQANAA0AUAATAA0AUABQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAUAANACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAEAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQACsAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXAA0ADQANAA0ADQANAA0ADQAeAA0AFgANAB4AHgAXABcAHgAeABcAFwAWABEAFgARABYAEQAWABEADQANAA0ADQATAFAADQANAB4ADQANAB4AHgAeAB4AHgAMAAwADQANAA0AHgANAA0AFgANAA0ADQANAA0ADQANACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACsAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAKwArACsAKwArACsAKwArACsAKwArACsAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAlACUAJQAlACUAJQAlACUAJQAlACUAJQArACsAKwArAA0AEQARACUAJQBHAFcAVwAWABEAFgARABYAEQAWABEAFgARACUAJQAWABEAFgARABYAEQAWABEAFQAWABEAEQAlAFcAVwBXAFcAVwBXAFcAVwBXAAQABAAEAAQABAAEACUAVwBXAFcAVwA2ACUAJQBXAFcAVwBHAEcAJQAlACUAKwBRAFcAUQBXAFEAVwBRAFcAUQBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFEAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBRAFcAUQBXAFEAVwBXAFcAVwBXAFcAUQBXAFcAVwBXAFcAVwBRAFEAKwArAAQABAAVABUARwBHAFcAFQBRAFcAUQBXAFEAVwBRAFcAUQBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFEAVwBRAFcAUQBXAFcAVwBXAFcAVwBRAFcAVwBXAFcAVwBXAFEAUQBXAFcAVwBXABUAUQBHAEcAVwArACsAKwArACsAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAKwArAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwArACUAJQBXAFcAVwBXACUAJQAlACUAJQAlACUAJQAlACUAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAKwArACsAKwArACUAJQAlACUAKwArACsAKwArACsAKwArACsAKwArACsAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQBRAFEAUQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACsAVwBXAFcAVwBXAFcAVwBXAFcAVwAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAE8ATwBPAE8ATwBPAE8ATwAlAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACUAJQAlACUAJQAlACUAJQAlACUAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAEcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAKwArACsAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAADQATAA0AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABLAEsASwBLAEsASwBLAEsASwBLAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAFAABAAEAAQABAAeAAQABAAEAAQABAAEAAQABAAEAAQAHgBQAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AUABQAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAeAA0ADQANAA0ADQArACsAKwArACsAKwArACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAFAAUABQAFAAUABQAFAAUABQAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AUAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgBQAB4AHgAeAB4AHgAeAFAAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArAB4AHgAeAB4AHgAeAB4AHgArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAAQAUABQAFAABABQAFAAUABQAAQAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAeAB4AHgAeACsAKwArACsAUABQAFAAUABQAFAAHgAeABoAHgArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAADgAOABMAEwArACsAKwArACsAKwArACsABAAEAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEACsAKwArACsAKwArACsAKwANAA0ASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABABQAFAAUABQAFAAUAAeAB4AHgBQAA4AUAArACsAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAA0ADQBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAKwArACsAKwArACsAKwArACsAKwArAB4AWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYAFgAWABYACsAKwArAAQAHgAeAB4AHgAeAB4ADQANAA0AHgAeAB4AHgArAFAASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArAB4AHgBcAFwAXABcAFwAKgBcAFwAXABcAFwAXABcAFwAXABcAEsASwBLAEsASwBLAEsASwBLAEsAXABcAFwAXABcACsAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwArACsAKwArACsAKwArAFAAUABQAAQAUABQAFAAUABQAFAAUABQAAQABAArACsASwBLAEsASwBLAEsASwBLAEsASwArACsAHgANAA0ADQBcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAKgAqACoAXAAqACoAKgBcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXAAqAFwAKgAqACoAXABcACoAKgBcAFwAXABcAFwAKgAqAFwAKgBcACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAFwAXABcACoAKgBQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEAA0ADQBQAFAAUAAEAAQAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUAArACsAUABQAFAAUABQAFAAKwArAFAAUABQAFAAUABQACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAAQADQAEAAQAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwArACsAVABVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBUAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVAFUAVQBVACsAKwArACsAKwArACsAKwArACsAKwArAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAWQBZAFkAKwArACsAKwBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAWgBaAFoAKwArACsAKwAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYABgAGAAYAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACUAJQBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAJQAlACUAJQAlACUAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAKwArACsAKwArAFYABABWAFYAVgBWAFYAVgBWAFYAVgBWAB4AVgBWAFYAVgBWAFYAVgBWAFYAVgBWAFYAVgArAFYAVgBWAFYAVgArAFYAKwBWAFYAKwBWAFYAKwBWAFYAVgBWAFYAVgBWAFYAVgBWAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAEQAWAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUAAaAB4AKwArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAGAARABEAGAAYABMAEwAWABEAFAArACsAKwArACsAKwAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACUAJQAlACUAJQAWABEAFgARABYAEQAWABEAFgARABYAEQAlACUAFgARACUAJQAlACUAJQAlACUAEQAlABEAKwAVABUAEwATACUAFgARABYAEQAWABEAJQAlACUAJQAlACUAJQAlACsAJQAbABoAJQArACsAKwArAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAAcAKwATACUAJQAbABoAJQAlABYAEQAlACUAEQAlABEAJQBXAFcAVwBXAFcAVwBXAFcAVwBXABUAFQAlACUAJQATACUAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXABYAJQARACUAJQAlAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwAWACUAEQAlABYAEQARABYAEQARABUAVwBRAFEAUQBRAFEAUQBRAFEAUQBRAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAEcARwArACsAVwBXAFcAVwBXAFcAKwArAFcAVwBXAFcAVwBXACsAKwBXAFcAVwBXAFcAVwArACsAVwBXAFcAKwArACsAGgAbACUAJQAlABsAGwArAB4AHgAeAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAKwAEAAQABAAQAB0AKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwBQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsADQANAA0AKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArAB4AHgAeAB4AHgAeAB4AHgAeAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgBQAFAAHgAeAB4AKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArACsAKwArAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4ABAArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAAQAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsADQBQAFAAUABQACsAKwArACsAUABQAFAAUABQAFAAUABQAA0AUABQAFAAUABQACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUAArACsAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQACsAKwArAFAAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAA0AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAB4AHgBQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsADQBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArAB4AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwBQAFAAUABQAFAABAAEAAQAKwAEAAQAKwArACsAKwArAAQABAAEAAQAUABQAFAAUAArAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsABAAEAAQAKwArACsAKwAEAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsADQANAA0ADQANAA0ADQANAB4AKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAB4AUABQAFAAUABQAFAAUABQAB4AUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEACsAKwArACsAUABQAFAAUABQAA0ADQANAA0ADQANABQAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwANAA0ADQANAA0ADQANAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAHgAeAB4AHgArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwBQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAA0ADQAeAB4AHgAeAB4AKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEAAQABAAEAAQABAAeAB4AHgANAA0ADQANACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwBLAEsASwBLAEsASwBLAEsASwBLACsAKwArACsAKwArAFAAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsASwBLAEsASwBLAEsASwBLAEsASwANAA0ADQANACsAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAeAA4AUAArACsAKwArACsAKwArACsAKwAEAFAAUABQAFAADQANAB4ADQAeAAQABAAEAB4AKwArAEsASwBLAEsASwBLAEsASwBLAEsAUAAOAFAADQANAA0AKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAAQABAAEAAQABAANAA0AHgANAA0AHgAEACsAUABQAFAAUABQAFAAUAArAFAAKwBQAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAA0AKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAAQABAAEAAQAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwArACsABAAEAAQABAArAFAAUABQAFAAUABQAFAAUAArACsAUABQACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAArACsABAAEACsAKwAEAAQABAArACsAUAArACsAKwArACsAKwAEACsAKwArACsAKwBQAFAAUABQAFAABAAEACsAKwAEAAQABAAEAAQABAAEACsAKwArAAQABAAEAAQABAArACsAKwArACsAKwArACsAKwArACsABAAEAAQABAAEAAQABABQAFAAUABQAA0ADQANAA0AHgBLAEsASwBLAEsASwBLAEsASwBLACsADQArAB4AKwArAAQABAAEAAQAUABQAB4AUAArACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEACsAKwAEAAQABAAEAAQABAAEAAQABAAOAA0ADQATABMAHgAeAB4ADQANAA0ADQANAA0ADQANAA0ADQANAA0ADQANAA0AUABQAFAAUAAEAAQAKwArAAQADQANAB4AUAArACsAKwArACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwAOAA4ADgAOAA4ADgAOAA4ADgAOAA4ADgAOACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXABcAFwAXAArACsAKwAqACoAKgAqACoAKgAqACoAKgAqACoAKgAqACoAKgArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAXABcAA0ADQANACoASwBLAEsASwBLAEsASwBLAEsASwBQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwBQAFAABAAEAAQABAAEAAQABAAEAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAFAABAAEAAQABAAOAB4ADQANAA0ADQAOAB4ABAArACsAKwArACsAKwArACsAUAAEAAQABAAEAAQABAAEAAQABAAEAAQAUABQAFAAUAArACsAUABQAFAAUAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAA0ADQANACsADgAOAA4ADQANACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAABAAEAAQABAAEAAQABAAEACsABAAEAAQABAAEAAQABAAEAFAADQANAA0ADQANACsAKwArACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwAOABMAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQACsAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAArACsAKwAEACsABAAEACsABAAEAAQABAAEAAQABABQAAQAKwArACsAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsADQANAA0ADQANACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAASABIAEgAQwBDAEMAUABQAFAAUABDAFAAUABQAEgAQwBIAEMAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAASABDAEMAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABIAEMAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAEsASwBLAEsASwBLAEsASwBLAEsAKwArACsAKwANAA0AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArAAQABAAEAAQABAANACsAKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAEAAQABAAEAAQABAAEAA0ADQANAB4AHgAeAB4AHgAeAFAAUABQAFAADQAeACsAKwArACsAKwArACsAKwArACsASwBLAEsASwBLAEsASwBLAEsASwArAFAAUABQAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAUAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsABAAEAAQABABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAEcARwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwArACsAKwArACsAKwArACsAKwArACsAKwArAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwBQAFAAUABQAFAAUABQAFAAUABQACsAKwAeAAQABAANAAQABAAEAAQAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACsAKwArACsAKwArACsAKwArACsAHgAeAB4AHgAeAB4AHgArACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4ABAAEAAQABAAEAB4AHgAeAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAHgAeAAQABAAEAAQABAAEAAQAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAEAAQABAAEAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgAEAAQABAAeACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwArACsAKwArACsAKwArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAKwArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArAFAAUAArACsAUAArACsAUABQACsAKwBQAFAAUABQACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AKwBQACsAUABQAFAAUABQAFAAUAArAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAKwAeAB4AUABQAFAAUABQACsAUAArACsAKwBQAFAAUABQAFAAUABQACsAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgArACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAFAAUABQAFAAUABQAFAAUABQAFAAUAAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAHgAeAB4AHgAeAB4AHgAeAB4AKwArAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsASwBLAEsABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAB4AHgAeAB4ABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAB4AHgAeAB4AHgAeAB4AHgAEAB4AHgAeAB4AHgAeAB4AHgAeAB4ABAAeAB4ADQANAA0ADQAeACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAAQABAAEAAQABAArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsABAAEAAQABAAEAAQABAArAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArACsABAAEAAQABAAEAAQABAArAAQABAArAAQABAAEAAQABAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwBQAFAAUABQAFAAKwArAFAAUABQAFAAUABQAFAAUABQAAQABAAEAAQABAAEAAQAKwArACsAKwArACsAKwArACsAHgAeAB4AHgAEAAQABAAEAAQABAAEACsAKwArACsAKwBLAEsASwBLAEsASwBLAEsASwBLACsAKwArACsAFgAWAFAAUABQAFAAKwBQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArAFAAUAArAFAAKwArAFAAKwBQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUAArAFAAKwBQACsAKwArACsAKwArAFAAKwArACsAKwBQACsAUAArAFAAKwBQAFAAUAArAFAAUAArAFAAKwArAFAAKwBQACsAUAArAFAAKwBQACsAUABQACsAUAArACsAUABQAFAAUAArAFAAUABQAFAAUABQAFAAKwBQAFAAUABQACsAUABQAFAAUAArAFAAKwBQAFAAUABQAFAAUABQAFAAUABQACsAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQACsAKwArACsAKwBQAFAAUAArAFAAUABQAFAAUAArAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUABQAFAAUAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArAB4AHgArACsAKwArACsAKwArACsAKwArACsAKwArACsATwBPAE8ATwBPAE8ATwBPAE8ATwBPAE8ATwAlACUAJQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAeACUAHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHgAeACUAJQAlACUAHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACkAKQApACkAKQApACkAKQApACkAKQApACkAKQApACkAKQApACkAKQApACkAKQApACkAKQAlACUAJQAlACUAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAB4AHgAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAHgAeACUAJQAlACUAJQAeACUAJQAlACUAJQAgACAAIAAlACUAIAAlACUAIAAgACAAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAIQAhACEAIQAhACUAJQAgACAAJQAlACAAIAAgACAAIAAgACAAIAAgACAAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAIAAgACAAIAAlACUAJQAlACAAJQAgACAAIAAgACAAIAAgACAAIAAlACUAJQAgACUAJQAlACUAIAAgACAAJQAgACAAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAeACUAHgAlAB4AJQAlACUAJQAlACAAJQAlACUAJQAeACUAHgAeACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAHgAeAB4AHgAeAB4AHgAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAIAAgACUAJQAlACUAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAIAAlACUAJQAlACAAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAB4AHgAeAB4AHgAeACUAJQAlACUAJQAlACUAIAAgACAAJQAlACUAIAAgACAAIAAgAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AFwAXABcAFQAVABUAHgAeAB4AHgAlACUAJQAgACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAIAAgACAAJQAlACUAJQAlACUAJQAlACUAIAAlACUAJQAlACUAJQAlACUAJQAlACUAIAAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAlACUAJQAlACUAJQAlACUAJQAlACUAJQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAlACUAJQAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAlACUAJQAlAB4AHgAeAB4AHgAeAB4AHgAeAB4AJQAlACUAJQAlACUAHgAeAB4AHgAeAB4AHgAeACUAJQAlACUAJQAlACUAJQAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeAB4AHgAeACUAJQAlACUAJQAlACUAJQAlACUAJQAlACAAIAAgACAAIAAlACAAIAAlACUAJQAlACUAJQAgACUAJQAlACUAJQAlACUAJQAlACAAIAAgACAAIAAgACAAIAAgACAAJQAlACUAIAAgACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACsAKwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAJQAlACUAJQAlACUAJQAlACUAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAJQAlACUAJQAlACUAJQAlACUAJQAlAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAVwBXAFcAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQAlACUAJQArAAQAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsAKwArACsA"),U=Array.isArray(a)?function(A){for(var e=A.length,t=[],r=0;r=this._value.length?-1:this._value[A]},yA.prototype.consumeUnicodeRangeToken=function(){for(var A=[],e=this.consumeCodePoint();aA(e)&&A.length<6;)A.push(e),e=this.consumeCodePoint();for(var t=!1;63===e&&A.length<6;)A.push(e),e=this.consumeCodePoint(),t=!0;if(t){var r=parseInt(l.apply(void 0,A.map(function(A){return 63===A?48:A})),16),B=parseInt(l.apply(void 0,A.map(function(A){return 63===A?70:A})),16);return{type:sA.UNICODE_RANGE_TOKEN,start:r,end:B}}var n=parseInt(l.apply(void 0,A),16);if(45===this.peekCodePoint(0)&&aA(this.peekCodePoint(1))){this.consumeCodePoint(),e=this.consumeCodePoint();for(var s=[];aA(e)&&s.length<6;)s.push(e),e=this.consumeCodePoint();return B=parseInt(l.apply(void 0,s),16),{type:sA.UNICODE_RANGE_TOKEN,start:n,end:B}}return{type:sA.UNICODE_RANGE_TOKEN,start:n,end:n}},yA.prototype.consumeIdentLikeToken=function(){var A=this.consumeName();return"url"===A.toLowerCase()&&40===this.peekCodePoint(0)?(this.consumeCodePoint(),this.consumeUrlToken()):40===this.peekCodePoint(0)?(this.consumeCodePoint(),{type:sA.FUNCTION_TOKEN,value:A}):{type:sA.IDENT_TOKEN,value:A}},yA.prototype.consumeUrlToken=function(){var A=[];if(this.consumeWhiteSpace(),-1===this.peekCodePoint(0))return{type:sA.URL_TOKEN,value:""};var e,t=this.peekCodePoint(0);if(39===t||34===t){var r=this.consumeStringToken(this.consumeCodePoint());return r.type===sA.STRING_TOKEN&&(this.consumeWhiteSpace(),-1===this.peekCodePoint(0)||41===this.peekCodePoint(0))?(this.consumeCodePoint(),{type:sA.URL_TOKEN,value:r.value}):(this.consumeBadUrlRemnants(),IA)}for(;;){var B=this.consumeCodePoint();if(-1===B||41===B)return{type:sA.URL_TOKEN,value:l.apply(void 0,A)};if(cA(B))return this.consumeWhiteSpace(),-1===this.peekCodePoint(0)||41===this.peekCodePoint(0)?(this.consumeCodePoint(),{type:sA.URL_TOKEN,value:l.apply(void 0,A)}):(this.consumeBadUrlRemnants(),IA);if(34===B||39===B||40===B||0<=(e=B)&&e<=8||11===e||14<=e&&e<=31||127===e)return this.consumeBadUrlRemnants(),IA;if(92===B){if(!uA(B,this.peekCodePoint(0)))return this.consumeBadUrlRemnants(),IA;A.push(this.consumeEscapedCodePoint())}else A.push(B)}},yA.prototype.consumeWhiteSpace=function(){for(;cA(this.peekCodePoint(0));)this.consumeCodePoint()},yA.prototype.consumeBadUrlRemnants=function(){for(;;){var A=this.consumeCodePoint();if(41===A||-1===A)return;uA(A,this.peekCodePoint(0))&&this.consumeEscapedCodePoint()}},yA.prototype.consumeStringSlice=function(A){for(var e="";0>8,r=255&A>>16,B=255&A>>24;return e<255?"rgba("+B+","+r+","+t+","+e/255+")":"rgb("+B+","+r+","+t+")"}function re(A,e){if(A.type===sA.NUMBER_TOKEN)return A.number;if(A.type!==sA.PERCENTAGE_TOKEN)return 0;var t=3===e?1:255;return 3===e?A.number/100*t:Math.round(A.number/100*t)}function Be(A){var e=A.filter(kA);if(3===e.length){var t=e.map(re),r=t[0],B=t[1],n=t[2];return ue(r,B,n,1)}if(4!==e.length)return 0;var s=e.map(re),o=(r=s[0],B=s[1],n=s[2],s[3]);return ue(r,B,n,o)}var ne=function(A,e){return e===sA.LEFT_CURLY_BRACKET_TOKEN&&A.type===sA.RIGHT_CURLY_BRACKET_TOKEN||(e===sA.LEFT_SQUARE_BRACKET_TOKEN&&A.type===sA.RIGHT_SQUARE_BRACKET_TOKEN||e===sA.LEFT_PARENTHESIS_TOKEN&&A.type===sA.RIGHT_PARENTHESIS_TOKEN)},se={type:sA.NUMBER_TOKEN,number:0,flags:4},oe={type:sA.PERCENTAGE_TOKEN,number:50,flags:4},ie={type:sA.PERCENTAGE_TOKEN,number:100,flags:4},ae=function(A,e){if(A.type===sA.PERCENTAGE_TOKEN)return A.number/100*e;if(xA(A))switch(A.unit){case"rem":case"em":return 16*A.number;case"px":default:return A.number}return A.number},ce=function(A){if(A.type===sA.DIMENSION_TOKEN)switch(A.unit){case"deg":return Math.PI*A.number/180;case"grad":return Math.PI/200*A.number;case"rad":return A.number;case"turn":return 2*Math.PI*A.number}throw new Error("Unsupported angle type")},Qe=function(A){return Math.PI*A/180},we=function(A){if(A.type===sA.FUNCTION){var e=he[A.name];if(void 0===e)throw new Error('Attempting to parse an unsupported color function "'+A.name+'"');return e(A.values)}if(A.type===sA.HASH_TOKEN){if(3===A.value.length){var t=A.value.substring(0,1),r=A.value.substring(1,2),B=A.value.substring(2,3);return ue(parseInt(t+t,16),parseInt(r+r,16),parseInt(B+B,16),1)}if(4===A.value.length){t=A.value.substring(0,1),r=A.value.substring(1,2),B=A.value.substring(2,3);var n=A.value.substring(3,4);return ue(parseInt(t+t,16),parseInt(r+r,16),parseInt(B+B,16),parseInt(n+n,16)/255)}if(6===A.value.length){t=A.value.substring(0,2),r=A.value.substring(2,4),B=A.value.substring(4,6);return ue(parseInt(t,16),parseInt(r,16),parseInt(B,16),1)}if(8===A.value.length){t=A.value.substring(0,2),r=A.value.substring(2,4),B=A.value.substring(4,6),n=A.value.substring(6,8);return ue(parseInt(t,16),parseInt(r,16),parseInt(B,16),parseInt(n,16)/255)}}if(A.type===sA.IDENT_TOKEN){var s=He[A.value.toUpperCase()];if(void 0!==s)return s}return He.TRANSPARENT},ue=function(A,e,t,r){return(A<<24|e<<16|t<<8|Math.round(255*r)<<0)>>>0};function Ue(A,e,t){return t<0&&(t+=1),1<=t&&(t-=1),t<1/6?(e-A)*t*6+A:t<.5?e:t<2/3?6*(e-A)*(2/3-t)+A:A}function le(A){var e=A.filter(kA),t=e[0],r=e[1],B=e[2],n=e[3],s=(t.type===sA.NUMBER_TOKEN?Qe(t.number):ce(t))/(2*Math.PI),o=qA(r)?r.number/100:0,i=qA(B)?B.number/100:0,a=void 0!==n&&qA(n)?ae(n,1):1;if(0==o)return ue(255*i,255*i,255*i,1);var c=i<=.5?i*(1+o):i+o-i*o,Q=2*i-c,w=Ue(Q,c,s+1/3),u=Ue(Q,c,s),U=Ue(Q,c,s-1/3);return ue(255*w,255*u,255*U,a)}var Ce,ge,Ee,Fe,he={hsl:le,hsla:le,rgb:Be,rgba:Be},He={ALICEBLUE:4042850303,ANTIQUEWHITE:4209760255,AQUA:16777215,AQUAMARINE:2147472639,AZURE:4043309055,BEIGE:4126530815,BISQUE:4293182719,BLACK:255,BLANCHEDALMOND:4293643775,BLUE:65535,BLUEVIOLET:2318131967,BROWN:2771004159,BURLYWOOD:3736635391,CADETBLUE:1604231423,CHARTREUSE:2147418367,CHOCOLATE:3530104575,CORAL:4286533887,CORNFLOWERBLUE:1687547391,CORNSILK:4294499583,CRIMSON:3692313855,CYAN:16777215,DARKBLUE:35839,DARKCYAN:9145343,DARKGOLDENROD:3095837695,DARKGRAY:2846468607,DARKGREEN:6553855,DARKGREY:2846468607,DARKKHAKI:3182914559,DARKMAGENTA:2332068863,DARKOLIVEGREEN:1433087999,DARKORANGE:4287365375,DARKORCHID:2570243327,DARKRED:2332033279,DARKSALMON:3918953215,DARKSEAGREEN:2411499519,DARKSLATEBLUE:1211993087,DARKSLATEGRAY:793726975,DARKSLATEGREY:793726975,DARKTURQUOISE:13554175,DARKVIOLET:2483082239,DEEPPINK:4279538687,DEEPSKYBLUE:12582911,DIMGRAY:1768516095,DIMGREY:1768516095,DODGERBLUE:512819199,FIREBRICK:2988581631,FLORALWHITE:4294635775,FORESTGREEN:579543807,FUCHSIA:4278255615,GAINSBORO:3705462015,GHOSTWHITE:4177068031,GOLD:4292280575,GOLDENROD:3668254975,GRAY:2155905279,GREEN:8388863,GREENYELLOW:2919182335,GREY:2155905279,HONEYDEW:4043305215,HOTPINK:4285117695,INDIANRED:3445382399,INDIGO:1258324735,IVORY:4294963455,KHAKI:4041641215,LAVENDER:3873897215,LAVENDERBLUSH:4293981695,LAWNGREEN:2096890111,LEMONCHIFFON:4294626815,LIGHTBLUE:2916673279,LIGHTCORAL:4034953471,LIGHTCYAN:3774873599,LIGHTGOLDENRODYELLOW:4210742015,LIGHTGRAY:3553874943,LIGHTGREEN:2431553791,LIGHTGREY:3553874943,LIGHTPINK:4290167295,LIGHTSALMON:4288707327,LIGHTSEAGREEN:548580095,LIGHTSKYBLUE:2278488831,LIGHTSLATEGRAY:2005441023,LIGHTSLATEGREY:2005441023,LIGHTSTEELBLUE:2965692159,LIGHTYELLOW:4294959359,LIME:16711935,LIMEGREEN:852308735,LINEN:4210091775,MAGENTA:4278255615,MAROON:2147483903,MEDIUMAQUAMARINE:1724754687,MEDIUMBLUE:52735,MEDIUMORCHID:3126187007,MEDIUMPURPLE:2473647103,MEDIUMSEAGREEN:1018393087,MEDIUMSLATEBLUE:2070474495,MEDIUMSPRINGGREEN:16423679,MEDIUMTURQUOISE:1221709055,MEDIUMVIOLETRED:3340076543,MIDNIGHTBLUE:421097727,MINTCREAM:4127193855,MISTYROSE:4293190143,MOCCASIN:4293178879,NAVAJOWHITE:4292783615,NAVY:33023,OLDLACE:4260751103,OLIVE:2155872511,OLIVEDRAB:1804477439,ORANGE:4289003775,ORANGERED:4282712319,ORCHID:3664828159,PALEGOLDENROD:4008225535,PALEGREEN:2566625535,PALETURQUOISE:2951671551,PALEVIOLETRED:3681588223,PAPAYAWHIP:4293907967,PEACHPUFF:4292524543,PERU:3448061951,PINK:4290825215,PLUM:3718307327,POWDERBLUE:2967529215,PURPLE:2147516671,REBECCAPURPLE:1714657791,RED:4278190335,ROSYBROWN:3163525119,ROYALBLUE:1097458175,SADDLEBROWN:2336560127,SALMON:4202722047,SANDYBROWN:4104413439,SEAGREEN:780883967,SEASHELL:4294307583,SIENNA:2689740287,SILVER:3233857791,SKYBLUE:2278484991,SLATEBLUE:1784335871,SLATEGRAY:1887473919,SLATEGREY:1887473919,SNOW:4294638335,SPRINGGREEN:16744447,STEELBLUE:1182971135,TAN:3535047935,TEAL:8421631,THISTLE:3636451583,TOMATO:4284696575,TRANSPARENT:0,TURQUOISE:1088475391,VIOLET:4001558271,WHEAT:4125012991,WHITE:4294967295,WHITESMOKE:4126537215,YELLOW:4294902015,YELLOWGREEN:2597139199};(ge=Ce||(Ce={}))[ge.VALUE=0]="VALUE",ge[ge.LIST=1]="LIST",ge[ge.IDENT_VALUE=2]="IDENT_VALUE",ge[ge.TYPE_VALUE=3]="TYPE_VALUE",ge[ge.TOKEN_VALUE=4]="TOKEN_VALUE",(Fe=Ee||(Ee={}))[Fe.BORDER_BOX=0]="BORDER_BOX",Fe[Fe.PADDING_BOX=1]="PADDING_BOX";function de(A){var e=we(A[0]),t=A[1];return t&&qA(t)?{color:e,stop:t}:{color:e,stop:null}}function fe(A,t){var e=A[0],r=A[A.length-1];null===e.stop&&(e.stop=se),null===r.stop&&(r.stop=ie);for(var B=[],n=0,s=0;s A.optimumDistance)?{optimumCorner:e,optimumDistance:B}:A},{optimumDistance:o?1/0:-1/0,optimumCorner:null}).optimumCorner}function Ie(A){var B=Qe(180),n=[];return WA(A).forEach(function(A,e){if(0===e){var t=A[0];if(t.type===sA.IDENT_TOKEN&&-1!==["top","left","right","bottom"].indexOf(t.value))return void(B=Ae(A));if($A(t))return void(B=(ce(t)+Qe(270))%Qe(360))}var r=de(A);n.push(r)}),{angle:B,stops:n,type:xe.LINEAR_GRADIENT}}function Te(A){return 0===A[0]&&255===A[1]&&0===A[2]&&255===A[3]}var me={name:"background-clip",initialValue:"border-box",prefix:!(Fe[Fe.CONTENT_BOX=2]="CONTENT_BOX"),type:Ce.LIST,parse:function(A){return A.map(function(A){if(zA(A))switch(A.value){case"padding-box":return Ee.PADDING_BOX;case"content-box":return Ee.CONTENT_BOX}return Ee.BORDER_BOX})}},Re={name:"background-color",initialValue:"transparent",prefix:!1,type:Ce.TYPE_VALUE,format:"color"},Le=function(A,e,t,r,B){var n="http://www.w3.org/2000/svg",s=document.createElementNS(n,"svg"),o=document.createElementNS(n,"foreignObject");return s.setAttributeNS(null,"width",A.toString()),s.setAttributeNS(null,"height",e.toString()),o.setAttributeNS(null,"width","100%"),o.setAttributeNS(null,"height","100%"),o.setAttributeNS(null,"x",t.toString()),o.setAttributeNS(null,"y",r.toString()),o.setAttributeNS(null,"externalResourcesRequired","true"),s.appendChild(o),o.appendChild(B),s},Oe=function(r){return new Promise(function(A,e){var t=new Image;t.onload=function(){return A(t)},t.onerror=e,t.src="data:image/svg+xml;charset=utf-8,"+encodeURIComponent((new XMLSerializer).serializeToString(r))})},ve={get SUPPORT_RANGE_BOUNDS(){var A=function(A){if(A.createRange){var e=A.createRange();if(e.getBoundingClientRect){var t=A.createElement("boundtest");t.style.height="123px",t.style.display="block",A.body.appendChild(t),e.selectNode(t);var r=e.getBoundingClientRect(),B=Math.round(r.height);if(A.body.removeChild(t),123===B)return!0}}return!1}(document);return Object.defineProperty(ve,"SUPPORT_RANGE_BOUNDS",{value:A}),A},get SUPPORT_SVG_DRAWING(){var A=function(A){var e=new Image,t=A.createElement("canvas"),r=t.getContext("2d");if(!r)return!1;e.src="data:image/svg+xml, ";try{r.drawImage(e,0,0),t.toDataURL()}catch(A){return!1}return!0}(document);return Object.defineProperty(ve,"SUPPORT_SVG_DRAWING",{value:A}),A},get SUPPORT_FOREIGNOBJECT_DRAWING(){var A="function"==typeof Array.from&&"function"==typeof window.fetch?function(r){var A=r.createElement("canvas"),B=100;A.width=B,A.height=B;var n=A.getContext("2d");if(!n)return Promise.reject(!1);n.fillStyle="rgb(0, 255, 0)",n.fillRect(0,0,B,B);var e=new Image,s=A.toDataURL();e.src=s;var t=Le(B,B,0,0,e);return n.fillStyle="red",n.fillRect(0,0,B,B),Oe(t).then(function(A){n.drawImage(A,0,0);var e=n.getImageData(0,0,B,B).data;n.fillStyle="red",n.fillRect(0,0,B,B);var t=r.createElement("div");return t.style.backgroundImage="url("+s+")",t.style.height="100px",Te(e)?Oe(Le(B,B,0,0,t)):Promise.reject(!1)}).then(function(A){return n.drawImage(A,0,0),Te(n.getImageData(0,0,B,B).data)}).catch(function(){return!1})}(document):Promise.resolve(!1);return Object.defineProperty(ve,"SUPPORT_FOREIGNOBJECT_DRAWING",{value:A}),A},get SUPPORT_CORS_IMAGES(){var A=void 0!==(new Image).crossOrigin;return Object.defineProperty(ve,"SUPPORT_CORS_IMAGES",{value:A}),A},get SUPPORT_RESPONSE_TYPE(){var A="string"==typeof(new XMLHttpRequest).responseType;return Object.defineProperty(ve,"SUPPORT_RESPONSE_TYPE",{value:A}),A},get SUPPORT_CORS_XHR(){var A="withCredentials"in new XMLHttpRequest;return Object.defineProperty(ve,"SUPPORT_CORS_XHR",{value:A}),A}},De=(Se.prototype.debug=function(){for(var A=[],e=0;eA.height?new I(A.left+(A.width-A.height)/2,A.top,A.height,A.height):A.width"),Wn(this.referenceElement.ownerDocument,B,n),o.replaceChild(o.adoptNode(this.documentElement),o.documentElement),o.close(),i},xn.prototype.createElementClone=function(A){return Fn(A)?this.createCanvasClone(A):rn(A)?this.createStyleClone(A):A.cloneNode(!1)},xn.prototype.createStyleClone=function(A){try{var e=A.sheet;if(e&&e.cssRules){var t=[].slice.call(e.cssRules,0).reduce(function(A,e){return e&&"string"==typeof e.cssText?A+e.cssText:A},""),r=A.cloneNode(!1);return r.textContent=t,r}}catch(A){if(De.getInstance(this.options.id).error("Unable to access cssRules property",A),"SecurityError"!==A.name)throw A}return A.cloneNode(!1)},xn.prototype.createCanvasClone=function(A){if(this.options.inlineImages&&A.ownerDocument){var e=A.ownerDocument.createElement("img");try{return e.src=A.toDataURL(),e}catch(A){De.getInstance(this.options.id).info("Unable to clone canvas contents, canvas is tainted")}}var t=A.cloneNode(!1);try{t.width=A.width,t.height=A.height;var r=A.getContext("2d"),B=t.getContext("2d");return B&&(r?B.putImageData(r.getImageData(0,0,A.width,A.height),0,0):B.drawImage(A,0,0)),t}catch(A){}return t},xn.prototype.cloneNode=function(A){if(Qn(A))return document.createTextNode(A.data);if(!A.ownerDocument)return A.cloneNode(!1);var e=A.ownerDocument.defaultView;if(un(A)&&e){var t=this.createElementClone(A),r=e.getComputedStyle(A),B=e.getComputedStyle(A,":before"),n=e.getComputedStyle(A,":after");this.referenceElement===A&&(this.clonedReferenceElement=t),En(t)&&$n(t);for(var s=this.counters.parse(new wB(r)),o=this.resolvePseudoContent(A,t,B,Ln.BEFORE),i=A.firstChild;i;i=i.nextSibling)wn(i)&&("SCRIPT"===i.tagName||i.hasAttribute(_n)||"function"==typeof this.options.ignoreElements&&this.options.ignoreElements(i))||this.options.copyStyles&&wn(i)&&rn(i)||t.appendChild(this.cloneNode(i));o&&t.insertBefore(o,t.firstChild);var a=this.resolvePseudoContent(A,t,n,Ln.AFTER);return a&&t.appendChild(a),this.counters.pop(s),r&&this.options.copyStyles&&!Hn(A)&&Gn(r,t),0===A.scrollTop&&0===A.scrollLeft||this.scrolledElements.push([t,A.scrollLeft,A.scrollTop]),(dn(A)||fn(A))&&(dn(t)||fn(t))&&(t.value=A.value),t}return A.cloneNode(!1)},xn.prototype.resolvePseudoContent=function(U,A,e,t){var l=this;if(e){var r=e.content,C=A.ownerDocument;if(C&&r&&"none"!==r&&"-moz-alt-content"!==r&&"none"!==e.display){this.counters.parse(new wB(e));var g=new QB(e),E=C.createElement("html2canvaspseudoelement");return Gn(e,E),g.content.forEach(function(A){if(A.type===sA.STRING_TOKEN)E.appendChild(C.createTextNode(A.value));else if(A.type===sA.URL_TOKEN){var e=C.createElement("img");e.src=A.value,e.style.opacity="1",E.appendChild(e)}else if(A.type===sA.FUNCTION){if("attr"===A.name){var t=A.values.filter(zA);t.length&&E.appendChild(C.createTextNode(U.getAttribute(t[0].value)||""))}else if("counter"===A.name){var r=A.values.filter(kA),B=r[0],n=r[1];if(B&&zA(B)){var s=l.counters.getCounterValue(B.value),o=n&&zA(n)?ir.parse(n.value):tr.DECIMAL;E.appendChild(C.createTextNode(yn(s,o,!1)))}}else if("counters"===A.name){var i=A.values.filter(kA),a=(B=i[0],i[1]);if(n=i[2],B&&zA(B)){var c=l.counters.getCounterValues(B.value),Q=n&&zA(n)?ir.parse(n.value):tr.DECIMAL,w=a&&a.type===sA.STRING_TOKEN?a.value:"",u=c.map(function(A){return yn(A,Q,!1)}).join(w);E.appendChild(C.createTextNode(u))}}}else if(A.type===sA.IDENT_TOKEN)switch(A.value){case"open-quote":E.appendChild(C.createTextNode(eB(g.quotes,l.quoteDepth++,!0)));break;case"close-quote":E.appendChild(C.createTextNode(eB(g.quotes,--l.quoteDepth,!1)))}}),E.className=qn+" "+Zn,A.className+=t===Ln.BEFORE?" "+qn:" "+Zn,E}}},xn);function xn(A,e){if(this.options=e,this.scrolledElements=[],this.referenceElement=A,this.counters=new pn,this.quoteDepth=0,!A.ownerDocument)throw new Error("Cloned element does not have an owner document");this.documentElement=this.cloneNode(A.ownerDocument.documentElement)}(On=Ln||(Ln={}))[On.BEFORE=0]="BEFORE",On[On.AFTER=1]="AFTER";var Vn,zn,Xn=function(A,e){var t=A.createElement("iframe");return t.className="html2canvas-container",t.style.visibility="hidden",t.style.position="fixed",t.style.left="-10000px",t.style.top="0px",t.style.border="0",t.width=e.width.toString(),t.height=e.height.toString(),t.scrolling="no",t.setAttribute(_n,"true"),A.body.appendChild(t),t},Jn=function(B){return new Promise(function(e,A){var t=B.contentWindow;if(!t)return A("No window assigned for iframe");var r=t.document;t.onload=B.onload=r.onreadystatechange=function(){t.onload=B.onload=r.onreadystatechange=null;var A=setInterval(function(){0"),e},Wn=function(A,e,t){A&&A.defaultView&&(e!==A.defaultView.pageXOffset||t!==A.defaultView.pageYOffset)&&A.defaultView.scrollTo(e,t)},Yn=function(A){var e=A[0],t=A[1],r=A[2];e.scrollLeft=t,e.scrollTop=r},qn="___html2canvas___pseudoelement_before",Zn="___html2canvas___pseudoelement_after",jn='{\n content: "" !important;\n display: none !important;\n}',$n=function(A){As(A,"."+qn+":before"+jn+"\n ."+Zn+":after"+jn)},As=function(A,e){var t=A.ownerDocument;if(t){var r=t.createElement("style");r.textContent=e,A.appendChild(r)}};(zn=Vn||(Vn={}))[zn.VECTOR=0]="VECTOR",zn[zn.BEZIER_CURVE=1]="BEZIER_CURVE";function es(A,t){return A.length===t.length&&A.some(function(A,e){return A===t[e]})}var ts=(rs.prototype.add=function(A,e){return new rs(this.x+A,this.y+e)},rs);function rs(A,e){this.type=Vn.VECTOR,this.x=A,this.y=e}function Bs(A,e,t){return new ts(A.x+(e.x-A.x)*t,A.y+(e.y-A.y)*t)}var ns=(ss.prototype.subdivide=function(A,e){var t=Bs(this.start,this.startControl,A),r=Bs(this.startControl,this.endControl,A),B=Bs(this.endControl,this.end,A),n=Bs(t,r,A),s=Bs(r,B,A),o=Bs(n,s,A);return e?new ss(this.start,t,n,o):new ss(o,s,B,this.end)},ss.prototype.add=function(A,e){return new ss(this.start.add(A,e),this.startControl.add(A,e),this.endControl.add(A,e),this.end.add(A,e))},ss.prototype.reverse=function(){return new ss(this.end,this.endControl,this.startControl,this.start)},ss);function ss(A,e,t,r){this.type=Vn.BEZIER_CURVE,this.start=A,this.startControl=e,this.endControl=t,this.end=r}function os(A){return A.type===Vn.BEZIER_CURVE}var is,as,cs=function(A){var e=A.styles,t=A.bounds,r=jA(e.borderTopLeftRadius,t.width,t.height),B=r[0],n=r[1],s=jA(e.borderTopRightRadius,t.width,t.height),o=s[0],i=s[1],a=jA(e.borderBottomRightRadius,t.width,t.height),c=a[0],Q=a[1],w=jA(e.borderBottomLeftRadius,t.width,t.height),u=w[0],U=w[1],l=[];l.push((B+o)/t.width),l.push((u+c)/t.width),l.push((n+U)/t.height),l.push((i+Q)/t.height);var C=Math.max.apply(Math,l);1t.width+p?0:o-p,i-H,is.TOP_RIGHT):new ts(t.left+t.width-d,t.top+H),this.bottomRightPaddingBox=0t.width+p+T?0:o-p+T,i-(H+N),is.TOP_RIGHT):new ts(t.left+t.width-(d+K),t.top+H+N),this.bottomRightContentBox=0A.element.container.styles.zIndex.order&&(i=e,!0)}),n.negativeZIndex.splice(i,0,s)}else if(0A.element.container.styles.zIndex.order&&(a=e+1,!0)}),n.positiveZIndex.splice(a,0,s)}else n.zeroOrAutoZIndexOrTransformedOrOpacity.push(s)}else A.styles.isFloating()?n.nonPositionedFloats.push(s):n.nonPositionedInlineLevel.push(s);ps(r,s,e?s:w,B)}else A.styles.isInlineLevel()?Q.inlineLevel.push(r):Q.nonInlineLevel.push(r),ps(r,Q,w,B);AB(A.flags,8)&&Ns(A,B)})},Ns=function(A,e){for(var t=A instanceof bB?A.start:1,r=A instanceof bB&&A.reversed,B=0;Bl){for(var t=0,e=f.length-s;e>t;t++)f[t]=f[t+s];f.length-=s,s=0}}f.length=0,s=0,c=!1}function o(n){var t=1,e=new a(n),r=document.createTextNode("");return e.observe(r,{characterData:!0}),function(){t=-t,r.data=t}}function i(n){return function(){function t(){clearTimeout(e),clearInterval(r),n()}var e=setTimeout(t,0),r=setInterval(t,50)}}t.exports=e;var u,f=[],c=!1,s=0,l=1024,a=n.MutationObserver||n.WebKitMutationObserver;u="function"==typeof a?o(r):i(r),e.requestFlush=u,e.makeRequestCallFromTimer=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],5:[function(n,t,e){"function"!=typeof Promise.prototype.done&&(Promise.prototype.done=function(n,t){var e=arguments.length?this.then.apply(this,arguments):this;e.then(null,function(n){setTimeout(function(){throw n},0)})})},{}],6:[function(n,t,e){n("asap");"undefined"==typeof Promise&&(Promise=n("./lib/core.js"),n("./lib/es6-extensions.js")),n("./polyfill-done.js")},{"./lib/core.js":1,"./lib/es6-extensions.js":2,"./polyfill-done.js":5,asap:3}]},{},[6]);
+//# sourceMappingURL=/polyfills/promise-7.0.4.min.js.map
\ No newline at end of file
diff --git a/QWebView/DreamTree.py b/QWebView/DreamTree.py
index 8263b01abe8cecddbe4efdcfeabc20e27a206133..071e2f1e7fbe2749429b4b688843a3e94ccafe1b 100644
--- a/QWebView/DreamTree.py
+++ b/QWebView/DreamTree.py
@@ -1,14 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年4月6日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: DreamTree
@description:
-'''
+"""
import sys
@@ -17,12 +17,8 @@ from PyQt5.QtGui import QPalette
from PyQt5.QtWebKitWidgets import QWebView
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout
-from Lib import data_rc # @UnusedImport @UnresolvedImport
# from PyQt5.QtWebKit import QWebSettings
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
# 要实现透明的webview,需要先用一个QWidget作为父控件
diff --git a/QWebView/GetCookie.py b/QWebView/GetCookie.py
index 41aa88ae33e94944c05a222ec2661714eb6ac236..1420837ea7bea45e27b7ba65453571e22869c5a5 100644
--- a/QWebView/GetCookie.py
+++ b/QWebView/GetCookie.py
@@ -1,37 +1,60 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月10日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: GetCookie
@description:
-'''
+"""
+import cgitb
import sys
-from PyQt5.QtCore import QUrl
+from PyQt5.QtCore import QUrl, QByteArray
from PyQt5.QtWebKitWidgets import QWebView
-from PyQt5.QtWidgets import QApplication
-
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+from PyQt5.QtWidgets import QApplication, QTextEdit
class WebView(QWebView):
def __init__(self, *args, **kwargs):
super(WebView, self).__init__(*args, **kwargs)
+ self.cookieView = QTextEdit()
+ self.cookieView.resize(800, 400)
+ self.cookieView.move(400, 400)
+ self.cookieView.setWindowTitle('Cookies')
+ self.cookieView.show()
self.loadFinished.connect(self.onLoadFinished)
+ def closeEvent(self, event):
+ self.cookieView.close()
+ super(WebView, self).closeEvent(event)
+
+ def bytestostr(self, data):
+ if isinstance(data, str):
+ return data
+ if isinstance(data, QByteArray):
+ data = data.data()
+ if isinstance(data, bytes):
+ data = data.decode(errors='ignore')
+ else:
+ data = str(data)
+ return data
+
def onLoadFinished(self):
allCookies = self.page().networkAccessManager().cookieJar().allCookies()
print("allCookies:", allCookies)
for cookie in allCookies:
- # if cookie.domain() == ".pyqt5.com":
+ # if cookie.domain() == ".pyqt.site":
+ self.cookieView.append(
+ "domain: " + self.bytestostr(cookie.domain()))
+ self.cookieView.append("path: " + self.bytestostr(cookie.path()))
+ self.cookieView.append("name: " + self.bytestostr(cookie.name()))
+ self.cookieView.append(
+ "value: " + self.bytestostr(cookie.value()))
+ self.cookieView.append('')
print("domain:", cookie.domain())
print("path:", cookie.path())
print("name:", cookie.name())
@@ -40,8 +63,9 @@ class WebView(QWebView):
if __name__ == "__main__":
+ cgitb.enable(format='text')
app = QApplication(sys.argv)
w = WebView()
w.show()
- w.load(QUrl("https://pyqt5.com"))
+ w.load(QUrl("https://pyqt.site"))
sys.exit(app.exec_())
diff --git a/QWebView/JsSignals.py b/QWebView/JsSignals.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a3d7a555333e2ccf1d43c68db981f234d84246f
--- /dev/null
+++ b/QWebView/JsSignals.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年4月27日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QWebEngineView.JsSignals
+@description:
+"""
+import os
+from time import time
+
+from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal
+from PyQt5.QtWebKit import QWebSettings
+from PyQt5.QtWebKitWidgets import QWebView
+from PyQt5.QtWidgets import QMessageBox, QWidget, QVBoxLayout, QPushButton
+
+
+class WebView(QWebView):
+ customSignal = pyqtSignal(str)
+
+ def __init__(self, *args, **kwargs):
+ super(WebView, self).__init__(*args, **kwargs)
+ self.initSettings()
+ # 暴露接口对象
+ self.page().mainFrame().javaScriptWindowObjectCleared.connect(self._exposeInterface)
+
+ def _exposeInterface(self):
+ """向Js暴露调用本地方法接口
+ """
+ self.page().mainFrame().addToJavaScriptWindowObject('Bridge', self)
+
+ # 注意pyqtSlot用于把该函数暴露给js可以调用
+ @pyqtSlot(str)
+ def callFromJs(self, text):
+ QMessageBox.information(self, "提示", "来自js调用:{}".format(text))
+
+ def sendCustomSignal(self):
+ # 发送自定义信号
+ self.customSignal.emit('当前时间: ' + str(time()))
+
+ @pyqtSlot(str)
+ @pyqtSlot(QUrl)
+ def load(self, url):
+ '''
+ eg: load("https://pyqt.site")
+ :param url: 网址
+ '''
+ return super(WebView, self).load(QUrl(url))
+
+ def initSettings(self):
+ '''
+ eg: 初始化设置
+ '''
+ # 获取浏览器默认设置
+ settings = self.settings()
+ # 开启开发人员工具
+ settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
+ # 设置默认编码
+ settings.setDefaultTextEncoding('UTF-8')
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+ self.webview = WebView(self)
+ layout.addWidget(self.webview)
+ layout.addWidget(QPushButton(
+ '发送自定义信号', self, clicked=self.webview.sendCustomSignal))
+
+ self.webview.windowTitleChanged.connect(self.setWindowTitle)
+ self.webview.load(QUrl.fromLocalFile(
+ os.path.abspath('Data/JsSignals.html')))
+
+
+if __name__ == "__main__":
+ from PyQt5.QtWidgets import QApplication
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ w.move(100, 100)
+ sys.exit(app.exec_())
diff --git a/QWebView/Lib/data_rc.py b/QWebView/Lib/data_rc.py
index 59b9117a956bef9fb4ae8bd3ee57f60e5638176d..17c0cc2449fb72bb4d118678cfe8e9ac536347d1 100644
--- a/QWebView/Lib/data_rc.py
+++ b/QWebView/Lib/data_rc.py
@@ -6216,10 +6216,13 @@ qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
"
+
def qInitResources():
QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
def qCleanupResources():
QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
qInitResources()
diff --git a/QWebView/PlayFlash.py b/QWebView/PlayFlash.py
new file mode 100644
index 0000000000000000000000000000000000000000..15da61d0ef0a51763606c7b486a34292ba83bed7
--- /dev/null
+++ b/QWebView/PlayFlash.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年9月18日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QWebView.PlayFlash
+@description: 播放Flash
+"""
+import os
+import sys
+
+from PyQt5.QtCore import QUrl
+from PyQt5.QtNetwork import QSslConfiguration, QSslCertificate, QSsl
+from PyQt5.QtWebKit import QWebSettings
+from PyQt5.QtWebKitWidgets import QWebView
+from PyQt5.QtWidgets import QApplication
+
+
+class Window(QWebView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(800, 600)
+ # 浏览器设置
+ setting = QWebSettings.globalSettings()
+ setting.setAttribute(QWebSettings.PluginsEnabled, True)
+ # 解决xp下ssl问题
+ self.page().networkAccessManager().sslErrors.connect(self.handleSslErrors)
+ sslconf = QSslConfiguration.defaultConfiguration()
+ clist = sslconf.caCertificates()
+ cnew = QSslCertificate.fromData(b"CaCertificates")
+ clist.extend(cnew)
+ sslconf.setCaCertificates(clist)
+ sslconf.setProtocol(QSsl.AnyProtocol)
+ QSslConfiguration.setDefaultConfiguration(sslconf)
+
+ def handleSslErrors(self, reply, errors):
+ # 解决ssl错误
+ reply.ignoreSslErrors()
+
+
+if __name__ == '__main__':
+ # 非常重要,设置为NPSWF32.dll文件所在目录
+ os.environ['QTWEBKIT_PLUGIN_PATH'] = os.path.abspath('Data')
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ w.load(QUrl(
+ 'https://www.17sucai.com/preview/8825/2013-07-07/%E4%B8%80%E6%AC%BE%E4%BA%BA%E5%BD%A2%E5%8A%A8%E4%BD%9C%E6%98%BE%E7%A4%BA%E7%9A%84flash%E6%97%B6%E9%97%B4/index.html'))
+ sys.exit(app.exec_())
diff --git a/QWebView/README.md b/QWebView/README.md
index 182c10e9d7ed4236865619406254a20302549c6e..557b5faafb727ce9b9d521b30a5c09baf5a19526 100644
--- a/QWebView/README.md
+++ b/QWebView/README.md
@@ -1,7 +1,16 @@
# QWebView
+- 目录
+ - [梦幻树](#1梦幻树)
+ - [获取Cookie](#2获取Cookie)
+ - [和Js交互操作](#3和Js交互操作)
+ - [网页整体截图](#4网页整体截图)
+ - [播放Flash](#5播放Flash)
+ - [拦截请求](#6拦截请求)
+
## 1、梦幻树
[运行 DreamTree.py](DreamTree.py)
+
在桌面上显示透明html效果,使用`QWebkit`加载html实现,采用窗口背景透明和穿透方式

@@ -12,4 +21,37 @@
从`page()`中得到`QNetworkAccessManager`,在从中得到`QNetworkCookieJar`,
最后得到cookie,当然也可以设置自己的`QNetworkCookieJar`
-
\ No newline at end of file
+
+
+## 3、和Js交互操作
+[运行 JsSignals.py](JsSignals.py)
+
+通过`QWebFrame`的`addToJavaScriptWindowObject`函数提供进行Python对象和Javascript的交互
+
+具体看代码中的注释
+
+
+
+## 4、网页整体截图
+[运行 ScreenShotPage.py](ScreenShotPage.py)
+
+1. 方式1:原理是通过`QWebView.QWebPage.QWebFrame`得到内容的高度,然后设置`QWebPage.setViewportSize`的大小,
+最后通过`QWebFrame.render`把图片截出来
+2. 方式2:通过js库`html2canvas`对指定元素截图,得到`base64`编码的数据并调用接口函数传递到py代码中
+
+
+
+## 5、播放Flash
+[运行 PlayFlash.py](PlayFlash.py)
+
+1. 重点在于设置 `os.environ['QTWEBKIT_PLUGIN_PATH'] = os.path.abspath('Data')` ,非常重要,设置为NPSWF32.dll文件所在目录
+2. 其次是xp下ssl问题,具体参考代码
+
+
+
+## 6、拦截请求
+[运行 BlockRequest.py](BlockRequest.py)
+
+通过`QNetworkAccessManager`中的`createRequest`方法对每个请求做拦截过滤
+
+
\ No newline at end of file
diff --git a/QWebView/ScreenShot/BlockRequest.png b/QWebView/ScreenShot/BlockRequest.png
new file mode 100644
index 0000000000000000000000000000000000000000..03d4a41b68da56cb19b6fec39ec79a015db9de51
Binary files /dev/null and b/QWebView/ScreenShot/BlockRequest.png differ
diff --git a/QWebView/ScreenShot/JsSignals.gif b/QWebView/ScreenShot/JsSignals.gif
new file mode 100644
index 0000000000000000000000000000000000000000..1629e8e9eb2f8dcdb49f89d00cc9e73495c4ede5
Binary files /dev/null and b/QWebView/ScreenShot/JsSignals.gif differ
diff --git a/QWebView/ScreenShot/PlayFlash.gif b/QWebView/ScreenShot/PlayFlash.gif
new file mode 100644
index 0000000000000000000000000000000000000000..d22f1aadc1e1876e6bd0b529ff89ee4b6536442d
Binary files /dev/null and b/QWebView/ScreenShot/PlayFlash.gif differ
diff --git a/QWebView/ScreenShot/ScreenShotPage.gif b/QWebView/ScreenShot/ScreenShotPage.gif
new file mode 100644
index 0000000000000000000000000000000000000000..1f514428ac2eadab0e23f364d6fbcc4ee07a5e26
Binary files /dev/null and b/QWebView/ScreenShot/ScreenShotPage.gif differ
diff --git a/QWebView/ScreenShotPage.py b/QWebView/ScreenShotPage.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a00fc9ac163bbd4eea3ff284417a48e4f4d5f11
--- /dev/null
+++ b/QWebView/ScreenShotPage.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年7月8日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ScreenShotPage
+@description: 网页整体截图
+"""
+import base64
+import cgitb
+import sys
+
+from PyQt5.QtCore import QUrl, Qt, pyqtSlot, QSize
+from PyQt5.QtGui import QImage, QPainter, QIcon, QPixmap
+from PyQt5.QtWebKit import QWebSettings
+from PyQt5.QtWebKitWidgets import QWebView
+from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButton, \
+ QGroupBox, QLineEdit, QHBoxLayout, QListWidget, QListWidgetItem, \
+ QProgressDialog
+
+# 对部分内容进行截图
+CODE = """
+var el = $("%s");
+html2canvas(el[0], {
+ width: el.outerWidth(true),
+ windowWidth: el.outerWidth(true),
+}).then(function(canvas) {
+ _self.saveImage(canvas.toDataURL());
+});
+"""
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(600, 400)
+ layout = QHBoxLayout(self)
+
+ # 左侧
+ widgetLeft = QWidget(self)
+ layoutLeft = QVBoxLayout(widgetLeft)
+ # 右侧
+ self.widgetRight = QListWidget(
+ self, minimumWidth=200, iconSize=QSize(150, 150))
+ self.widgetRight.setViewMode(QListWidget.IconMode)
+ layout.addWidget(widgetLeft)
+ layout.addWidget(self.widgetRight)
+
+ self.webView = QWebView()
+ layoutLeft.addWidget(self.webView)
+
+ # 截图方式一
+ groupBox1 = QGroupBox('截图方式一', self)
+ layout1 = QVBoxLayout(groupBox1)
+ layout1.addWidget(QPushButton('截图1', self, clicked=self.onScreenShot1))
+ layoutLeft.addWidget(groupBox1)
+
+ # 截图方式二(采用js)
+ groupBox2 = QGroupBox('截图方式二', self)
+ layout2 = QVBoxLayout(groupBox2)
+ self.codeEdit = QLineEdit(
+ 'body', groupBox2, placeholderText='请输入需要截图的元素、ID或者class:如body、#id .class')
+ layout2.addWidget(self.codeEdit)
+ self.btnMethod2 = QPushButton(
+ '', self, clicked=self.onScreenShot2, enabled=False)
+ layout2.addWidget(self.btnMethod2)
+ layoutLeft.addWidget(groupBox2)
+
+ # 开启开发人员工具
+ QWebSettings.globalSettings().setAttribute(
+ QWebSettings.DeveloperExtrasEnabled, True)
+ self.webView.loadStarted.connect(self.onLoadStarted)
+ self.webView.loadFinished.connect(self.onLoadFinished)
+ self.webView.load(QUrl("https://pyqt.site"))
+
+ # 暴露接口和加载完成后执行jquery等一些库文件
+ self.webView.page().mainFrame().javaScriptWindowObjectCleared.connect(
+ self.populateJavaScriptWindowObject)
+
+ def populateJavaScriptWindowObject(self):
+ self.webView.page().mainFrame().addToJavaScriptWindowObject(
+ '_self', self)
+
+ def onLoadStarted(self):
+ print('load started')
+ self.btnMethod2.setEnabled(False)
+ self.btnMethod2.setText('暂时无法使用(等待页面加载完成)')
+
+ def onLoadFinished(self):
+ # 注入脚本
+ mainFrame = self.webView.page().mainFrame()
+ # 执行jquery,promise,html2canvas
+ mainFrame.evaluateJavaScript(
+ open('Data/jquery.js', 'rb').read().decode())
+ mainFrame.evaluateJavaScript(
+ open('Data/promise-7.0.4.min.js', 'rb').read().decode())
+ mainFrame.evaluateJavaScript(
+ open('Data/html2canvas.min.js', 'rb').read().decode())
+ print('inject js ok')
+ self.btnMethod2.setText('截图2')
+ self.btnMethod2.setEnabled(True)
+
+ def onScreenShot1(self):
+ # 截图方式1
+ page = self.webView.page()
+ frame = page.mainFrame()
+ size = frame.contentsSize()
+ image = QImage(size, QImage.Format_ARGB32_Premultiplied)
+ image.fill(Qt.transparent)
+
+ painter = QPainter()
+ painter.begin(image)
+ painter.setRenderHint(QPainter.Antialiasing, True)
+ painter.setRenderHint(QPainter.TextAntialiasing, True)
+ painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
+
+ # 记录旧大小
+ oldSize = page.viewportSize()
+ # *****重点就是这里******
+ page.setViewportSize(size)
+ frame.render(painter)
+ painter.end()
+
+ # 截图完成后需要还原,否则界面不响应鼠标等
+ page.setViewportSize(oldSize)
+
+ # 添加到左侧list中
+ item = QListWidgetItem(self.widgetRight)
+ image = QPixmap.fromImage(image)
+ item.setIcon(QIcon(image))
+ item.setData(Qt.UserRole + 1, image)
+
+ def onScreenShot2(self):
+ # 截图方式2
+ code = self.codeEdit.text().strip()
+ if not code:
+ return
+ self.progressdialog = QProgressDialog(self, windowTitle='正在截图中')
+ self.progressdialog.setRange(0, 0)
+ self.webView.page().mainFrame().evaluateJavaScript(CODE % code)
+ self.progressdialog.exec_()
+
+ @pyqtSlot(str)
+ def saveImage(self, image):
+ self.progressdialog.close()
+ # data:image/png;base64,iVBORw0KG....
+ if not image.startswith('data:image'):
+ return
+ data = base64.b64decode(image.split(';base64,')[1])
+ image = QPixmap()
+ image.loadFromData(data)
+ # 添加到左侧list中
+ item = QListWidgetItem(self.widgetRight)
+ item.setIcon(QIcon(image))
+ item.setData(Qt.UserRole + 1, image)
+
+
+if __name__ == "__main__":
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QWidget/Lib/CustomPaintWidget.py b/QWidget/Lib/CustomPaintWidget.py
index 4d5d4ed61bc965a16db927fdd056321c1756ae8a..f006d0a65264862cdfdd9e8e1dd0dd26abd02108 100644
--- a/QWidget/Lib/CustomPaintWidget.py
+++ b/QWidget/Lib/CustomPaintWidget.py
@@ -1,21 +1,21 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月10日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: CustomPaintWidget
@description:
-'''
-from PyQt5.QtGui import QPainter
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QStyleOption, QStyle
+"""
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtGui import QPainter
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QStyleOption, QStyle
+except ImportError:
+ from PySide2.QtGui import QPainter
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QLabel, QStyleOption, QStyle
class CustomPaintWidget(QWidget):
diff --git a/QWidget/Lib/CustomWidget.py b/QWidget/Lib/CustomWidget.py
index 11edcfb3c74b722314f3a7772d6d383c196da3cd..0231fc94c4c956ab31d581c088180be97bc8fd99 100644
--- a/QWidget/Lib/CustomWidget.py
+++ b/QWidget/Lib/CustomWidget.py
@@ -1,20 +1,19 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月10日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: CustomWidget
@description:
-'''
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
+"""
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
+except ImportError:
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QLabel
class CustomWidget(QWidget):
@@ -22,4 +21,4 @@ class CustomWidget(QWidget):
def __init__(self, *args, **kwargs):
super(CustomWidget, self).__init__(*args, **kwargs)
layout = QVBoxLayout(self)
- layout.addWidget(QLabel("我是自定义CustomWidget", self))
\ No newline at end of file
+ layout.addWidget(QLabel("我是自定义CustomWidget", self))
diff --git a/QWidget/README.md b/QWidget/README.md
index 19272ac6e0803ea372cb7e70a1b8acf89fea6bc6..d5b6c1c986cef7edb2b346547a3cbf4028d32f74 100644
--- a/QWidget/README.md
+++ b/QWidget/README.md
@@ -1,5 +1,8 @@
# QWidget
+- 目录
+ - [样式表测试](#1样式表测试)
+
## 1、样式表测试
[运行 WidgetStyle.py](WidgetStyle.py)
diff --git a/QWidget/WidgetStyle.py b/QWidget/WidgetStyle.py
index 945407c65511b505d63289cf36083fe84564c1b3..842a29a6baa7acfc6631377f76921e2ed267107e 100644
--- a/QWidget/WidgetStyle.py
+++ b/QWidget/WidgetStyle.py
@@ -1,28 +1,28 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月10日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: test
@description:
-'''
+"""
+
import sys
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QWidget, QApplication, QHBoxLayout
from Lib.CustomPaintWidget import CustomPaintWidget # @UnresolvedImport
from Lib.CustomWidget import CustomWidget # @UnresolvedImport
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
-
-
class Window(QWidget):
def __init__(self, *args, **kwargs):
@@ -31,9 +31,9 @@ class Window(QWidget):
layout.addWidget(CustomPaintWidget(self))
layout.addWidget(CustomWidget(self))
# 注意
- w = CustomWidget(self)
- w.setAttribute(Qt.WA_StyledBackground) # 很重要
- layout.addWidget(w)
+ wc = CustomWidget(self)
+ wc.setAttribute(Qt.WA_StyledBackground) # 很重要
+ layout.addWidget(wc)
if __name__ == "__main__":
diff --git a/QtChart/AreaChart.py b/QtChart/AreaChart.py
new file mode 100644
index 0000000000000000000000000000000000000000..80b88682bfbf4c4b266142bcebf5a71bab43b260
--- /dev/null
+++ b/QtChart/AreaChart.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/2
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: AreaChart
+@description: 区域图表
+"""
+
+try:
+ from PyQt5.QtChart import QChartView, QChart, QLineSeries, QAreaSeries
+ from PyQt5.QtCore import QPointF
+ from PyQt5.QtGui import QColor, QGradient, QLinearGradient, QPainter, QPen
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtCore import QPointF
+ from PySide2.QtGui import QColor, QGradient, QLinearGradient, QPainter, QPen
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QLineSeries = QtCharts.QLineSeries
+ QAreaSeries = QtCharts.QAreaSeries
+
+
+class Window(QChartView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 300)
+ # 抗锯齿
+ self.setRenderHint(QPainter.Antialiasing)
+
+ # 图表
+ chart = QChart()
+ self.setChart(chart)
+ # 设置标题
+ chart.setTitle('Simple areachart example')
+ # 添加Series
+ chart.addSeries(self.getSeries())
+ # 创建默认轴线
+ chart.createDefaultAxes()
+ # 设置xy轴的范围
+ chart.axisX().setRange(0, 20)
+ chart.axisY().setRange(0, 10)
+
+ def getSeries(self):
+ # 创建Series
+ series0 = QLineSeries(self)
+ series1 = QLineSeries(self)
+
+ # 添加数据
+ series0 << QPointF(1, 5) << QPointF(3, 7) << QPointF(7, 6) << QPointF(9, 7) \
+ << QPointF(12, 6) << QPointF(16, 7) << QPointF(18, 5)
+ series1 << QPointF(1, 3) << QPointF(3, 4) << QPointF(7, 3) << QPointF(8, 2) \
+ << QPointF(12, 3) << QPointF(16, 4) << QPointF(18, 3)
+
+ # 创建区域图
+ series = QAreaSeries(series0, series1)
+ series.setName('Batman')
+
+ # 画笔
+ pen = QPen(0x059605)
+ pen.setWidth(3)
+ series.setPen(pen)
+
+ # 设置画刷
+ gradient = QLinearGradient(QPointF(0, 0), QPointF(0, 1))
+ gradient.setColorAt(0.0, QColor(0x3cc63c))
+ gradient.setColorAt(1.0, QColor(0x26f626))
+ gradient.setCoordinateMode(QGradient.ObjectBoundingMode)
+ series.setBrush(gradient)
+
+ return series
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QtChart/BarChart.py b/QtChart/BarChart.py
new file mode 100644
index 0000000000000000000000000000000000000000..fadcbdb08e81edc06195f53c253b188f306e2203
--- /dev/null
+++ b/QtChart/BarChart.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/2
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: BarChart
+@description: 柱状图表
+"""
+
+try:
+ from PyQt5.QtChart import QChartView, QChart, QBarSet, QBarSeries, QBarCategoryAxis
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QPainter
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QPainter
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QBarSet = QtCharts.QBarSet
+ QBarSeries = QtCharts.QBarSeries
+ QBarCategoryAxis = QtCharts.QBarCategoryAxis
+
+
+class Window(QChartView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 300)
+ # 抗锯齿
+ self.setRenderHint(QPainter.Antialiasing)
+
+ # 图表
+ chart = QChart()
+ self.setChart(chart)
+ # 设置标题
+ chart.setTitle('Simple barchart example')
+ # 开启动画效果
+ chart.setAnimationOptions(QChart.SeriesAnimations)
+ # 添加Series
+ series = self.getSeries()
+ chart.addSeries(series)
+ # 分类
+ categories = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
+ # 分类x轴
+ axis = QBarCategoryAxis()
+ axis.append(categories)
+ # 创建默认轴线
+ chart.createDefaultAxes()
+ # 替换默认x轴
+ chart.setAxisX(axis, series)
+ # 显示图例
+ chart.legend().setVisible(True)
+ chart.legend().setAlignment(Qt.AlignBottom)
+
+ def getSeries(self):
+ # 创建5个柱子
+ set0 = QBarSet('Jane')
+ set1 = QBarSet('John')
+ set2 = QBarSet('Axel')
+ set3 = QBarSet('Mary')
+ set4 = QBarSet('Samantha')
+
+ # 添加数据
+ set0 << 1 << 2 << 3 << 4 << 5 << 6
+ set1 << 5 << 0 << 0 << 4 << 0 << 7
+ set2 << 3 << 5 << 8 << 13 << 8 << 5
+ set3 << 5 << 6 << 7 << 3 << 4 << 5
+ set4 << 9 << 7 << 5 << 3 << 1 << 2
+
+ # 创建柱状条
+ series = QBarSeries()
+ series.append(set0)
+ series.append(set1)
+ series.append(set2)
+ series.append(set3)
+ series.append(set4)
+ return series
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QChart/BarStack.py b/QtChart/BarStack.py
similarity index 88%
rename from QChart/BarStack.py
rename to QtChart/BarStack.py
index 431021834af7c470137b8e70d5458521298cc8bd..f0ea25ccaba81f61ab7db77a9a1905ae6566d5f5 100644
--- a/QChart/BarStack.py
+++ b/QtChart/BarStack.py
@@ -1,28 +1,36 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月28日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: charts.bar.BarStack
@description: like http://echarts.baidu.com/demo.html#bar-stack
-'''
+"""
-from random import randint
import sys
+from random import randint
-from PyQt5.QtChart import QChartView, QChart, QBarSeries, QBarSet, QBarCategoryAxis
-from PyQt5.QtCore import Qt, QPointF, QRectF, QPoint
-from PyQt5.QtGui import QPainter, QPen
-from PyQt5.QtWidgets import QApplication, QGraphicsLineItem, QWidget, \
- QHBoxLayout, QLabel, QVBoxLayout, QGraphicsProxyWidget
-
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtChart import QChartView, QChart, QBarSeries, QBarSet, QBarCategoryAxis
+ from PyQt5.QtCore import Qt, QPointF, QRectF, QPoint
+ from PyQt5.QtGui import QPainter, QPen
+ from PyQt5.QtWidgets import QApplication, QGraphicsLineItem, QWidget, \
+ QHBoxLayout, QLabel, QVBoxLayout, QGraphicsProxyWidget
+except ImportError:
+ from PySide2.QtCore import Qt, QPointF, QRectF, QPoint
+ from PySide2.QtGui import QPainter, QPen
+ from PySide2.QtWidgets import QApplication, QGraphicsLineItem, QWidget, \
+ QHBoxLayout, QLabel, QVBoxLayout, QGraphicsProxyWidget
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QBarSeries = QtCharts.QBarSeries
+ QBarSet = QtCharts.QBarSet
+ QBarCategoryAxis = QtCharts.QBarCategoryAxis
class ToolTipItem(QWidget):
@@ -45,7 +53,6 @@ class ToolTipItem(QWidget):
class ToolTipWidget(QWidget):
-
Cache = {}
def __init__(self, *args, **kwargs):
@@ -135,7 +142,7 @@ class ChartView(QChartView):
serie = self._chart.series()[0]
bars = [(bar, bar.at(index))
for bar in serie.barSets() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
-# print(bars)
+ # print(bars)
if bars:
right_top = self._chart.mapToPosition(
QPointF(self.max_x, self.max_y))
@@ -154,10 +161,10 @@ class ChartView(QChartView):
t_height = self.toolTipWidget.height()
# 如果鼠标位置离右侧的距离小于tip宽度
x = pos.x() - t_width if self.width() - \
- pos.x() - 20 < t_width else pos.x()
+ pos.x() - 20 < t_width else pos.x()
# 如果鼠标位置离底部的高度小于tip高度
y = pos.y() - t_height if self.height() - \
- pos.y() - 20 < t_height else pos.y()
+ pos.y() - 20 < t_height else pos.y()
self.toolTipWidget.show(
title, bars, QPoint(x, y))
else:
diff --git a/QtChart/ChartThemes.py b/QtChart/ChartThemes.py
new file mode 100644
index 0000000000000000000000000000000000000000..059e7f15f996248440869a572f1dca0d4f4a746d
--- /dev/null
+++ b/QtChart/ChartThemes.py
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/2
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ChartThemes
+@description: 图表主题动画等
+"""
+#############################################################################
+##
+## Copyright (C) 2013 Riverbank Computing Limited
+## Copyright (C) 2012 Digia Plc
+## All rights reserved.
+##
+## This file is part of the PyQtChart examples.
+##
+## $QT_BEGIN_LICENSE$
+## Licensees holding valid Qt Commercial licenses may use this file in
+## accordance with the Qt Commercial License Agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and Digia.
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+
+import random
+
+try:
+ from PyQt5.QtChart import (QAreaSeries, QBarSet, QChart, QChartView,
+ QLineSeries, QPieSeries, QScatterSeries, QSplineSeries,
+ QStackedBarSeries)
+ from PyQt5.QtCore import pyqtSlot, QPointF, Qt
+ from PyQt5.QtGui import QColor, QPainter, QPalette
+ from PyQt5.QtWidgets import QApplication, QMainWindow, QCheckBox, QComboBox, QGridLayout, QHBoxLayout, \
+ QLabel, QSizePolicy, QWidget
+except ImportError:
+ from PySide2.QtCore import Slot as pyqtSlot, QPointF, Qt
+ from PySide2.QtGui import QColor, QPainter, QPalette
+ from PySide2.QtWidgets import QApplication, QMainWindow, QCheckBox, QComboBox, QGridLayout, QHBoxLayout, \
+ QLabel, QSizePolicy, QWidget
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QAreaSeries = QtCharts.QAreaSeries
+ QBarSet = QtCharts.QBarSet
+ QLineSeries = QtCharts.QLineSeries
+ QPieSeries = QtCharts.QPieSeries
+ QScatterSeries = QtCharts.QScatterSeries
+ QSplineSeries = QtCharts.QSplineSeries
+ QStackedBarSeries = QtCharts.QStackedBarSeries
+
+
+class ThemeWidget(QWidget):
+
+ def __init__(self, parent=None):
+ super(ThemeWidget, self).__init__(parent)
+
+ self.m_charts = []
+ self.m_listCount = 3
+ self.m_valueMax = 10
+ self.m_valueCount = 7
+ self.m_dataTable = self.generateRandomData(self.m_listCount,
+ self.m_valueMax, self.m_valueCount)
+ self.m_themeComboBox = self.createThemeBox()
+ self.m_antialiasCheckBox = QCheckBox("Anti-aliasing")
+ self.m_animatedComboBox = self.createAnimationBox()
+ self.m_legendComboBox = self.createLegendBox()
+
+ self.connectSignals()
+
+ # Create the layout.
+ baseLayout = QGridLayout()
+ settingsLayout = QHBoxLayout()
+ settingsLayout.addWidget(QLabel("Theme:"))
+ settingsLayout.addWidget(self.m_themeComboBox)
+ settingsLayout.addWidget(QLabel("Animation:"))
+ settingsLayout.addWidget(self.m_animatedComboBox)
+ settingsLayout.addWidget(QLabel("Legend:"))
+ settingsLayout.addWidget(self.m_legendComboBox)
+ settingsLayout.addWidget(self.m_antialiasCheckBox)
+ settingsLayout.addStretch()
+ baseLayout.addLayout(settingsLayout, 0, 0, 1, 3)
+
+ # Create the charts.
+ chartView = QChartView(self.createAreaChart())
+ baseLayout.addWidget(chartView, 1, 0)
+ self.m_charts.append(chartView)
+
+ chartView = QChartView(self.createBarChart(self.m_valueCount))
+ baseLayout.addWidget(chartView, 1, 1)
+ self.m_charts.append(chartView)
+
+ chartView = QChartView(self.createLineChart())
+ baseLayout.addWidget(chartView, 1, 2)
+ self.m_charts.append(chartView)
+
+ chartView = QChartView(self.createPieChart())
+ # Funny things happen if the pie slice labels no not fit the screen...
+ chartView.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
+ baseLayout.addWidget(chartView, 2, 0)
+ self.m_charts.append(chartView)
+
+ chartView = QChartView(self.createSplineChart())
+ baseLayout.addWidget(chartView, 2, 1)
+ self.m_charts.append(chartView)
+
+ chartView = QChartView(self.createScatterChart())
+ baseLayout.addWidget(chartView, 2, 2)
+ self.m_charts.append(chartView)
+
+ self.setLayout(baseLayout)
+
+ # Set the defaults.
+ self.m_antialiasCheckBox.setChecked(True)
+ self.updateUI()
+
+ def connectSignals(self):
+ self.m_themeComboBox.currentIndexChanged.connect(self.updateUI)
+ self.m_antialiasCheckBox.toggled.connect(self.updateUI)
+ self.m_animatedComboBox.currentIndexChanged.connect(self.updateUI)
+ self.m_legendComboBox.currentIndexChanged.connect(self.updateUI)
+
+ def generateRandomData(self, listCount, valueMax, valueCount):
+ random.seed()
+
+ dataTable = []
+
+ for i in range(listCount):
+ dataList = []
+ yValue = 0.0
+ f_valueCount = float(valueCount)
+
+ for j in range(valueCount):
+ yValue += random.uniform(0, valueMax) / f_valueCount
+ value = QPointF(
+ j + random.random() * self.m_valueMax / f_valueCount,
+ yValue)
+ label = "Slice " + str(i) + ":" + str(j)
+ dataList.append((value, label))
+
+ dataTable.append(dataList)
+
+ return dataTable
+
+ def createThemeBox(self):
+ themeComboBox = QComboBox()
+
+ themeComboBox.addItem("Light", QChart.ChartThemeLight)
+ themeComboBox.addItem("Blue Cerulean", QChart.ChartThemeBlueCerulean)
+ themeComboBox.addItem("Dark", QChart.ChartThemeDark)
+ themeComboBox.addItem("Brown Sand", QChart.ChartThemeBrownSand)
+ themeComboBox.addItem("Blue NCS", QChart.ChartThemeBlueNcs)
+ themeComboBox.addItem("High Contrast", QChart.ChartThemeHighContrast)
+ themeComboBox.addItem("Blue Icy", QChart.ChartThemeBlueIcy)
+
+ return themeComboBox
+
+ def createAnimationBox(self):
+ animationComboBox = QComboBox()
+
+ animationComboBox.addItem("No Animations", QChart.NoAnimation)
+ animationComboBox.addItem("GridAxis Animations", QChart.GridAxisAnimations)
+ animationComboBox.addItem("Series Animations", QChart.SeriesAnimations)
+ animationComboBox.addItem("All Animations", QChart.AllAnimations)
+
+ return animationComboBox
+
+ def createLegendBox(self):
+ legendComboBox = QComboBox()
+
+ legendComboBox.addItem("No Legend ", 0)
+ legendComboBox.addItem("Legend Top", Qt.AlignTop)
+ legendComboBox.addItem("Legend Bottom", Qt.AlignBottom)
+ legendComboBox.addItem("Legend Left", Qt.AlignLeft)
+ legendComboBox.addItem("Legend Right", Qt.AlignRight)
+
+ return legendComboBox
+
+ def createAreaChart(self):
+ chart = QChart()
+ chart.setTitle("Area chart")
+
+ # The lower series is initialized to zero values.
+ lowerSeries = None
+ y_points = []
+
+ for i, data_list in enumerate(self.m_dataTable):
+ upperSeries = QLineSeries(chart)
+ for j, (value, _) in enumerate(data_list):
+ y = value.y()
+
+ if lowerSeries is None:
+ upperSeries.append(QPointF(j, y))
+ y_points.append(y)
+ else:
+ new_y = y_points[i] + y
+ upperSeries.append(QPointF(j, new_y))
+ y_points[j] += new_y
+
+ area = QAreaSeries(upperSeries, lowerSeries)
+ area.setName("Series " + str(i))
+ chart.addSeries(area)
+ lowerSeries = upperSeries
+
+ chart.createDefaultAxes()
+
+ return chart
+
+ def createBarChart(self, valueCount):
+ chart = QChart()
+ chart.setTitle("Bar chart")
+
+ series = QStackedBarSeries(chart)
+
+ for i, data_list in enumerate(self.m_dataTable):
+ set = QBarSet("Bar set " + str(i))
+ for value, _ in data_list:
+ set << value.y()
+
+ series.append(set)
+
+ chart.addSeries(series)
+ chart.createDefaultAxes()
+
+ return chart
+
+ def createLineChart(self):
+ chart = QChart()
+ chart.setTitle("Line chart")
+
+ for i, data_list in enumerate(self.m_dataTable):
+ series = QLineSeries(chart)
+ for value, _ in data_list:
+ series.append(value)
+
+ series.setName("Series " + str(i))
+ chart.addSeries(series)
+
+ chart.createDefaultAxes()
+
+ return chart
+
+ def createPieChart(self):
+ chart = QChart()
+ chart.setTitle("Pie chart")
+
+ pieSize = 1.0 / len(self.m_dataTable)
+
+ for i, data_list in enumerate(self.m_dataTable):
+ series = QPieSeries(chart)
+ for value, label in data_list:
+ slice = series.append(label, value.y())
+ if series.count() == 1:
+ slice.setLabelVisible()
+ slice.setExploded()
+
+ hPos = (pieSize / 2) + (i / float(len(self.m_dataTable)))
+ series.setPieSize(pieSize)
+ series.setHorizontalPosition(hPos)
+ series.setVerticalPosition(0.5)
+
+ chart.addSeries(series)
+
+ return chart
+
+ def createSplineChart(self):
+ chart = QChart()
+ chart.setTitle("Spline chart")
+
+ for i, data_list in enumerate(self.m_dataTable):
+ series = QSplineSeries(chart)
+ for value, _ in data_list:
+ series.append(value)
+
+ series.setName("Series " + str(i))
+ chart.addSeries(series)
+
+ chart.createDefaultAxes()
+
+ return chart
+
+ def createScatterChart(self):
+ chart = QChart()
+ chart.setTitle("Scatter chart")
+
+ for i, data_list in enumerate(self.m_dataTable):
+ series = QScatterSeries(chart)
+ for value, _ in data_list:
+ series.append(value)
+
+ series.setName("Series " + str(i))
+ chart.addSeries(series)
+
+ chart.createDefaultAxes()
+
+ return chart
+
+ @pyqtSlot()
+ def updateUI(self):
+ theme = self.m_themeComboBox.itemData(
+ self.m_themeComboBox.currentIndex())
+
+ if self.m_charts[0].chart().theme() != theme:
+ for chartView in self.m_charts:
+ chartView.chart().setTheme(QChart.ChartTheme(theme))
+
+ pal = self.window().palette()
+
+ if theme == QChart.ChartThemeLight:
+ pal.setColor(QPalette.Window, QColor(0xf0f0f0))
+ pal.setColor(QPalette.WindowText, QColor(0x404044))
+ elif theme == QChart.ChartThemeDark:
+ pal.setColor(QPalette.Window, QColor(0x121218))
+ pal.setColor(QPalette.WindowText, QColor(0xd6d6d6))
+ elif theme == QChart.ChartThemeBlueCerulean:
+ pal.setColor(QPalette.Window, QColor(0x40434a))
+ pal.setColor(QPalette.WindowText, QColor(0xd6d6d6))
+ elif theme == QChart.ChartThemeBrownSand:
+ pal.setColor(QPalette.Window, QColor(0x9e8965))
+ pal.setColor(QPalette.WindowText, QColor(0x404044))
+ elif theme == QChart.ChartThemeBlueNcs:
+ pal.setColor(QPalette.Window, QColor(0x018bba))
+ pal.setColor(QPalette.WindowText, QColor(0x404044))
+ elif theme == QChart.ChartThemeHighContrast:
+ pal.setColor(QPalette.Window, QColor(0xffab03))
+ pal.setColor(QPalette.WindowText, QColor(0x181818))
+ elif theme == QChart.ChartThemeBlueIcy:
+ pal.setColor(QPalette.Window, QColor(0xcee7f0))
+ pal.setColor(QPalette.WindowText, QColor(0x404044))
+ else:
+ pal.setColor(QPalette.Window, QColor(0xf0f0f0))
+ pal.setColor(QPalette.WindowText, QColor(0x404044))
+
+ self.window().setPalette(pal)
+
+ checked = self.m_antialiasCheckBox.isChecked()
+ for chartView in self.m_charts:
+ chartView.setRenderHint(QPainter.Antialiasing, checked)
+
+ options = QChart.AnimationOptions(
+ self.m_animatedComboBox.itemData(
+ self.m_animatedComboBox.currentIndex()))
+
+ if self.m_charts[0].chart().animationOptions() != options:
+ for chartView in self.m_charts:
+ chartView.chart().setAnimationOptions(options)
+
+ alignment = self.m_legendComboBox.itemData(
+ self.m_legendComboBox.currentIndex())
+
+ for chartView in self.m_charts:
+ legend = chartView.chart().legend()
+
+ if alignment == 0:
+ legend.hide()
+ else:
+ legend.setAlignment(Qt.Alignment(alignment))
+ legend.show()
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+
+ window = QMainWindow()
+ widget = ThemeWidget()
+ window.setCentralWidget(widget)
+ window.resize(900, 600)
+ window.show()
+
+ sys.exit(app.exec_())
diff --git a/QtChart/CpuLineChart.py b/QtChart/CpuLineChart.py
new file mode 100644
index 0000000000000000000000000000000000000000..17b521e50d32a9ea7e57427d65ee6a8e5280eb39
--- /dev/null
+++ b/QtChart/CpuLineChart.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2021/5/13
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: CpuLineChart
+@description:
+"""
+
+import sys
+
+from PyQt5.QtChart import QChartView, QChart, QSplineSeries, QDateTimeAxis, QValueAxis
+from PyQt5.QtCore import Qt, QTimer, QDateTime, QPointF
+from PyQt5.QtGui import QPainter, QPen, QColor
+from PyQt5.QtWidgets import QApplication
+from psutil import cpu_percent
+
+
+class CpuLineChart(QChart):
+
+ def __init__(self, *args, **kwargs):
+ super(CpuLineChart, self).__init__(*args, **kwargs)
+ self.m_count = 10
+ # 隐藏图例
+ self.legend().hide()
+ self.m_series = QSplineSeries(self)
+ # 设置画笔
+ self.m_series.setPen(QPen(QColor('#3B8CFF'), 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
+ self.addSeries(self.m_series)
+ # x轴
+ self.m_axisX = QDateTimeAxis(self)
+ self.m_axisX.setTickCount(self.m_count + 1) # 设置刻度数量
+ self.m_axisX.setFormat('hh:mm:ss') # 设置时间显示格式
+ now = QDateTime.currentDateTime() # 前10秒到现在
+ self.m_axisX.setRange(now.addSecs(-self.m_count), now)
+ self.addAxis(self.m_axisX, Qt.AlignBottom)
+ self.m_series.attachAxis(self.m_axisX)
+ # y轴
+ self.m_axisY = QValueAxis(self)
+ self.m_axisY.setLabelFormat('%d') # 设置文本格式
+ self.m_axisY.setMinorTickCount(4) # 设置小刻度线的数目
+ self.m_axisY.setTickCount(self.m_count + 1)
+ self.m_axisY.setRange(0, 100)
+ self.addAxis(self.m_axisY, Qt.AlignLeft)
+ self.m_series.attachAxis(self.m_axisY)
+
+ # 填充11个初始点,注意x轴 需要转为秒的时间戳
+ self.m_series.append(
+ [QPointF(now.addSecs(-i).toMSecsSinceEpoch(), 0) for i in range(self.m_count, -1, -1)])
+
+ # 定时器获取数据
+ self.m_timer = QTimer()
+ self.m_timer.timeout.connect(self.update_data)
+ self.m_timer.start(1000)
+
+ def update_data(self):
+ value = cpu_percent()
+ now = QDateTime.currentDateTime()
+ self.m_axisX.setRange(now.addSecs(-self.m_count), now) # 重新调整x轴的时间范围
+ # 获取原来的所有点,去掉第一个并追加新的一个
+ points = self.m_series.pointsVector()
+ points.pop(0)
+ points.append(QPointF(now.toMSecsSinceEpoch(), value))
+ # 替换法速度更快
+ self.m_series.replace(points)
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ chart = CpuLineChart()
+ chart.setTitle('cpu')
+ # chart.setAnimationOptions(QChart.SeriesAnimations)
+
+ view = QChartView(chart)
+ view.setRenderHint(QPainter.Antialiasing) # 抗锯齿
+ view.resize(800, 600)
+ view.show()
+ sys.exit(app.exec_())
diff --git a/QChart/CustomXYaxis.py b/QtChart/CustomXYaxis.py
similarity index 88%
rename from QChart/CustomXYaxis.py
rename to QtChart/CustomXYaxis.py
index 9ab84fbea56b80a1950670914d6504e074d712a0..014ab6bc84be81f26de2df6268b8be5af8d7cd04 100644
--- a/QChart/CustomXYaxis.py
+++ b/QtChart/CustomXYaxis.py
@@ -1,22 +1,30 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月19日
-@author: Irony."[讽刺]
-@site: http://alyl.vip, http://orzorz.vip, http://coding.net/u/892768447, http://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: CustomXYaxis
@description:
-'''
+"""
import random
import sys
-from PyQt5.QtChart import QChartView, QLineSeries, QChart, QCategoryAxis
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout
-
-__version__ = "0.0.1"
+try:
+ from PyQt5.QtChart import QChartView, QLineSeries, QChart, QCategoryAxis
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QApplication, QWidget, QHBoxLayout
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QLineSeries = QtCharts.QLineSeries
+ QCategoryAxis = QtCharts.QCategoryAxis
m_listCount = 3
m_valueMax = 10
diff --git a/QtChart/DynamicSpline.py b/QtChart/DynamicSpline.py
new file mode 100644
index 0000000000000000000000000000000000000000..b6bed0e00471c5c2d008519e2a1157436f1bd68a
--- /dev/null
+++ b/QtChart/DynamicSpline.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年5月5日
+@author: Yimelia
+@site: https://github.com/yimelia
+@file: DynamicSpline
+@description: This example shows how to draw dynamic data. https://doc.qt.io/qt-5/qtcharts-dynamicspline-example.html
+"""
+import sys
+
+try:
+ from PyQt5.QtChart import QChartView, QChart, QSplineSeries, QValueAxis
+ from PyQt5.QtCore import Qt, QTimer, QRandomGenerator
+ from PyQt5.QtGui import QPainter, QPen
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtCore import Qt, QTimer, QRandomGenerator
+ from PySide2.QtGui import QPainter, QPen
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QSplineSeries = QtCharts.QSplineSeries
+ QValueAxis = QtCharts.QValueAxis
+
+
+class DynamicSpline(QChart):
+ def __init__(self):
+ super().__init__()
+ self.m_step = 0
+ self.m_x = 5
+ self.m_y = 1
+ # 初始化图像
+ self.series = QSplineSeries(self)
+ green_pen = QPen(Qt.red)
+ green_pen.setWidth(3)
+ self.series.setPen(green_pen)
+ self.axisX = QValueAxis()
+ self.axisY = QValueAxis()
+ self.series.append(self.m_x, self.m_y)
+
+ self.addSeries(self.series)
+ self.addAxis(self.axisX, Qt.AlignBottom)
+ self.addAxis(self.axisY, Qt.AlignLeft)
+ self.series.attachAxis(self.axisX)
+ self.series.attachAxis(self.axisY)
+ self.axisX.setTickCount(5)
+ self.axisX.setRange(0, 10)
+ self.axisY.setRange(-5, 10)
+
+ self.timer = QTimer(self)
+ self.timer.setInterval(1000)
+ self.timer.timeout.connect(self.handleTimeout)
+ self.timer.start()
+
+ def handleTimeout(self):
+ x = self.plotArea().width() / self.axisX.tickCount()
+ y = (self.axisX.max() - self.axisX.min()) / self.axisX.tickCount()
+ self.m_x += y
+ # 在PyQt5.11.3及以上版本中,QRandomGenerator.global()被重命名为global_()
+ self.m_y = QRandomGenerator.global_().bounded(5) - 2.5
+ self.series.append(self.m_x, self.m_y)
+ self.scroll(x, 0)
+ if self.m_x >= 100:
+ self.timer.stop()
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ chart = DynamicSpline()
+ chart.setTitle("Dynamic spline chart")
+ chart.legend().hide()
+ chart.setAnimationOptions(QChart.AllAnimations)
+
+ view = QChartView(chart)
+ view.setRenderHint(QPainter.Antialiasing) # 抗锯齿
+ view.resize(400, 300)
+ view.show()
+ sys.exit(app.exec_())
diff --git a/QtChart/HorizontalBarChart.py b/QtChart/HorizontalBarChart.py
new file mode 100644
index 0000000000000000000000000000000000000000..6018cefb1eb72d37871acbad4addc6f85bfddf0a
--- /dev/null
+++ b/QtChart/HorizontalBarChart.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/2
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: HorizontalBarChart
+@description: 横向柱状图表
+"""
+
+try:
+ from PyQt5.QtChart import QChartView, QChart, QBarSet, QHorizontalBarSeries, QBarCategoryAxis
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QPainter
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QPainter
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QBarSet = QtCharts.QBarSet
+ QHorizontalBarSeries = QtCharts.QHorizontalBarSeries
+ QBarCategoryAxis = QtCharts.QBarCategoryAxis
+
+
+class Window(QChartView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 300)
+ # 抗锯齿
+ self.setRenderHint(QPainter.Antialiasing)
+
+ # 图表
+ chart = QChart()
+ self.setChart(chart)
+ # 设置标题
+ chart.setTitle('Simple horizontal barchart example')
+ # 开启动画效果
+ chart.setAnimationOptions(QChart.SeriesAnimations)
+ # 添加Series
+ series = self.getSeries()
+ chart.addSeries(series)
+ # 分类
+ categories = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
+ # 分类x轴
+ axis = QBarCategoryAxis()
+ axis.append(categories)
+ # 创建默认轴线
+ chart.createDefaultAxes()
+ # 替换默认y轴
+ chart.setAxisY(axis, series)
+ # 显示图例
+ chart.legend().setVisible(True)
+ chart.legend().setAlignment(Qt.AlignBottom)
+
+ def getSeries(self):
+ # 创建5个柱子
+ set0 = QBarSet('Jane')
+ set1 = QBarSet('John')
+ set2 = QBarSet('Axel')
+ set3 = QBarSet('Mary')
+ set4 = QBarSet('Samantha')
+
+ # 添加数据
+ set0 << 1 << 2 << 3 << 4 << 5 << 6
+ set1 << 5 << 0 << 0 << 4 << 0 << 7
+ set2 << 3 << 5 << 8 << 13 << 8 << 5
+ set3 << 5 << 6 << 7 << 3 << 4 << 5
+ set4 << 9 << 7 << 5 << 3 << 1 << 2
+
+ # 创建柱状条
+ series = QHorizontalBarSeries()
+ series.append(set0)
+ series.append(set1)
+ series.append(set2)
+ series.append(set3)
+ series.append(set4)
+ return series
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QtChart/HorizontalPercentBarChart.py b/QtChart/HorizontalPercentBarChart.py
new file mode 100644
index 0000000000000000000000000000000000000000..785efc9f884baa679e795f42e976888c61976675
--- /dev/null
+++ b/QtChart/HorizontalPercentBarChart.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/2
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: HorizontalPercentBarChart
+@description: 横向百分比柱状图表
+"""
+
+try:
+ from PyQt5.QtChart import QChartView, QChart, QBarSet, QHorizontalPercentBarSeries, QBarCategoryAxis
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QPainter
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QPainter
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QBarSet = QtCharts.QBarSet
+ QHorizontalPercentBarSeries = QtCharts.QHorizontalPercentBarSeries
+ QBarCategoryAxis = QtCharts.QBarCategoryAxis
+
+
+class Window(QChartView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 300)
+ # 抗锯齿
+ self.setRenderHint(QPainter.Antialiasing)
+
+ # 图表
+ chart = QChart()
+ self.setChart(chart)
+ # 设置标题
+ chart.setTitle('Simple horizontal percent barchart example')
+ # 开启动画效果
+ chart.setAnimationOptions(QChart.SeriesAnimations)
+ # 添加Series
+ series = self.getSeries()
+ chart.addSeries(series)
+ # 分类
+ categories = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
+ # 分类x轴
+ axis = QBarCategoryAxis()
+ axis.append(categories)
+ # 创建默认轴线
+ chart.createDefaultAxes()
+ # 替换默认y轴
+ chart.setAxisY(axis, series)
+ # 显示图例
+ chart.legend().setVisible(True)
+ chart.legend().setAlignment(Qt.AlignBottom)
+
+ def getSeries(self):
+ # 创建5个柱子
+ set0 = QBarSet('Jane')
+ set1 = QBarSet('John')
+ set2 = QBarSet('Axel')
+ set3 = QBarSet('Mary')
+ set4 = QBarSet('Samantha')
+
+ # 添加数据
+ set0 << 1 << 2 << 3 << 4 << 5 << 6
+ set1 << 5 << 0 << 0 << 4 << 0 << 7
+ set2 << 3 << 5 << 8 << 13 << 8 << 5
+ set3 << 5 << 6 << 7 << 3 << 4 << 5
+ set4 << 9 << 7 << 5 << 3 << 1 << 2
+
+ # 创建柱状条
+ series = QHorizontalPercentBarSeries()
+ series.append(set0)
+ series.append(set1)
+ series.append(set2)
+ series.append(set3)
+ series.append(set4)
+ return series
+
+
+if __name__ == '__main__':
+ import sys
+ from PyQt5.QtWidgets import QApplication
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QChart/LineChart.py b/QtChart/LineChart.py
similarity index 48%
rename from QChart/LineChart.py
rename to QtChart/LineChart.py
index 579a00fe6b7f0e9c5537105803dd48aa4291f4f5..9386a99c6aab929651b75a4c12f57fc57adafd2d 100644
--- a/QChart/LineChart.py
+++ b/QtChart/LineChart.py
@@ -1,28 +1,33 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月19日
-@author: Irony."[讽刺]
-@site: http://alyl.vip, http://orzorz.vip, http://coding.net/u/892768447, http://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: LineChart
@description:
-'''
+"""
import sys
-from PyQt5.QtChart import QChartView, QLineSeries, QChart
-from PyQt5.QtGui import QPainter
-from PyQt5.QtWidgets import QApplication
+try:
+ from PyQt5.QtChart import QChartView, QLineSeries, QChart
+ from PyQt5.QtGui import QPainter
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtGui import QPainter
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtCharts import QtCharts
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QLineSeries = QtCharts.QLineSeries
-__version__ = "0.0.1"
-
-
-if __name__ == "__main__":
+if __name__ == '__main__':
app = QApplication(sys.argv)
chart = QChart()
- chart.setTitle("Line Chart 1")
+ chart.setTitle('Line Chart 1')
series = QLineSeries(chart)
series.append(0, 6)
series.append(2, 4)
diff --git a/QChart/LineStack.py b/QtChart/LineStack.py
similarity index 89%
rename from QChart/LineStack.py
rename to QtChart/LineStack.py
index 1ee13b39495f6403cb9689a6f96f0672fd69c759..fb03eaf1281491be698bdf6521911cc180d162fd 100644
--- a/QChart/LineStack.py
+++ b/QtChart/LineStack.py
@@ -1,28 +1,36 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月28日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: charts.line.LineStack
@description: like http://echarts.baidu.com/demo.html#line-stack
-'''
+"""
import sys
-from PyQt5.QtChart import QChartView, QChart, QLineSeries, QLegend,\
- QCategoryAxis
-from PyQt5.QtCore import Qt, QPointF, QRectF, QPoint
-from PyQt5.QtGui import QPainter, QPen
-from PyQt5.QtWidgets import QApplication, QGraphicsLineItem, QWidget, \
- QHBoxLayout, QLabel, QVBoxLayout, QGraphicsProxyWidget
-
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtChart import QChartView, QChart, QLineSeries, QLegend, \
+ QCategoryAxis
+ from PyQt5.QtCore import Qt, QPointF, QRectF, QPoint
+ from PyQt5.QtGui import QPainter, QPen
+ from PyQt5.QtWidgets import QApplication, QGraphicsLineItem, QWidget, \
+ QHBoxLayout, QLabel, QVBoxLayout, QGraphicsProxyWidget
+except ImportError:
+ from PySide2.QtCore import Qt, QPointF, QRectF, QPoint
+ from PySide2.QtGui import QPainter, QPen
+ from PySide2.QtWidgets import QApplication, QGraphicsLineItem, QWidget, \
+ QHBoxLayout, QLabel, QVBoxLayout, QGraphicsProxyWidget
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QLineSeries = QtCharts.QLineSeries
+ QLegend = QtCharts.QLegend
+ QCategoryAxis = QtCharts.QCategoryAxis
class ToolTipItem(QWidget):
@@ -45,7 +53,6 @@ class ToolTipItem(QWidget):
class ToolTipWidget(QWidget):
-
Cache = {}
def __init__(self, *args, **kwargs):
@@ -131,7 +138,7 @@ class ChartView(QChartView):
self.point_bottom = self._chart.mapToPosition(
QPointF(self.min_x, self.min_y))
self.step_x = (self.max_x - self.min_x) / \
- (self._chart.axisX().tickCount() - 1)
+ (self._chart.axisX().tickCount() - 1)
def mouseMoveEvent(self, event):
super(ChartView, self).mouseMoveEvent(event)
@@ -159,10 +166,10 @@ class ChartView(QChartView):
t_height = self.toolTipWidget.height()
# 如果鼠标位置离右侧的距离小于tip宽度
x = pos.x() - t_width if self.width() - \
- pos.x() - 20 < t_width else pos.x()
+ pos.x() - 20 < t_width else pos.x()
# 如果鼠标位置离底部的高度小于tip高度
y = pos.y() - t_height if self.height() - \
- pos.y() - 20 < t_height else pos.y()
+ pos.y() - 20 < t_height else pos.y()
self.toolTipWidget.show(
title, points, QPoint(x, y))
else:
@@ -174,7 +181,7 @@ class ChartView(QChartView):
if not marker:
return
visible = not marker.series().isVisible()
-# # 隐藏或显示series
+ # # 隐藏或显示series
marker.series().setVisible(visible)
marker.setVisible(True) # 要保证marker一直显示
# 透明度
diff --git a/QtChart/PercentBarChart.py b/QtChart/PercentBarChart.py
new file mode 100644
index 0000000000000000000000000000000000000000..dcbf8837796a2a9791e965925e9241dc5eaa16b6
--- /dev/null
+++ b/QtChart/PercentBarChart.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/2
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: PercentBarChart
+@description: 百分比柱状图表
+"""
+
+try:
+ from PyQt5.QtChart import QChartView, QChart, QBarSet, QPercentBarSeries, QBarCategoryAxis
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QPainter
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QPainter
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QBarSet = QtCharts.QBarSet
+ QPercentBarSeries = QtCharts.QPercentBarSeries
+ QBarCategoryAxis = QtCharts.QBarCategoryAxis
+
+
+class Window(QChartView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 300)
+ # 抗锯齿
+ self.setRenderHint(QPainter.Antialiasing)
+
+ # 图表
+ chart = QChart()
+ self.setChart(chart)
+ # 设置标题
+ chart.setTitle('Simple percentbarchart example')
+ # 开启动画效果
+ chart.setAnimationOptions(QChart.SeriesAnimations)
+ # 添加Series
+ series = self.getSeries()
+ chart.addSeries(series)
+ # 分类
+ categories = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
+ # 分类x轴
+ axis = QBarCategoryAxis()
+ axis.append(categories)
+ # 创建默认轴线
+ chart.createDefaultAxes()
+ # 替换默认x轴
+ chart.setAxisX(axis, series)
+ # 显示图例
+ chart.legend().setVisible(True)
+ chart.legend().setAlignment(Qt.AlignBottom)
+
+ def getSeries(self):
+ # 创建5个柱子
+ set0 = QBarSet('Jane')
+ set1 = QBarSet('John')
+ set2 = QBarSet('Axel')
+ set3 = QBarSet('Mary')
+ set4 = QBarSet('Samantha')
+
+ # 添加数据
+ set0 << 1 << 2 << 3 << 4 << 5 << 6
+ set1 << 5 << 0 << 0 << 4 << 0 << 7
+ set2 << 3 << 5 << 8 << 13 << 8 << 5
+ set3 << 5 << 6 << 7 << 3 << 4 << 5
+ set4 << 9 << 7 << 5 << 3 << 1 << 2
+
+ # 创建柱状条
+ series = QPercentBarSeries()
+ series.append(set0)
+ series.append(set1)
+ series.append(set2)
+ series.append(set3)
+ series.append(set4)
+ return series
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QtChart/PieChart.py b/QtChart/PieChart.py
new file mode 100644
index 0000000000000000000000000000000000000000..57fa5bdeb8b759572556726ad102840f5b2624fb
--- /dev/null
+++ b/QtChart/PieChart.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/2
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: PieChart
+@description: 饼状图表
+"""
+
+try:
+ from PyQt5.QtChart import QChartView, QChart, QPieSeries
+ from PyQt5.QtGui import QPainter, QColor
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtGui import QPainter, QColor
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QPieSeries = QtCharts.QPieSeries
+
+
+class Window(QChartView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 300)
+ # 抗锯齿
+ self.setRenderHint(QPainter.Antialiasing)
+
+ # 图表
+ chart = QChart()
+ self.setChart(chart)
+ # 设置标题
+ chart.setTitle('Simple piechart example')
+ # 添加Series
+ chart.addSeries(self.getSeries())
+
+ def getSeries(self):
+ series = QPieSeries()
+ slice0 = series.append('10%', 1)
+ series.append('20%', 2)
+ series.append('70%', 7)
+ # 显示label文字
+ series.setLabelsVisible()
+ series.setPieSize(0.5)
+ # 使第一块突出显示
+ slice0.setLabelVisible()
+ slice0.setExploded()
+ # 设置第一块颜色
+ slice0.setColor(QColor(255, 0, 0, 100))
+ return series
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QtChart/README.en.md b/QtChart/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QtChart/README.md b/QtChart/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..62271e5c1064d174505282a733b6d5d5470b0589
--- /dev/null
+++ b/QtChart/README.md
@@ -0,0 +1,105 @@
+# QtChart
+
+- 目录
+ - [折线图](#1折线图)
+ - [折线堆叠图](#2折线堆叠图)
+ - [柱状堆叠图](#3柱状堆叠图)
+ - [LineChart自定义xy轴](#4LineChart自定义xy轴)
+ - [ToolTip提示](#5ToolTip提示)
+ - [动态曲线图](#6动态曲线图)
+ - [区域图表](#7区域图表)
+ - [柱状图表](#8柱状图表)
+ - [饼状图表](#9饼状图表)
+ - [样条图表](#10样条图表)
+ - [百分比柱状图表](#11百分比柱状图表)
+ - [横向柱状图表](#12横向柱状图表)
+ - [横向百分比柱状图表](#13横向百分比柱状图表)
+ - [散点图表](#14散点图表)
+ - [图表主题动画](#15图表主题动画)
+ - [CPU动态折线图](#16CPU动态折线图)
+
+## 1、折线图
+[运行 LineChart.py](LineChart.py)
+
+
+
+## 2、折线堆叠图
+[运行 LineStack.py](LineStack.py)
+
+仿照 [line-stack](http://echarts.baidu.com/demo.html#line-stack)
+
+
+
+## 3、柱状堆叠图
+[运行 BarStack.py](BarStack.py)
+
+仿照 [bar-stack](http://echarts.baidu.com/demo.html#bar-stack)
+
+
+
+## 4、LineChart自定义xy轴
+[运行 CustomXYaxis.py](CustomXYaxis.py)
+
+
+
+## 5、ToolTip提示
+[运行 ToolTip.py](ToolTip.py) | [运行 ToolTip2.py](ToolTip2.py)
+
+ 
+
+## 6、动态曲线图
+[运行 DynamicSpline.py](DynamicSpline.py)
+
+
+
+## 7、区域图表
+[运行 AreaChart.py](AreaChart.py)
+
+
+
+## 8、柱状图表
+[运行 BarChart.py](BarChart.py)
+
+
+
+## 9、饼状图表
+[运行 PieChart.py](PieChart.py)
+
+
+
+## 10、样条图表
+[运行 SplineChart.py](SplineChart.py)
+
+
+
+## 11、百分比柱状图表
+[运行 PercentBarChart.py](PercentBarChart.py)
+
+
+
+## 12、横向柱状图表
+[运行 HorizontalBarChart.py](HorizontalBarChart.py)
+
+
+
+## 13、横向百分比柱状图表
+[运行 HorizontalPercentBarChart.py](HorizontalPercentBarChart.py)
+
+
+
+## 14、散点图表
+[运行 ScatterChart.py](ScatterChart.py)
+
+
+
+## 15、图表主题动画
+[运行 ChartThemes.py](ChartThemes.py)
+
+
+
+## 16、CPU动态折线图
+[运行 CpuLineChart.py](CpuLineChart.py)
+
+通过设置x轴的时间范围并替换y点达到动态移动效果
+
+
\ No newline at end of file
diff --git a/QtChart/ScatterChart.py b/QtChart/ScatterChart.py
new file mode 100644
index 0000000000000000000000000000000000000000..d523e83c751ecf419f2b6f21ccece4f7aa932c53
--- /dev/null
+++ b/QtChart/ScatterChart.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/2
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ScatterChart
+@description: 散点图表
+"""
+import random
+
+try:
+ from PyQt5.QtChart import QChartView, QChart, QScatterSeries
+ from PyQt5.QtCore import QPointF
+ from PyQt5.QtGui import QPainter
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtCore import QPointF
+ from PySide2.QtGui import QPainter
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QScatterSeries = QtCharts.QScatterSeries
+
+
+class Window(QChartView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 300)
+ # 抗锯齿
+ self.setRenderHint(QPainter.Antialiasing)
+ # 生成模拟数据
+ self.m_dataTable = self.generateRandomData(3, 10, 7)
+
+ # 图表
+ chart = QChart()
+ self.setChart(chart)
+ # 设置标题
+ chart.setTitle('Scatter chart')
+ # 添加Series
+ self.getSeries(chart)
+ # 创建默认xy轴
+ chart.createDefaultAxes()
+ chart.legend().setVisible(False)
+
+ def getSeries(self, chart):
+ for i, data_list in enumerate(self.m_dataTable):
+ series = QScatterSeries(chart)
+ for value, _ in data_list:
+ series.append(value)
+
+ series.setName('Series ' + str(i))
+ chart.addSeries(series)
+
+ def generateRandomData(self, listCount, valueMax, valueCount):
+ # 生成模拟随机数据
+ random.seed()
+
+ dataTable = []
+
+ for i in range(listCount):
+ dataList = []
+ yValue = 0.0
+ f_valueCount = float(valueCount)
+
+ for j in range(valueCount):
+ yValue += random.uniform(0, valueMax) / f_valueCount
+ value = QPointF(j + random.random() * valueMax / f_valueCount, yValue)
+ label = 'Slice ' + str(i) + ':' + str(j)
+ dataList.append((value, label))
+
+ dataTable.append(dataList)
+
+ return dataTable
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QtChart/ScreenShot/AreaChart.png b/QtChart/ScreenShot/AreaChart.png
new file mode 100644
index 0000000000000000000000000000000000000000..701c4365125d476fb9a0be47febc76ae854403cb
Binary files /dev/null and b/QtChart/ScreenShot/AreaChart.png differ
diff --git a/QtChart/ScreenShot/BarChart.png b/QtChart/ScreenShot/BarChart.png
new file mode 100644
index 0000000000000000000000000000000000000000..182e1f40a99d6845b265026bbbd279dba8ee2c0e
Binary files /dev/null and b/QtChart/ScreenShot/BarChart.png differ
diff --git a/QChart/ScreenShot/BarStack.gif b/QtChart/ScreenShot/BarStack.gif
similarity index 100%
rename from QChart/ScreenShot/BarStack.gif
rename to QtChart/ScreenShot/BarStack.gif
diff --git a/QtChart/ScreenShot/ChartThemes.gif b/QtChart/ScreenShot/ChartThemes.gif
new file mode 100644
index 0000000000000000000000000000000000000000..9ef212a1981f7c11ba46998e0a673fd6f54d097b
Binary files /dev/null and b/QtChart/ScreenShot/ChartThemes.gif differ
diff --git a/QtChart/ScreenShot/CpuLineChart.png b/QtChart/ScreenShot/CpuLineChart.png
new file mode 100644
index 0000000000000000000000000000000000000000..06cb3b2cfbf0a6f3a26e656a2902549cb83faa64
Binary files /dev/null and b/QtChart/ScreenShot/CpuLineChart.png differ
diff --git a/QChart/ScreenShot/CustomXYaxis.png b/QtChart/ScreenShot/CustomXYaxis.png
similarity index 100%
rename from QChart/ScreenShot/CustomXYaxis.png
rename to QtChart/ScreenShot/CustomXYaxis.png
diff --git a/QtChart/ScreenShot/DynamicSplineChart.gif b/QtChart/ScreenShot/DynamicSplineChart.gif
new file mode 100644
index 0000000000000000000000000000000000000000..71c2f6bdbe23de8417ea9bbb6af22b595d78578c
Binary files /dev/null and b/QtChart/ScreenShot/DynamicSplineChart.gif differ
diff --git a/QtChart/ScreenShot/HorizontalBarChart.png b/QtChart/ScreenShot/HorizontalBarChart.png
new file mode 100644
index 0000000000000000000000000000000000000000..5e4830ae55e33b1e7a97eab2c5c09aeb0ef1437d
Binary files /dev/null and b/QtChart/ScreenShot/HorizontalBarChart.png differ
diff --git a/QtChart/ScreenShot/HorizontalPercentBarChart.png b/QtChart/ScreenShot/HorizontalPercentBarChart.png
new file mode 100644
index 0000000000000000000000000000000000000000..554e8aec17cf93f77b11d9e59563cd0fc3be5a73
Binary files /dev/null and b/QtChart/ScreenShot/HorizontalPercentBarChart.png differ
diff --git a/QChart/ScreenShot/LineChart.png b/QtChart/ScreenShot/LineChart.png
similarity index 100%
rename from QChart/ScreenShot/LineChart.png
rename to QtChart/ScreenShot/LineChart.png
diff --git a/QChart/ScreenShot/LineStack.gif b/QtChart/ScreenShot/LineStack.gif
similarity index 100%
rename from QChart/ScreenShot/LineStack.gif
rename to QtChart/ScreenShot/LineStack.gif
diff --git a/QtChart/ScreenShot/PercentBarChart.png b/QtChart/ScreenShot/PercentBarChart.png
new file mode 100644
index 0000000000000000000000000000000000000000..d6cf3318869def0cb34e49cbc7d8cfbe3b9c1dc7
Binary files /dev/null and b/QtChart/ScreenShot/PercentBarChart.png differ
diff --git a/QtChart/ScreenShot/PieChart.png b/QtChart/ScreenShot/PieChart.png
new file mode 100644
index 0000000000000000000000000000000000000000..4fc450a7998fe60a74bd93bc8d096eb3aeee68dd
Binary files /dev/null and b/QtChart/ScreenShot/PieChart.png differ
diff --git a/QtChart/ScreenShot/ScatterChart.png b/QtChart/ScreenShot/ScatterChart.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa0f03ea08c88fe1c1aeb4eb68438ff3fce161d3
Binary files /dev/null and b/QtChart/ScreenShot/ScatterChart.png differ
diff --git a/QtChart/ScreenShot/SplineChart.png b/QtChart/ScreenShot/SplineChart.png
new file mode 100644
index 0000000000000000000000000000000000000000..170eb927ea9a448bd6751db83202e2d62ef4ef31
Binary files /dev/null and b/QtChart/ScreenShot/SplineChart.png differ
diff --git a/QChart/ScreenShot/ToolTip.gif b/QtChart/ScreenShot/ToolTip.gif
similarity index 100%
rename from QChart/ScreenShot/ToolTip.gif
rename to QtChart/ScreenShot/ToolTip.gif
diff --git a/QChart/ScreenShot/ToolTip2.gif b/QtChart/ScreenShot/ToolTip2.gif
similarity index 100%
rename from QChart/ScreenShot/ToolTip2.gif
rename to QtChart/ScreenShot/ToolTip2.gif
diff --git a/QtChart/SplineChart.py b/QtChart/SplineChart.py
new file mode 100644
index 0000000000000000000000000000000000000000..9fe2eaf8eb7fe081102af33c4a66359bb27cc6cd
--- /dev/null
+++ b/QtChart/SplineChart.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/2
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: SplineChart
+@description: 样条图表
+"""
+
+try:
+ from PyQt5.QtChart import QChartView, QChart, QSplineSeries
+ from PyQt5.QtCore import QPointF
+ from PyQt5.QtGui import QPainter
+ from PyQt5.QtWidgets import QApplication
+except ImportError:
+ from PySide2.QtCore import QPointF
+ from PySide2.QtGui import QPainter
+ from PySide2.QtWidgets import QApplication
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QSplineSeries = QtCharts.QSplineSeries
+
+
+class Window(QChartView):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.resize(400, 300)
+ # 抗锯齿
+ self.setRenderHint(QPainter.Antialiasing)
+
+ # 图表
+ chart = QChart()
+ self.setChart(chart)
+ # 设置标题
+ chart.setTitle('Simple splinechart example')
+ # 添加Series
+ self.getSeries(chart)
+ # 创建默认xy轴
+ chart.createDefaultAxes()
+ chart.legend().setVisible(False)
+
+ def getSeries(self, chart):
+ # 第一组
+ series = QSplineSeries(chart)
+ series << QPointF(1, 5) << QPointF(3, 7) << QPointF(7, 6) << QPointF(9, 7) \
+ << QPointF(12, 6) << QPointF(16, 7) << QPointF(18, 5)
+ chart.addSeries(series)
+
+ # 第二组
+ series = QSplineSeries(chart)
+ series << QPointF(1, 3) << QPointF(3, 4) << QPointF(7, 3) << QPointF(8, 2) \
+ << QPointF(12, 3) << QPointF(16, 4) << QPointF(18, 3)
+ chart.addSeries(series)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QChart/ToolTip.py b/QtChart/ToolTip.py
similarity index 88%
rename from QChart/ToolTip.py
rename to QtChart/ToolTip.py
index dc34a596bea3ef6d6d0b7ae5bb9a17d4e1207876..397254b1575a29feb98fb30e64b165b0413bfefc 100644
--- a/QChart/ToolTip.py
+++ b/QtChart/ToolTip.py
@@ -1,27 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月23日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ToolTip
@description:
-'''
+"""
import sys
-from PyQt5.QtChart import QChartView, QChart, QLineSeries
-from PyQt5.QtCore import Qt, QRectF, QPoint, QPointF
-from PyQt5.QtGui import QPainter, QCursor
-from PyQt5.QtWidgets import QApplication, QGraphicsProxyWidget, QLabel, \
- QWidget, QHBoxLayout, QVBoxLayout, QToolTip, QGraphicsLineItem
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
-
-'''
+try:
+ from PyQt5.QtChart import QChartView, QChart, QLineSeries
+ from PyQt5.QtCore import Qt, QRectF, QPoint, QPointF
+ from PyQt5.QtGui import QPainter, QCursor
+ from PyQt5.QtWidgets import QApplication, QGraphicsProxyWidget, QLabel, \
+ QWidget, QHBoxLayout, QVBoxLayout, QToolTip, QGraphicsLineItem
+except ImportError:
+ from PySide2.QtCore import Qt, QRectF, QPoint, QPointF
+ from PySide2.QtGui import QPainter, QCursor
+ from PySide2.QtWidgets import QApplication, QGraphicsProxyWidget, QLabel, \
+ QWidget, QHBoxLayout, QVBoxLayout, QToolTip, QGraphicsLineItem
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QLineSeries = QtCharts.QLineSeries
+
+"""
class CircleWidget(QGraphicsProxyWidget):
def __init__(self, color, *args, **kwargs):
@@ -57,7 +64,7 @@ class GraphicsWidget(QGraphicsWidget):
self.setGeometry(pos.x(), pos.y(), self.size().width(),
self.size().height())
super(GraphicsWidget, self).show()
-'''
+"""
class ToolTipItem(QWidget):
@@ -161,7 +168,8 @@ class ChartView(QChartView):
# print(x, pos_x, index, index * self.step_x + self.min_x)
# 得到在坐标系中的所有series的类型和点
points = [(serie, serie.at(index))
- for serie in self._chart.series() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
+ for serie in self._chart.series() if
+ self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
if points:
# 永远在轴上的黑线条
self.lineItem.setLine(pos_x.x(), self.point_top.y(),
diff --git a/QChart/ToolTip2.py b/QtChart/ToolTip2.py
similarity index 88%
rename from QChart/ToolTip2.py
rename to QtChart/ToolTip2.py
index 349473edfabd7ec872dcc4360ee9e61400afcf64..3a8ca3df1123f3d31b45c42a7b5e6b287c968588 100644
--- a/QChart/ToolTip2.py
+++ b/QtChart/ToolTip2.py
@@ -1,27 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月23日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ToolTip2
@description:
-'''
+"""
import sys
-from PyQt5.QtChart import QChartView, QChart, QLineSeries
-from PyQt5.QtCore import Qt, QRectF, QPoint, QPointF
-from PyQt5.QtGui import QPainter, QCursor
-from PyQt5.QtWidgets import QApplication, QGraphicsProxyWidget, QLabel, \
- QWidget, QHBoxLayout, QVBoxLayout, QToolTip, QGraphicsLineItem
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
-
-'''
+try:
+ from PyQt5.QtChart import QChartView, QChart, QLineSeries
+ from PyQt5.QtCore import Qt, QRectF, QPoint, QPointF
+ from PyQt5.QtGui import QPainter, QCursor
+ from PyQt5.QtWidgets import QApplication, QGraphicsProxyWidget, QLabel, \
+ QWidget, QHBoxLayout, QVBoxLayout, QToolTip, QGraphicsLineItem
+except ImportError:
+ from PySide2.QtCore import Qt, QRectF, QPoint, QPointF
+ from PySide2.QtGui import QPainter, QCursor
+ from PySide2.QtWidgets import QApplication, QGraphicsProxyWidget, QLabel, \
+ QWidget, QHBoxLayout, QVBoxLayout, QToolTip, QGraphicsLineItem
+ from PySide2.QtCharts import QtCharts
+
+ QChartView = QtCharts.QChartView
+ QChart = QtCharts.QChart
+ QLineSeries = QtCharts.QLineSeries
+
+"""
class CircleWidget(QGraphicsProxyWidget):
def __init__(self, color, *args, **kwargs):
@@ -57,7 +64,7 @@ class GraphicsWidget(QGraphicsWidget):
self.setGeometry(pos.x(), pos.y(), self.size().width(),
self.size().height())
super(GraphicsWidget, self).show()
-'''
+"""
class ToolTipItem(QWidget):
@@ -159,7 +166,8 @@ class ChartView(QChartView):
# print(x, pos_x, index, index * self.step_x + self.min_x)
# 得到在坐标系中的所有series的类型和点
points = [(serie, serie.at(index))
- for serie in self._chart.series() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
+ for serie in self._chart.series() if
+ self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y]
if points:
# 跟随鼠标的黑线条
self.lineItem.setLine(event.pos().x(), self.point_top.y(),
diff --git a/QChart/requirements.txt b/QtChart/requirements.txt
similarity index 100%
rename from QChart/requirements.txt
rename to QtChart/requirements.txt
diff --git a/QtDataVisualization/BarsVisualization.py b/QtDataVisualization/BarsVisualization.py
new file mode 100644
index 0000000000000000000000000000000000000000..7fe583dd664f54fdc70dba0c2be87a8adbe7f0f3
--- /dev/null
+++ b/QtDataVisualization/BarsVisualization.py
@@ -0,0 +1,468 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/4
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: BarsVisualization
+@description: 柱状图
+"""
+
+#############################################################################
+##
+## Copyright (C) 2014 Riverbank Computing Limited.
+## Copyright (C) 2014 Digia Plc
+## All rights reserved.
+## For any questions to Digia, please use contact form at http://qt.digia.com
+##
+## This file is part of the QtDataVisualization module.
+##
+## Licensees holding valid Qt Enterprise licenses may use this file in
+## accordance with the Qt Enterprise License Agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and Digia.
+##
+## If you have questions regarding the use of this file, please use
+## contact form at http://qt.digia.com
+##
+#############################################################################
+
+
+from PyQt5.QtCore import pyqtSignal, QObject, QSize, Qt, QLocale
+from PyQt5.QtDataVisualization import (Q3DBars, Q3DCamera, Q3DTheme,
+ QAbstract3DGraph, QAbstract3DSeries, QBar3DSeries, QBarDataItem,
+ QCategory3DAxis, QValue3DAxis)
+from PyQt5.QtGui import QFont
+from PyQt5.QtWidgets import (QApplication, QCheckBox, QComboBox, QFontComboBox,
+ QHBoxLayout, QLabel, QPushButton, QSizePolicy, QSlider, QVBoxLayout,
+ QWidget)
+
+country = QLocale.system().country()
+if country in (QLocale.China, QLocale.HongKong, QLocale.Taiwan):
+ Tr = {
+ 'Average temperature': '平均温度',
+ 'Average temperatures in Oulu and Helsinki, Finland (2006-2013)': '芬兰奥卢和赫尔辛基的平均气温(2006-2013)',
+ 'Change label style': '更改label样式',
+ 'Smooth bars': '平滑bars',
+ 'Change camera preset': '更改相机预设',
+ 'Show background': '显示背景',
+ 'Show grid': '显示网格',
+ 'Show second series': '显示第二个数据列',
+ 'Low': '低质量',
+ 'Medium': '中等质量',
+ 'High': '高质量',
+ 'Low Soft': '柔化低质量',
+ 'Medium Soft': '柔化中等质量',
+ 'High Soft': '柔化高质量',
+ 'Rotate horizontally': '水平旋转',
+ 'Rotate vertically': '垂直旋转',
+ 'Show year': '显示年份',
+ 'Change bar style': '更改条形图样式',
+ 'Change selection mode': '更改选择模式',
+ 'Change theme': '更改主题',
+ 'Adjust shadow quality': '调整阴影质量',
+ 'Change font': '更改字体',
+ 'Adjust font size': '调整字体大小'
+ }
+else:
+ Tr = {}
+
+
+class GraphModifier(QObject):
+ # 奥卢 温度
+ tempOulu = (
+ (-6.7, -11.7, -9.7, 3.3, 9.2, 14.0, 16.3, 17.8, 10.2, 2.1, -2.6, -0.3),
+ (-6.8, -13.3, 0.2, 1.5, 7.9, 13.4, 16.1, 15.5, 8.2, 5.4, -2.6, -0.8),
+ (-4.2, -4.0, -4.6, 1.9, 7.3, 12.5, 15.0, 12.8, 7.6, 5.1, -0.9, -1.3),
+ (-7.8, -8.8, -4.2, 0.7, 9.3, 13.2, 15.8, 15.5, 11.2, 0.6, 0.7, -8.4),
+ (-14.4, -12.1, -7.0, 2.3, 11.0, 12.6, 18.8, 13.8, 9.4, 3.9, -5.6, -13.0),
+ (-9.0, -15.2, -3.8, 2.6, 8.3, 15.9, 18.6, 14.9, 11.1, 5.3, 1.8, -0.2),
+ (-8.7, -11.3, -2.3, 0.4, 7.5, 12.2, 16.4, 14.1, 9.2, 3.1, 0.3, -12.1),
+ (-7.9, -5.3, -9.1, 0.8, 11.6, 16.6, 15.9, 15.5, 11.2, 4.0, 0.1, -1.9)
+ )
+
+ # 赫尔辛基 温度
+ tempHelsinki = (
+ (-3.7, -7.8, -5.4, 3.4, 10.7, 15.4, 18.6, 18.7, 14.3, 8.5, 2.9, 4.1),
+ (-1.2, -7.5, 3.1, 5.5, 10.3, 15.9, 17.4, 17.9, 11.2, 7.3, 1.1, 0.5),
+ (-0.6, 1.2, 0.2, 6.3, 10.2, 13.8, 18.1, 15.1, 10.1, 9.4, 2.5, 0.4),
+ (-2.9, -3.5, -0.9, 4.7, 10.9, 14.0, 17.4, 16.8, 13.2, 4.1, 2.6, -2.3),
+ (-10.2, -8.0, -1.9, 6.6, 11.3, 14.5, 21.0, 18.8, 12.6, 6.1, -0.5, -7.3),
+ (-4.4, -9.1, -2.0, 5.5, 9.9, 15.6, 20.8, 17.8, 13.4, 8.9, 3.6, 1.5),
+ (-3.5, -3.2, -0.7, 4.0, 11.1, 13.4, 17.3, 15.8, 13.1, 6.4, 4.1, -5.1),
+ (-4.8, -1.8, -5.0, 2.9, 12.8, 17.2, 18.0, 17.1, 12.5, 7.5, 4.5, 2.3)
+ )
+
+ months = (
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+ )
+
+ years = ("2006", "2007", "2008", "2009", "2010", "2011", "2012", "2013")
+
+ shadowQualityChanged = pyqtSignal(int) # 阴影质量改变信号
+ backgroundEnabledChanged = pyqtSignal(bool) # 背景开启信号
+ gridEnabledChanged = pyqtSignal(bool) # 网格开启信号
+ fontChanged = pyqtSignal(QFont) # 字体改变信号
+ fontSizeChanged = pyqtSignal(int) # 字体大小改变信号
+
+ def __init__(self, bargraph):
+ super(GraphModifier, self).__init__()
+
+ self.m_graph = bargraph # Q3DBars 实例
+
+ self.m_xRotation = 0.0 # 水平旋转角度
+ self.m_yRotation = 0.0 # 垂直旋转角度
+ self.m_fontSize = 30
+ self.m_segments = 4
+ self.m_subSegments = 3
+ self.m_minval = -20.0
+ self.m_maxval = 20.0
+ self.m_temperatureAxis = QValue3DAxis() # 温度轴
+ self.m_yearAxis = QCategory3DAxis() # 年份轴
+ self.m_monthAxis = QCategory3DAxis() # 月份轴
+ self.m_primarySeries = QBar3DSeries() # 主序列
+ self.m_secondarySeries = QBar3DSeries() # 次序列
+ self.m_barMesh = QAbstract3DSeries.MeshBevelBar # 预定义的网格类型
+ self.m_smooth = False # 是否平滑
+
+ # 阴影以柔化的边缘高质量渲染
+ self.m_graph.setShadowQuality(QAbstract3DGraph.ShadowQualitySoftMedium)
+ # 当前主题Q3DTheme设置背景是否可见
+ self.m_graph.activeTheme().setBackgroundEnabled(False)
+ # 当前主题Q3DTheme设置字体
+ self.m_graph.activeTheme().setFont(
+ QFont('Times New Roman', self.m_fontSize))
+ # 当前主题Q3DTheme设置标签是使用彩色背景还是使用完全透明的背景绘制
+ self.m_graph.activeTheme().setLabelBackgroundEnabled(True)
+ # 是否按比例将比例尺设置为单个系列比例尺来缩放比例
+ self.m_graph.setMultiSeriesUniform(True)
+
+ self.m_temperatureAxis.setTitle(Tr.get("Average temperature", "Average temperature"))
+ # 轴上的段数。这表明绘制了多少标签。要绘制的网格线的数量使用公式计算:segments * subsegments + 1。预设默认值为5。该值不能低于1
+ self.m_temperatureAxis.setSegmentCount(self.m_segments)
+ # 轴上每个段内的子段数。
+ # 除每个线段外,还在每个子线段之间绘制网格线。预设默认值为1。该值不能低于1。
+ self.m_temperatureAxis.setSubSegmentCount(self.m_subSegments)
+ self.m_temperatureAxis.setRange(self.m_minval, self.m_maxval)
+ self.m_temperatureAxis.setLabelFormat(u"%.1f \N{degree sign}C")
+
+ self.m_yearAxis.setTitle("Year")
+ self.m_monthAxis.setTitle("Month")
+
+ self.m_graph.setValueAxis(self.m_temperatureAxis)
+ # 设置活动行的轴为年份
+ self.m_graph.setRowAxis(self.m_yearAxis)
+ # 设置活动列的轴为月份
+ self.m_graph.setColumnAxis(self.m_monthAxis)
+
+ self.m_primarySeries.setItemLabelFormat(
+ "Oulu - @colLabel @rowLabel: @valueLabel")
+ # 设置网格类型
+ self.m_primarySeries.setMesh(QAbstract3DSeries.MeshBevelBar)
+ self.m_primarySeries.setMeshSmooth(False)
+
+ self.m_secondarySeries.setItemLabelFormat(
+ "Helsinki - @colLabel @rowLabel: @valueLabel")
+ self.m_secondarySeries.setMesh(QAbstract3DSeries.MeshBevelBar)
+ self.m_secondarySeries.setMeshSmooth(False)
+ self.m_secondarySeries.setVisible(False)
+
+ self.m_graph.addSeries(self.m_primarySeries)
+ self.m_graph.addSeries(self.m_secondarySeries)
+
+ self.m_preset = Q3DCamera.CameraPresetFront
+ self.changePresetCamera()
+
+ self.resetTemperatureData()
+
+ def resetTemperatureData(self):
+ # 重置温度数据
+ dataSet = []
+ for row in self.tempOulu:
+ dataSet.append([QBarDataItem(v) for v in row])
+
+ self.m_primarySeries.dataProxy().resetArray(dataSet, self.years,
+ self.months)
+
+ dataSet = []
+ for row in self.tempHelsinki:
+ dataSet.append([QBarDataItem(v) for v in row])
+
+ self.m_secondarySeries.dataProxy().resetArray(dataSet, self.years,
+ self.months)
+
+ def changeRange(self, range):
+ if range >= len(self.years):
+ self.m_yearAxis.setRange(0, len(self.years) - 1)
+ else:
+ self.m_yearAxis.setRange(range, range)
+
+ def changeStyle(self, style):
+ comboBox = self.sender()
+ if isinstance(comboBox, QComboBox):
+ self.m_barMesh = QAbstract3DSeries.Mesh(comboBox.itemData(style))
+ self.m_primarySeries.setMesh(self.m_barMesh)
+ self.m_secondarySeries.setMesh(self.m_barMesh)
+
+ def changePresetCamera(self):
+ # 修改摄像机的预定义位置
+ self.m_graph.scene().activeCamera().setCameraPreset(self.m_preset)
+
+ preset = int(self.m_preset) + 1
+ if preset > Q3DCamera.CameraPresetDirectlyBelow:
+ self.m_preset = Q3DCamera.CameraPresetFrontLow
+ else:
+ self.m_preset = Q3DCamera.CameraPreset(preset)
+
+ def changeTheme(self, theme):
+ # 切换主题
+ currentTheme = self.m_graph.activeTheme()
+ currentTheme.setType(Q3DTheme.Theme(theme))
+ self.backgroundEnabledChanged.emit(currentTheme.isBackgroundEnabled())
+ self.gridEnabledChanged.emit(currentTheme.isGridEnabled())
+ self.fontChanged.emit(currentTheme.font())
+ self.fontSizeChanged.emit(currentTheme.font().pointSize())
+
+ def changeLabelBackground(self):
+ self.m_graph.activeTheme().setLabelBackgroundEnabled(
+ not self.m_graph.activeTheme().isLabelBackgroundEnabled())
+
+ def changeSelectionMode(self, selectionMode):
+ comboBox = self.sender()
+ if isinstance(comboBox, QComboBox):
+ flags = comboBox.itemData(selectionMode)
+ self.m_graph.setSelectionMode(
+ QAbstract3DGraph.SelectionFlags(flags))
+
+ def changeFont(self, font):
+ self.m_graph.activeTheme().setFont(font)
+
+ def changeFontSize(self, fontsize):
+ self.m_fontSize = fontsize
+ font = self.m_graph.activeTheme().font()
+ font.setPointSize(self.m_fontSize)
+ self.m_graph.activeTheme().setFont(font)
+
+ def shadowQualityUpdatedByVisual(self, sq):
+ self.shadowQualityChanged.emit(int(sq))
+
+ def changeShadowQuality(self, quality):
+ sq = QAbstract3DGraph.ShadowQuality(quality)
+ self.m_graph.setShadowQuality(sq)
+ self.shadowQualityChanged.emit(quality)
+
+ def rotateX(self, rotation):
+ self.m_xRotation = rotation
+ self.m_graph.scene().activeCamera().setCameraPosition(
+ self.m_xRotation, self.m_yRotation)
+
+ def rotateY(self, rotation):
+ self.m_yRotation = rotation
+ self.m_graph.scene().activeCamera().setCameraPosition(
+ self.m_xRotation, self.m_yRotation)
+
+ def setBackgroundEnabled(self, enabled):
+ self.m_graph.activeTheme().setBackgroundEnabled(enabled)
+
+ def setGridEnabled(self, enabled):
+ self.m_graph.activeTheme().setGridEnabled(enabled)
+
+ def setSmoothBars(self, smooth):
+ self.m_smooth = bool(smooth)
+ self.m_primarySeries.setMeshSmooth(self.m_smooth)
+ self.m_secondarySeries.setMeshSmooth(self.m_smooth)
+
+ def setSeriesVisibility(self, enabled):
+ self.m_secondarySeries.setVisible(enabled)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ widgetgraph = Q3DBars()
+ container = QWidget.createWindowContainer(widgetgraph)
+
+ screenSize = widgetgraph.screen().size()
+ container.setMinimumSize(
+ QSize(screenSize.width() / 2, screenSize.height() / 1.5))
+ container.setMaximumSize(screenSize)
+ container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ container.setFocusPolicy(Qt.StrongFocus)
+
+ widget = QWidget()
+ hLayout = QHBoxLayout(widget)
+ vLayout = QVBoxLayout()
+ hLayout.addWidget(container, 1)
+ hLayout.addLayout(vLayout)
+
+ widget.setWindowTitle(Tr.get(
+ "Average temperatures in Oulu and Helsinki, Finland (2006-2013)",
+ "Average temperatures in Oulu and Helsinki, Finland (2006-2013)"))
+
+ themeList = QComboBox()
+ themeList.addItem("Qt")
+ themeList.addItem("Primary Colors")
+ themeList.addItem("Digia")
+ themeList.addItem("Stone Moss")
+ themeList.addItem("Army Blue")
+ themeList.addItem("Retro")
+ themeList.addItem("Ebony")
+ themeList.addItem("Isabelle")
+ themeList.setCurrentIndex(0)
+
+ labelButton = QPushButton(Tr.get("Change label style", "Change label style"))
+
+ smoothCheckBox = QCheckBox(Tr.get("Smooth bars", "Smooth bars"))
+
+ barStyleList = QComboBox()
+ barStyleList.addItem("Bar", QAbstract3DSeries.MeshBar)
+ barStyleList.addItem("Pyramid", QAbstract3DSeries.MeshPyramid)
+ barStyleList.addItem("Cone", QAbstract3DSeries.MeshCone)
+ barStyleList.addItem("Cylinder", QAbstract3DSeries.MeshCylinder)
+ barStyleList.addItem("Bevel bar", QAbstract3DSeries.MeshBevelBar)
+ barStyleList.addItem("Sphere", QAbstract3DSeries.MeshSphere)
+ barStyleList.setCurrentIndex(4)
+
+ cameraButton = QPushButton(Tr.get("Change camera preset", "Change camera preset"))
+
+ selectionModeList = QComboBox()
+ selectionModeList.addItem("None", QAbstract3DGraph.SelectionNone)
+ selectionModeList.addItem("Bar", QAbstract3DGraph.SelectionItem)
+ selectionModeList.addItem("Row", QAbstract3DGraph.SelectionRow)
+ selectionModeList.addItem("Bar and Row",
+ QAbstract3DGraph.SelectionItemAndRow)
+ selectionModeList.addItem("Column", QAbstract3DGraph.SelectionColumn)
+ selectionModeList.addItem("Bar and Column",
+ QAbstract3DGraph.SelectionItemAndColumn)
+ selectionModeList.addItem("Row and Column",
+ QAbstract3DGraph.SelectionRowAndColumn)
+ selectionModeList.addItem("Bar, Row and Column",
+ QAbstract3DGraph.SelectionItemRowAndColumn)
+ selectionModeList.addItem("Slice into Row",
+ QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionRow)
+ selectionModeList.addItem("Slice into Row and Item",
+ QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndRow)
+ selectionModeList.addItem("Slice into Column",
+ QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionColumn)
+ selectionModeList.addItem("Slice into Column and Item",
+ QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndColumn)
+ selectionModeList.addItem("Multi: Bar, Row, Col",
+ QAbstract3DGraph.SelectionItemRowAndColumn | QAbstract3DGraph.SelectionMultiSeries)
+ selectionModeList.addItem("Multi, Slice: Row, Item",
+ QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndRow | QAbstract3DGraph.SelectionMultiSeries)
+ selectionModeList.addItem("Multi, Slice: Col, Item",
+ QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndColumn | QAbstract3DGraph.SelectionMultiSeries)
+ selectionModeList.setCurrentIndex(1)
+
+ backgroundCheckBox = QCheckBox(Tr.get("Show background", "Show background"))
+
+ gridCheckBox = QCheckBox(Tr.get("Show grid", "Show grid"))
+ gridCheckBox.setChecked(True)
+
+ seriesCheckBox = QCheckBox(Tr.get("Show second series", "Show second series"))
+
+ rotationSliderX = QSlider(Qt.Horizontal)
+ rotationSliderX.setTickInterval(30)
+ rotationSliderX.setTickPosition(QSlider.TicksBelow)
+ rotationSliderX.setMinimum(-180)
+ rotationSliderX.setValue(0)
+ rotationSliderX.setMaximum(180)
+ rotationSliderY = QSlider(Qt.Horizontal)
+ rotationSliderY.setTickInterval(15)
+ rotationSliderY.setTickPosition(QSlider.TicksAbove)
+ rotationSliderY.setMinimum(-90)
+ rotationSliderY.setValue(0)
+ rotationSliderY.setMaximum(90)
+
+ fontSizeSlider = QSlider(Qt.Horizontal)
+ fontSizeSlider.setTickInterval(10)
+ fontSizeSlider.setTickPosition(QSlider.TicksBelow)
+ fontSizeSlider.setMinimum(1)
+ fontSizeSlider.setValue(30)
+ fontSizeSlider.setMaximum(100)
+
+ fontList = QFontComboBox()
+ fontList.setCurrentFont(QFont('Times New Roman'))
+
+ shadowQuality = QComboBox()
+ shadowQuality.addItem("None")
+ shadowQuality.addItem(Tr.get("Low", "Low"))
+ shadowQuality.addItem(Tr.get("Medium", "Medium"))
+ shadowQuality.addItem(Tr.get("High", "High"))
+ shadowQuality.addItem(Tr.get("Low Soft", "Low Soft"))
+ shadowQuality.addItem(Tr.get("Medium Soft", "Medium Soft"))
+ shadowQuality.addItem(Tr.get("High Soft", "High Soft"))
+ shadowQuality.setCurrentIndex(5)
+
+ rangeList = QComboBox()
+ rangeList.addItems(GraphModifier.years)
+ rangeList.addItem("All")
+ rangeList.setCurrentIndex(len(GraphModifier.years))
+
+ vLayout.addWidget(QLabel(Tr.get("Rotate horizontally", "Rotate horizontally")))
+ vLayout.addWidget(rotationSliderX, 0, Qt.AlignTop)
+ vLayout.addWidget(QLabel(Tr.get("Rotate vertically", "Rotate vertically")))
+ vLayout.addWidget(rotationSliderY, 0, Qt.AlignTop)
+ vLayout.addWidget(labelButton, 0, Qt.AlignTop)
+ vLayout.addWidget(cameraButton, 0, Qt.AlignTop)
+ vLayout.addWidget(backgroundCheckBox)
+ vLayout.addWidget(gridCheckBox)
+ vLayout.addWidget(smoothCheckBox)
+ vLayout.addWidget(seriesCheckBox)
+ vLayout.addWidget(QLabel(Tr.get("Show year", "Show year")))
+ vLayout.addWidget(rangeList)
+ vLayout.addWidget(QLabel(Tr.get("Change bar style", "Change bar style")))
+ vLayout.addWidget(barStyleList)
+ vLayout.addWidget(QLabel(Tr.get("Change selection mode", "Change selection mode")))
+ vLayout.addWidget(selectionModeList)
+ vLayout.addWidget(QLabel(Tr.get("Change theme", "Change theme")))
+ vLayout.addWidget(themeList)
+ vLayout.addWidget(QLabel(Tr.get("Adjust shadow quality", "Adjust shadow quality")))
+ vLayout.addWidget(shadowQuality)
+ vLayout.addWidget(QLabel(Tr.get("Change font", "Change font")))
+ vLayout.addWidget(fontList)
+ vLayout.addWidget(QLabel(Tr.get("Adjust font size", "Adjust font size")))
+ vLayout.addWidget(fontSizeSlider, 1, Qt.AlignTop)
+
+ modifier = GraphModifier(widgetgraph)
+
+ rotationSliderX.valueChanged.connect(modifier.rotateX)
+ rotationSliderY.valueChanged.connect(modifier.rotateY)
+
+ labelButton.clicked.connect(modifier.changeLabelBackground)
+ cameraButton.clicked.connect(modifier.changePresetCamera)
+
+ backgroundCheckBox.stateChanged.connect(modifier.setBackgroundEnabled)
+ gridCheckBox.stateChanged.connect(modifier.setGridEnabled)
+ smoothCheckBox.stateChanged.connect(modifier.setSmoothBars)
+ seriesCheckBox.stateChanged.connect(modifier.setSeriesVisibility)
+
+ modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
+ modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
+
+ rangeList.currentIndexChanged.connect(modifier.changeRange)
+
+ barStyleList.currentIndexChanged.connect(modifier.changeStyle)
+
+ selectionModeList.currentIndexChanged.connect(modifier.changeSelectionMode)
+
+ themeList.currentIndexChanged.connect(modifier.changeTheme)
+
+ shadowQuality.currentIndexChanged.connect(modifier.changeShadowQuality)
+
+ modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
+ widgetgraph.shadowQualityChanged.connect(
+ modifier.shadowQualityUpdatedByVisual)
+
+ fontSizeSlider.valueChanged.connect(modifier.changeFontSize)
+ fontList.currentFontChanged.connect(modifier.changeFont)
+
+ modifier.fontSizeChanged.connect(fontSizeSlider.setValue)
+ modifier.fontChanged.connect(fontList.setCurrentFont)
+
+ widget.show()
+ sys.exit(app.exec_())
diff --git a/QtDataVisualization/Data/mesh/largesphere.obj b/QtDataVisualization/Data/mesh/largesphere.obj
new file mode 100644
index 0000000000000000000000000000000000000000..631014135b434730b00fef7ef8cbb9752a669b4a
--- /dev/null
+++ b/QtDataVisualization/Data/mesh/largesphere.obj
@@ -0,0 +1,1938 @@
+# Blender v2.66 (sub 0) OBJ File: ''
+# www.blender.org
+o Sphere_Sphere.001
+v -1.251476 7.901507 0.000000
+v -2.472136 7.608452 0.000000
+v -3.631924 7.128052 0.000000
+v -4.702282 6.472136 0.000000
+v -5.656854 5.656854 0.000000
+v -6.472136 4.702282 0.000000
+v -7.128052 3.631924 0.000000
+v -7.608452 2.472136 0.000000
+v -7.901507 1.251475 0.000000
+v -8.000000 -0.000001 0.000000
+v -7.901506 -1.251477 0.000000
+v -7.608451 -2.472138 0.000000
+v -7.128051 -3.631927 0.000000
+v -6.472134 -4.702285 0.000000
+v -5.656851 -5.656857 0.000000
+v -4.702279 -6.472138 0.000000
+v -3.631920 -7.128055 0.000000
+v -2.472131 -7.608454 0.000000
+v -1.251470 -7.901508 0.000000
+v -1.190224 7.901506 -0.386729
+v -2.351141 7.608452 -0.763934
+v -3.454165 7.128052 -1.122328
+v -4.472136 6.472136 -1.453087
+v -5.379988 5.656855 -1.748066
+v -6.155366 4.702282 -2.000002
+v -6.779180 3.631924 -2.202692
+v -7.236068 2.472136 -2.351143
+v -7.514779 1.251475 -2.441702
+v -7.608452 -0.000001 -2.472138
+v -7.514779 -1.251477 -2.441702
+v -7.236067 -2.472138 -2.351143
+v -6.779179 -3.631927 -2.202691
+v -6.155365 -4.702285 -2.000002
+v -5.379985 -5.656857 -1.748065
+v -4.472132 -6.472138 -1.453086
+v -3.454160 -7.128055 -1.122327
+v -2.351136 -7.608454 -0.763933
+v -1.190218 -7.901508 -0.386727
+v 0.000006 -8.000000 0.000000
+v -1.012464 7.901506 -0.735603
+v -1.999999 7.608452 -1.453089
+v -2.938287 7.128052 -2.134795
+v -3.804224 6.472136 -2.763936
+v -4.576490 5.656855 -3.325019
+v -5.236066 4.702282 -3.804230
+v -5.766714 3.631924 -4.189768
+v -6.155365 2.472136 -4.472140
+v -6.392451 1.251475 -4.644393
+v -6.472135 -0.000001 -4.702287
+v -6.392451 -1.251477 -4.644393
+v -6.155365 -2.472138 -4.472139
+v -5.766713 -3.631927 -4.189767
+v -5.236064 -4.702285 -3.804229
+v -4.576487 -5.656857 -3.325018
+v -3.804221 -6.472138 -2.763934
+v -2.938283 -7.128055 -2.134793
+v -1.999994 -7.608454 -1.453086
+v -1.012459 -7.901508 -0.735599
+v -0.735596 7.901506 -1.012470
+v -1.453082 7.608452 -2.000005
+v -2.134788 7.128052 -2.938294
+v -2.763928 6.472136 -3.804231
+v -3.325012 5.656855 -4.576497
+v -3.804222 4.702282 -5.236073
+v -4.189760 3.631924 -5.766721
+v -4.472132 2.472136 -6.155373
+v -4.644385 1.251475 -6.392459
+v -4.702279 -0.000001 -6.472142
+v -4.644385 -1.251477 -6.392458
+v -4.472132 -2.472138 -6.155372
+v -4.189760 -3.631927 -5.766720
+v -3.804221 -4.702285 -5.236072
+v -3.325010 -5.656857 -4.576494
+v -2.763927 -6.472138 -3.804229
+v -2.134786 -7.128055 -2.938290
+v -1.453079 -7.608454 -2.000001
+v -0.735593 -7.901508 -1.012466
+v -0.386723 7.901506 -1.190230
+v -0.763927 7.608452 -2.351147
+v -1.122321 7.128052 -3.454171
+v -1.453079 6.472136 -4.472141
+v -1.748058 5.656855 -5.379994
+v -1.999994 4.702282 -6.155373
+v -2.202683 3.631924 -6.779186
+v -2.351135 2.472136 -7.236074
+v -2.441693 1.251475 -7.514785
+v -2.472130 -0.000001 -7.608459
+v -2.441693 -1.251477 -7.514784
+v -2.351135 -2.472138 -7.236073
+v -2.202683 -3.631927 -6.779185
+v -1.999993 -4.702285 -6.155371
+v -1.748057 -5.656857 -5.379991
+v -1.453079 -6.472138 -4.472138
+v -1.122320 -7.128055 -3.454167
+v -0.763926 -7.608454 -2.351142
+v -0.386721 -7.901508 -1.190225
+v 0.000007 7.901506 -1.251482
+v 0.000007 7.608452 -2.472142
+v 0.000007 7.128052 -3.631930
+v 0.000008 6.472136 -4.702287
+v 0.000008 5.656855 -5.656860
+v 0.000008 4.702282 -6.472141
+v 0.000008 3.631924 -7.128057
+v 0.000008 2.472136 -7.608458
+v 0.000009 1.251475 -7.901512
+v 0.000009 -0.000001 -8.000006
+v 0.000009 -1.251477 -7.901511
+v 0.000008 -2.472138 -7.608456
+v 0.000008 -3.631927 -7.128057
+v 0.000008 -4.702285 -6.472139
+v 0.000008 -5.656857 -5.656857
+v 0.000007 -6.472138 -4.702284
+v 0.000007 -7.128055 -3.631926
+v 0.000007 -7.608454 -2.472137
+v 0.000007 -7.901508 -1.251476
+v 0.386736 7.901506 -1.190230
+v 0.763941 7.608452 -2.351147
+v 1.122336 7.128052 -3.454170
+v 1.453094 6.472136 -4.472140
+v 1.748074 5.656855 -5.379993
+v 2.000010 4.702282 -6.155371
+v 2.202699 3.631924 -6.779185
+v 2.351151 2.472136 -7.236073
+v 2.441710 1.251475 -7.514783
+v 2.472147 -0.000001 -7.608457
+v 2.441710 -1.251477 -7.514783
+v 2.351151 -2.472138 -7.236071
+v 2.202699 -3.631927 -6.779184
+v 2.000009 -4.702285 -6.155369
+v 1.748073 -5.656857 -5.379990
+v 1.453093 -6.472138 -4.472137
+v 1.122334 -7.128055 -3.454166
+v 0.763939 -7.608454 -2.351141
+v 0.386734 -7.901508 -1.190225
+v 0.735610 7.901506 -1.012470
+v 1.453096 7.608452 -2.000005
+v 2.134803 7.128052 -2.938292
+v 2.763942 6.472136 -3.804229
+v 3.325027 5.656855 -4.576495
+v 3.804237 4.702282 -5.236070
+v 4.189775 3.631924 -5.766718
+v 4.472148 2.472136 -6.155370
+v 4.644401 1.251475 -6.392455
+v 4.702294 -0.000001 -6.472139
+v 4.644400 -1.251477 -6.392455
+v 4.472147 -2.472138 -6.155369
+v 4.189774 -3.631927 -5.766717
+v 3.804236 -4.702285 -5.236069
+v 3.325025 -5.656857 -4.576492
+v 2.763941 -6.472138 -3.804227
+v 2.134800 -7.128055 -2.938289
+v 1.453092 -7.608454 -2.000000
+v 0.735606 -7.901508 -1.012465
+v 1.012477 7.901506 -0.735602
+v 2.000012 7.608452 -1.453088
+v 2.938301 7.128052 -2.134794
+v 3.804237 6.472136 -2.763933
+v 4.576503 5.656855 -3.325017
+v 5.236080 4.702282 -3.804226
+v 5.766727 3.631924 -4.189765
+v 6.155380 2.472136 -4.472136
+v 6.392465 1.251475 -4.644389
+v 6.472148 -0.000001 -4.702283
+v 6.392465 -1.251477 -4.644389
+v 6.155378 -2.472138 -4.472136
+v 5.766726 -3.631927 -4.189764
+v 5.236078 -4.702285 -3.804225
+v 4.576500 -5.656857 -3.325016
+v 3.804235 -6.472138 -2.763932
+v 2.938297 -7.128055 -2.134791
+v 2.000007 -7.608454 -1.453085
+v 1.012472 -7.901508 -0.735599
+v 1.190237 7.901506 -0.386729
+v 2.351154 7.608452 -0.763933
+v 3.454178 7.128052 -1.122326
+v 4.472147 6.472136 -1.453085
+v 5.380000 5.656855 -1.748063
+v 6.155379 4.702282 -1.999998
+v 6.779192 3.631924 -2.202688
+v 7.236081 2.472136 -2.351139
+v 7.514791 1.251475 -2.441698
+v 7.608464 -0.000001 -2.472134
+v 7.514791 -1.251477 -2.441698
+v 7.236079 -2.472138 -2.351139
+v 6.779191 -3.631927 -2.202688
+v 6.155377 -4.702285 -1.999998
+v 5.379997 -5.656857 -1.748063
+v 4.472144 -6.472138 -1.453084
+v 3.454173 -7.128055 -1.122325
+v 2.351148 -7.608454 -0.763931
+v 1.190231 -7.901508 -0.386727
+v 1.251488 7.901506 0.000001
+v 2.472148 7.608452 0.000001
+v 3.631936 7.128052 0.000002
+v 4.702293 6.472136 0.000002
+v 5.656866 5.656855 0.000003
+v 6.472147 4.702282 0.000004
+v 7.128063 3.631924 0.000003
+v 7.608464 2.472136 0.000004
+v 7.901517 1.251475 0.000004
+v 8.000011 -0.000001 0.000004
+v 7.901517 -1.251477 0.000004
+v 7.608462 -2.472138 0.000004
+v 7.128062 -3.631927 0.000003
+v 6.472145 -4.702285 0.000004
+v 5.656862 -5.656857 0.000002
+v 4.702290 -6.472138 0.000002
+v 3.631932 -7.128055 0.000002
+v 2.472143 -7.608454 0.000001
+v 1.251483 -7.901508 0.000001
+v 1.190236 7.901506 0.386730
+v 2.351153 7.608452 0.763935
+v 3.454176 7.128052 1.122330
+v 4.472146 6.472136 1.453089
+v 5.379998 5.656855 1.748069
+v 6.155376 4.702282 2.000005
+v 6.779190 3.631924 2.202694
+v 7.236078 2.472136 2.351147
+v 7.514788 1.251475 2.441705
+v 7.608462 -0.000001 2.472141
+v 7.514788 -1.251477 2.441705
+v 7.236076 -2.472138 2.351146
+v 6.779189 -3.631927 2.202693
+v 6.155375 -4.702285 2.000004
+v 5.379995 -5.656857 1.748067
+v 4.472143 -6.472138 1.453088
+v 3.454172 -7.128055 1.122328
+v 2.351147 -7.608454 0.763933
+v 1.190231 -7.901508 0.386728
+v 1.012476 7.901506 0.735603
+v 2.000010 7.608452 1.453090
+v 2.938298 7.128052 2.134796
+v 3.804235 6.472136 2.763937
+v 4.576499 5.656855 3.325021
+v 5.236075 4.702282 3.804232
+v 5.766723 3.631924 4.189770
+v 6.155375 2.472136 4.472143
+v 6.392460 1.251475 4.644395
+v 6.472144 -0.000001 4.702289
+v 6.392460 -1.251477 4.644395
+v 6.155374 -2.472138 4.472141
+v 5.766722 -3.631927 4.189769
+v 5.236074 -4.702285 3.804231
+v 4.576497 -5.656857 3.325018
+v 3.804232 -6.472138 2.763935
+v 2.938295 -7.128055 2.134794
+v 2.000006 -7.608454 1.453086
+v 1.012471 -7.901508 0.735600
+v 0.735608 7.901506 1.012471
+v 1.453094 7.608452 2.000006
+v 2.134799 7.128052 2.938294
+v 2.763939 6.472136 3.804232
+v 3.325022 5.656855 4.576497
+v 3.804231 4.702282 5.236074
+v 4.189770 3.631924 5.766721
+v 4.472141 2.472136 6.155374
+v 4.644393 1.251475 6.392459
+v 4.702287 -0.000001 6.472143
+v 4.644393 -1.251477 6.392459
+v 4.472141 -2.472138 6.155372
+v 4.189768 -3.631927 5.766720
+v 3.804231 -4.702285 5.236073
+v 3.325021 -5.656857 4.576494
+v 2.763937 -6.472138 3.804228
+v 2.134797 -7.128055 2.938291
+v 1.453090 -7.608454 2.000001
+v 0.735605 -7.901508 1.012466
+v 0.386735 7.901506 1.190230
+v 0.763939 7.608452 2.351147
+v 1.122332 7.128052 3.454171
+v 1.453090 6.472136 4.472141
+v 1.748069 5.656855 5.379993
+v 2.000002 4.702282 6.155373
+v 2.202693 3.631924 6.779186
+v 2.351144 2.472136 7.236073
+v 2.441703 1.251475 7.514784
+v 2.472139 -0.000001 7.608459
+v 2.441703 -1.251477 7.514784
+v 2.351144 -2.472138 7.236072
+v 2.202692 -3.631927 6.779184
+v 2.000003 -4.702285 6.155371
+v 1.748068 -5.656857 5.379990
+v 1.453089 -6.472138 4.472137
+v 1.122331 -7.128055 3.454167
+v 0.763937 -7.608454 2.351141
+v 0.386733 -7.901508 1.190225
+v 0.000005 7.901506 1.251482
+v 0.000005 7.608452 2.472142
+v 0.000004 7.128052 3.631929
+v 0.000003 6.472136 4.702286
+v 0.000003 5.656855 5.656859
+v 0.000001 4.702282 6.472140
+v 0.000002 3.631924 7.128057
+v 0.000001 2.472136 7.608456
+v 0.000001 1.251475 7.901510
+v 0.000000 -0.000001 8.000005
+v 0.000001 -1.251477 7.901510
+v 0.000002 -2.472138 7.608455
+v 0.000001 -3.631927 7.128055
+v 0.000002 -4.702285 6.472138
+v 0.000003 -5.656857 5.656855
+v 0.000004 -6.472138 4.702283
+v 0.000004 -7.128055 3.631925
+v 0.000005 -7.608454 2.472136
+v 0.000006 -7.901508 1.251476
+v -0.386724 7.901506 1.190230
+v -0.763929 7.608452 2.351146
+v -1.122324 7.128052 3.454169
+v -1.453083 6.472136 4.472139
+v -1.748063 5.656855 5.379992
+v -2.000000 4.702282 6.155369
+v -2.202688 3.631924 6.779183
+v -2.351142 2.472136 7.236070
+v -2.441700 1.251475 7.514781
+v -2.472137 -0.000001 7.608455
+v -2.441700 -1.251477 7.514781
+v -2.351140 -2.472138 7.236069
+v -2.202689 -3.631927 6.779181
+v -1.999999 -4.702285 6.155367
+v -1.748061 -5.656857 5.379988
+v -1.453082 -6.472138 4.472136
+v -1.122323 -7.128055 3.454165
+v -0.763927 -7.608454 2.351140
+v -0.386722 -7.901508 1.190224
+v -0.735597 7.901506 1.012470
+v -1.453084 7.608452 2.000003
+v -2.134790 7.128052 2.938291
+v -2.763931 6.472136 3.804227
+v -3.325015 5.656855 4.576493
+v -3.804227 4.702282 5.236067
+v -4.189764 3.631924 5.766716
+v -4.472137 2.472136 6.155366
+v -4.644390 1.251475 6.392452
+v -4.702284 -0.000001 6.472136
+v -4.644390 -1.251477 6.392452
+v -4.472136 -2.472138 6.155365
+v -4.189764 -3.631927 5.766714
+v -3.804225 -4.702285 5.236066
+v -3.325012 -5.656857 4.576489
+v -2.763929 -6.472138 3.804224
+v -2.134788 -7.128055 2.938287
+v -1.453080 -7.608454 1.999999
+v -0.735594 -7.901508 1.012465
+v 0.000001 8.000000 0.000004
+v -1.012464 7.901506 0.735602
+v -1.999999 7.608452 1.453087
+v -2.938287 7.128052 2.134792
+v -3.804225 6.472136 2.763931
+v -4.576491 5.656855 3.325015
+v -5.236069 4.702282 3.804223
+v -5.766715 3.631924 4.189762
+v -6.155367 2.472136 4.472132
+v -6.392453 1.251475 4.644385
+v -6.472137 -0.000001 4.702279
+v -6.392453 -1.251477 4.644385
+v -6.155365 -2.472138 4.472132
+v -5.766714 -3.631927 4.189760
+v -5.236066 -4.702285 3.804223
+v -4.576488 -5.656857 3.325012
+v -3.804222 -6.472138 2.763929
+v -2.938284 -7.128055 2.134789
+v -1.999995 -7.608454 1.453083
+v -1.012460 -7.901508 0.735598
+v -1.190224 7.901506 0.386728
+v -2.351140 7.608452 0.763932
+v -3.454164 7.128052 1.122325
+v -4.472135 6.472136 1.453082
+v -5.379988 5.656855 1.748062
+v -6.155366 4.702282 1.999995
+v -6.779179 3.631924 2.202685
+v -7.236067 2.472136 2.351135
+v -7.514778 1.251475 2.441694
+v -7.608452 -0.000001 2.472130
+v -7.514778 -1.251477 2.441694
+v -7.236065 -2.472138 2.351135
+v -6.779178 -3.631927 2.202684
+v -6.155364 -4.702285 1.999995
+v -5.379983 -5.656857 1.748060
+v -4.472131 -6.472138 1.453082
+v -3.454160 -7.128055 1.122324
+v -2.351135 -7.608454 0.763930
+v -1.190218 -7.901508 0.386726
+vt 0.035483 0.289114
+vt 0.042491 0.337738
+vt 0.007397 0.296413
+vt 0.063422 0.533643
+vt 0.068439 0.582662
+vt 0.015908 0.545902
+vt 0.097669 0.776914
+vt 0.111929 0.824070
+vt 0.027296 0.795217
+vt 0.391159 0.929226
+vt 0.461397 0.895775
+vt 0.452242 0.950225
+vt 0.026572 0.240871
+vt 0.004734 0.246573
+vt 0.058567 0.484592
+vt 0.014398 0.495993
+vt 0.087726 0.728878
+vt 0.023823 0.745426
+vt 0.270350 0.936037
+vt 0.231553 0.980467
+vt 0.014254 0.193317
+vt 0.001048 0.196795
+vt 0.053639 0.435556
+vt 0.012882 0.446084
+vt 0.080129 0.680372
+vt 0.021272 0.695580
+vt 0.178869 0.909944
+vt 0.068145 0.942733
+vt 0.048386 0.386583
+vt 0.011281 0.396180
+vt 0.073887 0.631595
+vt 0.019234 0.645702
+vt 0.135028 0.869370
+vt 0.042415 0.894305
+vt 0.009500 0.346287
+vt 0.017489 0.595807
+vt 0.032618 0.844897
+vt 0.484179 0.902186
+vt 0.550590 0.948315
+vt 1.004734 0.246573
+vt 1.007397 0.296413
+vt 0.982704 0.246140
+vt 1.014398 0.495993
+vt 1.015908 0.545902
+vt 0.969883 0.495119
+vt 1.023823 0.745426
+vt 1.027296 0.795217
+vt 0.957447 0.744128
+vt 1.231553 0.980467
+vt 0.750122 0.974207
+vt 1.001048 0.196795
+vt 0.987598 0.196530
+vt 1.012882 0.446084
+vt 0.971892 0.445278
+vt 1.021272 0.695580
+vt 0.960802 0.694395
+vt 1.068145 0.942733
+vt 0.901563 0.939596
+vt 1.011281 0.396180
+vt 0.974014 0.395447
+vt 1.019234 0.645702
+vt 0.963490 0.644610
+vt 1.042415 0.894305
+vt 0.933349 0.892198
+vt 1.009500 0.346287
+vt 0.976377 0.345635
+vt 1.017489 0.595807
+vt 0.965794 0.594793
+vt 1.032618 0.844897
+vt 0.945957 0.843211
+vt 0.979169 0.295858
+vt 0.967884 0.544959
+vt 0.952893 0.793765
+vt 0.960930 0.239587
+vt 0.925822 0.482052
+vt 0.894284 0.725297
+vt 0.716363 0.929856
+vt 0.974473 0.192535
+vt 0.931207 0.433203
+vt 0.902436 0.677050
+vt 0.801451 0.904132
+vt 0.936960 0.384433
+vt 0.909171 0.628493
+vt 0.844888 0.864413
+vt 0.943427 0.335818
+vt 0.915075 0.579756
+vt 0.868695 0.819731
+vt 0.951128 0.287472
+vt 0.920529 0.530922
+vt 0.883697 0.773009
+vt 0.509882 0.901680
+vt 0.603672 0.925341
+vt 0.939868 0.227168
+vt 0.882765 0.457977
+vt 0.837646 0.692981
+vt 0.690972 0.886830
+vt 0.962264 0.185007
+vt 0.891191 0.410771
+vt 0.848554 0.646630
+vt 0.746350 0.860374
+vt 0.900372 0.363822
+vt 0.857974 0.599764
+vt 0.782623 0.823943
+vt 0.910879 0.317332
+vt 0.866529 0.552601
+vt 0.806908 0.782514
+vt 0.923572 0.271605
+vt 0.874667 0.505296
+vt 0.824298 0.738476
+vt 0.531785 0.894399
+vt 0.616896 0.895419
+vt 0.840708 0.424882
+vt 0.787319 0.652419
+vt 0.666434 0.846117
+vt 0.951650 0.174333
+vt 0.851788 0.379552
+vt 0.799275 0.607479
+vt 0.706276 0.816232
+vt 0.864196 0.334814
+vt 0.810089 0.562030
+vt 0.735147 0.779653
+vt 0.878757 0.291055
+vt 0.820304 0.516318
+vt 0.756661 0.739218
+vt 0.896714 0.248885
+vt 0.830369 0.470540
+vt 0.773481 0.696510
+vt 0.545835 0.882162
+vt 0.613251 0.864748
+vt 0.920019 0.209353
+vt 0.601814 0.835743
+vt 0.902011 0.186799
+vt 0.798974 0.385208
+vt 0.741339 0.608286
+vt 0.641659 0.808678
+vt 0.812374 0.341530
+vt 0.753261 0.563866
+vt 0.671741 0.774500
+vt 0.827915 0.298950
+vt 0.764518 0.519117
+vt 0.694750 0.736000
+vt 0.846764 0.258126
+vt 0.775586 0.474273
+vt 0.713019 0.694862
+vt 0.551827 0.867272
+vt 0.551336 0.851697
+vt 0.870653 0.220135
+vt 0.786910 0.429557
+vt 0.728181 0.652086
+vt 0.943465 0.161100
+vt 0.886800 0.160336
+vt 0.756203 0.341667
+vt 0.697515 0.564418
+vt 0.616262 0.775491
+vt 0.771631 0.298973
+vt 0.708699 0.519594
+vt 0.639253 0.736889
+vt 0.790354 0.258010
+vt 0.719689 0.474675
+vt 0.657473 0.695657
+vt 0.814109 0.219846
+vt 0.730930 0.429879
+vt 0.672573 0.652794
+vt 0.546100 0.836912
+vt 0.845356 0.186292
+vt 0.742904 0.385443
+vt 0.685664 0.608914
+vt 0.586129 0.809772
+vt 0.938802 0.146131
+vt 0.876174 0.131001
+vt 0.710309 0.297343
+vt 0.654008 0.524127
+vt 0.590087 0.747535
+vt 0.727302 0.254538
+vt 0.663845 0.478153
+vt 0.607276 0.704820
+vt 0.749262 0.214082
+vt 0.673880 0.432264
+vt 0.621259 0.660654
+vt 0.779077 0.177660
+vt 0.684564 0.386653
+vt 0.643951 0.570007
+vt 0.537514 0.823995
+vt 0.820645 0.148333
+vt 0.696450 0.341564
+vt 0.567816 0.787838
+vt 0.658464 0.256085
+vt 0.609517 0.490486
+vt 0.563122 0.725732
+vt 0.700288 0.169995
+vt 0.617324 0.442954
+vt 0.575103 0.679574
+vt 0.625569 0.395564
+vt 0.633223 0.615598
+vt 0.585045 0.632740
+vt 0.737701 0.133357
+vt 0.634697 0.348462
+vt 0.601779 0.538040
+vt 0.526630 0.813733
+vt 0.796084 0.107238
+vt 0.645335 0.301858
+vt 0.547763 0.770746
+vt 0.939113 0.130553
+vt 0.874144 0.100247
+vt 0.563421 0.466357
+vt 0.535483 0.710885
+vt 0.675759 0.211740
+vt 0.635028 0.130630
+vt 0.568439 0.417338
+vt 0.542491 0.662261
+vt 0.573887 0.368405
+vt 0.593757 0.585502
+vt 0.548386 0.613416
+vt 0.678869 0.090056
+vt 0.580129 0.319628
+vt 0.558567 0.515408
+vt 0.514254 0.806683
+vt 0.770350 0.063964
+vt 0.587726 0.271122
+vt 0.526572 0.759129
+vt 0.946160 0.115905
+vt 0.891158 0.070774
+vt 0.597669 0.223086
+vt 0.611929 0.175930
+vt 0.542415 0.105695
+vt 0.517489 0.404193
+vt 0.509500 0.653713
+vt 0.519234 0.354298
+vt 0.553639 0.564444
+vt 0.511281 0.603820
+vt 0.568145 0.057267
+vt 0.521272 0.304420
+vt 0.514398 0.504007
+vt 0.501048 0.803204
+vt 0.731552 0.019533
+vt 0.523822 0.254574
+vt 0.504734 0.753426
+vt 0.961397 0.104224
+vt 0.952242 0.049775
+vt 0.527296 0.204783
+vt 0.515908 0.454098
+vt 0.507397 0.703586
+vt 0.532618 0.155103
+vt 0.433350 0.107802
+vt 0.465794 0.405207
+vt 0.476377 0.654364
+vt 0.463490 0.355390
+vt 0.512882 0.553916
+vt 0.474014 0.604552
+vt 0.401564 0.060404
+vt 0.460802 0.305605
+vt 0.469884 0.504881
+vt 0.487599 0.803469
+vt 1.250123 0.025793
+vt 0.457447 0.255872
+vt 0.482704 0.753860
+vt 0.984179 0.097813
+vt 1.050589 0.051685
+vt 0.452893 0.206235
+vt 0.467885 0.455040
+vt 0.479169 0.704142
+vt 0.445957 0.156790
+vt 0.344888 0.135587
+vt 0.415075 0.420244
+vt 0.443428 0.664182
+vt 0.409171 0.371507
+vt 0.471892 0.554721
+vt 0.436960 0.615567
+vt 0.250123 0.025793
+vt 0.301452 0.095868
+vt 0.402437 0.322950
+vt 0.425822 0.517948
+vt 0.474473 0.807464
+vt 0.050589 0.051685
+vt 0.216363 0.070144
+vt 0.394284 0.274702
+vt 0.460930 0.760413
+vt 0.009882 0.098319
+vt 0.103671 0.074658
+vt 0.383698 0.226991
+vt 0.420529 0.469078
+vt 0.451128 0.712528
+vt 0.366529 0.447399
+vt 0.410880 0.682668
+vt 0.282623 0.176056
+vt 0.357975 0.400236
+vt 0.431207 0.566797
+vt 0.400372 0.636177
+vt 0.246351 0.139626
+vt 0.348554 0.353370
+vt 0.382765 0.542022
+vt 0.462264 0.814992
+vt 0.190972 0.113170
+vt 0.337646 0.307019
+vt 0.439868 0.772831
+vt 0.031785 0.105600
+vt 0.116896 0.104580
+vt 0.368695 0.180269
+vt 0.324298 0.261524
+vt 0.374667 0.494704
+vt 0.423573 0.728394
+vt 0.306908 0.217486
+vt 0.378757 0.708945
+vt 0.235147 0.220346
+vt 0.310089 0.437969
+vt 0.391191 0.589229
+vt 0.364196 0.665186
+vt 0.206276 0.183767
+vt 0.299275 0.392521
+vt 0.340709 0.575117
+vt 0.451650 0.825666
+vt 0.166434 0.153882
+vt 0.287319 0.347580
+vt 0.420019 0.790647
+vt 0.045835 0.117837
+vt 0.113250 0.135252
+vt 0.273481 0.303490
+vt 0.330370 0.529459
+vt 0.396715 0.751114
+vt 0.256661 0.260781
+vt 0.320304 0.483681
+vt 0.171741 0.225499
+vt 0.253261 0.436134
+vt 0.351788 0.620448
+vt 0.312374 0.658470
+vt 0.141659 0.191322
+vt 0.241339 0.391714
+vt 0.402011 0.813201
+vt 0.101814 0.164257
+vt 0.228181 0.347914
+vt 0.286910 0.570443
+vt 0.370653 0.779864
+vt 0.051826 0.132727
+vt 0.051336 0.148303
+vt 0.213019 0.305137
+vt 0.275586 0.525726
+vt 0.346765 0.741874
+vt 0.194750 0.264000
+vt 0.264518 0.480883
+vt 0.327915 0.701050
+vt 0.139253 0.263111
+vt 0.208699 0.480406
+vt 0.271631 0.701027
+vt 0.116262 0.224508
+vt 0.197515 0.435582
+vt 0.298975 0.614792
+vt 0.443465 0.838899
+vt 0.386800 0.839664
+vt 0.086129 0.190228
+vt 0.185664 0.391086
+vt 0.242904 0.614557
+vt 0.345357 0.813708
+vt 0.046099 0.163088
+vt 0.172573 0.347205
+vt 0.230930 0.570121
+vt 0.314109 0.780154
+vt 0.157473 0.304343
+vt 0.219689 0.525325
+vt 0.290354 0.741990
+vt 0.107276 0.295180
+vt 0.163845 0.521847
+vt 0.227302 0.745462
+vt 0.090087 0.252465
+vt 0.154008 0.475873
+vt 0.256203 0.658333
+vt 0.438803 0.853868
+vt 0.376175 0.868999
+vt 0.067816 0.212161
+vt 0.143951 0.429993
+vt 0.196450 0.658436
+vt 0.320646 0.851667
+vt 0.037514 0.176004
+vt 0.133223 0.384401
+vt 0.184564 0.613347
+vt 0.279077 0.822340
+vt 0.121259 0.339345
+vt 0.173881 0.567736
+vt 0.249262 0.785918
+vt 0.175759 0.788260
+vt 0.063122 0.274267
+vt 0.109517 0.509514
+vt 0.210309 0.702657
+vt 0.439113 0.869447
+vt 0.374144 0.899752
+vt 0.047763 0.229253
+vt 0.101779 0.461959
+vt 0.145335 0.698142
+vt 0.296085 0.892762
+vt 0.026630 0.186267
+vt 0.093757 0.414497
+vt 0.134697 0.651538
+vt 0.237701 0.866643
+vt 0.085045 0.367260
+vt 0.125569 0.604436
+vt 0.200288 0.830006
+vt 0.075103 0.320425
+vt 0.117324 0.557046
+vt 0.995265 0.147143
+vt 1.014254 0.193317
+vt 0.495265 0.852856
+vt 1.009882 0.098319
+vt 1.031785 0.105600
+vt 1.045835 0.117837
+vt 1.051826 0.132727
+vt 1.051336 0.148303
+vt 1.046099 0.163088
+vt 1.037514 0.176004
+vt 0.446160 0.884095
+vt 1.026630 0.186267
+vt 0.158464 0.743915
+vn -0.460158 -0.887814 0.000000
+vn -0.592853 -0.805292 0.000000
+vn -0.437635 -0.887814 -0.142186
+vn -0.951781 -0.306742 0.000000
+vn -0.987854 -0.155217 0.000000
+vn -0.905179 -0.306742 -0.294107
+vn -0.892575 0.450850 0.000000
+vn -0.811670 0.584063 0.000000
+vn -0.848903 0.450850 -0.275826
+vn -0.316050 0.948729 0.000000
+vn -0.164068 0.986419 0.000000
+vn -0.300577 0.948729 -0.097659
+vn -0.316050 -0.948729 0.000000
+vn -0.300577 -0.948729 -0.097659
+vn -0.892575 -0.450850 0.000000
+vn -0.848903 -0.450850 -0.275826
+vn -0.951781 0.306742 0.000000
+vn -0.905179 0.306742 -0.294107
+vn -0.460158 0.887814 0.000000
+vn -0.437635 0.887814 -0.142186
+vn -0.164068 -0.986419 0.000000
+vn -0.156011 -0.986419 -0.050691
+vn -0.811670 -0.584063 0.000000
+vn -0.771935 -0.584063 -0.250801
+vn -0.987854 0.155217 0.000000
+vn -0.939512 0.155217 -0.305246
+vn -0.592853 0.805292 0.000000
+vn -0.563829 0.805292 -0.183203
+vn -0.710959 -0.703207 0.000000
+vn -0.676168 -0.703207 -0.219703
+vn -1.000000 0.000000 0.000000
+vn -0.951048 0.000000 -0.309000
+vn -0.710959 0.703207 0.000000
+vn -0.676168 0.703207 -0.219703
+vn -0.563829 -0.805292 -0.183203
+vn -0.939512 -0.155217 -0.305246
+vn -0.771935 0.584063 -0.250801
+vn -0.156011 0.986419 -0.050691
+vn -0.255684 0.948729 -0.185766
+vn -0.255684 -0.948729 -0.185766
+vn -0.722098 -0.450850 -0.524644
+vn -0.770012 0.306742 -0.559435
+vn -0.372265 0.887814 -0.270455
+vn -0.132725 -0.986419 -0.096408
+vn -0.656667 -0.584063 -0.477096
+vn -0.799188 0.155217 -0.580645
+vn -0.479629 0.805292 -0.348460
+vn -0.575182 -0.703207 -0.417890
+vn -0.809015 0.000000 -0.587756
+vn -0.575182 0.703207 -0.417890
+vn -0.479629 -0.805292 -0.348460
+vn -0.799188 -0.155217 -0.580645
+vn -0.656667 0.584063 -0.477096
+vn -0.372265 -0.887814 -0.270455
+vn -0.770012 -0.306742 -0.559435
+vn -0.722098 0.450850 -0.524644
+vn -0.185766 -0.948729 -0.255684
+vn -0.524644 -0.450850 -0.722098
+vn -0.559435 0.306742 -0.770012
+vn -0.270455 0.887814 -0.372265
+vn -0.096408 -0.986419 -0.132725
+vn -0.477096 -0.584063 -0.656667
+vn -0.580645 0.155217 -0.799188
+vn -0.348460 0.805292 -0.479629
+vn -0.417890 -0.703207 -0.575182
+vn -0.587756 0.000000 -0.809015
+vn -0.417890 0.703207 -0.575182
+vn -0.348460 -0.805292 -0.479629
+vn -0.580645 -0.155217 -0.799188
+vn -0.477096 0.584063 -0.656667
+vn -0.270455 -0.887814 -0.372265
+vn -0.559435 -0.306742 -0.770012
+vn -0.524644 0.450850 -0.722098
+vn -0.132725 0.986419 -0.096408
+vn -0.185766 0.948729 -0.255684
+vn -0.097659 -0.948729 -0.300577
+vn -0.275826 -0.450850 -0.848903
+vn -0.294107 0.306742 -0.905179
+vn -0.142186 0.887814 -0.437635
+vn -0.050691 -0.986419 -0.156011
+vn -0.250801 -0.584063 -0.771935
+vn -0.305246 0.155217 -0.939512
+vn -0.183203 0.805292 -0.563829
+vn -0.219703 -0.703207 -0.676168
+vn -0.309000 0.000000 -0.951048
+vn -0.219703 0.703207 -0.676168
+vn -0.183203 -0.805292 -0.563829
+vn -0.305246 -0.155217 -0.939512
+vn -0.250801 0.584063 -0.771935
+vn -0.142186 -0.887814 -0.437635
+vn -0.294107 -0.306742 -0.905179
+vn -0.275826 0.450850 -0.848903
+vn -0.096408 0.986419 -0.132725
+vn -0.097659 0.948729 -0.300577
+vn 0.000000 -0.450850 -0.892575
+vn 0.000000 0.306742 -0.951781
+vn 0.000000 0.887814 -0.460158
+vn 0.000000 -0.986419 -0.164068
+vn 0.000000 -0.584063 -0.811670
+vn 0.000000 0.155217 -0.987854
+vn 0.000000 0.805292 -0.592853
+vn 0.000000 -0.703207 -0.710959
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.703207 -0.710959
+vn 0.000000 -0.805292 -0.592853
+vn 0.000000 -0.155217 -0.987854
+vn 0.000000 0.584063 -0.811670
+vn 0.000000 -0.887814 -0.460158
+vn 0.000000 -0.306742 -0.951781
+vn 0.000000 0.450850 -0.892575
+vn -0.050691 0.986419 -0.156011
+vn 0.000000 0.948729 -0.316050
+vn 0.000000 -0.948729 -0.316050
+vn 0.097659 0.948729 -0.300577
+vn 0.097659 -0.948729 -0.300577
+vn 0.275826 -0.450850 -0.848903
+vn 0.294107 0.306742 -0.905179
+vn 0.142186 0.887814 -0.437635
+vn 0.250801 -0.584063 -0.771935
+vn 0.305246 0.155217 -0.939512
+vn 0.183203 0.805292 -0.563829
+vn 0.219703 -0.703207 -0.676168
+vn 0.309000 0.000000 -0.951048
+vn 0.219703 0.703207 -0.676168
+vn 0.183203 -0.805292 -0.563829
+vn 0.305246 -0.155217 -0.939512
+vn 0.250801 0.584063 -0.771935
+vn 0.000000 0.986419 -0.164068
+vn 0.050691 0.986419 -0.156011
+vn 0.142186 -0.887814 -0.437635
+vn 0.294107 -0.306742 -0.905179
+vn 0.275826 0.450850 -0.848903
+vn 0.050691 -0.986419 -0.156011
+vn 0.185766 -0.948729 -0.255684
+vn 0.524644 -0.450850 -0.722098
+vn 0.559435 0.306742 -0.770012
+vn 0.270455 0.887814 -0.372265
+vn 0.477096 -0.584063 -0.656667
+vn 0.580645 0.155217 -0.799188
+vn 0.348460 0.805292 -0.479629
+vn 0.417890 -0.703207 -0.575182
+vn 0.587756 0.000000 -0.809015
+vn 0.417890 0.703207 -0.575182
+vn 0.348460 -0.805292 -0.479629
+vn 0.580645 -0.155217 -0.799188
+vn 0.477096 0.584063 -0.656667
+vn 0.096408 0.986419 -0.132725
+vn 0.270455 -0.887814 -0.372265
+vn 0.559435 -0.306742 -0.770012
+vn 0.524644 0.450850 -0.722098
+vn 0.185766 0.948729 -0.255684
+vn 0.096408 -0.986419 -0.132725
+vn 0.255684 -0.948729 -0.185766
+vn 0.722098 -0.450850 -0.524644
+vn 0.770012 0.306742 -0.559435
+vn 0.372265 0.887814 -0.270455
+vn 0.656667 -0.584063 -0.477096
+vn 0.799188 0.155217 -0.580645
+vn 0.479629 0.805292 -0.348460
+vn 0.575182 -0.703207 -0.417890
+vn 0.809015 0.000000 -0.587756
+vn 0.575182 0.703207 -0.417890
+vn 0.479629 -0.805292 -0.348460
+vn 0.799188 -0.155217 -0.580645
+vn 0.722098 0.450850 -0.524644
+vn 0.132725 0.986419 -0.096408
+vn 0.372265 -0.887814 -0.270455
+vn 0.770012 -0.306742 -0.559435
+vn 0.255684 0.948729 -0.185766
+vn 0.848903 -0.450850 -0.275826
+vn 0.905179 0.306742 -0.294107
+vn 0.437635 0.887814 -0.142186
+vn 0.676168 -0.703207 -0.219703
+vn 0.939512 0.155217 -0.305246
+vn 0.563829 0.805292 -0.183203
+vn 0.951048 0.000000 -0.309000
+vn 0.656667 0.584063 -0.477096
+vn 0.676168 0.703207 -0.219703
+vn 0.563829 -0.805292 -0.183203
+vn 0.939512 -0.155217 -0.305246
+vn 0.848903 0.450850 -0.275826
+vn 0.156011 0.986419 -0.050691
+vn 0.437635 -0.887814 -0.142186
+vn 0.905179 -0.306742 -0.294107
+vn 0.300577 0.948729 -0.097659
+vn 0.132725 -0.986419 -0.096408
+vn 0.300577 -0.948729 -0.097659
+vn 0.951781 0.306742 0.000000
+vn 0.460158 0.887814 0.000000
+vn 0.771935 -0.584063 -0.250801
+vn 0.710959 -0.703207 0.000000
+vn 0.987854 0.155217 0.000000
+vn 0.592853 0.805292 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.771935 0.584063 -0.250801
+vn 0.710959 0.703207 0.000000
+vn 0.592853 -0.805292 0.000000
+vn 0.987854 -0.155217 0.000000
+vn 0.892575 0.450850 0.000000
+vn 0.164068 0.986419 0.000000
+vn 0.460158 -0.887814 0.000000
+vn 0.951781 -0.306742 0.000000
+vn 0.316050 0.948729 0.000000
+vn 0.156011 -0.986419 -0.050691
+vn 0.316050 -0.948729 0.000000
+vn 0.892575 -0.450850 0.000000
+vn 0.811670 -0.584063 0.000000
+vn 0.676168 -0.703207 0.219703
+vn 0.939512 0.155217 0.305246
+vn 0.563829 0.805292 0.183203
+vn 0.951048 0.000000 0.309000
+vn 0.811670 0.584063 0.000000
+vn 0.676168 0.703207 0.219703
+vn 0.563829 -0.805292 0.183203
+vn 0.939512 -0.155217 0.305246
+vn 0.848903 0.450850 0.275826
+vn 0.156011 0.986419 0.050691
+vn 0.437635 -0.887814 0.142186
+vn 0.905179 -0.306742 0.294107
+vn 0.300577 0.948729 0.097659
+vn 0.164068 -0.986419 0.000000
+vn 0.300577 -0.948729 0.097659
+vn 0.848903 -0.450850 0.275826
+vn 0.905179 0.306742 0.294107
+vn 0.437635 0.887814 0.142186
+vn 0.771935 -0.584063 0.250801
+vn 0.575182 -0.703207 0.417890
+vn 0.799188 0.155217 0.580645
+vn 0.479629 0.805292 0.348460
+vn 0.809015 0.000000 0.587756
+vn 0.771935 0.584063 0.250801
+vn 0.575182 0.703207 0.417890
+vn 0.479629 -0.805292 0.348460
+vn 0.799188 -0.155217 0.580645
+vn 0.722098 0.450850 0.524644
+vn 0.132725 0.986419 0.096408
+vn 0.372265 -0.887814 0.270455
+vn 0.770012 -0.306742 0.559435
+vn 0.255684 0.948729 0.185766
+vn 0.156011 -0.986419 0.050691
+vn 0.255684 -0.948729 0.185766
+vn 0.722098 -0.450850 0.524644
+vn 0.770012 0.306742 0.559435
+vn 0.372265 0.887814 0.270455
+vn 0.656667 -0.584063 0.477096
+vn 0.417890 -0.703207 0.575182
+vn 0.580645 0.155217 0.799188
+vn 0.348460 0.805292 0.479629
+vn 0.587756 0.000000 0.809015
+vn 0.656667 0.584063 0.477096
+vn 0.417890 0.703207 0.575182
+vn 0.348460 -0.805292 0.479629
+vn 0.580645 -0.155217 0.799188
+vn 0.524644 0.450850 0.722098
+vn 0.096408 0.986419 0.132725
+vn 0.270455 -0.887814 0.372265
+vn 0.559435 -0.306742 0.770012
+vn 0.185766 0.948729 0.255684
+vn 0.132725 -0.986419 0.096408
+vn 0.185766 -0.948729 0.255684
+vn 0.524644 -0.450850 0.722098
+vn 0.559435 0.306742 0.770012
+vn 0.270455 0.887814 0.372265
+vn 0.305246 0.155217 0.939512
+vn 0.183203 0.805292 0.563829
+vn 0.219703 -0.703207 0.676168
+vn 0.309000 0.000000 0.951048
+vn 0.477096 0.584063 0.656667
+vn 0.219703 0.703207 0.676168
+vn 0.183203 -0.805292 0.563829
+vn 0.305246 -0.155217 0.939512
+vn 0.275826 0.450850 0.848903
+vn 0.050691 0.986419 0.156011
+vn 0.142186 -0.887814 0.437635
+vn 0.294107 -0.306742 0.905179
+vn 0.097659 0.948729 0.300577
+vn 0.096408 -0.986419 0.132725
+vn 0.097659 -0.948729 0.300577
+vn 0.477096 -0.584063 0.656667
+vn 0.275826 -0.450850 0.848903
+vn 0.294107 0.306742 0.905179
+vn 0.142186 0.887814 0.437635
+vn 0.250801 -0.584063 0.771935
+vn 0.000000 0.805292 0.592853
+vn 0.000000 -0.703207 0.710959
+vn 0.000000 0.000000 1.000000
+vn 0.250801 0.584063 0.771935
+vn 0.000000 0.703207 0.710959
+vn 0.000000 -0.805292 0.592853
+vn 0.000000 -0.155217 0.987854
+vn 0.000000 0.450850 0.892575
+vn 0.000000 0.986419 0.164068
+vn 0.000000 -0.887814 0.460158
+vn 0.000000 -0.306742 0.951781
+vn 0.000000 0.948729 0.316050
+vn 0.050691 -0.986419 0.156011
+vn 0.000000 -0.948729 0.316050
+vn 0.000000 -0.450850 0.892575
+vn 0.000000 0.306742 0.951781
+vn 0.000000 0.887814 0.460158
+vn 0.000000 -0.584063 0.811670
+vn 0.000000 0.155217 0.987854
+vn -0.183203 -0.805292 0.563829
+vn -0.305246 -0.155217 0.939512
+vn 0.000000 0.584063 0.811670
+vn -0.250801 0.584063 0.771935
+vn -0.142186 -0.887814 0.437635
+vn -0.294107 -0.306742 0.905179
+vn -0.097659 0.948729 0.300577
+vn -0.097659 -0.948729 0.300577
+vn -0.275826 -0.450850 0.848903
+vn -0.294107 0.306742 0.905179
+vn -0.142186 0.887814 0.437635
+vn 0.000000 -0.986419 0.164068
+vn -0.050691 -0.986419 0.156011
+vn -0.250801 -0.584063 0.771935
+vn -0.305246 0.155217 0.939512
+vn -0.183203 0.805292 0.563829
+vn -0.219703 -0.703207 0.676168
+vn -0.309000 0.000000 0.951048
+vn -0.219703 0.703207 0.676168
+vn -0.348460 -0.805292 0.479629
+vn -0.580645 -0.155217 0.799188
+vn -0.477096 0.584063 0.656667
+vn -0.270455 -0.887814 0.372265
+vn -0.559435 -0.306742 0.770012
+vn -0.275826 0.450850 0.848903
+vn -0.050691 0.986419 0.156011
+vn -0.185766 0.948729 0.255684
+vn -0.185766 -0.948729 0.255684
+vn -0.524644 -0.450850 0.722098
+vn -0.559435 0.306742 0.770012
+vn -0.270455 0.887814 0.372265
+vn -0.096408 -0.986419 0.132725
+vn -0.477096 -0.584063 0.656667
+vn -0.580645 0.155217 0.799188
+vn -0.348460 0.805292 0.479629
+vn -0.417890 -0.703207 0.575182
+vn -0.587756 0.000000 0.809015
+vn -0.417890 0.703207 0.575182
+vn -0.479629 -0.805292 0.348460
+vn -0.799188 -0.155217 0.580645
+vn -0.656667 0.584063 0.477096
+vn -0.372265 -0.887814 0.270455
+vn -0.770012 -0.306742 0.559435
+vn -0.524644 0.450850 0.722098
+vn -0.096408 0.986419 0.132725
+vn -0.255684 0.948729 0.185766
+vn -0.255684 -0.948729 0.185766
+vn -0.722098 -0.450850 0.524644
+vn -0.770012 0.306742 0.559435
+vn -0.372265 0.887814 0.270455
+vn -0.132725 -0.986419 0.096408
+vn -0.656667 -0.584063 0.477096
+vn -0.799188 0.155217 0.580645
+vn -0.479629 0.805292 0.348460
+vn -0.575182 -0.703207 0.417890
+vn -0.809015 0.000000 0.587756
+vn -0.575182 0.703207 0.417890
+vn -0.771935 0.584063 0.250801
+vn -0.437635 -0.887814 0.142186
+vn -0.905179 -0.306742 0.294107
+vn -0.722098 0.450850 0.524644
+vn -0.132725 0.986419 0.096408
+vn -0.300577 0.948729 0.097659
+vn -0.300577 -0.948729 0.097659
+vn -0.848903 -0.450850 0.275826
+vn -0.905179 0.306742 0.294107
+vn -0.437635 0.887814 0.142186
+vn -0.156011 -0.986419 0.050691
+vn -0.771935 -0.584063 0.250801
+vn -0.939512 0.155217 0.305246
+vn -0.563829 0.805292 0.183203
+vn -0.676168 -0.703207 0.219703
+vn -0.951048 0.000000 0.309000
+vn -0.676168 0.703207 0.219703
+vn -0.563829 -0.805292 0.183203
+vn -0.939512 -0.155217 0.305246
+vn 0.000000 -0.999969 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.156011 0.986419 0.050691
+vn -0.848903 0.450850 0.275826
+s 1
+f 17/1/1 16/2/2 36/3/3
+f 12/4/4 11/5/5 31/6/6
+f 7/7/7 6/8/8 26/9/9
+f 2/10/10 1/11/11 21/12/12
+f 18/13/13 17/1/1 37/14/14
+f 13/15/15 12/4/4 32/16/16
+f 8/17/17 7/7/7 27/18/18
+f 3/19/19 2/10/10 22/20/20
+f 19/21/21 18/13/13 38/22/22
+f 14/23/23 13/15/15 33/24/24
+f 9/25/25 8/17/17 28/26/26
+f 4/27/27 3/19/19 23/28/28
+f 15/29/29 14/23/23 34/30/30
+f 10/31/31 9/25/25 29/32/32
+f 5/33/33 4/27/27 24/34/34
+f 16/2/2 15/29/29 35/35/35
+f 11/5/5 10/31/31 30/36/36
+f 6/8/8 5/33/33 25/37/37
+f 21/12/12 20/38/38 41/39/39
+f 37/40/14 36/41/3 57/42/40
+f 32/43/16 31/44/6 52/45/41
+f 27/46/18 26/47/9 47/48/42
+f 22/49/20 21/12/12 42/50/43
+f 38/51/22 37/40/14 58/52/44
+f 33/53/24 32/43/16 53/54/45
+f 28/55/26 27/46/18 48/56/46
+f 23/57/28 22/49/20 43/58/47
+f 34/59/30 33/53/24 54/60/48
+f 29/61/32 28/55/26 49/62/49
+f 24/63/34 23/57/28 44/64/50
+f 35/65/35 34/59/30 55/66/51
+f 30/67/36 29/61/32 50/68/52
+f 25/69/37 24/63/34 45/70/53
+f 36/41/3 35/65/35 56/71/54
+f 31/44/6 30/67/36 51/72/55
+f 26/47/9 25/69/37 46/73/56
+f 57/42/40 56/71/54 76/74/57
+f 52/45/41 51/72/55 71/75/58
+f 47/48/42 46/73/56 66/76/59
+f 42/50/43 41/39/39 61/77/60
+f 58/52/44 57/42/40 77/78/61
+f 53/54/45 52/45/41 72/79/62
+f 48/56/46 47/48/42 67/80/63
+f 43/58/47 42/50/43 62/81/64
+f 54/60/48 53/54/45 73/82/65
+f 49/62/49 48/56/46 68/83/66
+f 44/64/50 43/58/47 63/84/67
+f 55/66/51 54/60/48 74/85/68
+f 50/68/52 49/62/49 69/86/69
+f 45/70/53 44/64/50 64/87/70
+f 56/71/54 55/66/51 75/88/71
+f 51/72/55 50/68/52 70/89/72
+f 46/73/56 45/70/53 65/90/73
+f 41/39/39 40/91/74 60/92/75
+f 76/74/57 75/88/71 95/93/76
+f 71/75/58 70/89/72 90/94/77
+f 66/76/59 65/90/73 85/95/78
+f 61/77/60 60/92/75 80/96/79
+f 77/78/61 76/74/57 96/97/80
+f 72/79/62 71/75/58 91/98/81
+f 67/80/63 66/76/59 86/99/82
+f 62/81/64 61/77/60 81/100/83
+f 73/82/65 72/79/62 92/101/84
+f 68/83/66 67/80/63 87/102/85
+f 63/84/67 62/81/64 82/103/86
+f 74/85/68 73/82/65 93/104/87
+f 69/86/69 68/83/66 88/105/88
+f 64/87/70 63/84/67 83/106/89
+f 75/88/71 74/85/68 94/107/90
+f 70/89/72 69/86/69 89/108/91
+f 65/90/73 64/87/70 84/109/92
+f 60/92/75 59/110/93 79/111/94
+f 90/94/77 89/108/91 109/112/95
+f 85/95/78 84/109/92 104/113/96
+f 80/96/79 79/111/94 99/114/97
+f 96/97/80 95/93/76 115/115/98
+f 91/98/81 90/94/77 110/116/99
+f 86/99/82 85/95/78 105/117/100
+f 81/100/83 80/96/79 100/118/101
+f 92/101/84 91/98/81 111/119/102
+f 87/102/85 86/99/82 106/120/103
+f 82/103/86 81/100/83 101/121/104
+f 93/104/87 92/101/84 112/122/105
+f 88/105/88 87/102/85 107/123/106
+f 83/106/89 82/103/86 102/124/107
+f 94/107/90 93/104/87 113/125/108
+f 89/108/91 88/105/88 108/126/109
+f 84/109/92 83/106/89 103/127/110
+f 79/111/94 78/128/111 98/129/112
+f 95/93/76 94/107/90 114/130/113
+f 99/114/97 98/129/112 117/131/114
+f 115/115/98 114/130/113 133/132/115
+f 110/116/99 109/112/95 128/133/116
+f 105/117/100 104/113/96 123/134/117
+f 100/118/101 99/114/97 118/135/118
+f 111/119/102 110/116/99 129/136/119
+f 106/120/103 105/117/100 124/137/120
+f 101/121/104 100/118/101 119/138/121
+f 112/122/105 111/119/102 130/139/122
+f 107/123/106 106/120/103 125/140/123
+f 102/124/107 101/121/104 120/141/124
+f 113/125/108 112/122/105 131/142/125
+f 108/126/109 107/123/106 126/143/126
+f 103/127/110 102/124/107 121/144/127
+f 98/129/112 97/145/128 116/146/129
+f 114/130/113 113/125/108 132/147/130
+f 109/112/95 108/126/109 127/148/131
+f 104/113/96 103/127/110 122/149/132
+f 134/150/133 133/132/115 152/151/134
+f 129/136/119 128/133/116 147/152/135
+f 124/137/120 123/134/117 142/153/136
+f 119/138/121 118/135/118 137/154/137
+f 130/139/122 129/136/119 148/155/138
+f 125/140/123 124/137/120 143/156/139
+f 120/141/124 119/138/121 138/157/140
+f 131/142/125 130/139/122 149/158/141
+f 126/143/126 125/140/123 144/159/142
+f 121/144/127 120/141/124 139/160/143
+f 132/147/130 131/142/125 150/161/144
+f 127/148/131 126/143/126 145/162/145
+f 122/149/132 121/144/127 140/163/146
+f 117/131/114 116/146/129 135/164/147
+f 133/132/115 132/147/130 151/165/148
+f 128/133/116 127/148/131 146/166/149
+f 123/134/117 122/149/132 141/167/150
+f 118/135/118 117/131/114 136/168/151
+f 153/169/152 152/151/134 171/170/153
+f 148/155/138 147/152/135 166/171/154
+f 143/156/139 142/153/136 161/172/155
+f 138/157/140 137/154/137 156/173/156
+f 149/158/141 148/155/138 167/174/157
+f 144/159/142 143/156/139 162/175/158
+f 139/160/143 138/157/140 157/176/159
+f 150/161/144 149/158/141 168/177/160
+f 145/162/145 144/159/142 163/178/161
+f 140/163/146 139/160/143 158/179/162
+f 151/165/148 150/161/144 169/180/163
+f 146/166/149 145/162/145 164/181/164
+f 141/167/150 140/163/146 160/182/165
+f 136/168/151 135/164/147 154/183/166
+f 152/151/134 151/165/148 170/184/167
+f 147/152/135 146/166/149 165/185/168
+f 142/153/136 141/167/150 160/182/165
+f 137/154/137 136/168/151 155/186/169
+f 167/174/157 166/171/154 185/187/170
+f 162/175/158 161/172/155 180/188/171
+f 157/176/159 156/173/156 175/189/172
+f 168/177/160 167/174/157 187/190/173
+f 163/178/161 162/175/158 181/191/174
+f 158/179/162 157/176/159 176/192/175
+f 169/180/163 168/177/160 187/190/173
+f 164/181/164 163/178/161 182/193/176
+f 159/194/177 158/179/162 177/195/178
+f 170/184/167 169/180/163 188/196/179
+f 165/185/168 164/181/164 183/197/180
+f 160/182/165 159/194/177 179/198/181
+f 155/186/169 154/183/166 173/199/182
+f 171/170/153 170/184/167 189/200/183
+f 166/171/154 165/185/168 184/201/184
+f 161/172/155 160/182/165 179/198/181
+f 156/173/156 155/186/169 174/202/185
+f 172/203/186 171/170/153 190/204/187
+f 181/191/174 180/188/171 199/205/188
+f 176/192/175 175/189/172 194/206/189
+f 187/190/173 186/207/190 206/208/191
+f 182/193/176 181/191/174 200/209/192
+f 177/195/178 176/192/175 195/210/193
+f 188/196/179 187/190/173 206/208/191
+f 183/197/180 182/193/176 201/211/194
+f 178/212/195 177/195/178 196/213/196
+f 189/200/183 188/196/179 207/214/197
+f 184/201/184 183/197/180 202/215/198
+f 179/198/181 178/212/195 198/216/199
+f 174/202/185 173/199/182 192/217/200
+f 190/204/187 189/200/183 208/218/201
+f 185/187/170 184/201/184 203/219/202
+f 180/188/171 179/198/181 198/216/199
+f 175/189/172 174/202/185 193/220/203
+f 191/221/204 190/204/187 209/222/205
+f 186/207/190 185/187/170 204/223/206
+f 206/208/191 205/224/207 225/225/208
+f 201/211/194 200/209/192 219/226/209
+f 196/213/196 195/210/193 214/227/210
+f 207/214/197 206/208/191 225/225/208
+f 202/215/198 201/211/194 220/228/211
+f 197/229/212 196/213/196 215/230/213
+f 208/218/201 207/214/197 226/231/214
+f 203/219/202 202/215/198 221/232/215
+f 198/216/199 197/229/212 217/233/216
+f 193/220/203 192/217/200 211/234/217
+f 209/222/205 208/218/201 227/235/218
+f 204/223/206 203/219/202 222/236/219
+f 199/205/188 198/216/199 217/233/216
+f 194/206/189 193/220/203 212/237/220
+f 210/238/221 209/222/205 228/239/222
+f 205/224/207 204/223/206 223/240/223
+f 200/209/192 199/205/188 218/241/224
+f 195/210/193 194/206/189 213/242/225
+f 225/225/208 224/243/226 244/244/227
+f 220/228/211 219/226/209 238/245/228
+f 215/230/213 214/227/210 233/246/229
+f 226/231/214 225/225/208 244/244/227
+f 221/232/215 220/228/211 239/247/230
+f 216/248/231 215/230/213 234/249/232
+f 227/235/218 226/231/214 245/250/233
+f 222/236/219 221/232/215 240/251/234
+f 217/233/216 216/248/231 236/252/235
+f 212/237/220 211/234/217 230/253/236
+f 228/239/222 227/235/218 246/254/237
+f 223/240/223 222/236/219 241/255/238
+f 218/241/224 217/233/216 236/252/235
+f 213/242/225 212/237/220 231/256/239
+f 229/257/240 228/239/222 247/258/241
+f 224/243/226 223/240/223 242/259/242
+f 219/226/209 218/241/224 237/260/243
+f 214/227/210 213/242/225 232/261/244
+f 244/244/227 243/262/245 263/263/246
+f 239/247/230 238/245/228 257/264/247
+f 234/249/232 233/246/229 252/265/248
+f 245/250/233 244/244/227 263/263/246
+f 240/251/234 239/247/230 258/266/249
+f 235/267/250 234/249/232 253/268/251
+f 246/269/237 245/250/233 264/270/252
+f 241/255/238 240/251/234 259/271/253
+f 236/252/235 235/267/250 255/272/254
+f 231/256/239 230/253/236 249/273/255
+f 247/274/241 246/269/237 265/275/256
+f 242/259/242 241/255/238 260/276/257
+f 237/260/243 236/252/235 255/272/254
+f 232/261/244 231/256/239 250/277/258
+f 248/278/259 247/274/241 266/279/260
+f 243/262/245 242/259/242 261/280/261
+f 238/245/228 237/260/243 256/281/262
+f 233/246/229 232/261/244 251/282/263
+f 258/266/249 257/264/247 276/283/264
+f 253/268/251 252/265/248 271/284/265
+f 264/270/252 263/263/246 282/285/266
+f 259/271/253 258/266/249 277/286/267
+f 254/287/268 253/268/251 272/288/269
+f 265/275/256 264/270/252 283/289/270
+f 260/276/257 259/271/253 278/290/271
+f 255/272/254 254/287/268 274/291/272
+f 250/277/258 249/273/255 268/292/273
+f 266/279/260 265/275/256 284/293/274
+f 261/280/261 260/276/257 279/294/275
+f 256/281/262 255/272/254 274/291/272
+f 251/282/263 250/277/258 269/295/276
+f 267/296/277 266/279/260 285/297/278
+f 262/298/279 261/280/261 280/299/280
+f 257/264/247 256/281/262 275/300/281
+f 252/265/248 251/282/263 270/301/282
+f 263/263/246 262/298/279 281/302/283
+f 272/288/269 271/284/265 290/303/284
+f 283/289/270 282/285/266 301/304/285
+f 278/290/271 277/286/267 296/305/286
+f 273/306/287 272/288/269 291/307/288
+f 284/293/274 283/289/270 302/308/289
+f 279/294/275 278/290/271 297/309/290
+f 274/291/272 273/306/287 293/310/291
+f 269/295/276 268/292/273 287/311/292
+f 285/297/278 284/293/274 303/312/293
+f 280/299/280 279/294/275 298/313/294
+f 275/300/281 274/291/272 293/310/291
+f 270/301/282 269/295/276 288/314/295
+f 286/315/296 285/297/278 304/316/297
+f 281/302/283 280/299/280 299/317/298
+f 276/283/264 275/300/281 294/318/299
+f 271/284/265 270/301/282 289/319/300
+f 282/285/266 281/302/283 300/320/301
+f 277/286/267 276/283/264 295/321/302
+f 302/308/289 301/304/285 321/322/303
+f 297/309/290 296/305/286 316/323/304
+f 292/324/305 291/307/288 311/325/306
+f 303/312/293 302/308/289 322/326/307
+f 298/313/294 297/309/290 317/327/308
+f 293/310/291 292/324/305 311/325/306
+f 288/314/295 287/311/292 307/328/309
+f 304/316/297 303/312/293 323/329/310
+f 299/317/298 298/313/294 318/330/311
+f 294/318/299 293/310/291 313/331/312
+f 289/319/300 288/314/295 308/332/313
+f 305/333/314 304/316/297 324/334/315
+f 300/320/301 299/317/298 319/335/316
+f 295/321/302 294/318/299 314/336/317
+f 290/303/284 289/319/300 309/337/318
+f 301/304/285 300/320/301 320/338/319
+f 296/305/286 295/321/302 315/339/320
+f 291/307/288 290/303/284 310/340/321
+f 321/322/303 320/338/319 340/341/322
+f 316/323/304 315/339/320 335/342/323
+f 311/325/306 310/340/321 330/343/324
+f 322/326/307 321/322/303 341/344/325
+f 317/327/308 316/323/304 336/345/326
+f 312/346/327 311/325/306 330/343/324
+f 307/328/309 306/347/328 326/348/329
+f 323/329/310 322/326/307 342/349/330
+f 318/330/311 317/327/308 337/350/331
+f 313/331/312 312/346/327 332/351/332
+f 308/332/313 307/328/309 327/352/333
+f 324/334/315 323/329/310 343/353/334
+f 319/335/316 318/330/311 338/354/335
+f 314/336/317 313/331/312 333/355/336
+f 309/337/318 308/332/313 328/356/337
+f 320/338/319 319/335/316 339/357/338
+f 315/339/320 314/336/317 334/358/339
+f 310/340/321 309/337/318 329/359/340
+f 340/341/322 339/357/338 360/360/341
+f 335/342/323 334/358/339 355/361/342
+f 330/343/324 329/359/340 350/362/343
+f 341/344/325 340/341/322 361/363/344
+f 336/345/326 335/342/323 356/364/345
+f 331/365/346 330/343/324 350/362/343
+f 326/348/329 325/366/347 346/367/348
+f 342/349/330 341/344/325 362/368/349
+f 337/350/331 336/345/326 357/369/350
+f 332/351/332 331/365/346 352/370/351
+f 327/352/333 326/348/329 347/371/352
+f 343/353/334 342/349/330 363/372/353
+f 338/354/335 337/350/331 358/373/354
+f 333/355/336 332/351/332 353/374/355
+f 328/356/337 327/352/333 348/375/356
+f 339/357/338 338/354/335 359/376/357
+f 334/358/339 333/355/336 354/377/358
+f 329/359/340 328/356/337 349/378/359
+f 350/362/343 349/378/359 369/379/360
+f 361/363/344 360/360/341 380/380/361
+f 356/364/345 355/361/342 375/381/362
+f 351/382/363 350/362/343 369/379/360
+f 346/367/348 345/383/364 365/384/365
+f 362/368/349 361/363/344 381/385/366
+f 357/369/350 356/364/345 376/386/367
+f 352/370/351 351/382/363 371/387/368
+f 347/371/352 346/367/348 366/388/369
+f 363/372/353 362/368/349 382/389/370
+f 358/373/354 357/369/350 377/390/371
+f 353/374/355 352/370/351 372/391/372
+f 348/375/356 347/371/352 367/392/373
+f 359/376/357 358/373/354 378/393/374
+f 354/377/358 353/374/355 373/394/375
+f 349/378/359 348/375/356 368/395/376
+f 360/360/341 359/376/357 379/396/377
+f 355/361/342 354/377/358 374/397/378
+f 39/398/379 19/399/21 38/51/22
+f 1/11/11 344/400/380 20/38/38
+f 39/398/379 38/51/22 58/52/44
+f 20/38/38 344/400/380 40/91/74
+f 39/398/379 58/52/44 77/78/61
+f 40/91/74 344/400/380 59/110/93
+f 39/398/379 77/78/61 96/97/80
+f 59/110/93 344/400/380 78/128/111
+f 39/398/379 96/97/80 115/115/98
+f 78/128/111 344/400/380 97/145/128
+f 39/398/379 115/115/98 134/150/133
+f 97/145/128 344/400/380 116/146/129
+f 39/398/379 134/150/133 153/169/152
+f 116/146/129 344/400/380 135/164/147
+f 39/398/379 153/169/152 172/203/186
+f 135/164/147 344/400/380 154/183/166
+f 39/398/379 172/203/186 191/221/204
+f 154/183/166 344/400/380 173/199/182
+f 39/398/379 191/221/204 210/238/221
+f 173/199/182 344/400/380 192/217/200
+f 39/398/379 210/238/221 229/257/240
+f 192/217/200 344/400/380 211/234/217
+f 39/398/379 229/257/240 248/401/259
+f 211/234/217 344/400/380 230/253/236
+f 39/398/379 248/401/259 267/402/277
+f 230/253/236 344/400/380 249/273/255
+f 249/273/255 344/400/380 268/292/273
+f 39/398/379 267/402/277 286/403/296
+f 268/292/273 344/400/380 287/311/292
+f 39/398/379 286/403/296 305/404/314
+f 287/311/292 344/400/380 306/347/328
+f 39/398/379 305/404/314 324/405/315
+f 306/347/328 344/400/380 325/366/347
+f 39/398/379 324/405/315 343/406/334
+f 325/366/347 344/400/380 345/383/364
+f 39/398/379 343/406/334 363/407/353
+f 345/383/364 344/400/380 364/408/381
+f 39/398/379 363/407/353 382/409/370
+f 364/408/381 344/400/380 1/11/11
+f 380/380/361 379/396/377 16/2/2
+f 375/381/362 374/397/378 12/4/4
+f 370/410/382 369/379/360 7/7/7
+f 365/384/365 364/408/381 2/10/10
+f 381/385/366 380/380/361 17/1/1
+f 376/386/367 375/381/362 13/15/15
+f 371/387/368 370/410/382 8/17/17
+f 366/388/369 365/384/365 3/19/19
+f 382/389/370 381/385/366 18/13/13
+f 377/390/371 376/386/367 13/15/15
+f 372/391/372 371/387/368 9/25/25
+f 367/392/373 366/388/369 4/27/27
+f 39/398/379 382/409/370 19/399/21
+f 378/393/374 377/390/371 14/23/23
+f 373/394/375 372/391/372 10/31/31
+f 368/395/376 367/392/373 5/33/33
+f 379/396/377 378/393/374 15/29/29
+f 374/397/378 373/394/375 11/5/5
+f 369/379/360 368/395/376 6/8/8
+f 16/2/2 35/35/35 36/3/3
+f 11/5/5 30/36/36 31/6/6
+f 6/8/8 25/37/37 26/9/9
+f 1/11/11 20/38/38 21/12/12
+f 17/1/1 36/3/3 37/14/14
+f 12/4/4 31/6/6 32/16/16
+f 7/7/7 26/9/9 27/18/18
+f 2/10/10 21/12/12 22/20/20
+f 18/13/13 37/14/14 38/22/22
+f 13/15/15 32/16/16 33/24/24
+f 8/17/17 27/18/18 28/26/26
+f 3/19/19 22/20/20 23/28/28
+f 14/23/23 33/24/24 34/30/30
+f 9/25/25 28/26/26 29/32/32
+f 4/27/27 23/28/28 24/34/34
+f 15/29/29 34/30/30 35/35/35
+f 10/31/31 29/32/32 30/36/36
+f 5/33/33 24/34/34 25/37/37
+f 20/38/38 40/91/74 41/39/39
+f 36/41/3 56/71/54 57/42/40
+f 31/44/6 51/72/55 52/45/41
+f 26/47/9 46/73/56 47/48/42
+f 21/12/12 41/39/39 42/50/43
+f 37/40/14 57/42/40 58/52/44
+f 32/43/16 52/45/41 53/54/45
+f 27/46/18 47/48/42 48/56/46
+f 22/49/20 42/50/43 43/58/47
+f 33/53/24 53/54/45 54/60/48
+f 28/55/26 48/56/46 49/62/49
+f 23/57/28 43/58/47 44/64/50
+f 34/59/30 54/60/48 55/66/51
+f 29/61/32 49/62/49 50/68/52
+f 24/63/34 44/64/50 45/70/53
+f 35/65/35 55/66/51 56/71/54
+f 30/67/36 50/68/52 51/72/55
+f 25/69/37 45/70/53 46/73/56
+f 56/71/54 75/88/71 76/74/57
+f 51/72/55 70/89/72 71/75/58
+f 46/73/56 65/90/73 66/76/59
+f 41/39/39 60/92/75 61/77/60
+f 57/42/40 76/74/57 77/78/61
+f 52/45/41 71/75/58 72/79/62
+f 47/48/42 66/76/59 67/80/63
+f 42/50/43 61/77/60 62/81/64
+f 53/54/45 72/79/62 73/82/65
+f 48/56/46 67/80/63 68/83/66
+f 43/58/47 62/81/64 63/84/67
+f 54/60/48 73/82/65 74/85/68
+f 49/62/49 68/83/66 69/86/69
+f 44/64/50 63/84/67 64/87/70
+f 55/66/51 74/85/68 75/88/71
+f 50/68/52 69/86/69 70/89/72
+f 45/70/53 64/87/70 65/90/73
+f 40/91/74 59/110/93 60/92/75
+f 75/88/71 94/107/90 95/93/76
+f 70/89/72 89/108/91 90/94/77
+f 65/90/73 84/109/92 85/95/78
+f 60/92/75 79/111/94 80/96/79
+f 76/74/57 95/93/76 96/97/80
+f 71/75/58 90/94/77 91/98/81
+f 66/76/59 85/95/78 86/99/82
+f 61/77/60 80/96/79 81/100/83
+f 72/79/62 91/98/81 92/101/84
+f 67/80/63 86/99/82 87/102/85
+f 62/81/64 81/100/83 82/103/86
+f 73/82/65 92/101/84 93/104/87
+f 68/83/66 87/102/85 88/105/88
+f 63/84/67 82/103/86 83/106/89
+f 74/85/68 93/104/87 94/107/90
+f 69/86/69 88/105/88 89/108/91
+f 64/87/70 83/106/89 84/109/92
+f 59/110/93 78/128/111 79/111/94
+f 89/108/91 108/126/109 109/112/95
+f 84/109/92 103/127/110 104/113/96
+f 79/111/94 98/129/112 99/114/97
+f 95/93/76 114/130/113 115/115/98
+f 90/94/77 109/112/95 110/116/99
+f 85/95/78 104/113/96 105/117/100
+f 80/96/79 99/114/97 100/118/101
+f 91/98/81 110/116/99 111/119/102
+f 86/99/82 105/117/100 106/120/103
+f 81/100/83 100/118/101 101/121/104
+f 92/101/84 111/119/102 112/122/105
+f 87/102/85 106/120/103 107/123/106
+f 82/103/86 101/121/104 102/124/107
+f 93/104/87 112/122/105 113/125/108
+f 88/105/88 107/123/106 108/126/109
+f 83/106/89 102/124/107 103/127/110
+f 78/128/111 97/145/128 98/129/112
+f 94/107/90 113/125/108 114/130/113
+f 118/135/118 99/114/97 117/131/114
+f 134/150/133 115/115/98 133/132/115
+f 129/136/119 110/116/99 128/133/116
+f 124/137/120 105/117/100 123/134/117
+f 119/138/121 100/118/101 118/135/118
+f 130/139/122 111/119/102 129/136/119
+f 125/140/123 106/120/103 124/137/120
+f 120/141/124 101/121/104 119/138/121
+f 131/142/125 112/122/105 130/139/122
+f 126/143/126 107/123/106 125/140/123
+f 121/144/127 102/124/107 120/141/124
+f 132/147/130 113/125/108 131/142/125
+f 127/148/131 108/126/109 126/143/126
+f 122/149/132 103/127/110 121/144/127
+f 117/131/114 98/129/112 116/146/129
+f 133/132/115 114/130/113 132/147/130
+f 128/133/116 109/112/95 127/148/131
+f 123/134/117 104/113/96 122/149/132
+f 153/169/152 134/150/133 152/151/134
+f 148/155/138 129/136/119 147/152/135
+f 143/156/139 124/137/120 142/153/136
+f 138/157/140 119/138/121 137/154/137
+f 149/158/141 130/139/122 148/155/138
+f 144/159/142 125/140/123 143/156/139
+f 139/160/143 120/141/124 138/157/140
+f 150/161/144 131/142/125 149/158/141
+f 145/162/145 126/143/126 144/159/142
+f 140/163/146 121/144/127 139/160/143
+f 151/165/148 132/147/130 150/161/144
+f 146/166/149 127/148/131 145/162/145
+f 141/167/150 122/149/132 140/163/146
+f 136/168/151 117/131/114 135/164/147
+f 152/151/134 133/132/115 151/165/148
+f 147/152/135 128/133/116 146/166/149
+f 142/153/136 123/134/117 141/167/150
+f 137/154/137 118/135/118 136/168/151
+f 172/203/186 153/169/152 171/170/153
+f 167/174/157 148/155/138 166/171/154
+f 162/175/158 143/156/139 161/172/155
+f 157/176/159 138/157/140 156/173/156
+f 168/177/160 149/158/141 167/174/157
+f 163/178/161 144/159/142 162/175/158
+f 158/179/162 139/160/143 157/176/159
+f 169/180/163 150/161/144 168/177/160
+f 164/181/164 145/162/145 163/178/161
+f 159/194/177 140/163/146 158/179/162
+f 170/184/167 151/165/148 169/180/163
+f 165/185/168 146/166/149 164/181/164
+f 140/163/146 159/194/177 160/182/165
+f 155/186/169 136/168/151 154/183/166
+f 171/170/153 152/151/134 170/184/167
+f 166/171/154 147/152/135 165/185/168
+f 161/172/155 142/153/136 160/182/165
+f 156/173/156 137/154/137 155/186/169
+f 186/207/190 167/174/157 185/187/170
+f 181/191/174 162/175/158 180/188/171
+f 176/192/175 157/176/159 175/189/172
+f 167/174/157 186/207/190 187/190/173
+f 182/193/176 163/178/161 181/191/174
+f 177/195/178 158/179/162 176/192/175
+f 188/196/179 169/180/163 187/190/173
+f 183/197/180 164/181/164 182/193/176
+f 178/212/195 159/194/177 177/195/178
+f 189/200/183 170/184/167 188/196/179
+f 184/201/184 165/185/168 183/197/180
+f 159/194/177 178/212/195 179/198/181
+f 174/202/185 155/186/169 173/199/182
+f 190/204/187 171/170/153 189/200/183
+f 185/187/170 166/171/154 184/201/184
+f 180/188/171 161/172/155 179/198/181
+f 175/189/172 156/173/156 174/202/185
+f 191/221/204 172/203/186 190/204/187
+f 200/209/192 181/191/174 199/205/188
+f 195/210/193 176/192/175 194/206/189
+f 186/207/190 205/224/207 206/208/191
+f 201/211/194 182/193/176 200/209/192
+f 196/213/196 177/195/178 195/210/193
+f 207/214/197 188/196/179 206/208/191
+f 202/215/198 183/197/180 201/211/194
+f 197/229/212 178/212/195 196/213/196
+f 208/218/201 189/200/183 207/214/197
+f 203/219/202 184/201/184 202/215/198
+f 178/212/195 197/229/212 198/216/199
+f 193/220/203 174/202/185 192/217/200
+f 209/222/205 190/204/187 208/218/201
+f 204/223/206 185/187/170 203/219/202
+f 199/205/188 180/188/171 198/216/199
+f 194/206/189 175/189/172 193/220/203
+f 210/238/221 191/221/204 209/222/205
+f 205/224/207 186/207/190 204/223/206
+f 205/224/207 224/243/226 225/225/208
+f 220/228/211 201/211/194 219/226/209
+f 215/230/213 196/213/196 214/227/210
+f 226/231/214 207/214/197 225/225/208
+f 221/232/215 202/215/198 220/228/211
+f 216/248/231 197/229/212 215/230/213
+f 227/235/218 208/218/201 226/231/214
+f 222/236/219 203/219/202 221/232/215
+f 197/229/212 216/248/231 217/233/216
+f 212/237/220 193/220/203 211/234/217
+f 228/239/222 209/222/205 227/235/218
+f 223/240/223 204/223/206 222/236/219
+f 218/241/224 199/205/188 217/233/216
+f 213/242/225 194/206/189 212/237/220
+f 229/257/240 210/238/221 228/239/222
+f 224/243/226 205/224/207 223/240/223
+f 219/226/209 200/209/192 218/241/224
+f 214/227/210 195/210/193 213/242/225
+f 224/243/226 243/262/245 244/244/227
+f 239/247/230 220/228/211 238/245/228
+f 234/249/232 215/230/213 233/246/229
+f 245/250/233 226/231/214 244/244/227
+f 240/251/234 221/232/215 239/247/230
+f 235/267/250 216/248/231 234/249/232
+f 246/269/237 227/235/218 245/250/233
+f 241/255/238 222/236/219 240/251/234
+f 216/248/231 235/267/250 236/252/235
+f 231/256/239 212/237/220 230/253/236
+f 247/258/241 228/239/222 246/254/237
+f 242/259/242 223/240/223 241/255/238
+f 237/260/243 218/241/224 236/252/235
+f 232/261/244 213/242/225 231/256/239
+f 248/401/259 229/257/240 247/258/241
+f 243/262/245 224/243/226 242/259/242
+f 238/245/228 219/226/209 237/260/243
+f 233/246/229 214/227/210 232/261/244
+f 243/262/245 262/298/279 263/263/246
+f 258/266/249 239/247/230 257/264/247
+f 253/268/251 234/249/232 252/265/248
+f 264/270/252 245/250/233 263/263/246
+f 259/271/253 240/251/234 258/266/249
+f 254/287/268 235/267/250 253/268/251
+f 265/275/256 246/269/237 264/270/252
+f 260/276/257 241/255/238 259/271/253
+f 235/267/250 254/287/268 255/272/254
+f 250/277/258 231/256/239 249/273/255
+f 266/279/260 247/274/241 265/275/256
+f 261/280/261 242/259/242 260/276/257
+f 256/281/262 237/260/243 255/272/254
+f 251/282/263 232/261/244 250/277/258
+f 267/296/277 248/278/259 266/279/260
+f 262/298/279 243/262/245 261/280/261
+f 257/264/247 238/245/228 256/281/262
+f 252/265/248 233/246/229 251/282/263
+f 277/286/267 258/266/249 276/283/264
+f 272/288/269 253/268/251 271/284/265
+f 283/289/270 264/270/252 282/285/266
+f 278/290/271 259/271/253 277/286/267
+f 273/306/287 254/287/268 272/288/269
+f 284/293/274 265/275/256 283/289/270
+f 279/294/275 260/276/257 278/290/271
+f 254/287/268 273/306/287 274/291/272
+f 269/295/276 250/277/258 268/292/273
+f 285/297/278 266/279/260 284/293/274
+f 280/299/280 261/280/261 279/294/275
+f 275/300/281 256/281/262 274/291/272
+f 270/301/282 251/282/263 269/295/276
+f 286/315/296 267/296/277 285/297/278
+f 281/302/283 262/298/279 280/299/280
+f 276/283/264 257/264/247 275/300/281
+f 271/284/265 252/265/248 270/301/282
+f 282/285/266 263/263/246 281/302/283
+f 291/307/288 272/288/269 290/303/284
+f 302/308/289 283/289/270 301/304/285
+f 297/309/290 278/290/271 296/305/286
+f 292/324/305 273/306/287 291/307/288
+f 303/312/293 284/293/274 302/308/289
+f 298/313/294 279/294/275 297/309/290
+f 273/306/287 292/324/305 293/310/291
+f 288/314/295 269/295/276 287/311/292
+f 304/316/297 285/297/278 303/312/293
+f 299/317/298 280/299/280 298/313/294
+f 294/318/299 275/300/281 293/310/291
+f 289/319/300 270/301/282 288/314/295
+f 305/333/314 286/315/296 304/316/297
+f 300/320/301 281/302/283 299/317/298
+f 295/321/302 276/283/264 294/318/299
+f 290/303/284 271/284/265 289/319/300
+f 301/304/285 282/285/266 300/320/301
+f 296/305/286 277/286/267 295/321/302
+f 301/304/285 320/338/319 321/322/303
+f 296/305/286 315/339/320 316/323/304
+f 291/307/288 310/340/321 311/325/306
+f 302/308/289 321/322/303 322/326/307
+f 297/309/290 316/323/304 317/327/308
+f 312/346/327 293/310/291 311/325/306
+f 287/311/292 306/347/328 307/328/309
+f 303/312/293 322/326/307 323/329/310
+f 298/313/294 317/327/308 318/330/311
+f 293/310/291 312/346/327 313/331/312
+f 288/314/295 307/328/309 308/332/313
+f 304/316/297 323/329/310 324/334/315
+f 299/317/298 318/330/311 319/335/316
+f 294/318/299 313/331/312 314/336/317
+f 289/319/300 308/332/313 309/337/318
+f 300/320/301 319/335/316 320/338/319
+f 295/321/302 314/336/317 315/339/320
+f 290/303/284 309/337/318 310/340/321
+f 320/338/319 339/357/338 340/341/322
+f 315/339/320 334/358/339 335/342/323
+f 310/340/321 329/359/340 330/343/324
+f 321/322/303 340/341/322 341/344/325
+f 316/323/304 335/342/323 336/345/326
+f 331/365/346 312/346/327 330/343/324
+f 306/347/328 325/366/347 326/348/329
+f 322/326/307 341/344/325 342/349/330
+f 317/327/308 336/345/326 337/350/331
+f 312/346/327 331/365/346 332/351/332
+f 307/328/309 326/348/329 327/352/333
+f 323/329/310 342/349/330 343/353/334
+f 318/330/311 337/350/331 338/354/335
+f 313/331/312 332/351/332 333/355/336
+f 308/332/313 327/352/333 328/356/337
+f 319/335/316 338/354/335 339/357/338
+f 314/336/317 333/355/336 334/358/339
+f 309/337/318 328/356/337 329/359/340
+f 339/357/338 359/376/357 360/360/341
+f 334/358/339 354/377/358 355/361/342
+f 329/359/340 349/378/359 350/362/343
+f 340/341/322 360/360/341 361/363/344
+f 335/342/323 355/361/342 356/364/345
+f 351/382/363 331/365/346 350/362/343
+f 325/366/347 345/383/364 346/367/348
+f 341/344/325 361/363/344 362/368/349
+f 336/345/326 356/364/345 357/369/350
+f 331/365/346 351/382/363 352/370/351
+f 326/348/329 346/367/348 347/371/352
+f 342/349/330 362/368/349 363/372/353
+f 337/350/331 357/369/350 358/373/354
+f 332/351/332 352/370/351 353/374/355
+f 327/352/333 347/371/352 348/375/356
+f 338/354/335 358/373/354 359/376/357
+f 333/355/336 353/374/355 354/377/358
+f 328/356/337 348/375/356 349/378/359
+f 349/378/359 368/395/376 369/379/360
+f 360/360/341 379/396/377 380/380/361
+f 355/361/342 374/397/378 375/381/362
+f 370/410/382 351/382/363 369/379/360
+f 345/383/364 364/408/381 365/384/365
+f 361/363/344 380/380/361 381/385/366
+f 356/364/345 375/381/362 376/386/367
+f 351/382/363 370/410/382 371/387/368
+f 346/367/348 365/384/365 366/388/369
+f 362/368/349 381/385/366 382/389/370
+f 357/369/350 376/386/367 377/390/371
+f 352/370/351 371/387/368 372/391/372
+f 347/371/352 366/388/369 367/392/373
+f 358/373/354 377/390/371 378/393/374
+f 353/374/355 372/391/372 373/394/375
+f 348/375/356 367/392/373 368/395/376
+f 359/376/357 378/393/374 379/396/377
+f 354/377/358 373/394/375 374/397/378
+f 17/1/1 380/380/361 16/2/2
+f 374/397/378 11/5/5 12/4/4
+f 369/379/360 6/8/8 7/7/7
+f 364/408/381 1/11/11 2/10/10
+f 18/13/13 381/385/366 17/1/1
+f 375/381/362 12/4/4 13/15/15
+f 370/410/382 7/7/7 8/17/17
+f 365/384/365 2/10/10 3/19/19
+f 19/21/21 382/389/370 18/13/13
+f 14/23/23 377/390/371 13/15/15
+f 371/387/368 8/17/17 9/25/25
+f 366/388/369 3/19/19 4/27/27
+f 15/29/29 378/393/374 14/23/23
+f 372/391/372 9/25/25 10/31/31
+f 367/392/373 4/27/27 5/33/33
+f 16/2/2 379/396/377 15/29/29
+f 373/394/375 10/31/31 11/5/5
+f 368/395/376 5/33/33 6/8/8
diff --git a/QtDataVisualization/Data/mesh/narrowarrow.obj b/QtDataVisualization/Data/mesh/narrowarrow.obj
new file mode 100644
index 0000000000000000000000000000000000000000..481fc1d1ab76d97e83498529cd66b135c6436631
--- /dev/null
+++ b/QtDataVisualization/Data/mesh/narrowarrow.obj
@@ -0,0 +1,413 @@
+# Blender v2.69 (sub 0) OBJ File: ''
+# www.blender.org
+v 0.000000 0.986570 0.000000
+v 0.000000 0.500000 -0.218399
+v -0.042608 0.500000 -0.214202
+v -0.083578 0.500000 -0.201774
+v -0.121336 0.500000 -0.181592
+v -0.154431 0.500000 -0.154431
+v -0.181592 0.500000 -0.121336
+v -0.201774 0.500000 -0.083578
+v -0.214202 0.500000 -0.042608
+v -0.218399 0.500000 -0.000000
+v -0.214202 0.500000 0.042607
+v -0.201774 0.500000 0.083578
+v -0.181592 0.500000 0.121336
+v -0.154431 0.500000 0.154431
+v -0.121336 0.500000 0.181592
+v -0.083578 0.500000 0.201774
+v -0.042607 0.500000 0.214202
+v 0.000000 0.500000 0.218399
+v 0.042608 0.500000 0.214202
+v 0.083578 0.500000 0.201774
+v 0.121336 0.500000 0.181592
+v 0.154431 0.500000 0.154431
+v 0.181592 0.500000 0.121336
+v 0.201774 0.500000 0.083577
+v 0.214202 0.500000 0.042607
+v 0.218399 0.500000 -0.000000
+v 0.214202 0.500000 -0.042608
+v 0.201774 0.500000 -0.083578
+v 0.181592 0.500000 -0.121336
+v 0.154431 0.500000 -0.154432
+v 0.121336 0.500000 -0.181592
+v 0.083577 0.500000 -0.201774
+v 0.042607 0.500000 -0.214202
+v 0.000000 0.000000 0.000000
+v 0.000000 0.000000 0.000000
+v -0.000000 0.500000 -0.126422
+v -0.024664 0.500000 -0.123993
+v -0.048380 0.500000 -0.116799
+v -0.070236 0.500000 -0.105116
+v -0.089394 0.500000 -0.089394
+v -0.105116 0.500000 -0.070236
+v -0.116799 0.500000 -0.048380
+v -0.123993 0.500000 -0.024664
+v -0.126422 0.500000 -0.000000
+v -0.123993 0.500000 0.024664
+v -0.116799 0.500000 0.048380
+v -0.105116 0.500000 0.070236
+v -0.089394 0.500000 0.089394
+v -0.070236 0.500000 0.105116
+v -0.048380 0.500000 0.116799
+v -0.024664 0.500000 0.123993
+v 0.000000 0.500000 0.126422
+v 0.024664 0.500000 0.123993
+v 0.048380 0.500000 0.116799
+v 0.070236 0.500000 0.105116
+v 0.089394 0.500000 0.089394
+v 0.105116 0.500000 0.070236
+v 0.116799 0.500000 0.048380
+v 0.123993 0.500000 0.024664
+v 0.126422 0.500000 -0.000000
+v 0.123993 0.500000 -0.024664
+v 0.116799 0.500000 -0.048380
+v 0.105116 0.500000 -0.070237
+v 0.089394 0.500000 -0.089394
+v 0.070236 0.500000 -0.105116
+v 0.048380 0.500000 -0.116799
+v 0.024664 0.500000 -0.123993
+v 0.126422 -0.983070 -0.000000
+v 0.123993 -0.983070 0.024664
+v -0.048380 -0.983070 -0.116799
+v -0.024664 -0.983070 -0.123993
+v -0.070236 -0.983070 0.105116
+v -0.089394 -0.983070 0.089394
+v 0.116799 -0.983070 0.048380
+v 0.105116 -0.983070 0.070236
+v -0.105116 -0.983070 0.070236
+v -0.116799 -0.983070 0.048380
+v 0.024664 -0.983070 -0.123993
+v 0.048380 -0.983070 -0.116799
+v 0.089394 -0.983070 0.089394
+v 0.070236 -0.983070 0.105116
+v -0.123993 -0.983070 0.024664
+v -0.126422 -0.983070 -0.000000
+v 0.070236 -0.983070 -0.105116
+v 0.089394 -0.983070 -0.089394
+v 0.048380 -0.983070 0.116799
+v 0.024664 -0.983070 0.123993
+v -0.123993 -0.983070 -0.024664
+v -0.116799 -0.983070 -0.048380
+v 0.105116 -0.983070 -0.070237
+v 0.116799 -0.983070 -0.048380
+v -0.105116 -0.983070 -0.070236
+v -0.089394 -0.983070 -0.089394
+v 0.000000 -0.983070 0.126422
+v -0.024664 -0.983070 0.123993
+v 0.123993 -0.983070 -0.024664
+v -0.070236 -0.983070 -0.105116
+v -0.048380 -0.983070 0.116799
+v -0.000000 -0.983070 -0.126422
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.500000 1.000000
+vt 0.597545 0.990393
+vt 0.402456 0.990393
+vt 0.691342 0.961940
+vt 0.777785 0.915735
+vt 0.853553 0.853553
+vt 0.915735 0.777785
+vt 0.961940 0.691342
+vt 0.990393 0.597545
+vt 1.000000 0.500000
+vt 0.990393 0.402455
+vt 0.961940 0.308658
+vt 0.915735 0.222215
+vt 0.853553 0.146447
+vt 0.777785 0.084265
+vt 0.691342 0.038060
+vt 0.597545 0.009607
+vt 0.308659 0.961940
+vt 0.222215 0.915735
+vt 0.146447 0.853554
+vt 0.308658 0.038060
+vt 0.500000 0.000000
+vt 0.402455 0.009607
+vt 0.222215 0.084265
+vt 0.146446 0.146447
+vt 0.084265 0.222215
+vt 0.009607 0.402455
+vt 0.038060 0.308659
+vt 0.009607 0.597546
+vt 0.000000 0.500000
+vt 0.038060 0.691342
+vt 0.084266 0.777786
+vn -0.089495 0.407852 -0.908651
+vn -0.265044 0.407852 -0.873733
+vn -0.430408 0.407852 -0.805236
+vn -0.579231 0.407852 -0.705796
+vn -0.705796 0.407852 -0.579232
+vn -0.805237 0.407852 -0.430408
+vn -0.873733 0.407852 -0.265043
+vn -0.908651 0.407852 -0.089495
+vn -0.908652 0.407852 0.089494
+vn -0.873732 0.407852 0.265044
+vn -0.805237 0.407852 0.430407
+vn -0.705796 0.407852 0.579232
+vn -0.579231 0.407852 0.705796
+vn -0.430408 0.407852 0.805237
+vn -0.265044 0.407852 0.873733
+vn -0.089494 0.407852 0.908652
+vn 0.089495 0.407852 0.908651
+vn 0.265044 0.407852 0.873732
+vn 0.430408 0.407852 0.805237
+vn 0.579232 0.407852 0.705796
+vn 0.705796 0.407852 0.579231
+vn 0.805237 0.407852 0.430406
+vn 0.873733 0.407852 0.265044
+vn 0.908652 0.407852 0.089493
+vn 0.908651 0.407852 -0.089496
+vn 0.873732 0.407852 -0.265045
+vn 0.805236 0.407852 -0.430409
+vn 0.705795 0.407852 -0.579232
+vn 0.579231 0.407852 -0.705796
+vn 0.430407 0.407852 -0.805237
+vn 0.265042 0.407852 -0.873733
+vn 0.089494 0.407852 -0.908652
+vn 0.000000 -1.000000 0.000000
+vn 0.000000 -1.000000 0.000062
+vn -0.290284 0.000000 0.956940
+vn -0.098017 0.000000 0.995185
+vn -0.634393 0.000000 -0.773010
+vn -0.773010 0.000000 -0.634393
+vn 0.956940 0.000000 -0.290286
+vn 0.881921 0.000000 -0.471398
+vn -0.881922 0.000000 -0.471396
+vn -0.956940 0.000000 -0.290285
+vn 0.098017 0.000000 0.995185
+vn 0.290285 0.000000 0.956940
+vn 0.773010 0.000000 -0.634394
+vn 0.634392 0.000000 -0.773011
+vn -0.995185 0.000000 -0.098017
+vn -0.995185 0.000000 0.098017
+vn 0.471397 0.000000 0.881921
+vn 0.634394 0.000000 0.773010
+vn 0.471395 0.000000 -0.881922
+vn 0.290283 0.000000 -0.956941
+vn 0.098016 0.000000 -0.995185
+vn -0.956940 0.000000 0.290285
+vn -0.881922 0.000000 0.471396
+vn 0.773011 0.000000 0.634393
+vn 0.881922 0.000000 0.471396
+vn -0.773010 0.000000 0.634393
+vn -0.634394 0.000000 0.773010
+vn -0.471397 0.000000 0.881921
+vn -0.098016 0.000000 -0.995185
+vn -0.290284 0.000000 -0.956940
+vn -0.471397 0.000000 -0.881921
+vn 0.956941 0.000000 0.290284
+vn 0.995185 0.000000 0.098016
+vn 0.995185 0.000000 -0.098018
+vn 0.000000 -1.000000 0.000001
+vn 0.000000 -1.000000 0.000018
+vn 0.000000 -1.000000 -0.000013
+vn 0.000000 -1.000000 0.000011
+vn 0.000000 -1.000000 0.000002
+vn 0.000000 -1.000000 -0.000031
+vn 0.000000 -1.000000 0.000031
+vn 0.000000 -1.000000 -0.000021
+vn 0.000000 -1.000000 -0.000016
+vn 0.000000 -1.000000 -0.000005
+vn 0.000000 -1.000000 0.000003
+vn -0.098015 0.000000 0.995185
+vn -0.773011 0.000000 -0.634393
+vn -0.881921 0.000000 -0.471396
+vn 0.773009 0.000000 -0.634394
+vn 0.098017 0.000000 -0.995185
+vn -0.634393 0.000000 0.773011
+vn -0.098018 0.000000 -0.995185
+vn -0.290285 0.000000 -0.956940
+s off
+f 1/1/1 2/2/1 3/3/1
+f 1/1/2 3/2/2 4/3/2
+f 1/1/3 4/2/3 5/3/3
+f 1/1/4 5/2/4 6/3/4
+f 1/1/5 6/2/5 7/3/5
+f 1/1/6 7/2/6 8/3/6
+f 1/1/7 8/2/7 9/3/7
+f 1/1/8 9/2/8 10/3/8
+f 1/1/9 10/2/9 11/3/9
+f 1/1/10 11/2/10 12/3/10
+f 1/1/11 12/2/11 13/3/11
+f 1/1/12 13/2/12 14/3/12
+f 1/1/13 14/2/13 15/3/13
+f 1/1/14 15/2/14 16/3/14
+f 1/1/15 16/2/15 17/3/15
+f 1/1/16 17/2/16 18/3/16
+f 1/1/17 18/2/17 19/3/17
+f 1/1/18 19/2/18 20/3/18
+f 1/1/19 20/2/19 21/3/19
+f 1/1/20 21/2/20 22/3/20
+f 1/1/21 22/2/21 23/3/21
+f 1/1/22 23/2/22 24/3/22
+f 1/1/23 24/2/23 25/3/23
+f 1/1/24 25/2/24 26/3/24
+f 1/1/25 26/2/25 27/3/25
+f 1/1/26 27/2/26 28/3/26
+f 1/1/27 28/2/27 29/3/27
+f 1/1/28 29/2/28 30/3/28
+f 1/1/29 30/2/29 31/3/29
+f 1/1/30 31/2/30 32/3/30
+f 1/1/31 32/2/31 33/3/31
+f 1/1/32 33/2/32 2/3/32
+f 24/1/33 23/2/33 58/4/33
+f 13/1/33 12/2/33 47/4/33
+f 2/1/33 33/2/33 36/4/33
+f 23/1/33 22/2/33 57/4/33
+f 12/1/33 11/2/33 46/4/33
+f 33/1/33 32/2/33 67/4/33
+f 22/1/33 21/2/33 56/4/33
+f 11/1/33 10/2/33 45/4/33
+f 32/1/33 31/2/33 66/4/33
+f 21/1/33 20/2/33 55/4/33
+f 10/1/33 9/2/33 44/4/33
+f 31/1/33 30/2/33 65/4/33
+f 20/1/33 19/2/33 54/4/33
+f 9/1/33 8/2/33 43/4/33
+f 30/1/33 29/2/33 64/4/33
+f 19/1/33 18/2/33 53/4/33
+f 3/1/33 2/2/33 37/4/33
+f 8/1/33 7/2/33 42/4/33
+f 29/1/33 28/2/33 63/4/33
+f 7/1/33 6/2/33 41/4/33
+f 18/1/33 17/2/33 52/4/33
+f 28/1/33 27/2/33 62/4/33
+f 6/1/33 5/2/33 40/4/33
+f 17/1/33 16/2/33 51/4/33
+f 27/1/33 26/2/33 61/4/33
+f 5/1/33 4/2/33 39/4/33
+f 16/1/33 15/2/33 50/4/33
+f 26/1/33 25/2/33 60/4/33
+f 4/1/33 3/2/33 38/4/33
+f 15/1/33 14/2/33 49/4/33
+f 25/1/33 24/2/33 59/4/33
+f 14/1/33 13/2/33 48/4/33
+f 81/5/34 86/6/34 80/7/34
+f 51/1/35 50/2/35 95/4/35
+f 52/1/36 51/2/36 94/4/36
+f 40/1/37 39/2/37 93/4/37
+f 41/1/38 40/2/38 92/4/38
+f 62/1/39 61/2/39 91/4/39
+f 63/1/40 62/2/40 90/4/40
+f 42/1/41 41/2/41 89/4/41
+f 43/1/42 42/2/42 88/4/42
+f 53/1/43 52/2/43 87/4/43
+f 54/1/44 53/2/44 86/4/44
+f 64/1/45 63/2/45 85/4/45
+f 65/1/46 64/2/46 84/4/46
+f 44/1/47 43/2/47 83/4/47
+f 45/1/48 44/2/48 82/4/48
+f 55/1/49 54/2/49 81/4/49
+f 56/1/50 55/2/50 80/4/50
+f 66/1/51 65/2/51 79/4/51
+f 67/1/52 66/2/52 78/4/52
+f 36/1/53 67/2/53 99/4/53
+f 46/1/54 45/2/54 77/4/54
+f 47/1/55 46/2/55 76/4/55
+f 57/1/56 56/2/56 75/4/56
+f 58/1/57 57/2/57 74/4/57
+f 48/1/58 47/2/58 73/4/58
+f 49/1/59 48/2/59 72/4/59
+f 50/1/60 49/2/60 98/4/60
+f 37/1/61 36/2/61 71/4/61
+f 38/1/62 37/2/62 70/4/62
+f 39/1/63 38/2/63 97/4/63
+f 59/1/64 58/2/64 69/4/64
+f 60/1/65 59/2/65 68/4/65
+f 61/1/66 60/2/66 96/4/66
+f 23/2/33 57/3/33 58/4/33
+f 12/2/33 46/3/33 47/4/33
+f 33/2/33 67/3/33 36/4/33
+f 22/2/33 56/3/33 57/4/33
+f 11/2/33 45/3/33 46/4/33
+f 32/2/67 66/3/67 67/4/67
+f 21/2/33 55/3/33 56/4/33
+f 10/2/33 44/3/33 45/4/33
+f 31/2/33 65/3/33 66/4/33
+f 20/2/33 54/3/33 55/4/33
+f 9/2/33 43/3/33 44/4/33
+f 30/2/33 64/3/33 65/4/33
+f 19/2/33 53/3/33 54/4/33
+f 8/2/33 42/3/33 43/4/33
+f 29/2/33 63/3/33 64/4/33
+f 18/2/33 52/3/33 53/4/33
+f 2/2/33 36/3/33 37/4/33
+f 7/2/33 41/3/33 42/4/33
+f 28/2/33 62/3/33 63/4/33
+f 6/2/33 40/3/33 41/4/33
+f 17/2/33 51/3/33 52/4/33
+f 27/2/33 61/3/33 62/4/33
+f 5/2/33 39/3/33 40/4/33
+f 16/2/67 50/3/67 51/4/67
+f 26/2/33 60/3/33 61/4/33
+f 4/2/33 38/3/33 39/4/33
+f 15/2/33 49/3/33 50/4/33
+f 25/2/33 59/3/33 60/4/33
+f 3/2/33 37/3/33 38/4/33
+f 14/2/33 48/3/33 49/4/33
+f 24/2/33 58/3/33 59/4/33
+f 13/2/33 47/3/33 48/4/33
+f 86/6/33 87/8/33 80/7/33
+f 87/8/33 94/9/33 80/7/33
+f 94/9/33 95/10/33 80/7/33
+f 95/10/68 98/11/68 80/7/68
+f 98/11/69 72/12/69 80/7/69
+f 72/12/70 73/13/70 80/7/70
+f 73/13/33 76/14/33 80/7/33
+f 76/14/33 77/15/33 80/7/33
+f 77/15/33 82/16/33 80/7/33
+f 82/16/33 83/17/33 80/7/33
+f 83/17/33 88/18/33 80/7/33
+f 88/18/33 89/19/33 80/7/33
+f 89/19/33 92/20/33 93/21/33
+f 80/7/71 89/19/71 93/21/71
+f 75/22/33 80/7/33 93/21/33
+f 74/23/33 75/22/33 93/21/33
+f 69/24/33 74/23/33 93/21/33
+f 71/25/72 97/26/72 70/27/72
+f 71/25/73 93/21/73 97/26/73
+f 99/28/74 93/21/74 71/25/74
+f 78/29/33 93/21/33 99/28/33
+f 79/30/33 93/21/33 78/29/33
+f 85/31/73 79/30/73 84/32/73
+f 85/31/33 93/21/33 79/30/33
+f 91/33/75 85/31/75 90/34/75
+f 91/33/33 93/21/33 85/31/33
+f 96/35/33 68/36/33 91/33/33
+f 68/36/76 69/24/76 93/21/76
+f 91/33/77 68/36/77 93/21/77
+f 50/2/35 98/3/35 95/4/35
+f 51/2/78 95/3/78 94/4/78
+f 39/2/37 97/3/37 93/4/37
+f 40/2/79 93/3/79 92/4/79
+f 61/2/39 96/3/39 91/4/39
+f 62/2/40 91/3/40 90/4/40
+f 41/2/80 92/3/80 89/4/80
+f 42/2/42 89/3/42 88/4/42
+f 52/2/43 94/3/43 87/4/43
+f 53/2/44 87/3/44 86/4/44
+f 63/2/81 90/3/81 85/4/81
+f 64/2/46 85/3/46 84/4/46
+f 43/2/47 88/3/47 83/4/47
+f 44/2/48 83/3/48 82/4/48
+f 54/2/49 86/3/49 81/4/49
+f 55/2/50 81/3/50 80/4/50
+f 65/2/51 84/3/51 79/4/51
+f 66/2/52 79/3/52 78/4/52
+f 67/2/82 78/3/82 99/4/82
+f 45/2/54 82/3/54 77/4/54
+f 46/2/55 77/3/55 76/4/55
+f 56/2/56 80/3/56 75/4/56
+f 57/2/57 75/3/57 74/4/57
+f 47/2/58 76/3/58 73/4/58
+f 48/2/83 73/3/83 72/4/83
+f 49/2/60 72/3/60 98/4/60
+f 36/2/84 99/3/84 71/4/84
+f 37/2/85 71/3/85 70/4/85
+f 38/2/63 70/3/63 97/4/63
+f 58/2/64 74/3/64 69/4/64
+f 59/2/65 69/3/65 68/4/65
+f 60/2/66 68/3/66 96/4/66
diff --git a/QtDataVisualization/MagneticOfSun.py b/QtDataVisualization/MagneticOfSun.py
new file mode 100644
index 0000000000000000000000000000000000000000..da9fa0d1589a3b58e0ea75fc40aa3a6f229150ab
--- /dev/null
+++ b/QtDataVisualization/MagneticOfSun.py
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/4
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: MagneticOfSun
+@description:
+"""
+
+#############################################################################
+##
+## Copyright (C) 2014 Riverbank Computing Limited.
+## Copyright (C) 2014 Digia Plc
+## All rights reserved.
+## For any questions to Digia, please use contact form at http://qt.digia.com
+##
+## This file is part of the QtDataVisualization module.
+##
+## Licensees holding valid Qt Enterprise licenses may use this file in
+## accordance with the Qt Enterprise License Agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and Digia.
+##
+## If you have questions regarding the use of this file, please use
+## contact form at http://qt.digia.com
+##
+#############################################################################
+
+
+import math
+
+from PyQt5.QtCore import QFileInfo, QObject, QSize, Qt, QTimer, QLocale
+from PyQt5.QtDataVisualization import (Q3DCamera, Q3DScatter, Q3DTheme,
+ QAbstract3DGraph, QAbstract3DSeries, QScatter3DSeries,
+ QScatterDataItem)
+from PyQt5.QtGui import QColor, QLinearGradient, QQuaternion, QVector3D
+from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QLabel, QPushButton,
+ QSlider, QSizePolicy, QVBoxLayout, QWidget)
+
+country = QLocale.system().country()
+if country in (QLocale.China, QLocale.HongKong, QLocale.Taiwan):
+ Tr = {
+ 'Item rotations example - Magnetic field of the sun': '项目旋转示例-太阳磁场',
+ 'Toggle animation': '开启/关闭 动画',
+ 'Toggle Sun': '显示/隐藏 太阳',
+ 'Field Lines (1 - 128):': '磁场线条数(1 - 128):',
+ 'Arrows per line (8 - 32):': '箭头数(8 - 32):'
+ }
+else:
+ Tr = {}
+
+
+class ScatterDataModifier(QObject):
+ verticalRange = 8.0
+ horizontalRange = verticalRange
+ ellipse_a = horizontalRange / 3.0
+ ellipse_b = verticalRange
+ doublePi = math.pi * 2.0
+ radiansToDegrees = 360.0 / doublePi
+ animationFrames = 30.0 # 动画帧数
+
+ def __init__(self, scatter):
+ super(ScatterDataModifier, self).__init__()
+
+ mesh_dir = QFileInfo(__file__).absolutePath() + '/Data/mesh'
+
+ self.m_graph = scatter # Q3DScatter 对象实例
+ self.m_rotationTimer = QTimer()
+ self.m_fieldLines = 12 # 初始磁场线数量
+ self.m_arrowsPerLine = 16 # 初始箭头数
+ self.m_magneticField = QScatter3DSeries() # 磁场线三维散点图
+ self.m_sun = QScatter3DSeries() # 太阳三维散点图
+ self.m_angleOffset = 0.0 # 角度偏移
+ self.m_angleStep = self.doublePi / self.m_arrowsPerLine / self.animationFrames
+
+ # 设置阴影质量
+ self.m_graph.setShadowQuality(QAbstract3DGraph.ShadowQualityNone)
+ # 设置当前场景中的激活的相机预设值
+ self.m_graph.scene().activeCamera().setCameraPreset(
+ Q3DCamera.CameraPresetFront)
+
+ # Magnetic field lines use custom narrow arrow.
+ # 磁力线使用自定义窄箭头。
+ self.m_magneticField.setItemSize(0.2)
+ self.m_magneticField.setMesh(QAbstract3DSeries.MeshUserDefined)
+ self.m_magneticField.setUserDefinedMesh(mesh_dir + '/narrowarrow.obj')
+ # 设置渐变颜色
+ fieldGradient = QLinearGradient(0, 0, 16, 1024)
+ fieldGradient.setColorAt(0.0, Qt.black)
+ fieldGradient.setColorAt(1.0, Qt.white)
+ self.m_magneticField.setBaseGradient(fieldGradient)
+ self.m_magneticField.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
+
+ # For 'sun' we use a custom large sphere.
+ # 使用一个自定义的球体代表太阳
+ self.m_sun.setItemSize(0.2)
+ self.m_sun.setName("Sun")
+ self.m_sun.setItemLabelFormat("@seriesName")
+ self.m_sun.setMesh(QAbstract3DSeries.MeshUserDefined)
+ self.m_sun.setUserDefinedMesh(mesh_dir + '/largesphere.obj')
+ self.m_sun.setBaseColor(QColor(0xff, 0xbb, 0x00))
+ self.m_sun.dataProxy().addItem(QScatterDataItem(QVector3D()))
+
+ self.m_graph.addSeries(self.m_magneticField)
+ self.m_graph.addSeries(self.m_sun)
+
+ # Configure the axes according to the data.
+ # 设置x轴的范围值
+ self.m_graph.axisX().setRange(-self.horizontalRange,
+ self.horizontalRange)
+ # 设置y轴的范围值
+ self.m_graph.axisY().setRange(-self.verticalRange, self.verticalRange)
+ # 设置z轴的范围值
+ self.m_graph.axisZ().setRange(-self.horizontalRange,
+ self.horizontalRange)
+ # x和z轴上的段数
+ # 这表明绘制了多少标签。要绘制的网格线的数量使用公式计算:segments * subsegments + 1。预设默认值为5。该值不能低于1。
+ self.m_graph.axisX().setSegmentCount(self.horizontalRange)
+ self.m_graph.axisZ().setSegmentCount(self.horizontalRange)
+
+ self.m_rotationTimer.timeout.connect(self.triggerRotation)
+
+ self.toggleRotation()
+ self.generateData()
+
+ def generateData(self):
+ # 生成模拟数据
+ magneticFieldArray = []
+
+ for i in range(self.m_fieldLines):
+ horizontalAngle = (self.doublePi * i) / self.m_fieldLines
+ xCenter = self.ellipse_a * math.cos(horizontalAngle)
+ zCenter = self.ellipse_a * math.sin(horizontalAngle)
+
+ # Rotate - arrow is always tangential to the origin.
+ # 旋转-箭头始终与原点相切。
+ yRotation = QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0,
+ horizontalAngle * self.radiansToDegrees)
+
+ for j in range(self.m_arrowsPerLine):
+ # Calculate the point on the ellipse centered on the origin and
+ # 计算椭圆上以原点为中心的点
+ # parallel to the x-axis.
+ # 平行于X轴。
+ verticalAngle = ((self.doublePi * j) / self.m_arrowsPerLine) + self.m_angleOffset
+ xUnrotated = self.ellipse_a * math.cos(verticalAngle)
+ y = self.ellipse_b * math.sin(verticalAngle)
+
+ # Rotate the ellipse around the y-axis.
+ # 围绕Y轴旋转椭圆。
+ xRotated = xUnrotated * math.cos(horizontalAngle)
+ zRotated = xUnrotated * math.sin(horizontalAngle)
+
+ # Add the offset.
+ # 添加偏移量。
+ x = xCenter + xRotated
+ z = zCenter + zRotated
+
+ zRotation = QQuaternion.fromAxisAndAngle(0.0, 0.0, 1.0,
+ verticalAngle * self.radiansToDegrees)
+ totalRotation = yRotation * zRotation
+
+ itm = QScatterDataItem(QVector3D(x, y, z), totalRotation)
+ magneticFieldArray.append(itm)
+
+ if self.m_graph.selectedSeries() is self.m_magneticField:
+ self.m_graph.clearSelection()
+
+ self.m_magneticField.dataProxy().resetArray(magneticFieldArray)
+
+ def setFieldLines(self, lines):
+ self.m_fieldLines = lines
+ self.generateData()
+
+ def setArrowsPerLine(self, arrows):
+ self.m_arrowsPerLine = arrows
+ self.m_angleOffset = 0.0
+ self.m_angleStep = self.doublePi / self.m_arrowsPerLine / self.animationFrames
+ self.generateData()
+
+ def triggerRotation(self):
+ self.m_angleOffset += self.m_angleStep
+ self.generateData()
+
+ def toggleSun(self):
+ self.m_sun.setVisible(not self.m_graph.seriesList()[1].isVisible())
+
+ def toggleRotation(self):
+ if self.m_rotationTimer.isActive():
+ self.m_rotationTimer.stop()
+ else:
+ self.m_rotationTimer.start(15)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ graph = Q3DScatter()
+ container = QWidget.createWindowContainer(graph)
+
+ screenSize = graph.screen().size()
+ container.setMinimumSize(
+ QSize(screenSize.width() / 2, screenSize.height() / 1.5))
+ container.setMaximumSize(screenSize)
+ container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ container.setFocusPolicy(Qt.StrongFocus)
+
+ widget = QWidget()
+ hLayout = QHBoxLayout(widget)
+ vLayout = QVBoxLayout()
+ hLayout.addWidget(container, 1)
+ hLayout.addLayout(vLayout)
+
+ widget.setWindowTitle(Tr.get("Item rotations example - Magnetic field of the sun",
+ "Item rotations example - Magnetic field of the sun"))
+
+ toggleRotationButton = QPushButton(Tr.get("Toggle animation", "Toggle animation"))
+ toggleSunButton = QPushButton(Tr.get("Toggle Sun", "Toggle Sun"))
+
+ fieldLinesSlider = QSlider(Qt.Horizontal)
+ fieldLinesSlider.setTickInterval(1)
+ fieldLinesSlider.setMinimum(1)
+ fieldLinesSlider.setValue(12)
+ fieldLinesSlider.setMaximum(128)
+
+ arrowsSlider = QSlider(Qt.Horizontal)
+ arrowsSlider.setTickInterval(1)
+ arrowsSlider.setMinimum(8)
+ arrowsSlider.setValue(16)
+ arrowsSlider.setMaximum(32)
+
+ vLayout.addWidget(toggleRotationButton)
+ vLayout.addWidget(toggleSunButton)
+ vLayout.addWidget(QLabel(Tr.get("Field Lines (1 - 128):", "Field Lines (1 - 128):")))
+ vLayout.addWidget(fieldLinesSlider)
+ vLayout.addWidget(QLabel(Tr.get("Arrows per line (8 - 32):", "Arrows per line (8 - 32):")))
+ vLayout.addWidget(arrowsSlider, 1, Qt.AlignTop)
+
+ modifier = ScatterDataModifier(graph)
+
+ toggleRotationButton.clicked.connect(modifier.toggleRotation)
+ toggleSunButton.clicked.connect(modifier.toggleSun)
+ fieldLinesSlider.valueChanged.connect(modifier.setFieldLines)
+ arrowsSlider.valueChanged.connect(modifier.setArrowsPerLine)
+
+ widget.show()
+ sys.exit(app.exec_())
diff --git a/QtDataVisualization/README.en.md b/QtDataVisualization/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QtDataVisualization/README.md b/QtDataVisualization/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..9366e669aed1af1cbbc75e0ace8ba0e911139194
--- /dev/null
+++ b/QtDataVisualization/README.md
@@ -0,0 +1,21 @@
+# QtDataVisualization
+
+- 目录
+ - [柱状图3D](#1柱状图3D)
+ - [太阳磁场线](#2太阳磁场线)
+ - [余弦波3D](#3余弦波3D)
+
+## 1、柱状图3D
+[运行 BarsVisualization.py](BarsVisualization.py)
+
+
+
+## 2、太阳磁场线
+[运行 MagneticOfSun.py](MagneticOfSun.py)
+
+
+
+## 3、余弦波3D
+[运行 ScatterVisualization.py](ScatterVisualization.py)
+
+
\ No newline at end of file
diff --git a/QtDataVisualization/ScatterVisualization.py b/QtDataVisualization/ScatterVisualization.py
new file mode 100644
index 0000000000000000000000000000000000000000..69eeeb82fb9ebf671926c41465fc41d8dafbf1c9
--- /dev/null
+++ b/QtDataVisualization/ScatterVisualization.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019/10/4
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ScatterVisualization
+@description:
+"""
+
+#############################################################################
+##
+## Copyright (C) 2014 Riverbank Computing Limited.
+## Copyright (C) 2014 Digia Plc
+## All rights reserved.
+## For any questions to Digia, please use contact form at http://qt.digia.com
+##
+## This file is part of the QtDataVisualization module.
+##
+## Licensees holding valid Qt Enterprise licenses may use this file in
+## accordance with the Qt Enterprise License Agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and Digia.
+##
+## If you have questions regarding the use of this file, please use
+## contact form at http://qt.digia.com
+##
+#############################################################################
+
+
+import math
+
+from PyQt5.QtCore import pyqtSignal, QObject, QSize, Qt, QLocale
+from PyQt5.QtDataVisualization import (Q3DCamera, Q3DTheme, Q3DScatter,
+ QAbstract3DGraph, QAbstract3DSeries, QScatter3DSeries,
+ QScatterDataItem, QScatterDataProxy)
+from PyQt5.QtGui import QFont, QVector3D
+from PyQt5.QtWidgets import (QApplication, QCheckBox, QComboBox, QFontComboBox,
+ QHBoxLayout, QLabel, QPushButton, QSizePolicy, QVBoxLayout, QWidget)
+
+country = QLocale.system().country()
+if country in (QLocale.China, QLocale.HongKong, QLocale.Taiwan):
+ Tr = {
+ 'A Cosine Wave': '余弦波',
+ 'Change label style': '更改label样式',
+ 'Smooth dots': '平滑圆点',
+ 'Change camera preset': '更改相机预设',
+ 'Show background': '显示背景',
+ 'Show grid': '显示网格',
+ 'Change dot style': '更改点样式',
+ 'Change theme': '更改主题',
+ 'Adjust shadow quality': '调整阴影质量',
+ 'Change font': '更改字体'
+ }
+else:
+ Tr = {}
+
+
+class ScatterDataModifier(QObject):
+ numberOfItems = 3600
+ curveDivider = 3.0
+ lowerNumberOfItems = 900
+ lowerCurveDivider = 0.75
+
+ backgroundEnabledChanged = pyqtSignal(bool)
+ gridEnabledChanged = pyqtSignal(bool)
+ shadowQualityChanged = pyqtSignal(int)
+ fontChanged = pyqtSignal(QFont)
+
+ def __init__(self, scatter):
+ super(ScatterDataModifier, self).__init__()
+
+ self.m_graph = scatter # Q3DScatter实例
+ self.m_fontSize = 40.0
+ self.m_style = QAbstract3DSeries.MeshSphere
+ self.m_smooth = True
+ self.m_itemCount = self.lowerNumberOfItems
+ self.m_curveDivider = self.lowerCurveDivider
+
+ # 设置当前主题类型
+ self.m_graph.activeTheme().setType(Q3DTheme.ThemeEbony)
+ # 设置当前主题的字体
+ font = self.m_graph.activeTheme().font()
+ font.setPointSize(self.m_fontSize)
+ self.m_graph.activeTheme().setFont(font)
+ # 设置阴影质量
+ self.m_graph.setShadowQuality(QAbstract3DGraph.ShadowQualitySoftLow)
+ self.m_graph.scene().activeCamera().setCameraPreset(
+ Q3DCamera.CameraPresetFront)
+
+ proxy = QScatterDataProxy()
+ series = QScatter3DSeries(proxy)
+ series.setItemLabelFormat(
+ "@xTitle: @xLabel @yTitle: @yLabel @zTitle: @zLabel")
+ series.setMeshSmooth(self.m_smooth)
+ self.m_graph.addSeries(series)
+
+ self.addData()
+
+ def addData(self):
+ # 添加数据
+ self.m_graph.axisX().setTitle("X")
+ self.m_graph.axisY().setTitle("Y")
+ self.m_graph.axisZ().setTitle("Z")
+
+ dataArray = []
+
+ limit = math.sqrt(self.m_itemCount) / 2.0
+ i = -limit
+ while i < limit:
+ j = -limit
+ while j < limit:
+ itm = QScatterDataItem(
+ QVector3D(i + 0.5,
+ math.cos(
+ math.radians((i * j) / self.m_curveDivider)),
+ j + 0.5))
+ dataArray.append(itm)
+ j += 1.0
+
+ i += 1.0
+
+ self.m_graph.seriesList()[0].dataProxy().resetArray(dataArray)
+
+ def changeStyle(self, style):
+ comboBox = self.sender()
+ if isinstance(comboBox, QComboBox):
+ self.m_style = QAbstract3DSeries.Mesh(comboBox.itemData(style))
+ self.m_graph.seriesList()[0].setMesh(self.m_style)
+
+ def setSmoothDots(self, smooth):
+ self.m_smooth = bool(smooth)
+ self.m_graph.seriesList()[0].setMeshSmooth(self.m_smooth)
+
+ def changeTheme(self, theme):
+ currentTheme = self.m_graph.activeTheme()
+ currentTheme.setType(Q3DTheme.Theme(theme))
+ self.backgroundEnabledChanged.emit(currentTheme.isBackgroundEnabled())
+ self.gridEnabledChanged.emit(currentTheme.isGridEnabled())
+ self.fontChanged.emit(currentTheme.font())
+
+ preset = int(Q3DCamera.CameraPresetFrontLow)
+
+ def changePresetCamera(self):
+ self.m_graph.scene().activeCamera().setCameraPreset(
+ Q3DCamera.CameraPreset(self.preset))
+
+ self.preset += 1
+
+ if self.preset > Q3DCamera.CameraPresetDirectlyBelow:
+ self.preset = int(Q3DCamera.CameraPresetFrontLow)
+
+ def changeLabelStyle(self):
+ self.m_graph.activeTheme().setLabelBackgroundEnabled(
+ not self.m_graph.activeTheme().isLabelBackgroundEnabled())
+
+ def changeFont(self, font):
+ newFont = QFont(font)
+ newFont.setPointSizeF(self.m_fontSize)
+ self.m_graph.activeTheme().setFont(newFont)
+
+ def shadowQualityUpdatedByVisual(self, sq):
+ self.shadowQualityChanged.emit(int(sq))
+
+ def changeShadowQuality(self, quality):
+ sq = QAbstract3DGraph.ShadowQuality(quality)
+ self.m_graph.setShadowQuality(sq)
+
+ def setBackgroundEnabled(self, enabled):
+ self.m_graph.activeTheme().setBackgroundEnabled(enabled)
+
+ def setGridEnabled(self, enabled):
+ self.m_graph.activeTheme().setGridEnabled(enabled)
+
+ def toggleItemCount(self):
+ if self.m_itemCount == self.numberOfItems:
+ self.m_itemCount = self.lowerNumberOfItems
+ self.m_curveDivider = self.lowerCurveDivider
+ else:
+ self.m_itemCount = self.numberOfItems
+ self.m_curveDivider = self.curveDivider
+
+ self.m_graph.seriesList()[0].dataProxy().resetArray(None)
+ self.addData()
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ graph = Q3DScatter()
+ container = QWidget.createWindowContainer(graph)
+
+ screenSize = graph.screen().size()
+ container.setMinimumSize(
+ QSize(screenSize.width() / 2, screenSize.height() / 1.5))
+ container.setMaximumSize(screenSize)
+ container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ container.setFocusPolicy(Qt.StrongFocus)
+
+ widget = QWidget()
+ hLayout = QHBoxLayout(widget)
+ vLayout = QVBoxLayout()
+ hLayout.addWidget(container, 1)
+ hLayout.addLayout(vLayout)
+
+ widget.setWindowTitle(Tr.get("A Cosine Wave", "A Cosine Wave"))
+
+ themeList = QComboBox()
+ themeList.addItem("Qt")
+ themeList.addItem("Primary Colors")
+ themeList.addItem("Digia")
+ themeList.addItem("Stone Moss")
+ themeList.addItem("Army Blue")
+ themeList.addItem("Retro")
+ themeList.addItem("Ebony")
+ themeList.addItem("Isabelle")
+ themeList.setCurrentIndex(6)
+
+ labelButton = QPushButton(Tr.get("Change label style", "Change label style"))
+
+ smoothCheckBox = QCheckBox(Tr.get("Smooth dots", "Smooth dots"), checked=True)
+
+ itemStyleList = QComboBox()
+ itemStyleList.addItem("Sphere", QAbstract3DSeries.MeshSphere)
+ itemStyleList.addItem("Cube", QAbstract3DSeries.MeshCube)
+ itemStyleList.addItem("Minimal", QAbstract3DSeries.MeshMinimal)
+ itemStyleList.addItem("Point", QAbstract3DSeries.MeshPoint)
+ itemStyleList.setCurrentIndex(0)
+
+ cameraButton = QPushButton(Tr.get("Change camera preset", "Change camera preset"))
+
+ itemCountButton = QPushButton(Tr.get("Toggle item count", "Toggle item count"))
+
+ backgroundCheckBox = QCheckBox(Tr.get("Show background", "Show background"), checked=True)
+
+ gridCheckBox = QCheckBox(Tr.get("Show grid", "Show grid"), checked=True)
+
+ shadowQuality = QComboBox()
+ shadowQuality.addItem("None")
+ shadowQuality.addItem("Low")
+ shadowQuality.addItem("Medium")
+ shadowQuality.addItem("High")
+ shadowQuality.addItem("Low Soft")
+ shadowQuality.addItem("Medium Soft")
+ shadowQuality.addItem("High Soft")
+ shadowQuality.setCurrentIndex(4)
+
+ fontList = QFontComboBox()
+ fontList.setCurrentFont(QFont('Arial'))
+
+ vLayout.addWidget(labelButton, 0, Qt.AlignTop)
+ vLayout.addWidget(cameraButton, 0, Qt.AlignTop)
+ vLayout.addWidget(itemCountButton, 0, Qt.AlignTop)
+ vLayout.addWidget(backgroundCheckBox)
+ vLayout.addWidget(gridCheckBox)
+ vLayout.addWidget(smoothCheckBox, 0, Qt.AlignTop)
+ vLayout.addWidget(QLabel(Tr.get("Change dot style", "Change dot style")))
+ vLayout.addWidget(itemStyleList)
+ vLayout.addWidget(QLabel(Tr.get("Change theme", "Change theme")))
+ vLayout.addWidget(themeList)
+ vLayout.addWidget(QLabel(Tr.get("Adjust shadow quality", "Adjust shadow quality")))
+ vLayout.addWidget(shadowQuality)
+ vLayout.addWidget(QLabel(Tr.get("Change font", "Change font")))
+ vLayout.addWidget(fontList, 1, Qt.AlignTop)
+
+ modifier = ScatterDataModifier(graph)
+
+ cameraButton.clicked.connect(modifier.changePresetCamera)
+ labelButton.clicked.connect(modifier.changeLabelStyle)
+ itemCountButton.clicked.connect(modifier.toggleItemCount)
+
+ backgroundCheckBox.stateChanged.connect(modifier.setBackgroundEnabled)
+ gridCheckBox.stateChanged.connect(modifier.setGridEnabled)
+ smoothCheckBox.stateChanged.connect(modifier.setSmoothDots)
+
+ modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
+ modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
+ itemStyleList.currentIndexChanged.connect(modifier.changeStyle)
+
+ themeList.currentIndexChanged.connect(modifier.changeTheme)
+
+ shadowQuality.currentIndexChanged.connect(modifier.changeShadowQuality)
+
+ modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
+ graph.shadowQualityChanged.connect(modifier.shadowQualityUpdatedByVisual)
+
+ fontList.currentFontChanged.connect(modifier.changeFont)
+
+ modifier.fontChanged.connect(fontList.setCurrentFont)
+
+ widget.show()
+ sys.exit(app.exec_())
diff --git a/QtDataVisualization/ScreenShot/BarsVisualization.gif b/QtDataVisualization/ScreenShot/BarsVisualization.gif
new file mode 100644
index 0000000000000000000000000000000000000000..4df9d08fb3cf3dacd25ef5922bf4817d4fde3c5a
Binary files /dev/null and b/QtDataVisualization/ScreenShot/BarsVisualization.gif differ
diff --git a/QtDataVisualization/ScreenShot/MagneticOfSun.gif b/QtDataVisualization/ScreenShot/MagneticOfSun.gif
new file mode 100644
index 0000000000000000000000000000000000000000..9d94c5cd1a489463a54e672e60a055bd1dd43d00
Binary files /dev/null and b/QtDataVisualization/ScreenShot/MagneticOfSun.gif differ
diff --git a/QtDataVisualization/ScreenShot/ScatterVisualization.gif b/QtDataVisualization/ScreenShot/ScatterVisualization.gif
new file mode 100644
index 0000000000000000000000000000000000000000..abb9848d450afe7806780d52ae18d3f37034a410
Binary files /dev/null and b/QtDataVisualization/ScreenShot/ScatterVisualization.gif differ
diff --git a/QtDataVisualization/requirements.txt b/QtDataVisualization/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a7972d6961a0854ca7e6db1cfe61d7e29e28fe91
--- /dev/null
+++ b/QtDataVisualization/requirements.txt
@@ -0,0 +1 @@
+PyQtDataVisualization
diff --git a/QtQuick/FlatStyle.py b/QtQuick/FlatStyle.py
new file mode 100644
index 0000000000000000000000000000000000000000..e11e984d6f31e63a1b121bce8c58baf8eaeff8e9
--- /dev/null
+++ b/QtQuick/FlatStyle.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年2月2日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QtQuick.FlatStyle
+@description:
+"""
+import os
+import sys
+
+try:
+ from PyQt5.QtCore import QCoreApplication, Qt, QUrl
+ from PyQt5.QtQml import QQmlApplicationEngine
+ from PyQt5.QtWidgets import QApplication, QMessageBox
+except ImportError:
+ from PySide2.QtCore import QCoreApplication, Qt, QUrl
+ from PySide2.QtQml import QQmlApplicationEngine
+ from PySide2.QtWidgets import QApplication, QMessageBox
+
+if __name__ == '__main__':
+ try:
+ QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
+ except:
+ pass
+
+ os.chdir('FlatStyle')
+
+ app = QApplication(sys.argv)
+ engine = QQmlApplicationEngine()
+ engine.objectCreated.connect(
+ lambda obj, _: (
+ QMessageBox.critical(None, '错误', '运行失败,可能是当前PyQt版本不支持'), engine.quit) if not obj else 0)
+ engine.addImportPath('imports')
+ engine.load(QUrl('flatstyle.qml'))
+
+ sys.exit(app.exec_())
diff --git a/QtQuick/FlatStyle/Flat/Button.qml b/QtQuick/FlatStyle/Flat/Button.qml
new file mode 100644
index 0000000000000000000000000000000000000000..c56a9c6a6c6706f2893cf9561e7f705ff5221796
--- /dev/null
+++ b/QtQuick/FlatStyle/Flat/Button.qml
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Templates 2.1 as T
+import Theme 1.0
+
+T.Button {
+ id: control
+
+ font: Theme.font
+
+ implicitWidth: Math.max(background ? background.implicitWidth : 0,
+ contentItem.implicitWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(background ? background.implicitHeight : 0,
+ contentItem.implicitHeight + topPadding + bottomPadding)
+ leftPadding: 4
+ rightPadding: 4
+
+ background: Rectangle {
+ id: buttonBackground
+ implicitWidth: 100
+ implicitHeight: 40
+ opacity: enabled ? 1 : 0.3
+ border.color: Theme.mainColor
+ border.width: 1
+ radius: 2
+
+ states: [
+ State {
+ name: "normal"
+ when: !control.down
+ PropertyChanges {
+ target: buttonBackground
+ }
+ },
+ State {
+ name: "down"
+ when: control.down
+ PropertyChanges {
+ target: buttonBackground
+ border.color: Theme.mainColorDarker
+ }
+ }
+ ]
+ }
+
+ contentItem: Text {
+ id: textItem
+ text: control.text
+
+ font: control.font
+ opacity: enabled ? 1.0 : 0.3
+ color: Theme.mainColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+
+ states: [
+ State {
+ name: "normal"
+ when: !control.down
+ },
+ State {
+ name: "down"
+ when: control.down
+ PropertyChanges {
+ target: textItem
+ color: Theme.mainColorDarker
+ }
+ }
+ ]
+ }
+}
+
diff --git a/QtQuick/FlatStyle/Flat/CheckBox.qml b/QtQuick/FlatStyle/Flat/CheckBox.qml
new file mode 100644
index 0000000000000000000000000000000000000000..332d63b84c9a86e518fc60d754f58fec4e03d73d
--- /dev/null
+++ b/QtQuick/FlatStyle/Flat/CheckBox.qml
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Templates 2.1 as T
+import Theme 1.0
+
+T.CheckBox {
+ id: control
+
+ font: Theme.font
+
+ implicitWidth: Math.max(background ? background.implicitWidth : 0,
+ contentItem.implicitWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(background ? background.implicitHeight : 0,
+ Math.max(contentItem.implicitHeight,
+ indicator ? indicator.implicitHeight : 0) + topPadding + bottomPadding)
+ leftPadding: 4
+ indicator: Rectangle {
+ id: checkboxHandle
+ implicitWidth: Theme.baseSize * 2.6
+ implicitHeight: Theme.baseSize * 2.6
+ x: control.leftPadding
+ anchors.verticalCenter: parent.verticalCenter
+ radius: 2
+ border.color: Theme.mainColor
+
+ Rectangle {
+ id: rectangle
+ width: Theme.baseSize * 1.4
+ height: Theme.baseSize * 1.4
+ x: Theme.baseSize * 0.6
+ y: Theme.baseSize * 0.6
+ radius: Theme.baseSize * 0.4
+ visible: false
+ color: Theme.mainColor
+ }
+
+ states: [
+ State {
+ name: "unchecked"
+ when: !control.checked && !control.down
+ },
+ State {
+ name: "checked"
+ when: control.checked && !control.down
+
+ PropertyChanges {
+ target: rectangle
+ visible: true
+ }
+ },
+ State {
+ name: "unchecked_down"
+ when: !control.checked && control.down
+
+ PropertyChanges {
+ target: rectangle
+ color: Theme.mainColorDarker
+ }
+
+ PropertyChanges {
+ target: checkboxHandle
+ border.color: Theme.mainColorDarker
+ }
+ },
+ State {
+ name: "checked_down"
+ extend: "unchecked_down"
+ when: control.checked && control.down
+
+ PropertyChanges {
+ target: rectangle
+ visible: true
+ }
+ }
+ ]
+ }
+
+ background: Rectangle {
+ implicitWidth: 140
+ implicitHeight: Theme.baseSize * 3.8
+ color: Theme.lightGray
+ border.color: Theme.gray
+ }
+
+ contentItem: Text {
+ leftPadding: control.indicator.width + 4
+
+ text: control.text
+ font: control.font
+ color: Theme.dark
+ elide: Text.ElideRight
+ visible: control.text
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ }
+}
+
diff --git a/QtQuick/FlatStyle/Flat/Switch.qml b/QtQuick/FlatStyle/Flat/Switch.qml
new file mode 100644
index 0000000000000000000000000000000000000000..00b38767a0962ed1894d6ecd106536a9379a43c2
--- /dev/null
+++ b/QtQuick/FlatStyle/Flat/Switch.qml
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Templates 2.1 as T
+import Theme 1.0
+
+T.Switch {
+ id: control
+
+ implicitWidth: indicator.implicitWidth
+ implicitHeight: background.implicitHeight
+
+ background: Rectangle {
+ implicitWidth: 140
+ implicitHeight: Theme.baseSize * 3.8
+ color: Theme.lightGray
+ border.color: Theme.gray
+ }
+
+ leftPadding: 4
+
+ indicator: Rectangle {
+ id: switchHandle
+ implicitWidth: Theme.baseSize * 4.8
+ implicitHeight: Theme.baseSize * 2.6
+ x: control.leftPadding
+ anchors.verticalCenter: parent.verticalCenter
+ radius: Theme.baseSize * 1.3
+ color: Theme.light
+ border.color: Theme.lightGray
+
+ Rectangle {
+ id: rectangle
+
+ width: Theme.baseSize * 2.6
+ height: Theme.baseSize * 2.6
+ radius: Theme.baseSize * 1.3
+ color: Theme.light
+ border.color: Theme.gray
+ }
+
+ states: [
+ State {
+ name: "off"
+ when: !control.checked && !control.down
+ },
+ State {
+ name: "on"
+ when: control.checked && !control.down
+
+ PropertyChanges {
+ target: switchHandle
+ color: Theme.mainColor
+ border.color: Theme.mainColor
+ }
+
+ PropertyChanges {
+ target: rectangle
+ x: parent.width - width
+
+ }
+ },
+ State {
+ name: "off_down"
+ when: !control.checked && control.down
+
+ PropertyChanges {
+ target: rectangle
+ color: Theme.light
+ }
+
+ },
+ State {
+ name: "on_down"
+ extend: "off_down"
+ when: control.checked && control.down
+
+ PropertyChanges {
+ target: rectangle
+ x: parent.width - width
+ color: Theme.light
+ }
+
+ PropertyChanges {
+ target: switchHandle
+ color: Theme.mainColorDarker
+ border.color: Theme.mainColorDarker
+ }
+ }
+ ]
+ }
+}
diff --git a/QtQuick/FlatStyle/MainForm.ui.qml b/QtQuick/FlatStyle/MainForm.ui.qml
new file mode 100644
index 0000000000000000000000000000000000000000..06b87d5e35f66bae739757bd2b21a56f1fd7dbb1
--- /dev/null
+++ b/QtQuick/FlatStyle/MainForm.ui.qml
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.0
+import Theme 1.0
+
+Item {
+ id: form
+
+ width: 320
+ height: 480
+ property alias slider: slider
+ property alias checkBoxUnderline: checkBoxUnderline
+ property alias checkBoxBold: checkBoxBold
+ property alias sizeSwitch: sizeSwitch
+ property alias button: button
+
+ Slider {
+ id: slider
+ width: 297
+ height: 38
+ stepSize: 1
+ to: 18
+ from: 10
+ value: 14
+ anchors.topMargin: Theme.baseSize
+ anchors.top: gridLayout.bottom
+ anchors.right: gridLayout.right
+ anchors.left: gridLayout.left
+ handle: Rectangle {
+ id: sliderHandle
+ x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
+ y: slider.topPadding + slider.availableHeight / 2 - height / 2
+ implicitWidth: 26
+ implicitHeight: 26
+ radius: 13
+ color: slider.pressed ? Theme.mainColorDarker : Theme.mainColor
+ border.color: Theme.gray
+ }
+ }
+
+ GridLayout {
+ id: gridLayout
+ anchors.top: parent.top
+ anchors.topMargin: 64
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ columnSpacing: Theme.baseSize * 0.5
+ rowSpacing: Theme.baseSize * 0.5
+ rows: 4
+ columns: 2
+
+ Label {
+ text: qsTr("Toggle Size")
+ font: Theme.font
+ }
+
+ Switch {
+ id: sizeSwitch
+ Layout.fillWidth: true
+ }
+
+ CheckBox {
+ id: checkBoxBold
+ text: qsTr("Bold")
+ checked: true
+ Layout.fillWidth: true
+ }
+
+ CheckBox {
+ id: checkBoxUnderline
+ text: qsTr("Underline")
+ Layout.fillWidth: true
+ }
+
+ Rectangle {
+ id: rectangle
+ color: Theme.mainColor
+ Layout.fillWidth: true
+ Layout.columnSpan: 2
+ Layout.preferredHeight: 38
+ Layout.preferredWidth: 297
+ }
+
+ Label {
+ id: label
+ text: qsTr("Customization")
+ font: Theme.font
+ }
+
+ Button {
+ id: button
+ text: qsTr("Change Color")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+ }
+}
diff --git a/QtQuick/FlatStyle/MainForm.ui.qmlc b/QtQuick/FlatStyle/MainForm.ui.qmlc
new file mode 100644
index 0000000000000000000000000000000000000000..f0f357a35c89afe04e57191858b1347f7eb401b9
Binary files /dev/null and b/QtQuick/FlatStyle/MainForm.ui.qmlc differ
diff --git a/QtQuick/FlatStyle/flatstyle.qml b/QtQuick/FlatStyle/flatstyle.qml
new file mode 100644
index 0000000000000000000000000000000000000000..f3e35876e02a886297172934332583491b09eb97
--- /dev/null
+++ b/QtQuick/FlatStyle/flatstyle.qml
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import Qt.labs.platform 1.0
+import Theme 1.0
+
+ApplicationWindow {
+ id: window
+ visible: true
+ minimumWidth: 360
+ height: 480
+
+ title: qsTr("Flat Style")
+
+ MainForm {
+ id: form
+
+ anchors.fill: parent
+ button.onClicked: colorDialog.open()
+
+ sizeSwitch.onCheckedChanged: Theme.baseSize = (sizeSwitch.checked ? Theme.largeSize : Theme.smallSize)
+ checkBoxBold.onCheckedChanged: Theme.font.bold = checkBoxBold.checked
+ checkBoxUnderline.onCheckedChanged: Theme.font.underline = checkBoxUnderline.checked
+ slider.onPositionChanged: Theme.font.pixelSize = slider.valueAt(slider.position)
+ }
+
+ ColorDialog {
+ id: colorDialog
+ onCurrentColorChanged: Theme.mainColor = currentColor
+ }
+}
diff --git a/QtQuick/FlatStyle/flatstyle.qmlc b/QtQuick/FlatStyle/flatstyle.qmlc
new file mode 100644
index 0000000000000000000000000000000000000000..9b9afa9ff37c0f956a8d36a2ee9a5e55b6b08cfe
Binary files /dev/null and b/QtQuick/FlatStyle/flatstyle.qmlc differ
diff --git a/QtQuick/FlatStyle/imports/Theme/Theme.qml b/QtQuick/FlatStyle/imports/Theme/Theme.qml
new file mode 100644
index 0000000000000000000000000000000000000000..003e743bc4c1eb457c8c4478208bf84b4faef51f
--- /dev/null
+++ b/QtQuick/FlatStyle/imports/Theme/Theme.qml
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+pragma Singleton
+
+import QtQuick 2.8
+
+QtObject {
+ readonly property color gray: "#b2b1b1"
+ readonly property color lightGray: "#dddddd"
+ readonly property color light: "#ffffff"
+ readonly property color blue: "#2d548b"
+ property color mainColor: "#17a81a"
+ readonly property color dark: "#222222"
+ readonly property color mainColorDarker: Qt.darker(mainColor, 1.5)
+
+ property int baseSize: 10
+
+ readonly property int smallSize: 10
+ readonly property int largeSize: 16
+
+ property font font
+ font.bold: true
+ font.underline: false
+ font.pixelSize: 14
+ font.family: "arial"
+}
diff --git a/QtQuick/FlatStyle/imports/Theme/Theme.qmlc b/QtQuick/FlatStyle/imports/Theme/Theme.qmlc
new file mode 100644
index 0000000000000000000000000000000000000000..b673e93b4fb092fc19f275e13b3da38de7742957
Binary files /dev/null and b/QtQuick/FlatStyle/imports/Theme/Theme.qmlc differ
diff --git a/QtQuick/FlatStyle/imports/Theme/qmldir b/QtQuick/FlatStyle/imports/Theme/qmldir
new file mode 100644
index 0000000000000000000000000000000000000000..4a58c13a907167ac2b07104846f55f1341d20264
--- /dev/null
+++ b/QtQuick/FlatStyle/imports/Theme/qmldir
@@ -0,0 +1,2 @@
+module Theme
+singleton Theme 1.0 Theme.qml
diff --git a/QtQuick/FlatStyle/qtquickcontrols2.conf b/QtQuick/FlatStyle/qtquickcontrols2.conf
new file mode 100644
index 0000000000000000000000000000000000000000..9cd59e5aa0788be70a4171d9adfb2e28632f7939
--- /dev/null
+++ b/QtQuick/FlatStyle/qtquickcontrols2.conf
@@ -0,0 +1,2 @@
+[Controls]
+Style=Flat
diff --git a/QtQuick/README.en.md b/QtQuick/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QtQuick/README.md b/QtQuick/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..253ed416d43fb3f4b48be4d5b2059236cbb5e178
--- /dev/null
+++ b/QtQuick/README.md
@@ -0,0 +1,31 @@
+# QtQuick
+
+- 目录
+ - [Flat样式](#1Flat样式)
+ - [QML与Python交互](#2QML与Python交互)
+
+## 1、Flat样式
+[运行 FlatStyle.py](FlatStyle.py)
+
+
+
+## 2、QML与Python交互
+[运行 Signals.py](Signals.py)
+
+交互的办法有很多种,由于主要界面功能都是有QML来实现,Python只是作为辅助提供部分功能。
+于是和浏览器中js与python交互方式类似,提供一个Python对象给QML访问。
+
+1. 通过 `engine.rootContext().setContextProperty('_Window', w)` 注册提供一个Python对象
+2. Python对象中被访问的方法前面使用装饰器 `@pyqtSlot`,比如: `@pyqtSlot(int)` 或者 `@pyqtSlot(str, result=str) # 可以获取返回值` 。
+3. QML中的信号或者Python对象中的信号都可以互相绑定对方的槽函数
+```js
+Component.onCompleted: {
+ // 绑定信号槽到python中的函数
+ valueChanged.connect(_Window.onValueChanged)
+ // 绑定python中的信号到qml中的函数
+ _Window.timerSignal.connect(appendText)
+}
+```
+
+
+
diff --git a/QtQuick/ScreenShot/FlatStyle.gif b/QtQuick/ScreenShot/FlatStyle.gif
new file mode 100644
index 0000000000000000000000000000000000000000..972e527020c1928a1fbecfdf6d5556b0a82f880b
Binary files /dev/null and b/QtQuick/ScreenShot/FlatStyle.gif differ
diff --git a/QtQuick/ScreenShot/Signals.gif b/QtQuick/ScreenShot/Signals.gif
new file mode 100644
index 0000000000000000000000000000000000000000..23ee874272e1cb911b06b77e1e4d4a03d13679f8
Binary files /dev/null and b/QtQuick/ScreenShot/Signals.gif differ
diff --git a/QtQuick/Signals.py b/QtQuick/Signals.py
new file mode 100644
index 0000000000000000000000000000000000000000..7735c944e6c82a21739c89c619630abace6649c5
--- /dev/null
+++ b/QtQuick/Signals.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年9月18日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QtQuick.Signals
+@description: 信号槽
+"""
+
+import sys
+from time import time
+
+try:
+ from PyQt5.QtCore import QCoreApplication, Qt, pyqtSlot, pyqtSignal, QTimer
+ from PyQt5.QtQml import QQmlApplicationEngine
+ from PyQt5.QtWidgets import QApplication, QMessageBox, QWidget, QVBoxLayout, \
+ QPushButton, QTextBrowser
+except ImportError:
+ from PySide2.QtCore import QCoreApplication, Qt, Slot as pyqtSlot, Signal as pyqtSignal, QTimer
+ from PySide2.QtQml import QQmlApplicationEngine
+ from PySide2.QtWidgets import QApplication, QMessageBox, QWidget, QVBoxLayout, \
+ QPushButton, QTextBrowser
+
+QML = """import QtQuick 2.0
+import QtQuick.Controls 1.6
+import QtQuick.Layouts 1.3
+
+ApplicationWindow {
+ visible: true
+ width: 400
+ height: 400
+ id: root
+ title: "editor"
+
+ // 定义信号槽
+ signal valueChanged(int value)
+
+ Component.onCompleted: {
+ // 绑定信号槽到python中的函数
+ valueChanged.connect(_Window.onValueChanged)
+ // 绑定python中的信号到qml中的函数
+ _Window.timerSignal.connect(appendText)
+ }
+
+ function appendText(text) {
+ // 定义添加文字函数
+ textArea.append(text)
+ }
+
+ ColumnLayout {
+ id: columnLayout
+ anchors.fill: parent
+
+ Button {
+ id: button
+ text: qsTr("Button")
+ Layout.fillWidth: true
+ onClicked: {
+ // 点击按钮调用python中的函数并得到返回值
+ var ret = _Window.testSlot("Button")
+ textArea.append("我调用了testSlot函数得到返回值: " + ret)
+ }
+ }
+
+ Slider {
+ id: sliderHorizontal
+ Layout.fillWidth: true
+ stepSize: 1
+ minimumValue: 0
+ maximumValue: 100
+ // 拉动条值改变时发送信号
+ onValueChanged: root.valueChanged(value)
+ }
+
+ TextArea {
+ id: textArea
+ Layout.fillWidth: true
+ }
+ }
+
+}
+"""
+
+
+class Window(QWidget):
+ # 定义一个时间信号
+ timerSignal = pyqtSignal(str)
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+ layout.addWidget(QPushButton('Python调用qml中的函数',
+ self, clicked=self.callQmlFunc))
+ self.resultView = QTextBrowser(self)
+ layout.addWidget(self.resultView)
+ self._timer = QTimer(self, timeout=self.onTimeout)
+ self._timer.start(2000)
+
+ def onTimeout(self):
+ # 定时器发送信号通知qml
+ self.timerSignal.emit('定时器发来:' + str(time()))
+
+ def callQmlFunc(self):
+ # 主动调用qml中的appendText函数
+ engine.rootObjects()[0].appendText('我是被Python调用了')
+
+ @pyqtSlot(int)
+ def onValueChanged(self, value):
+ # qml中的自定义信号valueChanged所绑定的槽函数
+ self.resultView.append('拉动条值: %s' % value)
+
+ @pyqtSlot(str, result=str) # 可以获取返回值
+ def testSlot(self, name):
+ # 被qml调用的函数
+ self.resultView.append('我被主动调用: %s' % name)
+ return str(len(name))
+
+
+if __name__ == '__main__':
+ try:
+ QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
+ except:
+ pass
+
+ app = QApplication(sys.argv)
+
+ # 测试界面
+ w = Window()
+ w.resize(400, 400)
+ w.show()
+ w.move(400, 400)
+
+ engine = QQmlApplicationEngine()
+ # 提供一个沟通的对象_Window,必须是要继承QObject的类
+ engine.rootContext().setContextProperty('_Window', w)
+
+ engine.objectCreated.connect(
+ lambda obj, _: QMessageBox.critical(None, '错误', '运行失败,请检查') if not obj else 0)
+ engine.loadData(QML.encode())
+
+ sys.exit(app.exec_())
diff --git a/QtQuick/__init__.py b/QtQuick/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QtRemoteObjects/README.en.md b/QtRemoteObjects/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..0f15d4bcbaab183557d6f862e9c57690b670fb6f
--- /dev/null
+++ b/QtRemoteObjects/README.en.md
@@ -0,0 +1 @@
+# QtRemoteObjects
\ No newline at end of file
diff --git a/QtRemoteObjects/README.md b/QtRemoteObjects/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ce6e3c5355b433b38594b37cd81b9579e04b0c7e
--- /dev/null
+++ b/QtRemoteObjects/README.md
@@ -0,0 +1,25 @@
+# QtRemoteObjects
+
+- 目录
+ - [modelview](#1modelview)
+ - [simpleswitch](#2simpleswitch)
+ - [简单界面数据同步](#3简单界面数据同步)
+
+## 1、modelview
+[运行 modelviewserver.py](modelview/modelviewserver.py) | [运行 modelviewclient.py](modelview/modelviewclient.py)
+
+官方关于QTreeView/QStandardItemModel的同步model例子
+
+## 2、simpleswitch
+[运行 directconnectdynamicserver.py](simpleswitch/directconnectdynamicserver.py) | [运行 directconnectdynamicclient.py](simpleswitch/directconnectdynamicclient.py)
+
+[运行 registryconnecteddynamicserver.py](simpleswitch/registryconnecteddynamicserver.py) | [运行 registryconnecteddynamicclient.py](simpleswitch/registryconnecteddynamicclient.py)
+
+官方关于简单的信号槽、属性访问测试例子
+
+## 3、简单界面数据同步
+[运行 WindowMaster.py](SyncUi/WindowMaster.py) | [运行 WindowSlave.py](SyncUi/WindowSlave.py)
+
+绑定信号槽同步双方数据,属性方法测试没通过,详细注释在代码中
+
+
\ No newline at end of file
diff --git a/QtRemoteObjects/ScreenShot/SyncUi.gif b/QtRemoteObjects/ScreenShot/SyncUi.gif
new file mode 100644
index 0000000000000000000000000000000000000000..da832bb2849c072717278d911dfe776f37e06410
Binary files /dev/null and b/QtRemoteObjects/ScreenShot/SyncUi.gif differ
diff --git a/QtRemoteObjects/SyncUi/ClipboardMaster.py b/QtRemoteObjects/SyncUi/ClipboardMaster.py
new file mode 100644
index 0000000000000000000000000000000000000000..50aea697027af801e0cafcd2271d83b243ba38be
--- /dev/null
+++ b/QtRemoteObjects/SyncUi/ClipboardMaster.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020/7/31
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ClipboardMaster
+@description:
+"""
+import sys
+
+from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal, QLoggingCategory, QVariant, QMimeData
+from PyQt5.QtRemoteObjects import QRemoteObjectHost
+from PyQt5.QtWidgets import QTextBrowser
+
+
+class WindowMaster(QTextBrowser):
+ SignalUpdateMimeData = pyqtSignal(
+ bool, QVariant, # color
+ bool, QVariant, # html
+ bool, QVariant, # image
+ bool, QVariant, # text
+ bool, QVariant, # urls
+ )
+
+ def __init__(self, *args, **kwargs):
+ super(WindowMaster, self).__init__(*args, **kwargs)
+ # 监听剪切板
+ clipboard = QApplication.clipboard()
+ clipboard.dataChanged.connect(self.on_data_changed)
+ # 开启节点
+ host = QRemoteObjectHost(QUrl('tcp://0.0.0.0:' + sys.argv[1]), parent=self)
+ host.enableRemoting(self, 'WindowMaster')
+ self.append('开启节点完成')
+
+ def on_data_changed(self):
+ # 服务端剪贴板变化后发送到客户端
+ clipboard = QApplication.clipboard()
+ clipboard.blockSignals(True)
+ mime_data = clipboard.mimeData()
+ self.SignalUpdateMimeData.emit(
+ mime_data.hasColor(), mime_data.colorData(),
+ mime_data.hasHtml(), mime_data.html(),
+ mime_data.hasImage(), mime_data.imageData(),
+ mime_data.hasText(), mime_data.text(),
+ mime_data.hasUrls(), mime_data.urls(),
+ )
+ clipboard.blockSignals(False)
+
+ @pyqtSlot(
+ bool, QVariant, # color
+ bool, QVariant, # html
+ bool, QVariant, # image
+ bool, QVariant, # text
+ bool, QVariant, # urls
+ bool, QVariant # files
+ )
+ def updateMimeData(self,
+ hasColor, color,
+ hasHtml, html,
+ hasImage, image,
+ hasText, text,
+ hasUrls, urls,
+ hasFiles, files,
+ ):
+ # 客户端剪切板同步到服务端
+ self.append('收到客户端发送的剪贴板')
+ clipboard = QApplication.clipboard()
+ clipboard.blockSignals(True)
+ data = QMimeData()
+ if hasColor:
+ data.setColorData(color)
+ if hasHtml:
+ data.setHtml(html)
+ if hasImage:
+ data.setImageData(image)
+ if hasText:
+ data.setText(text)
+ # if hasUrls:
+ # data.setUrls(urls)
+ if hasFiles:
+ data.setData('')
+ clipboard.setMimeData(data)
+ clipboard.blockSignals(False)
+
+
+if __name__ == '__main__':
+ import cgitb
+
+ cgitb.enable(format='text')
+ from PyQt5.QtWidgets import QApplication
+
+ QLoggingCategory.setFilterRules('qt.remoteobjects.debug=true\n'
+ 'qt.remoteobjects.warning=true')
+
+ app = QApplication(sys.argv)
+ w = WindowMaster()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QtRemoteObjects/SyncUi/ClipboardSlave.py b/QtRemoteObjects/SyncUi/ClipboardSlave.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e271b1c12d81635f1eec8371ae4dcd20cdbcd2e
--- /dev/null
+++ b/QtRemoteObjects/SyncUi/ClipboardSlave.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020/7/31
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ClipboardSlave
+@description:
+"""
+from PyQt5.QtCore import QUrl, pyqtSignal, QVariant, QMimeData
+from PyQt5.QtRemoteObjects import QRemoteObjectNode, QRemoteObjectReplica
+from PyQt5.QtWidgets import QTextBrowser
+
+
+class WindowSlave(QTextBrowser):
+ SignalUpdateMimeData = pyqtSignal(
+ bool, QVariant, # color
+ bool, QVariant, # html
+ bool, QVariant, # image
+ bool, QVariant, # text
+ bool, QVariant, # urls
+ bool, QVariant, # files
+ )
+
+ def __init__(self, *args, **kwargs):
+ super(WindowSlave, self).__init__(*args, **kwargs)
+ # 监听剪切板
+ clipboard = QApplication.clipboard()
+ clipboard.dataChanged.connect(self.on_data_changed)
+ # 加入Master节点
+ node = QRemoteObjectNode(parent=self)
+ node.connectToNode(QUrl('tcp://{}:{}'.format(sys.argv[1], sys.argv[2])))
+ # 获取WindowMaster对象
+ self.windowMaster = node.acquireDynamic('WindowMaster')
+ # 初始化成功后才能去绑定信号等
+ self.windowMaster.initialized.connect(self.onInitialized)
+ # 状态改变 https://doc.qt.io/qt-5/qremoteobjectreplica.html#State-enum
+ self.windowMaster.stateChanged.connect(self.onStateChanged)
+
+ def onStateChanged(self, newState, oldState):
+ if newState == QRemoteObjectReplica.Suspect:
+ self.append('连接丢失')
+
+ def onInitialized(self):
+ self.SignalUpdateMimeData.connect(self.windowMaster.updateMimeData)
+ self.windowMaster.SignalUpdateMimeData.connect(self.updateMimeData)
+ self.append('绑定信号槽完成')
+
+ def on_data_changed(self):
+ # 客户端剪贴板变化后发送到远程
+ print('on_data_changed')
+ clipboard = QApplication.clipboard()
+ clipboard.blockSignals(True)
+ mime_data = clipboard.mimeData()
+ files = mime_data.data('text/uri-list')
+ self.SignalUpdateMimeData.emit(
+ mime_data.hasColor(), mime_data.colorData(),
+ mime_data.hasHtml(), mime_data.html(),
+ mime_data.hasImage(), mime_data.imageData(),
+ mime_data.hasText(), mime_data.text(),
+ mime_data.hasUrls(), mime_data.urls(),
+ True if files else False, files,
+ )
+ clipboard.blockSignals(False)
+
+ def updateMimeData(self,
+ hasColor, color,
+ hasHtml, html,
+ hasImage, image,
+ hasText, text,
+ hasUrls, urls
+ ):
+ # 远程的剪贴板同步到客户端
+ clipboard = QApplication.clipboard()
+ clipboard.blockSignals(True)
+ data = QMimeData()
+ if hasColor:
+ data.setColorData(color)
+ if hasHtml:
+ data.setHtml(html)
+ if hasImage:
+ data.setImageData(image)
+ if hasText:
+ data.setText(text)
+ if hasUrls:
+ data.setUrls(urls)
+ clipboard.setMimeData(data)
+ clipboard.blockSignals(False)
+
+
+if __name__ == '__main__':
+ import sys
+ import cgitb
+
+ cgitb.enable(format='text')
+ from PyQt5.QtWidgets import QApplication
+
+ app = QApplication(sys.argv)
+ w = WindowSlave()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QtRemoteObjects/SyncUi/WindowMaster.py b/QtRemoteObjects/SyncUi/WindowMaster.py
new file mode 100644
index 0000000000000000000000000000000000000000..d93e65617cf9da04ad16de3ab9d515930308eedc
--- /dev/null
+++ b/QtRemoteObjects/SyncUi/WindowMaster.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年8月7日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QtRemoteObjects.SyncUi.WindowMaster
+@description: 主窗口
+"""
+from PyQt5.QtCore import QUrl, QTimer, pyqtSignal, pyqtSlot
+from PyQt5.QtRemoteObjects import QRemoteObjectHost
+from PyQt5.QtWidgets import QWidget, QLineEdit, QVBoxLayout, QCheckBox, \
+ QProgressBar
+
+
+class WindowMaster(QWidget):
+ # 输入框内容变化信号
+ editValueChanged = pyqtSignal(str)
+ # 勾选框变化信号
+ checkToggled = pyqtSignal(bool)
+ # 进度条变化信号
+ progressValueChanged = pyqtSignal(int)
+
+ def __init__(self, *args, **kwargs):
+ super(WindowMaster, self).__init__(*args, **kwargs)
+ self.setupUi()
+
+ # 开启节点
+ host = QRemoteObjectHost(QUrl('local:WindowMaster'), parent=self)
+ host.enableRemoting(self, 'WindowMaster')
+ print('开启节点完成')
+
+ # 定时器更新进度条
+ self._value = 0
+ self.utimer = QTimer(self, timeout=self.updateProgress)
+ self.utimer.start(200)
+
+ def setupUi(self):
+ self.setWindowTitle('WindowMaster')
+ self.resize(300, 400)
+ layout = QVBoxLayout(self)
+ # 输入框(双向同步)
+ self.lineEdit = QLineEdit(self)
+ self.lineEdit.textChanged.connect(self.editValueChanged.emit)
+ # 勾选框(双向同步)
+ self.checkBox = QCheckBox('来勾我啊', self)
+ self.checkBox.toggled.connect(self.checkToggled.emit)
+ # 进度条(Master更新Slave)
+ self.progressBar = QProgressBar(self)
+ self.progressBar.valueChanged.connect(self.progressValueChanged.emit)
+ layout.addWidget(self.lineEdit)
+ layout.addWidget(self.checkBox)
+ layout.addWidget(self.progressBar)
+
+ def updateProgress(self):
+ self._value += 1
+ if self._value > 100:
+ self._value = 0
+ self.progressBar.setValue(self._value)
+
+ @pyqtSlot(str)
+ def updateEdit(self, text):
+ """更新输入框内容的槽函数
+ :param text:
+ """
+ self.lineEdit.setText(text)
+
+ @pyqtSlot(bool)
+ def updateCheck(self, checked):
+ """更新勾选框的槽函数
+ :param checked:
+ """
+ self.checkBox.setChecked(checked)
+
+
+if __name__ == '__main__':
+ import sys
+ from PyQt5.QtWidgets import QApplication
+
+ app = QApplication(sys.argv)
+ w = WindowMaster()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QtRemoteObjects/SyncUi/WindowSlave.py b/QtRemoteObjects/SyncUi/WindowSlave.py
new file mode 100644
index 0000000000000000000000000000000000000000..dc3ea26a0f947da07b4798daceadd8da57a95664
--- /dev/null
+++ b/QtRemoteObjects/SyncUi/WindowSlave.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年8月7日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: QtRemoteObjects.SyncUi.WindowSlave
+@description: 备窗口
+"""
+from PyQt5.QtCore import QUrl
+from PyQt5.QtRemoteObjects import QRemoteObjectNode, QRemoteObjectReplica
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLineEdit, QCheckBox, \
+ QProgressBar, QMessageBox
+
+
+class WindowSlave(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(WindowSlave, self).__init__(*args, **kwargs)
+ self.setupUi()
+ # 加入Master节点
+ node = QRemoteObjectNode(parent=self)
+ node.connectToNode(QUrl('local:WindowMaster'))
+ # 获取WindowMaster对象
+ self.windowMaster = node.acquireDynamic('WindowMaster')
+ # 初始化成功后才能去绑定信号等
+ self.windowMaster.initialized.connect(self.onInitialized)
+ # 状态改变 https://doc.qt.io/qt-5/qremoteobjectreplica.html#State-enum
+ self.windowMaster.stateChanged.connect(self.onStateChanged)
+
+ def setupUi(self):
+ self.setWindowTitle('WindowSlave')
+ self.resize(300, 400)
+ layout = QVBoxLayout(self)
+ # 输入框(双向同步)
+ self.lineEdit = QLineEdit(self)
+ # 勾选框(双向同步)
+ self.checkBox = QCheckBox('来勾我啊', self)
+ # 进度条(Master更新Slave)
+ self.progressBar = QProgressBar(self)
+ layout.addWidget(self.lineEdit)
+ layout.addWidget(self.checkBox)
+ layout.addWidget(self.progressBar)
+
+ def onStateChanged(self, newState, oldState):
+ if newState == QRemoteObjectReplica.Suspect:
+ QMessageBox.critical(self, '错误', '连接丢失')
+
+ def onInitialized(self):
+ # Master和Slave输入框绑定
+ self.windowMaster.editValueChanged.connect(self.lineEdit.setText)
+ self.lineEdit.textChanged.connect(self.windowMaster.updateEdit)
+
+ # Master和Slave勾选框绑定
+ self.windowMaster.checkToggled.connect(self.checkBox.setChecked)
+ self.checkBox.toggled.connect(self.windowMaster.updateCheck)
+
+ # Master进度条同步到Slave
+ self.windowMaster.progressValueChanged.connect(
+ self.progressBar.setValue)
+
+ print('绑定信号槽完成')
+
+
+if __name__ == '__main__':
+ import sys
+ from PyQt5.QtWidgets import QApplication
+
+ app = QApplication(sys.argv)
+ w = WindowSlave()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QtRemoteObjects/__init__.py b/QtRemoteObjects/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QtRemoteObjects/modelview/modelviewclient.py b/QtRemoteObjects/modelview/modelviewclient.py
new file mode 100644
index 0000000000000000000000000000000000000000..1dd65f9f021a2a0ff76f3de0a2341278e6660aa9
--- /dev/null
+++ b/QtRemoteObjects/modelview/modelviewclient.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+
+
+#############################################################################
+##
+## Copyright (C) 2018 Riverbank Computing Limited
+## Copyright (C) 2017 Ford Motor Company
+##
+## This file is part of the examples of PyQt.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## BSD License Usage
+## Alternatively, you may use this file under the terms of the BSD license
+## as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+## $QT_END_LICENSE$
+#############################################################################
+
+
+import sys
+
+try:
+ from PyQt5.QtCore import QLoggingCategory, QUrl
+ from PyQt5.QtRemoteObjects import QRemoteObjectNode
+ from PyQt5.QtWidgets import QApplication, QTreeView
+except ImportError:
+ from PySide2.QtCore import QUrl
+ from PySide2.QtRemoteObjects import QRemoteObjectNode
+ from PySide2.QtWidgets import QApplication, QTreeView
+
+try:
+ QLoggingCategory.setFilterRules('qt.remoteobjects.debug=false\n'
+ 'qt.remoteobjects.warning=false\n'
+ 'qt.remoteobjects.models.debug=false\n'
+ 'qt.remoteobjects.models.debug=false')
+except NameError:
+ pass
+
+app = QApplication(sys.argv)
+
+node = QRemoteObjectNode(QUrl('local:registry'))
+node.setHeartbeatInterval(1000)
+
+view = QTreeView()
+view.setWindowTitle("RemoteView")
+view.resize(640, 480)
+
+model = node.acquireModel('RemoteModel')
+view.setModel(model)
+view.show()
+
+sys.exit(app.exec_())
diff --git a/QtRemoteObjects/modelview/modelviewserver.py b/QtRemoteObjects/modelview/modelviewserver.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2af99c19e25094745458023893f96356100f667
--- /dev/null
+++ b/QtRemoteObjects/modelview/modelviewserver.py
@@ -0,0 +1,177 @@
+#!/usr/bin/python
+
+
+#############################################################################
+##
+## Copyright (C) 2018 Riverbank Computing Limited
+## Copyright (C) 2017 Ford Motor Company
+##
+## This file is part of the PyQt examples.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## BSD License Usage
+## Alternatively, you may use this file under the terms of the BSD license
+## as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+
+import sys
+
+try:
+ from PyQt5.QtCore import pyqtSlot, QLoggingCategory, QModelIndex, QObject, Qt, QTimer, QUrl
+ from PyQt5.QtGui import QColor, QStandardItem, QStandardItemModel
+ from PyQt5.QtRemoteObjects import QRemoteObjectHost, QRemoteObjectRegistryHost
+ from PyQt5.QtWidgets import QApplication, QTreeView
+except ImportError:
+ from PySide2.QtCore import Slot as pyqtSlot, QModelIndex, QObject, Qt, QTimer, QUrl
+ from PySide2.QtGui import QColor, QStandardItem, QStandardItemModel
+ from PySide2.QtRemoteObjects import QRemoteObjectHost, QRemoteObjectRegistryHost
+ from PySide2.QtWidgets import QApplication, QTreeView
+
+
+class TimerHandler(QObject):
+
+ def __init__(self, model, parent=None):
+ super().__init__(parent)
+
+ self._model = model
+
+ @pyqtSlot()
+ def changeData(self):
+ for i in range(10, 50):
+ self._model.setData(self._model.index(i, 1), QColor(Qt.blue),
+ Qt.BackgroundRole)
+
+ @pyqtSlot()
+ def insertData(self):
+ self._model.insertRows(2, 9)
+
+ for i in range(2, 11):
+ self._model.setData(self._model.index(i, 1), QColor(Qt.green),
+ Qt.BackgroundRole)
+ self._model.setData(self._model.index(i, 1), "InsertedRow",
+ Qt.DisplayRole)
+
+ @pyqtSlot()
+ def removeData(self):
+ self._model.removeRows(2, 4)
+
+ @pyqtSlot()
+ def changeFlags(self):
+ item = self._model.item(0, 0)
+ item.setEnabled(False)
+
+ item = item.child(0, 0)
+ item.setFlags(item.flags() & Qt.ItemIsSelectable)
+
+ @pyqtSlot()
+ def moveData(self):
+ self._model.moveRows(QModelIndex(), 2, 4, QModelIndex(), 10)
+
+
+def addChild(numChildren, nestingLevel):
+ result = []
+
+ if nestingLevel == 0:
+ return result
+
+ for i in range(numChildren):
+ child = QStandardItem(
+ "Child num {}, nesting level {}".format(i + 1, nestingLevel))
+
+ if i == 0:
+ child.appendRow(addChild(numChildren, nestingLevel - 1))
+
+ result.append(child)
+
+ return result
+
+
+if __name__ == '__main__':
+
+ try:
+ QLoggingCategory.setFilterRules('qt.remoteobjects.debug=false\n'
+ 'qt.remoteobjects.warning=false')
+ except NameError:
+ pass
+
+ app = QApplication(sys.argv)
+
+ sourceModel = QStandardItemModel()
+ sourceModel.setHorizontalHeaderLabels(
+ ["First Column with spacing", "Second Column with spacing"])
+
+ for i in range(10000):
+ firstItem = QStandardItem("FancyTextNumber {}".format(i))
+ if i == 0:
+ firstItem.appendRow(addChild(2, 2))
+
+ secondItem = QStandardItem("FancyRow2TextNumber {}".format(i))
+ if i % 2 == 0:
+ firstItem.setBackground(Qt.red)
+
+ sourceModel.invisibleRootItem().appendRow([firstItem, secondItem])
+
+ # Needed by QMLModelViewClient.
+ roleNames = {
+ Qt.DisplayRole: b'_text',
+ Qt.BackgroundRole: b'_color'
+ }
+ sourceModel.setItemRoleNames(roleNames)
+
+ roles = [Qt.DisplayRole, Qt.BackgroundRole]
+
+ node = QRemoteObjectRegistryHost(QUrl('local:registry'))
+
+ node2 = QRemoteObjectHost(QUrl('local:replica'), QUrl('local:registry'))
+ node2.enableRemoting(sourceModel, 'RemoteModel', roles)
+
+ view = QTreeView()
+ view.setWindowTitle("SourceView")
+ view.setModel(sourceModel)
+ view.show()
+
+ handler = TimerHandler(sourceModel)
+ QTimer.singleShot(5000, handler.changeData)
+ QTimer.singleShot(10000, handler.insertData)
+ QTimer.singleShot(11000, handler.changeFlags)
+ QTimer.singleShot(12000, handler.removeData)
+ QTimer.singleShot(13000, handler.moveData)
+
+ sys.exit(app.exec_())
diff --git a/QtRemoteObjects/simpleswitch/directconnectdynamicclient.py b/QtRemoteObjects/simpleswitch/directconnectdynamicclient.py
new file mode 100644
index 0000000000000000000000000000000000000000..abee848026dcf64060b357bbf4fd956a9efecca2
--- /dev/null
+++ b/QtRemoteObjects/simpleswitch/directconnectdynamicclient.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+
+
+#############################################################################
+##
+## Copyright (C) 2018 Riverbank Computing Limited
+## Copyright (C) 2017 Ford Motor Company
+##
+## This file is part of the PyQt examples.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## BSD License Usage
+## Alternatively, you may use this file under the terms of the BSD license
+## as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+
+import sys
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QCoreApplication, QObject, QUrl
+from PyQt5.QtRemoteObjects import QRemoteObjectNode
+
+
+class DynamicClient(QObject):
+ # This signal is connected with server_slot() slot of the source object and
+ # echoes back the switch state received from the source.
+ echoSwitchState = pyqtSignal(bool)
+
+ def __init__(self, replica, parent=None):
+ super().__init__(parent)
+
+ self._replica = replica
+ self._clientSwitchState = False
+
+ replica.initialized.connect(self.initConnection)
+
+ @pyqtSlot(bool)
+ def recSwitchState(self, value):
+ self._clientSwitchState = self._replica.property('currState')
+
+ print("Received source state", value, self._clientSwitchState)
+
+ # Emit the signal to echo the received state back to the server.
+ self.echoSwitchState.emit(self._clientSwitchState)
+
+ @pyqtSlot()
+ def initConnection(self):
+ # Connect the replica source signal currStateChanged() with the
+ # client's recSwitchState() slot to receive the source's current state.
+ self._replica.currStateChanged.connect(self.recSwitchState)
+
+ # Connect the client's echoSwitchState() signal with replica's
+ # server_slot() to echo back the received state.
+ self.echoSwitchState.connect(self._replica.server_slot)
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+
+ # Create the remote object node.
+ repNode = QRemoteObjectNode()
+
+ # Connect with the remote host node.
+ repNode.connectToNode(QUrl('local:replica'))
+
+ # Acquire a replica of the source from the host node.
+ replica = repNode.acquireDynamic('SimpleSwitch')
+
+ # Create the client switch object and pass the replica to it.
+ rswitch = DynamicClient(replica)
+
+ sys.exit(app.exec_())
diff --git a/QtRemoteObjects/simpleswitch/directconnectdynamicserver.py b/QtRemoteObjects/simpleswitch/directconnectdynamicserver.py
new file mode 100644
index 0000000000000000000000000000000000000000..256d63bd5b836c74885e9e721d833465de636418
--- /dev/null
+++ b/QtRemoteObjects/simpleswitch/directconnectdynamicserver.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+
+
+#############################################################################
+##
+## Copyright (C) 2018 Riverbank Computing Limited
+## Copyright (C) 2017 Ford Motor Company
+##
+## This file is part of the PyQt examples.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## BSD License Usage
+## Alternatively, you may use this file under the terms of the BSD license
+## as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+
+import sys
+
+from PyQt5.QtCore import (pyqtProperty, pyqtSignal, pyqtSlot, QCoreApplication,
+ QObject, QTimer, QUrl)
+from PyQt5.QtRemoteObjects import QRemoteObjectHost
+
+
+class SimpleSwitch(QObject):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self._currState = False
+
+ self._stateChangeTimer = QTimer(self)
+ self._stateChangeTimer.timeout.connect(self._timeout)
+ self._stateChangeTimer.start(2000)
+
+ print("Source node started")
+
+ # PyQt does not support the use of static source types defined in .rep
+ # files. However we can manually specify a dynamic type that matches a
+ # .rep defined type by defining properties, signals and slots in the same
+ # order. We also have to account for any internals also generated by the
+ # .rep generator. At the moment this only includes an extra 'push' slot
+ # for each property (that never seems to get called). This allows this
+ # example to act as a server for Qt's C++ 'directconnectclient' example.
+ # It is not necessary when using with clients that use dynamic source types
+ # (written using either C++ or Python).
+ @pyqtSlot()
+ def pushCurrState(self, currState):
+ pass
+
+ def _get_currState(self):
+ return self._currState
+
+ def _set_currState(self, value):
+ # If the value has changed then update it and emit the notify signal.
+ if self._currState != value:
+ self._currState = value
+ self.currStateChanged.emit(value)
+
+ # The property's notify signal.
+ currStateChanged = pyqtSignal(bool)
+
+ # The property exposed to a remote client.
+ currState = pyqtProperty(bool, fget=_get_currState, fset=_set_currState,
+ notify=currStateChanged)
+
+ # The slot exposed to a remote client.
+ @pyqtSlot(bool)
+ def server_slot(self, clientState):
+ # The switch state echoed back by the client.
+ print("Replica state is", clientState)
+
+ def _timeout(self):
+ # Note that we don't decorate this callable so that it doesn't get
+ # exposed in a replica.
+ self.currState = not self.currState
+
+ print("Source state is", self.currState)
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+
+ # Create the simple switch.
+ srcSwitch = SimpleSwitch()
+
+ # Create the host object node.
+ srcNode = QRemoteObjectHost(QUrl('local:replica'))
+
+ # Enable remoting.
+ srcNode.enableRemoting(srcSwitch, 'SimpleSwitch')
+
+ sys.exit(app.exec_())
diff --git a/QtRemoteObjects/simpleswitch/registryconnecteddynamicclient.py b/QtRemoteObjects/simpleswitch/registryconnecteddynamicclient.py
new file mode 100644
index 0000000000000000000000000000000000000000..56b1d3814cc1cb3eb296691c418e39ce4e5f6746
--- /dev/null
+++ b/QtRemoteObjects/simpleswitch/registryconnecteddynamicclient.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+
+
+#############################################################################
+##
+## Copyright (C) 2018 Riverbank Computing Limited
+## Copyright (C) 2017 Ford Motor Company
+##
+## This file is part of the PyQt examples.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## BSD License Usage
+## Alternatively, you may use this file under the terms of the BSD license
+## as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+
+import sys
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QCoreApplication, QObject, QUrl
+from PyQt5.QtRemoteObjects import QRemoteObjectNode
+
+
+class DynamicClient(QObject):
+ # This signal is connected with server_slot() slot of the source object and
+ # echoes back the switch state received from the source.
+ echoSwitchState = pyqtSignal(bool)
+
+ def __init__(self, replica, parent=None):
+ super().__init__(parent)
+
+ self._replica = replica
+ self._clientSwitchState = False
+
+ replica.initialized.connect(self.initConnection)
+
+ @pyqtSlot(bool)
+ def recSwitchState(self, value):
+ self._clientSwitchState = self._replica.property('currState')
+
+ print("Received source state", value, self._clientSwitchState)
+
+ # Emit the signal to echo the received state back to the server.
+ self.echoSwitchState.emit(self._clientSwitchState)
+
+ @pyqtSlot()
+ def initConnection(self):
+ # Connect the replica source signal currStateChanged() with the
+ # client's recSwitchState() slot to receive the source's current state.
+ self._replica.currStateChanged.connect(self.recSwitchState)
+
+ # Connect the client's echoSwitchState() signal with replica's
+ # server_slot() to echo back the received state.
+ self.echoSwitchState.connect(self._replica.server_slot)
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+
+ # Create the remote object node.
+ repNode = QRemoteObjectNode(QUrl('local:registry'))
+
+ # Acquire a replica of the source from the host node.
+ replica = repNode.acquireDynamic('SimpleSwitch')
+
+ # Create the client switch object and pass the replica to it.
+ rswitch = DynamicClient(replica)
+
+ sys.exit(app.exec_())
diff --git a/QtRemoteObjects/simpleswitch/registryconnecteddynamicserver.py b/QtRemoteObjects/simpleswitch/registryconnecteddynamicserver.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7760b4a3aa167c404d2e3513e05469351627b2a
--- /dev/null
+++ b/QtRemoteObjects/simpleswitch/registryconnecteddynamicserver.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+
+
+#############################################################################
+##
+## Copyright (C) 2018 Riverbank Computing Limited
+## Copyright (C) 2017 Ford Motor Company
+##
+## This file is part of the PyQt examples.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## BSD License Usage
+## Alternatively, you may use this file under the terms of the BSD license
+## as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+
+import sys
+
+from PyQt5.QtCore import (pyqtProperty, pyqtSignal, pyqtSlot, QCoreApplication,
+ QObject, QTimer, QUrl)
+from PyQt5.QtRemoteObjects import QRemoteObjectHost, QRemoteObjectRegistryHost
+
+
+class SimpleSwitch(QObject):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self._currState = False
+
+ self._stateChangeTimer = QTimer(self)
+ self._stateChangeTimer.timeout.connect(self._timeout)
+ self._stateChangeTimer.start(2000)
+
+ print("Source node started")
+
+ # PyQt does not support the use of static source types defined in .rep
+ # files. However we can manually specify a dynamic type that matches a
+ # .rep defined type by defining properties, signals and slots in the same
+ # order. We also have to account for any internals also generated by the
+ # .rep generator. At the moment this only includes an extra 'push' slot
+ # for each property (that never seems to get called). This allows this
+ # example to act as a server for Qt's C++ 'directconnectclient' example.
+ # It is not necessary when using with clients that use dynamic source types
+ # (written using either C++ or Python).
+ @pyqtSlot()
+ def pushCurrState(self, currState):
+ pass
+
+ def _get_currState(self):
+ return self._currState
+
+ def _set_currState(self, value):
+ # If the value has changed then update it and emit the notify signal.
+ if self._currState != value:
+ self._currState = value
+ self.currStateChanged.emit(value)
+
+ # The property's notify signal.
+ currStateChanged = pyqtSignal(bool)
+
+ # The property exposed to a remote client.
+ currState = pyqtProperty(bool, fget=_get_currState, fset=_set_currState,
+ notify=currStateChanged)
+
+ # The slot exposed to a remote client.
+ @pyqtSlot(bool)
+ def server_slot(self, clientState):
+ # The switch state echoed back by the client.
+ print("Replica state is", clientState)
+
+ def _timeout(self):
+ # Note that we don't decorate this callable so that it doesn't get
+ # exposed in a replica.
+ self.currState = not self.currState
+
+ print("Source state is", self.currState)
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+
+ # Create the simple switch.
+ srcSwitch = SimpleSwitch()
+
+ # Create the node that hosts the registry. This could be in a separate
+ # process.
+ regNode = QRemoteObjectRegistryHost(QUrl('local:registry'))
+
+ # Create the host object node. This will connect to the registry node
+ # rather than to a client.
+ srcNode = QRemoteObjectHost(QUrl('local:replica'), QUrl('local:registry'))
+
+ # Enable remoting.
+ srcNode.enableRemoting(srcSwitch, 'SimpleSwitch')
+
+ sys.exit(app.exec_())
diff --git a/QtWinExtras/README.en.md b/QtWinExtras/README.en.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/QtWinExtras/README.md b/QtWinExtras/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c560e34e6a8754e0b283a791b8fce43b72d280b7
--- /dev/null
+++ b/QtWinExtras/README.md
@@ -0,0 +1,19 @@
+# QtWinExtras
+
+- 目录
+ - [任务栏进度条](#1任务栏进度条)
+ - [任务栏缩略图工具按钮](#2任务栏缩略图工具按钮)
+
+## 1、任务栏进度条
+[运行 TaskbarProgress.py](TaskbarProgress.py)
+
+`QWinTaskbarProgress`类似和`QProgressBar`一样的操作
+
+
+
+## 2、任务栏缩略图工具按钮
+[运行 ThumbnailToolBar.py](ThumbnailToolBar.py)
+
+`QWinThumbnailToolBar`和`QWinThumbnailToolButton`的组合实现音乐播放器的播放、上下一曲按钮
+
+
\ No newline at end of file
diff --git a/QtWinExtras/ScreenShot/TaskbarProgress.gif b/QtWinExtras/ScreenShot/TaskbarProgress.gif
new file mode 100644
index 0000000000000000000000000000000000000000..5193d963ddddd34a8aed68ae2ff9af585bd9fb69
Binary files /dev/null and b/QtWinExtras/ScreenShot/TaskbarProgress.gif differ
diff --git a/QtWinExtras/ScreenShot/ThumbnailToolBar.gif b/QtWinExtras/ScreenShot/ThumbnailToolBar.gif
new file mode 100644
index 0000000000000000000000000000000000000000..eae88c3e79a8474835edf7ec6224a66687103995
Binary files /dev/null and b/QtWinExtras/ScreenShot/ThumbnailToolBar.gif differ
diff --git a/QtWinExtras/TaskbarProgress.py b/QtWinExtras/TaskbarProgress.py
new file mode 100644
index 0000000000000000000000000000000000000000..96b3a0a31be7f7eef93428fcb33a2521815fe648
--- /dev/null
+++ b/QtWinExtras/TaskbarProgress.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020/7/1
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: TaskbarProgress
+@description:
+"""
+
+import cgitb
+import sys
+
+try:
+ from PyQt5.QtCore import QTimer
+ from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QSpinBox, QPushButton, QLabel
+ from PyQt5.QtWinExtras import QWinTaskbarButton
+except ImportError:
+ from PySide2.QtCore import QTimer
+ from PySide2.QtWidgets import QWidget, QApplication, QGridLayout, QSpinBox, QPushButton, QLabel
+ from PySide2.QtWinExtras import QWinTaskbarButton
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ # 获取任务栏按钮
+ self.taskButton = QWinTaskbarButton(self)
+ # 获取任务栏进度条
+ self.taskProgress = self.taskButton.progress()
+ # 定时器模拟进度
+ self.timerProgress = QTimer(self)
+ self.timerProgress.timeout.connect(self.update_progress)
+
+ self.setup_ui()
+
+ def showEvent(self, event):
+ super(Window, self).showEvent(event)
+ if not self.taskButton.window():
+ # 必须等窗口显示后设置才有效,或者通过软件流程在适当的时候设置也可以
+ self.taskButton.setWindow(self.windowHandle())
+ self.taskProgress.show()
+
+ def closeEvent(self, event):
+ self.timerProgress.stop()
+ super(Window, self).closeEvent(event)
+
+ def setup_ui(self):
+ layout = QGridLayout(self)
+
+ # 设置最新小值和最大值
+ self.spinBoxMin = QSpinBox(self)
+ self.spinBoxMax = QSpinBox(self)
+ self.spinBoxMax.setMaximum(100)
+ self.spinBoxMax.setValue(100)
+ layout.addWidget(self.spinBoxMin, 0, 0)
+ layout.addWidget(self.spinBoxMax, 0, 1)
+ layout.addWidget(QPushButton('设置范围值', self, clicked=self.set_range), 0, 2)
+
+ # 设置当前值
+ self.spinBoxCur = QSpinBox(self)
+ self.spinBoxCur.setMaximum(100)
+ self.spinBoxCur.setValue(50)
+ layout.addWidget(self.spinBoxCur, 0, 3)
+ layout.addWidget(QPushButton('设置当前值', self, clicked=self.set_current_value), 0, 4)
+
+ # 功能按钮
+ layout.addWidget(QPushButton('隐藏', self, clicked=self.set_show_hide), 1, 0)
+ layout.addWidget(QPushButton('暂停', self, clicked=self.set_pause_resume), 1, 1)
+ layout.addWidget(QPushButton('重置', self, clicked=self.set_reset), 1, 2)
+ layout.addWidget(QPushButton('停止', self, clicked=self.set_stop), 1, 3)
+ layout.addWidget(QPushButton('不可见', self, clicked=self.set_visible), 1, 4)
+
+ # 模拟进度
+ layout.addWidget(QPushButton('模拟进度动画', self, clicked=self.start_progress), 2, 0, 1, 5)
+
+ # 状态
+ layout.addWidget(QLabel('暂停信号 :', self), 3, 0)
+ self.labelPause = QLabel(self)
+ layout.addWidget(self.labelPause, 3, 1)
+ self.taskProgress.pausedChanged.connect(lambda v: self.labelPause.setText(str(v)))
+
+ layout.addWidget(QLabel('停止信号 :', self), 4, 0)
+ self.labelStop = QLabel(self)
+ layout.addWidget(self.labelStop, 4, 1)
+ self.taskProgress.stoppedChanged.connect(lambda v: self.labelStop.setText(str(v)))
+
+ layout.addWidget(QLabel('值改变信号:', self), 5, 0)
+ self.labelValue = QLabel(self)
+ layout.addWidget(self.labelValue, 5, 1)
+ self.taskProgress.valueChanged.connect(lambda v: self.labelValue.setText(str(v)))
+
+ layout.addWidget(QLabel('可见度信号:', self), 6, 0)
+ self.labelVisible = QLabel(self)
+ layout.addWidget(self.labelVisible, 6, 1)
+ self.taskProgress.visibilityChanged.connect(lambda v: self.labelVisible.setText(str(v)))
+
+ def set_range(self):
+ # 设置进度条范围值
+ vmin = min(self.spinBoxMin.value(), self.spinBoxMax.value())
+ vmax = max(self.spinBoxMin.value(), self.spinBoxMax.value())
+ self.taskProgress.setRange(vmin, vmax)
+
+ def set_current_value(self):
+ # 设置进度条当前值
+ self.taskProgress.setValue(self.spinBoxCur.value())
+
+ def set_show_hide(self):
+ # 显示/隐藏
+ visible = self.taskProgress.isVisible()
+ # 也可以使用self.taskProgress.setVisible
+ if visible:
+ self.taskProgress.hide()
+ self.sender().setText('显示')
+ else:
+ self.taskProgress.show()
+ self.sender().setText('隐藏')
+
+ def set_pause_resume(self):
+ # 暂停/恢复
+ paused = self.taskProgress.isPaused()
+ # 也可以使用self.taskProgress.setPaused
+ if paused:
+ self.taskProgress.resume()
+ self.timerProgress.start(100)
+ self.sender().setText('暂停')
+ else:
+ self.taskProgress.pause()
+ self.timerProgress.stop()
+ self.sender().setText('恢复')
+
+ def set_reset(self):
+ # 重置
+ self.taskProgress.reset()
+ paused = self.taskProgress.isPaused()
+ if not paused:
+ self.timerProgress.stop()
+ self.timerProgress.start(100)
+
+ def set_stop(self):
+ # 停止
+ self.timerProgress.stop()
+ self.taskProgress.stop()
+ self.setEnabled(False)
+
+ def set_visible(self):
+ # 可见/不可见
+ visible = self.taskProgress.isVisible()
+ self.taskProgress.setVisible(not visible)
+ self.sender().setText('可见' if visible else '不可见')
+
+ def start_progress(self):
+ # 模拟进度
+ self.timerProgress.start(100)
+ self.sender().setEnabled(False)
+
+ def update_progress(self):
+ value = self.taskProgress.value()
+ value += 1
+ if value > self.taskProgress.maximum():
+ value = 0
+ self.taskProgress.setValue(value)
+
+
+if __name__ == '__main__':
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/QtWinExtras/ThumbnailToolBar.py b/QtWinExtras/ThumbnailToolBar.py
new file mode 100644
index 0000000000000000000000000000000000000000..8aab7b7538d1f559acdf529bb0157abde42fcb5c
--- /dev/null
+++ b/QtWinExtras/ThumbnailToolBar.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020/7/3
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ThumbnailToolBar
+@description:
+"""
+
+import cgitb
+import sys
+
+try:
+ from PyQt5.QtWidgets import QWidget, QApplication, QLabel, QStyle, QVBoxLayout
+ from PyQt5.QtWinExtras import QWinThumbnailToolBar, QWinThumbnailToolButton
+except ImportError:
+ from PySide2.QtWidgets import QWidget, QApplication, QLabel, QStyle, QVBoxLayout
+ from PySide2.QtWinExtras import QWinThumbnailToolBar, QWinThumbnailToolButton
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ self.countPrev = 0
+ self.countNext = 0
+ self.init_ui()
+
+ def init_ui(self):
+ layout = QVBoxLayout(self)
+ self.labelPrev = QLabel(self)
+ self.labelControl = QLabel('暂停播放', self)
+ self.labelNext = QLabel(self)
+ layout.addWidget(self.labelPrev)
+ layout.addWidget(self.labelControl)
+ layout.addWidget(self.labelNext)
+
+ # 任务栏缩略图工具条
+ self.toolBar = QWinThumbnailToolBar(self)
+ # 上一首,播放/暂停,下一首按钮
+ self.toolBtnPrev = QWinThumbnailToolButton(self.toolBar)
+ self.toolBtnPrev.setToolTip('上一首')
+ self.toolBtnPrev.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipBackward))
+ self.toolBtnPrev.clicked.connect(self.set_prev)
+ self.toolBar.addButton(self.toolBtnPrev)
+
+ self.toolBtnControl = QWinThumbnailToolButton(self.toolBar)
+ self.toolBtnControl.setToolTip('播放')
+ self.toolBtnControl.setProperty('status', 0)
+ self.toolBtnControl.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
+ self.toolBtnControl.clicked.connect(self.set_control)
+ self.toolBar.addButton(self.toolBtnControl)
+
+ self.toolBtnNext = QWinThumbnailToolButton(self.toolBar)
+ self.toolBtnNext.setToolTip('下一首')
+ self.toolBtnNext.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipForward))
+ self.toolBtnNext.clicked.connect(self.set_next)
+ self.toolBar.addButton(self.toolBtnNext)
+
+ def set_prev(self):
+ self.countPrev += 1
+ self.labelPrev.setText('点击上一首按钮: %d 次' % self.countPrev)
+
+ def set_next(self):
+ self.countNext += 1
+ self.labelNext.setText('点击下一首按钮: %d 次' % self.countNext)
+
+ def set_control(self):
+ if self.toolBtnControl.property('status') == 0:
+ self.labelControl.setText('正在播放')
+ self.toolBtnControl.setProperty('status', 1)
+ self.toolBtnControl.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
+ else:
+ self.labelControl.setText('暂停播放')
+ self.toolBtnControl.setProperty('status', 0)
+ self.toolBtnControl.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
+
+ def showEvent(self, event):
+ super(Window, self).showEvent(event)
+ if not self.toolBar.window():
+ # 必须等窗口显示后设置才有效,或者通过软件流程在适当的时候设置也可以
+ self.toolBar.setWindow(self.windowHandle())
+
+
+if __name__ == '__main__':
+ cgitb.enable(format='text')
+ app = QApplication(sys.argv)
+ w = Window()
+ w.show()
+ sys.exit(app.exec_())
diff --git a/README.md b/README.md
index d366609284a8dbdab3a95675cd2b2fe3795d116a..2c6817b4bdcacc1f3cc61d6244ef5bca096cb6bd 100644
--- a/README.md
+++ b/README.md
@@ -1,81 +1,302 @@
# 各种各样的PyQt测试和例子
-[](https://pyqt5.com)
+[](https://pyqt.site)
+[](https://codebeat.co/projects/github-com-pyqt5-pyqt-master)
+[](https://996.icu/#/zh_CN)
+[](https://github.com/996icu/996.ICU/blob/master/LICENSE)
-https://pyqt5.com 社区是专门针对PyQt5学习和提升开设的博客网站,分享大家平时学习中记录的笔记和例子,以及对遇到的问题进行收集整理。
+[https://pyqt.site](https://pyqt.site) 论坛是专门针对PyQt5学习和提升开设的网站,分享大家平时学习中记录的笔记和例子,以及对遇到的问题进行收集整理。
+
+[](https://github.com/PyQt5/PyQt)
+[](https://github.com/PyQt5/PyQt)
+[](https://github.com/PyQt5/PyQt/fork)
+
+如果您觉得这里的东西对您有帮助,别忘了帮忙点一颗:star:小星星:star:
+
+[客户端下载](https://github.com/PyQt5/PyQtClient/releases) | [自定义控件](https://github.com/PyQt5/CustomWidgets)
+
+## QQ群
+
+ or
+
+ [PyQt 学习](https://jq.qq.com/?_wv=1027&k=5QVVEdF)
+
+ [PyQt 频道](https://pd.qq.com/s/157c1hiay)
+
+## 状态
+
## 目录
-| 分类 | 目录 |
-|:-------|:-------|
-| Demo | [Demo](Demo)
-| ActiveX | [QAxWidget](QAxWidget)
-| 日历 | [QCalendarWidget](QCalendarWidget)
-| 图标 | [QChart](QChart)
-| 复选框 | [QCheckBox](QCheckBox)
-| 列视图 | [QColumnView](QColumnView)
-| 组合框 | [QComboBox](QComboBox)
-| 日期时间 | [QDateTime](QDateTime)
-| 日期时间编辑 | [QDateTimeEdit](QDateTimeEdit)
-| 刻度盘 | [QDial](QDial)
-| 停靠窗口 | [QDockWidget](QDockWidget)
-| 双精度编辑 | [QDoubleSpinBox](QDoubleSpinBox)
-| 文件系统模型 | [QFileSystemModel](QFileSystemModel)
-| 流布局 | [QFlowLayout](QFlowLayout)
-| 字体选择 | [QFontComboBox](QFontComboBox)
-| 表单布局 | [QFormLayout](QFormLayout)
-| 边框容器 | [QFrame](QFrame)
-| 边框阴影 | [QGraphicsDropShadowEffect](QGraphicsDropShadowEffect)
-| 图形视图 | [QGraphicsView](QGraphicsView)
-| 网格布局 | [QGridLayout](QGridLayout)
-| 分组容器 | [QGroupBox](QGroupBox)
-| 横向布局 | [QHBoxLayout](QHBoxLayout)
-| 文本图片 | [QLabel](QLabel)
-| 类液晶屏显示 | [QLCDNumber](QLCDNumber)
-| 行输入框 | [QLineEdit](QLineEdit)
-| 列表视图 | [QListView](QListView)
-| 列表控件 | [QListWidget](QListWidget)
-| 子区域 | [QMdiArea](QMdiArea)
-| 菜单 | [QMenu](QMenu)
-| 消息提示框 | [QMessageBox](QMessageBox)
-| OpenGL | [QOpenGLWidget](QOpenGLWidget)
-| 纯文本 | [QPlainTextEdit](QPlainTextEdit)
-| 进度条 | [QProgressBar](QProgressBar)
-| 属性动画 | [QPropertyAnimation](QPropertyAnimation)
-| 代理样式 | [QProxyStyle](QProxyStyle)
-| 按钮 | [QPushButton](QPushButton)
-| 单选框 | [QRadioButton](QRadioButton)
-| 滚动区 | [QScrollArea](QScrollArea)
-| 滑动条 | [QScrollBar](QScrollBar)
-| 串口 | [QSerialPort](QSerialPort)
-| 拉动条 | [QSlider](QSlider)
-| 拉伸条 | [QSpacerItem](QSpacerItem)
-| 单精度编辑 | [QSpinBox](QSpinBox)
-| 拆分窗口 | [QSplitter](QSplitter)
-| 堆叠布局 | [QStackedLayout](QStackedLayout)
-| 堆叠控件 | [QStackedWidget](QStackedWidget)
-| 表格视图 | [QTableView](QTableView)
-| 表格控件 | [QTableWidget](QTableWidget)
-| 多标签 | [QTabWidget](QTabWidget)
-| 富文本 | [QTextBrowser](QTextBrowser)
-| 多行富文本 | [QTextEdit](QTextEdit)
-| 多线程 | [QThread](QThread)
-| 时间编辑 | [QTimeEdit](QTimeEdit)
-| 工具箱 | [QToolBox](QToolBox)
-| 工具按钮 | [QToolButton](QToolButton)
-| 树形视图 | [QTreeView](QTreeView)
-| 树形控件 | [QTreeWidget](QTreeWidget)
-| 纵向布局 | [QVBoxLayout](QVBoxLayout)
-| WebEngine | [QWebEngineView](QWebEngineView)
-| WebView | [QWebView](QWebView)
-| QWidget | [QWidget](QWidget)
-
-# QQ群
-
- - [PyQt 学习](https://jq.qq.com/?_wv=1027&k=5QVVEdF)
-
-
-# [Donate-打赏](Donate)
- or
-
-[一些Qt写的三方APP](https://github.com/892768447/PyQt/wiki/3rd-party-applications)
+- Layouts
+ - [QVBoxLayout](QVBoxLayout)
+ - [垂直布局](QVBoxLayout#1垂直布局)
+ - [边距和间隔](QVBoxLayout#2边距和间隔)
+ - [比例分配](QVBoxLayout#3比例分配)
+ - [QHBoxLayout](QHBoxLayout)
+ - [水平布局](QHBoxLayout#1水平布局)
+ - [边距和间隔](QHBoxLayout#2边距和间隔)
+ - [比例分配](QHBoxLayout#3比例分配)
+ - [QGridLayout](QGridLayout)
+ - [音乐热歌列表](QGridLayout/HotPlaylist.py)
+ - [QFormLayout](QFormLayout)
+ - [QFlowLayout](QFlowLayout)
+ - [音乐热歌列表](QFlowLayout/HotPlaylist.py)
+
+- Spacers
+ - [Horizontal Spacer](QSpacerItem)
+ - [Vertical Spacer](QSpacerItem)
+
+- Buttons
+ - [QPushButton](QPushButton)
+ - [普通样式](QPushButton/NormalStyle.py)
+ - [按钮底部线条进度](QPushButton/BottomLineProgress.py)
+ - [按钮文字旋转进度](QPushButton/FontRotate.py)
+ - [按钮常用信号](QPushButton/SignalsExample.py)
+ - [QToolButton](QToolButton)
+ - [QRadioButton](QRadioButton)
+ - [QCheckBox](QCheckBox)
+
+- Item Views
+ - [QListView](QListView)
+ - [显示自定义Widget](QListView/CustomWidgetItem.py)
+ - [显示自定义Widget并排序](QListView/CustomWidgetSortItem.py)
+ - [自定义角色排序](QListView/SortItemByRole.py)
+ - [QTreeView](QTreeView)
+ - [QTableView](QTableView)
+ - [表格内容复制](QTableView/CopyContent.py)
+ - [QColumnView](QColumnView)
+ - [QUndoView](QUndoView)
+
+- Item Widgets
+ - [QListWidget](QListWidget)
+ - [删除自定义Item](QListWidget/DeleteCustomItem.py)
+ - [自定义可拖拽Item](QListWidget/DragDrop.py)
+ - [音乐热歌列表](QListWidget/HotPlaylist.py)
+ - [仿折叠控件效果](QListWidget/FoldWidget.py)
+ - [列表常用信号](QListWidget/SignalsExample.py)
+ - [在item中添加图标](Test/partner_625781186/13.combo_listwidget)
+ - [QTreeWidget](QTreeWidget)
+ - [通过json数据生成树形结构](QTreeWidget/ParsingJson.py)
+ - [拖拽显示为图片](Test/partner_625781186/12.1拖拽显示为图片)
+ - [点击父节点全选/取消全选子节点](QTreeWidget/testTreeWidget.py)
+ - [禁止父节点](QTreeWidget/ParentNodeForbid.py)
+ - [QTableWidget](QTableWidget)
+ - [Sqlalchemy动态拼接字段查询显示表格](QTableWidget/SqlQuery.py)
+ - [TableWidget嵌入部件](QTableWidget/TableWidget.py)
+
+- Containers
+ - [QGroupBox](QGroupBox)
+ - [QScrollArea](QScrollArea)
+ - [仿QQ设置面板](QScrollArea/QQSettingPanel.py)
+ - [QToolBox](QToolBox)
+ - [QTabWidget](QTabWidget)
+ - [QStackedWidget](QStackedWidget)
+ - [左侧选项卡](QStackedWidget/LeftTabStacked.py)
+ - [QFrame](QFrame)
+ - [QWidget](QWidget)
+ - [样式表测试](QWidget/WidgetStyle.py)
+ - [QMdiArea](QMdiArea)
+ - [QDockWidget](QDockWidget)
+
+- Input Widgets
+ - [QComboBox](QComboBox)
+ - [下拉数据关联](QComboBox/CityLinkage.py)
+ - [文本居中显示](QComboBox/CenterText.py)
+ - [QFontComboBox](QFontComboBox)
+ - [QLineEdit](QLineEdit)
+ - [QTextEdit](QTextEdit)
+ - [文本查找高亮](QTextEdit/HighlightText.py)
+ - [QPlainTextEdit](QPlainTextEdit)
+ - [QSpinBox](QSpinBox)
+ - [QDoubleSpinBox](QDoubleSpinBox)
+ - [QTimeEdit](QTimeEdit)
+ - [QDateTime](QDateTime)
+ - [QDial](QDial)
+ - [QScrollBar](QScrollBar)
+ - [滚动条样式美化](QScrollBar/StyleScrollBar.py)
+ - [QSlider](QSlider)
+ - [滑动条点击定位](QSlider/ClickJumpSlider.py)
+ - [双层圆环样式](QSlider/QssQSlider.py)
+ - [低频率值变化](QSlider/LfSlider.py)
+
+- Display Widgets
+ - [QLabel](QLabel)
+ - [图片加载显示](QLabel/ShowImage.py)
+ - [图片旋转](QLabel/ImageRotate.py)
+ - [仿网页图片错位显示](QLabel/ImageSlipped.py)
+ - [显示.9格式图片(气泡)](QLabel/NinePatch.py)
+ - [圆形图片](QLabel/CircleImage.py)
+ - [QTextBrowser](QTextBrowser)
+ - [动态加载图片](QTextBrowser/DynamicRes.py)
+ - [QGraphicsView](QGraphicsView)
+ - [绘制世界地图](QGraphicsView/WorldMap.py)
+ - [添加QWidget](QGraphicsView/AddQWidget.py)
+ - [图片查看器](QGraphicsView/ImageView.py)
+ - [图标拖拽](QGraphicsView/DragGraphics.py)
+ - [QCalendarWidget](QCalendarWidget)
+ - [QSS美化日历样式](QCalendarWidget/CalendarQssStyle.py)
+ - [QLCDNumber](QLCDNumber)
+ - [QProgressBar](QProgressBar)
+ - [常规样式美化](QProgressBar/SimpleStyle.py)
+ - [圆圈进度条](QProgressBar/RoundProgressBar.py)
+ - [百分比进度条](QProgressBar/PercentProgressBar.py)
+ - [Metro进度条](QProgressBar/MetroCircleProgress.py)
+ - [水波纹进度条](QProgressBar/WaterProgressBar.py)
+ - [圆形水位进度条](QProgressBar/WaterProgress.py)
+ - [多彩动画进度条](QProgressBar/ColourfulProgress.py)
+ - [QSplashScreen](QSplashScreen)
+ - [启动画面动画](QSplashScreen/GifSplashScreen.py)
+ - [QOpenGLWidget](QOpenGLWidget)
+ - [QWebView](QWebView)
+ - [梦幻树](QWebView/DreamTree.py)
+ - [获取Cookie](QWebView/GetCookie.py)
+ - [和Js交互操作](QWebView/JsSignals.py)
+ - [网页整体截图](QWebView/ScreenShotPage.py)
+ - [播放Flash](QWebView/PlayFlash.py)
+ - [拦截请求](QWebView/BlockRequest.py)
+ - [QWebEngineView](QWebEngineView)
+ - [获取Cookie](QWebEngineView/GetCookie.py)
+ - [和Js交互操作](QWebEngineView/JsSignals.py)
+ - [网页整体截图](QWebEngineView/ScreenShotPage.py)
+ - [同网站不同用户](QWebEngineView/SiteDiffUser.py)
+ - [拦截请求](QWebEngineView/BlockRequest.py)
+ - [拦截请求内容](QWebEngineView/BlockRequestData.py)
+ - [设置Cookie](QWebEngineView/SetCookies.py)
+ - [浏览器下载文件](Test/partner_625781186/6.QWebEngineView下载文件)
+ - [打印网页](Test/partner_625781186/17_打印预览qwebengineview)
+ - [QWebChannel](QWebChannel)
+ - [和Js互相调用](QWebChannel/CallEachWithJs.py)
+
+- [QThread](QThread)
+ - [继承QThread](QThread/InheritQThread.py)
+ - [moveToThread](QThread/moveToThread.py)
+ - [线程挂起恢复](QThread/SuspendThread.py)
+ - [线程休眠唤醒](QThread/WakeupThread.py)
+ - [线程退出](QThread/QuitThread.py)
+
+- [QtQuick](QtQuick)
+ - [Flat样式](QtQuick/FlatStyle.py)
+ - [QML与Python交互](QtQuick/Signals.py)
+
+- [QtChart](QtChart)
+ - [折线图](QtChart/LineChart.py)
+ - [折线堆叠图](QtChart/LineStack.py)
+ - [柱状堆叠图](QtChart/BarStack.py)
+ - [LineChart自定义xy轴](QtChart/CustomXYaxis.py)
+ - [ToolTip提示](QtChart/ToolTip.py)
+ - [DynamicSpline动态曲线图](QtChart/DynamicSpline.py)
+ - [区域图表](QtChart/AreaChart.py)
+ - [柱状图表](QtChart/BarChart.py)
+ - [饼状图表](QtChart/PieChart.py)
+ - [样条图表](QtChart/SplineChart.py)
+ - [百分比柱状图表](QtChart/PercentBarChart.py)
+ - [横向柱状图表](QtChart/HorizontalBarChart.py)
+ - [横向百分比柱状图表](QtChart/HorizontalPercentBarChart.py)
+ - [散点图表](QtChart/ScatterChart.py)
+ - [图表主题动画](QtChart/ChartThemes.py)
+ - [CPU动态折线图](QtChart/CpuLineChart.py)
+
+- [QtDataVisualization](QtDataVisualization)
+ - [柱状图3D](QtDataVisualization/BarsVisualization.py)
+ - [太阳磁场线](QtDataVisualization/MagneticOfSun.py)
+ - [余弦波3D](QtDataVisualization/ScatterVisualization.py)
+
+- [PyQtGraph](PyQtGraph)
+ - [鼠标获取X轴坐标](PyQtGraph/mouseFlow.py)
+ - [禁止右键点击功能、鼠标滚轮,添加滚动条等功能](PyQtGraph/graph1.py)
+ - [工具类](PyQtGraph/tools.py)
+ - [滚动区相关](PyQtGraph/testGraphAnalysis.py)
+
+- [Animation](QPropertyAnimation)
+ - [窗口淡入淡出](QPropertyAnimation/FadeInOut.py)
+ - [右键菜单动画](QPropertyAnimation/MenuAnimation.py)
+ - [点阵特效](QPropertyAnimation/RlatticeEffect.py)
+ - [页面切换/图片轮播动画](QPropertyAnimation/PageSwitching.py)
+ - [窗口抖动](QPropertyAnimation/ShakeWindow.py)
+ - [窗口翻转动画(仿QQ)](QPropertyAnimation/FlipWidgetAnimation.py)
+ - [折叠动画](Test/partner_625781186/2.折叠控件)
+
+- [RemoteObjects](QtRemoteObjects)
+ - [简单界面数据同步](QtRemoteObjects/SyncUi)
+ - [modelview](QtRemoteObjects/modelview)
+ - [simpleswitch](QtRemoteObjects/simpleswitch)
+
+- [QPainter](QPainter)
+ - [QPainter绘制各种图形](QPainter/StockDialog.py)
+ - [简易画板](QPainter/Draw.py)
+
+- [QtWinExtras](QtWinExtras)
+ - [任务栏进度条](QtWinExtras/TaskbarProgress.py)
+ - [任务栏缩略图工具按钮](QtWinExtras/ThumbnailToolBar.py)
+
+- Others
+ - [QFont](QFont)
+ - [加载自定义字体](QFont/AwesomeFont.py)
+ - [QMenu](QMenu)
+ - [菜单设置多选并且不关闭](QMenu/MultiSelect.py)
+ - [仿QQ右键菜单](QMenu/QQMenu.py)
+ - [悬停菜单](Test/partner_625781186/5.hoverMenu)
+ - [QAxWidget](QAxWidget)
+ - [显示Word、Excel、PDF文件](QAxWidget/ViewOffice.py)
+ - [QSplitter](QSplitter)
+ - [分割窗口的分割条重绘](QSplitter/RewriteHandle.py)
+ - [QSerialPort](QSerialPort)
+ - [串口调试小助手](QSerialPort/SerialDebugAssistant.py)
+ - [QProcess](QProcess)
+ - [执行命令得到结果](QProcess/GetCmdResult.py)
+ - [交互执行命令](QProcess/InteractiveRun.py)
+ - [QProxyStyle](QProxyStyle)
+ - [Tab文字方向](QProxyStyle/TabTextDirection.py)
+ - [Tab角落控件位置占满](QProxyStyle/TabCornerWidget.py)
+ - [QMessageBox](QMessageBox)
+ - [消息对话框倒计时关闭](QMessageBox/CountDownClose.py)
+ - [自定义图标等](QMessageBox/CustomColorIcon.py)
+ - [消息框按钮文字汉化](QMessageBox/ChineseText.py)
+ - [QFileSystemModel](QFileSystemModel)
+ - [自定义图标](QFileSystemModel/CustomIcon.py)
+ - [QGraphicsDropShadowEffect](QGraphicsDropShadowEffect)
+ - [边框阴影动画](QGraphicsDropShadowEffect/ShadowEffect.py)
+ - [QSystemTrayIcon](QSystemTrayIcon)
+ - [最小化到系统托盘](QSystemTrayIcon/MinimizeToTray.py)
+ - [QSystemTrayIcon](QSystemTrayIcon)
+ - [系统托盘闪烁](QSystemTrayIcon/TrayNotify.py)
+ - [QMetaObject](QMetaObject)
+ - [在线程中操作UI](QMetaObject/CallInThread.py)
+
+- [Demo](Demo)
+ - [重启窗口Widget](Demo/RestartWindow.py)
+ - [简单的窗口贴边隐藏](Demo/WeltHideWindow.py)
+ - [嵌入外部窗口](Demo/EmbedWindow.py)
+ - [简单跟随其它窗口](Demo/FollowWindow.py)
+ - [调整窗口显示边框](Demo/ShowFrameWhenDrag.py)
+ - [简单探测窗口和放大截图](Demo/ProbeWindow.py)
+ - [无边框圆角对话框](Demo/FramelessDialog.py)
+ - [无边框自定义标题栏窗口](Demo/FramelessWindow.py)
+ - [右下角弹出框](Demo/WindowNotify.py)
+ - [程序重启](Demo/AutoRestart.py)
+ - [自定义属性](Demo/CustomProperties.py)
+ - [调用截图DLL](Demo/ScreenShotDll.py)
+ - [单实例应用](Demo/SingleApplication.py)
+ - [简单的右下角气泡提示](Demo/BubbleTips.py)
+ - [右侧消息通知栏](Demo/Notification.py)
+ - [验证码控件](Demo/VerificationCode.py)
+ - [人脸特征点](Demo/FacePoints.py)
+ - [使用Threading](Demo/QtThreading.py)
+ - [背景连线动画](Demo/CircleLine.py)
+ - [判断信号是否连接](Demo/IsSignalConnected.py)
+ - [调用虚拟键盘](Demo/CallVirtualKeyboard.py)
+ - [动态忙碌光标](Demo/GifCursor.py)
+ - [屏幕变动监听](Demo/ScreenNotify.py)
+ - [无边框窗口](Demo/NewFramelessWindow.py)
+
+
+## 其它项目
+[一些Qt写的三方APP](https://github.com/PyQt5/3rd-Apps)
+
+
+## [Donate-打赏](Donate)
+
+感谢所有捐助者的鼓励,[这里](https://github.com/PyQt5/thanks) 列出了捐助者名单(由于一些收款渠道无法知道对方是谁,如有遗漏请联系我修改)
+
+ or
diff --git a/Test/ButtomZoom.py b/Test/ButtomZoom.py
index 0a2b482ed574e6b16bad00466abaeb2c34a4a9ed..ab61e773d794dcad9458c128b710b74548fc9a93 100644
--- a/Test/ButtomZoom.py
+++ b/Test/ButtomZoom.py
@@ -4,21 +4,17 @@
"""
Created on 2018年10月30日
@author: Irony
-@site: http://pyqt5.com https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ButtomZoom
@description:
"""
-from PyQt5.QtCore import QPropertyAnimation, QRect
-from PyQt5.QtWidgets import QPushButton, QWidget, QHBoxLayout, QSpacerItem,\
- QSizePolicy
-
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import QPropertyAnimation, QRect
+ from PyQt5.QtWidgets import QApplication, QPushButton, QWidget, QHBoxLayout, QSpacerItem, QSizePolicy
+except ImportError:
+ from PySide2.QtCore import QPropertyAnimation, QRect
+ from PySide2.QtWidgets import QApplication, QPushButton, QWidget, QHBoxLayout, QSpacerItem, QSizePolicy
class ZoomButton(QPushButton):
@@ -70,11 +66,11 @@ class ZoomButton(QPushButton):
super(ZoomButton, self).mousePressEvent(event)
-class TestWindow(QWidget):
+class Window(QWidget):
# 测试窗口
def __init__(self, *args, **kwargs):
- super(TestWindow, self).__init__(*args, **kwargs)
+ super(Window, self).__init__(*args, **kwargs)
# 1. 加入布局中
layout = QHBoxLayout(self)
layout.addSpacerItem(QSpacerItem(
@@ -89,7 +85,7 @@ class TestWindow(QWidget):
# 以下两个方法需要重写
def showEvent(self, event):
- super(TestWindow, self).showEvent(event)
+ super(Window, self).showEvent(event)
# 更新按钮的位置
self.button1.updatePos()
# 针对不在控件中的按钮
@@ -98,7 +94,7 @@ class TestWindow(QWidget):
self.button2.updatePos()
def resizeEvent(self, event):
- super(TestWindow, self).resizeEvent(event)
+ super(Window, self).resizeEvent(event)
# 更新按钮的位置
self.button1.updatePos()
# 针对不在控件中的按钮
@@ -109,7 +105,7 @@ class TestWindow(QWidget):
if __name__ == '__main__':
import sys
- from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
app.setStyleSheet("""QPushButton {
border: none;
@@ -120,6 +116,6 @@ if __name__ == '__main__':
min-height: 40px;
background-color: white;
}""")
- w = TestWindow()
+ w = Window()
w.show()
sys.exit(app.exec_())
diff --git "a/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold.dll" "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold.dll"
new file mode 100644
index 0000000000000000000000000000000000000000..18e5c2c31e31e28a90233d1ff95db7b15337af0b
Binary files /dev/null and "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold.dll" differ
diff --git "a/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/Cold.pro" "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/Cold.pro"
new file mode 100644
index 0000000000000000000000000000000000000000..cb2bf646f8acd52c5b709cf4cfb6595b5efa7069
--- /dev/null
+++ "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/Cold.pro"
@@ -0,0 +1,30 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2020-03-02T16:57:48
+#
+#-------------------------------------------------
+
+TARGET = Cold
+TEMPLATE = lib
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which has been marked as deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ cold.cpp
+
+HEADERS += \
+ cold.h
+
+unix {
+ target.path = /usr/lib
+ INSTALLS += target
+}
diff --git "a/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/build.bat" "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/build.bat"
new file mode 100644
index 0000000000000000000000000000000000000000..2bdf9e07d0e336e7f96bc07e0347cfee60208cde
--- /dev/null
+++ "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/build.bat"
@@ -0,0 +1,2 @@
+qmake
+nmake
\ No newline at end of file
diff --git "a/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/cold.cpp" "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/cold.cpp"
new file mode 100644
index 0000000000000000000000000000000000000000..e940cae8e2c748693035787c8d61d35c328c4628
--- /dev/null
+++ "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/cold.cpp"
@@ -0,0 +1,19 @@
+#include "cold.h"
+
+void cold(QImage &src, int delta)
+{
+ int rows = src.height();
+ int cols = src.width();
+ for (int i = 0; i < rows; i++)
+ {
+ QRgb *line = (QRgb *)src.scanLine(i);
+ for (int j = 0; j < cols; j++)
+ {
+ int r = qRed(line[j]);
+ int g = qGreen(line[j]);
+ int b = qBlue(line[j]) + delta;
+ b = qBound(0, b, 255);
+ src.setPixel(j, i, qRgb(r, g, b));
+ }
+ }
+}
diff --git "a/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/cold.h" "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/cold.h"
new file mode 100644
index 0000000000000000000000000000000000000000..3ca42a453098949058e7860c401fa48ecade55bb
--- /dev/null
+++ "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Cold/cold.h"
@@ -0,0 +1,4 @@
+#include
+#include
+
+extern "C" __declspec(dllexport) void cold(QImage &src, int delta);
\ No newline at end of file
diff --git "a/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Test.py" "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Test.py"
new file mode 100644
index 0000000000000000000000000000000000000000..1431c2fd6b47970df755ed31a71cac5e28933f2b
--- /dev/null
+++ "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/Test.py"
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020年3月2日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file:
+@description: 冷色调
+"""
+
+import sys
+from ctypes import CDLL
+from time import time
+
+try:
+ # For PyQt5
+ try:
+ from PyQt5 import sip
+ except ImportError:
+ import sip
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QImage, QPixmap
+ from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QSlider, QVBoxLayout
+except ImportError:
+ # For PySide2
+ import shiboken2
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QImage, QPixmap
+ from PySide2.QtWidgets import QApplication, QWidget, QLabel, QSlider, QVBoxLayout
+
+
+class Window(QWidget):
+
+ def __init__(self, *args, **kwargs):
+ super(Window, self).__init__(*args, **kwargs)
+ layout = QVBoxLayout(self)
+ self.imgLabel = QLabel(self)
+ self.coldSlider = QSlider(Qt.Horizontal, self)
+ self.coldSlider.valueChanged.connect(self.doChange)
+ self.coldSlider.setRange(0, 255)
+ layout.addWidget(self.imgLabel)
+ layout.addWidget(self.coldSlider)
+
+ # 加载图片
+ self.srcImg = QImage('src.jpg')
+ self.imgLabel.setPixmap(QPixmap.fromImage(self.srcImg).scaledToWidth(800, Qt.SmoothTransformation))
+ # DLL库
+ self.dll = CDLL('Cold.dll')
+ print(self.dll)
+
+ def doChange(self, value):
+ t = time()
+ img = self.srcImg.copy() # 复制一份
+ if 'sip' in sys.modules:
+ # For PyQt5
+ self.dll.cold(sip.unwrapinstance(img), value)
+ elif 'shiboken2' in sys.modules:
+ # For PySide2
+ self.dll.cold(shiboken2.getCppPointer(img)[0], value)
+ self.imgLabel.setPixmap(QPixmap.fromImage(img).scaledToWidth(800, Qt.SmoothTransformation))
+ print('use time:', time() - t)
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ view = Window()
+ view.show()
+ sys.exit(app.exec_())
diff --git "a/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/src.jpg" "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/src.jpg"
new file mode 100644
index 0000000000000000000000000000000000000000..c455f6ac5b3ac48f8227730f2ed7b1ac96f902a7
Binary files /dev/null and "b/Test/C++\344\270\255\344\277\256\346\224\271PyQt\345\257\271\350\261\241/src.jpg" differ
diff --git a/Test/ChartView/ChartView.py b/Test/ChartView/ChartView.py
index a23d7491af85e0c95d95ce8d3dbb0a673adb83dd..2880867a45b645ab0ea4714c172a8b2dfef2e4e5 100644
--- a/Test/ChartView/ChartView.py
+++ b/Test/ChartView/ChartView.py
@@ -1,24 +1,21 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月18日
-@author: Irony."[讽刺]
-@site: http://alyl.vip, http://orzorz.vip, http://coding.net/u/892768447, http://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ChartView
@description:
-'''
+"""
import json
import os
+import chardet
from PyQt5.QtChart import QChart, QChartView, QLineSeries, QCategoryAxis
from PyQt5.QtCore import QMargins, Qt, QEasingCurve
from PyQt5.QtGui import QColor, QBrush, QFont, QPainter, QPen, QPixmap
-import chardet
-
-
-__version__ = "0.0.1"
# QEasingCurve 类型枚举
EasingCurve = dict(
@@ -55,8 +52,8 @@ class ChartView(QChartView):
data = data.decode(encoding.get("encoding") or "utf-8")
self.__analysis(json.loads(data))
-# def onSeriesHoverd(self, point, state):
-# print(point, state)
+ # def onSeriesHoverd(self, point, state):
+ # print(point, state)
def mouseMoveEvent(self, event):
super(ChartView, self).mouseMoveEvent(event)
@@ -89,8 +86,8 @@ class ChartView(QChartView):
return QColor(color)
def __getPen(self, pen=None, default=QPen(
- Qt.white, 1, Qt.SolidLine,
- Qt.SquareCap, Qt.BevelJoin)):
+ Qt.white, 1, Qt.SolidLine,
+ Qt.SquareCap, Qt.BevelJoin)):
'''
:param pen: pen json
'''
@@ -112,13 +109,14 @@ class ChartView(QChartView):
return getattr(Qt, "Align" + alignment.capitalize())
except:
return Qt.AlignTop
-# if alignment == "left":
-# return Qt.AlignLeft
-# if alignment == "right":
-# return Qt.AlignRight
-# if alignment == "bottom":
-# return Qt.AlignBottom
-# return Qt.AlignTop
+
+ # if alignment == "left":
+ # return Qt.AlignLeft
+ # if alignment == "right":
+ # return Qt.AlignRight
+ # if alignment == "bottom":
+ # return Qt.AlignBottom
+ # return Qt.AlignTop
def __setTitle(self, title=None):
'''
diff --git a/Test/ChartView/ChatWidget.py b/Test/ChartView/ChatWidget.py
index fedfa9ce1e0116098bcc550dbcc93812641234fe..a5f6622d15137f18fda4ea30627b609e374446f2 100644
--- a/Test/ChartView/ChatWidget.py
+++ b/Test/ChartView/ChatWidget.py
@@ -1,33 +1,30 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月20日
-@author: Irony."[讽刺]
-@site: http://alyl.vip, http://orzorz.vip, http://coding.net/u/892768447, http://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ChatWidget
@description:
-'''
-from collections import OrderedDict
+"""
import json
import os
import sys
+from collections import OrderedDict
+import chardet
from PyQt5.Qsci import QsciScintilla, QsciLexerJSON
from PyQt5.QtChart import QChartView
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QColor, QFont, QFontMetrics, QKeySequence, QMovie
-from PyQt5.QtWidgets import QSplitter, QTreeWidget, QApplication, QWidget,\
+from PyQt5.QtWidgets import QSplitter, QTreeWidget, QApplication, QWidget, \
QVBoxLayout, QPushButton, QTreeWidgetItem, QMessageBox, QShortcut, QLabel
-import chardet
from ChartView import ChartView # @UnresolvedImport
-__version__ = "0.0.1"
-
-
class LoadingWidget(QLabel):
def __init__(self, *args, **kwargs):
@@ -54,7 +51,6 @@ class LoadingWidget(QLabel):
class ClassifyWidget(QTreeWidget):
-
fileSelected = pyqtSignal(str)
def __init__(self, *args, **kwargs):
@@ -135,7 +131,6 @@ class CodeScintilla(QsciScintilla):
class CodeWidget(QWidget):
-
runSignal = pyqtSignal(str)
def __init__(self, *args, **kwargs):
diff --git a/Test/ColumnView.py b/Test/ColumnView.py
new file mode 100644
index 0000000000000000000000000000000000000000..da5e37454692a5f8d729eefc972d6d3f5420023b
--- /dev/null
+++ b/Test/ColumnView.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2020/9/14
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file: ColumnView
+@description:
+"""
+
+try:
+ from PyQt5.QtWidgets import QApplication, QComboBox, QFileSystemModel, QHBoxLayout, QSpacerItem, \
+ QSizePolicy
+except ImportError:
+ from PyQt5.QtWidgets import QApplication, QComboBox, QFileSystemModel, QHBoxLayout, QSpacerItem, \
+ QSizePolicy
+
+
+class PathComboBox(QComboBox):
+
+ def __init__(self, *args, is_item=False, **kwargs):
+ super(PathComboBox, self).__init__(*args, **kwargs)
+ self.is_item = is_item
+ if not self.is_item:
+ self.setEditable(True)
+ layout = QHBoxLayout(self)
+ layout.setSpacing(0)
+ layout.setContentsMargins(0, 0, 0, 23)
+ layout.addItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
+ else:
+ self.f_model = QFileSystemModel(self)
+ self.f_model.setRootPath('')
+ self.setModel(self.f_model)
+
+ def addWidget(self, widget):
+ self.layout().insertWidget(self.layout().count() - 1, widget)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QApplication(sys.argv)
+ w = PathComboBox()
+ w.show()
+ w.addWidget(PathComboBox(w, is_item=True))
+ sys.exit(app.exec_())
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/.suo" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/.suo"
deleted file mode 100644
index 9fa59224e8673d6dc695f84a33b321d8393e3b7a..0000000000000000000000000000000000000000
Binary files "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/.suo" and /dev/null differ
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/Browse.VC.db" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/Browse.VC.db"
deleted file mode 100644
index 083957731cea817e4d340f83dc4893a40cefb911..0000000000000000000000000000000000000000
Binary files "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/Browse.VC.db" and /dev/null differ
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/ipch/AutoPCH/7828e06aa30c0d3c/PYDEXT.ipch" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/ipch/AutoPCH/7828e06aa30c0d3c/PYDEXT.ipch"
deleted file mode 100644
index b59817e5d6bf26970cf1ea6feef772cc8eb6891b..0000000000000000000000000000000000000000
Binary files "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/ipch/AutoPCH/7828e06aa30c0d3c/PYDEXT.ipch" and /dev/null differ
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/ipch/AutoPCH/d5916c660b4c4bac/PYDEXT.ipch" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/ipch/AutoPCH/d5916c660b4c4bac/PYDEXT.ipch"
deleted file mode 100644
index 76f9a76ef0c0808d541b999a1ab793fac1dfe54c..0000000000000000000000000000000000000000
Binary files "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/.vs/pydext/v15/ipch/AutoPCH/d5916c660b4c4bac/PYDEXT.ipch" and /dev/null differ
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/pydext.vcxproj" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/pydext.vcxproj"
deleted file mode 100644
index 6184a2a57ca0141fd47b686d5c46dd3a2fac23fa..0000000000000000000000000000000000000000
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/pydext.vcxproj"
+++ /dev/null
@@ -1,203 +0,0 @@
-
-
-
-
- Debug
- Win32
-
-
- Release
- Win32
-
-
- Debug
- x64
-
-
- Release
- x64
-
-
-
- {023C9F4B-FECC-430A-81D2-7044FAFF15D0}
- pydext
- 3.5
- 10.0.16299.0
-
-
-
- DynamicLibrary
- true
- v141
- Unicode
-
-
- DynamicLibrary
- false
- v141
- true
- Unicode
-
-
- DynamicLibrary
- true
- v141
- Unicode
-
-
- DynamicLibrary
- false
- v141
- true
- Unicode
-
-
- RegistryView.Registry32
- RegistryView.Registry64
- $(PythonVersion)-32
- $(PythonVersion)
- $([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\$(PythonTag)\InstallPath', null, null, $(RegistryView)))
- $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\$(PythonTag)\InstallPath', null, null, $(RegistryView)))
- $([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\$(PythonTag)\InstallPath', 'ExecutablePath', null, $(RegistryView)))
- $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\$(PythonTag)\InstallPath', 'ExecutablePath', null, $(RegistryView)))
- $(PythonHome)python.exe
- $([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\$(PythonTag)\InstalledFeatures', 'dev', null, $(RegistryView)))
- $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\$(PythonTag)\InstalledFeatures', 'dev', null, $(RegistryView)))
- $([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\$(PythonTag)\InstalledFeatures', 'core_pdb', null, $(RegistryView)))
- $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\$(PythonTag)\InstalledFeatures', 'core_pdb', null, $(RegistryView)))
- $([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\$(PythonTag)\InstalledFeatures', 'core_d', null, $(RegistryView)))
- $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\$(PythonTag)\InstalledFeatures', 'core_d', null, $(RegistryView)))
- _d
- $([System.IO.Path]::GetDirectoryName($(PythonExe)))\python$(PythonDebugSuffix).exe
- $(PythonExe)
-
-
-
- WindowsLocalDebugger
- PythonDebugLaunchProvider
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- pydext$(PythonDebugSuffix)
- .pyd
- $(PythonDExe)
- -i -c "print('>>> import pydext'); import pydext"
- PYTHONPATH=$(OutDir)
- $(DefaultDebuggerFlavor)
-
-
- pydext
- .pyd
- $(PythonExe)
- -i -c "print('>>> import pydext'); import pydext"
- PYTHONPATH=$(OutDir)
- $(DefaultDebuggerFlavor)
-
-
- pydext$(PythonDebugSuffix)
- .pyd
- $(PythonDExe)
- -i -c "print('>>> import pydext'); import pydext"
- PYTHONPATH=$(OutDir)
- $(DefaultDebuggerFlavor)
-
-
- pydext
- .pyd
- $(PythonExe)
- -i -c "print('>>> import pydext'); import pydext"
- PYTHONPATH=$(OutDir)
- $(DefaultDebuggerFlavor)
-
-
-
- Level3
- Disabled
- MultithreadedDLL
- D:\soft\Python35\include;$(PythonHome)Include;%(AdditionalIncludeDirectories)
- true
-
-
- true
- $(PythonHome)libs;%(AdditionalLibraryDirectories)
-
-
-
-
- Level3
- Disabled
- MultithreadedDLL
- $(PythonHome)Include;%(AdditionalIncludeDirectories)
- true
-
-
- true
- $(PythonHome)libs;%(AdditionalLibraryDirectories)
-
-
-
-
- Level3
- MaxSpeed
- true
- true
- Multithreaded
- D:\soft\Python35\include;$(PythonHome)Include;%(AdditionalIncludeDirectories)
- true
-
-
- true
- true
- true
- libucrt.lib;%(IgnoreSpecificDefaultLibraries)
- ucrt.lib;%(AdditionalDependencies)
- $(PythonHome)libs;%(AdditionalLibraryDirectories)
-
-
-
-
- Level3
- MaxSpeed
- true
- true
- Multithreaded
- $(PythonHome)Include;%(AdditionalIncludeDirectories)
- true
-
-
- true
- true
- true
- libucrt.lib;%(IgnoreSpecificDefaultLibraries)
- ucrt.lib;%(AdditionalDependencies)
- $(PythonHome)libs;%(AdditionalLibraryDirectories)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/pydext.vcxproj.filters" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/pydext.vcxproj.filters"
deleted file mode 100644
index 2b201335abb2f375f2504ac0397e910b90d253a7..0000000000000000000000000000000000000000
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/pydext.vcxproj.filters"
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
- {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
- cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
-
-
- {93995380-89BD-4b04-88EB-625FBE52EBFB}
- h;hh;hpp;hxx;hm;inl;inc;xsd
-
-
- {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
- rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
-
-
-
-
- Source Files
-
-
-
\ No newline at end of file
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/pydext.vcxproj.user" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/pydext.vcxproj.user"
deleted file mode 100644
index be2507870701e486845b81bbf2a525758fa0e980..0000000000000000000000000000000000000000
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/pydext.vcxproj.user"
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/setup.py" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/setup.py"
index 03ce03ee0b82a9e243ccde8af5296ca6663b9427..233046e93fab341a5cc4b721974638ade7f7038c 100644
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/setup.py"
+++ "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/setup.py"
@@ -1,8 +1,8 @@
from distutils.core import setup, Extension
-
-module = Extension('pydext', sources = ['pydext.c'])
-
-setup (name = 'pydext',
- version = '1.0.0',
- description = 'This is pydext',
- ext_modules = [module])
\ No newline at end of file
+
+module = Extension('pydext', sources=['pydext.c'])
+
+setup(name='pydext',
+ version='1.0.0',
+ description='This is pydext',
+ ext_modules=[module])
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/test.py" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/test.py"
index 3a3d87038acc1eba67cdcfb79d8ea80057056001..130622969910dbbe987583cf85910399daf3e44e 100644
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/test.py"
+++ "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/pydext/test.py"
@@ -1,8 +1,8 @@
import sys
sys.path.insert(0,
- './build/lib.{0}-{1}.{2}'.format(sys.platform, sys.version_info.major, sys.version_info.minor))
-
+ './build/lib.{0}-{1}.{2}'.format(sys.platform, sys.version_info.major,
+ sys.version_info.minor))
import pydext
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/test.py" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/test.py"
index 38c6394e2d0e011570f90ee47fec65e32f1c6d1f..ecf23016ddf037bb9de549bf48ecf30f105c64f1 100644
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/test.py"
+++ "b/Test/C\345\222\214C++\346\211\251\345\261\225/pydext/test.py"
@@ -1,4 +1,5 @@
import sys
+
sys.path.insert(0, './Release')
import pydext
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/CalSpecSpea.pyx" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/CalSpecSpea.pyx"
index 669ea114c172c824f29bd9df0a81c1e9e105723d..bd6e390f906b37f14922ce0750654d6ba8e15666 100644
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/CalSpecSpea.pyx"
+++ "b/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/CalSpecSpea.pyx"
@@ -20,8 +20,8 @@ def calspecaccel(np.ndarray[double, ndim=1, mode="c"] acc, int length, double dt
cdef np.ndarray[double, ndim=1] Period = np.arange(0.0, maxPeriod + periodStep, periodStep) # 10.0 + 0.02, 0.02
Period[0] = 0.001
# 调用CalSpecSpeaLib.cpp定义的函数对数组进行处理
- cal_spec_accel( np.PyArray_DATA(acc), length, dt, maxPeriod, periodStep, dampRatio,
- np.PyArray_DATA(Period), np.PyArray_DATA(Fre),
- np.PyArray_DATA(MAcc), np.PyArray_DATA(MVel),
+ cal_spec_accel( np.PyArray_DATA(acc), length, dt, maxPeriod, periodStep, dampRatio,
+ np.PyArray_DATA(Period), np.PyArray_DATA(Fre),
+ np.PyArray_DATA(MAcc), np.PyArray_DATA(MVel),
np.PyArray_DATA(MDis), numt)
return Period, Fre, MAcc, MVel, MDis
\ No newline at end of file
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/setup.py" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/setup.py"
index eb42160a04f6c0182d9fa9e1196939f6ba52d5b5..0b689bf24f4eafd1a134d81d8740dcb07bfeb9fd 100644
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/setup.py"
+++ "b/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/setup.py"
@@ -1,11 +1,10 @@
from distutils.core import setup, Extension
-from Cython.Distutils import build_ext
import numpy
-
+from Cython.Distutils import build_ext
setup(
cmdclass={'build_ext': build_ext},
ext_modules=[Extension("CalSpecSpea", sources=[
- "CalSpecSpea.pyx", "CalSpecSpeaLib.cpp"], language="c++", include_dirs=[numpy.get_include()])]
+ "CalSpecSpea.pyx", "CalSpecSpeaLib.cpp"], language="c++", include_dirs=[numpy.get_include()])]
)
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/test.py" "b/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/test.py"
index c078137986b58b2e7551bf69fdd49a4e6c85250d..9ea580c500202bbc04239311ba6cea7e02e887ad 100644
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/test.py"
+++ "b/Test/C\345\222\214C++\346\211\251\345\261\225/pyx\345\222\214c++/test.py"
@@ -4,7 +4,6 @@ import time
sys.path.append(
'./build/lib.{0}-{1}.{2}'.format(sys.platform, sys.version_info.major, sys.version_info.minor))
-
from CalSpecSpea import calspecaccel
import matplotlib.pyplot as plt
import numpy as np
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/pydmod.py" "b/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/pydmod.py"
index 810a5bfb3e8edead175bb1fd1f746a3b0151173c..50ed74bb257cc245e0cdc955de6794806f2ef513 100644
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/pydmod.py"
+++ "b/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/pydmod.py"
@@ -1,18 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# Created on 2018年5月6日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file:
-# description:
-
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
+"""
+Created on 2018年5月6日
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
+@email: 892768447@qq.com
+@file:
+@description:
+"""
def sum(x, y):
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/setup.py" "b/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/setup.py"
index d3cc302fee35829e83e6decbfa9569a5ddab684a..b18bc64bccc835bff430986ecf69ee2c8a26c4b4 100644
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/setup.py"
+++ "b/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/setup.py"
@@ -2,7 +2,6 @@ from distutils.core import setup, Extension
from Cython.Distutils import build_ext
-
setup(
cmdclass={'build_ext': build_ext},
ext_modules=[Extension("pydmod", sources=["pydmod.py"])]
diff --git "a/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/test.py" "b/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/test.py"
index 3c45145442ac57c990525d962aeaa80bc5c0f7e8..387314a53500b9f7ca739cc11e40d620f0ec3968 100644
--- "a/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/test.py"
+++ "b/Test/C\345\222\214C++\346\211\251\345\261\225/py\350\275\254pyd/test.py"
@@ -1,7 +1,10 @@
import sys
+
sys.path.insert(0,
- './build/lib.{0}-{1}.{2}'.format(sys.platform, sys.version_info.major, sys.version_info.minor))
+ './build/lib.{0}-{1}.{2}'.format(sys.platform, sys.version_info.major,
+ sys.version_info.minor))
import pydmod
+
print(pydmod)
print(pydmod.sum(1, 5))
diff --git "a/Test/Network/\346\216\247\345\210\266\345\260\217\350\275\246/server.py" "b/Test/Network/\346\216\247\345\210\266\345\260\217\350\275\246/server.py"
index 2a6426149a42ebc4c8a57c2121108198c2d8f891..485aefb7d3c29de939b9c83a25c7f44b0fc049c1 100644
--- "a/Test/Network/\346\216\247\345\210\266\345\260\217\350\275\246/server.py"
+++ "b/Test/Network/\346\216\247\345\210\266\345\260\217\350\275\246/server.py"
@@ -10,26 +10,11 @@ from tornado.iostream import StreamClosedError
from tornado.options import options, define
from tornado.tcpserver import TCPServer
-
try:
import RPi.GPIO as GPIO # @UnusedImport @UnresolvedImport
except:
pass
-
-# Created on 2018年4月18日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: server
-# description:
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
-
-
define("port", default=8888, help="TCP port to listen on")
logger = logging.getLogger(__name__)
@@ -41,7 +26,6 @@ PARAM = [int(cv2.IMWRITE_JPEG_QUALITY), FPS]
class EchoServer(TCPServer):
-
IMAGE = None
def __init__(self, cap, *args, **kwargs):
@@ -104,7 +88,7 @@ class EchoServer(TCPServer):
while True:
try:
data = yield stream.read_until(b"\n")
-# logger.info("Received bytes: %s", data)
+ # logger.info("Received bytes: %s", data)
if not data.endswith(b"\n"):
data = data + b"\n"
if data == b'getimage\n' and self.cap and self.cap.isOpened():
diff --git "a/Test/Network/\346\216\247\345\210\266\345\260\217\350\275\246/\346\216\247\345\210\266\345\260\217\350\275\246.py" "b/Test/Network/\346\216\247\345\210\266\345\260\217\350\275\246/\346\216\247\345\210\266\345\260\217\350\275\246.py"
index e4b184e0998e9e0cc1cbd7fa2bfc8a0be48635a2..6e5b43104fa266c9fa8678f41e2a1f981b248f19 100644
--- "a/Test/Network/\346\216\247\345\210\266\345\260\217\350\275\246/\346\216\247\345\210\266\345\260\217\350\275\246.py"
+++ "b/Test/Network/\346\216\247\345\210\266\345\260\217\350\275\246/\346\216\247\345\210\266\345\260\217\350\275\246.py"
@@ -7,21 +7,7 @@ from PyQt5.QtNetwork import QTcpSocket
from PyQt5.QtWidgets import QWidget
-# Created on 2018年4月18日
-# author: Irony
-# site: https://pyqt5.com , https://github.com/892768447
-# email: 892768447@qq.com
-# file: ControlCar
-# description:
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = 'Copyright (c) 2018 Irony'
-__Version__ = 1.0
-
-
class ControlCar(QWidget):
-
HOST = '127.0.0.1'
PORT = 8888
@@ -156,6 +142,7 @@ class ControlCar(QWidget):
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
+
app = QApplication(sys.argv)
w = ControlCar()
w.show()
diff --git "a/Test/Network/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http/requirements.txt" "b/Test/Network/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http/requirements.txt"
new file mode 100644
index 0000000000000000000000000000000000000000..a17d8ae60f47539ed7c25d6afe9dd484378282ec
--- /dev/null
+++ "b/Test/Network/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http/requirements.txt"
@@ -0,0 +1 @@
+quamash
\ No newline at end of file
diff --git "a/Test/Network/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http.py" "b/Test/Network/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http.py"
index 4885b4201e14f4d39e90c907c6bb1a7446f5a5df..b3d63f939b5d49291cc150b389faa6f57d3c2023 100644
--- "a/Test/Network/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http.py"
+++ "b/Test/Network/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http/\347\252\227\345\217\243\351\205\215\345\220\210\345\274\202\346\255\245Http.py"
@@ -4,26 +4,34 @@
"""
Created on 2018年10月24日
@author: Irony
-@site: https://github.com/892768447
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: AsyncioUiClient
@description:
"""
import asyncio
+import os
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QPixmap, QMovie
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton,\
- QApplication, QListWidget, QListWidgetItem, QLabel, QMessageBox
import aiohttp
-from quamash import QEventLoop
+try:
+ from PyQt5.QtCore import Qt
+ from PyQt5.QtGui import QPixmap, QMovie
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication, QListWidget, QListWidgetItem, \
+ QLabel, QMessageBox
+except ImportError:
+ from PySide2.QtCore import Qt
+ from PySide2.QtGui import QPixmap, QMovie
+ from PySide2.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication, QListWidget, \
+ QListWidgetItem, \
+ QLabel, QMessageBox
+
+ os.environ['QUAMASH_QTIMPL'] = 'PySide2'
+ from PySide2 import QtGui
+
+ setattr(QtGui, 'QApplication', QApplication)
-__Author__ = """By: Irony
-QQ: 892768447
-Email: 892768447@qq.com"""
-__Copyright__ = "Copyright (c) 2018 Irony"
-__Version__ = "Version 1.0"
+from quamash import QEventLoop
Url = 'https://www.doutula.com/api/search?keyword=%E6%9C%80%E6%96%B0%E8%A1%A8%E6%83%85&mime=0&page={}'
Headers = {
@@ -67,6 +75,7 @@ class Window(QWidget):
# 初始化session
self.session = aiohttp.ClientSession(loop=loop)
print(self.session)
+
asyncio.ensure_future(_initSession(), loop=loop)
async def _doDownloadImage(self, url):
@@ -131,8 +140,9 @@ if __name__ == '__main__':
import sys
import cgitb
import os
+
os.makedirs('tmp', exist_ok=True)
- sys.excepthook = cgitb.enable(1, None, 5, 'text')
+ cgitb.enable(format='text')
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
diff --git "a/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/README.md" "b/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/README.md"
new file mode 100644
index 0000000000000000000000000000000000000000..8a64c14d8e5cdb6251fd5d4e865f27e28f4c0206
--- /dev/null
+++ "b/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/README.md"
@@ -0,0 +1,6 @@
+### 17 QWebEngineView + QPrintPreviewDialog
+
+Preview QWebEngineView.page() in QPrintPreviewDialog and print them authentically .
+
+
+
\ No newline at end of file
diff --git "a/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/ScreenShot/1.jpg" "b/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/ScreenShot/1.jpg"
new file mode 100644
index 0000000000000000000000000000000000000000..32e0fbeb94fc91383425fd5cbf406cc08f5982aa
Binary files /dev/null and "b/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/ScreenShot/1.jpg" differ
diff --git "a/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/bootstrap.min.css" "b/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/bootstrap.min.css"
new file mode 100644
index 0000000000000000000000000000000000000000..a9f35ceedfac7fc0559b121bed105eaf80f10bf2
--- /dev/null
+++ "b/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/bootstrap.min.css"
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap v3.2.0 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ *//*! normalize.css v3.0.1 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;width:100% \9;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;width:100% \9;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#777;opacity:1}.form-control:-ms-input-placeholder{color:#777}.form-control::-webkit-input-placeholder{color:#777}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px;line-height:1.42857143 \0}input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;min-height:20px;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],input[type=radio].disabled,input[type=checkbox].disabled,fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm,.form-horizontal .form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg,.form-horizontal .form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:25px;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#428bca;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn>input[type=radio],[data-toggle=buttons]>.btn>input[type=checkbox]{position:absolute;z-index:-1;filter:alpha(opacity=0);opacity:0}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#777}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#777}.navbar-inverse .navbar-nav>li>a{color:#777}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#777}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#777}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#428bca;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar[aria-valuenow="1"],.progress-bar[aria-valuenow="2"]{min-width:30px}.progress-bar[aria-valuenow="0"]{min-width:30px;color:#777;background-color:transparent;background-image:none;-webkit-box-shadow:none;box-shadow:none}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#777;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#428bca}.panel-primary>.panel-heading .badge{color:#428bca;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate3d(0,-25%,0);-o-transform:translate3d(0,-25%,0);transform:translate3d(0,-25%,0)}.modal.in .modal-dialog{-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-size:12px;line-height:1.4;visibility:visible;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed;-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file
diff --git "a/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/main.py" "b/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/main.py"
new file mode 100644
index 0000000000000000000000000000000000000000..b7fe96d37a5332247b855349e31e2cc6870cf162
--- /dev/null
+++ "b/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/main.py"
@@ -0,0 +1,97 @@
+
+""" QWebEngineView in QPrintPreviewDialog"""
+"""
+Created on 2019-01-17
+description: 摘抄自 eric6 和 https://github.com/pandel/opsiPackageBuilder/blob/c0e660ecc8d4ec8fb8dc242d2174490c5dc67930/oPB/gui/utilities.py
+author: 625781186@qq.com
+site: https://github.com/625781186
+更多经典例子:https://github.com/892768447/PyQt
+课件: https://github.com/625781186/WoHowLearn_PyQt5
+视频教程: https://space.bilibili.com/1863103/#/
+"""
+from PyQt5 import QtGui, QtWidgets, QtCore
+from PyQt5.QtCore import *
+from PyQt5.QtGui import *
+from PyQt5.QtWidgets import *
+from PyQt5.QtPrintSupport import *
+from PyQt5.QtWebEngineWidgets import *
+import sys
+
+
+class HtmlView(QWebEngineView):
+ """Subclass QWebView and connect to a QPrintPreviewDialog object"""
+
+ def __init__(self, url="", parent=None, ):
+ """
+ Constructor of HtmlView
+ :param parent: parent of the view
+ :param url: url to load, if set, a loadFInished signal is emitted
+ """
+ super().__init__(parent)
+
+ self.html = ""
+ self.setUrl(QUrl(url))
+
+ self.preview = QPrintPreviewDialog()
+
+ self.textedit = QTextEdit()
+
+ self.preview.paintRequested.connect(self.printPreview)
+
+ if url != "":
+ self.loadFinished.connect(self.execpreview)
+
+ def execpreview(self, arg):
+ self.preview.exec()
+
+ # -------------法一------------- ↓
+ ## 通过将Html 写到textedit , 再将textedit渲染到printpreviewdialog
+ # def execpreview(self, arg):
+ # self.page().toHtml(self.setHtml_)
+ # self.preview.exec()
+ #
+ # def printPreview(self, printer):
+ #
+ # self.textedit.print(printer)
+ #
+ # def setHtml_(self, html):
+ #
+ # self.textedit.setHtml(html)
+ #
+ # # small workaround to find the QPrintPreviewWidget inside the pre-defined dialog and force it to update its content
+ # wdg = self.preview.findChild(QPrintPreviewWidget)
+ # if wdg is not None:
+ # wdg.updatePreview()
+ # -------------法一------------- ↑
+
+ def printPreview(self, printer):
+ # 打印机颜色
+ printer.setColorMode(QPrinter.GrayScale)
+ # 起始页?
+ printer.setPageOrder(QPrinter.FirstPageFirst)
+ # 页边距
+ printer.setPageMargins(
+ 1.0 * 10, 1.0 * 10, 1.0 * 10, 1.0 * 10,
+ QPrinter.Millimeter
+ )
+ # 文档名
+ # printer.setPrinterName("打印机里显示的文档名")
+ # 设置DPI
+ printer.setResolution(150)
+ # ----------------------------------------------
+ ## !需要开启事件循环 , 否则无法渲染到 printpreviewdialog
+ loop = QEventLoop()
+ QTimer.singleShot(10000, loop.quit)
+
+ self.page().print(printer,
+ lambda *a: loop.quit() if loop and loop.isRunning() else None)
+
+ loop.exec_()
+
+
+if __name__ == "__main__":
+ app = QtWidgets.QApplication(sys.argv)
+ main_window = HtmlView(url="file:///报警记录2019-04-12 16-52-53.html")
+
+ main_window.show()
+ app.exec_()
diff --git "a/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/\346\212\245\350\255\246\350\256\260\345\275\2252019-04-12 16-52-53.html" "b/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/\346\212\245\350\255\246\350\256\260\345\275\2252019-04-12 16-52-53.html"
new file mode 100644
index 0000000000000000000000000000000000000000..b116ae7cb8694932fe50b615da44a00187ecfe2a
--- /dev/null
+++ "b/Test/partner_625781186/17_\346\211\223\345\215\260\351\242\204\350\247\210qwebengineview/\346\212\245\350\255\246\350\256\260\345\275\2252019-04-12 16-52-53.html"
@@ -0,0 +1,1128 @@
+
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
+
+
+ 报警发生时间
+ 报警防区
+ 报警类型
+ A电流(MA)
+ B电流(MA)
+ A电压(KV)
+ B电压(KV)
+
+
+
+
+
+ 2019-04-12 12:36:01
+ 主机3
+ 自检成功
+
+
+
+
+
+
+ 2019-04-12 12:36:01
+ 主机3
+ 自检成功
+
+
+
+
+
+
+ 2019-04-12 12:35:55
+ 主机2
+ 关机
+
+
+
+
+
+
+ 2019-04-12 12:35:55
+ 主机2
+ 关机
+
+
+
+
+
+
+ 2019-04-12 12:35:49
+ 主机3
+ 关机
+
+
+
+
+
+
+ 2019-04-12 12:35:49
+ 主机3
+ 关机
+
+
+
+
+
+
+ 2019-04-12 12:35:38
+ 主机4
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:35:38
+ 主机4
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:35:22
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:35:22
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:22
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:35:22
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:22
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:22
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:35:22
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:17
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:17
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:35:17
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:16
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:35:16
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:16
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:35:16
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:02
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:35:02
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:02
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:35:02
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:02
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:35:02
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:35:02
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:58
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:34:58
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:58
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:34:58
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:58
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:58
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:34:58
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:06
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:34:06
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:06
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:34:06
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:06
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:06
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:34:06
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:03
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:34:03
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:03
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:34:03
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:03
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:03
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:34:03
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:00
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:34:00
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:00
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:34:00
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:00
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:34:00
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:34:00
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:58
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:58
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:33:58
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:57
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:33:57
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:57
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:33:57
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:55
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:33:55
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:55
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:33:55
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:55
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:55
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:33:55
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:52
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:33:52
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:52
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:33:52
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:52
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:52
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:33:52
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:49
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:33:49
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:49
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:33:49
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:49
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:49
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:33:49
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:47
+ 5
+ 恢复正常
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:47
+ 3
+ 恢复正常
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:33:47
+ 4
+ 恢复正常
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:46
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:33:46
+ 5
+ 触网报警
+ 25
+ 25
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:46
+ 3
+ 短路报警
+ 51
+ 51
+ 10.4
+ 10.4
+
+
+ 2019-04-12 12:33:46
+ 4
+ 失联
+ 24
+ 24
+ 10.5
+ 10.5
+
+
+ 2019-04-12 12:33:23
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:33:20
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:33:17
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:33:15
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:33:12
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:32:56
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:32:53
+ 1
+ 断网报警
+ 50
+ 51
+ 10.4
+ 10.5
+
+
+ 2019-04-12 12:32:24
+ 主机1
+ 自检成功
+
+
+
+
+
+
+ 2019-04-12 12:32:24
+ 主机1
+ 自检成功
+
+
+
+
+
+
+ 2019-04-12 12:32:21
+ 主机1
+ 自检成功
+
+
+
+
+
+
+ 2019-04-12 12:32:21
+ 主机1
+ 自检成功
+
+
+
+
+
+
+ 2019-04-12 12:32:14
+ 主机1
+ 关机
+
+
+
+
+
+
+ 2019-04-12 12:32:13
+ 主机1
+ 关机
+
+
+
+
+
+
+ 2019-04-12 12:32:10
+ 主机1
+ 关机
+
+
+
+
+
+
+ 2019-04-12 12:32:10
+ 主机1
+ 关机
+
+
+
+
+
+
+ 2019-04-12 12:31:56
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:56
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:53
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:53
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:50
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:50
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:47
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:47
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:44
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:44
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:41
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:41
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:38
+ 主机1
+ 开机
+
+
+
+
+
+
+ 2019-04-12 12:31:38
+ 主机1
+ 开机
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/Test/\345\205\250\345\261\200\347\203\255\351\224\256/HotKey.py" "b/Test/\345\205\250\345\261\200\347\203\255\351\224\256/HotKey.py"
index 0d2d24097d8487b2fd89e44bfb37835ebb6a1e0f..b9b2f28692787227498a0aa004868fda4d433fe3 100644
--- "a/Test/\345\205\250\345\261\200\347\203\255\351\224\256/HotKey.py"
+++ "b/Test/\345\205\250\345\261\200\347\203\255\351\224\256/HotKey.py"
@@ -1,29 +1,27 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年12月11日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: HotKey
@description:
-'''
+"""
import sys
-from PyQt5.QtCore import pyqtSignal, Qt
-from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QTextBrowser, QPushButton,\
- QMessageBox
import keyboard
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+try:
+ from PyQt5.QtCore import pyqtSignal, Qt
+ from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QTextBrowser, QPushButton, QMessageBox
+except ImportError:
+ from PySide2.QtCore import Signal as pyqtSignal, Qt
+ from PySide2.QtWidgets import QWidget, QApplication, QVBoxLayout, QTextBrowser, QPushButton, QMessageBox
class Window(QWidget):
-
dialogShow = pyqtSignal()
def __init__(self, *args, **kwargs):
@@ -51,7 +49,8 @@ class Window(QWidget):
'ctrl+alt+del', lambda: self.logView.append('😏😏我知道你按了任务管理器😏😏'))
# 这个函数类似while True,由于这里有界面GUI的loop事件,可以达到类似的效果
-# keyboard.wait()#Block forever, like `while True`.==
+
+ # keyboard.wait()#Block forever, like `while True`.==
def onShow(self):
"""显示"""
diff --git "a/Test/\350\207\252\345\212\250\346\233\264\346\226\260/mylibs/testlibs.py" "b/Test/\350\207\252\345\212\250\346\233\264\346\226\260/mylibs/testlibs.py"
index 431226ea5ac61d42224c5bb9b5f8a551aef39793..6cd81036f88e56e6a24ff938d140dc694d52f442 100644
--- "a/Test/\350\207\252\345\212\250\346\233\264\346\226\260/mylibs/testlibs.py"
+++ "b/Test/\350\207\252\345\212\250\346\233\264\346\226\260/mylibs/testlibs.py"
@@ -1,18 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年5月7日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: 自动更新.mylibs.testlibs
@description:
-'''
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+"""
def version():
diff --git "a/Test/\350\207\252\345\212\250\346\233\264\346\226\260/setup.py" "b/Test/\350\207\252\345\212\250\346\233\264\346\226\260/setup.py"
index 6e994728eb8b79e0952d39afe539be68a7b79492..7bca2a0d4eb6c911f160980e02a228cf00ae67c9 100644
--- "a/Test/\350\207\252\345\212\250\346\233\264\346\226\260/setup.py"
+++ "b/Test/\350\207\252\345\212\250\346\233\264\346\226\260/setup.py"
@@ -1,13 +1,11 @@
-from distutils.core import setup
import glob
import os
import py_compile
import site
import sys
+from distutils.core import setup
from zipfile import ZipFile, ZIP_DEFLATED
-import py2exe # @UnusedImport
-
sys.argv.append("py2exe")
sitepackages = site.getsitepackages()[1]
diff --git "a/Test/\350\207\252\345\212\250\346\233\264\346\226\260/test.py" "b/Test/\350\207\252\345\212\250\346\233\264\346\226\260/test.py"
index b70643a77fef371e9266e99159e51c03ea1e86d4..dd7a40a960800640facffa94925695bbb289070d 100644
--- "a/Test/\350\207\252\345\212\250\346\233\264\346\226\260/test.py"
+++ "b/Test/\350\207\252\345\212\250\346\233\264\346\226\260/test.py"
@@ -1,26 +1,22 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2017年5月7日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: 自动更新.test
@description:
-'''
+"""
import sys
+
sys.path.append("mylibs")
import os
from time import sleep
from zipfile import ZipFile
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
-
-
def update():
# 更新
from mylibs import testlibs # @UnresolvedImport
@@ -50,6 +46,7 @@ def main():
os.startfile(sys.executable) # 重启
sys.exit() # 退出本身
+
if __name__ == "__main__":
main()
input("press any key exit")
diff --git "a/Test/\350\207\252\345\256\232\344\271\211import/IronyImporter.py" "b/Test/\350\207\252\345\256\232\344\271\211import/IronyImporter.py"
index 1a67eef0f4d8c77bccdd0cb441a5027aab9b07ae..d2b203f1d97a4b3966dd21fa6ecfb1a3dca5e336 100644
--- "a/Test/\350\207\252\345\256\232\344\271\211import/IronyImporter.py"
+++ "b/Test/\350\207\252\345\256\232\344\271\211import/IronyImporter.py"
@@ -1,14 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月28日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: IronyImporter
@description:
-'''
+"""
import base64
import os
import sys
@@ -16,11 +16,6 @@ from types import ModuleType
import xxtea # @UnresolvedImport
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
-
KEY = base64.b85decode("HF5^hbNbOVOKM=(SB`7h")
diff --git "a/Test/\350\207\252\345\256\232\344\271\211import/build.py" "b/Test/\350\207\252\345\256\232\344\271\211import/build.py"
index 86aeba8bba01229df7398a37dfcc8220e5c41f2c..21eeb3a198c4990d5efe834ab40410da2ac4873f 100644
--- "a/Test/\350\207\252\345\256\232\344\271\211import/build.py"
+++ "b/Test/\350\207\252\345\256\232\344\271\211import/build.py"
@@ -1,23 +1,19 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月28日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: build
@description:
-'''
+"""
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
import base64
import xxtea # @UnresolvedImport
-
KEY = base64.b85decode("HF5^hbNbOVOKM=(SB`7h")
with open("src/test.py", "rb") as fi:
diff --git "a/Test/\350\207\252\345\256\232\344\271\211import/main.py" "b/Test/\350\207\252\345\256\232\344\271\211import/main.py"
index cccc6cbbe5fa1413b9906aa45cc0aa0c86c983c0..47ebb2e2e42f98a8cbba6de679c2bf89b63a1714 100644
--- "a/Test/\350\207\252\345\256\232\344\271\211import/main.py"
+++ "b/Test/\350\207\252\345\256\232\344\271\211import/main.py"
@@ -1,25 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月28日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: main
@description:
-'''
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+"""
# 首先要引入importer
-import IronyImporter # @UnresolvedImport @UnusedImport
-
# 测试开始
import test
+
print(test)
print(dir(test))
print(test.test(1, 5)) # @UndefinedVariable
diff --git "a/Test/\350\207\252\345\256\232\344\271\211import/src/test.py" "b/Test/\350\207\252\345\256\232\344\271\211import/src/test.py"
index 07d5d45e3a19a7fe20f0bd3df3a1570905632ad3..4049409f568493a26e37798f6a63255064a4c7a3 100644
--- "a/Test/\350\207\252\345\256\232\344\271\211import/src/test.py"
+++ "b/Test/\350\207\252\345\256\232\344\271\211import/src/test.py"
@@ -1,18 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-'''
+"""
Created on 2018年1月28日
-@author: Irony."[讽刺]
-@site: https://pyqt5.com , https://github.com/892768447
+@author: Irony
+@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: test
@description:
-'''
-
-__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
-__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
-__Version__ = "Version 1.0"
+"""
def test(a, b):