基于PyQt5和Pywinauto自动化测试客户端

Pywinauto自动化测试钉钉客户端

  • 效果
  • 前言
  • 一、什么是pywinauto?
  • 二、使用步骤
    • 1.安装
    • 2.启动客户端
  • 三、什么是PyQt5?
  • 四、使用步骤
    • 1.安装
    • 2.配置QtDesigner
  • 项目代码

效果

基于PyQt5和Pywinauto自动化测试客户端_第1张图片
基于PyQt5和Pywinauto自动化测试客户端_第2张图片
基于PyQt5和Pywinauto自动化测试客户端_第3张图片


前言

一、什么是pywinauto?

中文官方文档
pywinauto是一组用于自动化Microsoft Windows GUI的python模块。 最简单的是,它允许您将鼠标和键盘操作发送到窗口对话框和控件。

二、使用步骤

1.安装

pip install pywinauto

2.启动客户端

代码如下(示例):

 self.app_path = "D:/package/DingDing/main/current/DingTalk.exe"
 app = Application(backend="uia").start(self.app_path) 

三、什么是PyQt5?

官方文档
PyQt5 是Digia的一套Qt5应用框架与python的结合,同时支持2.x和3.x。本教程使用的是3.x。Qt库由Riverbank Computing开发,是最强大的GUI库之一。

四、使用步骤

1.安装

pip install PyQt5
pip install pyqt5-tools 

2.配置QtDesigner

参考如何配置
代码如下(示例):

1.设计界面
在PyCharm中创建一个项目,然后点击“Tools”–“External Tools”–“QTDesinger”打开QT Desinger
基于PyQt5和Pywinauto自动化测试客户端_第4张图片
2. 设计如下
基于PyQt5和Pywinauto自动化测试客户端_第5张图片
保存,生成如下UI代码。
ui_mainwindow.ui



 MainWindow
 
  
   
    0
    0
    807
    676
   
  
  
   Check Account
  
  
   
  
  
   false
  
  
   
    
   
   
    
     
      
       
        Arial
        10
       
      
      
       
      
      
       2
      
      
       
        16
        16
       
      
      
       
        
         Arial
         10
         50
         false
        
       
       
        Tool
       
       
        
         
          340
          170
          71
          31
         
        
        
         RunButton
        
       
       
        
         
          70
          20
          641
          131
         
        
        
         
          Times New Roman
          9
          50
          false
         
        
       
       
        
         
          70
          220
          641
          341
         
        
        
         
          Times New Roman
          9
          50
          false
         
        
       
       
        
         
          10
          10
          51
          51
         
        
        
         
          Arial
          9
          50
          false
         
        
        
         Account
        
       
       
        
         
          10
          220
          41
          31
         
        
        
         
          Arial
          9
          50
          false
         
        
        
         Logs
        
       
      
      
       
        false
       
       
        
         ..
       
       
        Multiple
       
       
        
         
          10
          20
          51
          31
         
        
        
         
          Arial
          9
         
        
        
         Excel
        
       
       
        
         
          510
          20
          75
          31
         
        
        
         
          Arial
          10
         
        
        
         Select
        
       
       
        
         
          10
          120
          51
          51
         
        
        
         
          Arial
          9
         
        
        
         Account
        
       
       
        
         
          10
          330
          41
          31
         
        
        
         
          Arial
          9
         
        
        
         Logs
        
       
       
        
         
          360
          280
          81
          31
         
        
        
         
          Arial
          10
         
        
        
         RunButton
        
       
       
        
         
          70
          130
          641
          131
         
        
        
         
          Times New Roman
          9
         
        
       
       
        
         
          70
          330
          641
          231
         
        
        
         
          Times New Roman
          9
         
        
       
       
        
         
          10
          70
          51
          31
         
        
        
         
          Arial
          9
         
        
        
         Zoom
        
       
       
        
         
          510
          70
          75
          31
         
        
        
         
          Arial
          10
         
        
        
         Select
        
       
       
        
         
          70
          19
          411
          31
         
        
        
         
          Times New Roman
          9
         
        
       
       
        
         
          70
          70
          411
          31
         
        
        
         
          Times New Roman
          9
         
        
       
       label
       label_2
       label_3
       pushButton_2
       pushButton
       textEdit_6
       textEdit_8
       label_14
       pushButton_5
       lineEdit
       lineEdit_2
      
      
       
        Single
       
       
        
         
          10
          70
          61
          31
         
        
        
         
          Arial
          9
         
        
        
         Username
        
       
       
        
         
          10
          120
          61
          31
         
        
        
         
          Arial
          9
         
        
        
         Password
        
       
       
        
         
          10
          171
          121
          31
         
        
        
         
          Arial
          9
         
        
        
         Buy A License
        
       
       
        
         
          10
          220
          91
          31
         
        
        
         
          Arial
          9
         
        
        
         AccountType
        
       
       
        
         
          400
          70
          54
          31
         
        
        
         
          Arial
          9
         
        
        
         Role
        
       
       
        
         
          400
          110
          71
          51
         
        
        
         
          Arial
          9
         
        
        
         UserType
        
       
       
        
         
          400
          160
          54
          51
         
        
        
         
          Arial
          9
         
        
        
         K12(Y/N)
        
       
       
        
         
          600
          271
          91
          31
         
        
        
         
          Arial
          10
         
        
        
         RunButton
        
       
       
        
         
          270
          171
          81
          31
         
        
        
         
          Arial
          10
         
        
        
         No
        
       
       
        
         
          150
          171
          71
          31
         
        
        
         
          Arial
          10
         
        
        
         Yes
        
        
         true
        
        
         false
        
       
       
        
         
          100
          221
          231
          31
         
        
        
         
          Times New Roman
          10
         
        
        
         ArrowCursor
        
        
         
          
         
        
        
         
          enterprise-activehost
         
        
        
         
          Education
         
        
        
         
          biz-enterprise
         
        
        
         
          Single Pro (Named Host)
         
        
        
         
          single pro+webinar
         
        
        
         
          free with cc
         
        
        
         
          free without cc
         
        
        
         
          freetrail
         
        
        
         
          ZR
         
        
        
         
          pro(license>1)
         
        
        
         
          biz(license<100)
         
        
        
         
          API
         
        
        
         
          free with CC (Free-type2)
         
        
        
         
          free with CC (EDU - K12)
         
        
       
       
        
         
          480
          120
          211
          31
         
        
        
         
          Times New Roman
          10
         
        
        
         
          
         
        
        
         
          License
         
        
        
         
          Basic
         
        
        
         
          On-Prem
         
        
       
       
        
         
          10
          330
          54
          31
         
        
        
         
          Arial
          9
         
        
        
         Logs
        
       
       
        
         
          100
          330
          591
          211
         
        
        
         
          Times New Roman
          10
         
        
        
         
        
       
       
        
         
          480
          70
          211
          31
         
        
        
         
          Times New Roman
          10
         
        
        
         
          
         
        
        
         
          owner
         
        
        
         
          member
         
        
       
       
        
         
          480
          170
          211
          31
         
        
        
         
          Times New Roman
          10
         
        
        
         
          
         
        
        
         
          Yes
         
        
        
         
          No
         
        
       
       
        
         
          10
          20
          71
          31
         
        
        
         
          Arial
          9
         
        
        
         Zoom Path
        
       
       
        
         
          600
          20
          91
          31
         
        
        
         
          Arial
          10
         
        
        
         Select
        
       
       
        
         
          100
          20
          381
          31
         
        
        
         
          Times New Roman
          10
         
        
       
       
        
         
          100
          70
          271
          31
         
        
        
         
          Times New Roman
          10
         
        
       
       
        
         
          100
          120
          271
          31
         
        
        
         
          Times New Roman
          10
         
        
       
      
     
    
   
  
  
   
    
     0
     0
     807
     23
    
   
  
  
 
 
 

转换为python代码
ui_mainwindow.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'ui_mainwindow.ui'
#
# Created by: PyQt5 UI code generator 5.15.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(807, 676)
        MainWindow.setStyleSheet("")
        MainWindow.setUnifiedTitleAndToolBarOnMac(False)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setStyleSheet("")
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(10)
        self.tabWidget.setFont(font)
        self.tabWidget.setStyleSheet("")
        self.tabWidget.setIconSize(QtCore.QSize(16, 16))
        self.tabWidget.setObjectName("tabWidget")
        self.tab_3 = QtWidgets.QWidget()
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(10)
        font.setBold(False)
        font.setWeight(50)
        self.tab_3.setFont(font)
        self.tab_3.setObjectName("tab_3")
        self.pushButton_6 = QtWidgets.QPushButton(self.tab_3)
        self.pushButton_6.setGeometry(QtCore.QRect(340, 170, 71, 31))
        self.pushButton_6.setObjectName("pushButton_6")
        self.textEdit_11 = QtWidgets.QTextEdit(self.tab_3)
        self.textEdit_11.setGeometry(QtCore.QRect(70, 20, 641, 131))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(9)
        font.setBold(False)
        font.setWeight(50)
        self.textEdit_11.setFont(font)
        self.textEdit_11.setObjectName("textEdit_11")
        self.textEdit_12 = QtWidgets.QTextEdit(self.tab_3)
        self.textEdit_12.setGeometry(QtCore.QRect(70, 220, 641, 341))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(9)
        font.setBold(False)
        font.setWeight(50)
        self.textEdit_12.setFont(font)
        self.textEdit_12.setObjectName("textEdit_12")
        self.label_15 = QtWidgets.QLabel(self.tab_3)
        self.label_15.setGeometry(QtCore.QRect(10, 10, 51, 51))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        font.setBold(False)
        font.setWeight(50)
        self.label_15.setFont(font)
        self.label_15.setObjectName("label_15")
        self.label_16 = QtWidgets.QLabel(self.tab_3)
        self.label_16.setGeometry(QtCore.QRect(10, 220, 41, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        font.setBold(False)
        font.setWeight(50)
        self.label_16.setFont(font)
        self.label_16.setObjectName("label_16")
        self.tabWidget.addTab(self.tab_3, "")
        self.tab = QtWidgets.QWidget()
        self.tab.setTabletTracking(False)
        self.tab.setObjectName("tab")
        self.label = QtWidgets.QLabel(self.tab)
        self.label.setGeometry(QtCore.QRect(10, 20, 51, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label.setFont(font)
        self.label.setObjectName("label")
        self.pushButton = QtWidgets.QPushButton(self.tab)
        self.pushButton.setGeometry(QtCore.QRect(510, 20, 75, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(10)
        self.pushButton.setFont(font)
        self.pushButton.setObjectName("pushButton")
        self.label_2 = QtWidgets.QLabel(self.tab)
        self.label_2.setGeometry(QtCore.QRect(10, 120, 51, 51))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_2.setFont(font)
        self.label_2.setObjectName("label_2")
        self.label_3 = QtWidgets.QLabel(self.tab)
        self.label_3.setGeometry(QtCore.QRect(10, 330, 41, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_3.setFont(font)
        self.label_3.setObjectName("label_3")
        self.pushButton_2 = QtWidgets.QPushButton(self.tab)
        self.pushButton_2.setGeometry(QtCore.QRect(360, 280, 81, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(10)
        self.pushButton_2.setFont(font)
        self.pushButton_2.setObjectName("pushButton_2")
        self.textEdit_6 = QtWidgets.QTextEdit(self.tab)
        self.textEdit_6.setGeometry(QtCore.QRect(70, 130, 641, 131))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(9)
        self.textEdit_6.setFont(font)
        self.textEdit_6.setObjectName("textEdit_6")
        self.textEdit_8 = QtWidgets.QTextEdit(self.tab)
        self.textEdit_8.setGeometry(QtCore.QRect(70, 330, 641, 231))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(9)
        self.textEdit_8.setFont(font)
        self.textEdit_8.setObjectName("textEdit_8")
        self.label_14 = QtWidgets.QLabel(self.tab)
        self.label_14.setGeometry(QtCore.QRect(10, 70, 51, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_14.setFont(font)
        self.label_14.setObjectName("label_14")
        self.pushButton_5 = QtWidgets.QPushButton(self.tab)
        self.pushButton_5.setGeometry(QtCore.QRect(510, 70, 75, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(10)
        self.pushButton_5.setFont(font)
        self.pushButton_5.setObjectName("pushButton_5")
        self.lineEdit = QtWidgets.QLineEdit(self.tab)
        self.lineEdit.setGeometry(QtCore.QRect(70, 19, 411, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(9)
        self.lineEdit.setFont(font)
        self.lineEdit.setObjectName("lineEdit")
        self.lineEdit_2 = QtWidgets.QLineEdit(self.tab)
        self.lineEdit_2.setGeometry(QtCore.QRect(70, 70, 411, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(9)
        self.lineEdit_2.setFont(font)
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.label.raise_()
        self.label_2.raise_()
        self.label_3.raise_()
        self.pushButton_2.raise_()
        self.pushButton.raise_()
        self.textEdit_6.raise_()
        self.textEdit_8.raise_()
        self.label_14.raise_()
        self.pushButton_5.raise_()
        self.lineEdit.raise_()
        self.lineEdit_2.raise_()
        icon = QtGui.QIcon.fromTheme("dsa")
        self.tabWidget.addTab(self.tab, icon, "")
        self.tab_2 = QtWidgets.QWidget()
        self.tab_2.setObjectName("tab_2")
        self.label_4 = QtWidgets.QLabel(self.tab_2)
        self.label_4.setGeometry(QtCore.QRect(10, 70, 61, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_4.setFont(font)
        self.label_4.setObjectName("label_4")
        self.label_5 = QtWidgets.QLabel(self.tab_2)
        self.label_5.setGeometry(QtCore.QRect(10, 120, 61, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_5.setFont(font)
        self.label_5.setObjectName("label_5")
        self.label_6 = QtWidgets.QLabel(self.tab_2)
        self.label_6.setGeometry(QtCore.QRect(10, 171, 121, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_6.setFont(font)
        self.label_6.setObjectName("label_6")
        self.label_7 = QtWidgets.QLabel(self.tab_2)
        self.label_7.setGeometry(QtCore.QRect(10, 220, 91, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_7.setFont(font)
        self.label_7.setObjectName("label_7")
        self.label_8 = QtWidgets.QLabel(self.tab_2)
        self.label_8.setGeometry(QtCore.QRect(400, 70, 54, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_8.setFont(font)
        self.label_8.setObjectName("label_8")
        self.label_9 = QtWidgets.QLabel(self.tab_2)
        self.label_9.setGeometry(QtCore.QRect(400, 110, 71, 51))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_9.setFont(font)
        self.label_9.setObjectName("label_9")
        self.label_10 = QtWidgets.QLabel(self.tab_2)
        self.label_10.setGeometry(QtCore.QRect(400, 160, 54, 51))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_10.setFont(font)
        self.label_10.setObjectName("label_10")
        self.pushButton_3 = QtWidgets.QPushButton(self.tab_2)
        self.pushButton_3.setGeometry(QtCore.QRect(600, 271, 91, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(10)
        self.pushButton_3.setFont(font)
        self.pushButton_3.setObjectName("pushButton_3")
        self.radioButton = QtWidgets.QRadioButton(self.tab_2)
        self.radioButton.setGeometry(QtCore.QRect(270, 171, 81, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(10)
        self.radioButton.setFont(font)
        self.radioButton.setObjectName("radioButton")
        self.radioButton_2 = QtWidgets.QRadioButton(self.tab_2)
        self.radioButton_2.setGeometry(QtCore.QRect(150, 171, 71, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(10)
        self.radioButton_2.setFont(font)
        self.radioButton_2.setChecked(True)
        self.radioButton_2.setAutoRepeat(False)
        self.radioButton_2.setObjectName("radioButton_2")
        self.comboBox = QtWidgets.QComboBox(self.tab_2)
        self.comboBox.setGeometry(QtCore.QRect(100, 221, 231, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(10)
        self.comboBox.setFont(font)
        self.comboBox.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
        self.comboBox.setObjectName("comboBox")
        self.comboBox.addItem("")
        self.comboBox.setItemText(0, "")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox_2 = QtWidgets.QComboBox(self.tab_2)
        self.comboBox_2.setGeometry(QtCore.QRect(480, 120, 211, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(10)
        self.comboBox_2.setFont(font)
        self.comboBox_2.setObjectName("comboBox_2")
        self.comboBox_2.addItem("")
        self.comboBox_2.setItemText(0, "")
        self.comboBox_2.addItem("")
        self.comboBox_2.addItem("")
        self.comboBox_2.addItem("")
        self.label_12 = QtWidgets.QLabel(self.tab_2)
        self.label_12.setGeometry(QtCore.QRect(10, 330, 54, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_12.setFont(font)
        self.label_12.setObjectName("label_12")
        self.textEdit_4 = QtWidgets.QTextEdit(self.tab_2)
        self.textEdit_4.setGeometry(QtCore.QRect(100, 330, 591, 211))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(10)
        self.textEdit_4.setFont(font)
        self.textEdit_4.setStyleSheet("")
        self.textEdit_4.setObjectName("textEdit_4")
        self.comboBox_3 = QtWidgets.QComboBox(self.tab_2)
        self.comboBox_3.setGeometry(QtCore.QRect(480, 70, 211, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(10)
        self.comboBox_3.setFont(font)
        self.comboBox_3.setObjectName("comboBox_3")
        self.comboBox_3.addItem("")
        self.comboBox_3.setItemText(0, "")
        self.comboBox_3.addItem("")
        self.comboBox_3.addItem("")
        self.comboBox_4 = QtWidgets.QComboBox(self.tab_2)
        self.comboBox_4.setGeometry(QtCore.QRect(480, 170, 211, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(10)
        self.comboBox_4.setFont(font)
        self.comboBox_4.setObjectName("comboBox_4")
        self.comboBox_4.addItem("")
        self.comboBox_4.setItemText(0, "")
        self.comboBox_4.addItem("")
        self.comboBox_4.addItem("")
        self.label_13 = QtWidgets.QLabel(self.tab_2)
        self.label_13.setGeometry(QtCore.QRect(10, 20, 71, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        self.label_13.setFont(font)
        self.label_13.setObjectName("label_13")
        self.pushButton_4 = QtWidgets.QPushButton(self.tab_2)
        self.pushButton_4.setGeometry(QtCore.QRect(600, 20, 91, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(10)
        self.pushButton_4.setFont(font)
        self.pushButton_4.setObjectName("pushButton_4")
        self.lineEdit_3 = QtWidgets.QLineEdit(self.tab_2)
        self.lineEdit_3.setGeometry(QtCore.QRect(100, 20, 381, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(10)
        self.lineEdit_3.setFont(font)
        self.lineEdit_3.setObjectName("lineEdit_3")
        self.lineEdit_4 = QtWidgets.QLineEdit(self.tab_2)
        self.lineEdit_4.setGeometry(QtCore.QRect(100, 70, 271, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(10)
        self.lineEdit_4.setFont(font)
        self.lineEdit_4.setObjectName("lineEdit_4")
        self.lineEdit_5 = QtWidgets.QLineEdit(self.tab_2)
        self.lineEdit_5.setGeometry(QtCore.QRect(100, 120, 271, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(10)
        self.lineEdit_5.setFont(font)
        self.lineEdit_5.setObjectName("lineEdit_5")
        self.tabWidget.addTab(self.tab_2, "")
        self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 807, 23))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.tabWidget.setCurrentIndex(2)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Check Account"))
        self.pushButton_6.setText(_translate("MainWindow", "RunButton"))
        self.label_15.setText(_translate("MainWindow", "Account"))
        self.label_16.setText(_translate("MainWindow", "Logs"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Tool"))
        self.label.setText(_translate("MainWindow", "Excel"))
        self.pushButton.setText(_translate("MainWindow", "Select"))
        self.label_2.setText(_translate("MainWindow", "Account"))
        self.label_3.setText(_translate("MainWindow", "Logs"))
        self.pushButton_2.setText(_translate("MainWindow", "RunButton"))
        self.label_14.setText(_translate("MainWindow", "Zoom"))
        self.pushButton_5.setText(_translate("MainWindow", "Select"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Multiple"))
        self.label_4.setText(_translate("MainWindow", "Username"))
        self.label_5.setText(_translate("MainWindow", "Password"))
        self.label_6.setText(_translate("MainWindow", "Buy A License"))
        self.label_7.setText(_translate("MainWindow", "AccountType"))
        self.label_8.setText(_translate("MainWindow", "Role"))
        self.label_9.setText(_translate("MainWindow", "UserType"))
        self.label_10.setText(_translate("MainWindow", "K12(Y/N)"))
        self.pushButton_3.setText(_translate("MainWindow", "RunButton"))
        self.radioButton.setText(_translate("MainWindow", "No"))
        self.radioButton_2.setText(_translate("MainWindow", "Yes"))
        self.comboBox.setItemText(1, _translate("MainWindow", "Enterprise-activehost"))
        self.comboBox.setItemText(2, _translate("MainWindow", "Education"))
        self.comboBox.setItemText(3, _translate("MainWindow", "Biz-enterprise"))
        self.comboBox.setItemText(4, _translate("MainWindow", "Single Pro (Named Host)"))
        self.comboBox.setItemText(5, _translate("MainWindow", "Single Pro+webinar"))
        self.comboBox.setItemText(6, _translate("MainWindow", "Free with cc"))
        self.comboBox.setItemText(7, _translate("MainWindow", "Free without cc"))
        self.comboBox.setItemText(8, _translate("MainWindow", "Freetrail"))
        self.comboBox.setItemText(9, _translate("MainWindow", "ZR"))
        self.comboBox.setItemText(10, _translate("MainWindow", "Pro(license>1)"))
        self.comboBox.setItemText(11, _translate("MainWindow", "Biz(license<100)"))
        self.comboBox.setItemText(12, _translate("MainWindow", "API"))
        self.comboBox.setItemText(13, _translate("MainWindow", "Free with CC (Free-type2)"))
        self.comboBox.setItemText(14, _translate("MainWindow", "Free with CC (EDU-K12)"))
        self.comboBox_2.setItemText(1, _translate("MainWindow", "License"))
        self.comboBox_2.setItemText(2, _translate("MainWindow", "Basic"))
        self.comboBox_2.setItemText(3, _translate("MainWindow", "On-Prem"))
        self.label_12.setText(_translate("MainWindow", "Logs"))
        self.comboBox_3.setItemText(1, _translate("MainWindow", "Owner"))
        self.comboBox_3.setItemText(2, _translate("MainWindow", "Member"))
        self.comboBox_4.setItemText(1, _translate("MainWindow", "Yes"))
        self.comboBox_4.setItemText(2, _translate("MainWindow", "No"))
        self.label_13.setText(_translate("MainWindow", "Zoom Path"))
        self.pushButton_4.setText(_translate("MainWindow", "Select"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Single"))

项目代码

基于PyQt5和Pywinauto自动化测试客户端_第6张图片
main.py

# -*- coding: utf-8 -*-
import os
import sys
import time
import threading

from mainwindow import MainWindow
from PyQt5.QtWidgets import QApplication

parent_path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(parent_path)


class RunGUI(object):
    """运行GUI界面"""

    def gui_start(self):
        """启动gui界面"""
        app = QApplication(sys.argv)  # QApplication采用事件循环机制,当QApplication初始化后,就进入应用程序的主循环
        main_window = MainWindow()  # 打开主界面
        main_window.show()  # 显示主界面
        sys.exit(app.exec_())  # sys.exit()函数可以结束一个应用程序,使应用程序在主循环中退出。

    def create_thread(self, func, *args):
        """将函数放入线程中执行"""
        t = threading.Thread(target=func, args=args)  # 创建线程
        t.setDaemon(True)  # 守护线程
        t.start()  # 启动线程

    def run(self):
        self.create_thread(self.gui_start)  # 将GUI界面放到守护线程中运行
        while True:
            time.sleep(1)
            print(len(threading.enumerate()))
            if len(threading.enumerate()) < 2:  # 当只剩下1个线程时,即GUI线程关闭,主线程退出
                break


if __name__ == '__main__':
    r = RunGUI()
    r.run()

mainwindow.py

# coding: utf-8
import os
import xlrd
from PyQt5 import QtWidgets
from PyQt5.QtGui import QTextCursor

from thread_work import WorkThread
from ui_mainwindow import Ui_MainWindow
from PyQt5.QtWidgets import QMessageBox, QWidget, QMainWindow


class MainWindow(QMainWindow, Ui_MainWindow):
    """GUI主界面操作"""

    def __init__(self):
        """初始化"""
        super(MainWindow, self).__init__()  # super().__init__()继承父类的init方法
        self.setupUi(self)  # 初始化窗口

        self.app_path = None  # app路径

        self.account_lists = []  # 账号list
        self.account_path = None  # 账号路径

        self.thread = None  # 初始化线程

        self.log_msg = ""  # 简单界面log日志

        # 绑定多账号按钮点击事件
        self.pushButton.clicked.connect(self.get_multiple_file_path)  # 绑定账号文件选择按钮点击事件
        self.pushButton_5.clicked.connect(self.get_multiple_app_path)  # 绑定app选择按钮点击事件
        self.pushButton_2.clicked.connect(self.multiple_run_button)  # 绑定运行按钮点击事件

        # 绑定单账号按钮点击事件
        self.pushButton_3.clicked.connect(self.single_run_button)  # 绑定运行按钮点击事件
        self.pushButton_4.clicked.connect(self.get_single_app_path)  # 绑定app选择按钮点击事件

        # 绑定多账号简单界面按钮点击事件
        self.pushButton_6.clicked.connect(self.simple_run_button)  # 绑定app选择按钮点击事件

    def simple_run_button(self):
        """多账号简单界面点击运行按钮"""
        self.pushButton_6.setEnabled(False)
        app_path = os.getenv("APPDATA") + "\\Zoom\\bin\\Zoom.exe"  # 获取app路径
        self.account_path = os.getcwd() + "\\Zoom.xlsx"  # 获取账号文件路径
        self.get_account()

        self.thread = WorkThread()  # 初始化一条线程
        self.thread.account_lists = self.account_lists  # 将账号list传给线程
        self.thread.app_path = app_path  # 将 app path 传给线程
        if app_path:
            self.thread.sinOut.connect(self.simple_output)  # 绑定信号槽,获取信号
            self.thread.complete_sinOut.connect(self.simple_complete_sinOut)
            self.thread.start()  # 开启线程
        else:
            w = QWidget()
            QMessageBox.about(w, 'Error', 'Failed to get Zoom.exe path')
            self.pushButton_6.setEnabled(True)

    def simple_complete_sinOut(self, msg):
        """多账号简单界面完成信号槽信息"""
        w = QWidget()
        QMessageBox.about(w, 'Success', 'All account test completed')
        self.pushButton_6.setEnabled(True)

    def simple_output(self, msg):
        """多账号简单界面信号槽信息"""
        res = self.account_msg(msg[0])  # 拼接账号信息显示框展示内容
        self.textEdit_11.setText(res)

        log = self.logs_msg(msg[1])  # 拼接账号信息显示框展示内容
        self.log_msg += log
        self.textEdit_12.setText(self.log_msg)

        self.move_cursor(self.textEdit_12)  # 移动光标到文档最后

        self.pushButton_6.setEnabled(False)

    def get_multiple_file_path(self):
        """多账号界面获取账号文件信息"""
        account_path, file_type = QtWidgets.QFileDialog.getOpenFileName(None, "选取账号文件",
                                                                        os.getcwd() + "\\Zoom.xlsx", )  # 打开文件选择框
        if str(account_path).strip() == "" or str(account_path).split(".")[-1] != "xlsx":
            w = QWidget()
            QMessageBox.about(w, 'Error', 'The file is not xlsx')  # 提示框
            self.account_path = ''
            self.lineEdit.setText(self.account_path)  # 设置输入框value
        else:
            self.account_path = account_path
            self.lineEdit.setText(self.account_path)

    def get_multiple_app_path(self):
        """多账号界面获取app路径"""
        if self.account_path:
            self.get_account()  # 调用读取账号文件函数获取所有账号list
            app_path, file_type = QtWidgets.QFileDialog.getOpenFileName(None, "选取Zoom软件", os.getenv(
                "APPDATA") + "\\Zoom\\bin\\Zoom.exe", )  # 路径
            if str(app_path).strip() == "" or app_path.split("/")[-1] != "Zoom.exe":
                w = QWidget()
                QMessageBox.about(w, 'Error', 'The file is not Zoom.exe')
                self.app_path = ''
                self.lineEdit_2.setText(self.app_path)
            else:
                self.lineEdit_2.setText(app_path)
                self.app_path = app_path
        else:
            w = QWidget()
            QMessageBox.about(w, 'Error', 'Please select the account path')
            self.pushButton_2.setEnabled(True)  # 设置按钮可点击状态

    def multiple_run_button(self):
        """多账号界面点击运行按钮"""
        self.pushButton_2.setEnabled(False)
        self.thread = WorkThread()  # 初始化一条线程
        self.thread.account_lists = self.account_lists  # 将账号list传给线程
        self.thread.app_path = self.app_path  # 将 app path 传给线程
        if self.app_path:
            self.thread.sinOut.connect(self.multiple_output)  # 绑定信号槽,获取信号
            self.thread.complete_sinOut.connect(self.multiple_complete_sinOut)
            self.thread.start()  # 开启线程
        else:
            w = QWidget()
            QMessageBox.about(w, 'Error', 'Please select the Zoom.exe path')
            self.pushButton_2.setEnabled(True)

    def multiple_complete_sinOut(self, msg):
        """多账号界面完成信号槽信息"""
        w = QWidget()
        QMessageBox.about(w, 'Success', 'All account test completed')
        self.pushButton_2.setEnabled(True)

    def multiple_output(self, msg):
        """多账号界面信号槽信息"""
        res = self.account_msg(msg[0])  # 拼接账号信息显示框展示内容
        self.textEdit_6.setText(res)

        log = self.logs_msg(msg[1])  # 拼接账号信息显示框展示内容
        self.log_msg += log
        self.textEdit_8.setText(self.log_msg)

        self.move_cursor(self.textEdit_8)  # 移动光标到文档最后

        self.pushButton_2.setEnabled(False)

    def get_single_app_path(self):
        """单账号界面获取app路径"""
        app_path, file_type = QtWidgets.QFileDialog.getOpenFileName(None, "选取Zoom软件",
                                                                    os.getenv("APPDATA") + "\\Zoom\\bin\\Zoom.exe", )
        if str(app_path).strip() == "" or app_path.split("/")[-1] != "Zoom.exe":
            w = QWidget()
            QMessageBox.about(w, 'Error', 'The file is not Zoom.exe')
            self.app_path = ''
            self.lineEdit_3.setText(self.app_path)
        else:
            self.lineEdit_3.setText(app_path)
            self.app_path = app_path

    def single_run_button(self):
        """单账号界面点击运行按钮"""
        if self.app_path:
            self.pushButton_3.setEnabled(False)
            account_dict = {}
            username = self.lineEdit_4.text().replace(' ', '')  # 获取输入框信息
            password = self.lineEdit_5.text().replace(' ', '')
            buy_a_license_yes = self.radioButton_2.isChecked()  # 获取单选框是否被选择
            account_type = self.comboBox.currentText()  # 获取复选框当前选择信息
            user_type = self.comboBox_2.currentText()
            role = self.comboBox_3.currentText()
            k12 = self.comboBox_4.currentText()
            if buy_a_license_yes:
                buy_a_license = "Yes"
            else:
                buy_a_license = "No"
            if self.check_value(username, "Username") and self.check_value(password, "Password") and self.check_value(
                    account_type, "AccountType") and self.check_value(user_type, "UserType") and \
                    self.check_value(role, "Role") and self.check_value(k12, "K12"):  # 判断所有信息是否都填写完毕
                account_dict["AccountType"] = account_type
                account_dict["Role"] = role
                account_dict["UserType"] = user_type
                account_dict["K12"] = k12
                account_dict["UserName"] = username
                account_dict["PassWord"] = password
                account_dict["BuySign"] = buy_a_license
                self.account_lists.append(account_dict)
                self.thread = WorkThread()
                self.thread.account_lists = self.account_lists
                self.thread.app_path = self.app_path
                self.thread.sinOut.connect(self.single_output)
                self.thread.start()
        else:
            w = QWidget()
            QMessageBox.about(w, 'Error', 'Please select the Zoom.exe path')
            self.pushButton_3.setEnabled(True)

    def single_output(self, msg):
        """单账号界面信号槽信息"""
        log = self.logs_msg(msg[1])  # 拼接账号信息显示框展示内容
        self.log_msg += log
        self.textEdit_4.setText(self.log_msg)
        self.pushButton_3.setEnabled(True)

    def check_value(self, msg, name):
        """校验单账号界面,输入框、单选框、多选框等信息是否填写"""
        if msg.strip() == "":
            w = QWidget()
            QMessageBox.about(w, 'Error', 'Please input the %s' % name)
            self.pushButton_3.setEnabled(True)
            return False
        else:
            return True

    def get_account(self):
        """获取账号信息"""
        try:
            wb = xlrd.open_workbook(self.account_path)  # 打开excel文件
            table = wb.sheets()[0]  # 获取第一个sheet
            rows = table.nrows  # 总行数
            for i in range(1, rows):
                account_dict = {}
                row_values = table.row_values(i)
                account_type = row_values[0]
                role = row_values[1]
                user_type = row_values[2]
                k12 = row_values[3]
                username = row_values[4]
                password = row_values[5]
                buy_sign = row_values[6]
                account_dict["UserName"] = str(username).replace(' ', '')
                account_dict["PassWord"] = str(password).replace(' ', '')
                account_dict["BuySign"] = str(buy_sign).strip()
                account_dict["AccountType"] = str(account_type).strip()
                account_dict["Role"] = str(role).strip()
                account_dict["UserType"] = str(user_type).strip()
                account_dict["K12"] = str(k12).strip()
                self.account_lists.append(account_dict)
            return True
        except Exception as e:
            w = QWidget()  # 没有父类的widget将被作为窗口使用
            QMessageBox.about(w, 'Error', 'Failed to read account information in excel!')
            return False

    def account_msg(self, msg):
        """拼接账号信息显示框展示内容"""
        res = ""
        for k, v in msg.items():
            res += "" + str(
                k) + ":" + "" + \
                   "" + str(v) + "" + "
"
return res def logs_msg(self, m): """装饰日志框展示内容""" if str(m).__contains__("Fail") or str(m).__contains__("Error"): msg = "" + str(m) + "" + "
"
else: msg = "" + str(m) + "" + "
"
return msg def move_cursor(self, edit): """移动光标到文档最后""" edit.setText(self.log_msg) cursor = edit.textCursor() # 获取logs框鼠标光标 cursor.movePosition(QTextCursor.End) # 光标移动到文档末尾 edit.setTextCursor(cursor) # 设置文档光标

thread_work.py

# -*- coding: utf-8 -*-
import time

from PyQt5.QtCore import QThread, pyqtSignal

from check_a_license import ZoomClientAutoTest


class WorkThread(QThread):
    """工作线程"""
    sinOut = pyqtSignal(list)  # 信号槽:和UI主线程通讯, 数据类型str、int、list等
    complete_sinOut = pyqtSignal(str)  # 测试完成信号槽

    def __init__(self):
        """初始化"""
        super(WorkThread, self).__init__()

    def run(self):
        """线程运行"""
        for account_dict in self.account_lists:
            time.sleep(1)
            sin_out_list = []
            username = account_dict["UserName"]
            password = account_dict["PassWord"]
            buy_sign = account_dict["BuySign"]
            account_type = account_dict["AccountType"]
            user_type = account_dict["UserType"]
            k12 = account_dict["K12"]
            role = account_dict["Role"]
            sin_out_list.append(account_dict)
            try:
                z = ZoomClientAutoTest(self.app_path, account_type, role, user_type, k12, username, password, buy_sign)
                res = z.run()
                sin_out_list.append(str(res))
            except Exception as e:
                sin_out_list.append(" %s: Error" % username)
            self.sinOut.emit(sin_out_list)  # 发射信号到主界面
        self.complete_sinOut.emit("True")

check_a_license.py

# coding: utf-8
import pytesseract
import os, re, sys, time
import cv2, psutil
from PIL import ImageGrab
from log_module import LogClass
from pywinauto import keyboard, mouse
from pywinauto.application import Application

parent_path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(parent_path)


class ZoomClientAutoTest(LogClass):
    """ZoomClientAutoTest"""

    def __init__(self, app_path, account_type, role, user_type, k12, username, password, buy_sign):
        """__init__"""
        LogClass.__init__(self, logName='ZoomClientAutoTest')

        self.setTimedRotatingFileHandler(fileName='ZoomClientAutoTest')  # 日志

        self.app_path = app_path  # zoom客户端路径

        self.img_path = parent_path + "\\IMG\\"  # 截图路径

        self.account_type = account_type
        self.role = role
        self.user_type = user_type  # 账号信息
        self.k12 = k12
        self.username = username
        self.password = password
        self.buy_sign = buy_sign

    def start_app(self):
        """start_app"""
        app = Application(backend="uia").start(self.app_path)  # 启动程序
        return app

    def get_PID(self, pName):
        """获取 zoom.exe PID"""
        p = psutil.process_iter()
        for r in p:
            aa = str(r)
            f = re.compile(pName)
            if f.search(aa):
                PID = aa.split('pid=')[1].split(',')[0]
                return PID
            else:
                return None

    def connect_app(self):
        """根据 zoom PID 绑定程序"""
        PID = self.get_PID("Zoom.exe")
        # app = Application().connect(path=self.app_path)  # 程序路径绑定,常用方法
        app = Application().connect(process=PID)  # 用于连接已经启动的程序,注意会改变
        # app = Application().connect(handle=0x100A6E)  # 应用程序的窗口句柄,注意会改变
        # app = Application().connect(title_re="Zoom*", class_name="ZPFTEWndClass")  # 标题、类型等匹配
        return app

    def click_before_login_sign_in(self, app):
        """click_before_login_sign_in"""
        dlg_spec = app.window(class_name='ZPFTEWndClass')  # 定位窗口
        dlg_spec.wait("exists ready", timeout=5, retry_interval=3)  # 等到窗口真的开着
        time.sleep(1)
        dlg_spec.window(title=r'Sign In', control_type="Button").click()  # 点击Button控件,进入登录界面

        return dlg_spec

    def login_app(self, dlg_spec, username, password):
        """login_app"""
        dlg_spec.wait("exists ready", timeout=5, retry_interval=3)  # 等到窗户真的开着
        time.sleep(1)
        keyboard.send_keys('^a^c')  # 全选并剪贴
        keyboard.send_keys(username)  # 输入用户名
        time.sleep(1)
        keyboard.send_keys('{VK_TAB}')  # 跳转输入框下一行
        keyboard.send_keys('^a^c')
        keyboard.send_keys(password)
        time.sleep(1)
        dlg_spec.window(title=r'Sign In', control_type="Button").click()

    def main_operation_zoom_interface(self, app, account_type, role, user_type, k12, username, password, buy_sign):
        zoom = app.window(class_name=r'ZPPTMainFrmWndClassEx')
        zoom.wait("exists ready", timeout=5, retry_interval=3)  # 等到窗户真的开着
        # zoom.print_control_identifiers()  # 打印详细的窗口信息

        zoom.window(title=r'最大化', control_type="Button").click()  # 注意定位到多个相同title,用control_type区分
        time.sleep(2)
        mouse.click(coords=(1895, 52))  # 鼠标点击头像  TODO:先写死
        time.sleep(2)

        coord = (1680, 585, 1885, 605)  # 截图  TODO:先写死
        im = ImageGrab.grab(coord)
        if os.path.exists(self.img_path) is False:
            os.makedirs(self.img_path)
        im.save(self.img_path + username + ".jpg")

        pytesseract.pytesseract.tesseract_cmd = parent_path + '\\Tesseract-OCR\\tesseract.exe'
        im = cv2.imread(self.img_path + username + ".jpg")
        im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)  # 灰度处理图片

        CONFIG = ("-l eng --oem 2 --psm 4")
        content = pytesseract.image_to_string(im_gray, config=CONFIG)  # 识别图片字母
        if str(buy_sign).lower().strip() == "no":
            if content == "":
                self.logger.info("AccountType:%s | Role:%s | UserType:%s | K12:%s | Username:%s | Password:%s |"
                                 " HaveBuyALicense?:%s | Content:%s | TestResult:%s" % (
                                     account_type, role, user_type, k12, username, password, buy_sign, content, "No"))
                result = " %s: Success" % username
            else:
                self.logger.error("AccountType:%s | Role:%s | UserType:%s | K12:%s | Username:%s | Password:%s |"
                                  " HaveBuyALicense?:%s | Content:%s | TestResult:%s" % (
                                      account_type, role, user_type, k12, username, password, buy_sign, content, "Yes"))
                result = " %s: Fail" % username
        else:
            if content == "":
                self.logger.error("AccountType:%s | Role:%s | UserType:%s | K12:%s | Username:%s | Password:%s |"
                                  " HaveBuyALicense?:%s | Content:%s | TestResult:%s" % (
                                      account_type, role, user_type, k12, username, password, buy_sign, content, "No"))
                result = " %s: Fail" % username
            else:
                self.logger.info("AccountType:%s | Role:%s | UserType:%s | K12:%s | Username:%s | Password:%s |"
                                 " HaveBuyALicense?:%s | Content:%s | TestResult:%s" % (
                                     account_type, role, user_type, k12, username, password, buy_sign, content, "Yes"))
                result = " %s: Success" % username
        time.sleep(2)
        mouse.click(coords=(1895, 520))  # switch account TODO:To be optimized
        time.sleep(2)
        app.kill()
        return result

    def run(self):
        s_app = self.start_app()  # 注意:如果软件未启动,启动程序之前将账号退出登录并关闭窗口
        # self.connect_app() # 如果软件已启动,连接已经启动的程序 TODO:连接失败,暂不用
        dlg_spec = self.click_before_login_sign_in(s_app)

        self.login_app(dlg_spec, self.username, self.password)  # 登录客户端
        res = self.main_operation_zoom_interface(s_app, self.account_type, self.role, self.user_type, self.k12,
                                                 self.username, self.password, self.buy_sign)

        return res

log_module.py

# coding: utf-8
import os
import sys
import datetime
import logging
from logging import handlers

parent_path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(parent_path)


class LogClass(object):

    def __init__(self, logName="ete"):
        self.logName = logName
        self.logger = logging.getLogger(self.logName)
        self.logger.setLevel(logging.DEBUG)
        self.now = datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d")

    def setTimedRotatingFileHandler(self, path=parent_path + "\\logs", fileName="ete"):
        if os.path.exists(path) is False:
            os.makedirs(path)
        self.fileFullPath = os.path.join(path, self.now + '_' + fileName + ".log")
        self.Handlers = handlers.TimedRotatingFileHandler(self.fileFullPath, when='midnight', interval=1,
                                                          backupCount=30)
        self.Handlers.setLevel(logging.DEBUG)

        self.formatter = logging.Formatter(
            '[%(asctime)s][%(filename)s][%(lineno)s][%(levelname)s] - [%(message)s]', "%Y-%m-%d %H:%M:%S")
        self.Handlers.setFormatter(self.formatter)

        self.logger.addHandler(self.Handlers)

        return self.logger

你可能感兴趣的:(PyQt5,Python,pyqt5,pywinauto,python)