另外作者的博客主页也找到了,放在下面,很优秀的一位学长,嘿嘿。
二、小罗碎碎念
首先申明,这个项目并不是我开发的,原作的链接已经放上面了,我写这篇文章的目的是想借鉴一下作者的开发思路,方便以后我也设计类似的轻量级应用。
这个项目已经有可以直接使用的APP了,大家点击链接直接下载,解压以后点击这个鼠鼠头像即可直接启动应用。
然后你在回收站就可以看到被删除的文件了,这一步是为了避免个别文件被误删,你找到对应的去恢复就好了。
源码解析
————————————
好的,爱折腾的同学可以接着看下面的内容,可能需要一丢丢代码基础,大家根据自己的情况自行决定。还是那句话,有问题可以随时与我联系。
首先我们从GitHub把源代码下载下来,解压以后会看到这些文件夹。
我们逐个来分析一下。
1、requirements
注意:原则上开始一个项目,第一个要看的文档是readme,但是我已经看过了,并且觉得没啥有价值的信息,所以就跳过了,想看的,直接用记事本打开就行了,不用去折腾专门看markdown文件的APP。
打开这个文本文档,发现里面非常简单。
这些是一些Python库的版本信息:
- Send2Trash 1.5.0
- PyQt5 5.13.0
- python_dateutil 2.7.5
这些库用于不同的用途:
- Send2Trash是一个用于**将文件或文件夹移动到操作系统回收站**的库。
- PyQt5是一个用于**创建图形用户界面(GUI)应用程序的库,它是Python绑定Qt框架的一部分**。
- python_dateutil是一个用于**解析、操作和计算日期和时间的库**,它提供了许多方便的日期和时间处理功能。
如果你需要使用这些库,你可以使用pip命令来安装它们。例如,要安装Send2Trash 1.5.0,可以运行以下命令:
pip install Send2Trash==1.5.0
类似地,你可以使用相应的命令来安装PyQt5和python_dateutil库。请注意,版本号是可选的,如果不指定版本号,pip将会安装最新版本的库。
2、main
2-1:前期准备
接下来我们用Pycharm打开我们的main文件,也是看看我们的主程序是如何设计的。这里建议直接将整个项目导入Pycharm,方便后期调用程序。创建新项目的时候,会让你选择编译器,记得选择自己常用的就可以,最好是你之前已经创建好的虚拟环境,不然后续可能会频繁报错。
听不懂上面那段话也没关系,走一步看一步也很好,哈哈,遇到一个问题再一个个去解决就好了。比如我现在就遇到了一个问题,这里的意思是版本过低了,所以我们手动来安装一下。
我们先进入主程序的界面,然后会发现最上方会跳出提示栏。
直接点击安装要求即可进入以下界面。
尽管还是报错,但是我们得到了一个有用的信息。
接下来我们打开Prompt。
需要注意的是,别直接复制Pycharm提供的代码,不然还是会提示你版本不匹配,这里我们直接版本号去掉就行了。
2-2:导入相关的库文件
首先把整体代码贴过来,算了,尝试了一下,还是不了,因为太长了(手动狗头)。我还是分块解释吧。
import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsDropShadowEffect, QListWidgetItem, QListView, QWidget, \ QLabel, QHBoxLayout, QFileDialog from PyQt5.QtCore import Qt, QPropertyAnimation, QEasingCurve, QThread, pyqtSignal, QMutex, QSize, QEvent, QPoint, QTimer from PyQt5.QtGui import QMouseEvent, QCursor, QColor from PyQt5.uic import loadUi
这段代码使用了PyQt5库来创建图形用户界面(GUI)应用程序。它导入了一些PyQt5的模块和类,并使用了一些常用的功能和特性。
其中,import sys
是导入sys模块,它提供了与Python解释器和运行环境交互的功能。
from PyQt5.QtWidgets
导入了一些QtWidgets模块中的类,用于创建GUI界面的各种组件,如应用程序、主窗口、标签、布局等。from PyQt5.QtCore
导入了一些QtCore模块中的类,用于处理Qt的核心功能,如事件处理、动画效果、线程等。from PyQt5.QtGui
导入了一些QtGui模块中的类,用于处理GUI界面的图形相关功能,如鼠标事件、光标、颜色等。from PyQt5.uic
导入了loadUi函数,它用于加载UI文件,将设计的GUI界面与代码进行关联。
这段代码中还使用了一些其他的Python模块和函数,如QFileDialog
用于打开文件对话框。
from pathlib import Path, PureWindowsPath from dateutil import relativedelta import utils.resources import os, datetime, time, re, math, shutil, json
这段代码导入了一些Python模块和函数:
from pathlib import Path, PureWindowsPath
导入了pathlib
模块中的Path
和PureWindowsPath
类,用于处理文件路径和操作系统相关的路径操作。from dateutil import relativedelta
导入了dateutil
模块中的relativedelta
类,用于处理日期和时间的相对差异。import utils.resources
导入了自定义模块utils.resources
,其中包含一些资源相关的功能。import os, datetime, time, re, math, shutil, json
导入了一些Python标准库模块,包括操作系统功能、日期和时间处理、正则表达式、数学计算、文件操作和JSON解析等。
这些导入语句使得你可以在代码中使用这些模块和函数的功能。例如,你可以使用Path
类来处理文件路径,使用datetime
模块来获取当前日期和时间,使用shutil
模块来进行文件和文件夹的复制、移动和删除等操作。
from utils.deleteThread import * from utils.multiDeleteThread import multiDeleteThread from utils.selectVersion import * from utils.selectVersion import check_dir, existing_user_config
这段代码导入了一些自定义的模块和函数:
from utils.deleteThread import *
导入了utils.deleteThread
模块中的所有内容。这个模块包含一个或多个线程类或函数,用于执行删除操作。from utils.multiDeleteThread import multiDeleteThread
导入了utils.multiDeleteThread
模块中的multiDeleteThread
类。这个类是一个多线程的删除操作的实现。from utils.selectVersion import *
导入了utils.selectVersion
模块中的所有内容。这个模块包含一个或多个函数或类,用于选择版本或配置。from utils.selectVersion import check_dir, existing_user_config
导入了utils.selectVersion
模块中的check_dir
函数和existing_user_config
函数。这些函数用于检查目录或现有用户配置的存在。
补充:*是什么意思
在Python中,使用 *
作为导入语句的一部分,可以表示导入模块中的所有内容。当你使用 from module import *
的形式时,它会导入模块中的所有函数、类和变量,使得你可以直接使用它们,而不需要在代码中使用模块名作为前缀。
例如,from utils.deleteThread import *
表示导入utils.deleteThread
模块中的所有内容,包括所有的函数、类和变量。这样,你就可以直接在代码中使用这些导入的内容,而无需在前面加上模块名。
使用 *
导入所有内容可能会导致一些命名冲突或不明确性,因为你无法准确知道导入的模块中有哪些内容。这可能会导致代码可读性下降,并且可能引入不必要的命名冲突。因此,一般来说,最好是明确地导入需要使用的函数、类和变量,而不是使用 *
导入所有内容。
2-3:工作目录获取
# determine if application is a script file or frozen exe if getattr(sys, 'frozen', False): working_dir = os.path.dirname(os.path.realpath(sys.executable)) elif __file__: working_dir = os.path.split(os.path.realpath(__file__))[0]
这段代码用于确定应用程序是一个**脚本文件还是一个被冻结(打包)为可执行文件(exe)的文件**。
首先,代码检查 sys.frozen
属性是否为真。sys.frozen
是一个由 PyInstaller 打包工具设置的属性,用于指示应用程序是否被冻结为可执行文件。如果 sys.frozen
为真,表示应用程序是一个被冻结的可执行文件。
如果 sys.frozen
为真,代码使用 sys.executable
获取当前可执行文件的路径,然后使用 os.path.dirname
函数获取该路径的父目录,即工作目录。
如果 sys.frozen
为假,表示应用程序是一个脚本文件。在这种情况下,代码使用 file
获取当前脚本文件的路径,然后使用 os.path.realpath
函数获取该路径的真实路径,再**使用 os.path.split
函数将路径拆分为目录和文件名**,最后取目录部分作为工作目录。
无论是脚本文件还是冻结的可执行文件,最终的工作目录都会被存储在 working_dir
变量中供后续使用。这个工作目录可以用于确定相对路径、读取文件等操作。
补充:什么是脚本文件?
脚本文件是包含一系列可执行命令的文本文件,通常使用脚本语言编写,比如Python、Bash、JavaScript等。脚本文件通常**包含一系列命令、函数和逻辑,用于自动化执行特定任务或实现特定功能**。
与编译型语言相比,脚本语言的脚本文件不需要进行显式的编译过程。它们在运行时由解释器逐行解释和执行。这使得**脚本文件更加灵活和易于编写、调试和修改**。
脚本文件常用于自动化任务、批处理处理、系统管理、数据处理、网站开发等领域。它们可以通过命令行或调用解释器来执行,也可以通过其他程序或工具来调用。
在**Python中,脚本文件通常以 .py
作为文件扩展名,并且可以通过命令行或Python解释器来执行**。例如,你可以使用 python script.py
命令来执行名为 script.py
的Python脚本文件。
脚本文件的优点是灵活性和易用性,可以快速编写和调试。然而,与编译型语言相比,脚本文件的执行速度可能较慢,因为每次执行都需要解释器进行解释。
2-4:class Window
先把这一小部分的整体代码放上,首先进行一个全局分析,然后我们再逐个函数进行解释。
class Window(QMainWindow): def mousePressEvent(self, event): # 重写一堆方法使其支持拖动 if event.button() == Qt.LeftButton: self.m_drag = True self.m_DragPosition = event.globalPos() - self.pos() event.accept() # self.setCursor(QCursor(Qt.OpenHandCursor)) def mouseMoveEvent(self, QMouseEvent): try: if Qt.LeftButton and self.m_drag: self.move(QMouseEvent.globalPos() - self.m_DragPosition) QMouseEvent.accept() except: pass def mouseReleaseEvent(self, QMouseEvent): self.m_drag = False # self.setCursor(QCursor(Qt.ArrowCursor)) def _frame(self): # 边框 self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground, True) # 阴影 effect = QGraphicsDropShadowEffect(blurRadius=12, xOffset=0, yOffset=0) effect.setColor(QColor(25, 25, 25, 170)) self.mainFrame.setGraphicsEffect(effect) def doFadeIn(self): # 动画 self.animation = QPropertyAnimation(self, b'windowOpacity') # 持续时间250ms self.animation.setDuration(250) try: # 尝试先取消动画完成后关闭窗口的信号 self.animation.finished.disconnect(self.close) except: pass self.animation.stop() # 透明度范围从0逐渐增加到1 self.animation.setEasingCurve(QEasingCurve.InOutCubic) self.animation.setStartValue(0) self.animation.setEndValue(1) self.animation.start() def doFadeOut(self): self.animation.stop() # 动画完成则关闭窗口 self.animation.finished.connect(self.close) # 透明度范围从1逐渐减少到0s self.animation.setEasingCurve(QEasingCurve.InOutCubic) self.animation.setStartValue(1) self.animation.setEndValue(0) self.animation.start() def setWarninginfo(self, text): self.lab_info.setStyleSheet(""" .QLabel { border:1px solid #ffccc7; border-radius:3px; line-height: 140px; padding: 5px; color: #434343; background: #fff2f0; } """) self.lab_info.setText(text) def setSuccessinfo(self, text): self.lab_info.setStyleSheet(""" .QLabel { border:1px solid #b7eb8f; border-radius:3px; line-height: 140px; padding: 5px; color: #434343; background: #f6ffed; } """) self.lab_info.setText(text)
这段代码继续定义了 Window
类的其他方法,用于实现窗口的一些特殊效果和功能。
mouseMoveEvent
方法:当**鼠标移动时触发,用于实现拖动窗口的效果**。在方法中,首先判断鼠标左键是否按下且窗口正在被拖动(self.m_drag
为True
),然后通过self.move()
方法将窗口移动到当前鼠标位置减去初始偏移量的位置。mouseReleaseEvent
方法:当**鼠标释放时触发,用于停止拖动窗口**的效果。在方法中,将self.m_drag
设置为False
,表示窗口不再被拖动。_frame
方法:用于**设置窗口的边框和阴影效果**。通过设置窗口的标志位和属性,实现无边框窗口和透明背景,并添加一个带有阴影效果的QGraphicsDropShadowEffect
。doFadeIn
方法:用于**实现窗口的淡入效果**。通过创建一个QPropertyAnimation
对象,设置动画的目标对象为当前窗口,属性为windowOpacity
(窗口的透明度),持续时间为250毫秒。然后设置动画的起始值为0,结束值为1,设置缓动曲线为QEasingCurve.InOutCubic
,并启动动画。doFadeOut
方法:用于**实现窗口的淡出效果**。与doFadeIn
方法类似,设置动画的起始值为1,结束值为0,然后启动动画。在动画完成后,连接self.close
方法,以便在动画完成后关闭窗口。setWarninginfo
和setSuccessinfo
方法:用于设置窗口上的信息标签的样式和文本。这两个方法分别**设置了警告信息和成功信息**的样式,包括边框、背景色等,并通过setText
方法设置文本内容。
这些方法提供了一些额外的功能,如拖动窗口、窗口特效和信息标签的样式设置,以增强GUI窗口的交互和视觉效果。所以,我们有必要逐个分析一下。
——mousePressEvent
通过下列操作,可实现拖动窗口的功能。当用户按下鼠标左键时,窗口会进入拖动状态,并记录下鼠标按下时的初始位置和窗口的初始位置之间的偏移量。在后续的 mouseMoveEvent 方法中,可以根据这个偏移量来实现窗口的跟随移动。
def mousePressEvent(self, event): # 重写一堆方法使其支持拖动 if event.button() == Qt.LeftButton: self.m_drag = True self.m_DragPosition = event.globalPos() - self.pos()#拖动距离 event.accept() # self.setCursor(QCursor(Qt.OpenHandCursor))
这部分代码是 mousePressEvent
方法的实现。当鼠标按下时,该方法会被调用。在这段代码中,首先通过 event.button()
判断鼠标按下的是左键(Qt.LeftButton
)。
如果鼠标按下的是左键,以下操作将被执行:
self.m_drag = True
:将一个成员变量m_drag
设置为True
,用于表示窗口正在被拖动。self.m_DragPosition = event.globalPos() - self.pos()
:计算鼠标按下时的全局坐标与窗口当前位置的差值,并将结果保存在成员变量m_DragPosition
中。这样可以**记录下鼠标按下时的初始位置和窗口的初始位置之间的偏移量**。event.accept()
:接受鼠标事件,表示该事件已经被处理。
注意:# self.setCursor(QCursor(Qt.OpenHandCursor))
这行代码的作用是**设置鼠标指针的样式为打开手指形状**(Qt.OpenHandCursor
),由于被注释掉了,所以在当前代码段并没有生效。。
通常情况下,当用户将鼠标悬停在可拖动的区域时,将鼠标指针设置为打开手指形状可以提供一种视觉反馈,表明该区域可以进行拖动操作。这样可以增强用户体验,让用户知道他们可以通过拖动来移动窗口。
如果你想启用这行代码,你可以取消注释,即将 #
符号删除,然后重新运行代码。这样,在鼠标按下时,鼠标指针将会变成打开手指形状。
未完待续
最新更新日期
2023/10/28-09:58