十六、PyQt5 之 窗口、绘图、QSS、动画效果
一、窗口
1.1 设置窗口风格(QApplication.setStyle(...))
#!/usr/bin/python # -*- coding:utf-8 -*- """ 设置窗口中控件的风格 QApplication.setStyle(...) void QComboBox::activated ( int index ) [signal] void QComboBox::activated ( const QString & text ) [signal] """ import sys from PyQt5.QtWidgets import * from PyQt5 import QtCore # 查看有哪些风格类型 # print(QStyleFactory.keys()) # ['windowsvista', 'Windows', 'Fusion'] class WindowStyle(QWidget): def __init__(self): super(WindowStyle, self).__init__() self.setWindowTitle("设置窗口风格") layout = QHBoxLayout() self.styleLabel = QLabel("设置窗口风格: ") self.styleComboBox = QComboBox() self.styleComboBox.addItems(QStyleFactory.keys()) # 获取当前窗口的风格 print(QApplication.style().objectName()) # windowsvista # 选择当前styleComboBox内的风格, 并返回风格索引 index = self.styleComboBox.findText(QApplication.style().objectName(), QtCore.Qt.MatchFixedString) # 根据风格索引来设置界面风格 self.styleComboBox.setCurrentIndex(index) # activated[str] Python没有C++所具有的强类型函数重载。因此,PyQt通过拥有一个可以连接的插槽字典来处理这个问题。这个字典的关键是句柄将接受的参数的type self.styleComboBox.activated[str].connect(self.handleStyleChanged) layout.addWidget(self.styleLabel) layout.addWidget(self.styleComboBox) self.setLayout(layout) def handleStyleChanged(self, style): QApplication.setStyle(style) if __name__ == "__main__": app = QApplication(sys.argv) form = WindowStyle() form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 设置窗口样式 (主要是窗口边框,标题栏以及窗口本身的 样式) setWindowFlags FrameWindowHint : 没有边框的窗口 WindowStaysOnTopHint : 总在最zhi上面的窗口 CustomizeWindowHint : 自定义窗口标题栏 WindowTitleHint : 显示窗口标题栏 WindowSystemMenuHint : 显示系统菜单 WindowMinimizeButtonHint : 显示最小化按钮 WindowMaximizeButtonHint : 显示最大化按钮 WindowMinMaxButtonsHint : 显示最小化按钮和最大化按钮 WindowCloseButtonHint : 显示关闭按钮 """ from PyQt5.QtCore import * import sys from PyQt5.QtWidgets import * class WindowPattern(QMainWindow): def __init__(self): super(WindowPattern, self).__init__() self.resize(500, 260) self.setWindowTitle("设置窗口的样式") # 设置最大按钮,或者最顶端(置顶) self.setWindowFlags(Qt.WindowMaximizeButtonHint | Qt.WindowStaysOnTopHint) # 设置窗口的名字 self.setObjectName("MainWindow") # 背景图,设置语法类似CSS self.setStyleSheet("#MainWindow{border-image:url(images/python.jpg);}") if __name__ == "__main__": app = QApplication(sys.argv) form = WindowPattern() form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 用代码控制窗口的最大化和最小化 """ import sys from PyQt5.QtWidgets import * from PyQt5.QtCore import * class WindowMaxMin(QWidget): ### 构造函数 def __init__(self, parent=None): '''构造函数''' # 调用父类构造函数 super(WindowMaxMin, self).__init__(parent) self.resize(300, 400) self.setWindowTitle("用代码控制窗口的最大化和最小化") self.setWindowFlags(Qt.WindowMaximizeButtonHint) layout = QVBoxLayout() maxButton1 = QPushButton() maxButton1.setText('窗口最大化1') maxButton1.clicked.connect(self.maximized1) maxButton2 = QPushButton() maxButton2.setText('窗口最大化2') maxButton2.clicked.connect(self.showMaximized) minButton = QPushButton() minButton.setText('窗口最小化') minButton.clicked.connect(self.showMinimized) layout.addWidget(maxButton1) layout.addWidget(maxButton2) layout.addWidget(minButton) self.setLayout(layout) def maximized1(self): desktop = QApplication.desktop() # 获取桌面可用尺寸 rect = desktop.availableGeometry() self.setGeometry(rect) if __name__ == "__main__": app = QApplication(sys.argv) window = WindowMaxMin() window.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 创建透明窗口 """ from PyQt5.Qt import * import sys if __name__ == '__main__': app = QApplication(sys.argv) win = QMainWindow() win.setWindowTitle('窗口的透明度设置') # 0到1,1表示不透明,0表示完全透明 win.setWindowOpacity(0.6) button = QPushButton('我的按钮', win) win.resize(400, 200) win.show() sys.exit(app.exec())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 实现绘图应用 需要解决3个核心内容 1. 如何绘图: 在paintEvent方法中绘图,通过调用update方法触发painEvent的调用 2. 在哪里绘图: 在白色背景的QPixmap对象中绘图 3. 如何通过移动鼠标进行绘图 鼠标拥有3个事件: (1)鼠标按下:mousePressEvent (2)鼠标移动:mouseMoveEvent (3)鼠标抬起:mouseReleaseEvent """ import sys from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtGui import QPainter, QPixmap from PyQt5.QtCore import Qt, QPoint class Drawing(QWidget): def __init__(self, parent=None): super(Drawing, self).__init__(parent) self.setWindowTitle("绘图应用") self.pix = QPixmap() self.lastPoint = QPoint() self.endPoint = QPoint() self.initUi() def initUi(self): self.resize(600, 600) # 画布大小为600*600,背景为白色 self.pix = QPixmap(600, 600) self.pix.fill(Qt.white) def paintEvent(self, event): pp = QPainter(self.pix) # 根据鼠标指针前后两个位置绘制直线 pp.drawLine(self.lastPoint, self.endPoint) # 让前一个坐标值等于后一个坐标值, # 这样就能实现画出连续的线 self.lastPoint = self.endPoint painter = QPainter(self) painter.drawPixmap(0, 0, self.pix) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: # 获取鼠标按下的点 self.lastPoint = event.pos() def mouseMoveEvent(self, event): if event.buttons() and Qt.LeftButton: # 移动过程中,设置当前位置为结束位置,随时更新过 self.endPoint = event.pos() # 会自动调用 paintEvent 方法 self.update() def mouseReleaseEvent(self, event): # 鼠标左键释放, 和鼠标移动的时候代码是一样的 if event.button() == Qt.LeftButton: self.endPoint = event.pos() # 进行重新绘制 self.update() if __name__ == "__main__": app = QApplication(sys.argv) form = Drawing() form.show() sys.exit(app.exec_())
三、QSS
3.1 QSS基础(setStyleSheet)
#!/usr/bin/python # -*- coding:utf-8 -*- """ QSS基础 QSS(Qt Style Sheets) Qt样式表 用于设置控件的样式 """ from PyQt5.QtWidgets import * import sys class BasicQSS(QWidget): def __init__(self): super().__init__() self.setWindowTitle("QSS样式") btn1 = QPushButton(self) btn1.setText("按钮1") btn2 = QPushButton(self) btn2.setText("按钮2") btn3 = QPushButton(self) btn3.setText("按钮3") vbox = QVBoxLayout() vbox.addWidget(btn1) vbox.addWidget(btn2) vbox.addWidget(btn3) self.setLayout(vbox) if __name__ == '__main__': app = QApplication(sys.argv) form = BasicQSS() # 选择器 类似CSS选择器 # 所有的QPushButton控件都设置为背景颜色为红色 qssStyle = """ QPushButton { background-color:red } """ form.setStyleSheet(qssStyle) form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ QSS基础 QSS(Qt Style Sheets) Qt样式表 用于设置控件的样式 """ from PyQt5.QtWidgets import * import sys class QSSSelector(QWidget): def __init__(self): super().__init__() self.setWindowTitle("QSS样式") btn1 = QPushButton(self) btn1.setText("按钮1") btn2 = QPushButton(self) # setProperty(key, value) btn2.setProperty("name", "btn2") btn2.setText("按钮2") btn3 = QPushButton(self) btn3.setProperty("name", "btn3") btn3.setText("按钮3") vbox = QVBoxLayout() vbox.addWidget(btn1) vbox.addWidget(btn2) vbox.addWidget(btn3) self.setLayout(vbox) if __name__ == '__main__': app = QApplication(sys.argv) form = QSSSelector() # 选择器 qssStyle = """ QPushButton[name="btn2"] { background-color:red; color:yellow; height:120; font-size:60px; } QPushButton[name="btn3"] { background-color:blue; color:yellow; height:60; font-size:30px; } """ form.setStyleSheet(qssStyle) form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ QSS子控件选择器 """ from PyQt5.QtWidgets import * import sys class QSSSubControl(QWidget): def __init__(self): super().__init__() self.setWindowTitle("QSS子控件选择器") # 下拉框 combo = QComboBox(self) combo.setObjectName("myComboBox") combo.addItem("Window") combo.addItem("Linux") combo.addItem("Mac OS X") combo.move(50, 50) self.setGeometry(250, 200, 320, 150) if __name__ == "__main__": app = QApplication(sys.argv) form = QSSSubControl() # #myComboBox 相当于 CSS 中的id ::drop-down 代表子控件 代表的是点击选择下拉列表那个点击下拉按钮 qssStyle = """ QComboBox#myComboBox::drop-down { image:url(./images/python.jpg) } """ form.setStyleSheet(qssStyle) form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 使用QSS为标签和按钮添加背景图 """ from PyQt5.QtWidgets import * import sys class LabelButtonBackground(QWidget): def __init__(self): super(LabelButtonBackground, self).__init__() label1 = QLabel(self) label1.setToolTip('这是一个文本标签') label1.setStyleSheet('QLabel{border-image:url(./images/python.jpg);}') label1.setFixedWidth(476) label1.setFixedHeight(259) btn1 = QPushButton(self) btn1.setObjectName('btn1') btn1.setMaximumSize(48, 48) btn1.setMinimumSize(48, 48) style = ''' #btn1{ border-radius:4px; background-image:url('./images/add.png'); } #btn1:Pressed { background-image:url('./images/addhover.png'); } ''' btn1.setStyleSheet(style) vbox = QVBoxLayout() vbox.addWidget(label1) vbox.addStretch() vbox.addWidget(btn1) self.setLayout(vbox) self.setWindowTitle('使用QSS为标签和按钮添加背景图') if __name__ == "__main__": app = QApplication(sys.argv) form = LabelButtonBackground() form.show() sys.exit(app.exec_())
3.5 三种方式设置背景色和背景图片
3.5.1 方式一 QSS
#!/usr/bin/python # -*- coding:utf-8 -*- """ 使用多种方式设置窗口背景色和背景图片 通过QSS动态修改窗口的背景颜色和背景图片 """ import sys from PyQt5.QtWidgets import * if __name__ == '__main__': app = QApplication(sys.argv) win = QMainWindow() win.setWindowTitle("背景图片") win.resize(350, 250) win.setObjectName("MainWindow") # 通过QSS动态修改窗口的背景颜色和背景图片 win.setStyleSheet("#MainWindow{border-image:url(./images/python.jpg);}") # win.setStyleSheet("#MainWindow{background-color:yellow}") win.show() sys.exit(app.exec())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 使用多种方式设置窗口背景色和背景图片 通过QPalette设置背景图片和背景颜色 """ import sys from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtCore import * if __name__ == '__main__': app = QApplication(sys.argv) win = QMainWindow() win.setWindowTitle("背景图片") win.resize(350, 250) win.setObjectName("MainWindow") # 通过QPalette设置背景图片和背景颜色 palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap("./images/python.jpg"))) # palette.setColor(QPalette.Background, Qt.red) win.setPalette(palette) win.show() sys.exit(app.exec())
3.5.3 方式三 直接绘制
3.5.3.1 QPainter + setBrush
#!/usr/bin/python # -*- coding:utf-8 -*- """ 使用多种方式设置窗口背景色和背景图片 直接绘制 """ import sys from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5.QtCore import * class Background(QWidget): def __init__(self): super(Background, self).__init__() self.setWindowTitle("绘制背景颜色") def paintEvent(self, event): painter = QPainter(self) painter.setBrush(Qt.yellow) painter.drawRect(self.rect()) if __name__ == "__main__": app = QApplication(sys.argv) form = Background() form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 使用多种方式设置窗口背景色和背景图片 直接绘制 """ import sys from PyQt5.QtWidgets import * from PyQt5.QtGui import * class Background(QWidget): def __init__(self): super(Background, self).__init__() self.setWindowTitle("绘制背景颜色") def paintEvent(self, event): painter = QPainter(self) pixmap = QPixmap('./images/screen1.jpg') painter.drawPixmap(self.rect(), pixmap) if __name__ == "__main__": app = QApplication(sys.argv) form = Background() form.show() sys.exit(app.exec_())
// style.qss QMainWindow{ border-image:url(./images/python.jpg); } QToolTip { border:1px solid rgb(45,45,45); background:white; color:red }# CommonHelper.py class CommonHelper: @staticmethod def readQSS(style): with open(style,'r') as f: return f.read()# !/usr/bin/python # -*- coding:utf-8 -*- """ 装载QSS文件 """ import sys from PyQt5.QtWidgets import * from windows.CommonHelper import CommonHelper class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.resize(477, 258) self.setWindowTitle("加载QSS文件") btn = QPushButton() btn.setText('装载QSS文件') btn.setToolTip('提示文本') vbox = QVBoxLayout() vbox.addWidget(btn) btn.clicked.connect(self.onClick) self.setLayout(vbox) widget = QWidget(self) self.setCentralWidget(widget) widget.setLayout(vbox) def onClick(self): styleFile = './style.qss' qssStyle = CommonHelper.readQSS(styleFile) win.setStyleSheet(qssStyle) if __name__ == "__main__": app = QApplication(sys.argv) win = MainWindow() win.show() sys.exit(app.exec_())
四、异型窗口
4.1 实现不规则的异形窗口并移动窗口
#!/usr/bin/python # -*- coding:utf-8 -*- """ 实现不规则的窗口(异形窗口) 通过mask实现异形窗口 需要一张透明的png图,透明部分被扣出,形成一个非矩形的区域 异型窗口可以理解为一个不规则的图相框, 然后填充图片覆盖背景, 异形窗口本身是不能移动的,需要自己操作函数移动和关闭不规则窗口(实时计算窗口坐标) 而且windows下窗口的标题栏也没了,所以问题会出现窗口栏关不掉了 """ import sys from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5.QtCore import * class AbnormityWindow(QWidget): def __init__(self): super(AbnormityWindow, self).__init__() self.setWindowTitle("异形窗口") # 创建一个异形窗口图片 self.pix = QBitmap('./images/mask.png') self.resize(self.pix.size()) # 设置掩模 self.setMask(self.pix) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.m_drag = True self.m_DragPosition = event.globalPos() - self.pos() self.setCursor(QCursor(Qt.OpenHandCursor)) print(event.globalPos()) # print(event.pos()) print(self.pos()) if event.button() == Qt.RightButton: self.close() def mouseMoveEvent(self, QMouseEvent): if Qt.LeftButton and self.m_drag: # 当左键移动窗体修改偏移值 # QPoint # 实时计算窗口左上角坐标 self.move(QMouseEvent.globalPos() - self.m_DragPosition) def mouseReleaseEvent(self, QMouseEvent): self.m_drag = False self.setCursor(QCursor(Qt.ArrowCursor)) def paintEvent(self, event): painter = QPainter(self) # 设置背景图片 painter.drawPixmap(0, 0, self.pix.width(), self.pix.height(), QPixmap('./images/screen1.jpg')) if __name__ == "__main__": app = QApplication(sys.argv) form = AbnormityWindow() form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 不规则窗体的动画实现(Windows 下起作用) """ import sys from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtGui import QPixmap, QPainter, QCursor from PyQt5.QtCore import Qt, QTimer class AnimationWindows(QWidget): def __init__(self, parent=None): super(AnimationWindows, self).__init__(parent) self.i = 1 self.mypix() # 创建一个定时器,每隔 500ms 更换一次图片 self.timer = QTimer() self.timer.setInterval(500) # 500毫秒 self.timer.timeout.connect(self.timeChange) self.timer.start() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.m_drag = True self.m_DragPosition = event.globalPos() - self.pos() self.setCursor(QCursor(Qt.OpenHandCursor)) def mouseMoveEvent(self, QMouseEvent): if Qt.LeftButton and self.m_drag: self.move(QMouseEvent.globalPos() - self.m_DragPosition) def mouseReleaseEvent(self, QMouseEvent): self.m_drag = False self.setCursor(QCursor(Qt.ArrowCursor)) def paintEvent(self, event): painter = QPainter(self) painter.drawPixmap(0, 0, self.pix.width(), self.pix.height(), self.pix) # 每500毫秒修改paint def timeChange(self): self.i += 1 self.mypix() # 显示不规则 pic def mypix(self): self.update() if self.i == 5: self.i = 1 self.mypic = {1: './images/left.png', 2: "./images/up.png", 3: './images/right.png', 4: './images/down.png'} self.pix = QPixmap(self.mypic[self.i]) self.resize(self.pix.size()) self.setMask(self.pix.mask()) self.dragPosition = None if __name__ == '__main__': app = QApplication(sys.argv) form = AnimationWindows() form.show() sys.exit(app.exec_())
五、动画效果
5.1 装载 gif动画文件
#!/usr/bin/python # -*- coding:utf-8 -*- """ 装载Gif动画 QMovie """ import sys from PyQt5.QtWidgets import QApplication, QLabel, QWidget from PyQt5.QtCore import Qt from PyQt5.QtGui import QMovie class LoadingGif(QWidget): def __init__(self): super(LoadingGif, self).__init__() self.label = QLabel("", self) self.setFixedSize(120, 128) self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint) self.movie = QMovie('./images/loading.gif') self.label.setMovie(self.movie) self.movie.start() if __name__ == "__main__": app = QApplication(sys.argv) form = LoadingGif() form.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 缩放图片 QImage.scaled """ from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import Qt import sys class ScaleImage(QWidget): def __init__(self): super().__init__() self.setWindowTitle("图片大小缩放例子") filename = './images/Cloudy_72px.png' img = QImage(filename) label1 = QLabel(self) label1.setFixedWidth(200) label1.setFixedHeight(200) # 宽度, 高度, 忽略图片原本的比例, 图片平滑显示 result = img.scaled(label1.width(), label1.height(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation) label1.setPixmap(QPixmap.fromImage(result)) vbox = QVBoxLayout() vbox.addWidget(label1) self.setLayout(vbox) if __name__ == "__main__": app = QApplication(sys.argv) win = ScaleImage() win.show() sys.exit(app.exec_())
#!/usr/bin/python # -*- coding:utf-8 -*- """ 用动画效果改变窗口尺寸 QPropertyAnimation """ from PyQt5.QtCore import * from PyQt5.QtWidgets import * import sys class AnimWindow(QWidget): def __init__(self): super(AnimWindow, self).__init__() self.OrigHeight = 50 self.ChangeHeight = 150 self.setGeometry(QRect(500, 400, 150, self.OrigHeight)) self.btn = QPushButton('展开', self) self.btn.setGeometry(10, 10, 60, 35) self.btn.clicked.connect(self.change) def change(self): # 保存当前的高度 currentHeight = self.height() if self.OrigHeight == currentHeight: startHeight = self.OrigHeight endHeight = self.ChangeHeight self.btn.setText("收缩") else: startHeight = self.ChangeHeight endHeight = self.OrigHeight self.btn.setText("展开") # geometry 动画效果 self.animation = QPropertyAnimation(self, b'geometry') self.animation.setDuration(500) self.animation.setStartValue(QRect(500, 400, 150, startHeight)) self.animation.setEndValue(QRect(500, 400, 150, endHeight)) self.animation.start() if __name__ == '__main__': app = QApplication(sys.argv) window = AnimWindow() window.show() sys.exit(app.exec_())
# !/usr/bin/python # -*- coding:utf-8 -*- """ 用动画效果一不同速度移动窗口 """ from PyQt5.QtCore import * from PyQt5.QtWidgets import * import sys if __name__ == '__main__': app = QApplication(sys.argv) window1 = QMainWindow() window1.show() window2 = QMainWindow() window2.show() animation1 = QPropertyAnimation(window1, b'geometry') animation2 = QPropertyAnimation(window2, b'geometry') # group = QParallelAnimationGroup() # 并行, 两个动画同时运行 group = QSequentialAnimationGroup() # 串行 group.addAnimation(animation1) group.addAnimation(animation2) # 持续时间 3s animation1.setDuration(3000) animation1.setStartValue(QRect(0, 0, 100, 30)) animation1.setEndValue(QRect(250, 250, 100, 30)) # QEasingCurve.OutBounce 动画特效 animation1.setEasingCurve(QEasingCurve.OutBounce) animation2.setDuration(4000) animation2.setStartValue(QRect(250, 150, 100, 30)) animation2.setEndValue(QRect(850, 250, 100, 30)) animation2.setEasingCurve(QEasingCurve.CosineCurve) group.start() sys.exit(app.exec())