使用pyqt5里的painter绘图函数画了一个时钟。使用库pyqt5、pyautogui。
代码编写
首先去walnutpi.com-pyqt5教程这里复制了一个基本代码,等下在他的基础上进行修改
qt技巧-坐标系平移
translate
函数可以对坐标系原点进行平移。默认状态下左上角是(0,0),这里将窗口正中间作为(0,0)点,方便后续围绕窗口中心来绘制图形
painter.translate( self.width()/2, self.height()/2) #坐标系平移
qt技巧-坐标系缩放
scale
函数,可以设置一个坐标点所对应的实际屏幕像素点数量。
比如我调用scale(10,50)函数后,坐标(0,0)跟坐标(0,1)之间就相差了50个像素点距离。坐标(0,0)跟坐标(1,0)之间就相差了10个像素点距离。
我使用这段代码,后面如果使用画线函数绘制一条长度100的线,无论窗口大小怎么变动,这条线都会是窗口较短那条边的一半
short_side = min( self.width(), self.height())
painter.scale(short_side / 200.0, short_side / 200.0)
绘制指针
拿时钟绘制举例,大概流程如下
save
函数保存painter的状态rotate
函数对坐标系进行整体旋转drawConvexPolygon
函数来绘制多边形restore
函数将painter重置回save时的状态,已经绘制好的图形不会变,这里是为了将坐标系回正。
首先定义指针的各个坐标点
# 时针形状
NEEDLE_HOUR_heigh = 20
NEEDLE_HOUR_width = 4
NEEDLE_HOUR_heigh_anoth = 6
Pointr_hour=[
QPoint(int(NEEDLE_HOUR_width/2), NEEDLE_HOUR_heigh_anoth),
QPoint(int(-NEEDLE_HOUR_width/2), NEEDLE_HOUR_heigh_anoth),
QPoint(int(-NEEDLE_HOUR_width/2), -NEEDLE_HOUR_heigh),
QPoint(0, -NEEDLE_HOUR_heigh-6),
QPoint(int(NEEDLE_HOUR_width/2), -NEEDLE_HOUR_heigh),
]
然后在加入paintEvent内加入绘制部分
# 时针
painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush( Color_hour_pointer )
painter.save()
painter.rotate(30.0 * ( datetime.now().hour + datetime.now().minute / 60 ))
painter.drawConvexPolygon(Pointr_hour)
painter.restore()
绘制刻度线
跟绘制表针的套路一样,只是这里换成了用drawline
save
函数保存painter的状态rotate
函数对坐标系进行旋转drawConvexPolygon
函数来绘制多边形restore
函数将painter重置回save时的状态
# 表盘-分钟线
painter.save()
painter.setPen(Color_minute_line)
for i in range(0, 360, 6):
if i%30 !=0 :
painter.drawLine(95, 0, 96, 0)
painter.rotate(6)
painter.restore()
绘制表盘上的数字
- 使用
setFont
来设置字体大小 - 调用math库的sin cos函数,计算文本要放置的坐标
- 使用
drawText
函数来绘制文本
# 表盘-数字
painter.setPen( Color_hour_line )
painter.setBrush( Color_hour_line )
tmp_font = QFont(painter.font())
tmp_font.setPointSize(int(short_side/50))
painter.setFont(tmp_font)
size = painter.font().pointSize()
for i in range(1, 13):
if i == datetime.now().hour%12:
painter.setPen( Color_hour_pointer )
painter.setBrush( Color_hour_pointer )
else :
painter.setPen( Color_hour_line )
painter.setBrush( Color_hour_line )
mx = 80 * math.sin(math.radians(i*30)) - size / 2
my = 80 * math.cos(math.radians(i*30)) - size / 2
mx = int( mx )
my = int( -my )
# painter.drawLine(0, 0, mx, my)
painter.drawText(mx, my, str(i))
定时触发重绘事件
重绘事件只在窗口初始化,窗口大小改变之类的关键点才会自动触发。时钟嘛,肯定要让他一秒触发一次。
# 定时触发重绘
window.timer = QTimer() # 定时器
window.timer.timeout.connect(window.update)
window.timer.start(1000) # 每1s 更新一次
点击窗口任意位置退出
我希望这个窗口全屏显示,但不想做丑死的退出按钮。还好窗口提供了一个鼠标点击事件,在这里面调用退出即可。
就像paintEvent一样,在window的类里面定义即可
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.close()
程序启动后将鼠标移动到右下角
鼠标太丑了,全屏黑色背景下太扎眼。这里调用一个库来强制移动鼠标位置。
import pyautogui
screen_width, screen_height = pyautogui.size()
pyautogui.moveTo(screen_width-1, screen_height-1)
最终代码
# -*- coding: utf-8 -*-
# pyQT5 For WalnutPi
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QWidget
import math
from datetime import datetime
import pyautogui
Color_hour_line = QColor(100, 100, 100, 200)
Color_hour_pointer = QColor(255, 120, 0, 200)
Color_minute_line = QColor(200, 200, 200, 150)
Color_minute_pointer = QColor(0, 255, 0, 200)
Color_sec_pointer = QColor(200, 200, 0, 150)
Color_center = QColor(50, 50, 100, 255)
# 时针形状
NEEDLE_HOUR_heigh = 20
NEEDLE_HOUR_width = 4
NEEDLE_HOUR_heigh_anoth = 6
Pointr_hour=[
QPoint(int(NEEDLE_HOUR_width/2), NEEDLE_HOUR_heigh_anoth),
QPoint(int(-NEEDLE_HOUR_width/2), NEEDLE_HOUR_heigh_anoth),
QPoint(int(-NEEDLE_HOUR_width/2), -NEEDLE_HOUR_heigh),
QPoint(0, -NEEDLE_HOUR_heigh-6),
QPoint(int(NEEDLE_HOUR_width/2), -NEEDLE_HOUR_heigh),
]
# 时针形状
NEEDLE_minute_heigh = 60
NEEDLE_minute_width = 4
NEEDLE_minute_heigh_anoth = 3
Pointr_minute=[
QPoint(int(NEEDLE_minute_width/2), NEEDLE_minute_heigh_anoth),
QPoint(int(-NEEDLE_minute_width/2), NEEDLE_minute_heigh_anoth),
QPoint(int(-NEEDLE_minute_width/2), -NEEDLE_minute_heigh),
QPoint(0, -NEEDLE_minute_heigh-6),
QPoint(int(NEEDLE_minute_width/2), -NEEDLE_minute_heigh),
]
# 秒针形状
NEEDLE_sec_heigh = 97
NEEDLE_sec_width = 2
NEEDLE_sec_heigh_anoth = 6
Pointr_sec=[
QPoint(int(NEEDLE_sec_width/2), NEEDLE_sec_heigh_anoth),
QPoint(int(-NEEDLE_sec_width/2), NEEDLE_sec_heigh_anoth),
QPoint(int(-NEEDLE_sec_width/2), -NEEDLE_sec_heigh),
QPoint(int(NEEDLE_sec_width/2), -NEEDLE_sec_heigh),
]
class Window(QWidget):
def __init__(self):
super().__init__() #同时执行父对象QWidget的初始化程序
self.setWindowTitle("WalnutPi Paint") # 设置窗口标题
self.resize(480,320) # 设置窗口大小
#窗口背景颜色设置
self.setObjectName("Paint_Window")
self.setStyleSheet("#Paint_Window{background-color: black}") #黑色
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.close()
def paintEvent(self,event):
painter = QPainter(self)
painter.translate( self.width()/2, self.height()/2) #坐标系平移
short_side = min( self.width(), self.height())
painter.scale(short_side / 200.0, short_side / 200.0)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
# 表盘-数字
painter.setPen( Color_hour_line )
painter.setBrush( Color_hour_line )
tmp_font = QFont(painter.font())
tmp_font.setPointSize(int(short_side/50))
painter.setFont(tmp_font)
size = painter.font().pointSize()
for i in range(1, 13):
if i == datetime.now().hour%12:
painter.setPen( Color_hour_pointer )
painter.setBrush( Color_hour_pointer )
else :
painter.setPen( Color_hour_line )
painter.setBrush( Color_hour_line )
mx = 80 * math.sin(math.radians(i*30)) - size / 2
my = 80 * math.cos(math.radians(i*30)) - size / 2
mx = int( mx )
my = int( -my )
# painter.drawLine(0, 0, mx, my)
painter.drawText(mx, my, str(i))
# 时针
painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush( Color_hour_pointer )
painter.save()
painter.rotate(30.0 * ( datetime.now().hour + datetime.now().minute / 60 ))
painter.drawConvexPolygon(Pointr_hour)
painter.restore()
# 分针
painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush( Color_minute_pointer )
painter.save()
painter.rotate(6 * datetime.now().minute)
painter.drawConvexPolygon(Pointr_minute)
painter.restore()
# 秒针
painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush( Color_sec_pointer )
painter.save()
painter.rotate(6 * datetime.now().second)
painter.drawConvexPolygon(Pointr_sec)
painter.restore()
# 中心圆
painter.setPen(Color_center)
painter.setBrush(Color_center)
CENTER_r = 2
painter.drawEllipse(-CENTER_r, -CENTER_r, CENTER_r*2, CENTER_r*2)
# 表盘-小时线
painter.save()
painter.setPen(Color_hour_line)
painter.rotate(-90)
for i in range(0, 360, 30):
painter.drawLine(88, 0, 96, 0)
painter.rotate(30)
painter.restore()
# 表盘-当前小时线
painter.save()
painter.setPen( Color_hour_pointer )
painter.rotate(datetime.now().hour % 12 * 30 - 90 )
painter.drawLine(88, 0, 96, 0)
painter.restore()
# 表盘-分钟线
painter.save()
painter.setPen(Color_minute_line)
for i in range(0, 360, 6):
if i%30 !=0 :
painter.drawLine(95, 0, 96, 0)
painter.rotate(6)
painter.restore()
# 表盘-当前分钟线
painter.save()
painter.setPen( Color_minute_pointer )
painter.rotate(datetime.now().minute % 60 * 6 - 90 )
painter.drawLine(90, 0, 96, 0)
painter.restore()
#################
# 主程序代码 #
#################
import sys
#【可选代码】允许Thonny远程运行
import os
os.environ["DISPLAY"] = ":0.0"
#【可选代码】解决2K以上分辨率显示器显示缺失问题
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
#主程序入口,构建窗口并显示
app = QtWidgets.QApplication(sys.argv)
window = Window() #构建窗口对象
# window.show() #显示窗口
window.showFullScreen() #全屏显示窗口
#【建议代码】允许终端通过ctrl+c中断窗口,方便调试
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
timer = QtCore.QTimer()
timer.start(100) # You may change this if you wish.
timer.timeout.connect(lambda: None) # Let the interpreter run each 100 ms
# 定时触发重绘
window.timer = QTimer() # 定时器
window.timer.timeout.connect(window.update)
window.timer.start(1000) # 每1s 更新一次
# 将鼠标强制移动到右下角
screen_width, screen_height = pyautogui.size()
pyautogui.moveTo(screen_width-1, screen_height-1)
sys.exit(app.exec_()) #程序关闭时退出进程