Skleciłem sobie małą apkę wspomagającą konwersję obrazków tak, aby się nadawały do wrzucenia w plikach *.cbz
do czytnika ebooków.
Czasem obrazki, z którymi mam do czynienia mają duży rozmiar. Np. wysokość może wynieść 3000px
.
Rozdzielczość mojego czytnika to 1440x1920
, więc dobrze jest zmniejszyć obrazki, żeby ładowały się szybciej i przy okazji zajmowały mniej miejsca.
Nie potrzebujemy większych obrazków, jeśli nie zamierzamy robić zooma podczas czytania.
Nazwy plików bywają takie: 1_1.jpg
, 1_2.jpg
, ..., 1_9.jpg
, 1_10.jpg
, 1_11.jpg
.
A to się źle sortuje, jeśli soft nie sortuje plików według wartości liczbowych. Wtedy np. 1_10.jpg
jest po 1_1.jpg
, zamiast po 1_9.jpg
.
Windows 10 dobrze sortuje takie pliki, ale mój czytnik ebooków nie, dlatego zmieniam wszystkie liczby w nazwach plików na "stałą szerokość":
000001_000001.jpg
, 000001_000002.jpg
, ..., 000001_000009.jpg
, 000001_000010.jpg
, 000001_000011.jpg
Przez jakiś czas po prostu w każdym folderze uruchamiałem skrypty, które wykonują operacje dostosowujące obrazki dla czytnika ebooków.
Teraz w końcu zebrałem te skrypty w jedną apkę z GUI. Wystarczy przeciągać pliki i katalogi na okienko aplikacji, a ona według zakodowanych kryteriów skonwertuje pliki.
Nie trzeba czekać aż wszystkie pliki z jednego dropa zostaną przetworzone. Można na to samo okienko przeciągać kolejne pliki/foldery jeden za drugim.
Sam szkielet aplikacji może się komuś przydać. Wystarczy podmienić operacje, które będą wykonywane na liście dropniętych plików i już macie apkę dostosowaną do Waszych potrzeb :]
Kod:
# Requires Python 3.5+, PyQt5 and ImageMagick installed!
# pip install PyQt5
# https://imagemagick.org/script/download.php
# ImageMagick should be present in system PATH
import sys, os, re, glob
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QLabel,
QCheckBox,
)
from PyQt5.QtCore import (
Qt,
QUrl,
QRunnable,
QThreadPool,
)
class Runnable(QRunnable):
def __init__(self, links, mogrifyChecked, renameChecked, statusLabel):
super().__init__()
self.links = links
self.mogrifyChecked = mogrifyChecked
self.renameChecked = renameChecked
self.statusLabel = statusLabel
def run(self):
self.unpack_dirs()
self.process_files()
def unpack_dirs(self):
result = []
for link in self.links:
if os.path.isdir(link):
for imagePath in glob.iglob(os.path.join(link, '**/*.jpg'), recursive=True):
result.append(imagePath)
for imagePath in glob.iglob(os.path.join(link, '**/*.png'), recursive=True):
result.append(imagePath)
for imagePath in glob.iglob(os.path.join(link, '**/*.jpeg'), recursive=True):
result.append(imagePath)
else:
_, ext = os.path.splitext(link)
if ext.lower() in (".jpg", ".png", ".jpeg"):
result.append(link)
self.links = result
def process_files(self):
links_count = len(self.links)
for i, link in enumerate(self.links):
self.statusLabel.setText("%04d/%04d"%(i + 1, links_count,))
if self.mogrifyChecked:
cmd = 'magick mogrify -resize "1800x1800>" -colorspace gray "%s"'%(link,)
os.system(cmd)
if self.renameChecked:
os.rename(link, self.get_rename_filename(link))
def get_rename_filename(self, current_path):
file_path, file_name = os.path.split(current_path)
file_name = re.sub('\d+', self.process_filename_number, file_name)
return os.path.join(file_path, file_name)
def process_filename_number(self, number_re_match):
return "%06d"%(int(number_re_match.group(0)),)
class DropBatch(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Drop Batch")
self.setWindowFlags(Qt.MSWindowsFixedSizeDialogHint | Qt.WindowStaysOnTopHint)
self.setStyleSheet("background-color: darkblue;")
self.setAcceptDrops(True)
#self.resize(350, 200)
self.setGeometry(20, 50, 350, 200)
self.statusLabel = QLabel("Drag files and folders here!", self)
self.statusLabel.setStyleSheet("color: white;")
self.statusLabel.setGeometry(50, 20, 250, 30)
self.mogrifyCheckbox = QCheckBox("Mogrify", self)
self.mogrifyCheckbox.setStyleSheet("color: white;")
self.mogrifyCheckbox.setGeometry(50, 100, 250, 25)
self.mogrifyCheckbox.setChecked(True)
self.renameCheckbox = QCheckBox("Rename", self)
self.renameCheckbox.setStyleSheet("color: white;")
self.renameCheckbox.setGeometry(50, 130, 250, 25)
self.renameCheckbox.setChecked(True)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
if url.isLocalFile():
links.append(str(url.toLocalFile()))
else:
pass
#links.append(str(url.toString()))
mogrifyChecked = self.mogrifyCheckbox.isChecked() == True
renameChecked = self.renameCheckbox.isChecked() == True
pool = QThreadPool.globalInstance()
runnable = Runnable(links, mogrifyChecked, renameChecked, self.statusLabel)
pool.start(runnable)
else:
event.ignore()
app = QApplication(sys.argv)
window = DropBatch()
window.show()
sys.exit(app.exec_())