├── README.md ├── main.py ├── png.ico ├── requirements.txt ├── test.py ├── tool.ico ├── tool.py ├── tools.png ├── tools_png.py └── ui.py /README.md: -------------------------------------------------------------------------------- 1 | ## 环境依赖 2 | 3 | 1. python3.X 4 | 2. Pillow:主要用于拼接图片,修改图片分辨率 5 | 6 | ```python 7 | pip install Pillow 8 | ``` 9 | 10 | 3. pymupdf:用于将pdf转成图片 11 | 12 | ```python 13 | pip install pymupdf 14 | ``` 15 | 16 | 4. PyQt5:用于GUI代码编写 17 | 18 | ```python 19 | pip install PyQt5 20 | ``` 21 | 22 | 5. PyQt5-tools:GUI配套工具 23 | 24 | ```python 25 | pip install PyQt5-tools 26 | ``` 27 | 28 | 6. Pyinstaller:用于打包python到应用程序 29 | 30 | ```python 31 | pip install pyinstaller 32 | ``` 33 | 34 | - 一键安装依赖(上面单个安装和下面一键安装都行) 35 | 36 | ```python 37 | pip -r requirements.txt 38 | ``` 39 | 40 | ## 目录结构 41 | 42 | ```shell 43 | - main.py 主程序入口 44 | - png.ico windows打包用的图标 45 | - README.md 使用说明文档 46 | - requirements.txt 依赖记录文件 47 | - tools.ico Mac打包用的图标,可忽略 48 | - tools.py 主要为pdf与图片操作的后端源代码,核心所在 49 | - tools.png 主程序菜单栏的图标,不可删除 50 | - ui.py UI设计界面 51 | - test.py 用于将图片转base64二进制码 52 | - tools_png.py 利用图片转成的py文件,里面存了一个base64字节码,main程序调用的时候会自动释放用来生成图片。 53 | ``` 54 | 55 | ## 软件打包 56 | 57 | - 直接使用Pyinstaller打包主程序 58 | 59 | ```python 60 | pyinstaller -i png.ico -F -w main.py 61 | ``` 62 | 63 | 64 | ## 其它建议 65 | 66 | - 如果对软件有更好的建议,可以直接给我发邮件371043382@qq.com 67 | - 如果你也是python的学习爱好者,欢迎加群一起学习:974759263 68 | - Python个人博客,不定期更新python学习教程:https://www.vbahome.cn/ 69 | 70 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from ui import UI 2 | import sys 3 | from tool import * 4 | from os import path, listdir 5 | from PyQt5.QtWidgets import QApplication, QFileDialog, QMessageBox, QProgressDialog, QLabel, qApp 6 | from PyQt5.QtGui import QIcon 7 | import json 8 | import base64 9 | from tools_png import img 10 | 11 | 12 | class PdfMain(UI): 13 | def __init__(self): 14 | super(PdfMain, self).__init__() 15 | # --- 窗口1 --- # 16 | self.my_dialog1_1 = None # 会话1,展示pdf转png进度 17 | self.pdf_path = None 18 | 19 | # --- 窗口2 --- # 20 | self.pdf_dir1 = None 21 | self.my_dialog2_1 = None # 会话2_1,用于单列转长图 22 | self.my_dialog2_2 = None # 会话2_2,用于转矩阵长图 23 | self.dialog_line_edit1_value = None # 拼接数量 24 | self.dialog_line_edit2_value = None # 拼接行数 25 | self.dialog_line_edit3_value = None # 拼接列数 26 | self.dialog_line_edit4_value = None # 拼接间隙 27 | 28 | # --- 窗口3 --- # 29 | self.pdf_dir2 = None # 文件夹路径 30 | self.width_model = '普通' # 图片宽度模式 31 | self.picture_type = 'jpg' # 图片格式 32 | self.zip_num = None # 图片压缩率 33 | self.my_dialog3 = None # 会话3,用于压缩图片 34 | 35 | # -- 运行窗口 -- # 36 | self.init_action() 37 | 38 | def init_action(self): 39 | self.show_frame1() 40 | 41 | def show_frame1(self): 42 | """ 43 | 重写窗口1的方法,加入按钮监控 44 | :return: 45 | """ 46 | super(PdfMain, self).show_frame1() 47 | self.push_button1_1.clicked.connect(self.file_select) # 连接选择文件 48 | self.push_button1_2.clicked.connect(self.do_convert) # 连接开始转换 49 | self.push_button1_3.clicked.connect(qApp.quit) # 连接退出软件 50 | 51 | def show_frame2(self): 52 | super(PdfMain, self).show_frame2() 53 | self.push_button2_1.clicked.connect(self.dir1_select) # 连接选择文件夹 54 | self.push_button2_2.clicked.connect(self.one_step2long) # 连接一键长图 55 | 56 | def show_frame3(self): 57 | super(PdfMain, self).show_frame3() 58 | 59 | # 加入上次的输入框 60 | width_model = self.read_data_dict('width_model') 61 | picture_type = self.read_data_dict('picture_type') 62 | pdf_dir2 = self.read_data_dict('pdf_dir2') 63 | zip_num = self.read_data_dict('zip_num') 64 | if pdf_dir2 is not None: 65 | self.line_edit3_1.setText(pdf_dir2) 66 | self.pdf_dir2 = pdf_dir2 67 | if zip_num is not None: 68 | self.line_edit3_2.setText(zip_num) 69 | self.zip_num = zip_num 70 | else: 71 | self.line_edit3_2.setText('92') 72 | self.zip_num = '92' 73 | if width_model is not None: 74 | self.width_model = width_model 75 | self.combo_box3_1.setCurrentText(width_model) 76 | if picture_type is not None: 77 | self.picture_type = picture_type 78 | self.combo_box3_2.setCurrentText(picture_type) 79 | # 监控按键3-1:自定义路径 80 | self.push_button3_1.clicked.connect(self.dir2_select) 81 | # 检测下拉框:压缩宽度 82 | self.combo_box3_1.activated[str].connect(self.combo_box3_1_select) # 图片宽度模式 83 | self.combo_box3_2.activated[str].connect(self.combo_box3_2_select) # 图片格式 84 | self.line_edit3_2.textEdited.connect(self.line_edit3_2_change) # 压缩比 85 | # 连接一键压缩 86 | self.push_button3_2.clicked.connect(self.one_step_zip) # 连接一键压缩 87 | 88 | # 公用进度条 89 | def show_process_bar(self, label_text): 90 | """ 91 | 显示进度条 92 | """ 93 | self.progress = QProgressDialog() # 设定窗口 94 | # self.progress.setGeometry(320, 190, 250, 100) 95 | self.progress.resize(250, 100) 96 | self.progress.setWindowIcon(QIcon('tools.png')) 97 | self.progress.setMinimumSize(250, 100) 98 | label = QLabel(label_text, self.progress) 99 | label.move(12, 18) 100 | label.setStyleSheet("font-size:12px; font-weight:normal") 101 | self.progress.setWindowTitle('进度条') 102 | self.progress.setRange(0, 100) 103 | self.progress.show() 104 | 105 | # ----- 以下方法用于窗口1------------- # 106 | def file_select(self): 107 | """ 108 | 文件选择,用于窗口1 109 | """ 110 | f_dialog = QFileDialog() 111 | # f_dialog.setGeometry(350, 350, 300, 150) 112 | f_dialog.setWindowIcon(QIcon('tools.png')) 113 | f_dialog.setWindowTitle('选择文件') 114 | file = f_dialog.getOpenFileName() 115 | if bool(file[0]): 116 | self.line_edit1_1.setText(file[0]) 117 | self.pdf_path = file[0] 118 | 119 | def do_convert(self): 120 | """ 121 | 进度条, main Process 122 | :param 123 | """ 124 | if self.pdf_path is None: 125 | pdf_path = self.read_data_dict('pdf_path') 126 | if pdf_path is None: 127 | text = '亲,你还没有选择文件路径' 128 | QMessageBox.warning(self, '错误', text, 129 | QMessageBox.No | QMessageBox.Yes, QMessageBox.No) 130 | else: 131 | text = '亲,确认使用上次的路径?' 132 | replay = QMessageBox.warning(self, '错误', text, 133 | QMessageBox.No | QMessageBox.Yes, QMessageBox.No) 134 | if replay != 65536: 135 | self.pdf_path = pdf_path 136 | self.pdf2png() 137 | 138 | else: 139 | file_name0 = path.split(self.pdf_path)[1] 140 | # pdf_name = path.splitext(file_name0)[0] 141 | file_type = path.splitext(file_name0)[1] 142 | if file_type.upper() != '.PDF': 143 | text = '亲,你选择的不是pdf文件' 144 | QMessageBox.warning(self, '错误', text, 145 | QMessageBox.No | QMessageBox.Yes, QMessageBox.No) 146 | else: 147 | self.write_data_dict('pdf_path', self.pdf_path) # 将pdf路径保存到字典 148 | self.pdf2png() 149 | 150 | def pdf2png(self): 151 | """ 152 | 用于pdf转png 153 | :return: 154 | """ 155 | pdf = PdfInit(self.pdf_path) # 处理pdf 156 | self.show_process_bar('正在将pdf转成单张图片, 请稍后') 157 | self.my_dialog1_1 = Pdf2Png(pdf) 158 | self.my_dialog1_1.start() 159 | self.my_dialog1_1.trigger.connect(self.process_pdf2png) 160 | 161 | def process_pdf2png(self, int1, int2): 162 | print(int1, int2) 163 | value = int((int1 + 1) * 100 / int2) # 这里的页面数量需要加1 164 | self.progress.setValue(value) 165 | 166 | # ----- 以下方法用于窗口2------------- # 167 | def dir1_select(self): 168 | """ 169 | 自定义路径 170 | :return: 171 | """ 172 | f_dialog2 = QFileDialog() 173 | # f_dialog.setGeometry(350, 350, 300, 150) 174 | f_dialog2.setWindowIcon(QIcon('tools.png')) 175 | f_dialog2.setWindowTitle('选择文件夹') 176 | dir1 = f_dialog2.getExistingDirectory() 177 | if bool(dir1): 178 | self.line_edit2_1.setText(dir1) 179 | self.pdf_dir1 = dir1 180 | 181 | def one_step2long(self): 182 | """ 183 | 一键生成长图 184 | :return: 185 | """ 186 | text_list = listdir(self.pdf_dir1) 187 | text_list2 = [fn for fn in text_list if fn.endswith('.png') or fn.endswith('jpg')] 188 | if len(text_list2) == 0: 189 | text = "你当前路径下没有任何png或者jpg图片" 190 | QMessageBox.warning(self, '警告', text, 191 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 192 | else: 193 | if self.pdf_dir1 is not None: 194 | png_list = listdir(self.pdf_dir1) 195 | png_num = len(png_list) # 获取图片数量 196 | parent_path = path.split(self.pdf_dir1)[0] # 获取父目录 197 | out_path = parent_path + '/一键长图' 198 | my_dialog2_1 = GenLongPng(png_num, self.pdf_dir1, out_path) 199 | my_dialog2_1.run() 200 | text = "长图生成成功,请到pdf同路径的”一键长图“文件夹查看" 201 | QMessageBox.warning(self, '恭喜', text, 202 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 203 | 204 | else: 205 | text = "亲!你还没有选择图片所在文件夹" 206 | QMessageBox.warning(self, '警告', text, 207 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 208 | 209 | def show_dialog1(self): 210 | """ 211 | 重写会话1系统,用于单列拼接 212 | :return: 213 | """ 214 | super(PdfMain, self).show_dialog1() 215 | # 加入上次的输入框 216 | dialog_line_edit1_value = self.read_data_dict('dialog_line_edit1_value') 217 | if dialog_line_edit1_value is not None: 218 | self.dialog_line_edit1.setText(dialog_line_edit1_value) 219 | self.dialog_line_edit1_value = dialog_line_edit1_value 220 | # 监控输入框变化 221 | self.dialog_line_edit1.textEdited.connect(self.dialog_line_edit1_change) 222 | # 检测按钮:一键转换单列长图 223 | self.dialog_push_button1.clicked.connect(self.customize_one_column_long_png) 224 | self.dialog1.show() 225 | 226 | def customize_one_column_long_png(self): 227 | text_list = listdir(self.pdf_dir1) 228 | text_list2 = [fn for fn in text_list if fn.endswith('.png') or fn.endswith('jpg')] 229 | if len(text_list2) == 0: 230 | text = "你当前路径下没有任何png或者jpg图片" 231 | QMessageBox.warning(self, '警告', text, 232 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 233 | else: 234 | if self.dialog_line_edit1_value is None: 235 | png_list = listdir(self.pdf_dir1) 236 | png_num = len(png_list) # 获取图片数量 237 | text = "亲!还没有输入”拼接数量“呢?最大不能超过图片总数{}哦".format(png_num) 238 | QMessageBox.warning(self, '警告', text, 239 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 240 | else: 241 | parent_path = path.split(self.pdf_dir1)[0] # 获取父目录 242 | out_path = parent_path + '/单列长图' 243 | self.write_data_dict('dialog_line_edit1_value', self.dialog_line_edit1_value) # 存入字典 244 | self.show_process_bar('正在将图片拼接成多张长图, 请稍后') 245 | self.my_dialog2_1 = GenLongPng(int(self.dialog_line_edit1_value), self.pdf_dir1, out_path) 246 | self.my_dialog2_1.start() 247 | self.my_dialog2_1.trigger.connect(self.process_pdf2png) 248 | 249 | def dialog_line_edit1_change(self, value): 250 | if self.is_number(value) and '.' not in value: # 如果属于数字并且为整数 251 | png_list = listdir(self.pdf_dir1) 252 | png_num = len(png_list) # 获取图片数量 253 | if int(float(value)) > png_num: 254 | text = "亲!最大不能超过图片数量,当前文件夹只有{}张图片。".format(png_num) 255 | QMessageBox.warning(self, '警告', text, 256 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 257 | self.dialog_line_edit1.setText(None) 258 | else: 259 | self.dialog_line_edit1_value = str(value) 260 | else: 261 | text = "亲!只能输入整数" 262 | QMessageBox.warning(self, '警告', text, 263 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 264 | self.dialog_line_edit1.setText(None) 265 | 266 | @staticmethod 267 | def is_number(s): 268 | """ 269 | 检测是否为数字 270 | :param s: 271 | :return: 272 | """ 273 | try: 274 | float(s) 275 | return True 276 | except ValueError: 277 | pass 278 | 279 | try: 280 | import unicodedata 281 | unicodedata.numeric(s) 282 | return True 283 | except (TypeError, ValueError): 284 | pass 285 | 286 | return False 287 | 288 | def show_dialog2(self): 289 | """ 290 | 重写会话2系统,用于矩阵拼接 291 | :return: 292 | """ 293 | super(PdfMain, self).show_dialog2() 294 | # 加入上次的输入框 295 | dialog_line_edit2_value = self.read_data_dict('dialog_line_edit2_value') 296 | dialog_line_edit3_value = self.read_data_dict('dialog_line_edit3_value') 297 | dialog_line_edit4_value = self.read_data_dict('dialog_line_edit4_value') 298 | if dialog_line_edit2_value is not None: 299 | self.dialog_line_edit2.setText(dialog_line_edit2_value) 300 | self.dialog_line_edit2_value = dialog_line_edit2_value 301 | if dialog_line_edit3_value is not None: 302 | self.dialog_line_edit3.setText(dialog_line_edit3_value) 303 | self.dialog_line_edit3_value = dialog_line_edit3_value 304 | else: 305 | self.dialog_line_edit3.setText('1') 306 | self.dialog_line_edit3_value = '1' 307 | if dialog_line_edit4_value is not None: 308 | self.dialog_line_edit4.setText(dialog_line_edit4_value) 309 | self.dialog_line_edit4_value = dialog_line_edit4_value 310 | else: 311 | self.dialog_line_edit4.setText('0') 312 | self.dialog_line_edit4_value = '0' 313 | # 监控输入框变化 314 | self.dialog_line_edit2.textEdited.connect(self.dialog_line_edit2_change) # 检测行数 315 | self.dialog_line_edit3.textEdited.connect(self.dialog_line_edit3_change) # 检测列数 316 | self.dialog_line_edit4.textEdited.connect(self.dialog_line_edit4_change) # 检测px像素 317 | self.dialog_push_button2.clicked.connect(self.matrix_long_png) # 矩阵拼图 318 | self.dialog2.show() 319 | 320 | def dialog_line_edit2_change(self, value): 321 | """ 322 | 检测列数量 323 | :param value: 324 | :return: 325 | """ 326 | if self.is_number(value) and '.' not in value: # 如果属于数字并且为整数 327 | png_list = listdir(self.pdf_dir1) 328 | png_num = len(png_list) # 获取图片数量 329 | if int(float(value)) > png_num: 330 | text = "亲!最大不能超过图片数量,当前文件夹只有{}张图片。".format(png_num) 331 | QMessageBox.warning(self, '警告', text, 332 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 333 | self.dialog_line_edit2.setText(None) 334 | else: 335 | self.dialog_line_edit2_value = str(value) 336 | else: 337 | text = "亲!只能输入整数" 338 | QMessageBox.warning(self, '警告', text, 339 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 340 | self.dialog_line_edit2.setText(None) 341 | 342 | def dialog_line_edit3_change(self, value): 343 | """ 344 | 检测行数量 345 | :param value: 346 | :return: 347 | """ 348 | if self.is_number(value) and '.' not in value: # 如果属于数字并且为整数 349 | png_list = listdir(self.pdf_dir1) 350 | png_num = len(png_list) # 获取图片数量 351 | if int(float(value)) > png_num: 352 | text = "亲!最大不能超过图片数量,当前文件夹只有{}张图片。".format(png_num) 353 | QMessageBox.warning(self, '警告', text, 354 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 355 | self.dialog_line_edit3.setText(None) 356 | else: 357 | self.dialog_line_edit3_value = str(value) 358 | else: 359 | text = "亲!只能输入整数" 360 | QMessageBox.warning(self, '警告', text, 361 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 362 | self.dialog_line_edit3.setText(None) 363 | 364 | def dialog_line_edit4_change(self, value): 365 | """ 366 | 检测像素间隔px值 367 | :param value: 368 | :return: 369 | """ 370 | if self.is_number(value) and '.' not in value: # 如果属于数字并且为整数 371 | self.dialog_line_edit4_value = str(value) 372 | else: 373 | text = "亲!只能输入整数" 374 | QMessageBox.warning(self, '警告', text, 375 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 376 | self.dialog_line_edit4.setText(None) 377 | 378 | def matrix_long_png(self): 379 | """ 380 | 自定义拼图之矩阵拼图 381 | :return: 382 | """ 383 | text_list = listdir(self.pdf_dir1) 384 | text_list2 = [fn for fn in text_list if fn.endswith('.png') or fn.endswith('jpg')] 385 | if len(text_list2) == 0: 386 | text = "你当前路径下没有任何png或者jpg图片" 387 | QMessageBox.warning(self, '警告', text, 388 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 389 | else: 390 | result = False 391 | png_list = listdir(self.pdf_dir1) 392 | png_num = len(png_list) # 获取图片数量 393 | if self.dialog_line_edit2_value is None or self.dialog_line_edit3_value is None or \ 394 | self.dialog_line_edit3_value is None: 395 | text = "亲!还有三个空没填全,没法开始" 396 | QMessageBox.warning(self, '警告', text, 397 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 398 | elif int(float(self.dialog_line_edit2_value)) * int(float(self.dialog_line_edit3_value)) > png_num: 399 | text = "行与列之积大于总图片数量,将会产生较多空白,是否继续?" 400 | replay = QMessageBox.warning(self, '警告', text, 401 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 402 | if replay != 65536: # 如果选择”yes“ 403 | result = True 404 | else: 405 | result = True 406 | if bool(result): 407 | self.write_data_dict('dialog_line_edit2_value', self.dialog_line_edit2_value) 408 | self.write_data_dict('dialog_line_edit3_value', self.dialog_line_edit3_value) 409 | self.write_data_dict('dialog_line_edit4_value', self.dialog_line_edit4_value) 410 | row = int(self.dialog_line_edit2_value) 411 | column = int(self.dialog_line_edit3_value) 412 | interval = int(self.dialog_line_edit4_value) 413 | parent_path = path.split(self.pdf_dir1)[0] # 获取父目录 414 | out_path = parent_path + '/矩阵长图' 415 | self.show_process_bar('正在将图片拼接成多张长图, 请稍后') 416 | self.my_dialog2_2 = GenLongPng2(row, column, interval, self.pdf_dir1, out_path) 417 | self.my_dialog2_2.start() 418 | self.my_dialog2_2.trigger.connect(self.process_pdf2png) 419 | 420 | # --- 以下代码用于窗口3 --- # 421 | def dir2_select(self): 422 | """ 423 | 自定义路径 424 | :return: 425 | """ 426 | f_dialog2 = QFileDialog() 427 | # f_dialog.setGeometry(350, 350, 300, 150) 428 | f_dialog2.setWindowIcon(QIcon('tools.png')) 429 | f_dialog2.setWindowTitle('选择文件夹') 430 | dir2 = f_dialog2.getExistingDirectory() 431 | if bool(dir2): 432 | self.line_edit3_1.setText(dir2) 433 | self.pdf_dir2 = dir2 434 | 435 | def combo_box3_1_select(self, text): 436 | """ 437 | 此方法用于选择图片宽度 438 | :param text: 接收的文本 439 | :return: 440 | """ 441 | self.width_model = str(text) 442 | print(text) 443 | 444 | def combo_box3_2_select(self, text): 445 | """ 446 | 此方法用于选择图片格式 447 | :param text: 接收的文本 448 | :return: 449 | """ 450 | self.picture_type = str(text) 451 | print(text) 452 | 453 | def line_edit3_2_change(self, value): 454 | if self.is_number(value) and '.' not in value: # 如果属于数字并且为整数 455 | self.zip_num = str(value) 456 | else: 457 | text = "亲!只能输入整数" 458 | QMessageBox.warning(self, '警告', text, 459 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 460 | self.line_edit3_2.setText(None) 461 | 462 | def one_step_zip(self): 463 | # 查看一下路径里面有没有图片 464 | text_list = listdir(self.pdf_dir2) 465 | text_list2 = [fn for fn in text_list if fn.endswith('.png') or fn.endswith('jpg')] 466 | if len(text_list2) == 0: 467 | text = "你当前路径下没有任何png或者jpg图片" 468 | QMessageBox.warning(self, '警告', text, 469 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 470 | else: 471 | text = "你的当前配置是:宽度模式:”{}“, 格式: ”{}“, 压缩比:”{}%“
路径:”{}“
是否确认?"\ 472 | .format(self.width_model, self.picture_type, self.zip_num, self.pdf_dir2) 473 | replay = QMessageBox.warning(self, '提示', text, 474 | QMessageBox.Yes | QMessageBox.Yes, QMessageBox.No) 475 | print(replay) 476 | if replay != 65536: 477 | # -- 保存相关配置到字典中 -- # 478 | self.write_data_dict('width_model', self.width_model) 479 | self.write_data_dict('picture_type', self.picture_type) 480 | self.write_data_dict('zip_num', self.zip_num) 481 | self.write_data_dict('pdf_dir2', self.pdf_dir2) 482 | # -- 保存完毕 --- # 483 | self.show_process_bar('正在压缩图片, 请稍后') 484 | # print(self.width_model, self.picture_type, self.zip_num, self.pdf_dir2) 485 | self.my_dialog3 = ZipPng(self.width_model, self.picture_type, int(self.zip_num), self.pdf_dir2) 486 | self.my_dialog3.start() 487 | self.my_dialog3.trigger.connect(self.process_pdf2png) 488 | 489 | 490 | if __name__ == '__main__': 491 | # 创建一个data.json文件,用于储存变量参数 492 | if not path.exists('data.json'): 493 | data_dict = {} 494 | with open('data.json', 'wt', encoding='utf-8') as f: 495 | json.dump(data_dict, f) 496 | if not path.exists('tools.png'): 497 | img_data = base64.b64decode(img) 498 | f = open('tools.png', 'wb') 499 | f.write(img_data) 500 | f.close() 501 | app = QApplication(sys.argv) 502 | pdf_main = PdfMain() 503 | sys.exit(app.exec_()) -------------------------------------------------------------------------------- /png.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tlntin/PyQt5_PdfTools/74bad54dc3e75c075595695d3b5c1b6f0e2ba5f1/png.ico -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | altgraph==0.17 2 | certifi==2023.7.22 3 | click==7.1.2 4 | future==0.18.3 5 | pefile==2019.4.18 6 | Pillow>=8.1.1 7 | PyInstaller==3.6 8 | PyMuPDF==1.16.18 9 | PyQt5==5.14.2 10 | PyQt5-sip==12.7.2 11 | pyqt5-tools==5.14.2.1.7b3 12 | PySimpleGUI==4.14.1 13 | python-dotenv==0.13.0 14 | pywin32-ctypes==0.2.0 15 | wincertstore==0.2 16 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | 4 | def pic2py(picture_name): 5 | """ 6 | 将图像文件转换为py文件 7 | :param picture_name: 8 | :return: 9 | """ 10 | open_pic = open("%s" % picture_name, 'rb') 11 | b64str = base64.b64encode(open_pic.read()) 12 | open_pic.close() 13 | # 注意这边b64str一定要加上.decode() 14 | write_data = 'img = "%s"' % b64str.decode() 15 | f = open('%s.py' % picture_name.replace('.', '_'), 'w+') 16 | f.write(write_data) 17 | f.close() 18 | 19 | 20 | if __name__ == '__main__': 21 | pics = ["tools.png"] 22 | for i in pics: 23 | pic2py(i) -------------------------------------------------------------------------------- /tool.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tlntin/PyQt5_PdfTools/74bad54dc3e75c075595695d3b5c1b6f0e2ba5f1/tool.ico -------------------------------------------------------------------------------- /tool.py: -------------------------------------------------------------------------------- 1 | from os import path, mkdir, getcwd, listdir, remove 2 | from PIL import Image 3 | import fitz 4 | from PyQt5.QtCore import pyqtSignal, QThread 5 | from math import ceil, sqrt 6 | 7 | 8 | class PdfInit(object): 9 | """ 10 | pdf初始化 11 | """ 12 | 13 | def __init__(self, file_path): 14 | self.file_path = file_path # 图片路径 15 | self.out_put_dir = None # 输出路径 16 | self.file_name = None # 文件名 17 | self.page_num = None 18 | self.doc = None 19 | self.get_page_num() # 初始化 20 | 21 | def get_page_num(self): 22 | """ 23 | 此方法用于获取pdf页面数量,并且计算导出路径 24 | :param 25 | """ 26 | dir_path = path.split(self.file_path)[0] # 获取路径所在文件夹 27 | b = path.join(dir_path, '导出图片') 28 | self.out_put_dir = b 29 | if not path.exists(b): # 如果路径不存在 30 | mkdir(b) 31 | # 打开PDF文件,生成一个对象 32 | self.doc = fitz.open(self.file_path) 33 | self.page_num = self.doc.pageCount 34 | 35 | 36 | class Pdf2Png(QThread): 37 | """ 38 | 这个类用于将pdf转成png图片 39 | """ 40 | trigger = pyqtSignal(int, int) # 创建信号池,共发射两个信号 41 | 42 | def __init__(self, pdf_init): 43 | super(Pdf2Png, self).__init__() 44 | self.file_path = pdf_init.file_path 45 | self.doc = pdf_init.doc 46 | self.page_num = pdf_init.page_num 47 | self.output_dir = pdf_init.out_put_dir 48 | # 设置窗口 49 | 50 | def run(self): 51 | for pg in range(self.page_num): 52 | # print('正在导出第{}张图片'.format(pg)) 53 | page = self.doc[pg] 54 | rotate = int(0) 55 | # 每个尺寸的缩放系数为2,这将为我们生成分辨率提高四倍的图像。 56 | zoom_x = 2.0 57 | zoom_y = 2.0 58 | trans = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate) 59 | pm = page.getPixmap(matrix=trans, alpha=False) 60 | pm.writePNG('%s/page_%s.png' % (self.output_dir, pg)) # 保存图片 61 | self.trigger.emit(pg, self.page_num) # 发射信号 62 | print('图片导出成功') 63 | 64 | 65 | class GenLongPng(QThread): 66 | """ 67 | 这个类用于将单张png转成长图 68 | :param 69 | """ 70 | trigger = pyqtSignal(int, int) # 构建触发器,用于后面发送消息.第一个信号为当前进度,第二个信号为总进度 71 | 72 | def __init__(self, png_num, png_dir, out_dir): 73 | """ 74 | :param png_num: 拼接的图片数量 75 | :param png_dir: 图片所在路径 76 | :param out_dir: 导出图片路径 77 | """ 78 | super(GenLongPng, self).__init__() 79 | self.png_num = png_num 80 | self.png_dir = png_dir # 图片地址 81 | self.out_dir = out_dir # 导出的长图地址 82 | self.long_png_path = None # 长图路径 83 | 84 | def run(self): 85 | """ 86 | 用于图片拼接 87 | :return: 88 | """ 89 | if not path.exists(self.out_dir): 90 | mkdir(self.out_dir) 91 | else: 92 | list1 = listdir(self.out_dir) 93 | data = [remove(self.out_dir + '/' + li) for li in list1] # 清空原有文件 94 | self.long_png_path = self.out_dir + '/' + 'page_{}.png' # 长图地址 95 | png_list = listdir(self.png_dir) # 获取所有图片 96 | try: # 尝试排序 97 | png_list.sort(key=lambda m: int(m[5: len(m) - 4])) 98 | except Exception as err: 99 | print('排序失败') 100 | print(err) 101 | ims = [Image.open(path.join(self.png_dir, fn)) for fn in png_list if fn.endswith('.png')] # 获取所有图片信息 102 | number = ceil(len(ims) / self.png_num) # 向上取整,判断可以分成的长图的个数 103 | remain = len(ims) % self.png_num # 余数,判断最后一张长图剩余数 104 | for x in range(number): # 开始循环生成长图 105 | if remain > 0: # 如果余数大于0 106 | if x < number - 1: # 如果不是最后一张图 107 | self.paste_png(ims, self.png_num, x) # 按最大拼接拼接图片 108 | else: # 如果是最后一张图 109 | self.paste_png(ims, remain, x) # 按余数拼接图片 110 | elif remain == 0: # 如果余数为零 111 | self.paste_png(ims, self.png_num, x) # 按最大拼接图片,不用考虑最后一张图 112 | self.trigger.emit(x, number) # 发射信号 113 | 114 | def paste_png(self, ims, n1, x): 115 | """ 116 | 此方法用于粘贴单个长图 117 | :param ims: 所有图片的信息 118 | :param n1: 本次预计拼接的图片数量 119 | :param x: x为当前图片的页码数减1 120 | """ 121 | width, height = ims[0].size # 获取单张图片大小 122 | whiter_picture1 = Image.new(ims[0].mode, (width, height * n1)) # 创建空白图,长度为n1的长度 123 | for y, im in enumerate(ims[x * self.png_num: self.png_num * x + n1]): 124 | whiter_picture1.paste(im, box=(0, y * height)) # 图片粘贴拼凑 125 | whiter_picture1.save(self.long_png_path.format(x)) # 长图保存 126 | 127 | 128 | class GenLongPng2(QThread): 129 | """ 130 | 这个类用于将单张png转成长图,包含矩阵算法 131 | :param 132 | """ 133 | trigger = pyqtSignal(int, int) # 构建触发器,用于后面发送消息.第一个信号为当前进度,第二个信号为总进度 134 | 135 | def __init__(self, png_row, png_column, png_interval, png_dir, out_dir): 136 | """ 137 | :param png_row: 拼接的图片的行数 138 | :param png_column: 拼接图片的列数 139 | :param png_interval:凭借图片的间隙 140 | :param png_dir: 图片的路径 141 | :param out_dir: 导出图片路径 142 | """ 143 | super(GenLongPng2, self).__init__() 144 | self.png_row = png_row 145 | self.png_column = png_column 146 | self.png_interval = png_interval 147 | self.png_dir = png_dir # 图片地址 148 | self.out_dir = out_dir # 导出的长图地址 149 | self.long_png_path = None # 长图路径 150 | 151 | def run(self): 152 | """ 153 | 用于图片拼接 154 | :return: 155 | """ 156 | if not path.exists(self.out_dir): 157 | mkdir(self.out_dir) 158 | else: 159 | list1 = listdir(self.out_dir) 160 | data = [remove(self.out_dir + '/' + li) for li in list1] # 清空原有文件 161 | self.long_png_path = self.out_dir + '/' + 'page_{}.png' # 长图地址 162 | png_list = listdir(self.png_dir) # 获取所有图片 163 | try: # 尝试排序 164 | png_list.sort(key=lambda m: int(m[5: len(m) - 4])) 165 | except Exception as err: 166 | print('排序失败') 167 | print(err) 168 | ims = [Image.open(path.join(self.png_dir, fn)) for fn in png_list if fn.endswith('.png')] # 获取所有图片信息 169 | if len(ims) > (self.png_row * self.png_column): 170 | number = ceil(len(ims) / (self.png_row * self.png_column)) # 向上取整,判断可以分成的长图的个数 171 | if number > 1: 172 | remain = len(ims) % (self.png_row * self.png_column) # 余数,判断最后一张长图剩余数 173 | else: 174 | remain = 0 175 | else: 176 | number = 1 177 | remain = len(ims) 178 | for x in range(number): # 开始循环生成长图 179 | if remain > 0: # 如果余数大于0 180 | if x < number - 1: # 如果不是最后一张图 181 | self.paste_png(ims, self.png_row * self.png_column, x) # 按最大拼接拼接图片 182 | else: # 如果是最后一张图 183 | self.paste_png(ims, remain, x) # 按余数拼接图片 184 | elif remain == 0: # 如果余数为零 185 | self.paste_png(ims, self.png_row * self.png_column, x) # 按最大拼接图片,不用考虑最后一张图 186 | self.trigger.emit(x, number) # 发射信号 187 | 188 | def paste_png(self, ims, n1, x): 189 | """ 190 | 此方法用于粘贴单个长图 191 | :param ims: 所有图片的信息 192 | :param n1: 本次预计拼接的图片数量 193 | :param x: x为当前图片的页码数减1 194 | """ 195 | width, height = ims[0].size # 获取单张图片大小 196 | # 创建空白白图备用 197 | whiter_picture2 = Image.new("RGB", (width, height), "#FFFFFF") 198 | # 判断本次是否会产生空白图 199 | # -- 如果没有空白 -- # 200 | if n1 == self.png_column * self.png_row: 201 | new_width = width * self.png_column + (self.png_column - 1) * self.png_interval 202 | new_height = height * self.png_row + (self.png_row - 1) * self.png_interval 203 | else: 204 | # 如果只有一行图 205 | if n1 <= self.png_column: 206 | new_width = width * n1 + (n1 - 1) * self.png_interval 207 | new_height = height 208 | else: 209 | rows = ceil(n1 / self.png_column) # 计算总行数 210 | new_width = width * self.png_column + (self.png_column - 1) * self.png_interval 211 | new_height = height * rows + (rows - 1) * self.png_interval 212 | if self.png_column != 1: # 如果存在多余空白框 213 | # -- 用于填充--- 214 | need_n = self.png_column * self.png_row - n1 215 | n1 = self.png_column * self.png_row 216 | # 批量插入白图 217 | for i in range(need_n): 218 | ims.append(whiter_picture2) 219 | print(len(ims)) 220 | # 创建透明大图 221 | big_picture = Image.new(ims[0].mode, (new_width, new_height)) 222 | for y, im in enumerate(ims[x * self.png_row * self.png_column: self.png_row * self.png_column * x + n1]): 223 | p = y + 1 224 | row = int(y / self.png_column) + 1 225 | p = p - self.png_column * (row - 1) 226 | big_picture.paste(im, box=((p-1) * (width + self.png_interval), (row-1) * (height + self.png_interval))) 227 | big_picture.save(self.long_png_path.format(x)) # 长图保存 228 | 229 | 230 | class ZipPng(QThread): 231 | trigger = pyqtSignal(int, int) # 设定信号发送器 232 | 233 | def __init__(self, width_model, picture_type, zip_num, png_dir): 234 | """ 235 | 初始化构造函数 236 | :param width_model: 图片宽度模式,普通样式还是720p/1080p 237 | :param zip_num: 保存图片压缩率 238 | :param picture_type: 图片格式,png/jpg 239 | :param png_dir:待压缩图片来源 240 | """ 241 | super().__init__() 242 | self.width_model = width_model 243 | self.picture_type = picture_type 244 | self.zip_num = zip_num 245 | self.png_dir = png_dir 246 | 247 | def run(self): 248 | """ 249 | 用于压缩文件 250 | :return: 251 | """ 252 | parent_dir = path.split(self.png_dir)[0] # 找到图片的父路径 253 | if self.picture_type == '720p': 254 | c = parent_dir + '/' + '720P图(压缩后)' 255 | elif self.picture_type == '1080p': 256 | c = parent_dir + '/' + '1080P图(压缩后)' 257 | else: # 如果是普通图片 258 | c = parent_dir + '/' + '普通图(压缩后)' 259 | if not path.exists(c): 260 | mkdir(c) 261 | else: 262 | list1 = listdir(c) 263 | for li in list1: 264 | remove(c + '/' + li) 265 | png_list = listdir(self.png_dir) 266 | zip_picture = c + '/' + 'page_{}.' + self.picture_type # 压缩图输出路径 267 | png_list = listdir(self.png_dir) # 计算未压缩的长图路径里面的图片数量 268 | try: 269 | png_list.sort(key=lambda m: int(m[5: len(m) - 4])) # 文件排序 270 | except Exception as err: 271 | print('排序失败') 272 | print(err) 273 | png_0 = self.png_dir + '/' + png_list[0] 274 | png_file_size = path.getsize(png_0) # 第一张图片文件大小 275 | e = round(sqrt(png_file_size / (1024 ** 2)), 4) # 计算要压缩的倍数,平方根,取3位小数,这里取这里根据图片数量自动计算压缩倍数 276 | print(e) 277 | # 获取所有图片信息 278 | ims = [Image.open(self.png_dir + '/' + fn) for fn in png_list if fn.endswith('.png') or fn.endswith('jpg')] 279 | i = 0 280 | for im in ims: 281 | w, h = im.size # 获取文件的宽、高 282 | if self.picture_type == '720p': 283 | w1 = 720 284 | h1 = 720 * h / w 285 | elif self.picture_type == '1080p': 286 | w1 = 1080 287 | h1 = 1080 * h / w 288 | else: # 如果是普通模式 289 | w1 = w / e 290 | h1 = h / e 291 | im.thumbnail((int(w1), int(h1))) # 缩放并取整 292 | im.save(zip_picture.format(i), quality=int(self.zip_num)) # 保存缩放后的图片,保存质量92% 293 | i += 1 294 | self.trigger.emit(i, len(png_list)) # 发送信号 295 | 296 | 297 | if __name__ == '__main__': 298 | pass 299 | -------------------------------------------------------------------------------- /tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tlntin/PyQt5_PdfTools/74bad54dc3e75c075595695d3b5c1b6f0e2ba5f1/tools.png -------------------------------------------------------------------------------- /tools_png.py: -------------------------------------------------------------------------------- 1 | img = "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" -------------------------------------------------------------------------------- /ui.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QMainWindow, QMenuBar, QApplication, QAction, QWidget, QPushButton, QLabel, QLineEdit,\ 2 | QComboBox, QDialog, QDesktopWidget 3 | from PyQt5.QtGui import QIcon, QFont 4 | from PyQt5.QtCore import Qt 5 | import sys 6 | import json 7 | from os import path, getcwd 8 | 9 | 10 | class UI(QMainWindow): 11 | def __init__(self): 12 | super(UI, self).__init__() 13 | # -- 基础设置 -- # 14 | self.cp = QDesktopWidget().availableGeometry().center() 15 | qr = self.frameGeometry() 16 | qr.moveCenter(self.cp) 17 | self.move(qr.topLeft()) 18 | self.resize(340, 150) 19 | self.setMinimumSize(340, 150) 20 | self.setMaximumSize(340, 150) 21 | self.setWindowTitle('pdf转长图助手') 22 | self.setWindowIcon(QIcon('tools.png')) 23 | # --字体设置--# 24 | # 字体8号 25 | self.font_8 = QFont() 26 | self.font_8.setPointSize(8) 27 | # 字体9号 28 | self.font_9 = QFont() 29 | self.font_9.setPointSize(9) 30 | 31 | # --菜单控件设置 -- # 32 | self.menubar = QMenuBar() # 这里暂时不设置父类 33 | self.menubar.setFont(self.font_8) 34 | menu = self.menubar.addMenu('&菜单') 35 | self.menu_about = self.menubar.addMenu('&关于') 36 | self.action4 = QAction('版本号', self) 37 | self.action4.setFont(self.font_8) 38 | self.menu_about.addAction(self.action4) 39 | self.action4.triggered.connect(self.about_dialog) # 菜单栏4触发器 40 | self.action1 = QAction('使用说明', self) 41 | self.action1.setFont(self.font_8) 42 | self.action2 = QAction('更新记录', self) 43 | self.action2.setFont(self.font_8) 44 | self.action3 = QAction('我要反馈', self) 45 | self.action3.setFont(self.font_8) 46 | menu.addAction(self.action1) 47 | menu.addAction(self.action2) 48 | menu.addAction(self.action3) 49 | 50 | # -- 菜单控制连接器 51 | self.action1.triggered.connect(self.notice_dialog) # 菜单栏1触发器 52 | self.action2.triggered.connect(self.update_dialog) # 菜单栏2触发器 53 | self.action3.triggered.connect(self.feedback_dialog) # 菜单栏3触发器 54 | 55 | # -- 窗口1 56 | self.frame1 = None 57 | self.line_edit1_1 = None # 窗口1的文件夹路径 58 | self.pdf_path = None # 窗口1的pdf路径 59 | self.push_button1_1 = QPushButton() # 按钮1_1,选择文件 60 | self.push_button1_2 = QPushButton() # 按钮1_2,pdf转png 61 | self.push_button1_3 = QPushButton() # 按钮1_3,我要退出 62 | 63 | # --窗口2 64 | self.frame2 = None 65 | self.line_edit2_1 = None # 输入框 66 | self.pdf_dir1 = None # pdf转成图片后的文件夹 67 | self.push_button2_1 = QPushButton() # 按钮2_1:自定义路径 68 | self.push_button2_2 = QPushButton() # 按钮2_2:一键长图 69 | self.push_button2_3 = QPushButton() # 按钮2_3:自定义拼接 70 | 71 | # --窗口3 72 | self.frame3 = None 73 | self.pdf_dir2 = None 74 | self.line_edit3_1 = None # 输入框 75 | self.combo_box3_1 = None # 多选框3_1 76 | self.combo_box3_2 = None # 多选框3_2 77 | self.line_edit3_2 = None # 输入框百分比 78 | self.push_button3_1 = QPushButton() # 自定义路径 79 | self.push_button3_2 = QPushButton() # 一键压缩 80 | self.push_button3_3 = QPushButton() # 退出软件 81 | 82 | # --- dialog会话 -- 83 | self.dialog_line_edit1 = None # 会话输入框1:拼接数量 84 | self.dialog_line_edit2 = None # 会话输入框2:拼接列数 85 | self.dialog_line_edit3 = None # 会话输入框3:拼接行数 86 | self.dialog_line_edit4 = None # 会话输入框4:拼接间隙 87 | self.dialog_push_button1 = None # 会话按钮1:开始单列拼接 88 | self.dialog_push_button2 = None # 会话按钮1:开始矩阵拼接 89 | self.dialog1 = None # 单列拼接 90 | self.dialog2 = None # 矩阵拼接 91 | 92 | # --运行主界面--# 93 | self.init_ui() 94 | 95 | def init_ui(self): 96 | self.show_frame1() 97 | self.show() 98 | 99 | def show_frame1(self): 100 | self.frame1 = QWidget(self) 101 | super(QWidget, self.frame1).__init__() 102 | # --按钮标签1:格式转换,用来代替按钮1 103 | button_label1 = QLabel(self.frame1) 104 | button_label1.setGeometry(90, 0, 80, 24) 105 | button_label1.setFont(self.font_8) 106 | button_label1.setText('格式转换') 107 | button_label1.setAlignment(Qt.AlignCenter) 108 | # 转换按键 109 | # --按钮2:图片拼接 110 | push_button2 = QPushButton(self.frame1) 111 | push_button2.setGeometry(170, 0, 80, 24) 112 | push_button2.setFont(self.font_8) 113 | push_button2.setText('图片拼接') 114 | push_button2.clicked.connect(self.show_frame2) # 连接件显示窗口2 115 | # --按钮3:图片压缩 116 | push_button3 = QPushButton(self.frame1) 117 | push_button3.setGeometry(250, 0, 80, 24) 118 | push_button3.setFont(self.font_8) 119 | push_button3.setText('图片压缩') 120 | push_button3.clicked.connect(self.show_frame3) # 连接件显示窗口3 121 | 122 | # --标签1:pdf路径 123 | label1_1 = QLabel(self.frame1) 124 | label1_1.setText('pdf路径') 125 | label1_1.setFont(self.font_8) 126 | label1_1.setAlignment(Qt.AlignCenter) 127 | label1_1.setGeometry(15, 40, 69, 20) 128 | 129 | # --路径1:pdf路径 130 | self.line_edit1_1 = QLineEdit(self.frame1) 131 | self.line_edit1_1.setFont(self.font_8) 132 | self.line_edit1_1.setGeometry(90, 40, 220, 26) 133 | # 加入上次的路径保留 134 | pdf_path = self.read_data_dict('pdf_path') 135 | if pdf_path is not None: 136 | self.line_edit1_1.setText(pdf_path) 137 | 138 | # -- 按钮1_1:选择pdf文件 139 | self.push_button1_1 = QPushButton(self.frame1) 140 | self.push_button1_1.setGeometry(10, 95, 93, 29) 141 | self.push_button1_1.setText('选择文件') 142 | self.push_button1_1.setFont(self.font_9) 143 | # -- 按钮1_2:pdf转png 144 | self.push_button1_2 = QPushButton(self.frame1) 145 | self.push_button1_2.setGeometry(115, 95, 93, 29) 146 | self.push_button1_2.setText('pdf转png') 147 | self.push_button1_2.setFont(self.font_9) 148 | # -- 按钮1_3:我要退出 149 | self.push_button1_3 = QPushButton(self.frame1) 150 | self.push_button1_3.setGeometry(220, 95, 93, 29) 151 | self.push_button1_3.setText('退出软件') 152 | self.push_button1_3.setFont(self.font_9) 153 | 154 | # 移动其它控件 155 | self.menubar.setParent(self.frame1) # 设置菜单栏依赖于窗口1 156 | self.setCentralWidget(self.frame1) # 设置主窗口的当前窗口为窗口1 157 | self.frame1.setVisible(True) 158 | 159 | def show_frame2(self): 160 | # 重新定义构件 161 | self.frame2 = QWidget(self) 162 | super(QWidget, self.frame2).__init__() 163 | # --按钮1:格式转换 164 | push_button1 = QPushButton(self.frame2) 165 | push_button1.setGeometry(90, 0, 80, 24) 166 | push_button1.setFont(self.font_8) 167 | push_button1.setText('格式转换') 168 | push_button1.clicked.connect(self.show_frame1) 169 | 170 | # --按钮标签2:格式转换,用来代替按钮2 171 | button_label2 = QLabel(self.frame2) # 暂时不设置父标签 172 | button_label2.setGeometry(170, 0, 80, 24) 173 | button_label2.setFont(self.font_8) 174 | button_label2.setText('图片拼接') 175 | button_label2.setAlignment(Qt.AlignCenter) 176 | # --按钮3:图片压缩 177 | push_button3 = QPushButton(self.frame2) 178 | push_button3.setGeometry(250, 0, 80, 24) 179 | push_button3.setFont(self.font_8) 180 | push_button3.setText('图片压缩') 181 | push_button3.clicked.connect(self.show_frame3) # 连接件显示窗口3 182 | 183 | # -- 标签2_1:文件夹路径 184 | label2_1 = QLabel(self.frame2) 185 | label2_1.setGeometry(15, 40, 80, 20) 186 | label2_1.setText('文件夹路径') 187 | label2_1.setFont(self.font_8) 188 | 189 | # -- 输入框2_1:文件夹路径 190 | self.line_edit2_1 = QLineEdit(self.frame2) 191 | self.line_edit2_1.setGeometry(90, 40, 220, 26) 192 | self.line_edit2_1.setFont(self.font_8) 193 | 194 | # 加入默认路径 195 | pdf_path = self.read_data_dict('pdf_path') 196 | if pdf_path is not None: 197 | dir1 = path.split(pdf_path)[0] 198 | self.pdf_dir1 = dir1 + '/导出图片' 199 | self.line_edit2_1.setText(self.pdf_dir1) 200 | 201 | # -- 按钮2_1:自定义路径 202 | self.push_button2_1 = QPushButton(self.frame2) 203 | self.push_button2_1.setGeometry(10, 95, 93, 29) 204 | self.push_button2_1.setText('自定义路径') 205 | self.push_button2_1.setFont(self.font_9) 206 | # -- 按钮2_2:一键长图 207 | self.push_button2_2 = QPushButton(self.frame2) 208 | self.push_button2_2.setGeometry(115, 95, 93, 29) 209 | self.push_button2_2.setText('一键长图') 210 | self.push_button2_2.setFont(self.font_9) 211 | # -- 按钮2_3:自定义拼接 212 | self.push_button2_3 = QPushButton(self.frame2) 213 | self.push_button2_3.setGeometry(220, 95, 93, 29) 214 | self.push_button2_3.setText('自定义拼接') 215 | self.push_button2_3.setFont(self.font_9) 216 | self.push_button2_3.clicked.connect(self.show_dialog) 217 | # -- 移动其它控件 218 | self.menubar.setParent(self.frame2) # 设置菜单栏依赖于窗口2 219 | self.setCentralWidget(self.frame2) 220 | self.frame2.setVisible(True) 221 | 222 | def show_frame3(self): 223 | self.frame3 = QWidget(self) 224 | super(QWidget, self.frame3).__init__() 225 | # --按钮1:格式转换 226 | push_button1 = QPushButton(self.frame3) 227 | push_button1.setGeometry(90, 0, 80, 24) 228 | push_button1.setFont(self.font_8) 229 | push_button1.setText('格式转换') 230 | push_button1.clicked.connect(self.show_frame1) 231 | 232 | # --按钮2:图片拼接 233 | push_button2 = QPushButton(self.frame3) 234 | push_button2.setGeometry(170, 0, 80, 24) 235 | push_button2.setFont(self.font_8) 236 | push_button2.setText('图片拼接') 237 | push_button2.clicked.connect(self.show_frame2) # 连接件显示窗口2 238 | # -- 设置主窗口 239 | self.setCentralWidget(self.frame3) 240 | 241 | # -- 标签3:图片压缩 242 | button_label3 = QLabel(self.frame3) 243 | button_label3.setGeometry(250, 0, 80, 24) 244 | button_label3.setFont(self.font_8) 245 | button_label3.setText('图片压缩') 246 | button_label3.setAlignment(Qt.AlignCenter) 247 | # -- 标签3_1:文件夹路径 248 | label3_1 = QLabel(self.frame3) 249 | label3_1.setGeometry(15, 40, 80, 20) 250 | label3_1.setText('文件夹路径') 251 | label3_1.setFont(self.font_8) 252 | # -- 输入框3_1:文件夹路径 253 | self.line_edit3_1 = QLineEdit(self.frame3) 254 | self.line_edit3_1.setFont(self.font_8) 255 | self.line_edit3_1.setGeometry(90, 40, 220, 26) 256 | # 加入默认路径 257 | pdf_path = self.read_data_dict('pdf_path') 258 | if pdf_path is not None: 259 | dir1 = path.split(pdf_path)[0] 260 | self.pdf_dir2 = dir1 + '/单列长图' 261 | self.line_edit3_1.setText(self.pdf_dir2) 262 | # -- 标签3_2:宽度 263 | label3_2 = QLabel(self.frame3) 264 | label3_2.setGeometry(10, 80, 35, 20) 265 | label3_2.setText('宽度') 266 | label3_2.setFont(self.font_8) 267 | # -- 多选框3_1 268 | self.combo_box3_1 = QComboBox(self.frame3) 269 | self.combo_box3_1.setGeometry(45, 80, 60, 25) 270 | self.combo_box3_1.addItems(['普通', '720p', '1080p']) 271 | self.combo_box3_1.setFont(self.font_8) 272 | # -- 多选框3_2 273 | self.combo_box3_2 = QComboBox(self.frame3) 274 | self.combo_box3_2.setGeometry(155, 80, 50, 25) 275 | self.combo_box3_2.addItems(['jpg', 'png']) 276 | self.combo_box3_2.setFont(self.font_8) 277 | # -- 标签3_3 278 | label3_3 = QLabel(self.frame3) 279 | label3_3.setGeometry(120, 80, 40, 20) 280 | label3_3.setText('格式') 281 | label3_3.setFont(self.font_8) 282 | # -- 标签3_4 283 | label3_4 = QLabel(self.frame3) 284 | label3_4.setGeometry(220, 80, 51, 20) 285 | label3_4.setText('压缩比') 286 | label3_4.setFont(self.font_8) 287 | # -- 输入框3_2:压缩百分比 288 | self.line_edit3_2 = QLineEdit(self.frame3) 289 | self.line_edit3_2.setGeometry(265, 80, 25, 25) 290 | self.line_edit3_2.setFont(self.font_8) 291 | # -- 标签3_5 292 | label3_4 = QLabel(self.frame3) 293 | label3_4.setGeometry(295, 80, 14, 20) 294 | label3_4.setText('%') 295 | label3_4.setFont(self.font_8) 296 | # -- 按键3_1:自定义路径 297 | self.push_button3_1 = QPushButton(self.frame3) 298 | self.push_button3_1.setGeometry(10, 115, 93, 29) 299 | self.push_button3_1.setText('自定义路径') 300 | self.push_button3_1.setFont(self.font_9) 301 | # -- 按键3_2:一键压缩 302 | self.push_button3_2 = QPushButton(self.frame3) 303 | self.push_button3_2.setGeometry(115, 115, 93, 29) 304 | self.push_button3_2.setText('一键压缩') 305 | self.push_button3_2.setFont(self.font_9) 306 | # -- 按键3_3:退出软件 307 | self.push_button3_3 = QPushButton(self.frame3) 308 | self.push_button3_3.setGeometry(220, 115, 93, 29) 309 | self.push_button3_3.setText('我要退出') 310 | self.push_button3_3.setFont(self.font_9) 311 | # -- 设置其它依赖转移过来 312 | self.menubar.setParent(self.frame3) # 设置菜单栏依赖于窗口3 313 | self.frame3.setVisible(True) 314 | 315 | def show_dialog(self): 316 | self.show_dialog1() 317 | 318 | def show_dialog1(self): 319 | if self.dialog2 is not None: 320 | self.dialog2.close() 321 | self.dialog1 = QDialog(self) 322 | self.dialog1.resize(340, 150) 323 | self.dialog1.setMinimumSize(340, 150) 324 | self.dialog1.setMaximumSize(340, 150) 325 | self.dialog1.setWindowIcon(QIcon('tools.png')) 326 | self.dialog1.setWindowTitle('自定义拼接') 327 | self.dialog1.setFont(self.font_8) 328 | 329 | # -- 设置标签1--- 330 | label1 = QLabel(self.dialog1) 331 | label1.setGeometry(40, 10, 70, 29) 332 | label1.setText('单列拼接') 333 | label1.setAlignment(Qt.AlignCenter) 334 | # -- 设置按键2 --- 335 | push_button2 = QPushButton(self.dialog1) 336 | push_button2.setGeometry(40, 70, 70, 29) 337 | push_button2.setText('矩阵拼接') 338 | push_button2.clicked.connect(self.show_dialog2) 339 | 340 | # -- 标签2:拼接数量 341 | label2 = QLabel(self.dialog1) 342 | label2.setText('拼接数量') 343 | label2.setGeometry(150, 20, 51, 20) 344 | 345 | # -- 输入框:单列拼接数量 346 | self.dialog_line_edit1 = QLineEdit(self.dialog1) 347 | self.dialog_line_edit1.setGeometry(210, 20, 30, 20) 348 | self.dialog_push_button1 = QPushButton(self.dialog1) 349 | self.dialog_push_button1.setGeometry(150, 60, 93, 25) 350 | self.dialog_push_button1.setText('开始单列拼接') 351 | 352 | def show_dialog2(self): 353 | if self.dialog1 is not None: 354 | self.dialog1.close() # 关闭会话1 355 | self.dialog2 = QDialog(self) 356 | self.dialog2.resize(340, 150) 357 | self.dialog2.setFont(self.font_8) 358 | self.dialog2.setMinimumSize(340, 150) 359 | self.dialog2.setMaximumSize(340, 150) 360 | self.dialog2.setWindowIcon(QIcon('tools.png')) 361 | self.dialog2.setWindowTitle('矩阵拼接') 362 | # -- 设置按键1--- 363 | push_button1 = QPushButton(self.dialog2) 364 | push_button1.setGeometry(40, 10, 70, 29) 365 | push_button1.setText('单列拼接') 366 | push_button1.clicked.connect(self.show_dialog1) 367 | 368 | # -- 设置标签2--- 369 | label1 = QLabel(self.dialog2) 370 | label1.setGeometry(40, 70, 70, 29) 371 | label1.setText('矩阵拼接') 372 | label1.setAlignment(Qt.AlignCenter) 373 | 374 | # -- 标签2:拼接列数 375 | label2 = QLabel(self.dialog2) 376 | label2.setText('拼接行数') 377 | label2.setGeometry(160, 20, 51, 20) 378 | # -- 标签3:拼接行数 379 | label3 = QLabel(self.dialog2) 380 | label3.setText('拼接列数') 381 | label3.setGeometry(160, 45, 51, 20) 382 | # -- 标签4:拼接行数 383 | label4 = QLabel(self.dialog2) 384 | label4.setText('拼接间隙') 385 | label4.setGeometry(160, 70, 51, 20) 386 | 387 | # -- 标签5:行数 388 | label4 = QLabel(self.dialog2) 389 | label4.setText('行') 390 | label4.setGeometry(260, 20, 41, 20) 391 | 392 | # -- 标签6:列数 393 | label4 = QLabel(self.dialog2) 394 | label4.setText('列') 395 | label4.setGeometry(260, 50, 41, 20) 396 | 397 | # -- 标签7:px像素 398 | label4 = QLabel(self.dialog2) 399 | label4.setText('px') 400 | label4.setGeometry(260, 70, 41, 20) 401 | 402 | # -- 输入框:拼接列数 403 | self.dialog_line_edit2 = QLineEdit(self.dialog2) 404 | self.dialog_line_edit2.setGeometry(220, 20, 30, 20) 405 | # -- 输入框:拼接行数 406 | self.dialog_line_edit3 = QLineEdit(self.dialog2) 407 | self.dialog_line_edit3.setGeometry(220, 45, 30, 20) 408 | # -- 输入框:拼接间隙 409 | self.dialog_line_edit4 = QLineEdit(self.dialog2) 410 | self.dialog_line_edit4.setGeometry(220, 70, 30, 20) 411 | 412 | self.dialog_push_button2 = QPushButton(self.dialog2) 413 | self.dialog_push_button2.setGeometry(160, 100, 93, 25) 414 | self.dialog_push_button2.setText('开始矩阵拼接') 415 | 416 | def notice_dialog(self): 417 | dialog = QDialog(self) 418 | dialog.resize(500, 450) 419 | dialog.setMinimumSize(340, 150) 420 | text = """\ 421 | 使用说明: 422 | 主要功能有格式转换、图片拼接,图片压缩三项
423 | 格式转换:目前只支持pdf转png
424 | 图片拼接
425 | 1.支持自定义拼接路径。
426 | 2.默认路径是你转成png图片后的路径。
427 | 3.可以设置为一键拼接成一张长图。
428 | 4.自定义拼接还可以按单列拼成长图,或者按矩阵进行拼接。
429 | 5.单列拼接指的是按多张图拼接成一张图,比如15张图,
430 | 可以按5张图拼成一张长图,这样就获得了3张长图。
431 | 6.矩阵拼接类似微信的九宫格,比如3行三列为一张长图,
432 | 中间可以设置10像素的间隙,然后可以获得多张这样的长图。
433 | 7.矩阵拼接兼容单列拼接,即列为1,间隙为0时即为矩阵拼接。
434 | 图片压缩
435 | 1.关于宽度
436 | 普通模式即自动压缩成1M以内,
437 | 720p和1080p则自动压缩图片为这个宽度
438 | 2.关于格式
439 | 即压缩后图片的格式是jpg还是png
440 | 3.关于压缩比
441 | 即压缩成jpg格式时,图片的质量百分比。 442 | """ 443 | dialog.setWindowTitle('使用说明') 444 | label = QLabel(text, dialog) 445 | label.setFont(self.font_9) 446 | dialog.show() 447 | 448 | def update_dialog(self): 449 | """ 450 | 更新记录 451 | """ 452 | dialog = QDialog(self) 453 | dialog.resize(500, 450) 454 | dialog.setMinimumSize(340, 150) 455 | text = """\ 456 | V1.1版更新记录:
457 | 1.增加窗口抽屉功能,将图片转换、图片拼接、图片压缩功能拆分。
458 | 2.增加更多自定义功能,可以自定义图片文件夹来源,
459 | 还可以自定义拼接方式了。
460 | 3.优化了压缩图片算法的一个小bug,增加了压缩图片的最终格式自定义,
461 | 可以选择压缩为jpg或者png。
462 | 4.自定义拼接中,单列拼接与矩阵拼接多了一个翻转的过度动画。
463 | 5.加入操作历史记录保留,自动保留上次的操作的方式
464 | 注意,只会保留你的最后一次转图片、拼接图片、压缩图片的记录,
465 | 选择文件的记录不在此列
466 | 6.导出路径更改为与原pdf路径同级
467 | 7.导出图片时自动删除上次导出的图片
468 | 7.其他更新请阅读使用说明
469 |
470 | V1.0版更新记录:
471 | 1.使用PyQt5重构了界面
472 | 2.增加了进度条,减少等待时间
473 | 3.选择pdf文件时,默认上次的路径
474 | 4.增加了二次确认操作
475 | 5.使用多线程,提高了软件速度
476 | 6.美化了GUI,变得好看了
477 | """ 478 | 479 | dialog.setWindowTitle('更新记录') 480 | label = QLabel(text, dialog) 481 | label.setFont(self.font_9) 482 | dialog.show() 483 | 484 | def feedback_dialog(self): 485 | """ 486 | 我要反馈 487 | """ 488 | dialog = QDialog(self) 489 | dialog.resize(500, 50) 490 | dialog.setMinimumSize(340, 150) 491 | text = """\ 492 | qq群:974759263
493 | """ 494 | 495 | dialog.setWindowTitle('我要反馈') 496 | label = QLabel(text, dialog) 497 | label.setFont(self.font_9) 498 | dialog.show() 499 | 500 | def about_dialog(self): 501 | dialog = QDialog(self) 502 | dialog.resize(500, 50) 503 | dialog.setMinimumSize(340, 150) 504 | text = """\ 505 | 版本号:V1.1
506 | 编译日期:2020年6月3日
507 | """ 508 | dialog.setWindowTitle('关于') 509 | label = QLabel(text, dialog) 510 | label.setFont(self.font_9) 511 | dialog.show() 512 | 513 | # 公用读取字典 514 | @staticmethod 515 | def read_data_dict(key, data_path='./data.json'): 516 | """ 517 | 根据索引读取data.json的内容 518 | :param key: 索引 519 | :param data_path: json所在路径 520 | :return: data:读取到的值 521 | """ 522 | with open(data_path, 'rt', encoding='utf-8') as f1: 523 | data_dict1 = json.load(f1) 524 | data = data_dict1.get(key, None) # 没有内容则为None 525 | return data 526 | 527 | # 公用写入字典 528 | @staticmethod 529 | def write_data_dict(key, value2, data_path='./data.json'): 530 | """ 531 | 根据索引读取data.json的内容 532 | :param key: 写入键 533 | :return: value2:写入值 534 | :param data_path: json所在路径 535 | """ 536 | f1 = open(data_path, 'rt', encoding='utf-8') 537 | data_dict1 = json.load(f1) 538 | f1.close() 539 | with open(data_path, 'wt', encoding='utf-8') as f2: 540 | data_dict1[key] = value2 541 | json.dump(data_dict1, f2) 542 | 543 | 544 | if __name__ == '__main__': 545 | # 创建一个data.json文件,用于储存变量参数 546 | if not path.exists('data.json'): 547 | data_dict = {} 548 | with open('data.json', 'wt', encoding='utf-8') as f: 549 | json.dump(data_dict, f) 550 | app = QApplication(sys.argv) 551 | ui = UI() 552 | sys.exit(app.exec_()) 553 | 554 | 555 | --------------------------------------------------------------------------------