1. 配置Pycharm外部工具

  1. 点击Settings
  2. 找到External Tools 即,外部工具,点击右侧面板的加号添加
  3. 输入工具名称NameProgram填写外部工具程序的exe地址,Arguments填写数据参数,Working directory填写文件的工作地址
  4. Pycharm提供了宏命令,点击加号可以查看Macros为宏命令,底下的Macro preview可以查看当前宏命令的内容

使用pycharm外部文件命令,唤出Qt Creator打开对应的ui文件

配置Qt Creator

Program: D:\qt562\Tools\QtCreator\bin\qtcreator.exe

Arguments: $FilePath$

Working directory: $FileDir$

参数解释:

使用安装好的Qt Creator软件,输入参数(Argument)即文件地址,使用Qt Creator软件打开当前的文件

配置PyUIC

PyUIC的作用是将ui文件转换为py文件

Program: D:\Anaconda3501\envs\python368\python.exe

Arguments: -m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py

Working directory: $FileDir$

参数解释:

程序调用python文件,执行python.exe -m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py表示使用PyQt5.uic.pyuic命令,执行当前文件名,-o输出的地址为$FileNameWithoutExtension$.py,即将$FileName$文件转换为python文件

2. Qt Creator命令

找到Qt Creator的位置,执行命令qtcreator.exe -h可以看到程序的命令介绍

3. Qt Designer命令

qt designer是python的一个库文件,位置一般在python385\Lib\site-packages\qt5_applications\Qt\bin

找到Qt Designer的位置,执行命令designer.exe -h可以看到程序的命令介绍

4. 对话框居中

dialog = QtWidgets.QDialog(MainWindow)
d = Ui_Dialog()
d.setupUi(dialog)

# 获取屏幕的尺寸
screen = QApplication.desktop()
# 获取对话框的尺寸
dialog_size = dialog.geometry()
# 计算对话框的位置
x = (screen.width() - dialog_size.width()) // 2
y = (screen.height() - dialog_size.height()) // 2
# 设置对话框的位置
dialog.setGeometry(x, y, dialog_size.width(), dialog_size.height())
# 显示对话框
dialog.exec_()

5. QListWidgetItem组件添加数据

item = QtWidgets.QListWidgetItem()  # 实例化list item

item.setData(1, 'SYS01') # 设置item的数据项,表示为item对象的第一个位置的值为'SYS01'

item.setData(2, 1) # 表示为item对象的第二个位置的值为1

item.setData(3, {
'id': 1,
'name': 'test'
}) # 表示为item对象的第三个位置的值为字典 {'id': 1, 'name': 'test'}

item.setText(_translate("Dialog", "微易邮箱配置")) # 设置这项的文本内容

6. QListWidget添加右键自定义菜单

listWidget_2添加邮件自定义菜单

def __init__(self):
"""
"""
self.listWidget_2.setContextMenuPolicy(Qt.CustomContextMenu) # 配置List的上下文菜单策略
self.listWidget_2.customContextMenuRequested.connect(self.show_email_context_menu) # 邮箱配置右键菜单

def show_email_context_menu(self, position):
"""
自定义右键邮件配置菜单
:return:
"""
menu = QMenu()
# 修改菜单
update_action = QAction("修改", menu)
update_action.triggered.connect(self.open_update_email_config) # open_update_email_config需要自己实现
menu.addAction(update_action)
# 删除菜单
delete_action = QAction("删除", menu)
delete_action.triggered.connect(self.delete_email_config) # delete_email_config需要自己实现
menu.addAction(delete_action)

menu.exec_(self.listWidget_2.viewport().mapToGlobal(position))
# 表示该菜单运行在listWidget_2组件上

7. QT开发服务流程

Dao层、Service层、Controller层

Dao层主要与数据库交互

Service层主要实现业务逻辑

Controller层主要处理业务层所需要的数据

Dao层出错

Dao层遇到错误,应该将其抛到Service层

Dao层

class EmailConfigDao:
def __init__(self):
pass

def select_all_email_config(self):
"""
查询所有邮箱配置
"""
# 实现查询逻辑
sql = """
SELECT *
FROM EmailConfig
"""
res = None
try:
res = db.fetch_all(sql)
except Exception as err:
print(err)
raise err
return res

Service层

class EmailConfigService:
"""
邮箱配置Service层
"""

def __init__(self):
# 初始化
self.email_config_dao = EmailConfigDao() # 邮件请求队列Dao

def select_all_email_config(self):
"""
查询所有邮箱配置
"""
# 实现查询逻辑
res = None
try:
res = self.email_config_dao.select_all_email_config()
except Exception as err:
print(err)
return False, "获取数据失败"
return JsonUtil.database_data_to_list_json(res)

8. 启动QT的同时启动FLask

可以在 PyQt 的主线程中启动 Flask 服务,但是为了避免阻塞 GUI,需要再线程中启动 Flask 服务。同时,当 PyQt 应用退出时,也可以关闭 Flask 服务。

class FlaskThreading(Thread):
"""
启动Flask线程
"""

def __init__(self):
Thread.__init__(self)
# 初始化
self.srv = None
self.app = app
self.ctx = self.app.app_context()
self.ctx.push()

def run(self):
"""
启动线程
:return:
"""
try:
self.srv = make_server(HOST, PORT, self.app)
print("Flask正在运行...")
self.srv.serve_forever()
except Exception as err:
print("启动错误【%s】" % str(err))

def shutdown(self):
"""
关闭线程
:return:
"""
try:
self.srv.shutdown()
print("成功关闭Flask")
except Exception as err:
print("关闭错误【%s】" % str(err))
def shutdown_flask():
"""
关闭Flask线程
:return:
"""
flask_threading.shutdown()

if __name__ == '__main__':
# 初始化Sqlite
init_table()

# 初始化窗口
qt_app = QApplication(sys.argv)
my_main_win = Main()
my_main_win.show()

# 定义窗口退出事件响应
qt_app.aboutToQuit.connect(shutdown_flask)

# 启动Flask服务
flask_threading.start()

# 启动Qt
sys.exit(qt_app.exec())

9. ui文件与逻辑代码分离

新建UI文件夹,implement为对应ui文件的逻辑文件。

可以使用继承,通过self访问父级属性

from PyQt5.QtWidgets import QDialog
from service.EmailConfigService import EmailConfigService
from UI.emailConfig import Ui_Dialog
from UI.component.Message import Message # 重点
from common.Validate import Validate


class EmailConfig(QDialog, Ui_Dialog): # 重点
"""
邮件配置窗口
"""

def __init__(self, call_back=None, config_mode='add'):
super(EmailConfig, self).__init__()
self.setupUi(self)
self.setWindowTitle("邮箱配置")

# 初始化
self.call_back = call_back
self.config_mode = config_mode # add:表示添加框;update:表示更新框
self.data = {} # 数据
self.email_config_service = EmailConfigService()

# 定义事件
self.pushButton.clicked.connect(self.save) # 保存
self.pushButton_2.clicked.connect(self.cancel) # 取消

10. 主窗口

同样使用继承

class Main(QMainWindow, Ui_MainWindow):
def __init__(self):
super(Main, self).__init__()
self.setupUi(self) # 重点

# 初始化
self.setWindowTitle("邮件和短信服务")

# 初始化变量
self.exportConfigDialog = None # 配置页面

# 定义事件
self.action.triggered.connect(self.open_config)

def open_config(self):
"""
打开配置导航栏
:return:
"""
self.exportConfigDialog = ConfigDialog()
self.exportConfigDialog.exec()

打开窗口的写法

self.exportConfigDialog = ConfigDialog()
self.exportConfigDialog.exec()

启动窗口

if __name__ == '__main__':
# 初始化窗口
qt_app = QApplication(sys.argv)
my_main_win = Main()
my_main_win.show()
# 启动Qt
sys.exit(qt_app.exec())

11. 定时器

场景:两个线程同时使用一个日志类,该日志类会使用qt的TextBrowser将日志输出。如果日志数量太多,就会导致qt界面卡顿、闪退

解决方案:使用Qt定时器,将日志内容分段输出,避免使用线程的同时操作Qt

class MyLoggerInfo(MyLogger):
text_browser = None # 日志信息框
lock = threading.Lock() # 线程锁
log_cache = [] # 缓存日志

def __init__(self):
super().__init__()

@staticmethod
def set_text_browser(text_browser):
"""
设置日志信息框
:param text_browser:
:return:
"""
MyLoggerInfo.text_browser = text_browser

def print_log(self, msg, level='INFO'):
"""
输出日志
:param msg: 日志信息
:param level: 日志等级
:return:
"""
with self.lock:
text_log = super().print_log(msg, level)
MyLoggerInfo.log_cache.append(StringUtils.get_formate_info_color(msg, level))
return text_log

@staticmethod
def display_logs():
"""
用于分段输出日志
:return:
"""
show_logs_num = 10 # 每次输出最大日志数
to_show = MyLoggerInfo.log_cache[:show_logs_num] # 需要输出的日志
MyLoggerInfo.log_cache = MyLoggerInfo.log_cache[show_logs_num:] # 清除已输出的日志
for log_msg in to_show:
if MyLoggerInfo.text_browser is not None:
# textBrowser显示日志内容
MyLoggerInfo.text_browser.append(log_msg)
# 添加完提示框之后,自动向下滑
MyLoggerInfo.text_browser.verticalScrollBar().setValue(
MyLoggerInfo.text_browser.verticalScrollBar().maximum())
if __name__ == '__main__':
# 初始化Sqlite
init_table()

# 初始化窗口
qt_app = QApplication(sys.argv)
my_main_win = Main()
timer = QTimer()

my_logger_info = MyLoggerInfo() # 日志对象
MyLoggerInfo.set_text_browser(my_main_win.textBrowser_2)

timer.timeout.connect(my_logger_info.display_logs) # 绑定日志显示函数
timer.start(1000)

# 定义窗口退出事件响应
qt_app.aboutToQuit.connect(my_main_win.shutdown_threading)

# 开启线程
my_main_win.open_threading()

my_main_win.showMaximized()
my_main_win.show()
# 启动Qt
sys.exit(qt_app.exec())
  1. 在该日志类添加一个缓存日志log_cache,使用静态是为了保证所有的日志都在该缓存日志里
  2. 在该日志类添加一个输出日志方法display_logs,用于展示日志信息
  3. 在主函数中,实例化定时器,将display_logs方法绑定到该定时器中