JimyFengqi 2020-03-08
主要是为了练习下 面向对象, 不断提醒自己代码一定要写成 营销风格, 和优雅.
最近在B站上看一下关于 Python GUI 编程的内容. 恰好呢, 前不久的一个 将本地 Ecxcel 数据 发布到 Tableau Server 中, 当核心接口搞定后, 工程化领导让弄 web, 我们果断拒绝了, 不太熟前端也暂时不想学, 就用 Python 自带的 Tk 模块来写一版, 涉及安全就不能共享出来, 总体蛮简单的. 后来, 倒是引发我对 gui 的一点小兴趣. 就没事看看 PyQt5 的内容, 没事学学, 目的就是为了熟练 Pyhton 而已.
在 tk 中, 给控件绑定自定义函数, 通过 command 来实现, 还有 bind 等事件绑定. 跟 js 也是一样的. PyQt5 中, 被称为 信号与槽. 从开发的角度来看, 差不多的内容. 然后 我发现, 代码重构 是一个特别有趣, 特别有挑战的事情, 重构就意味着要 封装性更好, 代码质量高. 我是有这方面的洁癖, so, 总是喜欢不断优化, 追求完美.
这里取了一个B站视频的 小片段而已, 怎么把 乱七八糟的代码 进行 面向对象.
简化了哈, 就是要自己实现一个顶层窗口, 包含3个控件, 最大化, 最小化, 退出. (蛮简单的哈) 效果图如下
然后就照着写, 代码并不难, 就是控件的计算可能需要绕一点而已.
from PyQt5.Qt import * import sys # 1. 创建应用程序的对象 app = QApplication(sys.argv) # 接收命令行参数(整个项目都能用) # 2. 控件的操作 # 2.1 创建控件 # 去掉边框, 标题栏 # win = QWidget(flags=Qt.FramelessWindowHint) win = QWidget() # 去掉标题栏 + 设置透明度 win.setWindowFlags(Qt.FramelessWindowHint) win.setWindowOpacity(0.8) # 2.2 设置控件 win.setWindowTitle("顶层窗口操作-案例") win.resize(500, 300) top_margin = 10 btn_w = 80 btn_h = 30 # 添加3个子控件, 窗口的右上角 close_btn = QPushButton(win) close_btn.setText("关闭") close_btn.resize(btn_w, btn_h) # move_x: 窗口的宽度 - 控件的宽度 # close_btn_x = win.width()- close_btn.width() close_btn_x = win.width() - btn_w close_btn_y = top_margin close_btn.move(close_btn_x, close_btn_y) # 最大化按钮 max_btn = QPushButton(win) max_btn.setText("最大化") max_btn.resize(btn_w, btn_h) max_btn_x = close_btn_x - btn_w max_btn.move(max_btn_x, top_margin) mini_btn = QPushButton(win) mini_btn.setText("最小化") mini_btn.resize(btn_w, btn_h) mini_btn_x = max_btn_x - btn_w mini_btn.move(mini_btn_x, top_margin) # 监听: 信号与槽, 点击实现 最大化, 最小化, 关闭 close_btn.pressed.connect(win.close) def max_normal(): if win.isMaximized(): win.showNormal() # 变回普通 max_btn.setText("最大化") else: win.showMaximized() max_btn.setText("恢复") max_btn.pressed.connect(max_normal) mini_btn.pressed.connect(win.showMinimized) # 2.3 展示控件 win.show() # 3. 让主程序进入死循环 (tk 里面的 mainloop) if __name__ == '__main__': sys.exit(app.exec())
其实写到这里, 就会发现, 随着控件的增多, 代码逻辑其实就会混乱. 代码跟函数 放混在一起, 这是绝不能这样做的. 不然后面就维护起来非常困难了. 因此才需要重构.
其实就是将这个 业务过程的代码, 封装为面向对象, 阅读体验更好和维护更加容易.
最为直接 和 最为最要的一点就是, 封装类的结构设计. 这里首先就是要对 设计全局的 QWidget 对象进行封装, 我们全程需要用到它, 而且又有新的功能扩充或者交互, 因此, 我们首先设计的类, 必须要 继承 , 然后新功能则 重写.
OOP
掌握 继承, 就是拥有父类的所有功能, 当然, 理解 self 的本质 和 super() 也是很关键的. self 还好, 就是一个形参, 表示类对象自身嘛(属性和方法) ;
理解 super( ) 从功能上, 修复了 类搜索的 "钻石调用问题", 采用了其 mro 顺序搜索. (本质是 广度优先哦) 不清楚可以 看看之前的记录:
多继承 MRO: https://www.cnblogs.com/chenjieyouge/p/12246113.html
from PyQt5.Qt import * import sys class Window(QWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setWindowTitle("顶层窗口操作") self.resize(500, 300) # 去掉标题栏 + 设置透明度 self.setWindowFlags(Qt.FramelessWindowHint) self.setWindowOpacity(0.8) # 1. 创建应用程序的对象 app = QApplication(sys.argv) # 接收命令行参数(整个项目都能用) # win = QWidget() win = Window() # 先省略了后面的代码.
这种继承再重写, 追加的思维, 真的蛮用得很普遍的. 很优雅. 但我的数据工作呢. 其实更多是过程, 脚本..只能在博客上来练习面向对象...
class Window(QWidget): def __init__(self, *args, **kwargs): # 为了直接表示, 省略了这里的代码. # 控件相关 self.setup_ui() def setup_ui(self): # 公共数据 (全局变量) top_margin = 10 btn_w = 80 btn_h = 30 # 控件布局 # 添加3个子控件, 窗口的右上角 close_btn = QPushButton(self) close_btn.setText("关闭") close_btn.resize(btn_w, btn_h) close_btn_x = self.width() - btn_w close_btn_y = top_margin close_btn.move(close_btn_x, close_btn_y) # 最大化按钮 max_btn = QPushButton(self) max_btn.setText("最大化") max_btn.resize(btn_w, btn_h) max_btn_x = close_btn_x - btn_w max_btn.move(max_btn_x, top_margin) mini_btn = QPushButton(self) mini_btn.setText("最小化") mini_btn.resize(btn_w, btn_h) mini_btn_x = max_btn_x - btn_w mini_btn.move(mini_btn_x, top_margin) # 监听: 信号与槽, 点击实现 最大化, 最小化, 关闭 close_btn.pressed.connect(self.close) def max_normal(): if self.isMaximized(): self.showNormal() # 变回普通 max_btn.setText("最大化") else: self.showMaximized() max_btn.setText("恢复") max_btn.pressed.connect(max_normal) mini_btn.pressed.connect(self.showMinimized)
from PyQt5.Qt import * import sys class Window(QWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setWindowTitle("顶层窗口操作") self.resize(500, 300) # 去掉标题栏 + 设置透明度 self.setWindowFlags(Qt.FramelessWindowHint) self.setWindowOpacity(0.8) # 公共数据 (全局变量) self.top_margin = 10 self.btn_w = 80 self.btn_h = 30 # 控件相关, 一定要放在最后哦 self.setup_ui() def setup_ui(self): # 控件布局 # 添加3个子控件, 窗口的右上角 close_btn = QPushButton(self) self.close_btn = close_btn # 临时局部变量 close_btn.setText("关闭") close_btn.resize(self.btn_w, self.btn_h) # 最大化按钮 max_btn = QPushButton(self) self.max_btn = max_btn max_btn.setText("最大化") max_btn.resize(self.btn_w, self.btn_h) mini_btn = QPushButton(self) self.mini_btn = mini_btn mini_btn.setText("最小化") mini_btn.resize(self.btn_w, self.btn_h) # 监听: 信号与槽, 点击实现 最大化, 最小化, 关闭 close_btn.pressed.connect(self.close) def max_normal(): if self.isMaximized(): self.showNormal() # 变回普通 max_btn.setText("最大化") else: self.showMaximized() max_btn.setText("恢复") max_btn.pressed.connect(max_normal) mini_btn.pressed.connect(self.showMinimized) def resizeEvent(self, QResizeEvent): close_btn_x = self.width() - self.btn_w close_btn_y = self.top_margin self.close_btn.move(close_btn_x, close_btn_y) max_btn_x = close_btn_x - self.btn_w self.max_btn.move(max_btn_x, self.top_margin) mini_btn_x = max_btn_x - self.btn_w self.mini_btn.move(mini_btn_x, self.top_margin) app = QApplication(sys.argv) win = Window() win.show() if __name__ == '__main__': sys.exit(app.exec())
达到非常容易阅读, 和易维护的工程代码.
咱说呢, 我感觉, GUI 用 纯Pyhthon 来写, 感觉还可以哦, 虽然现在 web 是主流, 但我感觉, gui 反而让我更加有成就感一点. 后面也适当记录一波学习的过程. 虽然都是搬砖, 但理解是不同的.