First Commit of PICT_numtool NG
This commit is contained in:
1
.gitignore
vendored
Executable file
1
.gitignore
vendored
Executable file
@@ -0,0 +1 @@
|
||||
/PICT_numtool_ui.pyc
|
||||
17
.project
Executable file
17
.project
Executable file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>PICT_numTool</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.python.pydev.PyDevBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.python.pydev.pythonNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
8
.pydevproject
Executable file
8
.pydevproject
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?eclipse-pydev version="1.0"?><pydev_project>
|
||||
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
||||
<path>/${PROJECT_DIR_NAME}</path>
|
||||
</pydev_pathproperty>
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">python</pydev_property>
|
||||
</pydev_project>
|
||||
3550
LOG-PICT_numtool.py.log
Executable file
3550
LOG-PICT_numtool.py.log
Executable file
File diff suppressed because it is too large
Load Diff
764
PICT_numtool.py
Executable file
764
PICT_numtool.py
Executable file
@@ -0,0 +1,764 @@
|
||||
#!/usr/bin/python
|
||||
'''
|
||||
=========================================================================================================
|
||||
T&T Software and Systems
|
||||
Project: Renumbering tool
|
||||
Module: Main module
|
||||
Description: Tool to renumber ohoto and video replace VB one
|
||||
|
||||
|
||||
Tiziano Trabattoni | 2016/12/10 | first version
|
||||
Tiziano Trabattoni | 2022/07/31 | Porting su MacOS, PyQt5, python3
|
||||
Tiziano Trabattoni | 2023/05/22 | Porting su MacOS per pyqt6
|
||||
Tiziano Trabattoni | 2023/08/14 | Sorting by Creation date with exiftool
|
||||
Tiziano Trabattoni | 2024/08/19 | Changed movie tags to include filemodification date for .mp4 from Android
|
||||
Tiziano Trabattoni | 2024/12/28 | Introduced possibility to ignore metadata and sort only by filename for Undetermined Metadata (old slides)
|
||||
|
||||
####### Developed under poetry (with pyqt6 M1 ARM64 code available)
|
||||
~/Develop/Personal/PICT_poetry » poetry remove pyqt5
|
||||
~/Develop/Personal/PICT_poetry » poetry add pyqt6
|
||||
|
||||
~/Develop/Personal/PICT_poetry » pip freeze ttrabatt@TTRABATT-M-3QXG
|
||||
PyQt6==6.5.0
|
||||
PyQt6-Qt6==6.5.0
|
||||
PyQt6-sip==13.5.1
|
||||
(pict-numtool-py3.9) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
~/Develop/Personal/PICT_poetry » poetry show ttrabatt@TTRABATT-M-3QXG
|
||||
pyqt6 6.5.0 Python bindings for the Qt cross platform application toolkit
|
||||
pyqt6-qt6 6.5.0 The subset of a Qt installation needed by PyQt6.
|
||||
pyqt6-sip 13.5.1 The sip module support for PyQt6
|
||||
(pict-numtool-py3.9) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
~/Develop/Personal/PICT_poetry »
|
||||
|
||||
|
||||
|
||||
=========================================================================================================
|
||||
'''
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
import subprocess
|
||||
import tempfile
|
||||
import json
|
||||
from datetime import datetime
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
''' PyQT6 imports'''
|
||||
from PICT_numtool_ui import Ui_MainWindow
|
||||
from PyQt6 import QtGui
|
||||
from PyQt6.QtWidgets import QTreeView, QLabel, QTreeWidgetItem, QMainWindow, QApplication, QFileDialog, QAbstractItemView
|
||||
from PyQt6.QtGui import QPixmap
|
||||
#from PyQt5.QtCore import *
|
||||
#from PyQt6 import QtCore
|
||||
global LOGGER
|
||||
|
||||
''' Module version '''
|
||||
mod_ver = "v2.1"
|
||||
mod_dver = "2023:08:17"
|
||||
|
||||
''' Logger Variables '''
|
||||
LOG_FORMAT = '%(asctime)s|%(levelname)-7s|%(funcName)-15s|%(lineno)-5d: %(message)-50s'
|
||||
LOG_TIME_FORMAT = '%m-%d %H:%M:%S'
|
||||
|
||||
CONSOLE_DEBUG = logging.INFO
|
||||
FILE_DEBUG = logging.INFO
|
||||
|
||||
PHOTO_filter = 'Images (*.jpg *.jpeg *.png *.tiff *.heic, *.tif)'
|
||||
MOVIE_filter = 'Movies (*.mov *.mp4)'
|
||||
|
||||
# file format and extension variables
|
||||
EXIFTOOL = "exiftool"
|
||||
EXIF_PHOTO_EXT = (".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG", ".heic", ".HEIC", ".tiff", ".TIFF", ".tif", ".TIF")
|
||||
EXIF_MOVIE_EXT = (".mov", ".MOV", ".mp4", ".MP4",)
|
||||
EXIF_PHOTO_TAGS = ("-datetimeoriginal", "-filecreatedate", "-filemodifydate",)
|
||||
EXIF_MOVIE_TAGS = ("-mediacreatedate","-filemodifydate",)
|
||||
|
||||
FILE_TREE_CNTL = [('Filename.', 300, QTreeView),
|
||||
('FullNAme', 800, QtGui.QTextItem), ('--', 20, QLabel)]
|
||||
|
||||
YEARS_RANGE = [1970, 2040]
|
||||
|
||||
COL_WHITE = "QPushButton { color: white;}"
|
||||
COL_RED = "QPushButton { color: red;}"
|
||||
COL_GREEN = "QPushButton { color: green;}"
|
||||
COL_YELLOW = "QPushButton { color: yellow;}"
|
||||
COL_BLUE = "QPushButton { color: blue;}"
|
||||
COL_BLACK = "QPushButton { color: black;}"
|
||||
|
||||
BCOL_WHITE = "background-color: white;"
|
||||
BCOL_RED = "background-color: red;"
|
||||
BCOL_GREEN = "background-color: green;"
|
||||
BCOL_YELLOW = "background-color: yellow;"
|
||||
BCOL_CYAN = "background-color: cyan;"
|
||||
BCOL_BLACK = "background-color: black;"
|
||||
|
||||
''' Default instances '''
|
||||
DEF_INSTANCE = 'select'
|
||||
NON_FILE_CHARACTERS = '\|/*$?:;<>+"'''
|
||||
|
||||
''' Ordering dictionary '''
|
||||
WorkData = {}
|
||||
WorkDir = ''
|
||||
TmpDir = ''
|
||||
|
||||
class PICTitem(QTreeWidgetItem):
|
||||
'''
|
||||
Custom QTreeWidgetItem with Widgets
|
||||
'''
|
||||
_basename = ""
|
||||
_name = ""
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
return self._basename
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
def __init__(self, parent, basename, name):
|
||||
'''
|
||||
parent (QTreeWidget) : Item's QTreeWidget parent.
|
||||
value (str) : Item's name. just an example.
|
||||
'''
|
||||
|
||||
## Init super class ( QtGui.QTreeWidgetItem )
|
||||
super(PICTitem, self).__init__(parent)
|
||||
|
||||
''' '''
|
||||
self.setText(0, basename)
|
||||
#self.setTextAlignment(0, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self.setText(1, name)
|
||||
#self.setTextAlignment(1, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self._name=name
|
||||
self._basename=basename
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self)
|
||||
|
||||
def __str__(self):
|
||||
return f"<class '{self.__class__.__name__}'> Filename:{self.basename}"
|
||||
|
||||
|
||||
''' ======================== MAIN WINDOW CLASS with all methods ================================ '''
|
||||
class main(QMainWindow):
|
||||
def __init__(self):
|
||||
QMainWindow.__init__(self)
|
||||
self.ui = Ui_MainWindow()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
''' create Progressbar element '''
|
||||
self.pb = PB(self.ui.pb_Run,mi = 0, ma =100)
|
||||
|
||||
''' set default values '''
|
||||
self.day = 0
|
||||
self.month = 0
|
||||
self.year = 1900
|
||||
self.name = ''
|
||||
self.fullName = ''
|
||||
self.ineditname = False
|
||||
self.filecriteria = PHOTO_filter
|
||||
self.TmpDir = ''
|
||||
self.WorkDir = ''
|
||||
self.WorkData = {}
|
||||
self.toRename = []
|
||||
|
||||
|
||||
''' set the tree_OUT widget'''
|
||||
self.ui.tree_OUT.setColumnCount(len(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setHeaderLabels(self.set_qtree_headers(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setUniformRowHeights(True)
|
||||
#self.ui.tree_OUT.setSelectionMode(QabsW.ExtendedSelection)
|
||||
|
||||
''' set all Methods '''
|
||||
self.ui.btn_Renumber.setEnabled(False)
|
||||
self.ui.btn_Renumber.clicked.connect(self.click_Renumber)
|
||||
self.ui.btn_Open.clicked.connect(self.click_Open)
|
||||
self.ui.btn_Exit.clicked.connect(self.click_Exit)
|
||||
#self.ui.txt_Name.editingFinished.connect(self.edited_Name)
|
||||
self.ui.rbtn_Photo.setChecked(True)
|
||||
|
||||
''' set all combo '''
|
||||
for i in range(1,32):
|
||||
self.ui.cmb_Day.addItem(str(i))
|
||||
for i in range(1,13):
|
||||
self.ui.cmb_Month.addItem(str(i))
|
||||
for i in range(YEARS_RANGE[0],YEARS_RANGE[1]+1):
|
||||
self.ui.cmb_Year.addItem(str(i))
|
||||
|
||||
''' set date and time '''
|
||||
# print datetime.now().strftime('%d/%m/%Y')
|
||||
self.day = int(datetime.now().strftime('%d'))
|
||||
self.month = int(datetime.now().strftime('%m'))
|
||||
self.year = int(datetime.now().strftime('%Y'))
|
||||
self.ui.cmb_Day.setCurrentIndex(self.day - 1)
|
||||
self.ui.cmb_Month.setCurrentIndex(self.month - 1)
|
||||
self.ui.cmb_Year.setCurrentIndex(self.year - YEARS_RANGE[0])
|
||||
self.ui.date_Day.setDate(datetime.now())
|
||||
self.ui.txt_Name.textChanged.connect(self.changed_Name)
|
||||
|
||||
######## These are OLD PyQT4 signal sintax ###########
|
||||
#''' set Object signals '''
|
||||
#''' visualization selection via Radio Button '''
|
||||
#self.ui.rbtn_Movies.connect(self.ui.rbtn_Movies, QtCore.SIGNAL("clicked()"),self.selected_rbtn_Movies)
|
||||
#self.ui.rbtn_Photo.connect(self.ui.rbtn_Photo, QtCore.SIGNAL("clicked()"), self.selected_rbtn_Photos)
|
||||
#self.ui.date_Day.connect(self.ui.date_Day, QtCore.SIGNAL("dateChanged(QDate)"), self.change_date)
|
||||
#
|
||||
#''' Signals '''
|
||||
#self.ui.cmb_Day.connect(self.ui.cmb_Day, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.choose_Day)
|
||||
#self.ui.cmb_Month.connect(self.ui.cmb_Month, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.choose_Month)
|
||||
#self.ui.cmb_Year.connect(self.ui.cmb_Year, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.choose_Year)
|
||||
## self.ui.txt_Name.connect(self.ui.txt_Name, QtCore.SIGNAL("insertPlainText (const Qstring&"), self.changed_Name)
|
||||
|
||||
''' set Object signals '''
|
||||
''' visualization selection via Radio Button '''
|
||||
self.ui.rbtn_Movies.clicked.connect(self.selected_rbtn_Movies)
|
||||
self.ui.rbtn_Photo.clicked.connect(self.selected_rbtn_Photos)
|
||||
self.ui.date_Day.dateChanged.connect(self.change_date)
|
||||
self.ui.tree_OUT.itemSelectionChanged.connect(self.on_selectionChanged)
|
||||
|
||||
''' Signals '''
|
||||
self.ui.cmb_Day.currentIndexChanged.connect(self.choose_Day)
|
||||
self.ui.cmb_Month.currentIndexChanged.connect(self.choose_Month)
|
||||
self.ui.cmb_Year.currentIndexChanged.connect(self.choose_Year)
|
||||
|
||||
''' other setting '''
|
||||
self.ui.pb_Run.setMinimum(0)
|
||||
self.ui.pb_Run.setMaximum(100)
|
||||
self.ui.pb_Run.setVisible(False)
|
||||
self.ui.chk_IgnMeta.setChecked(False)
|
||||
|
||||
def click_Exit(self):
|
||||
sys.exit(0)
|
||||
return
|
||||
|
||||
def QaddFILEChild(self, parent, basename, name):
|
||||
item = PICTitem(parent, basename, name)
|
||||
return item
|
||||
|
||||
|
||||
def on_selectionChanged(self):
|
||||
selected = [ s.basename for s in self.ui.tree_OUT.selectedItems() ]
|
||||
for f in self.WorkData:
|
||||
if f in selected:
|
||||
self.WorkData[f]['rename'] = True
|
||||
else:
|
||||
self.WorkData[f]['rename'] = False
|
||||
return
|
||||
|
||||
|
||||
def click_Open(self):
|
||||
''' open source directory '''
|
||||
#files = QFileDialog.getOpenFileNames(self, "Open Directory", "" , self.filecriteria, None, QFileDialog.DontUseNativeDialog)
|
||||
files = QFileDialog.getOpenFileNames(self, "Open Directory", "" , self.filecriteria, None)
|
||||
nf = len(files)
|
||||
# SHIT now QfileDialog return a Tuple instead of list
|
||||
# Has to be addressed as [0]
|
||||
# because files[1] contains the Type: <class 'str'>, - Value >Images (*.jpg *.jpeg *.png *.tiff)
|
||||
|
||||
|
||||
if len(files[0]) > 0:
|
||||
''' clear and set the tree_OUT widget'''
|
||||
self.ui.tree_OUT.clear()
|
||||
self.ui.tree_OUT.setColumnCount(len(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setHeaderLabels(self.set_qtree_headers(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setUniformRowHeights(True)
|
||||
|
||||
self.pb.start(steps=nf)
|
||||
|
||||
''' initialized work list dictionary '''
|
||||
self.WorkData = {}
|
||||
|
||||
# LOGGER.debug("Type of files[] >%s<" % (str(type(files))))
|
||||
# self.WorkDir = os.path.dirname(str(files[0]))
|
||||
# SHIT now QfileDialog return a Tuple instead of list
|
||||
# Has to be addressed as [0]
|
||||
# because files[1] contains the Type: <class 'str'>, - Value >Images (*.jpg *.jpeg *.png *.tiff)
|
||||
fileuno = files[0][0]
|
||||
#LOGGER.debug("fileuno >%s<" % fileuno)
|
||||
|
||||
self.WorkDir = os.path.dirname(fileuno)
|
||||
LOGGER.debug(f"Workdir >{self.WorkDir}<")
|
||||
|
||||
''' tempory dir must be on same disk otherwise is too slow'''
|
||||
basedir = os.path.dirname(self.WorkDir)
|
||||
self.TmpDir = basedir + '/PICTtool-tmp'
|
||||
if not os.path.exists(self.TmpDir):
|
||||
os.mkdir(self.TmpDir)
|
||||
count = 1
|
||||
|
||||
#for fname in files[0]:
|
||||
# LOGGER.debug("Type: %s, - Value >%s<" % (type(fname), fname))
|
||||
|
||||
''' for all selected files '''
|
||||
for fname in files[0]:
|
||||
self.pb.incr()
|
||||
self.repaint()
|
||||
''' add an item to Qtree including resizing of pictures '''
|
||||
# self.add_Qtree_item(fname)
|
||||
|
||||
basename = os.path.basename(str(fname))
|
||||
LOGGER.info(f"Reading Metadata from: >{basename}< ")
|
||||
|
||||
# Get File Metadata via external exiftool
|
||||
# requires installation
|
||||
# https://exiftool.org/index.html#running
|
||||
#
|
||||
|
||||
if self.ui.chk_IgnMeta:
|
||||
# dont look at meta data of file just sort for name
|
||||
''' fill dictionary '''
|
||||
self.WorkData.update({
|
||||
# int(time.mktime(time.strptime(dt, '%Y:%m:%d|%H:%M:%S'))) :
|
||||
basename :
|
||||
{
|
||||
#'itemNum' : str(count),
|
||||
'fname' : basename,
|
||||
'fileExt' : self.get_extension(str(fname)),
|
||||
'tmpName' : self.TmpDir + '/PICTtmp-' + next(tempfile._get_candidate_names()),
|
||||
'rename' : True,
|
||||
'date' : "",
|
||||
'time' : ""
|
||||
}
|
||||
})
|
||||
count += 1
|
||||
|
||||
|
||||
else:
|
||||
# Normal way looking at Metadata to get file sosted with creation time
|
||||
|
||||
# exiftool has produced with json and TAGS
|
||||
if basename.endswith(EXIF_MOVIE_EXT):
|
||||
EXIF_TAGS = EXIF_MOVIE_TAGS
|
||||
else:
|
||||
EXIF_TAGS = EXIF_PHOTO_TAGS
|
||||
|
||||
args=[EXIFTOOL, "-json", *EXIF_TAGS, os.path.join(self.WorkDir,basename)]
|
||||
LOGGER.debug(str(args))
|
||||
process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
res=json.loads(process.stdout.read())
|
||||
LOGGER.debug(json.dumps(res,indent=2))
|
||||
dat = res[0]
|
||||
|
||||
# reset of all tag values
|
||||
dt = odate = fmdate = fcdate = mcdate = None
|
||||
if basename.endswith(EXIF_MOVIE_EXT):
|
||||
# MOVIEs
|
||||
try:
|
||||
fmdate = dat['FileModifyDate']
|
||||
except KeyError:
|
||||
LOGGER.debug(f"File {basename} does not contain tag FileModifyDate")
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing error {e}")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
try:
|
||||
mcdate = dat['MediaCreateDate']
|
||||
except KeyError:
|
||||
LOGGER.debug(f"File {basename} does not contain tag MediaCreateDate")
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing error {e}")
|
||||
continue
|
||||
|
||||
if not mcdate and not fmdate:
|
||||
LOGGER.error(f"File {basename} does NOT contain expected metadata tags, please verify results")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
|
||||
if fmdate:
|
||||
try:
|
||||
# dt = f"{mcdate[0]}:{mcdate[1]}:{mcdate[2].split(' ')[0]}|{mcdate[2].split(' ')[1]}:{mcdate[3]}:{(mcdate[4].split('+'))[0]}".strip()
|
||||
pd = int(time.mktime(time.strptime(fmdate, '%Y:%m:%d %H:%M:%S%z')))
|
||||
pod = datetime.fromtimestamp(pd)
|
||||
dt = pod.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing tag value: {e} ...Skipping...")
|
||||
continue # for fname in files[0]:
|
||||
else:
|
||||
try:
|
||||
# dt = f"{mcdate[0]}:{mcdate[1]}:{mcdate[2].split(' ')[0]}|{mcdate[2].split(' ')[1]}:{mcdate[3]}:{(mcdate[4].split('+'))[0]}".strip()
|
||||
pd = int(time.mktime(time.strptime(mcdate, '%Y:%m:%d %H:%M:%S')))
|
||||
pod = datetime.fromtimestamp(pd)
|
||||
dt = pod.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing tag value: {e} ...Skipping...")
|
||||
continue # for fname in files[0]:
|
||||
else:
|
||||
# PICTUREs
|
||||
try:
|
||||
odate = dat['DateTimeOriginal']
|
||||
except KeyError:
|
||||
LOGGER.debug(f"File {basename} does not contain tag DateTimeOriginal")
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing error {e}")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
try:
|
||||
fcdate = dat['FileCreateDate']
|
||||
except KeyError:
|
||||
LOGGER.debug(f"File {basename} does not contain tag FileCreateDate")
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing error {e}")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
try:
|
||||
fmdate = dat['FileModifyDate']
|
||||
except KeyError:
|
||||
LOGGER.debug(f"File {basename} does not contain tag FileModifyDate")
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing error {e}")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
if not odate and not fcdate and not fmdate:
|
||||
LOGGER.error(f"File {basename} does NOT contain expected metadata tags, please verify results")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
if odate:
|
||||
#take DateTimeOriginal
|
||||
od = int(time.mktime(time.strptime(odate, '%Y:%m:%d %H:%M:%S')))
|
||||
ood = datetime.fromtimestamp(od)
|
||||
dt = ood.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
elif fcdate and fmdate:
|
||||
# take older between FileCreateDate and FileModifyDate
|
||||
fcfmdate = min(int(time.mktime(time.strptime(fcdate, '%Y:%m:%d %H:%M:%S%z'))), int(time.mktime(time.strptime(fmdate, '%Y:%m:%d %H:%M:%S%z'))))
|
||||
ofcfmdate = datetime.fromtimestamp(fcfmdate)
|
||||
dt = ofcfmdate.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
elif fcdate and not fmdate:
|
||||
# take fcdate
|
||||
fc = int(time.mktime(time.strptime(fcdate, '%Y:%m:%d %H:%M:%S%z')))
|
||||
dt = fc.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
else:
|
||||
# take fmdate
|
||||
fm = int(time.mktime(time.strptime(fmdate, '%Y:%m:%d %H:%M:%S%z')))
|
||||
dt = fm.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
|
||||
if not dt:
|
||||
LOGGER.error(f"File {basename} does NOT contain expected metadata tags, please verify results")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
# check if same modification time exist so it loops adding one second to modification time
|
||||
adt = int(time.mktime(time.strptime(dt, '%Y:%m:%d|%H:%M:%S')))
|
||||
|
||||
while adt in self.WorkData.keys():
|
||||
adt+=1
|
||||
|
||||
LOGGER.info(f"Time: {dt} --> {basename}")
|
||||
|
||||
''' fill dictionary '''
|
||||
self.WorkData.update({
|
||||
# int(time.mktime(time.strptime(dt, '%Y:%m:%d|%H:%M:%S'))) :
|
||||
adt :
|
||||
{
|
||||
#'itemNum' : str(count),
|
||||
'fname' : basename,
|
||||
'fileExt' : self.get_extension(str(fname)),
|
||||
'tmpName' : self.TmpDir + '/PICTtmp-' + next(tempfile._get_candidate_names()),
|
||||
'rename' : True,
|
||||
'date': dt.split('|')[0],
|
||||
'time': dt.split('|')[1]
|
||||
}
|
||||
})
|
||||
count += 1
|
||||
# end of sorting with meta data
|
||||
self.pb.end()
|
||||
# print unsorted dictionary
|
||||
# print(f"{json.dumps(self.WorkData, indent=4)}")
|
||||
|
||||
# transpose disctionary and sort it by date of creation of the file
|
||||
flts = dict(sorted(self.WorkData.items()))
|
||||
# self.WorkData = flts
|
||||
|
||||
# print(f"{json.dumps(self.WorkData, indent=4)}")
|
||||
|
||||
for _,v in self.WorkData.items():
|
||||
''' add an item to Qtree including resizing of pictures '''
|
||||
# self.add_Qtree_item(f"{v['fname']}-->{v['date']}|{v['time']}")
|
||||
self.add_Qtree_item(f"{v['fname']}")
|
||||
|
||||
return
|
||||
|
||||
def click_Renumber(self):
|
||||
''' renumber and create a new WorkData for next phases '''
|
||||
newWorkData = {}
|
||||
|
||||
''' only if WorkData Contains elements '''
|
||||
nf = len(self.WorkData)
|
||||
if nf > 0:
|
||||
self.pb.start(steps=nf * 2)
|
||||
|
||||
''' for all files move to temp to avoid overlapping'''
|
||||
for _,v in self.WorkData.items():
|
||||
LOGGER.debug(f"Moving file {v}")
|
||||
if v['rename']:
|
||||
self.pb.incr()
|
||||
try:
|
||||
tmpfile = v['tmpName']
|
||||
origfile = self.WorkDir + '/' + v['fname']
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Parsing file attributes [{v['fname']} : {e} ... Skipping ...")
|
||||
else:
|
||||
try:
|
||||
shutil.move(origfile, tmpfile)
|
||||
#os.rename(origfile, tmpfile)
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Renaming file [{origfile}] to tmpfile: {tmpfile} : {e} ... Skipping ...")
|
||||
else:
|
||||
# LOGGER.debug(f"Origfile [{origfile}] ==> TmpFile [{tmpfile}]')
|
||||
pass
|
||||
|
||||
''' for all files move to current directory with new name'''
|
||||
count = 1
|
||||
for _,v in self.WorkData.items():
|
||||
LOGGER.debug(f"Moving file {v}")
|
||||
if v['rename']:
|
||||
self.pb.incr()
|
||||
try:
|
||||
tmpfile = v['tmpName']
|
||||
extension = v['fileExt']
|
||||
#count = int(self.WorkData[fname]['itemNum'])
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Parsing file attributes [{v['fname']} : {e} ... Skipping ...")
|
||||
else:
|
||||
targetfile = self.WorkDir + '/' + self.fullName + '-{0:04d}'.format(count) + extension
|
||||
targetfname = self.fullName + '-{0:04d}'.format(count) + extension
|
||||
try:
|
||||
shutil.move(tmpfile, targetfile)
|
||||
#os.rename(tmpfile, targetfile)
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Renaming tmpfile [{tmpfile}] to: {targetfname} : {e} ... Skipping ...")
|
||||
else:
|
||||
#LOGGER.debug('TmpFile [%s] ==> TargetFIle [%s]' % (tmpfile, targetfile))
|
||||
LOGGER.info(f"Renamed [{tmpfile}] ==> [{targetfile}]")
|
||||
pass
|
||||
|
||||
''' fill dictionary '''
|
||||
newWorkData.update({
|
||||
targetfname : {
|
||||
'itemNum' : str(count),
|
||||
'fileExt' : self.get_extension(str(targetfname)),
|
||||
'tmpName' : self.TmpDir + '/PICTtmp-' + next(tempfile._get_candidate_names()),
|
||||
'rename' : True
|
||||
},
|
||||
})
|
||||
count += 1
|
||||
|
||||
self.pb.end()
|
||||
|
||||
''' also rename directory '''
|
||||
basedir = os.path.dirname(self.WorkDir)
|
||||
olddir = self.WorkDir
|
||||
newdir = basedir + '/' + self.fullName
|
||||
try:
|
||||
shutil.move(olddir, newdir)
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Rename of directory to [{basedir}] failed {e} ...Skipping...")
|
||||
|
||||
''' sel new Current working directory and Workdata to new dictionary '''
|
||||
self.WorkDir = newdir
|
||||
os.chdir(self.WorkDir)
|
||||
self.WorkData = newWorkData
|
||||
|
||||
''' empties Qtree '''
|
||||
self.ui.tree_OUT.clear()
|
||||
self.ui.tree_OUT.setColumnCount(len(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setHeaderLabels(self.set_qtree_headers(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setUniformRowHeights(True)
|
||||
|
||||
''' and refill it again '''
|
||||
nf = len (self.WorkData)
|
||||
WorkData_s = OrderedDict(self.WorkData)
|
||||
|
||||
# redraw the dialog
|
||||
self.pb.start(steps=nf)
|
||||
for fname in WorkData_s:
|
||||
self.pb.incr()
|
||||
self.add_Qtree_item(self.WorkDir + '/' + fname)
|
||||
|
||||
self.pb.end()
|
||||
|
||||
return
|
||||
|
||||
def add_Qtree_item(self, fname):
|
||||
basefile = os.path.basename(str(fname))
|
||||
|
||||
if self.ui.chk_Preview.isChecked():
|
||||
''' review is activated need to elaborate thumbnail '''
|
||||
item = self.QaddFILEChild(self.ui.tree_OUT, basefile, str(fname))
|
||||
try:
|
||||
''' rezize file '''
|
||||
#img = QtGui.QImage(fname)
|
||||
# qim = img.scaled(64, 64, QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.SmoothTransformation)
|
||||
# qim = img.scaled(64, 64)
|
||||
# pict = QPixmap.fromImage(qim)
|
||||
pict = QPixmap(fname)
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Unable to process image [{fname}] {e} ... Skipping ...")
|
||||
label = QLabel()
|
||||
label.setText("Preview Error")
|
||||
else:
|
||||
''' create a Label with picture as background '''
|
||||
label = QLabel()
|
||||
label.setPixmap(pict)
|
||||
|
||||
''' add column 2 a Label with picture in pixmap '''
|
||||
item.treeWidget().setItemWidget(item, 2, label)
|
||||
else:
|
||||
item = self.QaddFILEChild(self.ui.tree_OUT, basefile, str(fname))
|
||||
return item
|
||||
|
||||
def get_extension(self, fname):
|
||||
basename = os.path.basename(fname) # os independent
|
||||
ext = '.'.join(basename.split('.')[1:])
|
||||
return '.' + ext if ext else None
|
||||
|
||||
def change_date(self):
|
||||
self.day = self.ui.date_Day.date().day()
|
||||
self.month = self.ui.date_Day.date().month()
|
||||
self.year = self.ui.date_Day.date().year()
|
||||
self.ui.cmb_Day.setCurrentIndex(self.day - 1)
|
||||
self.ui.cmb_Month.setCurrentIndex(self.month - 1)
|
||||
self.ui.cmb_Year.setCurrentIndex(self.year - YEARS_RANGE[0] )
|
||||
|
||||
''' set fullname '''
|
||||
self.set_fullName()
|
||||
|
||||
return
|
||||
|
||||
def changed_Name(self):
|
||||
if self.ineditname:
|
||||
return
|
||||
else:
|
||||
tc = self.ui.txt_Name.textCursor()
|
||||
name = str(self.ui.txt_Name.toPlainText())
|
||||
filename = "".join(i for i in name if i not in NON_FILE_CHARACTERS)
|
||||
self.ineditname = True
|
||||
self.ui.txt_Name.setPlainText(filename)
|
||||
self.ui.txt_Name.setTextCursor(tc)
|
||||
self.ineditname = False
|
||||
self.name = filename
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def selected_rbtn_Movies(self):
|
||||
self.filecriteria = MOVIE_filter
|
||||
return
|
||||
|
||||
def selected_rbtn_Photos(self):
|
||||
self.filecriteria = PHOTO_filter
|
||||
return
|
||||
|
||||
def choose_Day(self):
|
||||
self.day = int(self.ui.cmb_Day.currentText())
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def choose_Month(self):
|
||||
self.month = int(self.ui.cmb_Month.currentText())
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def choose_Year(self):
|
||||
self.year = int(self.ui.cmb_Year.currentText())
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def set_qtree_headers(self, tree_ctnl):
|
||||
tree_HEADERS = []
|
||||
i = 0
|
||||
for treeitem in tree_ctnl:
|
||||
tree_HEADERS.append(treeitem[0])
|
||||
self.ui.tree_OUT.setColumnWidth(i, treeitem[1])
|
||||
i += 1
|
||||
return tree_HEADERS
|
||||
|
||||
def set_fullName(self):
|
||||
self.fullName = '{0:4d}{1:02d}{2:02d}-{3:s}'.format(self.year, self.month, self.day, self.name)
|
||||
self.ui.txt_FullName.setText(self.fullName)
|
||||
if len(self.fullName) > 9:
|
||||
self.ui.btn_Renumber.setEnabled(True)
|
||||
else:
|
||||
self.ui.btn_Renumber.setEnabled(False)
|
||||
|
||||
# def msg_print(self, msg):
|
||||
# self.ui.txt_Error.setText(str(msg))
|
||||
# self.repaint()
|
||||
|
||||
''' ========================= Other Functions ==================================================='''
|
||||
''' progress bar class and methods '''
|
||||
class PB():
|
||||
def __init__(self, progressbar, mi = 0, ma = 100 ):
|
||||
# super(PB, self).__init__(parent)
|
||||
self.val = 0
|
||||
self.min = mi
|
||||
self.max = ma
|
||||
self.steps = 10
|
||||
self.step = (self.max - self.min)/self.steps
|
||||
self.progressbar = progressbar
|
||||
self.progressbar.setMinimum(self.min)
|
||||
self.progressbar.setMaximum(self.max)
|
||||
|
||||
def start(self, steps=10):
|
||||
self.steps = (steps if steps > 0 else 10)
|
||||
self.progressbar.setVisible(True)
|
||||
self.progressbar.setValue(0)
|
||||
|
||||
def end(self):
|
||||
self.progressbar.setValue(self.max)
|
||||
# start a timer to make progressbar invisible
|
||||
threading.Timer(3,self.clear).start()
|
||||
|
||||
def clear(self):
|
||||
self.progressbar.setVisible(False)
|
||||
|
||||
def upd(self, val):
|
||||
self.val = val
|
||||
self.progressbar.setValue(self.val)
|
||||
|
||||
def incr(self):
|
||||
actv = self.progressbar.value()
|
||||
newv = actv + self.step
|
||||
if newv <= self.max:
|
||||
self.progressbar.setValue(newv)
|
||||
|
||||
|
||||
'''=========================================================================================================================='''
|
||||
''' Main Program '''
|
||||
if __name__ == '__main__':
|
||||
|
||||
''' Enabling Logger
|
||||
'''
|
||||
prog_name = os.path.basename(sys.argv[0:][0])
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
LOGGER.setLevel(logging.DEBUG)
|
||||
LOGGER.propagate = False
|
||||
logging.captureWarnings(True)
|
||||
formatter = logging.Formatter(LOG_FORMAT, LOG_TIME_FORMAT)
|
||||
|
||||
''' File logging
|
||||
'''
|
||||
log_name = "LOG-" + prog_name + ".log"
|
||||
fh = logging.FileHandler(log_name)
|
||||
fh.setLevel(FILE_DEBUG)
|
||||
fh.setFormatter(formatter)
|
||||
LOGGER.addHandler(fh)
|
||||
|
||||
''' Console logging
|
||||
'''
|
||||
cl = logging.StreamHandler(sys.stdout)
|
||||
cl.setLevel(CONSOLE_DEBUG)
|
||||
cl.setFormatter(formatter)
|
||||
LOGGER.addHandler(cl)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
LOGGER.info("%s: Ver %s - Date: %s" % (prog_name, mod_ver, mod_dver))
|
||||
|
||||
''' Open window and give control to QTPY '''
|
||||
app = QApplication(sys.argv)
|
||||
window = main()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
573
PICT_numtool_220731.py
Executable file
573
PICT_numtool_220731.py
Executable file
@@ -0,0 +1,573 @@
|
||||
#!/usr/bin/python
|
||||
'''
|
||||
=========================================================================================================
|
||||
T&T Software and Systems
|
||||
Project: Renumbering tool
|
||||
Module: Main module
|
||||
Description: Tool to renumber ohoto and video replace VB one
|
||||
|
||||
|
||||
Tiziano Trabattoni | 2016/12/10 | first version
|
||||
Tiziano Trabattoni | 2022/07/31 | Porting su MacOS, PyQt5, python3
|
||||
=========================================================================================================
|
||||
|
||||
Usefull link: http://www.freeformatter.com/json-formatter.html#ad-output
|
||||
|
||||
'''
|
||||
|
||||
from collections import OrderedDict
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
from datetime import datetime
|
||||
import tempfile
|
||||
import json
|
||||
|
||||
''' PyQT4 imports'''
|
||||
from PICT_numtool_ui import Ui_MainWindow
|
||||
from PyQt6.QtWidgets import QTreeView, QLabel, QTreeWidgetItem, QMainWindow, QApplication, QFileDialog, QAbstractItemView as QabsW
|
||||
from PyQt6 import QtGui
|
||||
#from PyQt5.QtCore import *
|
||||
from PyQt6 import QtCore
|
||||
|
||||
''' Module version '''
|
||||
mod_ver = "v1.1"
|
||||
mod_dver = "2022:07:31"
|
||||
|
||||
''' Logger Variables '''
|
||||
LOG_FORMAT = '%(asctime)s|%(levelname)-7s|%(funcName)-15s|%(lineno)-5d: %(message)-50s'
|
||||
LOG_TIME_FORMAT = '%m-%d %H:%M:%S'
|
||||
|
||||
PHOTO_filter = 'Images (*.jpg *.jpeg *.png *.tiff *.heic)'
|
||||
MOVIE_filter = 'Movies (*.mov *.avi *.mp4 *.wmk)'
|
||||
|
||||
FILE_TREE_CNTL = [('Filename.', 300, QTreeView),
|
||||
('FullNAme', 600, QtGui.QTextItem), ('Preview', 100, QLabel)]
|
||||
|
||||
YEARS_RANGE = [1970, 2040]
|
||||
|
||||
COL_WHITE = "QPushButton { color: white;}"
|
||||
COL_RED = "QPushButton { color: red;}"
|
||||
COL_GREEN = "QPushButton { color: green;}"
|
||||
COL_YELLOW = "QPushButton { color: yellow;}"
|
||||
COL_BLUE = "QPushButton { color: blue;}"
|
||||
COL_BLACK = "QPushButton { color: black;}"
|
||||
|
||||
BCOL_WHITE = "background-color: white;"
|
||||
BCOL_RED = "background-color: red;"
|
||||
BCOL_GREEN = "background-color: green;"
|
||||
BCOL_YELLOW = "background-color: yellow;"
|
||||
BCOL_CYAN = "background-color: cyan;"
|
||||
BCOL_BLACK = "background-color: black;"
|
||||
|
||||
''' Default instances '''
|
||||
DEF_INSTANCE = 'select'
|
||||
NON_FILE_CHARACTERS = '\|/*$?:;<>+"'''
|
||||
|
||||
''' Ordering dictionary '''
|
||||
WorkData = {}
|
||||
WorkDir = ''
|
||||
TmpDir = ''
|
||||
|
||||
class PICTitem(QTreeWidgetItem):
|
||||
'''
|
||||
Custom QTreeWidgetItem with Widgets
|
||||
'''
|
||||
_basename = ""
|
||||
_name = ""
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
return self._basename
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
def __init__(self, parent, basename, name):
|
||||
'''
|
||||
parent (QTreeWidget) : Item's QTreeWidget parent.
|
||||
value (str) : Item's name. just an example.
|
||||
'''
|
||||
|
||||
## Init super class ( QtGui.QTreeWidgetItem )
|
||||
super(PICTitem, self).__init__(parent)
|
||||
|
||||
''' '''
|
||||
self.setText(0, basename)
|
||||
self.setTextAlignment(0, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self.setText(1, name)
|
||||
self.setTextAlignment(1, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self._name=name
|
||||
self._basename=basename
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self)
|
||||
|
||||
def __str__(self):
|
||||
return f"<class '{self.__class__.__name__}'> Filename:{self.basename}"
|
||||
|
||||
|
||||
''' ======================== MAIN WINDOW CLASS with all methods ================================ '''
|
||||
class main(QMainWindow):
|
||||
def __init__(self):
|
||||
QMainWindow.__init__(self)
|
||||
self.ui = Ui_MainWindow()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
''' create Progressbar element '''
|
||||
self.pb = PB(self.ui.pb_Run,mi = 0, ma =100)
|
||||
|
||||
''' set default values '''
|
||||
self.day = 0
|
||||
self.month = 0
|
||||
self.year = 1900
|
||||
self.name = ''
|
||||
self.fullName = ''
|
||||
self.ineditname = False
|
||||
self.filecriteria = PHOTO_filter
|
||||
self.TmpDir = ''
|
||||
self.WorkDir = ''
|
||||
self.WorkData = {}
|
||||
self.toRename = []
|
||||
|
||||
|
||||
''' set the tree_OUT widget'''
|
||||
self.ui.tree_OUT.setColumnCount(len(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setHeaderLabels(self.set_qtree_headers(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setUniformRowHeights(True)
|
||||
#self.ui.tree_OUT.setSelectionMode(QabsW.ExtendedSelection)
|
||||
|
||||
''' set all Methods '''
|
||||
self.ui.btn_Renumber.setEnabled(False)
|
||||
self.ui.btn_Renumber.clicked.connect(self.click_Renumber)
|
||||
self.ui.btn_Open.clicked.connect(self.click_Open)
|
||||
self.ui.btn_Exit.clicked.connect(self.click_Exit)
|
||||
#self.ui.txt_Name.editingFinished.connect(self.edited_Name)
|
||||
self.ui.rbtn_Photo.setChecked(True)
|
||||
|
||||
''' set all combo '''
|
||||
for i in range(1,32):
|
||||
self.ui.cmb_Day.addItem(str(i))
|
||||
for i in range(1,13):
|
||||
self.ui.cmb_Month.addItem(str(i))
|
||||
for i in range(YEARS_RANGE[0],YEARS_RANGE[1]+1):
|
||||
self.ui.cmb_Year.addItem(str(i))
|
||||
|
||||
''' set date and time '''
|
||||
# print datetime.now().strftime('%d/%m/%Y')
|
||||
self.day = int(datetime.now().strftime('%d'))
|
||||
self.month = int(datetime.now().strftime('%m'))
|
||||
self.year = int(datetime.now().strftime('%Y'))
|
||||
self.ui.cmb_Day.setCurrentIndex(self.day - 1)
|
||||
self.ui.cmb_Month.setCurrentIndex(self.month - 1)
|
||||
self.ui.cmb_Year.setCurrentIndex(self.year - YEARS_RANGE[0])
|
||||
self.ui.date_Day.setDate(datetime.now())
|
||||
self.ui.txt_Name.textChanged.connect(self.changed_Name)
|
||||
|
||||
######## These are OLD PyQT4 signal sintax ###########
|
||||
#''' set Object signals '''
|
||||
#''' visualization selection via Radio Button '''
|
||||
#self.ui.rbtn_Movies.connect(self.ui.rbtn_Movies, QtCore.SIGNAL("clicked()"),self.selected_rbtn_Movies)
|
||||
#self.ui.rbtn_Photo.connect(self.ui.rbtn_Photo, QtCore.SIGNAL("clicked()"), self.selected_rbtn_Photos)
|
||||
#self.ui.date_Day.connect(self.ui.date_Day, QtCore.SIGNAL("dateChanged(QDate)"), self.change_date)
|
||||
#
|
||||
#''' Signals '''
|
||||
#self.ui.cmb_Day.connect(self.ui.cmb_Day, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.choose_Day)
|
||||
#self.ui.cmb_Month.connect(self.ui.cmb_Month, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.choose_Month)
|
||||
#self.ui.cmb_Year.connect(self.ui.cmb_Year, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.choose_Year)
|
||||
## self.ui.txt_Name.connect(self.ui.txt_Name, QtCore.SIGNAL("insertPlainText (const Qstring&"), self.changed_Name)
|
||||
|
||||
''' set Object signals '''
|
||||
''' visualization selection via Radio Button '''
|
||||
self.ui.rbtn_Movies.clicked.connect(self.selected_rbtn_Movies)
|
||||
self.ui.rbtn_Photo.clicked.connect(self.selected_rbtn_Photos)
|
||||
self.ui.date_Day.dateChanged.connect(self.change_date)
|
||||
self.ui.tree_OUT.itemSelectionChanged.connect(self.on_selectionChanged)
|
||||
|
||||
''' Signals '''
|
||||
self.ui.cmb_Day.currentIndexChanged.connect(self.choose_Day)
|
||||
self.ui.cmb_Month.currentIndexChanged.connect(self.choose_Month)
|
||||
self.ui.cmb_Year.currentIndexChanged.connect(self.choose_Year)
|
||||
|
||||
''' other setting '''
|
||||
self.ui.pb_Run.setMinimum(0)
|
||||
self.ui.pb_Run.setMaximum(100)
|
||||
self.ui.pb_Run.setVisible(False)
|
||||
|
||||
def click_Exit(self):
|
||||
sys.exit(0)
|
||||
return
|
||||
|
||||
def QaddFILEChild(self, parent, basename, name):
|
||||
item = PICTitem(parent, basename, name)
|
||||
return item
|
||||
|
||||
|
||||
def on_selectionChanged(self):
|
||||
selected = [ s.basename for s in self.ui.tree_OUT.selectedItems() ]
|
||||
for f in self.WorkData:
|
||||
if f in selected:
|
||||
self.WorkData[f]['rename'] = True
|
||||
else:
|
||||
self.WorkData[f]['rename'] = False
|
||||
return
|
||||
|
||||
|
||||
def click_Open(self):
|
||||
''' open source directory '''
|
||||
files = QFileDialog.getOpenFileNames(self, "Open Directory", "" , self.filecriteria, None, QFileDialog.DontUseNativeDialog)
|
||||
nf = len(files)
|
||||
# SHIT now QfileDialog return a Tuple instead of list
|
||||
# Has to be addressed as [0]
|
||||
# because files[1] contains the Type: <class 'str'>, - Value >Images (*.jpg *.jpeg *.png *.tiff)
|
||||
|
||||
|
||||
if len(files[0]) > 0:
|
||||
''' clear and set the tree_OUT widget'''
|
||||
self.ui.tree_OUT.clear()
|
||||
self.ui.tree_OUT.setColumnCount(len(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setHeaderLabels(self.set_qtree_headers(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setUniformRowHeights(True)
|
||||
|
||||
self.pb.start(steps=nf)
|
||||
|
||||
''' initialized work list dictionary '''
|
||||
self.WorkData = {}
|
||||
|
||||
#LOGGER.debug("Type of files[] >%s<" % (str(type(files))))
|
||||
#self.WorkDir = os.path.dirname(str(files[0]))
|
||||
# SHIT now QfileDialog return a Tuple instead of list
|
||||
# Has to be addressed as [0]
|
||||
# because files[1] contains the Type: <class 'str'>, - Value >Images (*.jpg *.jpeg *.png *.tiff)
|
||||
fileuno = files[0][0]
|
||||
#LOGGER.debug("fileuno >%s<" % fileuno)
|
||||
self.WorkDir = os.path.dirname(fileuno)
|
||||
LOGGER.debug("Workdir >%s<" % self.WorkDir)
|
||||
|
||||
''' tempory dir must be on same disk otherwise is too slow'''
|
||||
basedir = os.path.dirname(self.WorkDir)
|
||||
self.TmpDir = basedir + '/PICTtool-tmp'
|
||||
if not os.path.exists(self.TmpDir):
|
||||
os.mkdir(self.TmpDir)
|
||||
count = 1
|
||||
|
||||
#for fname in files[0]:
|
||||
# LOGGER.debug("Type: %s, - Value >%s<" % (type(fname), fname))
|
||||
|
||||
''' for all selected files '''
|
||||
for fname in files[0]:
|
||||
self.pb.incr()
|
||||
''' add an item to Qtree including resizing of pictures '''
|
||||
self.add_Qtree_item(fname)
|
||||
|
||||
basename = os.path.basename(str(fname))
|
||||
''' fill dictionary '''
|
||||
self.WorkData.update({
|
||||
basename : {
|
||||
'itemNum' : str(count),
|
||||
'fileExt' : self.get_extension(str(fname)),
|
||||
'tmpName' : self.TmpDir + '/PICTtmp-' + next(tempfile._get_candidate_names()),
|
||||
'rename' : True
|
||||
},
|
||||
})
|
||||
count += 1
|
||||
|
||||
self.pb.end()
|
||||
return
|
||||
|
||||
def click_Renumber(self):
|
||||
|
||||
''' renumber and create a new WorkData for next phases '''
|
||||
newWorkData = {}
|
||||
|
||||
''' only if WorkData Contains elements '''
|
||||
nf = len(self.WorkData)
|
||||
if nf > 0:
|
||||
self.pb.start(steps=nf * 2)
|
||||
|
||||
# WorkDataKL = self.WorkData.keys()
|
||||
# WorkDataKL.sort()
|
||||
WorkData_s = OrderedDict(self.WorkData)
|
||||
#print(json.dumps(WorkData_s, indent=2))
|
||||
#WorkData_s = {k: self.WorkData[k] for k in sorted(self.WorkData)}
|
||||
|
||||
#for fname in WorkData_s:
|
||||
# LOGGER.debug("fname: >%s<" % fname)
|
||||
|
||||
''' for all files move to temp to avoid overlapping'''
|
||||
for fname in WorkData_s:
|
||||
if WorkData_s[fname]['rename']:
|
||||
self.pb.incr()
|
||||
try:
|
||||
tmpfile = self.WorkData[fname]['tmpName']
|
||||
origfile = self.WorkDir + '/' + fname
|
||||
except:
|
||||
LOGGER.error('Parsing file attribites [%s] ... Skipping ...' % (fname))
|
||||
else:
|
||||
try:
|
||||
shutil.move(origfile, tmpfile)
|
||||
#os.rename(origfile, tmpfile)
|
||||
except:
|
||||
LOGGER.error('Renaming file [%s] to: %s ... Skipping ...' % (origfile, tmpfile))
|
||||
else:
|
||||
# LOGGER.debug('Origfile [%s] ==> TmpFile [%s]' % (origfile, tmpfile))
|
||||
pass
|
||||
|
||||
''' for all files move to current directory with new name'''
|
||||
count = 1
|
||||
for fname in WorkData_s:
|
||||
if WorkData_s[fname]['rename']:
|
||||
self.pb.incr()
|
||||
try:
|
||||
tmpfile = self.WorkData[fname]['tmpName']
|
||||
extension = self.WorkData[fname]['fileExt']
|
||||
count = int(self.WorkData[fname]['itemNum'])
|
||||
except:
|
||||
LOGGER.error('Parsing file attributes [%s] ... Skipping ...' % (fname))
|
||||
else:
|
||||
targetfile = self.WorkDir + '/' + self.fullName + '-{0:04d}'.format(count) + extension
|
||||
targetfname = self.fullName + '-{0:04d}'.format(count) + extension
|
||||
try:
|
||||
shutil.move(tmpfile, targetfile)
|
||||
#os.rename(tmpfile, targetfile)
|
||||
except:
|
||||
LOGGER.error('Renaming file [%s] to: %s ... Skipping ...' % (tmpfile , targetfile))
|
||||
else:
|
||||
#LOGGER.debug('TmpFile [%s] ==> TargetFIle [%s]' % (tmpfile, targetfile))
|
||||
LOGGER.info('Renanme [%s] ==> [%s]' % (fname, targetfile))
|
||||
pass
|
||||
|
||||
''' fill dictionary '''
|
||||
newWorkData.update({
|
||||
targetfname : {
|
||||
'itemNum' : str(count),
|
||||
'fileExt' : self.get_extension(str(targetfname)),
|
||||
'tmpName' : self.TmpDir + '/PICTtmp-' + next(tempfile._get_candidate_names()),
|
||||
'rename' : True
|
||||
},
|
||||
})
|
||||
count += 1
|
||||
|
||||
self.pb.end()
|
||||
|
||||
''' also rename directory '''
|
||||
basedir = os.path.dirname(self.WorkDir)
|
||||
olddir = self.WorkDir
|
||||
newdir = basedir + '/' + self.fullName
|
||||
try:
|
||||
shutil.move(olddir, newdir)
|
||||
except:
|
||||
LOGGER.error('Renanme of directory to [%s] failed ...Skipping...' % (basedir))
|
||||
|
||||
''' sel new Current working directory and Workdata to new dictionary '''
|
||||
self.WorkDir = newdir
|
||||
os.chdir(self.WorkDir)
|
||||
self.WorkData = newWorkData
|
||||
|
||||
''' empties Qtree '''
|
||||
self.ui.tree_OUT.clear()
|
||||
self.ui.tree_OUT.setColumnCount(len(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setHeaderLabels(self.set_qtree_headers(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setUniformRowHeights(True)
|
||||
|
||||
''' and refill it again '''
|
||||
nf = len (self.WorkData)
|
||||
# WorkDataKL = self.WorkData.keys()
|
||||
# WorkDataKL.sort()
|
||||
# WorkData_s = {k: self.WorkData[k] for k in sorted(self.WorkData)}
|
||||
WorkData_s = OrderedDict(self.WorkData)
|
||||
|
||||
|
||||
self.pb.start(steps=nf)
|
||||
for fname in WorkData_s:
|
||||
self.pb.incr()
|
||||
self.add_Qtree_item(self.WorkDir + '/' + fname)
|
||||
|
||||
self.pb.end()
|
||||
|
||||
return
|
||||
|
||||
def add_Qtree_item(self, fname):
|
||||
basefile = os.path.basename(str(fname))
|
||||
|
||||
if self.ui.chk_Preview.isChecked():
|
||||
''' review is activated need to elaborate thumbnail '''
|
||||
item = self.QaddFILEChild(self.ui.tree_OUT, basefile, str(fname))
|
||||
try:
|
||||
''' rezize file '''
|
||||
img = QtGui.QImage(fname)
|
||||
qim = img.scaled(64, 64, QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.SmoothTransformation)
|
||||
pict = QPixmap.fromImage(qim)
|
||||
except:
|
||||
LOGGER.error('Unable to process image [%s] ... Skipping ...' % (fname))
|
||||
label = QLabel()
|
||||
label.setText("Preview Error")
|
||||
else:
|
||||
''' create a Label with picture as background '''
|
||||
label = QLabel()
|
||||
label.setPixmap(pict)
|
||||
|
||||
''' add column 2 a Label with picture in pixmap '''
|
||||
item.treeWidget().setItemWidget(item, 2, label)
|
||||
else:
|
||||
item = self.QaddFILEChild(self.ui.tree_OUT, basefile, str(fname))
|
||||
return item
|
||||
|
||||
def get_extension(self, fname):
|
||||
basename = os.path.basename(fname) # os independent
|
||||
ext = '.'.join(basename.split('.')[1:])
|
||||
return '.' + ext if ext else None
|
||||
|
||||
def change_date(self):
|
||||
self.day = self.ui.date_Day.date().day()
|
||||
self.month = self.ui.date_Day.date().month()
|
||||
self.year = self.ui.date_Day.date().year()
|
||||
self.ui.cmb_Day.setCurrentIndex(self.day - 1)
|
||||
self.ui.cmb_Month.setCurrentIndex(self.month - 1)
|
||||
self.ui.cmb_Year.setCurrentIndex(self.year - YEARS_RANGE[0] )
|
||||
|
||||
''' set fullname '''
|
||||
self.set_fullName()
|
||||
|
||||
return
|
||||
|
||||
def changed_Name(self):
|
||||
if self.ineditname:
|
||||
return
|
||||
else:
|
||||
tc = self.ui.txt_Name.textCursor()
|
||||
name = str(self.ui.txt_Name.toPlainText())
|
||||
filename = "".join(i for i in name if i not in NON_FILE_CHARACTERS)
|
||||
self.ineditname = True
|
||||
self.ui.txt_Name.setPlainText(filename)
|
||||
self.ui.txt_Name.setTextCursor(tc)
|
||||
self.ineditname = False
|
||||
self.name = filename
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def selected_rbtn_Movies(self):
|
||||
self.filecriteria = MOVIE_filter
|
||||
return
|
||||
|
||||
def selected_rbtn_Photos(self):
|
||||
self.filecriteria = PHOTO_filter
|
||||
return
|
||||
|
||||
def choose_Day(self):
|
||||
self.day = int(self.ui.cmb_Day.currentText())
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def choose_Month(self):
|
||||
self.month = int(self.ui.cmb_Month.currentText())
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def choose_Year(self):
|
||||
self.year = int(self.ui.cmb_Year.currentText())
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def set_qtree_headers(self, tree_ctnl):
|
||||
tree_HEADERS = []
|
||||
i = 0
|
||||
for treeitem in tree_ctnl:
|
||||
tree_HEADERS.append(treeitem[0])
|
||||
self.ui.tree_OUT.setColumnWidth(i, treeitem[1])
|
||||
i += 1
|
||||
return tree_HEADERS
|
||||
|
||||
def set_fullName(self):
|
||||
self.fullName = '{0:4d}{1:02d}{2:02d}-{3:s}'.format(self.year, self.month, self.day, self.name)
|
||||
self.ui.txt_FullName.setText(self.fullName)
|
||||
if len(self.fullName) > 9:
|
||||
self.ui.btn_Renumber.setEnabled(True)
|
||||
else:
|
||||
self.ui.btn_Renumber.setEnabled(False)
|
||||
|
||||
def msg_print(self, msg):
|
||||
self.ui.txt_Error.setText(str(msg))
|
||||
self.repaint()
|
||||
|
||||
''' ========================= Other Functions ==================================================='''
|
||||
''' progress bar class and methods '''
|
||||
class PB():
|
||||
def __init__(self, progressbar, mi = 0, ma = 100 ):
|
||||
# super(PB, self).__init__(parent)
|
||||
self.val = 0
|
||||
self.min = mi
|
||||
self.max = ma
|
||||
self.steps = 10
|
||||
self.step = (self.max - self.min)/self.steps
|
||||
self.progressbar = progressbar
|
||||
self.progressbar.setMinimum(self.min)
|
||||
self.progressbar.setMaximum(self.max)
|
||||
|
||||
def start(self, steps=10):
|
||||
self.steps = (steps if steps > 0 else 10)
|
||||
self.progressbar.setVisible(True)
|
||||
self.progressbar.setValue(0)
|
||||
|
||||
def end(self):
|
||||
self.progressbar.setValue(self.max)
|
||||
# start a timer to make progressbar invisible
|
||||
threading.Timer(3,self.clear).start()
|
||||
|
||||
def clear(self):
|
||||
self.progressbar.setVisible(False)
|
||||
|
||||
def upd(self, val):
|
||||
self.val = val
|
||||
self.progressbar.setValue(self.val)
|
||||
|
||||
def incr(self):
|
||||
actv = self.progressbar.value()
|
||||
newv = actv + self.step
|
||||
if newv <= self.max:
|
||||
self.progressbar.setValue(newv)
|
||||
|
||||
|
||||
'''=========================================================================================================================='''
|
||||
''' Main Program '''
|
||||
if __name__ == '__main__':
|
||||
|
||||
global LOGGER
|
||||
|
||||
''' Enabling Logger
|
||||
'''
|
||||
prog_name = os.path.basename(sys.argv[0:][0])
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
LOGGER.setLevel(logging.DEBUG)
|
||||
LOGGER.propagate = False
|
||||
logging.captureWarnings(True)
|
||||
formatter = logging.Formatter(LOG_FORMAT, LOG_TIME_FORMAT)
|
||||
|
||||
''' File logging
|
||||
'''
|
||||
log_name = "LOG-" + prog_name + ".log"
|
||||
fh = logging.FileHandler(log_name)
|
||||
fh.setLevel(logging.DEBUG)
|
||||
fh.setFormatter(formatter)
|
||||
LOGGER.addHandler(fh)
|
||||
|
||||
''' Console logging
|
||||
'''
|
||||
cl = logging.StreamHandler(sys.stdout)
|
||||
cl.setLevel(logging.DEBUG)
|
||||
cl.setFormatter(formatter)
|
||||
LOGGER.addHandler(cl)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
LOGGER.info("%s: Ver %s - Date: %s" % (prog_name, mod_ver, mod_dver))
|
||||
|
||||
''' Open window and give control to QTPY '''
|
||||
app = QApplication(sys.argv)
|
||||
window = main()
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
||||
742
PICT_numtool_240819_save.py
Executable file
742
PICT_numtool_240819_save.py
Executable file
@@ -0,0 +1,742 @@
|
||||
#!/usr/bin/python
|
||||
'''
|
||||
=========================================================================================================
|
||||
T&T Software and Systems
|
||||
Project: Renumbering tool
|
||||
Module: Main module
|
||||
Description: Tool to renumber ohoto and video replace VB one
|
||||
|
||||
|
||||
Tiziano Trabattoni | 2016/12/10 | first version
|
||||
Tiziano Trabattoni | 2022/07/31 | Porting su MacOS, PyQt5, python3
|
||||
Tiziano Trabattoni | 2023/05/22 | Porting su MacOS per pyqt6
|
||||
Tiziano Trabattoni | 2023/08/14 | Sorting by Creation date with exiftool
|
||||
Tiziano Trabattoni | 2024/08/19 | Chenged movie tags to include filemodification date for .mp4 from Android
|
||||
|
||||
####### Developed under poetry (with pyqt6 M1 ARM64 code available)
|
||||
~/Develop/Personal/PICT_poetry » poetry remove pyqt5
|
||||
~/Develop/Personal/PICT_poetry » poetry add pyqt6
|
||||
|
||||
~/Develop/Personal/PICT_poetry » pip freeze ttrabatt@TTRABATT-M-3QXG
|
||||
PyQt6==6.5.0
|
||||
PyQt6-Qt6==6.5.0
|
||||
PyQt6-sip==13.5.1
|
||||
(pict-numtool-py3.9) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
~/Develop/Personal/PICT_poetry » poetry show ttrabatt@TTRABATT-M-3QXG
|
||||
pyqt6 6.5.0 Python bindings for the Qt cross platform application toolkit
|
||||
pyqt6-qt6 6.5.0 The subset of a Qt installation needed by PyQt6.
|
||||
pyqt6-sip 13.5.1 The sip module support for PyQt6
|
||||
(pict-numtool-py3.9) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
~/Develop/Personal/PICT_poetry »
|
||||
|
||||
|
||||
|
||||
=========================================================================================================
|
||||
'''
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
import subprocess
|
||||
import tempfile
|
||||
import json
|
||||
from datetime import datetime
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
''' PyQT6 imports'''
|
||||
from PICT_numtool_ui import Ui_MainWindow
|
||||
from PyQt6 import QtGui
|
||||
from PyQt6.QtWidgets import QTreeView, QLabel, QTreeWidgetItem, QMainWindow, QApplication, QFileDialog, QAbstractItemView
|
||||
from PyQt6.QtGui import QPixmap
|
||||
#from PyQt5.QtCore import *
|
||||
#from PyQt6 import QtCore
|
||||
global LOGGER
|
||||
|
||||
''' Module version '''
|
||||
mod_ver = "v2.1"
|
||||
mod_dver = "2023:08:17"
|
||||
|
||||
''' Logger Variables '''
|
||||
LOG_FORMAT = '%(asctime)s|%(levelname)-7s|%(funcName)-15s|%(lineno)-5d: %(message)-50s'
|
||||
LOG_TIME_FORMAT = '%m-%d %H:%M:%S'
|
||||
|
||||
CONSOLE_DEBUG = logging.INFO
|
||||
FILE_DEBUG = logging.INFO
|
||||
|
||||
PHOTO_filter = 'Images (*.jpg *.jpeg *.png *.tiff *.heic)'
|
||||
MOVIE_filter = 'Movies (*.mov *.mp4)'
|
||||
|
||||
# file format and extension variables
|
||||
EXIFTOOL = "exiftool"
|
||||
EXIF_PHOTO_EXT = (".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG", ".heic", ".HEIC", ".tiff", ".TIFF",)
|
||||
EXIF_MOVIE_EXT = (".mov", ".MOV", ".mp4", ".MP4",)
|
||||
EXIF_PHOTO_TAGS = ("-datetimeoriginal", "-filecreatedate", "-filemodifydate",)
|
||||
EXIF_MOVIE_TAGS = ("-mediacreatedate","-filemodifydate",)
|
||||
|
||||
FILE_TREE_CNTL = [('Filename.', 300, QTreeView),
|
||||
('FullNAme', 600, QtGui.QTextItem), ('Preview', 100, QLabel)]
|
||||
|
||||
YEARS_RANGE = [1970, 2040]
|
||||
|
||||
COL_WHITE = "QPushButton { color: white;}"
|
||||
COL_RED = "QPushButton { color: red;}"
|
||||
COL_GREEN = "QPushButton { color: green;}"
|
||||
COL_YELLOW = "QPushButton { color: yellow;}"
|
||||
COL_BLUE = "QPushButton { color: blue;}"
|
||||
COL_BLACK = "QPushButton { color: black;}"
|
||||
|
||||
BCOL_WHITE = "background-color: white;"
|
||||
BCOL_RED = "background-color: red;"
|
||||
BCOL_GREEN = "background-color: green;"
|
||||
BCOL_YELLOW = "background-color: yellow;"
|
||||
BCOL_CYAN = "background-color: cyan;"
|
||||
BCOL_BLACK = "background-color: black;"
|
||||
|
||||
''' Default instances '''
|
||||
DEF_INSTANCE = 'select'
|
||||
NON_FILE_CHARACTERS = '\|/*$?:;<>+"'''
|
||||
|
||||
''' Ordering dictionary '''
|
||||
WorkData = {}
|
||||
WorkDir = ''
|
||||
TmpDir = ''
|
||||
|
||||
class PICTitem(QTreeWidgetItem):
|
||||
'''
|
||||
Custom QTreeWidgetItem with Widgets
|
||||
'''
|
||||
_basename = ""
|
||||
_name = ""
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
return self._basename
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
def __init__(self, parent, basename, name):
|
||||
'''
|
||||
parent (QTreeWidget) : Item's QTreeWidget parent.
|
||||
value (str) : Item's name. just an example.
|
||||
'''
|
||||
|
||||
## Init super class ( QtGui.QTreeWidgetItem )
|
||||
super(PICTitem, self).__init__(parent)
|
||||
|
||||
''' '''
|
||||
self.setText(0, basename)
|
||||
#self.setTextAlignment(0, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self.setText(1, name)
|
||||
#self.setTextAlignment(1, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self._name=name
|
||||
self._basename=basename
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self)
|
||||
|
||||
def __str__(self):
|
||||
return f"<class '{self.__class__.__name__}'> Filename:{self.basename}"
|
||||
|
||||
|
||||
''' ======================== MAIN WINDOW CLASS with all methods ================================ '''
|
||||
class main(QMainWindow):
|
||||
def __init__(self):
|
||||
QMainWindow.__init__(self)
|
||||
self.ui = Ui_MainWindow()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
''' create Progressbar element '''
|
||||
self.pb = PB(self.ui.pb_Run,mi = 0, ma =100)
|
||||
|
||||
''' set default values '''
|
||||
self.day = 0
|
||||
self.month = 0
|
||||
self.year = 1900
|
||||
self.name = ''
|
||||
self.fullName = ''
|
||||
self.ineditname = False
|
||||
self.filecriteria = PHOTO_filter
|
||||
self.TmpDir = ''
|
||||
self.WorkDir = ''
|
||||
self.WorkData = {}
|
||||
self.toRename = []
|
||||
|
||||
|
||||
''' set the tree_OUT widget'''
|
||||
self.ui.tree_OUT.setColumnCount(len(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setHeaderLabels(self.set_qtree_headers(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setUniformRowHeights(True)
|
||||
#self.ui.tree_OUT.setSelectionMode(QabsW.ExtendedSelection)
|
||||
|
||||
''' set all Methods '''
|
||||
self.ui.btn_Renumber.setEnabled(False)
|
||||
self.ui.btn_Renumber.clicked.connect(self.click_Renumber)
|
||||
self.ui.btn_Open.clicked.connect(self.click_Open)
|
||||
self.ui.btn_Exit.clicked.connect(self.click_Exit)
|
||||
#self.ui.txt_Name.editingFinished.connect(self.edited_Name)
|
||||
self.ui.rbtn_Photo.setChecked(True)
|
||||
|
||||
''' set all combo '''
|
||||
for i in range(1,32):
|
||||
self.ui.cmb_Day.addItem(str(i))
|
||||
for i in range(1,13):
|
||||
self.ui.cmb_Month.addItem(str(i))
|
||||
for i in range(YEARS_RANGE[0],YEARS_RANGE[1]+1):
|
||||
self.ui.cmb_Year.addItem(str(i))
|
||||
|
||||
''' set date and time '''
|
||||
# print datetime.now().strftime('%d/%m/%Y')
|
||||
self.day = int(datetime.now().strftime('%d'))
|
||||
self.month = int(datetime.now().strftime('%m'))
|
||||
self.year = int(datetime.now().strftime('%Y'))
|
||||
self.ui.cmb_Day.setCurrentIndex(self.day - 1)
|
||||
self.ui.cmb_Month.setCurrentIndex(self.month - 1)
|
||||
self.ui.cmb_Year.setCurrentIndex(self.year - YEARS_RANGE[0])
|
||||
self.ui.date_Day.setDate(datetime.now())
|
||||
self.ui.txt_Name.textChanged.connect(self.changed_Name)
|
||||
|
||||
######## These are OLD PyQT4 signal sintax ###########
|
||||
#''' set Object signals '''
|
||||
#''' visualization selection via Radio Button '''
|
||||
#self.ui.rbtn_Movies.connect(self.ui.rbtn_Movies, QtCore.SIGNAL("clicked()"),self.selected_rbtn_Movies)
|
||||
#self.ui.rbtn_Photo.connect(self.ui.rbtn_Photo, QtCore.SIGNAL("clicked()"), self.selected_rbtn_Photos)
|
||||
#self.ui.date_Day.connect(self.ui.date_Day, QtCore.SIGNAL("dateChanged(QDate)"), self.change_date)
|
||||
#
|
||||
#''' Signals '''
|
||||
#self.ui.cmb_Day.connect(self.ui.cmb_Day, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.choose_Day)
|
||||
#self.ui.cmb_Month.connect(self.ui.cmb_Month, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.choose_Month)
|
||||
#self.ui.cmb_Year.connect(self.ui.cmb_Year, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.choose_Year)
|
||||
## self.ui.txt_Name.connect(self.ui.txt_Name, QtCore.SIGNAL("insertPlainText (const Qstring&"), self.changed_Name)
|
||||
|
||||
''' set Object signals '''
|
||||
''' visualization selection via Radio Button '''
|
||||
self.ui.rbtn_Movies.clicked.connect(self.selected_rbtn_Movies)
|
||||
self.ui.rbtn_Photo.clicked.connect(self.selected_rbtn_Photos)
|
||||
self.ui.date_Day.dateChanged.connect(self.change_date)
|
||||
self.ui.tree_OUT.itemSelectionChanged.connect(self.on_selectionChanged)
|
||||
|
||||
''' Signals '''
|
||||
self.ui.cmb_Day.currentIndexChanged.connect(self.choose_Day)
|
||||
self.ui.cmb_Month.currentIndexChanged.connect(self.choose_Month)
|
||||
self.ui.cmb_Year.currentIndexChanged.connect(self.choose_Year)
|
||||
|
||||
''' other setting '''
|
||||
self.ui.pb_Run.setMinimum(0)
|
||||
self.ui.pb_Run.setMaximum(100)
|
||||
self.ui.pb_Run.setVisible(False)
|
||||
|
||||
def click_Exit(self):
|
||||
sys.exit(0)
|
||||
return
|
||||
|
||||
def QaddFILEChild(self, parent, basename, name):
|
||||
item = PICTitem(parent, basename, name)
|
||||
return item
|
||||
|
||||
|
||||
def on_selectionChanged(self):
|
||||
selected = [ s.basename for s in self.ui.tree_OUT.selectedItems() ]
|
||||
for f in self.WorkData:
|
||||
if f in selected:
|
||||
self.WorkData[f]['rename'] = True
|
||||
else:
|
||||
self.WorkData[f]['rename'] = False
|
||||
return
|
||||
|
||||
|
||||
def click_Open(self):
|
||||
''' open source directory '''
|
||||
#files = QFileDialog.getOpenFileNames(self, "Open Directory", "" , self.filecriteria, None, QFileDialog.DontUseNativeDialog)
|
||||
files = QFileDialog.getOpenFileNames(self, "Open Directory", "" , self.filecriteria, None)
|
||||
nf = len(files)
|
||||
# SHIT now QfileDialog return a Tuple instead of list
|
||||
# Has to be addressed as [0]
|
||||
# because files[1] contains the Type: <class 'str'>, - Value >Images (*.jpg *.jpeg *.png *.tiff)
|
||||
|
||||
|
||||
if len(files[0]) > 0:
|
||||
''' clear and set the tree_OUT widget'''
|
||||
self.ui.tree_OUT.clear()
|
||||
self.ui.tree_OUT.setColumnCount(len(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setHeaderLabels(self.set_qtree_headers(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setUniformRowHeights(True)
|
||||
|
||||
self.pb.start(steps=nf)
|
||||
|
||||
''' initialized work list dictionary '''
|
||||
self.WorkData = {}
|
||||
|
||||
# LOGGER.debug("Type of files[] >%s<" % (str(type(files))))
|
||||
# self.WorkDir = os.path.dirname(str(files[0]))
|
||||
# SHIT now QfileDialog return a Tuple instead of list
|
||||
# Has to be addressed as [0]
|
||||
# because files[1] contains the Type: <class 'str'>, - Value >Images (*.jpg *.jpeg *.png *.tiff)
|
||||
fileuno = files[0][0]
|
||||
#LOGGER.debug("fileuno >%s<" % fileuno)
|
||||
|
||||
self.WorkDir = os.path.dirname(fileuno)
|
||||
LOGGER.debug(f"Workdir >{self.WorkDir}<")
|
||||
|
||||
''' tempory dir must be on same disk otherwise is too slow'''
|
||||
basedir = os.path.dirname(self.WorkDir)
|
||||
self.TmpDir = basedir + '/PICTtool-tmp'
|
||||
if not os.path.exists(self.TmpDir):
|
||||
os.mkdir(self.TmpDir)
|
||||
count = 1
|
||||
|
||||
#for fname in files[0]:
|
||||
# LOGGER.debug("Type: %s, - Value >%s<" % (type(fname), fname))
|
||||
|
||||
''' for all selected files '''
|
||||
for fname in files[0]:
|
||||
self.pb.incr()
|
||||
self.repaint()
|
||||
''' add an item to Qtree including resizing of pictures '''
|
||||
# self.add_Qtree_item(fname)
|
||||
|
||||
basename = os.path.basename(str(fname))
|
||||
LOGGER.info(f"Reading Metadata from: >{basename}< ")
|
||||
|
||||
# Get File Metadata via external exiftool
|
||||
# requires installation
|
||||
# https://exiftool.org/index.html#running
|
||||
#
|
||||
|
||||
|
||||
|
||||
# exiftool has produced with json and TAGS
|
||||
if basename.endswith(EXIF_MOVIE_EXT):
|
||||
EXIF_TAGS = EXIF_MOVIE_TAGS
|
||||
else:
|
||||
EXIF_TAGS = EXIF_PHOTO_TAGS
|
||||
|
||||
args=[EXIFTOOL, "-json", *EXIF_TAGS, os.path.join(self.WorkDir,basename)]
|
||||
LOGGER.debug(str(args))
|
||||
process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
res=json.loads(process.stdout.read())
|
||||
LOGGER.debug(json.dumps(res,indent=2))
|
||||
dat = res[0]
|
||||
|
||||
# reset of all tag values
|
||||
dt = odate = fmdate = fcdate = mcdate = None
|
||||
if basename.endswith(EXIF_MOVIE_EXT):
|
||||
# MOVIEs
|
||||
try:
|
||||
fmdate = dat['FileModifyDate']
|
||||
except KeyError:
|
||||
LOGGER.debug(f"File {basename} does not contain tag FileModifyDate")
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing error {e}")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
try:
|
||||
mcdate = dat['MediaCreateDate']
|
||||
except KeyError:
|
||||
LOGGER.debug(f"File {basename} does not contain tag MediaCreateDate")
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing error {e}")
|
||||
continue
|
||||
|
||||
if not mcdate and not fmdate:
|
||||
LOGGER.error(f"File {basename} does NOT contain expected metadata tags, please verify results")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
|
||||
if fmdate:
|
||||
try:
|
||||
# dt = f"{mcdate[0]}:{mcdate[1]}:{mcdate[2].split(' ')[0]}|{mcdate[2].split(' ')[1]}:{mcdate[3]}:{(mcdate[4].split('+'))[0]}".strip()
|
||||
pd = int(time.mktime(time.strptime(fmdate, '%Y:%m:%d %H:%M:%S%z')))
|
||||
pod = datetime.fromtimestamp(pd)
|
||||
dt = pod.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing tag value: {e} ...Skipping...")
|
||||
continue # for fname in files[0]:
|
||||
else:
|
||||
try:
|
||||
# dt = f"{mcdate[0]}:{mcdate[1]}:{mcdate[2].split(' ')[0]}|{mcdate[2].split(' ')[1]}:{mcdate[3]}:{(mcdate[4].split('+'))[0]}".strip()
|
||||
pd = int(time.mktime(time.strptime(mcdate, '%Y:%m:%d %H:%M:%S')))
|
||||
pod = datetime.fromtimestamp(pd)
|
||||
dt = pod.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing tag value: {e} ...Skipping...")
|
||||
continue # for fname in files[0]:
|
||||
else:
|
||||
# PICTUREs
|
||||
try:
|
||||
odate = dat['DateTimeOriginal']
|
||||
except KeyError:
|
||||
LOGGER.debug(f"File {basename} does not contain tag DateTimeOriginal")
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing error {e}")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
try:
|
||||
fcdate = dat['FileCreateDate']
|
||||
except KeyError:
|
||||
LOGGER.debug(f"File {basename} does not contain tag FileCreateDate")
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing error {e}")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
try:
|
||||
fmdate = dat['FileModifyDate']
|
||||
except KeyError:
|
||||
LOGGER.debug(f"File {basename} does not contain tag FileModifyDate")
|
||||
except Exception as e:
|
||||
LOGGER.error(f"File {basename} parsing error {e}")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
if not odate and not fcdate and not fmdate:
|
||||
LOGGER.error(f"File {basename} does NOT contain expected metadata tags, please verify results")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
if odate:
|
||||
#take DateTimeOriginal
|
||||
od = int(time.mktime(time.strptime(odate, '%Y:%m:%d %H:%M:%S')))
|
||||
ood = datetime.fromtimestamp(od)
|
||||
dt = ood.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
elif fcdate and fmdate:
|
||||
# take older between FileCreateDate and FileModifyDate
|
||||
fcfmdate = min(int(time.mktime(time.strptime(fcdate, '%Y:%m:%d %H:%M:%S%z'))), int(time.mktime(time.strptime(fmdate, '%Y:%m:%d %H:%M:%S%z'))))
|
||||
ofcfmdate = datetime.fromtimestamp(fcfmdate)
|
||||
dt = ofcfmdate.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
elif fcdate and not fmdate:
|
||||
# take fcdate
|
||||
fc = int(time.mktime(time.strptime(fcdate, '%Y:%m:%d %H:%M:%S%z')))
|
||||
dt = fc.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
else:
|
||||
# take fmdate
|
||||
fm = int(time.mktime(time.strptime(fmdate, '%Y:%m:%d %H:%M:%S%z')))
|
||||
dt = fm.strftime('%Y:%m:%d|%H:%M:%S')
|
||||
|
||||
if not dt:
|
||||
LOGGER.error(f"File {basename} does NOT contain expected metadata tags, please verify results")
|
||||
continue # for fname in files[0]:
|
||||
|
||||
# check if same modification time exist so it loops adding one second to modification time
|
||||
adt = int(time.mktime(time.strptime(dt, '%Y:%m:%d|%H:%M:%S')))
|
||||
|
||||
while adt in self.WorkData.keys():
|
||||
adt+=1
|
||||
|
||||
LOGGER.info(f"Time: {dt} --> {basename}")
|
||||
|
||||
''' fill dictionary '''
|
||||
self.WorkData.update({
|
||||
# int(time.mktime(time.strptime(dt, '%Y:%m:%d|%H:%M:%S'))) :
|
||||
adt :
|
||||
{
|
||||
#'itemNum' : str(count),
|
||||
'fname' : basename,
|
||||
'fileExt' : self.get_extension(str(fname)),
|
||||
'tmpName' : self.TmpDir + '/PICTtmp-' + next(tempfile._get_candidate_names()),
|
||||
'rename' : True,
|
||||
'date': dt.split('|')[0],
|
||||
'time': dt.split('|')[1]
|
||||
}
|
||||
})
|
||||
count += 1
|
||||
|
||||
self.pb.end()
|
||||
# print unsorted dictionary
|
||||
# print(f"{json.dumps(self.WorkData, indent=4)}")
|
||||
|
||||
# transpose disctionary and sort it by date of creation of the file
|
||||
flts = dict(sorted(self.WorkData.items()))
|
||||
self.WorkData = flts
|
||||
|
||||
# print(f"{json.dumps(self.WorkData, indent=4)}")
|
||||
|
||||
for _,v in self.WorkData.items():
|
||||
''' add an item to Qtree including resizing of pictures '''
|
||||
# self.add_Qtree_item(f"{v['fname']}-->{v['date']}|{v['time']}")
|
||||
self.add_Qtree_item(f"{v['fname']}")
|
||||
|
||||
return
|
||||
|
||||
def click_Renumber(self):
|
||||
''' renumber and create a new WorkData for next phases '''
|
||||
newWorkData = {}
|
||||
|
||||
''' only if WorkData Contains elements '''
|
||||
nf = len(self.WorkData)
|
||||
if nf > 0:
|
||||
self.pb.start(steps=nf * 2)
|
||||
|
||||
''' for all files move to temp to avoid overlapping'''
|
||||
for _,v in self.WorkData.items():
|
||||
LOGGER.debug(f"Moving file {v}")
|
||||
if v['rename']:
|
||||
self.pb.incr()
|
||||
try:
|
||||
tmpfile = v['tmpName']
|
||||
origfile = self.WorkDir + '/' + v['fname']
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Parsing file attributes [{v['fname']} : {e} ... Skipping ...")
|
||||
else:
|
||||
try:
|
||||
shutil.move(origfile, tmpfile)
|
||||
#os.rename(origfile, tmpfile)
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Renaming file [{origfile}] to tmpfile: {tmpfile} : {e} ... Skipping ...")
|
||||
else:
|
||||
# LOGGER.debug(f"Origfile [{origfile}] ==> TmpFile [{tmpfile}]')
|
||||
pass
|
||||
|
||||
''' for all files move to current directory with new name'''
|
||||
count = 1
|
||||
for _,v in self.WorkData.items():
|
||||
LOGGER.debug(f"Moving file {v}")
|
||||
if v['rename']:
|
||||
self.pb.incr()
|
||||
try:
|
||||
tmpfile = v['tmpName']
|
||||
extension = v['fileExt']
|
||||
#count = int(self.WorkData[fname]['itemNum'])
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Parsing file attributes [{v['fname']} : {e} ... Skipping ...")
|
||||
else:
|
||||
targetfile = self.WorkDir + '/' + self.fullName + '-{0:04d}'.format(count) + extension
|
||||
targetfname = self.fullName + '-{0:04d}'.format(count) + extension
|
||||
try:
|
||||
shutil.move(tmpfile, targetfile)
|
||||
#os.rename(tmpfile, targetfile)
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Renaming tmpfile [{tmpfile}] to: {targetfname} : {e} ... Skipping ...")
|
||||
else:
|
||||
#LOGGER.debug('TmpFile [%s] ==> TargetFIle [%s]' % (tmpfile, targetfile))
|
||||
LOGGER.info(f"Renamed [{tmpfile}] ==> [{targetfile}]")
|
||||
pass
|
||||
|
||||
''' fill dictionary '''
|
||||
newWorkData.update({
|
||||
targetfname : {
|
||||
'itemNum' : str(count),
|
||||
'fileExt' : self.get_extension(str(targetfname)),
|
||||
'tmpName' : self.TmpDir + '/PICTtmp-' + next(tempfile._get_candidate_names()),
|
||||
'rename' : True
|
||||
},
|
||||
})
|
||||
count += 1
|
||||
|
||||
self.pb.end()
|
||||
|
||||
''' also rename directory '''
|
||||
basedir = os.path.dirname(self.WorkDir)
|
||||
olddir = self.WorkDir
|
||||
newdir = basedir + '/' + self.fullName
|
||||
try:
|
||||
shutil.move(olddir, newdir)
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Rename of directory to [{basedir}] failed {e} ...Skipping...")
|
||||
|
||||
''' sel new Current working directory and Workdata to new dictionary '''
|
||||
self.WorkDir = newdir
|
||||
os.chdir(self.WorkDir)
|
||||
self.WorkData = newWorkData
|
||||
|
||||
''' empties Qtree '''
|
||||
self.ui.tree_OUT.clear()
|
||||
self.ui.tree_OUT.setColumnCount(len(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setHeaderLabels(self.set_qtree_headers(FILE_TREE_CNTL))
|
||||
self.ui.tree_OUT.setUniformRowHeights(True)
|
||||
|
||||
''' and refill it again '''
|
||||
nf = len (self.WorkData)
|
||||
WorkData_s = OrderedDict(self.WorkData)
|
||||
|
||||
# redraw the dialog
|
||||
self.pb.start(steps=nf)
|
||||
for fname in WorkData_s:
|
||||
self.pb.incr()
|
||||
self.add_Qtree_item(self.WorkDir + '/' + fname)
|
||||
|
||||
self.pb.end()
|
||||
|
||||
return
|
||||
|
||||
def add_Qtree_item(self, fname):
|
||||
basefile = os.path.basename(str(fname))
|
||||
|
||||
if self.ui.chk_Preview.isChecked():
|
||||
''' review is activated need to elaborate thumbnail '''
|
||||
item = self.QaddFILEChild(self.ui.tree_OUT, basefile, str(fname))
|
||||
try:
|
||||
''' rezize file '''
|
||||
#img = QtGui.QImage(fname)
|
||||
# qim = img.scaled(64, 64, QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.SmoothTransformation)
|
||||
# qim = img.scaled(64, 64)
|
||||
# pict = QPixmap.fromImage(qim)
|
||||
pict = QPixmap(fname)
|
||||
except Exception as e:
|
||||
LOGGER.error(f"Unable to process image [{fname}] {e} ... Skipping ...")
|
||||
label = QLabel()
|
||||
label.setText("Preview Error")
|
||||
else:
|
||||
''' create a Label with picture as background '''
|
||||
label = QLabel()
|
||||
label.setPixmap(pict)
|
||||
|
||||
''' add column 2 a Label with picture in pixmap '''
|
||||
item.treeWidget().setItemWidget(item, 2, label)
|
||||
else:
|
||||
item = self.QaddFILEChild(self.ui.tree_OUT, basefile, str(fname))
|
||||
return item
|
||||
|
||||
def get_extension(self, fname):
|
||||
basename = os.path.basename(fname) # os independent
|
||||
ext = '.'.join(basename.split('.')[1:])
|
||||
return '.' + ext if ext else None
|
||||
|
||||
def change_date(self):
|
||||
self.day = self.ui.date_Day.date().day()
|
||||
self.month = self.ui.date_Day.date().month()
|
||||
self.year = self.ui.date_Day.date().year()
|
||||
self.ui.cmb_Day.setCurrentIndex(self.day - 1)
|
||||
self.ui.cmb_Month.setCurrentIndex(self.month - 1)
|
||||
self.ui.cmb_Year.setCurrentIndex(self.year - YEARS_RANGE[0] )
|
||||
|
||||
''' set fullname '''
|
||||
self.set_fullName()
|
||||
|
||||
return
|
||||
|
||||
def changed_Name(self):
|
||||
if self.ineditname:
|
||||
return
|
||||
else:
|
||||
tc = self.ui.txt_Name.textCursor()
|
||||
name = str(self.ui.txt_Name.toPlainText())
|
||||
filename = "".join(i for i in name if i not in NON_FILE_CHARACTERS)
|
||||
self.ineditname = True
|
||||
self.ui.txt_Name.setPlainText(filename)
|
||||
self.ui.txt_Name.setTextCursor(tc)
|
||||
self.ineditname = False
|
||||
self.name = filename
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def selected_rbtn_Movies(self):
|
||||
self.filecriteria = MOVIE_filter
|
||||
return
|
||||
|
||||
def selected_rbtn_Photos(self):
|
||||
self.filecriteria = PHOTO_filter
|
||||
return
|
||||
|
||||
def choose_Day(self):
|
||||
self.day = int(self.ui.cmb_Day.currentText())
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def choose_Month(self):
|
||||
self.month = int(self.ui.cmb_Month.currentText())
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def choose_Year(self):
|
||||
self.year = int(self.ui.cmb_Year.currentText())
|
||||
self.set_fullName()
|
||||
return
|
||||
|
||||
def set_qtree_headers(self, tree_ctnl):
|
||||
tree_HEADERS = []
|
||||
i = 0
|
||||
for treeitem in tree_ctnl:
|
||||
tree_HEADERS.append(treeitem[0])
|
||||
self.ui.tree_OUT.setColumnWidth(i, treeitem[1])
|
||||
i += 1
|
||||
return tree_HEADERS
|
||||
|
||||
def set_fullName(self):
|
||||
self.fullName = '{0:4d}{1:02d}{2:02d}-{3:s}'.format(self.year, self.month, self.day, self.name)
|
||||
self.ui.txt_FullName.setText(self.fullName)
|
||||
if len(self.fullName) > 9:
|
||||
self.ui.btn_Renumber.setEnabled(True)
|
||||
else:
|
||||
self.ui.btn_Renumber.setEnabled(False)
|
||||
|
||||
# def msg_print(self, msg):
|
||||
# self.ui.txt_Error.setText(str(msg))
|
||||
# self.repaint()
|
||||
|
||||
''' ========================= Other Functions ==================================================='''
|
||||
''' progress bar class and methods '''
|
||||
class PB():
|
||||
def __init__(self, progressbar, mi = 0, ma = 100 ):
|
||||
# super(PB, self).__init__(parent)
|
||||
self.val = 0
|
||||
self.min = mi
|
||||
self.max = ma
|
||||
self.steps = 10
|
||||
self.step = (self.max - self.min)/self.steps
|
||||
self.progressbar = progressbar
|
||||
self.progressbar.setMinimum(self.min)
|
||||
self.progressbar.setMaximum(self.max)
|
||||
|
||||
def start(self, steps=10):
|
||||
self.steps = (steps if steps > 0 else 10)
|
||||
self.progressbar.setVisible(True)
|
||||
self.progressbar.setValue(0)
|
||||
|
||||
def end(self):
|
||||
self.progressbar.setValue(self.max)
|
||||
# start a timer to make progressbar invisible
|
||||
threading.Timer(3,self.clear).start()
|
||||
|
||||
def clear(self):
|
||||
self.progressbar.setVisible(False)
|
||||
|
||||
def upd(self, val):
|
||||
self.val = val
|
||||
self.progressbar.setValue(self.val)
|
||||
|
||||
def incr(self):
|
||||
actv = self.progressbar.value()
|
||||
newv = actv + self.step
|
||||
if newv <= self.max:
|
||||
self.progressbar.setValue(newv)
|
||||
|
||||
|
||||
'''=========================================================================================================================='''
|
||||
''' Main Program '''
|
||||
if __name__ == '__main__':
|
||||
|
||||
''' Enabling Logger
|
||||
'''
|
||||
prog_name = os.path.basename(sys.argv[0:][0])
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
LOGGER.setLevel(logging.DEBUG)
|
||||
LOGGER.propagate = False
|
||||
logging.captureWarnings(True)
|
||||
formatter = logging.Formatter(LOG_FORMAT, LOG_TIME_FORMAT)
|
||||
|
||||
''' File logging
|
||||
'''
|
||||
log_name = "LOG-" + prog_name + ".log"
|
||||
fh = logging.FileHandler(log_name)
|
||||
fh.setLevel(FILE_DEBUG)
|
||||
fh.setFormatter(formatter)
|
||||
LOGGER.addHandler(fh)
|
||||
|
||||
''' Console logging
|
||||
'''
|
||||
cl = logging.StreamHandler(sys.stdout)
|
||||
cl.setLevel(CONSOLE_DEBUG)
|
||||
cl.setFormatter(formatter)
|
||||
LOGGER.addHandler(cl)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
LOGGER.info("%s: Ver %s - Date: %s" % (prog_name, mod_ver, mod_dver))
|
||||
|
||||
''' Open window and give control to QTPY '''
|
||||
app = QApplication(sys.argv)
|
||||
window = main()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
263
PICT_numtool_ui.py
Executable file
263
PICT_numtool_ui.py
Executable file
@@ -0,0 +1,263 @@
|
||||
# Form implementation generated from reading ui file 'PICT_numtool_ui.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.7.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
MainWindow.setObjectName("MainWindow")
|
||||
MainWindow.resize(1728, 838)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
|
||||
MainWindow.setSizePolicy(sizePolicy)
|
||||
self.wdg_Main = QtWidgets.QWidget(parent=MainWindow)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.MinimumExpanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.wdg_Main.sizePolicy().hasHeightForWidth())
|
||||
self.wdg_Main.setSizePolicy(sizePolicy)
|
||||
self.wdg_Main.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.wdg_Main.setObjectName("wdg_Main")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(self.wdg_Main)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.layH_DateName = QtWidgets.QHBoxLayout()
|
||||
self.layH_DateName.setObjectName("layH_DateName")
|
||||
self.date_Day = QtWidgets.QDateEdit(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.date_Day.sizePolicy().hasHeightForWidth())
|
||||
self.date_Day.setSizePolicy(sizePolicy)
|
||||
self.date_Day.setMinimumSize(QtCore.QSize(100, 20))
|
||||
self.date_Day.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.date_Day.setTime(QtCore.QTime(0, 0, 0))
|
||||
self.date_Day.setCalendarPopup(True)
|
||||
self.date_Day.setTimeSpec(QtCore.Qt.TimeSpec.LocalTime)
|
||||
self.date_Day.setObjectName("date_Day")
|
||||
self.layH_DateName.addWidget(self.date_Day)
|
||||
self.lbl_day = QtWidgets.QLabel(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_day.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_day.setSizePolicy(sizePolicy)
|
||||
self.lbl_day.setMinimumSize(QtCore.QSize(40, 20))
|
||||
self.lbl_day.setObjectName("lbl_day")
|
||||
self.layH_DateName.addWidget(self.lbl_day)
|
||||
self.cmb_Day = QtWidgets.QComboBox(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.cmb_Day.sizePolicy().hasHeightForWidth())
|
||||
self.cmb_Day.setSizePolicy(sizePolicy)
|
||||
self.cmb_Day.setMinimumSize(QtCore.QSize(50, 20))
|
||||
self.cmb_Day.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.cmb_Day.setObjectName("cmb_Day")
|
||||
self.layH_DateName.addWidget(self.cmb_Day)
|
||||
self.lbl_Month = QtWidgets.QLabel(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_Month.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_Month.setSizePolicy(sizePolicy)
|
||||
self.lbl_Month.setMinimumSize(QtCore.QSize(50, 20))
|
||||
self.lbl_Month.setObjectName("lbl_Month")
|
||||
self.layH_DateName.addWidget(self.lbl_Month)
|
||||
self.cmb_Month = QtWidgets.QComboBox(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.cmb_Month.sizePolicy().hasHeightForWidth())
|
||||
self.cmb_Month.setSizePolicy(sizePolicy)
|
||||
self.cmb_Month.setMinimumSize(QtCore.QSize(50, 20))
|
||||
self.cmb_Month.setMaximumSize(QtCore.QSize(16777215, 16777215))
|
||||
self.cmb_Month.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.cmb_Month.setObjectName("cmb_Month")
|
||||
self.layH_DateName.addWidget(self.cmb_Month)
|
||||
self.lbl_Year = QtWidgets.QLabel(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_Year.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_Year.setSizePolicy(sizePolicy)
|
||||
self.lbl_Year.setObjectName("lbl_Year")
|
||||
self.layH_DateName.addWidget(self.lbl_Year)
|
||||
self.cmb_Year = QtWidgets.QComboBox(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.cmb_Year.sizePolicy().hasHeightForWidth())
|
||||
self.cmb_Year.setSizePolicy(sizePolicy)
|
||||
self.cmb_Year.setMinimumSize(QtCore.QSize(80, 20))
|
||||
self.cmb_Year.setMaximumSize(QtCore.QSize(80, 16777215))
|
||||
self.cmb_Year.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.cmb_Year.setObjectName("cmb_Year")
|
||||
self.layH_DateName.addWidget(self.cmb_Year)
|
||||
self.lbl_Name_2 = QtWidgets.QLabel(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_Name_2.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_Name_2.setSizePolicy(sizePolicy)
|
||||
self.lbl_Name_2.setMinimumSize(QtCore.QSize(0, 20))
|
||||
self.lbl_Name_2.setObjectName("lbl_Name_2")
|
||||
self.layH_DateName.addWidget(self.lbl_Name_2)
|
||||
self.txt_Name = QtWidgets.QPlainTextEdit(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.txt_Name.sizePolicy().hasHeightForWidth())
|
||||
self.txt_Name.setSizePolicy(sizePolicy)
|
||||
self.txt_Name.setMinimumSize(QtCore.QSize(200, 20))
|
||||
self.txt_Name.setMaximumSize(QtCore.QSize(16777215, 30))
|
||||
self.txt_Name.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhNoAutoUppercase)
|
||||
self.txt_Name.setObjectName("txt_Name")
|
||||
self.layH_DateName.addWidget(self.txt_Name)
|
||||
self.verticalLayout.addLayout(self.layH_DateName)
|
||||
self.lay_Properties = QtWidgets.QHBoxLayout()
|
||||
self.lay_Properties.setObjectName("lay_Properties")
|
||||
self.lbl_Name = QtWidgets.QLabel(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_Name.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_Name.setSizePolicy(sizePolicy)
|
||||
self.lbl_Name.setMinimumSize(QtCore.QSize(0, 20))
|
||||
self.lbl_Name.setObjectName("lbl_Name")
|
||||
self.lay_Properties.addWidget(self.lbl_Name)
|
||||
self.txt_FullName = QtWidgets.QLineEdit(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.txt_FullName.sizePolicy().hasHeightForWidth())
|
||||
self.txt_FullName.setSizePolicy(sizePolicy)
|
||||
self.txt_FullName.setMinimumSize(QtCore.QSize(200, 30))
|
||||
self.txt_FullName.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.txt_FullName.setReadOnly(True)
|
||||
self.txt_FullName.setObjectName("txt_FullName")
|
||||
self.lay_Properties.addWidget(self.txt_FullName)
|
||||
self.verticalLayout.addLayout(self.lay_Properties)
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.tree_OUT = QtWidgets.QTreeWidget(parent=self.wdg_Main)
|
||||
self.tree_OUT.setMinimumSize(QtCore.QSize(0, 20))
|
||||
self.tree_OUT.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.tree_OUT.setColumnCount(2)
|
||||
self.tree_OUT.setObjectName("tree_OUT")
|
||||
self.tree_OUT.headerItem().setText(0, "1")
|
||||
self.tree_OUT.headerItem().setText(1, "2")
|
||||
self.verticalLayout_2.addWidget(self.tree_OUT)
|
||||
self.verticalLayout.addLayout(self.verticalLayout_2)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.grp_Type = QtWidgets.QGroupBox(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.grp_Type.sizePolicy().hasHeightForWidth())
|
||||
self.grp_Type.setSizePolicy(sizePolicy)
|
||||
self.grp_Type.setMinimumSize(QtCore.QSize(200, 30))
|
||||
self.grp_Type.setMaximumSize(QtCore.QSize(200, 16777215))
|
||||
self.grp_Type.setTitle("")
|
||||
self.grp_Type.setObjectName("grp_Type")
|
||||
self.rbtn_Movies = QtWidgets.QRadioButton(parent=self.grp_Type)
|
||||
self.rbtn_Movies.setGeometry(QtCore.QRect(90, 0, 101, 31))
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.rbtn_Movies.sizePolicy().hasHeightForWidth())
|
||||
self.rbtn_Movies.setSizePolicy(sizePolicy)
|
||||
self.rbtn_Movies.setMinimumSize(QtCore.QSize(50, 20))
|
||||
self.rbtn_Movies.setChecked(True)
|
||||
self.rbtn_Movies.setObjectName("rbtn_Movies")
|
||||
self.rbtn_Photo = QtWidgets.QRadioButton(parent=self.grp_Type)
|
||||
self.rbtn_Photo.setGeometry(QtCore.QRect(0, 0, 78, 31))
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.rbtn_Photo.sizePolicy().hasHeightForWidth())
|
||||
self.rbtn_Photo.setSizePolicy(sizePolicy)
|
||||
self.rbtn_Photo.setMinimumSize(QtCore.QSize(25, 0))
|
||||
self.rbtn_Photo.setChecked(False)
|
||||
self.rbtn_Photo.setObjectName("rbtn_Photo")
|
||||
self.horizontalLayout.addWidget(self.grp_Type, 0, QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
self.chk_IgnMeta = QtWidgets.QCheckBox(parent=self.wdg_Main)
|
||||
self.chk_IgnMeta.setMinimumSize(QtCore.QSize(150, 20))
|
||||
self.chk_IgnMeta.setMaximumSize(QtCore.QSize(150, 20))
|
||||
self.chk_IgnMeta.setObjectName("chk_IgnMeta")
|
||||
self.horizontalLayout.addWidget(self.chk_IgnMeta)
|
||||
self.chk_Preview = QtWidgets.QCheckBox(parent=self.wdg_Main)
|
||||
self.chk_Preview.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.chk_Preview.sizePolicy().hasHeightForWidth())
|
||||
self.chk_Preview.setSizePolicy(sizePolicy)
|
||||
self.chk_Preview.setMinimumSize(QtCore.QSize(150, 20))
|
||||
self.chk_Preview.setObjectName("chk_Preview")
|
||||
self.horizontalLayout.addWidget(self.chk_Preview)
|
||||
self.btn_Open = QtWidgets.QPushButton(parent=self.wdg_Main)
|
||||
self.btn_Open.setMinimumSize(QtCore.QSize(100, 30))
|
||||
self.btn_Open.setObjectName("btn_Open")
|
||||
self.horizontalLayout.addWidget(self.btn_Open)
|
||||
self.btn_Renumber = QtWidgets.QPushButton(parent=self.wdg_Main)
|
||||
self.btn_Renumber.setMinimumSize(QtCore.QSize(100, 0))
|
||||
self.btn_Renumber.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus)
|
||||
self.btn_Renumber.setObjectName("btn_Renumber")
|
||||
self.horizontalLayout.addWidget(self.btn_Renumber)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
self.lay_ErrExit = QtWidgets.QHBoxLayout()
|
||||
self.lay_ErrExit.setObjectName("lay_ErrExit")
|
||||
self.pb_Run = QtWidgets.QProgressBar(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.pb_Run.sizePolicy().hasHeightForWidth())
|
||||
self.pb_Run.setSizePolicy(sizePolicy)
|
||||
self.pb_Run.setMinimumSize(QtCore.QSize(600, 0))
|
||||
self.pb_Run.setMaximumSize(QtCore.QSize(900, 16777215))
|
||||
self.pb_Run.setProperty("value", 0)
|
||||
self.pb_Run.setObjectName("pb_Run")
|
||||
self.lay_ErrExit.addWidget(self.pb_Run)
|
||||
self.btn_Exit = QtWidgets.QPushButton(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.btn_Exit.sizePolicy().hasHeightForWidth())
|
||||
self.btn_Exit.setSizePolicy(sizePolicy)
|
||||
self.btn_Exit.setMinimumSize(QtCore.QSize(100, 30))
|
||||
self.btn_Exit.setMaximumSize(QtCore.QSize(100, 30))
|
||||
self.btn_Exit.setObjectName("btn_Exit")
|
||||
self.lay_ErrExit.addWidget(self.btn_Exit, 0, QtCore.Qt.AlignmentFlag.AlignRight)
|
||||
self.verticalLayout.addLayout(self.lay_ErrExit)
|
||||
MainWindow.setCentralWidget(self.wdg_Main)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "Photo and Video renumbering tool"))
|
||||
self.date_Day.setDisplayFormat(_translate("MainWindow", "dd/MM/yyyy"))
|
||||
self.lbl_day.setText(_translate("MainWindow", "Day"))
|
||||
self.lbl_Month.setText(_translate("MainWindow", "Month"))
|
||||
self.lbl_Year.setText(_translate("MainWindow", "Year"))
|
||||
self.lbl_Name_2.setText(_translate("MainWindow", "Name"))
|
||||
self.lbl_Name.setText(_translate("MainWindow", "New Name"))
|
||||
self.rbtn_Movies.setWhatsThis(_translate("MainWindow", "<html><head/><body><p>Show/Edit Networks for this L3Out</p></body></html>"))
|
||||
self.rbtn_Movies.setText(_translate("MainWindow", "Movies"))
|
||||
self.rbtn_Photo.setWhatsThis(_translate("MainWindow", "<html><head/><body><p>Show/Edit Logical Node Profiles</p></body></html>"))
|
||||
self.rbtn_Photo.setText(_translate("MainWindow", "Photos"))
|
||||
self.chk_IgnMeta.setText(_translate("MainWindow", "Ignore Metadata"))
|
||||
self.chk_Preview.setText(_translate("MainWindow", "Show Preview"))
|
||||
self.btn_Open.setText(_translate("MainWindow", "Open Folder"))
|
||||
self.btn_Renumber.setText(_translate("MainWindow", "Renumber Photo and Movies"))
|
||||
self.btn_Exit.setText(_translate("MainWindow", "Exit"))
|
||||
531
PICT_numtool_ui.ui
Executable file
531
PICT_numtool_ui.ui
Executable file
@@ -0,0 +1,531 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1728</width>
|
||||
<height>838</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Photo and Video renumbering tool</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="wdg_Main">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="layH_DateName">
|
||||
<item>
|
||||
<widget class="QDateEdit" name="date_Day">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<property name="time">
|
||||
<time>
|
||||
<hour>0</hour>
|
||||
<minute>0</minute>
|
||||
<second>0</second>
|
||||
</time>
|
||||
</property>
|
||||
<property name="displayFormat">
|
||||
<string>dd/MM/yyyy</string>
|
||||
</property>
|
||||
<property name="calendarPopup">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="timeSpec">
|
||||
<enum>Qt::LocalTime</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_day">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Day</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cmb_Day">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_Month">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Month</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cmb_Month">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_Year">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cmb_Year">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_Name_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="txt_Name">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhNoAutoUppercase</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="lay_Properties">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_Name">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txt_FullName">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="tree_OUT">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">2</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item alignment="Qt::AlignVCenter">
|
||||
<widget class="QGroupBox" name="grp_Type">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<widget class="QRadioButton" name="rbtn_Movies">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>90</x>
|
||||
<y>0</y>
|
||||
<width>101</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Show/Edit Networks for this L3Out</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Movies</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QRadioButton" name="rbtn_Photo">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>78</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>25</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>Show/Edit Logical Node Profiles</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Photos</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chk_IgnMeta">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ignore Metadata</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chk_Preview">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Preview</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_Open">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_Renumber">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Renumber Photo and Movies</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="lay_ErrExit">
|
||||
<item>
|
||||
<widget class="QProgressBar" name="pb_Run">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>900</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignRight">
|
||||
<widget class="QPushButton" name="btn_Exit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
263
PICT_numtool_ui_ui.py
Normal file
263
PICT_numtool_ui_ui.py
Normal file
@@ -0,0 +1,263 @@
|
||||
# Form implementation generated from reading ui file '/Users/Tiziano/Library/CloudStorage/OneDrive-Personal/Documents/Develop/Develop-Personal/PICT_poetry_devel/PICT_numtool_ui.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.7.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
MainWindow.setObjectName("MainWindow")
|
||||
MainWindow.resize(1728, 838)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
|
||||
MainWindow.setSizePolicy(sizePolicy)
|
||||
self.wdg_Main = QtWidgets.QWidget(parent=MainWindow)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.MinimumExpanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.wdg_Main.sizePolicy().hasHeightForWidth())
|
||||
self.wdg_Main.setSizePolicy(sizePolicy)
|
||||
self.wdg_Main.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.wdg_Main.setObjectName("wdg_Main")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(self.wdg_Main)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.layH_DateName = QtWidgets.QHBoxLayout()
|
||||
self.layH_DateName.setObjectName("layH_DateName")
|
||||
self.date_Day = QtWidgets.QDateEdit(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.date_Day.sizePolicy().hasHeightForWidth())
|
||||
self.date_Day.setSizePolicy(sizePolicy)
|
||||
self.date_Day.setMinimumSize(QtCore.QSize(100, 20))
|
||||
self.date_Day.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.date_Day.setTime(QtCore.QTime(0, 0, 0))
|
||||
self.date_Day.setCalendarPopup(True)
|
||||
self.date_Day.setTimeSpec(QtCore.Qt.TimeSpec.LocalTime)
|
||||
self.date_Day.setObjectName("date_Day")
|
||||
self.layH_DateName.addWidget(self.date_Day)
|
||||
self.lbl_day = QtWidgets.QLabel(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_day.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_day.setSizePolicy(sizePolicy)
|
||||
self.lbl_day.setMinimumSize(QtCore.QSize(40, 20))
|
||||
self.lbl_day.setObjectName("lbl_day")
|
||||
self.layH_DateName.addWidget(self.lbl_day)
|
||||
self.cmb_Day = QtWidgets.QComboBox(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.cmb_Day.sizePolicy().hasHeightForWidth())
|
||||
self.cmb_Day.setSizePolicy(sizePolicy)
|
||||
self.cmb_Day.setMinimumSize(QtCore.QSize(50, 20))
|
||||
self.cmb_Day.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.cmb_Day.setObjectName("cmb_Day")
|
||||
self.layH_DateName.addWidget(self.cmb_Day)
|
||||
self.lbl_Month = QtWidgets.QLabel(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_Month.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_Month.setSizePolicy(sizePolicy)
|
||||
self.lbl_Month.setMinimumSize(QtCore.QSize(50, 20))
|
||||
self.lbl_Month.setObjectName("lbl_Month")
|
||||
self.layH_DateName.addWidget(self.lbl_Month)
|
||||
self.cmb_Month = QtWidgets.QComboBox(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.cmb_Month.sizePolicy().hasHeightForWidth())
|
||||
self.cmb_Month.setSizePolicy(sizePolicy)
|
||||
self.cmb_Month.setMinimumSize(QtCore.QSize(50, 20))
|
||||
self.cmb_Month.setMaximumSize(QtCore.QSize(16777215, 16777215))
|
||||
self.cmb_Month.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.cmb_Month.setObjectName("cmb_Month")
|
||||
self.layH_DateName.addWidget(self.cmb_Month)
|
||||
self.lbl_Year = QtWidgets.QLabel(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_Year.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_Year.setSizePolicy(sizePolicy)
|
||||
self.lbl_Year.setObjectName("lbl_Year")
|
||||
self.layH_DateName.addWidget(self.lbl_Year)
|
||||
self.cmb_Year = QtWidgets.QComboBox(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.cmb_Year.sizePolicy().hasHeightForWidth())
|
||||
self.cmb_Year.setSizePolicy(sizePolicy)
|
||||
self.cmb_Year.setMinimumSize(QtCore.QSize(80, 20))
|
||||
self.cmb_Year.setMaximumSize(QtCore.QSize(80, 16777215))
|
||||
self.cmb_Year.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.cmb_Year.setObjectName("cmb_Year")
|
||||
self.layH_DateName.addWidget(self.cmb_Year)
|
||||
self.lbl_Name_2 = QtWidgets.QLabel(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_Name_2.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_Name_2.setSizePolicy(sizePolicy)
|
||||
self.lbl_Name_2.setMinimumSize(QtCore.QSize(0, 20))
|
||||
self.lbl_Name_2.setObjectName("lbl_Name_2")
|
||||
self.layH_DateName.addWidget(self.lbl_Name_2)
|
||||
self.txt_Name = QtWidgets.QPlainTextEdit(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.txt_Name.sizePolicy().hasHeightForWidth())
|
||||
self.txt_Name.setSizePolicy(sizePolicy)
|
||||
self.txt_Name.setMinimumSize(QtCore.QSize(200, 20))
|
||||
self.txt_Name.setMaximumSize(QtCore.QSize(16777215, 30))
|
||||
self.txt_Name.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhNoAutoUppercase)
|
||||
self.txt_Name.setObjectName("txt_Name")
|
||||
self.layH_DateName.addWidget(self.txt_Name)
|
||||
self.verticalLayout.addLayout(self.layH_DateName)
|
||||
self.lay_Properties = QtWidgets.QHBoxLayout()
|
||||
self.lay_Properties.setObjectName("lay_Properties")
|
||||
self.lbl_Name = QtWidgets.QLabel(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_Name.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_Name.setSizePolicy(sizePolicy)
|
||||
self.lbl_Name.setMinimumSize(QtCore.QSize(0, 20))
|
||||
self.lbl_Name.setObjectName("lbl_Name")
|
||||
self.lay_Properties.addWidget(self.lbl_Name)
|
||||
self.txt_FullName = QtWidgets.QLineEdit(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.txt_FullName.sizePolicy().hasHeightForWidth())
|
||||
self.txt_FullName.setSizePolicy(sizePolicy)
|
||||
self.txt_FullName.setMinimumSize(QtCore.QSize(200, 30))
|
||||
self.txt_FullName.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.txt_FullName.setReadOnly(True)
|
||||
self.txt_FullName.setObjectName("txt_FullName")
|
||||
self.lay_Properties.addWidget(self.txt_FullName)
|
||||
self.verticalLayout.addLayout(self.lay_Properties)
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.tree_OUT = QtWidgets.QTreeWidget(parent=self.wdg_Main)
|
||||
self.tree_OUT.setMinimumSize(QtCore.QSize(0, 20))
|
||||
self.tree_OUT.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
|
||||
self.tree_OUT.setColumnCount(2)
|
||||
self.tree_OUT.setObjectName("tree_OUT")
|
||||
self.tree_OUT.headerItem().setText(0, "1")
|
||||
self.tree_OUT.headerItem().setText(1, "2")
|
||||
self.verticalLayout_2.addWidget(self.tree_OUT)
|
||||
self.verticalLayout.addLayout(self.verticalLayout_2)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.grp_Type = QtWidgets.QGroupBox(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.grp_Type.sizePolicy().hasHeightForWidth())
|
||||
self.grp_Type.setSizePolicy(sizePolicy)
|
||||
self.grp_Type.setMinimumSize(QtCore.QSize(200, 30))
|
||||
self.grp_Type.setMaximumSize(QtCore.QSize(200, 16777215))
|
||||
self.grp_Type.setTitle("")
|
||||
self.grp_Type.setObjectName("grp_Type")
|
||||
self.rbtn_Movies = QtWidgets.QRadioButton(parent=self.grp_Type)
|
||||
self.rbtn_Movies.setGeometry(QtCore.QRect(90, 0, 101, 31))
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.rbtn_Movies.sizePolicy().hasHeightForWidth())
|
||||
self.rbtn_Movies.setSizePolicy(sizePolicy)
|
||||
self.rbtn_Movies.setMinimumSize(QtCore.QSize(50, 20))
|
||||
self.rbtn_Movies.setChecked(True)
|
||||
self.rbtn_Movies.setObjectName("rbtn_Movies")
|
||||
self.rbtn_Photo = QtWidgets.QRadioButton(parent=self.grp_Type)
|
||||
self.rbtn_Photo.setGeometry(QtCore.QRect(0, 0, 78, 31))
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.rbtn_Photo.sizePolicy().hasHeightForWidth())
|
||||
self.rbtn_Photo.setSizePolicy(sizePolicy)
|
||||
self.rbtn_Photo.setMinimumSize(QtCore.QSize(25, 0))
|
||||
self.rbtn_Photo.setChecked(False)
|
||||
self.rbtn_Photo.setObjectName("rbtn_Photo")
|
||||
self.horizontalLayout.addWidget(self.grp_Type, 0, QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
self.chk_IgnMeta = QtWidgets.QCheckBox(parent=self.wdg_Main)
|
||||
self.chk_IgnMeta.setMinimumSize(QtCore.QSize(150, 20))
|
||||
self.chk_IgnMeta.setMaximumSize(QtCore.QSize(150, 20))
|
||||
self.chk_IgnMeta.setObjectName("chk_IgnMeta")
|
||||
self.horizontalLayout.addWidget(self.chk_IgnMeta)
|
||||
self.chk_Preview = QtWidgets.QCheckBox(parent=self.wdg_Main)
|
||||
self.chk_Preview.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.chk_Preview.sizePolicy().hasHeightForWidth())
|
||||
self.chk_Preview.setSizePolicy(sizePolicy)
|
||||
self.chk_Preview.setMinimumSize(QtCore.QSize(150, 20))
|
||||
self.chk_Preview.setObjectName("chk_Preview")
|
||||
self.horizontalLayout.addWidget(self.chk_Preview)
|
||||
self.btn_Open = QtWidgets.QPushButton(parent=self.wdg_Main)
|
||||
self.btn_Open.setMinimumSize(QtCore.QSize(100, 30))
|
||||
self.btn_Open.setObjectName("btn_Open")
|
||||
self.horizontalLayout.addWidget(self.btn_Open)
|
||||
self.btn_Renumber = QtWidgets.QPushButton(parent=self.wdg_Main)
|
||||
self.btn_Renumber.setMinimumSize(QtCore.QSize(100, 0))
|
||||
self.btn_Renumber.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus)
|
||||
self.btn_Renumber.setObjectName("btn_Renumber")
|
||||
self.horizontalLayout.addWidget(self.btn_Renumber)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
self.lay_ErrExit = QtWidgets.QHBoxLayout()
|
||||
self.lay_ErrExit.setObjectName("lay_ErrExit")
|
||||
self.pb_Run = QtWidgets.QProgressBar(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.pb_Run.sizePolicy().hasHeightForWidth())
|
||||
self.pb_Run.setSizePolicy(sizePolicy)
|
||||
self.pb_Run.setMinimumSize(QtCore.QSize(600, 0))
|
||||
self.pb_Run.setMaximumSize(QtCore.QSize(900, 16777215))
|
||||
self.pb_Run.setProperty("value", 0)
|
||||
self.pb_Run.setObjectName("pb_Run")
|
||||
self.lay_ErrExit.addWidget(self.pb_Run)
|
||||
self.btn_Exit = QtWidgets.QPushButton(parent=self.wdg_Main)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.btn_Exit.sizePolicy().hasHeightForWidth())
|
||||
self.btn_Exit.setSizePolicy(sizePolicy)
|
||||
self.btn_Exit.setMinimumSize(QtCore.QSize(100, 30))
|
||||
self.btn_Exit.setMaximumSize(QtCore.QSize(100, 30))
|
||||
self.btn_Exit.setObjectName("btn_Exit")
|
||||
self.lay_ErrExit.addWidget(self.btn_Exit, 0, QtCore.Qt.AlignmentFlag.AlignRight)
|
||||
self.verticalLayout.addLayout(self.lay_ErrExit)
|
||||
MainWindow.setCentralWidget(self.wdg_Main)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "Photo and Video renumbering tool"))
|
||||
self.date_Day.setDisplayFormat(_translate("MainWindow", "dd/MM/yyyy"))
|
||||
self.lbl_day.setText(_translate("MainWindow", "Day"))
|
||||
self.lbl_Month.setText(_translate("MainWindow", "Month"))
|
||||
self.lbl_Year.setText(_translate("MainWindow", "Year"))
|
||||
self.lbl_Name_2.setText(_translate("MainWindow", "Name"))
|
||||
self.lbl_Name.setText(_translate("MainWindow", "New Name"))
|
||||
self.rbtn_Movies.setWhatsThis(_translate("MainWindow", "<html><head/><body><p>Show/Edit Networks for this L3Out</p></body></html>"))
|
||||
self.rbtn_Movies.setText(_translate("MainWindow", "Movies"))
|
||||
self.rbtn_Photo.setWhatsThis(_translate("MainWindow", "<html><head/><body><p>Show/Edit Logical Node Profiles</p></body></html>"))
|
||||
self.rbtn_Photo.setText(_translate("MainWindow", "Photos"))
|
||||
self.chk_IgnMeta.setText(_translate("MainWindow", "Ignore Metadata"))
|
||||
self.chk_Preview.setText(_translate("MainWindow", "Show Preview"))
|
||||
self.btn_Open.setText(_translate("MainWindow", "Open Folder"))
|
||||
self.btn_Renumber.setText(_translate("MainWindow", "Renumber Photo and Movies"))
|
||||
self.btn_Exit.setText(_translate("MainWindow", "Exit"))
|
||||
7
poetry.lock
generated
Normal file
7
poetry.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||
package = []
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "c595a0588c25d58f3e3834ad7169126836d262b925fe6ca9b5d540dcf301d254"
|
||||
15
pyproject.toml
Executable file
15
pyproject.toml
Executable file
@@ -0,0 +1,15 @@
|
||||
[tool.poetry]
|
||||
name = "pict-poetry-devel"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["tiziano"]
|
||||
readme = "README.md"
|
||||
packages = [{include = "pict_poetry_devel"}]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
50
requirements.txt
Executable file
50
requirements.txt
Executable file
@@ -0,0 +1,50 @@
|
||||
attrs==22.1.0
|
||||
CacheControl==0.12.11
|
||||
cachy==0.3.0
|
||||
certifi==2022.9.24
|
||||
cffi==1.15.1
|
||||
charset-normalizer==2.1.1
|
||||
cleo==1.0.0a5
|
||||
crashtest==0.3.1
|
||||
distlib==0.3.6
|
||||
dulwich==0.20.50
|
||||
ExifRead==3.0.0
|
||||
filelock==3.8.0
|
||||
html5lib==1.1
|
||||
idna==3.4
|
||||
importlib-metadata==4.13.0
|
||||
jaraco.classes==3.2.3
|
||||
jsonschema==4.17.0
|
||||
keyring==23.11.0
|
||||
lockfile==0.12.2
|
||||
more-itertools==9.0.0
|
||||
msgpack==1.0.4
|
||||
packaging==21.3
|
||||
pexpect==4.8.0
|
||||
Pillow==10.0.0
|
||||
pkginfo==1.8.3
|
||||
platformdirs==2.5.3
|
||||
poetry==1.2.2
|
||||
poetry-core==1.3.2
|
||||
poetry-plugin-export==1.2.0
|
||||
ptyprocess==0.7.0
|
||||
pycparser==2.21
|
||||
pyheif @ git+https://github.com/carsales/pyheif.git@2eaefe983acc01d52ca6b0094d986739cd7b32a5
|
||||
pylev==1.4.0
|
||||
pyparsing==3.0.9
|
||||
PyQt6==6.5.1
|
||||
PyQt6-Qt6==6.5.1
|
||||
PyQt6-sip==13.5.1
|
||||
pyrsistent==0.19.2
|
||||
requests==2.28.1
|
||||
requests-toolbelt==0.9.1
|
||||
shellingham==1.5.0
|
||||
six==1.16.0
|
||||
support-developer==1.0.5
|
||||
tomlkit==0.11.6
|
||||
transpose-dict==1.1.3
|
||||
urllib3==1.26.12
|
||||
virtualenv==20.16.6
|
||||
webencodings==0.5.1
|
||||
xattr==0.9.9
|
||||
zipp==3.10.0
|
||||
93
tt.py
Executable file
93
tt.py
Executable file
@@ -0,0 +1,93 @@
|
||||
from transpose_dict import transpose_dict
|
||||
import os
|
||||
import io
|
||||
import time
|
||||
from datetime import datetime
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
# import metadata file objects
|
||||
from PIL import Image
|
||||
from PIL.ExifTags import TAGS
|
||||
import exifread
|
||||
import pyheif
|
||||
|
||||
EXIFTOOL="exiftool"
|
||||
|
||||
def get_pict_meta(exifdata):
|
||||
for tag_id in exifdata:
|
||||
# get the tag name, instead of human unreadable tag id
|
||||
tag = TAGS.get(tag_id, tag_id)
|
||||
if tag == "DateTime":
|
||||
data = exifdata.get(tag_id)
|
||||
# decode bytes
|
||||
if isinstance(data, bytes):
|
||||
data = data.decode()
|
||||
return(data)
|
||||
|
||||
def get_heic_meta(heif_file):
|
||||
for metadata in heif_file.metadata:
|
||||
file_stream = io.BytesIO(metadata['data'][6:])
|
||||
|
||||
tags = exifread.process_file(file_stream, details=False)
|
||||
|
||||
for k,v in tags.items():
|
||||
#print(f"TAG:{k}->{v}")
|
||||
if k == "Image DateTime":
|
||||
return(str(v))
|
||||
|
||||
|
||||
# this is the main
|
||||
extensions = (".jpg", ".JPG", ".heic", ".HEIC", ".mov", ".MOV")
|
||||
#extensions = (".jpg", ".JPG")
|
||||
fl = {}
|
||||
for file in os.listdir("./test/work"):
|
||||
if file.endswith(extensions):
|
||||
# if file.endswith(('.jpg','.JPG')):
|
||||
# image = Image.open(os.path.join("./test/work", file))
|
||||
# exifdata = image.getexif()
|
||||
# dt = (get_pict_meta(exifdata)).replace(' ','|')
|
||||
# #print(f"{file}-->{dt}<")
|
||||
#
|
||||
# elif file.endswith(('.heic','.HEIC')):
|
||||
# heif_file = pyheif.read_heif(os.path.join("./test/work", file))
|
||||
#
|
||||
# dt = (get_heic_meta(heif_file)).replace(' ','|')
|
||||
# #print(f"{file}++>{dt}<")
|
||||
# elif file.endswith(('.mov','.MOV')):
|
||||
# Open image file for reading (must be in binary mode)
|
||||
# f = open(os.path.join("./test/work", file) , 'rb')
|
||||
|
||||
dt="2023:08:14|12:00:00"
|
||||
# -createdate for .mov provides Zulu time while for other format timezone time
|
||||
# for .mov need to use -creationdate TAG
|
||||
if file.endswith(('.mov','.MOV')):
|
||||
tags = '-creationdate'
|
||||
else:
|
||||
tags = '-createdate'
|
||||
|
||||
process = subprocess.Popen([EXIFTOOL,os.path.join("./test/work", file), tags ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
for out in process.stdout:
|
||||
print(f"{out.decode()}")
|
||||
#if ((out.decode().split(':'))[0]).strip() == "Creation Date":
|
||||
line = ((out.decode().split(':'))[1:])
|
||||
print(f"{line} -- {os.path.join('./test/work', file)}")
|
||||
dt = f"{line[0]}:{line[1]}:{line[2].split(' ')[0]}|{line[2].split(' ')[1]}:{line[3]}:{(line[4].split('+'))[0]}".strip()
|
||||
print(f"{dt}")
|
||||
|
||||
fl.update({file : {
|
||||
int(time.mktime(time.strptime(dt, '%Y:%m:%d|%H:%M:%S'))) :
|
||||
{
|
||||
'date': dt.split('|')[0],
|
||||
'time': dt.split('|')[1]
|
||||
}
|
||||
}
|
||||
})
|
||||
# print unsorted dictionary
|
||||
#print(f"{json.dumps(fl, indent=4)}")
|
||||
|
||||
# transpose disctionary and sort it by date of creation of the file
|
||||
flt = dict(sorted(transpose_dict(fl,1).items()))
|
||||
|
||||
# print
|
||||
print(f"{json.dumps(flt, indent=4)}")
|
||||
Reference in New Issue
Block a user