使用Python编写批量添加图片水印程序:二、桌面客户端

在上一篇文章《使用Python编写批量添加图片水印程序:一、代码方案》中,我们介绍了使用Python为图片批量添加图像水印的方法,今天,我们继续优化这个小工具,借助QT for Python模块(PyQt5、PySide2)将其制作为一个桌面客户端程序。

一、编写基础界面

首先,我们来为这个添加图片水印的桌面客户端程序编写一个基础的图形界面。其包含:

  • 一个显示图片所在路径的文本输入框;
  • 一个用于选择图片所在目录的按钮;
  • 一个显示水印图片路径的文本输入框;
  • 一个用于选择水印图片的按钮;
  • 一个显示图片保存目录的文本输入框;
  • 一个用于选择图片保存目录的按钮;
  • 一个水印放置位置的下拉框;
  • 一个用于批量添加图片水印的按钮;

上述的模块我们通过QLineEdit()模块实现输入框、QPushButton()实现按钮、QComboBox()实现下拉框。同时,我们使用网格布局QGridLayout()对模块进行布局。

其最终的图形界面如下图所示:

这个图形界面的创建代码如下图所示:

class MainApp(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("一键图片水印") # 窗口标题
        self.setFixedSize(350,160) # 设置窗口高和宽

        self.main_widget = QtWidgets.QWidget() # 窗口主控件
        self.main_layout = QtWidgets.QGridLayout() # 窗口主控件布局
        self.main_widget.setLayout(self.main_layout)

        self.folder_input = QtWidgets.QLineEdit()
        self.folder_input.setReadOnly(True)
        self.folder_btn = QtWidgets.QPushButton("选择文件夹")

        self.wm_input = QtWidgets.QLineEdit()
        self.wm_input.setReadOnly(True)
        self.wm_btn = QtWidgets.QPushButton("选择水印图片")

        self.save_input = QtWidgets.QLineEdit()
        self.save_input.setReadOnly(True)
        self.save_btn = QtWidgets.QPushButton("保存目录")

        self.position_label = QtWidgets.QLabel('水印位置(州的先生zmister.com):')
        self.position_box = QtWidgets.QComboBox()
        self.position_box.addItem("左上")
        self.position_box.addItem("左下")
        self.position_box.addItem("右上")
        self.position_box.addItem("右下")
        self.position_box.addItem("居中")

        self.submit_btn = QtWidgets.QPushButton("生成图片")

        self.main_layout.addWidget(self.folder_input,0,0,1,1)
        self.main_layout.addWidget(self.folder_btn, 0, 1, 1, 1)
        self.main_layout.addWidget(self.wm_input, 1, 0, 1, 1)
        self.main_layout.addWidget(self.wm_btn, 1, 1, 1, 1)
        self.main_layout.addWidget(self.save_input,2,0,1,1)
        self.main_layout.addWidget(self.save_btn,2,1,1,1)
        self.main_layout.addWidget(self.position_label, 3, 0, 1, 1)
        self.main_layout.addWidget(self.position_box, 3, 1, 1, 1)
        self.main_layout.addWidget(self.submit_btn, 4, 0, 1, 2)
        self.setCentralWidget(self.main_widget) # 设置窗口初始化控件

这样,我们的基础界面已经创建好了,下面我们来对几个按钮的事件进行响应。

二、实现图片选取

在图形界面中,我们设置了4个按钮,其中3个用于目录和文件的选择,一个用于最终批量处理图片。在3个用于目录和文件选择的按钮中,其中:

  • 第一个按钮用于选择图片所在的文件夹;
  • 第二个按钮用于选择图片水印;
  • 第三个按钮用于选择新图片保存的目录;

我们通过QFileDialog.getExistingDirectory()来选择文件夹、通过QFileDialog.getOpenFileName()来选择文件。最终,我们在MainApp()中新建3个方法:

# 选择图片文件夹
def select_folder(self):
    file = str(QtWidgets.QFileDialog.getExistingDirectory(self, "选择文件夹"))
    print('文件夹为:',file)
    if file != '':
        self.folder_input.setText(file)

# 选择水印图片
def select_wm_img(self):
    file,_ = QtWidgets.QFileDialog.getOpenFileName(
        self,'选择水印图片','','Images (*.png *.xpm *.jpg)'
    )
    print("水印图片为:",file)
    if file != '':
        self.wm_input.setText(file)

# 选择保存目录
def select_save_folder(self):
    file = str(QtWidgets.QFileDialog.getExistingDirectory(self, "选择文件夹"))
    print('文件夹为:', file)
    if file != '':
        self.save_input.setText(file)

然后将3个按钮的点击信号连接到这3个方法上:

self.save_btn.clicked.connect(self.select_save_folder)
self.wm_btn.clicked.connect(self.select_wm_img)
self.folder_btn.clicked.connect(self.select_folder)

这样,我们我们图形界面中的按钮就有实际的功能了,其效果如下动图所示:

这样,我们就可以开始利用选择的东西对图片进行水印操作了。

三、绑定水印添加线程

因为批量对图片添加水印在原图片数量多的情况下,可能会非常耗时。如果我们将其放置在主类里面可能会导致主界面进行的堵塞,所以在此我们通过QThread在子线程中进行批量添加图片水印的任务。其定义如下所示:

class WmThread(QtCore.QThread):
    finished_signal = QtCore.pyqtSignal(int) # 使用PySide2模块需要将pyqtSignal改成Signal

    def __init__(self,parent=None,wm_file=None,fpath=None,save_path=None,position=None):
        super().__init__(parent)
        self.wm_file = wm_file
        self.fpath = fpath
        self.save_path = save_path
        self.position = position
        print(self.wm_file,self.fpath)

    # 获取文件夹图片
    def get_folder(self,fpath):
        try:
            img_suffix_list = ['png', 'jpg', 'bmp']
            for i in os.listdir(fpath):
                if i.split('.')[-1] in img_suffix_list:
                    img_path = fpath + '/' + i
                    self.img_water_mark(img_file=img_path,wm_file=self.wm_file)
        except Exception as e:
            print(traceback.print_exc())

    # 图片添加水印
    def img_water_mark(self,img_file, wm_file):
        try:
            img = Image.open(img_file)  # 打开图片
            watermark = Image.open(wm_file)  # 打开水印
            img_size = img.size
            wm_size = watermark.size
            # 如果图片大小小于水印大小
            if img_size[0] < wm_size[0]:
                watermark.resize(tuple(map(lambda x: int(x * 0.5), watermark.size)))
            print('图片大小:', img_size,"水印位置:",self.position)
            if self.position == '左上':
                wm_position = (0,0)
            elif self.position == '左下':
                wm_position = (0,img_size[1]-wm_size[1])
            elif self.position == '右上':
                wm_position = (img_size[0]-wm_size[0],0)
            elif self.position == '右下':
                wm_position = (img_size[0]-wm_size[0],img_size[1]-wm_size[1])
            elif self.position == '居中':
                wm_position = (img_size[0]//2-wm_size[0]//2,img_size[1]//2-wm_size[1]//2)
            layer = Image.new('RGBA', img.size)  # 新建一个图层
            layer.paste(watermark, wm_position)  # 将水印图片添加到图层上
            mark_img = Image.composite(layer, img, layer)
            new_file_name = '/new_'+img_file.split('/')[-1]
            if self.save_path == '':
                mark_img.save(self.fpath+new_file_name)
            else:
                mark_img.save(self.save_path + new_file_name)
        except Exception as e:
            print(traceback.print_exc())

    def run(self):
        try:
            print("开始处理……")
            self.get_folder(fpath=self.fpath)
            self.finished_signal.emit(1)
        except Exception as e:
            print(traceback.print_exc())
            self.finished_signal.emit(0)

这样,我们就定义了一个子线程类,批量添加图片水印的实际工作都有这个子线程类来处理。然后我们需要在主类MainApp()中对这个子线程类进行调用:

# 生成水印图片
def generate_img(self):
    try:
        print("获取图片目录及水印文件名")
        folder_text = self.folder_input.text()
        wm_text = self.wm_input.text()
        save_text = self.save_input.text()
        position_text = self.position_box.currentText()
        print('判断是否为空')
        if folder_text != '' and wm_text != '':
            self.submit_btn.setEnabled(False)
            self.genare_thread = WmThread(
                wm_file= wm_text,
                fpath=folder_text,
                save_path = save_text,
                position = position_text
            )
            print('启动线程')
            self.genare_thread.finished_signal.connect(self.thread_resp)
            self.genare_thread.start()

    except Exception as e:
        print(traceback.print_exc())

def thread_resp(self,value):
    if value == 1:
        QtWidgets.QMessageBox.information(self,'提示','处理完成!',QtWidgets.QMessageBox.Yes)
    if value == 0:
        QtWidgets.QMessageBox.information(self,'提示', '处理出错!',QtWidgets.QMessageBox.Yes)
    self.submit_btn.setEnabled(True)

最后将generate_img()方法绑定到生成按钮的点击信号上:

self.submit_btn.clicked.connect(self.generate_img)

这样,我们的批量添加图片水印客户端程序就编写完成了。

四、将代码打包为EXE程序

最后,我们借助PyInstaller模块将编写好的代码打包为EXE格式的可执行文件。使用如下命令将其打包为一个文件夹:

pyinstaller -w main.py

最后打包生成了一个最终的文件夹,如下图所示:

最后,我们运行main.exe文件,就会出现之前编写的图形界面,功能亦是相同的。

这样,我们就完成了图片水印批量添加程序的开发和打包。有问题欢迎留言讨论,本程序将会逐步迭代更新并开放免费使用,争取早日实现同类型商业化软件的功能,敬请关注。

猜你也喜欢

发表评论

电子邮件地址不会被公开。