范例 - 小画家
这篇教学会将 PyQt6 结合 QPixmap、QLabel 和 QPushbutton 等功能元件,实作一个简单的小画家功能 ( 调整画笔粗细、颜色 ),让用户可以使用鼠标进行绘图,并且将绘制的图片存档成为 PNG。
快速导览:
因为 Google Colab 不支援 PyQt6,所以请使用本机环境 ( 参考:使用 Python 虚拟环境 ) 或使用 Anaconda Jupyter 进行实作 ( 参考:使用 Anaconda )。
预计划面功能与效果
要实作一个简单的小画家,大概需要有下列几种要素:
- 开新文件、另存文件和关闭的视窗选单。
- 使用鼠标绘图的空白画布。
- 颜色选择按钮以及画笔粗细选择按钮。
透过这几种要素的组合,预期可以得到下图一样的效果,接下来就准备按照下图进行实作:
使用鼠标绘图
参考“QPainter 绘图”和“侦测鼠标事件”的教学范例,可以透过编辑 mousePressEvent、mouseMoveEvent 和 mouseReleaseEvent 属性,实作出在 QPixmap 元件中绘图的功能 ( 使用方法和 PyQt5 略有不同 ),详细说明写在下方程序码中:
from PyQt6 import QtWidgets
from PyQt6.QtGui import *
from PyQt6.QtCore import *
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
MainWindow.setObjectName("MainWindow")
MainWindow.setWindowTitle("oxxo.studio")
MainWindow.resize(400, 300) # 主視窗大小
canvas = QPixmap(400,240) # 建立 QPixmap 元件作為畫布,並設定畫布大小
canvas.fill(QColor('#ffffff')) # 畫布填滿白色
label = QtWidgets.QLabel(MainWindow) # 建立 QLabel
label.setGeometry(0, 0, 400, 240) # 設定大小位置,下方留下一些空白
label.setPixmap(canvas) # 放入畫布
last_x, last_y = None, None # 設定兩個變數紀錄滑鼠座標
penSize = 10 # 畫筆預設粗細
penColor = QColor('#000000') # 畫筆預設顏色
# 放開滑鼠的函式
def release(self):
global last_x, last_y
last_x, last_y = None, None # 清空座標內容
# 按下滑鼠的函式
def mousePress(self):
global penColor, penSize, canvas
mx = int(QEnterEvent.position(self).x())
my = int(QEnterEvent.position(self).y())
qpainter = QPainter() # 建立 QPainter 元件
qpainter.begin(canvas) # 在畫布中開始繪圖
qpainter.setPen(QPen(QColor(penColor), penSize, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) # 設定畫筆樣式
qpainter.drawPoint(mx, my) # 下筆畫出一個點
qpainter.end() # 結束繪圖
label.setPixmap(canvas)
MainWindow.update() # 更新主視窗內容
# 按下滑鼠並移動滑鼠的函式
def draw(self):
global last_x, last_y, penColor, penSize, canvas
mx = int(QEnterEvent.position(self).x())
my = int(QEnterEvent.position(self).y())
if last_x is None:
last_x = mx # 紀錄滑鼠當下的座標
last_y = my
return
qpainter = QPainter() # 建立 QPainter 元件
qpainter.begin(canvas) # 在畫布中開始繪圖
qpainter.setPen(QPen(penColor, penSize, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) # 設定畫筆樣式
qpainter.drawLine(last_x, last_y, mx, my) # 下筆畫出一條線
qpainter.end() # 結束繪圖
label.setPixmap(canvas)
MainWindow.update() # 更新主視窗內容
last_x = mx # 紀錄結束座標
last_y = my
label.mousePressEvent = mousePress # 設定按下滑鼠並移動的事件
label.mouseMoveEvent = draw # 設定按下滑鼠的事件
label.mouseReleaseEvent = release # 設定放開滑鼠的事件
MainWindow.show()
sys.exit(app.exec())
class 写法:
from PyQt6 import QtWidgets
from PyQt6.QtGui import *
from PyQt6.QtCore import *
import sys
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('oxxo.studio')
self.resize(300, 200)
self.setUpdatesEnabled(True)
self.last_x, self.last_y = None, None # 設定兩個變數紀錄滑鼠座標
self.penSize = 10 # 畫筆預設粗細
self.penColor = QColor('#000000') # 畫筆預設顏色
self.ui()
def ui(self):
self.canvas = QPixmap(400,240) # 建立 QPixmap 元件作為畫布,並設定畫布大小
self.canvas.fill(QColor('#ffffff')) # 畫布填滿白色
self.label = QtWidgets.QLabel(self) # 建立 QLabel
self.label.setGeometry(0, 0, 400, 240) # 設定大小位置,下方留下一些空白
self.label.setPixmap(self.canvas) # 放入畫布
def mousePressEvent(self, event):
mx = int(QEnterEvent.position(event).x())
my = int(QEnterEvent.position(event).y())
qpainter = QPainter() # 建立 QPainter 元件
qpainter.begin(self.canvas) # 在畫布中開始繪圖
qpainter.setPen(QPen(QColor(self.penColor), self.penSize, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) # 設定畫筆樣式
qpainter.drawPoint(mx, my) # 下筆畫出一個點
qpainter.end() # 結束繪圖
self.label.setPixmap(self.canvas)
self.update() # 更新主視窗內容
def mouseMoveEvent(self, event):
mx = int(QEnterEvent.position(event).x())
my = int(QEnterEvent.position(event).y())
if self.last_x is None:
self.last_x = mx # 紀錄滑鼠當下的座標
self.last_y = my
return
qpainter = QPainter() # 建立 QPainter 元件
qpainter.begin(self.canvas) # 在畫布中開始繪圖
qpainter.setPen(QPen(self.penColor, self.penSize, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) # 設定畫筆樣式
qpainter.drawLine(self.last_x, self.last_y, mx, my) # 下筆畫出一條線
qpainter.end() # 結束繪圖
self.label.setPixmap(self.canvas)
self.update() # 更新主視窗內容
self.last_x = mx # 紀錄結束座標
self.last_y = my
def mouseReleaseEvent(self, event):
self.last_x, self.last_y = None, None # 清空座標內容
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
Form = MyWidget()
Form.show()
sys.exit(app.exec())
加入颜色选择按钮
延续上述的程序,在主程序里加入下面的程序码,执行后就会出现可以选择并切换颜色的按钮 ( 注意 lambda 要额外加入一个 checked 参数 )。
# 點擊按鈕更換顏色函式
def changeColor(self, color):
global penColor, btn
penColor = QColor(color) # 設定畫筆顏色
for i in btn:
btn[i].setDisabled(False) # 啟用所有按鈕
self.setDisabled(True) # 停用所點擊的按鈕
# 設定顏色清單
colors = ['#ff0000','#ff8800','#ffee00','#00cc00','#0066ff','#0000cc','#cc00cc','#000000','#ffffff']
btn = {} # 因為有很多按鈕,所以使用字典方式紀錄元件
# 依序讀取顏色清單中的顏色
for i in colors:
index = colors.index(i) # 取得該顏色的位置 ( 按鈕定位使用 )
btn[i] = QtWidgets.QPushButton(MainWindow) # 建立按鈕元件
# 設定樣式,當中額外設定禁用時的樣式
btn[i].setStyleSheet('''
QPushButton{
background:'''+i+''';
margin-right:5px;
}
QPushButton:disabled{
border:3px solid #000;
}
''')
btn[i].setGeometry(index*30+10,250,30,30) # 設定每個按鈕的位置
btn[i].clicked.connect(lambda checked, b=btn[i], c=i: changeColor(b, c)) # 設定點擊事件
使用 class 写法,在 self.ui() 里加上下面这段程序码:
# 設定顏色清單
self.colors = ['#ff0000','#ff8800','#ffee00','#00cc00','#0066ff','#0000cc','#cc00cc','#000000','#ffffff']
self.btn = {} # 因為有很多按鈕,所以使用字典方式紀錄元件
# 依序讀取顏色清單中的顏色
for i in self.colors:
index = self.colors.index(i) # 取得該顏色的位置 ( 按鈕定位使用 )
self.btn[i] = QtWidgets.QPushButton(self) # 建立按鈕元件
# 設定樣式,當中額外設定禁用時的樣式
self.btn[i].setStyleSheet('''
QPushButton{
background:'''+i+''';
margin-right:5px;
}
QPushButton:disabled{
border:3px solid #000;
}
''')
self.btn[i].setGeometry(index*30+10,250,30,30) # 設定每個按鈕的位置
self.btn[i].clicked.connect(lambda checked, b=self.btn[i], c=i: self.changeColor(b, c)) # 設定點擊事件
使用 class 写法,在主程序码里新增下面这段:
# 點擊按鈕更換顏色函式
def changeColor(self, thisBtn, color):
self.penColor = QColor(color) # 設定畫筆顏色
for i in self.btn:
self.btn[i].setDisabled(False) # 啟用所有按鈕
thisBtn.setDisabled(True) # 停用所點擊的按鈕
加入画笔粗细选择按钮
延续上述的程序,在主程序里加入下面的程序码,执行后就会出现可以选择并切换画笔粗细的按钮。
# 切換畫筆粗細函式
def changeSize(self, size):
global penSize
btn_s.setDisabled(False) # 啟用「細」的按鈕
btn_m.setDisabled(False) # 啟用「中」的按鈕
btn_l.setDisabled(False) # 啟用「粗」的按鈕
penSize = size # 設定畫筆粗細
self.setDisabled(True) # 停用所點選的按鈕
btn_s = QtWidgets.QPushButton(MainWindow) # 建立「細」的按鈕
btn_s.setText('細')
btn_s.setGeometry(280, 250, 45, 30) # 設定位置
btn_s.clicked.connect(lambda: changeSize(btn_s, 3)) # 設定點擊事件
btn_m = QtWidgets.QPushButton(MainWindow) # 建立「中」的按鈕
btn_m.setText('中')
btn_m.setGeometry(315, 250, 45, 30) # 設定位置
btn_m.setDisabled(True) # 因為預設中,所以先停用中的按鈕
btn_m.clicked.connect(lambda: changeSize(btn_m, 10)) # 設定點擊事件
btn_l = QtWidgets.QPushButton(MainWindow) # 建立「粗」的按鈕
btn_l.setText('粗')
btn_l.setGeometry(350, 250, 45, 30) # 設定位置
btn_l.clicked.connect(lambda: changeSize(btn_l, 20)) # 設定點擊事件
使用 class 写法,在 self.ui() 里加上下面这段程序码:
self.btn_s = QtWidgets.QPushButton(self) # 建立「細」的按鈕
self.btn_s.setText('細')
self.btn_s.setGeometry(280, 250, 45, 30) # 設定位置
self.btn_s.clicked.connect(lambda: self.changeSize(self.btn_s, 3)) # 設定點擊事件
self.btn_m = QtWidgets.QPushButton(self) # 建立「中」的按鈕
self.btn_m.setText('中')
self.btn_m.setGeometry(315, 250, 45, 30) # 設定位置
self.btn_m.setDisabled(True) # 因為預設中,所以先停用中的按鈕
self.btn_m.clicked.connect(lambda: self.changeSize(self.btn_m, 10)) # 設定點擊事件
self.btn_l = QtWidgets.QPushButton(self) # 建立「粗」的按鈕
self.btn_l.setText('粗')
self.btn_l.setGeometry(350, 250, 45, 30) # 設定位置
self.btn_l.clicked.connect(lambda: self.changeSize(self.btn_l, 20)) # 設定點擊事件
使用 class 写法,在主程序码里新增下面这段:
def changeSize(self, thisBtn, size):
self.btn_s.setDisabled(False) # 啟用「細」的按鈕
self.btn_m.setDisabled(False) # 啟用「中」的按鈕
self.btn_l.setDisabled(False) # 啟用「粗」的按鈕
self.penSize = size # 設定畫筆粗細
thisBtn.setDisabled(True) # 停用所點選的按鈕
加入视窗选单功能 ( 开新文件、另存新档 )
延续上述的程序,在主程序里加入下面的程序码,执行后在视窗的最上方,就会出现视窗选单 ( 参考“QMenuBar、QMenu、QAction 视窗选单” )。
# 開新檔案的函式
def newFile():
ret = mbox.question(MainWindow, 'question', '確定開新檔案?') # 出現對話視窗確認
if ret == mbox.StandardButton.Yes:
canvas.fill(QColor('#ffffff')) # 如果按下 yes,用白色填滿畫布
label.setPixmap(canvas) # 重新設定畫布
else:
return # 否則就不做動作,跳出函式
# 儲存檔案的函式
def saveFile():
filePath, filterType = QtWidgets.QFileDialog.getSaveFileName(MainWindow, '另存新檔', '', 'JPG(*.jpg)') # 建立開啟檔案對話視窗,設定成存檔方式
label.pixmap().save(filePath,'JPG',90) # 儲存為 jpg,品質 90
# 關閉的函式
def closeFile():
app.quit() # 結束視窗
mbox = QtWidgets.QMessageBox(MainWindow) # 建立對話視窗
menubar = QtWidgets.QMenuBar(MainWindow) # 建立 menubar
menu_file = QtWidgets.QMenu('檔案') # 建立一個 File 選項 ( QMenu )
action_new = QAction('開新檔案') # 建立一個 new 選項 ( QAction )
action_new.triggered.connect(newFile)
menu_file.addAction(action_new) # 將 new 選項放入 File 選項裡
action_save = QAction('另存新檔') # 建立一個 save 選項 ( QAction )
menu_file.addAction(action_save) # 將 save 選項放入 File 選項裡
action_save.triggered.connect(saveFile)
action_close = QAction('關閉') # 建立一個 close 選項 ( QAction )
menu_file.addAction(action_close) # 將 close 選項放入 File 選項裡
action_close.triggered.connect(closeFile)
menubar.addMenu(menu_file) # 將 File 選項放入 menubar 裡
class 的写法在 self.ui() 里加入下面这段程序码:
self.mbox = QtWidgets.QMessageBox(self) # 建立對話視窗
self.menubar = QtWidgets.QMenuBar(self) # 建立 menubar
self.menu_file = QtWidgets.QMenu('檔案') # 建立一個 File 選項 ( QMenu )
self.action_new = QAction('開新檔案') # 建立一個 new 選項 ( QAction )
self.action_new.triggered.connect(self.newFile)
self.menu_file.addAction(self.action_new) # 將 new 選項放入 File 選項裡
self.action_save = QAction('另存新檔') # 建立一個 save 選項 ( QAction )
self.menu_file.addAction(self.action_save) # 將 save 選項放入 File 選項裡
self.action_save.triggered.connect(self.saveFile)
self.action_close = QAction('關閉') # 建立一個 close 選項 ( QAction )
self.menu_file.addAction(self.action_close) # 將 close 選項放入 File 選項裡
self.action_close.triggered.connect(self.closeFile)
self.menubar.addMenu(self.menu_file) # 將 File 選項放入 menubar 裡
class 的写法在主程序里加入下面这段:
# 開新檔案的函式
def newFile(self):
ret = self.mbox.question(self, 'question', '確定開新檔案?') # 出現對話視窗確認
if ret == self.mbox.StandardButton.Yes:
self.canvas.fill(QColor('#ffffff')) # 如果按下 yes,用白色填滿畫布
self.label.setPixmap(self.canvas) # 重新設定畫布
else:
return # 否則就不做動作,跳出函式
# 儲存檔案的函式
def saveFile(self):
filePath, filterType = QtWidgets.QFileDialog.getSaveFileName(self, '另存新檔', '', 'JPG(*.jpg)') # 建立開啟檔案對話視窗,設定成存檔方式
self.label.pixmap().save(filePath,'JPG',90) # 儲存為 jpg,品質 90
# 關閉的函式
def closeFile(self):
app.quit() # 結束視窗
完整程序码
一般写法:
from PyQt6 import QtWidgets
from PyQt6.QtGui import *
from PyQt6.QtCore import *
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
MainWindow.setObjectName("MainWindow")
MainWindow.setWindowTitle("oxxo.studio")
MainWindow.resize(400, 300) # 主視窗大小
canvas = QPixmap(400,240) # 建立 QPixmap 元件作為畫布,並設定畫布大小
canvas.fill(QColor('#ffffff')) # 畫布填滿白色
label = QtWidgets.QLabel(MainWindow) # 建立 QLabel
label.setGeometry(0, 0, 400, 240) # 設定大小位置,下方留下一些空白
label.setPixmap(canvas) # 放入畫布
last_x, last_y = None, None # 設定兩個變數紀錄滑鼠座標
penSize = 10 # 畫筆預設粗細
penColor = QColor('#000000') # 畫筆預設顏色
# 放開滑鼠的函式
def release(self):
global last_x, last_y
last_x, last_y = None, None # 清空座標內容
# 按下滑鼠的函式
def mousePress(self):
global penColor, penSize, canvas
mx = int(QEnterEvent.position(self).x())
my = int(QEnterEvent.position(self).y())
qpainter = QPainter() # 建立 QPainter 元件
qpainter.begin(canvas) # 在畫布中開始繪圖
qpainter.setPen(QPen(QColor(penColor), penSize, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) # 設定畫筆樣式
qpainter.drawPoint(mx, my) # 下筆畫出一個點
qpainter.end() # 結束繪圖
label.setPixmap(canvas)
MainWindow.update() # 更新主視窗內容
# 按下滑鼠並移動滑鼠的函式
def draw(self):
global last_x, last_y, penColor, penSize, canvas
mx = int(QEnterEvent.position(self).x())
my = int(QEnterEvent.position(self).y())
if last_x is None:
last_x = mx # 紀錄滑鼠當下的座標
last_y = my
return
qpainter = QPainter() # 建立 QPainter 元件
qpainter.begin(canvas) # 在畫布中開始繪圖
qpainter.setPen(QPen(penColor, penSize, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) # 設定畫筆樣式
qpainter.drawLine(last_x, last_y, mx, my) # 下筆畫出一條線
qpainter.end() # 結束繪圖
label.setPixmap(canvas)
MainWindow.update() # 更新主視窗內容
last_x = mx # 紀錄結束座標
last_y = my
label.mousePressEvent = mousePress # 設定按下滑鼠並移動的事件
label.mouseMoveEvent = draw # 設定按下滑鼠的事件
label.mouseReleaseEvent = release # 設定放開滑鼠的事件
# 點擊按鈕更換顏色函式
def changeColor(self, color):
global penColor, btn
penColor = QColor(color) # 設定畫筆顏色
for i in btn:
btn[i].setDisabled(False) # 啟用所有按鈕
self.setDisabled(True) # 停用所點擊的按鈕
# 設定顏色清單
colors = ['#ff0000','#ff8800','#ffee00','#00cc00','#0066ff','#0000cc','#cc00cc','#000000','#ffffff']
btn = {} # 因為有很多按鈕,所以使用字典方式紀錄元件
# 依序讀取顏色清單中的顏色
for i in colors:
index = colors.index(i) # 取得該顏色的位置 ( 按鈕定位使用 )
btn[i] = QtWidgets.QPushButton(MainWindow) # 建立按鈕元件
# 設定樣式,當中額外設定禁用時的樣式
btn[i].setStyleSheet('''
QPushButton{
background:'''+i+''';
margin-right:5px;
}
QPushButton:disabled{
border:3px solid #000;
}
''')
btn[i].setGeometry(index*30+10,250,30,30) # 設定每個按鈕的位置
btn[i].clicked.connect(lambda checked, b=btn[i], c=i: changeColor(b, c)) # 設定點擊事件
# 切換畫筆粗細函式
def changeSize(self, size):
global penSize
btn_s.setDisabled(False) # 啟用「細」的按鈕
btn_m.setDisabled(False) # 啟用「中」的按鈕
btn_l.setDisabled(False) # 啟用「粗」的按鈕
penSize = size # 設定畫筆粗細
self.setDisabled(True) # 停用所點選的按鈕
btn_s = QtWidgets.QPushButton(MainWindow) # 建立「細」的按鈕
btn_s.setText('細')
btn_s.setGeometry(280, 250, 45, 30) # 設定位置
btn_s.clicked.connect(lambda: changeSize(btn_s, 3)) # 設定點擊事件
btn_m = QtWidgets.QPushButton(MainWindow) # 建立「中」的按鈕
btn_m.setText('中')
btn_m.setGeometry(315, 250, 45, 30) # 設定位置
btn_m.setDisabled(True) # 因為預設中,所以先停用中的按鈕
btn_m.clicked.connect(lambda: changeSize(btn_m, 10)) # 設定點擊事件
btn_l = QtWidgets.QPushButton(MainWindow) # 建立「粗」的按鈕
btn_l.setText('粗')
btn_l.setGeometry(350, 250, 45, 30) # 設定位置
btn_l.clicked.connect(lambda: changeSize(btn_l, 20)) # 設定點擊事件
# 開新檔案的函式
def newFile():
ret = mbox.question(MainWindow, 'question', '確定開新檔案?') # 出現對話視窗確認
if ret == mbox.StandardButton.Yes:
canvas.fill(QColor('#ffffff')) # 如果按下 yes,用白色填滿畫布
label.setPixmap(canvas) # 重新設定畫布
else:
return # 否則就不做動作,跳出函式
# 儲存檔案的函式
def saveFile():
filePath, filterType = QtWidgets.QFileDialog.getSaveFileName(MainWindow, '另存新檔', '', 'JPG(*.jpg)') # 建立開啟檔案對話視窗,設定成存檔方式
label.pixmap().save(filePath,'JPG',90) # 儲存為 jpg,品質 90
# 關閉的函式
def closeFile():
app.quit() # 結束視窗
mbox = QtWidgets.QMessageBox(MainWindow) # 建立對話視窗
menubar = QtWidgets.QMenuBar(MainWindow) # 建立 menubar
menu_file = QtWidgets.QMenu('檔案') # 建立一個 File 選項 ( QMenu )
action_new = QAction('開新檔案') # 建立一個 new 選項 ( QAction )
action_new.triggered.connect(newFile)
menu_file.addAction(action_new) # 將 new 選項放入 File 選項裡
action_save = QAction('另存新檔') # 建立一個 save 選項 ( QAction )
menu_file.addAction(action_save) # 將 save 選項放入 File 選項裡
action_save.triggered.connect(saveFile)
action_close = QAction('關閉') # 建立一個 close 選項 ( QAction )
menu_file.addAction(action_close) # 將 close 選項放入 File 選項裡
action_close.triggered.connect(closeFile)
menubar.addMenu(menu_file) # 將 File 選項放入 menubar 裡
MainWindow.show()
sys.exit(app.exec())
使用 class 的写法:
from PyQt6 import QtWidgets
from PyQt6.QtGui import *
from PyQt6.QtCore import *
import sys
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('oxxo.studio')
self.resize(400, 300)
self.setUpdatesEnabled(True)
self.last_x, self.last_y = None, None # 設定兩個變數紀錄滑鼠座標
self.penSize = 10 # 畫筆預設粗細
self.penColor = QColor('#000000') # 畫筆預設顏色
self.ui()
def ui(self):
self.canvas = QPixmap(400,240) # 建立 QPixmap 元件作為畫布,並設定畫布大小
self.canvas.fill(QColor('#ffffff')) # 畫布填滿白色
self.label = QtWidgets.QLabel(self) # 建立 QLabel
self.label.setGeometry(0, 0, 400, 240) # 設定大小位置,下方留下一些空白
self.label.setPixmap(self.canvas) # 放入畫布
# 設定顏色清單
self.colors = ['#ff0000','#ff8800','#ffee00','#00cc00','#0066ff','#0000cc','#cc00cc','#000000','#ffffff']
self.btn = {} # 因為有很多按鈕,所以使用字典方式紀錄元件
# 依序讀取顏色清單中的顏色
for i in self.colors:
index = self.colors.index(i) # 取得該顏色的位置 ( 按鈕定位使用 )
self.btn[i] = QtWidgets.QPushButton(self) # 建立按鈕元件
# 設定樣式,當中額外設定禁用時的樣式
self.btn[i].setStyleSheet('''
QPushButton{
background:'''+i+''';
margin-right:5px;
}
QPushButton:disabled{
border:3px solid #000;
}
''')
self.btn[i].setGeometry(index*30+10,250,30,30) # 設定每個按鈕的位置
self.btn[i].clicked.connect(lambda checked, b=self.btn[i], c=i: self.changeColor(b, c)) # 設定點擊事件
self.btn_s = QtWidgets.QPushButton(self) # 建立「細」的按鈕
self.btn_s.setText('細')
self.btn_s.setGeometry(280, 250, 45, 30) # 設定位置
self.btn_s.clicked.connect(lambda: self.changeSize(self.btn_s, 3)) # 設定點擊事件
self.btn_m = QtWidgets.QPushButton(self) # 建立「中」的按鈕
self.btn_m.setText('中')
self.btn_m.setGeometry(315, 250, 45, 30) # 設定位置
self.btn_m.setDisabled(True) # 因為預設中,所以先停用中的按鈕
self.btn_m.clicked.connect(lambda: self.changeSize(self.btn_m, 10)) # 設定點擊事件
self.btn_l = QtWidgets.QPushButton(self) # 建立「粗」的按鈕
self.btn_l.setText('粗')
self.btn_l.setGeometry(350, 250, 45, 30) # 設定位置
self.btn_l.clicked.connect(lambda: self.changeSize(self.btn_l, 20)) # 設定點擊事件
self.mbox = QtWidgets.QMessageBox(self) # 建立對話視窗
self.menubar = QtWidgets.QMenuBar(self) # 建立 menubar
self.menu_file = QtWidgets.QMenu('檔案') # 建立一個 File 選項 ( QMenu )
self.action_new = QAction('開新檔案') # 建立一個 new 選項 ( QAction )
self.action_new.triggered.connect(self.newFile)
self.menu_file.addAction(self.action_new) # 將 new 選項放入 File 選項裡
self.action_save = QAction('另存新檔') # 建立一個 save 選項 ( QAction )
self.menu_file.addAction(self.action_save) # 將 save 選項放入 File 選項裡
self.action_save.triggered.connect(self.saveFile)
self.action_close = QAction('關閉') # 建立一個 close 選項 ( QAction )
self.menu_file.addAction(self.action_close) # 將 close 選項放入 File 選項裡
self.action_close.triggered.connect(self.closeFile)
self.menubar.addMenu(self.menu_file) # 將 File 選項放入 menubar 裡
def mousePressEvent(self, event):
mx = int(QEnterEvent.position(event).x())
my = int(QEnterEvent.position(event).y())
qpainter = QPainter() # 建立 QPainter 元件
qpainter.begin(self.canvas) # 在畫布中開始繪圖
qpainter.setPen(QPen(QColor(self.penColor), self.penSize, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) # 設定畫筆樣式
qpainter.drawPoint(mx, my) # 下筆畫出一個點
qpainter.end() # 結束繪圖
self.label.setPixmap(self.canvas)
self.update() # 更新主視窗內容
def mouseMoveEvent(self, event):
mx = int(QEnterEvent.position(event).x())
my = int(QEnterEvent.position(event).y())
if self.last_x is None:
self.last_x = mx # 紀錄滑鼠當下的座標
self.last_y = my
return
qpainter = QPainter() # 建立 QPainter 元件
qpainter.begin(self.canvas) # 在畫布中開始繪圖
qpainter.setPen(QPen(self.penColor, self.penSize, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) # 設定畫筆樣式
qpainter.drawLine(self.last_x, self.last_y, mx, my) # 下筆畫出一條線
qpainter.end() # 結束繪圖
self.label.setPixmap(self.canvas)
self.update() # 更新主視窗內容
self.last_x = mx # 紀錄結束座標
self.last_y = my
def mouseReleaseEvent(self, event):
self.last_x, self.last_y = None, None # 清空座標內容
# 點擊按鈕更換顏色函式
def changeColor(self, thisBtn, color):
self.penColor = QColor(color) # 設定畫筆顏色
for i in self.btn:
self.btn[i].setDisabled(False) # 啟用所有按鈕
thisBtn.setDisabled(True) # 停用所點擊的按鈕
def changeSize(self, thisBtn, size):
self.btn_s.setDisabled(False) # 啟用「細」的按鈕
self.btn_m.setDisabled(False) # 啟用「中」的按鈕
self.btn_l.setDisabled(False) # 啟用「粗」的按鈕
self.penSize = size # 設定畫筆粗細
thisBtn.setDisabled(True) # 停用所點選的按鈕
# 開新檔案的函式
def newFile(self):
ret = self.mbox.question(self, 'question', '確定開新檔案?') # 出現對話視窗確認
if ret == self.mbox.StandardButton.Yes:
self.canvas.fill(QColor('#ffffff')) # 如果按下 yes,用白色填滿畫布
self.label.setPixmap(self.canvas) # 重新設定畫布
else:
return # 否則就不做動作,跳出函式
# 儲存檔案的函式
def saveFile(self):
filePath, filterType = QtWidgets.QFileDialog.getSaveFileName(self, '另存新檔', '', 'JPG(*.jpg)') # 建立開啟檔案對話視窗,設定成存檔方式
self.label.pixmap().save(filePath,'JPG',90) # 儲存為 jpg,品質 90
# 關閉的函式
def closeFile(self):
app.quit() # 結束視窗
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
Form = MyWidget()
Form.show()
sys.exit(app.exec())
微信扫码关注
抖音扫码关注