【没钱买ps,PyQt自己写】Day 28 - final project - 1 / 来搞一个自己的 photoshop 吧!UI 篇 + 纯程序架构篇 (结合 PyQt + OpenCV)

看完这篇文章你会得到的成果图

此篇文章的范例程序码 github

https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day28_final_project_day1

之前内容的重点复习 (前情提要)

我们接下来的讨论,会基於读者已经先读过我 day5 文章 的架构下去进行程序设计
如果还不清楚我程序设计的逻辑 (UI.py、controller.py、start.py 分别在干麻)
建议先阅读 day5 文章後再来阅读此文。

https://www.wongwonggoods.com/python/pyqt5-5/

设计我们的 UI

虽然原本我有想直接拿 Day17 的内容继续改,
後来觉得架构上还不够漂亮,最後决定乾脆直接砍掉重练比较快哈哈哈。

既然要搞就一次搞到最大吧!

这次我设计的 final project UI 初版长这样

欸等等先别吐血啊!!! 这 UI 不是一天搞出来的啊!!
UI 就请大家自己慢慢刻哈哈哈哈哈!!!

(你说突然这也突然跳太高阶了吧?!
其实只是设定一些颜色而已XD,之前因为这个相对简单就没特别介绍)

排版控制 Layout 系列

如果还有机会的话有空在介绍,不过这边的内容多半是用於排版,
所以也难能以单一例子举出 demo 在干嘛,
总之可以自己玩玩看 Layouts 这一块,
先「拉一个 Layout」,再把「要排版的元件丢进去」,就对自动对整齐了!

  • Layouts 系列:

设定字体颜色,背景颜色的地方位於 styleSheet

使用的应该是类似 css 语法? 我不确定XD
总之有兴趣可以直接参考下面的延伸阅读进行修改,
设定位置在 Qt desinger 的这边

延伸阅读:给想研究更多怎麽设定颜色的人,请参考 Qt Style Sheets Examples

设定 Logo 的地方

Qlabel 就是一个我们可以输入图片的地方,
透过右边的「...」,选择一张我们要的图片,就可以直接把 LOGO 嵌入 UI 中了。

执行看看 UI.py 画面是否如同我们想像

一样,这程序只有介面 (视觉上的呈现),没有任何互动功能

  • 看看我们制作出来的介面
python UI.py

赞到不行XDDDD (自己讲

程序架构篇

这次的 final project 真的内容会做太多,如果没有好好规划一下架构,
很有可能写出来的东西会一团乱XDDD
所以我们要先趁今天来规划,明天再来慢慢把功能实现

先从大方向切入,我们有那些大的「重要元素」?

依照 day5 的设计,我们至少也会有

  • UI (作为前端显示介面)
  • controller (做为後端逻辑控制)
  • start (启动用,没什麽好讨论的)

而 UI 因为我们在 Qtdesigner 已经设计得够复杂了,也不太需要另外写程序,
所以基本上在经由 pyuic 的转换後,就已经完成了 UI.py

pyuic5 -x day28.ui -o UI.py

controller 是我们这边要讨论的重点,在我们的 photoshop 後端逻辑里面,
我之前在 day17 实作的最终版是把「图像+图像变化方法」写在同一个 class 当中,
我们如果继续让我们的「变化方法增加」,不适不能这样实作,
但最终我们会有一个超级巨大的 class 共同实作 「图像+图像变化方法」,
於是我决定把「变化方法」拆出去独立一个 class,
使得「图像」与「变化方法」分开成两套独立机制,而能互向协助。

这也符合 design pattern 的 Interface Segregation Principle(ISP) 介面隔离原则
以游戏来说,我们也可以举例:
我们大可以把一个角色的所有「属性、装备、技能」全部都包在这个角色当中,
但如果另外一个玩家也玩了同一个职业,我们何必把技能「整个复制进去另外一个角色当中呢?
於是我们可以把「技能」这个介面分离出去,让有需要的类别再去呼叫这个介面即可。

因此,这时候就能透过这样的拆分方式,把我们实作的弹性与扩充功能的弹性提升。

  • 举例:

套用 design pattern 前

套用 design pattern 前,基本上这个设计没什麽问题,
硬要说缺点只是维护职业技能时,「需要一个个角色去调整内部的技能

套用 design pattern 後 (使用 Interface Segregation Principle(ISP) 介面隔离原则)

套用 design pattern 的 Interface Segregation Principle(ISP) 介面隔离原则後,
我们把技能独立出来维护,这样的好处就是我们可以调整一次职业技能,
所有的角色都能得到修正!

而这边我们也要做同样的事情,我们要把「修改图片的方法」这个介面独立出来!

於是整理一下,得到我们目前想设计的架构

独立「图片本身」与「图片处理方法」,
另外因为滑鼠事件的资讯比较特别,需要从图片上获取,所以我们也另外独立出来处理

【重要】抓出谁会是触发事件的 trigger

第一次设计这个系统的时候,就碰到一个最关键的问题,
没有提早先想好架构就开始写,导致绕了很多弯路」,

应该要先想好再下去写的问题就是,在这个系统里面,「什麽事情会是触发事件的 trigger?

如果我们不知道「什麽事件会被使用者触发」,
就不知道要从哪个点开始启动後续的修改」,

我一开始就是疯狂地开始写功能XDD,然後没注意谁呼叫谁,
结果功能都有了,但是 trigger 位置写在错误的 class,
所以要重搞顺序XDD

以结论来说,这个系统的 trigger 位置如下

所以在写系统时,我们需要特别注意,
我们箭头指的地方就是「会触发事件的 trigger」,
凡经过此处的程序都是「一次修改的起点」,引导我们进行往後的修改。

图形修改介面 (interface) 设计

基本上我们设计的「修改图片」都有符合一些同样的原则,
我们可以使用「介面继承」的方式来实现,
为了达到这样的效果,我们需要 「import abc」 这个 python 酷酷的 library,
细节的部分我会再另外写一篇文章,这边我们先直接实作。

注:冷知识(?) abc = the infrastructure for defining 「abstract base classes (ABCs)」 in Python
简单来说,就是「抽象的类别 (class)」定义

我们预计设计的架构如下,一共会有三层,底下会一层层介绍:

介面的大祖宗 (method_interface),一切介面方法从此开始实作

我们可以大致归纳我们全部的变化介面都会有两个基本的要素:

  • 初始化参数:__init__
  • 更新图片: update_img

另外,我们强制这两个介面在继承後都必须被定义,
所以我们透过「@abc.abstractmethod」,定义这个抽象的方法 (未实作功能),
以及「return NotImplemented」,如果使用者继承介面後未定义这个函数,
会跳 「NotImplemented error」,强制跳错 (逼使用者一定要定义介面内容)。

import abc

class method_interface(abc.ABC):
    @abc.abstractmethod
    def __init__(self):
        return NotImplemented

    @abc.abstractmethod
    def update_img(self):
        return NotImplemented 

介面的父母 (滑条方法介面 slider_method_interface, 画笔方法介面 pen_method_interface)

我们会实作的「图像变化方法」,能够产生变化的方法大概有两种,
一种是滑条类,另一种是画笔类。

这两类在实作时,都会继承我们的介面大祖宗 (method_interface),
然後再分别基於滑条的特性与画笔的特性,实作各自的介面。

这所有的介面方法都属於「图片变化方法」,所以我们会强烈推荐用继承的方法撰写。
会比起一个个慢慢定义有更好的架构与维护性
(维护一个父类别,也许底下的所有子类别都能全部受惠到这次的更动。不用一个个慢慢改。)

最大的差别就是:

  • 滑条拉回去後,可以复原 (不会是破坏原图的变动)
  • 画笔画下去後,不可复原 (会破坏原图的变动)

因此这两个方法我们要分开实作,实作细节一样我们明日再提,
今天光是讲大架构就已经够累了。

各个细部的方法实作

在看该方法是「滑条方法介面 slider_method_interface」或是「画笔方法介面 pen_method_interface」後,
我们就可以开始实作细部功能了,这部分的实作细节我们就明日在提吧。
(今天光是讲完大架构相信大家已经都累了XDDD)

最终系统架构图

这个是旧版,滑鼠那部分因为是新加的,目前还没更新上去,
不过相信上面已经说明得相当完整了,应该能大约类推出他在图形上会呈现的细节

Reference


★ 本文也同步发於我的个人网站(会有内容目录与显示各个小节,阅读起来更流畅):【PyQt5】Day 28 final project - 1 / 来搞一个自己的 photoshop 吧!UI 篇 + 纯程序架构篇 (结合 PyQt + OpenCV)


<<:  【Day32】[演算法]-内插搜寻法Interpolation Search

>>:  30天学会Python语言: Day 27-时间管理大师

[Day 26] review 一下我们的程序,谈谈 DSL 和 DAO 的差异

前面讲了很多 Kotlin Exposed 框架使用的方式。 今天来讲点观念性的东西,谈谈 Expo...

[想试试看JavaScript ] 各种事件处理 (二)

事件种类 事件处理是由各个浏览器提供的功能,然後我们再去呼叫出来使用,所以随着浏览器版本的更新,一些...

APP 开发 组别

APP 开发 组别 https://wolkesau.medium.com/app-开发-组别-49...

Vue.js 从零开始:SPA怎麽改善SEO呢? MVC与关注点分离又是什麽?

上一篇讲到SPA的缺点,Vue是用JvaScript载入後台的数据,并且动态产生元件,SEO只能抓取...

建立AWS RDS

接下来後半段实作SQL. 这边使用的是MS SQL. 由於安全性原则, 先建立一个Security ...