(十五)视频换脸、无训练高速换脸、一张图片即可完成、批量处理

(十五)视频换脸、无训练高速换脸、一张图片即可完成、批量处理

本文的代码的功能是:可以对人物视频进行换脸操作,不用预先耗时训练模型,效率极高;
可进行批量视频处理,使用了人工智能的算法。

本文与前几篇博文关联性较强,请事先阅读前几篇。 对此文感兴趣的可以加微深入探讨:herbert156
可运行的试用版本下载:https://pan.baidu.com/s/1UOLa06iVMD5PNjjlGYg6lg
提取码: b2wk

如果提示过期,可以向博主索要新的SN文件。

一、主要功能:
以下的Python代码的功能:批量选择视频、批量处理,主要包括:
1、对视频进行换脸操作,并输出变换后的文件;
2、可以批量处理,在选择文件的对话框里可以选择多个文件,进行批量操作;
3、如果电脑有GPU,则会自动选择GPU处理,加快处理速度;
4、信息统计里面可以实时显示处理的各种统计信息;
5、视频处理完毕后自动进行音频的处理与合成。

软件运行界面如下:

在这里插入图片描述
二、主要代码:

话不多说,上代码!

UI的Python代码:

# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ai_repire(object):
    def setupUi(self, ai_repire):
        ai_repire.setObjectName("ai_repire")
        ai_repire.setEnabled(True)
        ai_repire.resize(912, 770)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        ai_repire.setFont(font)
        ai_repire.setMouseTracking(False)
        self.layoutWidget = QtWidgets.QWidget(ai_repire)
        self.layoutWidget.setGeometry(QtCore.QRect(360, 710, 531, 41))
        self.layoutWidget.setObjectName("layoutWidget")
        self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.layoutWidget)
        self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        self.startButton = QtWidgets.QPushButton(self.layoutWidget)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.startButton.setFont(font)
        self.startButton.setObjectName("startButton")
        self.horizontalLayout_5.addWidget(self.startButton)
        self.stopButton = QtWidgets.QPushButton(self.layoutWidget)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.stopButton.setFont(font)
        self.stopButton.setObjectName("stopButton")
        self.horizontalLayout_5.addWidget(self.stopButton)
        spacerItem = QtWidgets.QSpacerItem(60, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_5.addItem(spacerItem)
        self.helpButton = QtWidgets.QPushButton(self.layoutWidget)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.helpButton.setFont(font)
        self.helpButton.setObjectName("helpButton")
        self.horizontalLayout_5.addWidget(self.helpButton)
        self.quitButton = QtWidgets.QPushButton(self.layoutWidget)
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.quitButton.setFont(font)
        self.quitButton.setObjectName("quitButton")
        self.horizontalLayout_5.addWidget(self.quitButton)
        self.groupBox_2 = QtWidgets.QGroupBox(ai_repire)
        self.groupBox_2.setGeometry(QtCore.QRect(10, 20, 881, 281))
        self.groupBox_2.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
        self.groupBox_2.setAlignment(QtCore.Qt.AlignCenter)
        self.groupBox_2.setObjectName("groupBox_2")
        self.my_label1 = QtWidgets.QLabel(self.groupBox_2)
        self.my_label1.setGeometry(QtCore.QRect(12, 30, 427, 240))
        self.my_label1.setObjectName("my_label1")
        self.my_label2 = QtWidgets.QLabel(self.groupBox_2)
        self.my_label2.setGeometry(QtCore.QRect(443, 30, 427, 240))
        self.my_label2.setObjectName("my_label2")
        self.groupBox_4 = QtWidgets.QGroupBox(ai_repire)
        self.groupBox_4.setGeometry(QtCore.QRect(10, 320, 881, 151))
        self.groupBox_4.setAlignment(QtCore.Qt.AlignCenter)
        self.groupBox_4.setObjectName("groupBox_4")
        self.filesButton = QtWidgets.QPushButton(self.groupBox_4)
        self.filesButton.setGeometry(QtCore.QRect(20, 30, 78, 24))
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.filesButton.setFont(font)
        self.filesButton.setObjectName("filesButton")
        self.outButton = QtWidgets.QPushButton(self.groupBox_4)
        self.outButton.setGeometry(QtCore.QRect(20, 108, 78, 24))
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.outButton.setFont(font)
        self.outButton.setObjectName("outButton")
        self.txt1 = QtWidgets.QLabel(self.groupBox_4)
        self.txt1.setGeometry(QtCore.QRect(110, 32, 591, 20))
        self.txt1.setObjectName("txt1")
        self.txt2 = QtWidgets.QLabel(self.groupBox_4)
        self.txt2.setGeometry(QtCore.QRect(110, 110, 511, 20))
        self.txt2.setObjectName("txt2")
        self.filesButton1 = QtWidgets.QPushButton(self.groupBox_4)
        self.filesButton1.setGeometry(QtCore.QRect(20, 70, 78, 24))
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.filesButton1.setFont(font)
        self.filesButton1.setObjectName("filesButton1")
        self.txt3 = QtWidgets.QLabel(self.groupBox_4)
        self.txt3.setGeometry(QtCore.QRect(110, 70, 581, 20))
        self.txt3.setObjectName("txt3")
        self.my_label3 = QtWidgets.QLabel(self.groupBox_4)
        self.my_label3.setGeometry(QtCore.QRect(650, 20, 213, 120))
        self.my_label3.setObjectName("my_label3")
        self.groupBox_5 = QtWidgets.QGroupBox(ai_repire)
        self.groupBox_5.setGeometry(QtCore.QRect(10, 580, 881, 101))
        self.groupBox_5.setAlignment(QtCore.Qt.AlignCenter)
        self.groupBox_5.setObjectName("groupBox_5")
        self.txt11 = QtWidgets.QLabel(self.groupBox_5)
        self.txt11.setGeometry(QtCore.QRect(20, 30, 861, 16))
        self.txt11.setObjectName("txt11")
        self.txt12 = QtWidgets.QLabel(self.groupBox_5)
        self.txt12.setGeometry(QtCore.QRect(20, 60, 861, 21))
        self.txt12.setObjectName("txt12")
        self.groupBox_6 = QtWidgets.QGroupBox(ai_repire)
        self.groupBox_6.setGeometry(QtCore.QRect(10, 490, 881, 71))
        self.groupBox_6.setAlignment(QtCore.Qt.AlignCenter)
        self.groupBox_6.setObjectName("groupBox_6")
        self.checkBox_1 = QtWidgets.QCheckBox(self.groupBox_6)
        self.checkBox_1.setGeometry(QtCore.QRect(40, 30, 141, 16))
        self.checkBox_1.setObjectName("checkBox_1")
        self.buttonGroup1 = QtWidgets.QButtonGroup(ai_repire)
        self.buttonGroup1.setObjectName("buttonGroup1")
        self.buttonGroup1.addButton(self.checkBox_1)
        self.checkBox_2 = QtWidgets.QCheckBox(self.groupBox_6)
        self.checkBox_2.setGeometry(QtCore.QRect(260, 30, 141, 16))
        self.checkBox_2.setObjectName("checkBox_2")
        self.buttonGroup1.addButton(self.checkBox_2)
        self.check_result = QtWidgets.QPushButton(ai_repire)
        self.check_result.setGeometry(QtCore.QRect(130, 714, 121, 31))
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(12)
        self.check_result.setFont(font)
        self.check_result.setObjectName("check_result")

        self.retranslateUi(ai_repire)
        QtCore.QMetaObject.connectSlotsByName(ai_repire)

    def retranslateUi(self, ai_repire):
        _translate = QtCore.QCoreApplication.translate
        ai_repire.setWindowTitle(_translate("ai_repire", "AI换脸工具"))
        self.startButton.setText(_translate("ai_repire", "开始处理"))
        self.stopButton.setText(_translate("ai_repire", "停止处理"))
        self.helpButton.setText(_translate("ai_repire", "帮助"))
        self.quitButton.setText(_translate("ai_repire", "退出"))
        self.groupBox_2.setTitle(_translate("ai_repire", "预览窗口"))
        self.my_label1.setText(_translate("ai_repire", "原图"))
        self.my_label2.setText(_translate("ai_repire", "输出"))
        self.groupBox_4.setTitle(_translate("ai_repire", "文件设置"))
        self.filesButton.setText(_translate("ai_repire", "换脸视频"))
        self.outButton.setText(_translate("ai_repire", "输出目录"))
        self.txt1.setText(_translate("ai_repire", "请选择被换脸的视频文件[Ctrl+A全选、Ctrl/Shift+鼠标可多选]......"))
        self.txt2.setText(_translate("ai_repire", "换脸完成的视频输出目录"))
        self.filesButton1.setText(_translate("ai_repire", "换脸图片"))
        self.txt3.setText(_translate("ai_repire", "请选择自己的脸部图片"))
        self.my_label3.setText(_translate("ai_repire", "换脸图片"))
        self.groupBox_5.setTitle(_translate("ai_repire", "信息统计"))
        self.txt11.setText(_translate("ai_repire", "【视频信息】"))
        self.txt12.setText(_translate("ai_repire", "【运行信息】"))
        self.groupBox_6.setTitle(_translate("ai_repire", "AI模型选择"))
        self.checkBox_1.setText(_translate("ai_repire", "大模型512x512"))
        self.checkBox_2.setText(_translate("ai_repire", "小模型224x224"))
        self.check_result.setText(_translate("ai_repire", "查看换脸结果"))

软件核心代码如下:

#AI视频处理工具_
# import glob, shutil
import numpy as np
import os, sys, time, cv2, threading
from PIL import Image,ImageDraw,ImageFont
pil_img = Image.open("util/start_img.jpg")
ImageDraw.Draw(pil_img).text((160,150), "    AI换脸处理工具", (255,255,255),font=ImageFont.truetype("msyh.ttc", 36))
img_start = cv2.resize(cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR), (427, 240))
ImageDraw.Draw(pil_img).text((410,320), "正在加载AI模型,请稍后 ......", (255,255,255),font=ImageFont.truetype("msyh.ttc", 16))
img_s = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
run_flag = 0
def showpic():  # 以下代码显示软件初始界面
    global ret, frame
    while run_flag == 0:
        cv2.imshow("AI Repire Transfer System", img_s)
        cv2.waitKey(100)
    cv2.destroyAllWindows()
t = threading.Thread(target=showpic)
t.start()

import torch, fractions
import torch.nn.functional as F
from torchvision import transforms
from models.models import create_model
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import QWidget,QMessageBox,QFileDialog,QApplication
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QPixmap, QIcon
from AI_SwapFace_UI import Ui_ai_repire
from tqdm import tqdm
from moviepy.editor import AudioFileClip, VideoFileClip
import _fuction

DEBUG_FLAG = False
# DEBUG_FLAG = True
os.environ['TORCH_HOME'] = './torch_model'

my_title = "AI换脸工具"
work_path = os.getcwd()
input_path = work_path + '\input'; out_dir=work_path + '\output'
my_pic_a_path = work_path + '\\util\\face.jpg'
files = [work_path + '\\input\\test.mp4']

filesnums = 1 ; stop_flag = False ; t0 = 0; iii = 0
stop_flag_1 = False

def lcm(a, b): return abs(a * b) / fractions.gcd(a, b) if a and b else 0

transformer = transforms.Compose([transforms.ToTensor(),])
transformer_Arcface = transforms.Compose([transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
def _totensor(array):
    tensor = torch.from_numpy(array)
    img = tensor.transpose(0, 1).transpose(0, 2).contiguous()
    return img.float().div(255)

class MainWin(QWidget, Ui_ai_repire):
    def __init__(self):
        super(MainWin, self).__init__()
        self.setupUi(self)
        global hwnd, run_flag
        self.createLayout()
        self.setWindowIcon(QIcon("util/anime.ico"))
        self.setWindowFlags(Qt.WindowMinimizeButtonHint)
        self.show()
        run_flag = 1
        self.flash_item = True
        self.Style_flag = '1'

    def CV2toPIL(self, img):  # cv2转PIL
        return Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGRA2RGBA))
    def PILtoCV2(self, img):  # PIL转cv2
        return cv2.cvtColor(np.array(img), cv2.COLOR_RGBA2BGRA)
    def two_pic_combine_PIL(self, back_img, fore_img): #2个图片合并
        back_img = self.CV2toPIL(back_img); fore_img = self.CV2toPIL(fore_img); r,g,b,alpha = fore_img.split()
        return cv2.cvtColor(self.PILtoCV2(Image.composite(fore_img, back_img, alpha)), cv2.COLOR_BGRA2BGR)
    def water_print(self, img):  # 打文字水印
        # cv2.putText(图像,需要添加字符串,需要绘制的坐标,字体类型,字号,字体颜色,字体粗细)
        w = img.shape[1]  # 宽度
        h = img.shape[0]  # 高度
        bk_img = np.zeros((160, 1280, 3), np.uint8)  # Creat a Image
        bk_img = cv2.putText(bk_img, 'wx:herbert156', (80, 130), cv2.LINE_AA, 5, (180, 180, 180), 12)
        bk_img = cv2.resize(bk_img, (w, int(w * 16 / 128)))
        mask = 255 * np.ones(bk_img.shape, bk_img.dtype)
        width, height, channels = img.shape
        center = (height // 2, width // 2)  # 融合的位置,可以自己设置
        res = cv2.seamlessClone(bk_img, img, mask, center, cv2.MONOCHROME_TRANSFER)
        return res

    def ShowCarton(self, result, img_path, style):  # 处理完成的帧
        # out_name = out_dir + '/' + os.path.splitext(os.path.split(img_path)[1])[0] + '_'+style+'.'
        # if not _fuction.if_legal or _fuction.softname != "AI_VideoRepire":
        #     result = self.water_print(result)
        # outfile = cv2.resize(result, (img_w, img_h))
        # return result
        pass

    def RunAI(self, img_path):  # 
        if not _fuction.if_legal or _fuction.softname != "AI_SwapFace":
            result = self.water_print(img_path)
        return result


    def CvMatToQImage(self, ptr):  # Converts an opencv MAT format into a QImage
        ptr = cv2.cvtColor(ptr, cv2.COLOR_BGRA2RGBA)  # 颜色格式转换
        QtImg = QtGui.QImage(ptr.data, ptr.shape[1], ptr.shape[0], QtGui.QImage.Format_RGBA8888)
        return QtGui.QPixmap.fromImage(QtImg)

    def show_error(self, str):
        r_button = QMessageBox.question(self, my_title,'\n\n'+str+'\n\n', QMessageBox.Ok)
    # def show_error(self, str):
    #     infoBox = QMessageBox()
    #     infoBox.setIcon(QMessageBox.Information)
    #     infoBox.setText(str)
    #     infoBox.setStandardButtons(QMessageBox.Ok)
    #     infoBox.button(QMessageBox.Ok).animateClick(30000)  # 10秒自动关闭
    #     infoBox.exec_()

    def set_False_Btn(self):
        self.filesButton.setEnabled(False);       self.outButton.setEnabled(False)
        self.startButton.setEnabled(False);       self.stopButton.setEnabled(True)
        self.quitButton.setEnabled(False);        self.filesButton1.setEnabled(False)
    def set_True_Btn(self):
        self.filesButton.setEnabled(True);       self.outButton.setEnabled(True)
        self.startButton.setEnabled(True);       self.stopButton.setEnabled(False)
        self.quitButton.setEnabled(True);        self.filesButton1.setEnabled(True)

    def startrun(self):
        global iii,stop_flag,stop_flag_1, t0
        iii = 0; stop_flag = False
        stop_flag_1 = False
        t0 = time.time()
        if files == []: self.show_error('请选择需要处理的视频文件!'); return
        if my_pic_a_path == []: self.show_error('请选择自己的人像文件!'); return
        if not os.path.exists(out_dir): self.show_error('输出目录不存在,请重新选择!'); return
        self.set_False_Btn()
        self.set_text_info('【运行信息】 正在初始化AI模型......')

        def run_thread():
            global iii, stop_flag, stop_flag_1, t0
            self.flash_item = True
            # 处理前调入相关模型选择
            if self.checkBox_1.isChecked():  # 512x512
                self.crop_size_choose = 512
            if self.checkBox_2.isChecked():  # 224x224
                self.crop_size_choose = 224

            for file in files:
                iii += 1
                if stop_flag: break
                out_file = out_dir + '\\' + os.path.splitext(os.path.split(file)[1])[0] + '_1.mp4'
                self.video_change(file, out_file)
            stop_flag_1 = True

        t = threading.Thread(target=run_thread)
        t.start()

        self.my_timer = QTimer(self)
        self.my_timer.start(500)
        self.my_timer.timeout.connect(self.set_run_over)

    def set_text_info(self, str):
        self.txt12.setText(str)
        self.flash_item_str = str

    def set_run_over(self):
        if stop_flag_1:
            self.my_timer.stop()
            self.set_True_Btn()
            return

        if self.txt12.text() == '【运行信息】': self.txt12.setText(self.flash_item_str)
        else: self.txt12.setText('【运行信息】')

        # if self.flash_item:
        #     print(self.flash_item_str)
        #     self.txt12.setText(self.flash_item_str)
        #     self.flash_item_str = self.flash_item_str + '.'
        #     self.flash_item = not self.flash_item
        # else:
        #     if self.txt12.text == '【运行信息】 ':  self.txt12.setText(self.flash_item_str)
        #     self.txt12.setText('【运行信息】 ')
        #     self.flash_item = not self.flash_item

    def stoprun(self):
        global stop_flag
        r_button = QMessageBox.question(self, my_title,
                                        "\n\n    确定要停止视频处理吗?\n\n", QMessageBox.Yes | QMessageBox.No)
        if r_button == QMessageBox.Yes: stop_flag = True

    def helpWin(self):
        str="\n\n\n1、【换脸视频】选择需要处理的视频文件(可多选);\n" \
            "2、【换脸图片】选择自己的脸部图片(单选);\n" + \
            "3、【输出目录】处理后的文件目录,文件名:源文件_*.mp4;\n"+\
            "4、如没有Nvidia系列GPU,AI算法自动选择CPU处理;\n\n\n"+\
            "      本软件著作权归属:xxx         网址:xxx.com\n\n"
        QMessageBox.question(self, my_title, str, QMessageBox.Ok)
    def quitWin(self):
        r_button = QMessageBox.question(self, my_title,
                                        "\n\n退出将终止处理过程...... \n\n确认退出吗?\n\n", QMessageBox.Yes | QMessageBox.No)
        if r_button == QMessageBox.Yes: sys.exit()

    def checkresult(self):
        os.startfile(out_dir)

    def filesButton_fuc(self):
        global files,filesnums,input_path
        files, ok1 = QFileDialog.getOpenFileNames(self,'请选择视频文件[全选:Ctrl+A、多选:Ctrl/Shift+鼠标]',
                                                       input_path,"*.mp4;*.avi;*.mkv;;*.*")
        filesnums = len(files)
        if files!=[]:
            txt='目录:'+os.path.split(files[0])[0]+'|已选文件:'+str(filesnums)+'个|文件名:'
            for file in files: txt=txt+ os.path.split(file)[1]+'; '
            self.txt1.setText(txt)
            input_path = os.path.dirname(files[0])
        else:
            self.txt1.setText('请选择视频文件[全选:Ctrl+A、多选:Ctrl/Shift+鼠标]......')
    def filesButton1_fuc(self): #选择自己的人脸图片
        global input_path, my_pic_a_path
        my_pic_a_path, ok1 = QFileDialog.getOpenFileName(self,'请选择脸部图片文件[只能单选]',
                                                       input_path, "*.jpg;*.png;;*.*")
        if my_pic_a_path != []:
            img = cv2.imread(my_pic_a_path)
            size_x = img.shape[1]  # 宽度
            size_y = img.shape[0]  # 高度
            if self.checkBox_1.isChecked():  # 512x512
                if size_x < 512 or size_y < 512:
                    my_pic_a_path = []
                    self.show_error('\n请保证图片分辨率大于512x512(AI大模型).....\n\n'+
                                    '您选择的图片分辨率:'+str(size_x)+'x'+str(size_y));  return
            else:
                if size_x < 224 or size_y < 224:
                    my_pic_a_path = []
                    self.show_error('\n请保证图片分辨率大于224x224(AI小模型).....\n\n'+
                                    '您选择的图片分辨率:'+str(size_x)+'x'+str(size_y));  return
            self.txt3.setText(my_pic_a_path + ' | 分辨率:'+str(size_x)+'x'+str(size_y))
            input_path = os.path.dirname(my_pic_a_path)
            pix_img = QPixmap(my_pic_a_path)
            pix_img = pix_img.scaled(213, 120, Qt.KeepAspectRatio)
            self.my_label3.setAlignment(Qt.AlignCenter)
            self.my_label3.setPixmap(pix_img)
        else:
            self.txt3.setText('请选择脸部图片文件[只能单选]......')
    def outButton_fuc(self):
        global out_dir
        out_dir = QFileDialog.getExistingDirectory(self,'选择转换后的输出文件夹', work_path)
        if out_dir == '': self.txt2.setText('请选择视频变换后的文件保存目录......')
        else: self.txt2.setText(out_dir)

    def createLayout(self):
        self.my_label1.setPixmap(self.CvMatToQImage(img_start))
        self.my_label2.setPixmap(self.CvMatToQImage(img_start))
        pix_img = QPixmap(my_pic_a_path)
        pix_img = pix_img.scaled(213, 120, Qt.KeepAspectRatio)
        self.my_label3.setAlignment(Qt.AlignCenter)
        self.my_label3.setPixmap(pix_img)

        self.my_label1.setAlignment(Qt.AlignCenter); self.my_label2.setAlignment(Qt.AlignCenter)
        self.my_label1.setFixedSize(427, 240); self.my_label2.setFixedSize(427, 240)
        self.my_label1.setAlignment(Qt.AlignCenter); self.my_label2.setAlignment(Qt.AlignCenter)
        self.my_label1.setToolTip("本区域,显示的是原始视频缩略图...")
        self.my_label2.setToolTip("本区域,显示的是处理后的缩略图...")
        self.my_label3.setToolTip("本区域,显示的是用户选择的人脸图片...")

        self.checkBox_1.setChecked(True)

        self.filesButton.setToolTip("选择即将被处理的的视频文件,可单选、多选...")
        self.filesButton1.setToolTip("选择请选择自己的脸部图片...")
        self.outButton.setToolTip("选择输出文件目录,处理后的文件将存在此目录...")
        print(files)
        self.txt1.setText(files[0])
        self.txt2.setText(out_dir)
        self.txt3.setText(my_pic_a_path)

        self.filesButton.clicked.connect(self.filesButton_fuc)
        self.filesButton1.clicked.connect(self.filesButton1_fuc)
        self.outButton.clicked.connect(self.outButton_fuc)

        self.stopButton.setEnabled(False)
        self.startButton.clicked.connect(self.startrun)
        self.stopButton.clicked.connect(self.stoprun)
        self.helpButton.clicked.connect(self.helpWin)
        self.quitButton.clicked.connect(self.quitWin)
        self.check_result.clicked.connect(self.checkresult)

#if __name__ == '__main__':
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
app = QtWidgets.QApplication(sys.argv)
MainWin = MainWin()
sys.exit(app.exec_())


猜你喜欢

转载自blog.csdn.net/weixin_42398606/article/details/128235374