PyQt5快速入门(六)PyQt5 GUI界面设计
一、窗口风格
1、设置窗口风格
Qt实现的窗口样式默认使用的是当前操作系统的原生窗口样式,在不同操作系统下原生窗口样式显示的风格是不一样的。
可以为每个Widget设置风格:setStyle(QStyle style)
获取当前平台支持的原有QStyle样式QStyleFactory.keys()
对QApplication设置QStyle样式QApplication.setStyle(QStyleFactory.create(“WindowsXP”))
如果其它Widget没有设置QStyle,则默认使用QApplication使用的QStyle。
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.layout = QHBoxLayout()
label = QLabel("Set Style:",self)
combo = QComboBox(self)
combo.addItems(QStyleFactory.keys())
# 选择当前窗口风格
index = combo.findText(QApplication.style().objectName(), Qt.MatchFixedString)
# 设置当前窗口风格
combo.setCurrentIndex(index)
combo.activated[str].connect(self.onCurrentIndexChanged)
self.layout.addWidget(label)
self.layout.addWidget(combo)
self.setLayout(self.layout)
self.setWindowTitle("Application Style")
self.resize(300, 100)
def onCurrentIndexChanged(self, style):
QApplication.setStyle(style)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
2、设置窗口样式
PyQt5使用setWindowFlags(Qt.WindowFlags)函数设置窗口样式。
Qt的窗口类型如下:
Qt.Widget:默认窗口,由最大化、最小化、关闭按钮
Qt.Window:普通窗口,有最大化、最小化、关闭按钮
Qt.Dialog:对话框窗口,有问号和关闭按钮
Qt.Popup:弹出窗口,窗口无边框
Qt.ToolTip:提示窗口,窗口无边框,无任务栏
Qt.SplashScreen:闪屏,窗口无边框,无任务栏
Qt.SubWindow:子窗口,窗口无按钮,但有标题栏
Qt自定义的顶层窗口外观标识:
Qt.MSWindowsFixedSizeDialogHint:窗口无法调整大小
Qt.FrameLessWindowHint:窗口无边框
Qt.CustomWinodwHint:有边框但无标题栏和按钮,不能移动和拖动
Qt.WindowTitleHint:添加一个标题栏和一个关闭按钮
Qt.WindowSystemMenuHint:添加系统目录和一个关闭按钮
Qt.WindowMaximizeButtonHint:激活最大化和关闭按钮,禁止最小化按钮
Qt.WindowMinimizeButtonHint:激活最小化和关闭按钮,禁止最大化按钮
Qt.WindowMinMaxButtonsHint:激活最大化、最小化和关闭按钮
Qt.WindowCloseButtonHint:增加一个关闭按钮
Qt.WindowContextHelpButtonHint:增加问号和关闭按钮
Qt.WindowStaysOnTopHint:窗口始终处于顶层位置
Qt.WindowStaysOnBottomHint:窗口始终处于底层位置
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("MainWindow")
self.setWindowFlags(Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint)
self.resize(400, 200)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
3、自定义窗口
自定义一个无边框、铺满整个显示屏的窗口实现如下:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("MainWindow")
self.setWindowFlags(Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint)
self.setStyleSheet("background-color:blue;")
def showMaximized(self):
# 获取桌面控件
desktop = QApplication.desktop()
# 获取屏幕可显示矩形区域
rect = desktop.availableGeometry()
# 设置窗口尺寸
self.setGeometry(rect)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.showMaximized()
sys.exit(app.exec_())
二、绘图
1、图像类
Qt中有四个图像类,QPixmap、QImage、QPicture、QBitmap。
QPixmap专为绘图而设计,在绘制图片时需要QPixmap。
QImage提供了一个与硬件无关的图像表示函数,可以用于图片的像素级访问。
QPicture是绘图设备类,继承自QPainter,可以使用QPainter的begin函数在QPicture上绘图,使用end结束绘图,使用QPicture的save函数将QPainter使用过的绘图指令保存到文件中。
QBitmap是一个继承自QPixmap的便利类,提供了1bit深度的二值图像的类,提供的单×××像可以用来制作游标QCursor或者画刷QBrush。
2、绘图
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.pix = QPixmap(400, 400)
self.pix.fill(Qt.white)
self.lastPoint = QPoint()
self.endPoint = QPoint()
self.setWindowFlags(Qt.WindowTitleHint)
self.resize(400, 400)
def paintEvent(self, event):
painter = QPainter(self.pix)
painter.drawLine(self.lastPoint, self.endPoint)
self.lastPoint = self.endPoint
painter = QPainter(self)
painter.drawPixmap(0, 0, self.pix)
def mouseMoveEvent(self, event):
if event.buttons() and Qt.LeftButton:
self.endPoint = event.pos()
self.update()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.lastPoint = event.pos()
self.endPoint = self.lastPoint
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.endPoint = event.pos()
self.update()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
3、双缓冲绘图
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.pix = QPixmap(400, 400)
self.pix.fill(Qt.white)
self.lastPoint = QPoint()
self.endPoint = QPoint()
self.setWindowFlags(Qt.WindowTitleHint)
self.resize(400, 400)
def paintEvent(self, event):
painter1 = QPainter(self)
x = self.lastPoint.x()
y = self.lastPoint.y()
w = self.endPoint.x() - x
h = self.endPoint.y() - y
# 将图形会值在PixMap画布
painter2 = QPainter(self.pix)
painter2.drawRect(x, y, w, h)
# 将pximap绘制到窗口
painter1.drawPixmap(0, 0, self.pix)
def mouseMoveEvent(self, event):
if event.buttons() and Qt.LeftButton:
self.endPoint = event.pos()
self.update()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.lastPoint = event.pos()
self.endPoint = self.lastPoint
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.endPoint = event.pos()
self.update()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
上述代码中,首先使用painter将图形绘制在Pixmap上,然后将Pixmap绘制到窗口中。在拖动鼠标绘制矩形的过程中会出现重影,鼠标拖动过程中,屏幕会刷新很多次,paintEvent函数会被执行很多次,每次都会绘制一个矩形。
为了避免重影,可以使用双缓冲绘制技术。即使用两块画布,pix和tempPix,tempPix作为临时缓冲区,当拖动鼠标绘制矩形时,将内容先绘制到tempPix上,然后再将tempPix绘制到界面窗口。当释放鼠标完成矩形绘制时,将tempPix的内容复制到pix缓冲区,最终完成绘制到窗口。
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.pix = QPixmap(400, 400)
self.pix.fill(Qt.white)
self.lastPoint = QPoint()
self.endPoint = QPoint()
self.setWindowFlags(Qt.WindowTitleHint)
self.resize(400, 400)
self.isDrawing = False
self.tempPix = QPixmap()
def paintEvent(self, event):
painter = QPainter(self)
x = self.lastPoint.x()
y = self.lastPoint.y()
w = self.endPoint.x() - x
h = self.endPoint.y() - y
if self.isDrawing:
self.tempPix = self.pix
pp = QPainter(self.tempPix)
pp.drawRect(x, y, w, h)
painter.drawPixmap(0, 0, self.tempPix)
else:
pp = QPainter(self.pix)
pp.drawRect(x, y, w, h)
painter.drawPixmap(0, 0, self.pix)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.lastPoint = event.pos()
self.endPoint = self.lastPoint
self.isDrawing = True
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.endPoint = event.pos()
self.update()
self.isDrawing = False
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
三、QSS样式设置
1、QSS语法规则
QSS(Qt Style Sheets),即Qt样式表,是用来自定义控件外观的一种机制。QSS语法规则与CSS大体相同,QSS样式由两部分组成,选择器用于指定哪些控件受到影响,声明用于指定哪些属性应该在控件上进行设置。声明部分是一系列的“属性:值”,使用分号分隔各个不同的属性值对,使用大括号将所有的声明包括在内。QPushButton{color:red;}
表示设置QPushButton及其子类所有实例的前景色为红色,QPushButton表示选择器,指定所有的QPushButton类及其子类都会受到影响。
可以使用多个选择器指定相关的声明,使用逗号将各个选择器分隔,如QPushButton,QLineEdit{color:red;}
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button = QPushButton("Button")
self.layout = QHBoxLayout()
self.layout.addWidget(button)
self.setLayout(self.layout)
qss = "QPushButton{color:red;}"
self.setStyleSheet(qss)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
2、QSS选择器类型
QSS选择器有如下几种类型:
1、通配选择器:*
,匹配所有的控件
2、类型选择器,如QPushButton,用于匹配所有的类及其子类的实例。
3、属性选择器:匹配所有的属性及属性值的实例,如QPushButton[name=”okButton”]将匹配所有的name属性为okButton的按钮实例。
4、类选择器:.ClassName,如.QPushButton用于匹配所有的QPushButton实例,但不匹配其子类。
5、ID选择器:#objectName,如#okButton匹配所有的ID为okButton的控件,ID即为obejctName。
6、后代选择器:如QDialog QPushButton,匹配所有的QDialog容器中包含的QPushButton,不管是直接的还是间接的。
7、子选择器:如QDialog > QPushButton,匹配所有的QDialog容器中包含的QPushButton,要求QPushButton的直接父容器是QDialog。
3、QSS子控件
QSS子控件是一种选择器,通常应用在复杂控件上,如QComboBox,QComboBox有一个矩形的外边框,右边有一个下拉箭头,点击后会弹出下拉列表。
::drop-down子控件选择器可以与选择器组合使用,QComboBox#combo::drop-down{imge:url(dropdown.png)}
表示为指定ID为combo的QComboBox控件的下拉箭头自定义图标。
4、QSS伪状态
QSS伪状态选择器是以:开头的一个选择表达式,如:hover,表示当鼠标经过时的状态。伪状态选择器限制了当控件处于某种状态时才可以使用的QSS规则,伪状态只能描述一个控件或复合控件的子控件状态,因此只能放在选择器的最后面。QComboBox::drop-down:hover{background-color:red;}
5、QDarkStyleSheet
QDarkStyleSheet是一个用于PyQt应用的深黑色样式表。
GitHub:
https://github.com/ColinDuquesnoy/QDarkStyleSheet
四、设置窗口背景
窗口背景主要包括背景色和背景图片。设置窗口背景主要有三种方法:QSS设置窗口背景;QPalette设置窗口背景;paintEvent函数内部使用QPainter绘制窗口背景。
1、QSS设置窗口背景
通过QSS可以设置窗口的背景色或背景图片。
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("mainWindow")
qss = "QWidget#mainWindow{background-color:black;}"
# qss = "QWidget#mainWindow{border-image:url(background.png);}"
self.setStyleSheet(qss)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
2、QPalette设置窗口背景
使用QPalette设置窗口背景颜色。
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
palette = QPalette()
palette.setColor(QPalette.Background, Qt.black)
self.setPalette(palette)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
使用QPalette设置窗口背景图片时,需要考虑背景图片的尺寸,当背景图片的宽度和高度大于窗口的宽度和高度时,背景图片将会平铺整个窗口;当背景图片的宽度和高度小于窗口的宽度和高度时,则加载多个背景图片。
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
palette = QPalette()
palette.setBrush(QPalette.Background, QBrush(QPixmap("background.png")))
self.setPalette(palette)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
3、paintEvent绘制窗口背景
在paintEvent函数内部绘制背景色:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("MainWindow")
def paintEvent(self, event):
painter = QPainter(self)
painter.setBrush(Qt.black)
painter.drawRect(self.rect())
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
在paintEvent函数内部绘制背景图片:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("MainWindow")
def paintEvent(self, event):
painter = QPainter(self)
pixmap = QPixmap("background.png")
painter.drawPixmap(self.rect(), pixmap)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
五、样式设置实例
1、为标签添加背景图片
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("MainWindow")
self.layout = QVBoxLayout()
label = QLabel()
label.setStyleSheet("QLabel{border-image:url(background.png);}")
label.setFixedHeight(400)
label.setFixedWidth(600)
self.layout.addWidget(label)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
2、为按钮添加背景图片
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("MainWindow")
self.layout = QVBoxLayout()
button = QPushButton("Button")
button.setMaximumSize(60, 30)
button.setObjectName("okButton")
button.setStyleSheet("QPushButton#okButton{border-image:url(background.png);}")
self.layout.addWidget(button)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
3、缩放图片
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("MainWindow")
self.layout = QVBoxLayout()
label = QLabel()
img = QImage("background.png")
result = img.scaled(label.width(), label.height(),Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
label.setPixmap(QPixmap.fromImage(result))
self.layout.addWidget(label)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
4、设置窗口透明
如果窗口是透明的,通过窗口可以看到桌面,要想实现窗口的透明效果,需要设置窗口透明度。透明度取值范围为0(完全透明)到1(不透明)。
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("MainWindow")
self.setWindowOpacity(0.5)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
5、加载QSS
Qt中,为了降低耦合性,将UI代码与业务逻辑代码进行分离,通常会定义QSS文件,编写各种控件的样式,最后使用QApplication.setStyleSheet或QWidget.setStyleSheet设置QSS。
通常编写QSS文件需要加载到qrc资源文件中,然后编写一个公用类加载QSS。
class CommonHelper(object):
@staticmethod
def setStyleSheet(filename):
if filename is not None:
style = ""
with open(filename, "r") as f:
style = f.read()
qApp.setStyleSheet(style)