代码重构(OOP)-小栗子(PyQt5)

主要是为了练习下 面向对象, 不断提醒自己代码一定要写成 营销风格, 和优雅.

最近在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

最为直接 和 最为最要的一点就是, 封装类的结构设计. 这里首先就是要对 设计全局的 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()

# 先省略了后面的代码.

这种继承再重写, 追加的思维, 真的蛮用得很普遍的. 很优雅. 但我的数据工作呢. 其实更多是过程, 脚本..只能在博客上来练习面向对象...

重构 __ init __ 与 ui 连接

  • 将控件都封装在 setup_ui () 中, 并 将其置与 __ init __()
  • 将 win 全部替换为 self
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)

重构 - 全局变量

  • 将用到的全局变量, 给 self 绑定, 作为类的一部分
  • 修复
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 程序来练习代码重构, 类的封装, 继承 (重写, 追加) 等操作
  • 加深的 self 和 super() 从本质上去理解, 非常关键对于面向对象
  • 锻炼下代码风格, 在功能实现的基础上, 如何增加可读性, 和鲁棒性, 易维护等. 我觉得更为重要.

咱说呢, 我感觉, GUI 用 纯Pyhthon 来写, 感觉还可以哦, 虽然现在 web 是主流, 但我感觉, gui 反而让我更加有成就感一点. 后面也适当记录一波学习的过程. 虽然都是搬砖, 但理解是不同的.

原文地址:https://www.cnblogs.com/chenjieyouge/p/12444940.html

时间: 2024-10-27 13:33:06

代码重构(OOP)-小栗子(PyQt5)的相关文章

最实用的代码重构小技巧排行榜TOP10

这次我们抛开JAVA虚拟机源码这些相对底层的东西,来与各位探讨一下几个代码重构的小技巧,这些内容部分来自于书籍当中,部分来自于项目当中的一些实践经验.如果猿友们曾经用过这种手法,也不妨参与到文章的留言当中,将你的小心得.小体会共享与他人,也可以拿来冲击LZ自己定义的排行榜,LZ不甚欢迎. 重构的手法有很多种,相对而言,一篇文章的涵盖量自然是无法提到所有,LZ这里也只能提出一些平时会经常使用的一些手法,像一些比较高端的手法,各位有兴趣的可以去找一些专门的书籍涉猎. 本文部分重构小技巧可能主要与JA

CSS代码重构

CSS代码重构的目的 我们写CSS代码时,不仅仅只是完成页面设计的效果,还应该让CSS代码易于管理,维护.我们对CSS代码重构主要有两个目的:1.提高代码性能2.提高代码的可维护性 提高代码性能 提高CSS代码性能主要有两个点:1.提高页面的加载性能提高页面的加载性能,简单说就是减小CSS文件的大小,提高页面的加载速度,尽可以的利用http缓存2.提高CSS代码性能不同的CSS代码,浏览器对其解析的速度也是不一样的,如何提高浏览器解析CSS代码的速度也是我们要考虑的 提高代码的可维护性 提高CS

CSS代码重构与优化之路

写CSS的同学们往往会体会到,随着项目规模的增加,项目中的CSS代码也会越来越多,如果没有及时对CSS代码进行维护,CSS代码不断会越来越多.CSS代码交错复杂,像一张庞大的蜘蛛网分布在网站的各个位置,你不知道修改这行代码会有什么影响,所以如果有修改或增加新功能时,开发人员往往不敢去删除旧的冗余的代码,而保险地增加新代码,最终的坏处就是项目中的CSS会越来越多,最终陷入无底洞. CSS代码重构的目的 我们写CSS代码时,不仅仅只是完成页面设计的效果,还应该让CSS代码易于管理,维护.我们对CSS

让代码重构渐行渐远系列(3)——string.Equals取代直接比较与非比较

重构背景及原因 最近由于项目组的人员在不断扩充,导致项目中代码风格各异,大有百花齐放甚至怒放之势.考虑到团队的生存与发展,经过众人多次舌战之后,最终决定项目组根据业务分成几个小分队,以加强团队管理与提高效率,同时也能培养阶梯人才.各个小分队为了“统一”代码风格,提高成员的代码能力以便最终能提高项目代码质量,减少以后的维护成本,最终决定“每日”进行小组内的代码走查/审查(Code Review),然后进行代码重构. 直接比较与非比较:我所谓的直接比较与非比较是指"=="判断方式与&quo

我的代码重构经验

说明 本文在<MDU某产品OMCI模块代码质量现状分析>一文的基础上,分享作者对该模块进行重构时的实践经验. 具体的重构手段可参考<代码大全2>或<重构:改善既有代码的设计>,本文不再班门弄斧,而侧重重构时一些粗浅的“方法论”,旨在提高重构效率. 作者未采用重量级的重构工具,仅用到Source Insight的”Smart Rename”功能.也未使用CUnit等单元测试工具,而是通过在线调测和自动化测试保证代码的正确性. 一 背景 MDU系列产品从他处接手,OMCI模

关于代码重构

最近几天实习做需求,很多都是代码优化,代码重构方面的,有必要阅读相关的文章或书籍,整理整理形成点小方法论指导受用. 相关不错的文章:代码重构之道 代码重构方向原则指导 重构代码的7个阶段 书籍——<重构:改善既有代码的设计> 可以在哪些方面对代码进行重构: 1.重命名:对类,接口,方法,属性等重命名,以使得更易理解 2.抽取代码:将方法内的一段代码抽取为另一个方法,以使得该段代码可以被其他方法调用,这是重构中很重要很常用的,此举可以极大的精炼代码,减少方法的代码行数 3.封装字段:将类的某个字

代码重构方向原则指导

http://www.aqee.net/hill-climbing-wonkish/ 重构是一种对软件进行修改的行为,但它并不改变软件的功能特征,而是通过让软件程序更清晰,更简洁和更条理来改进软件的质量.代码重构之于软件,相当于结构修改之于散文.每次人们对如何对代码进行重构的讨论就像是讨论如果对一篇文学作品进行修订一样无休无止.所有人都知道应该根据项目的自身情况来对代码进行重构,而重构是无止境的.莫扎特从来不不对他的作品进行修订,特罗洛普对自己作品修订的恰到好处,大多数作家认为他们俩这样做都是合

关于代码重构:是微修还是全部推倒重来

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 虽然不是很切题但还是放在Cocos2D的学习系列博文中吧,因为这是我写cocos2D代码中体会到的. RPG游戏码代码到现在已经写了不少行代码了. 最近在加入新功能的时候发现以前遗留的人物对话问题一直没有解决,游戏对话逻辑是RPG中重要的逻辑,而我的代码问题具体表现在: 对话代码逻辑及其复杂.因为以前从来没有写过类似的代码,要想支持游戏剧情的复杂性,必须将对话

何时进行代码重构?

很多人可能会觉得项目初期的时候,可以先不考虑重构以及优化, 正如我当前这个项目就是这样做的,但是,最终的结果是什么?出现了一种比较悲剧的场面, 面对着复杂的类.函数.基类与派生类之间的耦合度过高,派生类的重复性,内聚性高低,都慢慢的暴露了出来,当项目完成之后,在想着重构的时候,会发现连自己写的东西都懒得动了.为什么会导致这样? 原因就是前期没有注重它(重构). 何时进行重构,如果进行重构? 如何进行重构?这个就是一个说不完的话题了,在这里先说说何时进行重构 重构的意思是在不改变程序原有功能基础上