python 手写板程序

(1)function.py
#此文件是一些函数 有加载数据模块
import datetime
import struct
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import os
 
 
def jiexi_image(path):
    # 用二进制读取
    data = open(path, 'rb').read()
    offset = 0
    fmt_header = '>iiii'
    magic_number, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, data, offset)
    print('魔数:%d, 图片数量: %d张, 图片大小: %d*%d' % (magic_number, num_images, num_rows, num_cols))
    image_size = num_rows * num_cols
    offset += struct.calcsize(fmt_header)
    fmt_image = '>' + str(image_size) + 'B'
    images = np.empty((num_images, num_rows, num_cols))
    for i in range(num_images):
        if (i + 1) % 10000 == 0:
            print('已解析 %d' % (i + 1) + '张')
        images[i] = np.array(struct.unpack_from(fmt_image, data, offset)).reshape((num_rows, num_cols))
        offset += struct.calcsize(fmt_image)
    return images
 
def jiexi_label(path):
    data = open(path, 'rb').read()
    offset = 0
    fmt_header = '>ii'
    magic_number, num_images = struct.unpack_from(fmt_header, data, offset)
    print('魔数:%d, 图片数量: %d张' % (magic_number, num_images))
    # 解析数据集
    offset += struct.calcsize(fmt_header)
    fmt_image = '>B'
    labels = np.empty(num_images)
    for i in range(num_images):
        if (i + 1) % 10000 == 0:
            print('已解析 %d' % (i + 1) + '张')
        labels[i] = struct.unpack_from(fmt_image, data, offset)[0]
        offset += struct.calcsize(fmt_image)
    return labels
 
def plot_data(images,labels,n,issave = False):
    for i in range(n):
        print(labels[i])
        plt.imshow(images[i], cmap='gray')
        plt.show()
        # if(issave == True):
            # plt.savefig(fname = "save"+str(datetime.datetime.now())+".jpg")
 
    print('done')
 
 
 
 
 
## 说明:输入原始图像路径和新建图像文件夹名称 默认修改出长度宽度为64*64
def stdimage(pathorg, name, pathnew=None, width=64, length=64):
    # 检查文件是否建立
    if pathnew == None:  # 如果没有手动创建
        tage = os.path.exists(os.getcwd() + '\\' + name)  # 检查一下是否属实
        if not tage:  # 没有整个新文件夹
            os.mkdir(os.getcwd() + "\\" + name)  # 创建文件夹,name
        image_path = os.getcwd() + "\\" + name + "\\"
    else:  # 已经手动创建
        tage = os.path.exists(pathnew + "\\" + name)
        if not tage:
            path = os.getcwd()
            os.mkdir(path + "\\" + name)
        image_path = path + "\\" + name + "\\"
 
    ## 开始处理
    i = 1  # 从一开始
    list_name = os.listdir(pathorg)  # 获取图片名称列表
    for item in list_name:
        # 检查是否有图片
        tage = os.path.exists(pathorg + str(i) + '.png')
        if not tage:
            image = Image.open(pathorg + '\\' + item)
            std = image.resize((width, length), Image.ANTIALIAS)
            ## 模式为RGB
            if not std.mode == "RGB":
                std = std.convert('RGB')
            std.save(image_path + str(i) + '.png')
        i += 1
 
 
 
 
def label_init(lable):
    n = lable.shape[0]
    label_Y = np.zeros([10, n])
    res = lable.astype(int)
    for i in range(0, label_Y.shape[1]):
        label_Y[res[i], i] = 1
    return label_Y
 
 
 
 
def get_X(path):
    im_name_list = os.listdir(path)
    all_data = []
    for item in im_name_list:
        try:
            all_data.append(plt.imread(path + '\\' + item).tolist())
        except:
            print(item + " open error ")
    return all_data
 


(2)DeepNet.py
 
import matplotlib.pyplot as plt
import numpy as np
import datetime
import os
import sys
import h5py
 
# sigmoid
def sigmoid(Z):
    A = 1/(1+np.exp(-Z))
    cache = Z
    return A, cache
 
def sigmoid_backward(dA, cache):
    Z = cache
    s = 1/(1+np.exp(-Z))
    dZ = dA * s * (1-s)
    return dZ
 
 
# relu函数 和反向求导
def relu(Z):
    A = np.maximum(0,Z)
    cache = Z
    return A, cache
 
def relu_backward(dA, cache):
    Z = cache
    dZ = np.array(dA, copy=True)
    dZ[Z <= 0] = 0
    return dZ
 
 
# Softmax
def softmax(Z):
    A = np.exp(Z)/np.sum(np.exp(Z),axis=0)
    cache = Z
    return A, cache
# 初始化w
# def INIT_W(n_x,n_h1,n_h2,n_y):
#     W1 = np.random.randn(n_h1, n_x ) * 0.01
#     b1 = np.zeros((n_h1, 1))
#     W2 = np.random.randn(n_h2,n_h1)*0.01
#     b2 = np.zeros((n_h2,1))
#     W3 = np.random.randn(n_y, n_h2) * 0.01
#     b3 = np.zeros((n_y, 1))
#     INIT = {
#         "W1" : W1,
#         "b1" : b1,
#         "W2" : W2,
#         "b2" : b2,
#         "W3" : W3,
#         "b3" : b3
#     }
#     return INIT
 
 
def init_W(layers_dims):
    np.random.seed(3)
    parameters = {}
    L = len(layers_dims)
    for l in range(1, L):
        parameters["W" + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) / np.sqrt(layers_dims[l - 1])
        parameters["b" + str(l)] = np.zeros((layers_dims[l], 1))
    return parameters
 
 
 
# 向前
def L_forword_sum(W,A,b):
    Z = np.dot(W,A)+b
    cache = (A,W,b)
    return Z,cache
 
def L_activate_forworld(A_prev,W,b,activation):
    if activation == "relu":
        Z ,linear_cache =  L_forword_sum(W,A_prev,b)
        A, activation_cache = relu(Z)
    elif activation == "sigmoid":
        Z, linear_cache = L_forword_sum(W, A_prev, b)
        A, activation_cache = sigmoid(Z)
    elif activation == "softmax":
        Z, linear_cache = L_forword_sum(W, A_prev, b)
        A, activation_cache = softmax(Z)
    cache = (linear_cache, activation_cache)
    return A,cache
 
 
def L_forword(X, parameters):
    caches = []
    A = X
    L = len(parameters) // 2
    for l in range(1, L):
        A_prev = A
        A, cache = L_activate_forworld(A_prev, parameters['W' + str(l)], parameters['b' + str(l)], "relu")
        caches.append(cache)
    # 最后一层使用softmax
    AL, cache  = L_activate_forworld(A, parameters['W' + str(L)], parameters['b' + str(L)], "softmax")
    caches.append(cache)
    return AL, caches
 
#计算代价
def cost(Y_out,Y):
    cost = -np.sum(np.multiply(np.log(Y_out), Y)) / Y_out.shape[1]
    cost = np.squeeze(cost)
    return cost
 
#线性返回
def linear_backward(dZ, cache):
    A_prev, W, b = cache
    m = A_prev.shape[1]
    dW = np.dot(dZ, A_prev.T) / m
    db = np.sum(dZ, axis=1, keepdims=True) / m
    dA_prev = np.dot(W.T, dZ)
    return dA_prev, dW, db
 
 
def linear_activation_backward(dA, cache, Y,activation="relu"):
    linear_cache, activation_cache = cache
    if activation == "relu":
        dZ = relu_backward(dA, activation_cache)
        dA_prev, dW, db = linear_backward(dZ, linear_cache)
    elif activation == "sigmoid":
        dZ = sigmoid_backward(dA, activation_cache)
        dA_prev, dW, db = linear_backward(dZ, linear_cache)
    elif activation == "softmax":
        dZ = dA - Y
        dA_prev, dW, db = linear_backward(dZ, linear_cache)
    return dA_prev, dW, db
 
 
def L_model_backward(AL, Y, caches,case):
    grads = {}
    L = len(caches)
    m = AL.shape[1]
    Y = Y.reshape(AL.shape)
    dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
    if case == "softmax":
        current_cache = caches[L - 1]
        grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(AL, current_cache,Y,"softmax")
 
    elif case  == "sigmoid":
        current_cache = caches[L - 1]
        grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(AL, current_cache,Y, "sigmoid")
 
    for l in reversed(range(L - 1)):
        current_cache = caches[l]
        dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 2)], current_cache, Y ,"relu")
        grads["dA" + str(l + 1)] = dA_prev_temp
        grads["dW" + str(l + 1)] = dW_temp
        grads["db" + str(l + 1)] = db_temp
    return grads
 
 
def update_parameters(parameters, grads, learning_rate):
    L = len(parameters) // 2  # 整除
    for l in range(L):
        parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
        parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]
    return parameters
 
 
 
 
 
 
def deepnet(X, Y,net_layers,learning_rate=0.0075, num_iterations=3000,step =1, print_cost=False, isPlot=True):
    np.random.seed(1) #设计种子
    costs = [] #用于画图
    parameters = init_W(net_layers)
    for i in range(0, num_iterations):
        # 迭代
        AL, caches = L_forword(X, parameters)
 
        costi = cost(AL, Y) #这里的Y是标准化的Y
 
        grads = L_model_backward(AL, Y, caches,"softmax")
 
        parameters = update_parameters(parameters, grads, learning_rate)
 
        if i % step == 0:
            # 记录成本
            costs.append(costi)
            # 是否打印成本值
            if print_cost:
                print("第", i, "次迭代,成本值为:", np.squeeze(costi))
 
 
    if isPlot:
        plt.plot(np.squeeze(costs))
        plt.ylabel('cost')
        plt.xlabel('iterations (per tens)')
        plt.title("Learning rate =" + str(learning_rate))
        plt.show()
        # plt.savefig(fnme = "cast"+str(datetime.datetime.now())+".jig")
    return parameters
 
 
def predict(X, y, parameters,Y_org):
 
    m = X.shape[1]
    n = len(parameters) // 2  # 神经网络的层数
    p = np.zeros((1, m))
 
    # 根据参数前向传播
    probas, caches = L_forword(X, parameters)
    p = np.argmax(probas,axis=0)
    zql = float(np.sum((p == Y_org)) / m)
    print("准确度为: " + str(float(np.sum((p == Y_org)) / m)))
 
    error_list = []
    for i in range(m):
        if p[i] != Y_org[i]:
            error_list.append(i)
    return p,error_list,zql
 
 
def save_model(parameters):
    np.set_printoptions(threshold=sys.maxsize)
 
    model_number = 0
    f = open("model/model" + str(model_number) + ".txt", "a+")
    f.write(str(datetime.datetime.now()) + "\n")
    f.write("model_number " + str(model_number) + "\n")
    for i, j in parameters.items():
        f.write(str(i) + "\n")
        f.write(str(j) + "\n")
    f.close()
    return 0
 
#保存为h5数据格式
def save_h5(data,layers,zql):
    str1 = "./model/model"+str(datetime.datetime.now().strftime("%Y%m%d%H%M%S"))+".h5"
    f = h5py.File(str1, "w")
    ID = ["model layer "]
    f.create_dataset("layers",data = layers)
    i = len(data) // 2
    for j in range(i):
        f.create_dataset("W"+str(j+1),data = data["W"+str(j+1)])
        f.create_dataset("b"+str(j+1),data = data["b"+str(j+1)])
    f.create_dataset("accuracy",data = zql)
    f.close()
 
 
 
 
 
def predict1(X, parameters):
    # 根据参数前向传播
    probas, caches = L_forword(X, parameters)
    p = np.argmax(probas,axis=0)
    return p
 
def read_ccs(path):
    w = h5py.File(path, "r")
    layers = w["layers"][:]
    l = len(layers)
    p = {}
    # print(l)
    for i in range(1, l):
        p["W" + str(i)] = w["W" + str(i)][:]
        p["b" + str(i)] = w["b" + str(i)][:]
    return p, layers


(3)train.py
 
import DeepNET
import time
 
import function
 
 
 
if __name__ == '__main__':
    # 计时开始
    start1 = time.time()
    train_image_path = './MNIST/train-images-idx3-ubyte/train-images.idx3-ubyte'
    train_lable_path = './MNIST/train-labels-idx1-ubyte/train-labels.idx1-ubyte'
    teat_image_path = './MNIST/t10k-images-idx3-ubyte/t10k-images.idx3-ubyte'
    teat_lable_path = './MNIST/t10k-labels-idx1-ubyte/t10k-labels.idx1-ubyte'
    # #加载数据
    train_image = function.jiexi_image(train_image_path)
    train_lable = function.jiexi_label(train_lable_path)
    teat_image = function.jiexi_image(teat_image_path)
    test_lable = function.jiexi_label(teat_lable_path)
    # print(train_image.shape)
 
    function.plot_data(train_image,train_lable,10,True)
 
    train_image = train_image.reshape(train_image.shape[0], -1).T / 255
    teat_image = teat_image.reshape(teat_image.shape[0], -1).T / 255
    train_lable1 = function.label_init(train_lable)
    test_lable1 =  function.label_init(test_lable)
 
    print(train_image.shape)
 
    end1 = time.time()
 
    start2 = time.time()
    layers = [784, 200, 150, 10]
    parameters = DeepNET.deepnet(train_image, train_lable1,layers , learning_rate=0.0075, num_iterations=3000,
                                 step=100, print_cost=True, isPlot=True)
    end2 = time.time()
 
    p ,error_list_train,zql1 = DeepNET.predict(train_image, train_lable1, parameters, train_lable)
    p0 ,error_list_test ,zql2 = DeepNET.predict(teat_image,test_lable1,parameters,test_lable)
    zql = [[zql1],[zql2]]
    print("数据加载时间:",end1-start1," 秒")
    print("模型训练时间:",end2-start2," 秒")
 
    DeepNET.save_h5(parameters,layers,zql)


(4) main.py
 
# 加载库
from MainWidget import MainWidget
from PyQt5.QtWidgets import QApplication
import sys
 
def main():
    app = QApplication(sys.argv)
 
    mainWidget = MainWidget()  # 新建一个主界面
    mainWidget.show()  # 显示主界面
 
    exit(app.exec_())  # 进入消息循环
 
 
if __name__ == '__main__':
    main()
 
 
 
 (5)mainwidget.py

使用画板程序之前得跟该你的模型路径名字。就是红色部分。

def yuce(self):
        # #标准化图片 获取Y
        savePath = "./image_rgzn/test.png"
        image = self.__paintBoard.GetContentAsQImage()
        image.save(savePath)
        img = Image.open(savePath)
        img = img.convert("I")
        img = img.resize((28, 28))
        x = np.array(img)
        train_image = x.reshape(1, -1).T / 255
        w,layer = DeepNET.read_ccs("./model/model20221119225104.h5")
        p = DeepNET.predict1(train_image,w)
        self.__text_out.setText(str(p[0]))
        print(p)
        # print("hello")
        # res = QMessageBox.information(self,"人工智能判断为:",str(p),QMessageBox.Yes|QMessageBox.No)
        # res.exec_()
        # 读取数据权重

        # 预测并输出
'''
Created on 2018年8月8日
@author: Freedom
'''
from PyQt5.Qt import QWidget, QColor, QPixmap, QIcon, QSize, QCheckBox
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QPushButton, QSplitter, \
    QComboBox, QLabel, QSpinBox, QFileDialog,QTextEdit
from PaintBoard import PaintBoard
import numpy as np
from PIL import Image
import DeepNET
 
 
class MainWidget(QWidget):
 
    def __init__(self, Parent=None):
        '''
        Constructor
        '''
        super().__init__(Parent)
 
        self.__InitData()  # 先初始化数据,再初始化界面
        self.__InitView()
 
    def __InitData(self):
        '''
                  初始化成员变量
        '''
        self.__paintBoard = PaintBoard(self)
        # 获取颜色列表(字符串类型)
        self.__colorList = QColor.colorNames()
 
    def __InitView(self):
        '''
                  初始化界面
        '''
        self.setFixedSize(640, 480)
        self.setWindowTitle("PaintBoard Example PyQt5")
 
        # 新建一个水平布局作为本窗体的主布局
        main_layout = QHBoxLayout(self)
        # 设置主布局内边距以及控件间距为10px
        main_layout.setSpacing(10)
 
        # 在主界面左侧放置画板
        main_layout.addWidget(self.__paintBoard)
 
        # 新建垂直子布局用于放置按键
        sub_layout = QVBoxLayout()
 
        # 设置此子布局和内部控件的间距为10px
        sub_layout.setContentsMargins(10, 10, 10, 10)
 
        self.__btn_Clear = QPushButton("清空画板")
        self.__btn_Clear.setParent(self)  # 设置父对象为本界面
        # 将按键按下信号与画板清空函数相关联
        self.__btn_Clear.clicked.connect(self.__paintBoard.Clear)
        sub_layout.addWidget(self.__btn_Clear)
 
        self.__btn_yuce = QPushButton("人工智能预测")
        self.__btn_yuce.setParent(self)  # 设置父对象为本界面
        self.__btn_yuce.clicked.connect(lambda:self.yuce())
        sub_layout.addWidget(self.__btn_yuce)
 
 
        self.__text_out = QTextEdit(self)
        self.__text_out.setParent(self)
        self.__text_out.setObjectName("预测结果为:")
        sub_layout.addWidget(self.__text_out)
 
 
        self.__btn_Quit = QPushButton("退出")
        self.__btn_Quit.setParent(self)  # 设置父对象为本界面
        self.__btn_Quit.clicked.connect(self.Quit)
        sub_layout.addWidget(self.__btn_Quit)
 
        self.__btn_Save = QPushButton("保存作品")
        self.__btn_Save.setParent(self)
        self.__btn_Save.clicked.connect(self.on_btn_Save_Clicked)
        sub_layout.addWidget(self.__btn_Save)
 
        self.__cbtn_Eraser = QCheckBox("  使用橡皮擦")
        self.__cbtn_Eraser.setParent(self)
        self.__cbtn_Eraser.clicked.connect(self.on_cbtn_Eraser_clicked)
        sub_layout.addWidget(self.__cbtn_Eraser)
 
        splitter = QSplitter(self)  # 占位符
        sub_layout.addWidget(splitter)
 
        self.__label_penThickness = QLabel(self)
        self.__label_penThickness.setText("画笔粗细")
        self.__label_penThickness.setFixedHeight(20)
        sub_layout.addWidget(self.__label_penThickness)
 
        self.__spinBox_penThickness = QSpinBox(self)
        self.__spinBox_penThickness.setMaximum(40)
        self.__spinBox_penThickness.setMinimum(2)
        self.__spinBox_penThickness.setValue(20)  # 默认粗细为10
        self.__spinBox_penThickness.setSingleStep(2)  # 最小变化值为2
        self.__spinBox_penThickness.valueChanged.connect(
            self.on_PenThicknessChange)  # 关联spinBox值变化信号和函数on_PenThicknessChange
        sub_layout.addWidget(self.__spinBox_penThickness)
 
        self.__label_penColor = QLabel(self)
        self.__label_penColor.setText("画笔颜色")
        self.__label_penColor.setFixedHeight(20)
        sub_layout.addWidget(self.__label_penColor)
 
        self.__comboBox_penColor = QComboBox(self)
        self.__fillColorList(self.__comboBox_penColor)  # 用各种颜色填充下拉列表
        self.__comboBox_penColor.currentIndexChanged.connect(
            self.on_PenColorChange)  # 关联下拉列表的当前索引变更信号与函数on_PenColorChange
        sub_layout.addWidget(self.__comboBox_penColor)
 
        main_layout.addLayout(sub_layout)  # 将子布局加入主布局
 
    def __fillColorList(self, comboBox):
 
        index_black = 0
        index = 0
        for color in self.__colorList:
            if color == "black":
                index_black = index
            index += 1
            pix = QPixmap(70, 20)
            pix.fill(QColor(color))
            comboBox.addItem(QIcon(pix), None)
            comboBox.setIconSize(QSize(70, 20))
            comboBox.setSizeAdjustPolicy(QComboBox.AdjustToContents)
 
        comboBox.setCurrentIndex(index_black)
 
    def on_PenColorChange(self):
        color_index = self.__comboBox_penColor.currentIndex()
        color_str = self.__colorList[color_index]
        self.__paintBoard.ChangePenColor(color_str)
 
    def on_PenThicknessChange(self):
        penThickness = self.__spinBox_penThickness.value()
        self.__paintBoard.ChangePenThickness(penThickness)
 
    def on_btn_Save_Clicked(self):
        savePath = QFileDialog.getSaveFileName(self, 'Save Your Paint', '.\\', '*.png')
        print(savePath)
        if savePath[0] == "":
            print("Save cancel")
            return
        image = self.__paintBoard.GetContentAsQImage()
        image.save(savePath[0])
 
    def on_cbtn_Eraser_clicked(self):
        if self.__cbtn_Eraser.isChecked():
            self.__paintBoard.EraserMode = True  # 进入橡皮擦模式
        else:
            self.__paintBoard.EraserMode = False  # 退出橡皮擦模式
 
    def Quit(self):
        self.close()
 
 
    def yuce(self):
        # #标准化图片 获取Y
        savePath = "./image_rgzn/test.png"
        image = self.__paintBoard.GetContentAsQImage()
        image.save(savePath)
        img = Image.open(savePath)
        img = img.convert("I")
        img = img.resize((28, 28))
        x = np.array(img)
        train_image = x.reshape(1, -1).T / 255
        w,layer = DeepNET.read_ccs("./model/model20221119225104.h5")
        p = DeepNET.predict1(train_image,w)
        self.__text_out.setText(str(p[0]))
        print(p)
        # print("hello")
        # res = QMessageBox.information(self,"人工智能判断为:",str(p),QMessageBox.Yes|QMessageBox.No)
        # res.exec_()
        # 读取数据权重
 
        # 预测并输出

(6)paintboard.py
'''
Created on 2018年8月9日
@author: Freedom
'''
from PyQt5.QtWidgets import QWidget
from PyQt5.Qt import QPixmap, QPainter, QPoint, QPaintEvent, QMouseEvent, QPen, \
    QColor, QSize
from PyQt5.QtCore import Qt
 
 
class PaintBoard(QWidget):
 
    def __init__(self, Parent=None):
        '''
        Constructor
        '''
        super().__init__(Parent)
 
        self.__InitData()  # 先初始化数据,再初始化界面
        self.__InitView()
 
    def __InitData(self):
 
        self.__size = QSize(280, 280)
 
        # 新建QPixmap作为画板,尺寸为__size
        self.__board = QPixmap(self.__size)
        self.__board.fill(Qt.black)  # 用白色填充画板
 
        self.__IsEmpty = True  # 默认为空画板
        self.EraserMode = False  # 默认为禁用橡皮擦模式
 
        self.__lastPos = QPoint(0, 0)  # 上一次鼠标位置
        self.__currentPos = QPoint(0, 0)  # 当前的鼠标位置
 
        self.__painter = QPainter()  # 新建绘图工具
 
        self.__thickness = 10  # 默认画笔粗细为10px
        self.__penColor = QColor("white")  # 设置默认画笔颜色为黑色
        self.__colorList = QColor.colorNames()  # 获取颜色列表
 
    def __InitView(self):
        # 设置界面的尺寸为__size
        self.setFixedSize(self.__size)
 
    def Clear(self):
        # 清空画板
        self.__board.fill(Qt.black)
        self.update()
        self.__IsEmpty = True
 
    def ChangePenColor(self, color="black"):
        # 改变画笔颜色
        self.__penColor = QColor(color)
 
    def ChangePenThickness(self, thickness=10):
        # 改变画笔粗细
        self.__thickness = thickness
 
    def IsEmpty(self):
        # 返回画板是否为空
        return self.__IsEmpty
 
    def GetContentAsQImage(self):
        # 获取画板内容(返回QImage)
        image = self.__board.toImage()
        return image
 
    def paintEvent(self, paintEvent):
        # 绘图事件
        # 绘图时必须使用QPainter的实例,此处为__painter
        # 绘图在begin()函数与end()函数间进行
        # begin(param)的参数要指定绘图设备,即把图画在哪里
        # drawPixmap用于绘制QPixmap类型的对象
        self.__painter.begin(self)
        # 0,0为绘图的左上角起点的坐标,__board即要绘制的图
        self.__painter.drawPixmap(0, 0, self.__board)
        self.__painter.end()
 
    def mousePressEvent(self, mouseEvent):
        # 鼠标按下时,获取鼠标的当前位置保存为上一次位置
        self.__currentPos = mouseEvent.pos()
        self.__lastPos = self.__currentPos
 
    def mouseMoveEvent(self, mouseEvent):
        # 鼠标移动时,更新当前位置,并在上一个位置和当前位置间画线
        self.__currentPos = mouseEvent.pos()
        self.__painter.begin(self.__board)
 
        if self.EraserMode == False:
            # 非橡皮擦模式
            self.__painter.setPen(QPen(self.__penColor, self.__thickness))  # 设置画笔颜色,粗细
        else:
            # 橡皮擦模式下画笔为纯白色,粗细为10
            self.__painter.setPen(QPen(Qt.white, 10))
 
        # 画线
        self.__painter.drawLine(self.__lastPos, self.__currentPos)
        self.__painter.end()
        self.__lastPos = self.__currentPos
 
        self.update()  # 更新显示
 
    def mouseReleaseEvent(self, mouseEvent):
        self.__IsEmpty = False  # 画板不再为空
 
 
 
 
 
 
 
 

  • 27
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值