mirror of
https://gitlab.os-k.eu/neox/CNIRevelator.git
synced 2023-08-25 14:03:10 +02:00
Working on image display
This commit is contained in:
parent
f75b1db8ea
commit
6687437247
@ -64,7 +64,10 @@ def main():
|
|||||||
if globs.CNIRNewVersion:
|
if globs.CNIRNewVersion:
|
||||||
showinfo('Changelog : résumé de mise à jour', ('Version du logiciel : CNIRevelator ' + globs.verstring_full + '\n\n' + globs.changelog), parent=mainw)
|
showinfo('Changelog : résumé de mise à jour', ('Version du logiciel : CNIRevelator ' + globs.verstring_full + '\n\n' + globs.changelog), parent=mainw)
|
||||||
logfile.printdbg('main() : **** Launching App_main() ****')
|
logfile.printdbg('main() : **** Launching App_main() ****')
|
||||||
mainw.mainloop()
|
try:
|
||||||
|
mainw.mainloop()
|
||||||
|
except Exception as e:
|
||||||
|
showerror("CNIRevelator Fatal Error", "An error has occured : {}".format(e), parent=mainw)
|
||||||
logfile.printdbg('main() : **** Ending App_main() ****')
|
logfile.printdbg('main() : **** Ending App_main() ****')
|
||||||
|
|
||||||
logfile.printdbg('*** CNIRevelator LOGFILE. Goodbye World ! ***')
|
logfile.printdbg('*** CNIRevelator LOGFILE. Goodbye World ! ***')
|
||||||
|
@ -31,7 +31,7 @@ verstring_full = "{}.{}.{} {}".format(version[0], version[1], version[2], v
|
|||||||
verstring = "{}.{}".format(version[0], version[1])
|
verstring = "{}.{}".format(version[0], version[1])
|
||||||
debug = True
|
debug = True
|
||||||
|
|
||||||
changelog = "Version 3.0.6 \nMise-à-jour mineure avec les corrections suivantes :\n- Changement de l'apparence du launcher de l'application\n\n" + \
|
changelog = "Version 3.0.6 \nMise-à-jour mineure avec les corrections suivantes :\n- Changement de l'apparence du launcher de l'application\n- Améliorations de l'interface, notamment de la stabilité\n- Ajout de la signature numérique de l'exécutable\n\n" + \
|
||||||
"Version 3.0.5 \nMise-à-jour mineure avec les corrections suivantes :\n- Changement de l'icône de l'exécutable afin de refléter le changement de version majeur accompli en 3.0\n\n" + \
|
"Version 3.0.5 \nMise-à-jour mineure avec les corrections suivantes :\n- Changement de l'icône de l'exécutable afin de refléter le changement de version majeur accompli en 3.0\n\n" + \
|
||||||
"Version 3.0.4 \nMise-à-jour mineure avec les corrections suivantes :\n- Correction d'un bug affectant le système de mise-à-jour\n\n" + \
|
"Version 3.0.4 \nMise-à-jour mineure avec les corrections suivantes :\n- Correction d'un bug affectant le système de mise-à-jour\n\n" + \
|
||||||
"Version 3.0.3 \nMise-à-jour mineure avec les corrections suivantes :\n- Correction d'un bug affectant le changelog\n- Correction d'une erreur avec la touche Suppr Arrière et Suppr causant une perte de données\n\n" + \
|
"Version 3.0.3 \nMise-à-jour mineure avec les corrections suivantes :\n- Correction d'un bug affectant le changelog\n- Correction d'une erreur avec la touche Suppr Arrière et Suppr causant une perte de données\n\n" + \
|
||||||
|
182
src/ihm.py
182
src/ihm.py
@ -27,9 +27,12 @@ from tkinter import *
|
|||||||
from tkinter.messagebox import *
|
from tkinter.messagebox import *
|
||||||
from tkinter import filedialog
|
from tkinter import filedialog
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
|
import cv2
|
||||||
|
import PIL.Image, PIL.ImageTk
|
||||||
|
|
||||||
import logger # logger.py
|
import logger # logger.py
|
||||||
import globs # globs.py
|
import globs # globs.py
|
||||||
|
import image # image.py
|
||||||
|
|
||||||
|
|
||||||
controlKeys = ["Escape", "Right", "Left", "Up", "Down", "Home", "End", "BackSpace", "Delete", "Inser", "Shift_L", "Shift_R", "Control_R", "Control_L"]
|
controlKeys = ["Escape", "Right", "Left", "Up", "Down", "Home", "End", "BackSpace", "Delete", "Inser", "Shift_L", "Shift_R", "Control_R", "Control_L"]
|
||||||
@ -121,9 +124,18 @@ class LauncherWindow(Tk):
|
|||||||
self.geometry('%dx%d+%d+%d' % (wwidth, wheight, x, y))
|
self.geometry('%dx%d+%d+%d' % (wwidth, wheight, x, y))
|
||||||
|
|
||||||
# Creating objects
|
# Creating objects
|
||||||
self.image = PhotoImage(file = "background.png")
|
# Load an image using OpenCV
|
||||||
|
cv_img = cv2.imread("background.png")
|
||||||
|
cv_img = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
|
||||||
|
cv_img = cv2.blur(cv_img, (15, 15))
|
||||||
|
# Get the image dimensions (OpenCV stores image data as NumPy ndarray)
|
||||||
|
height, width, no_channels = cv_img.shape
|
||||||
|
# Get the image dimensions (OpenCV stores image data as NumPy ndarray)
|
||||||
|
height, width, no_channels = cv_img.shape
|
||||||
|
# Use PIL (Pillow) to convert the NumPy ndarray to a PhotoImage
|
||||||
|
self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(cv_img))
|
||||||
self.mainCanvas = Canvas(self, width=wwidth, height=wheight, bg=globs.CNIRLColor, highlightthickness=0)
|
self.mainCanvas = Canvas(self, width=wwidth, height=wheight, bg=globs.CNIRLColor, highlightthickness=0)
|
||||||
self.mainCanvas.create_image(wwidth /2, wheight /2, image=self.image)
|
self.mainCanvas.create_image(wwidth /2, wheight /2, image=self.photo)
|
||||||
|
|
||||||
# Column
|
# Column
|
||||||
self.mainCanvas.grid_rowconfigure(0, weight=1, minsize=(wheight / 10 * 9))
|
self.mainCanvas.grid_rowconfigure(0, weight=1, minsize=(wheight / 10 * 9))
|
||||||
@ -158,6 +170,9 @@ class LauncherWindow(Tk):
|
|||||||
def printmsg(self, msg):
|
def printmsg(self, msg):
|
||||||
self.mainCanvas.itemconfigure(self.msg, text=(msg))
|
self.mainCanvas.itemconfigure(self.msg, text=(msg))
|
||||||
|
|
||||||
|
def exit(self):
|
||||||
|
self.after(1000, self.destroy)
|
||||||
|
|
||||||
class AutoScrollbar(ttk.Scrollbar):
|
class AutoScrollbar(ttk.Scrollbar):
|
||||||
|
|
||||||
def set(self, lo, hi):
|
def set(self, lo, hi):
|
||||||
@ -173,156 +188,21 @@ class AutoScrollbar(ttk.Scrollbar):
|
|||||||
def place(self, **kw):
|
def place(self, **kw):
|
||||||
raise TclError('Cannot use place with the widget ' + self.__class__.__name__)
|
raise TclError('Cannot use place with the widget ' + self.__class__.__name__)
|
||||||
|
|
||||||
class OpenPageDialog(Toplevel):
|
class ResizeableCanvas(Canvas):
|
||||||
|
def __init__(self,parent,**kwargs):
|
||||||
def __init__(self, parent, number):
|
Canvas.__init__(self,parent,**kwargs)
|
||||||
super().__init__(parent)
|
self.bind("<Configure>", self.on_resize)
|
||||||
self.parent = parent
|
self.height = self.winfo_reqheight()
|
||||||
self.title("Choisir la page à afficher de l'image selectionnée")
|
self.width = self.winfo_reqwidth()
|
||||||
self.resizable(width=False, height=False)
|
|
||||||
self.termtext = Label(self, text='Merci de selectionner un numéro de page dans la liste ci-dessous.')
|
|
||||||
self.termtext.grid(column=0, row=0, sticky='N', padx=5, pady=5)
|
|
||||||
self.combotry = ttk.Combobox(self)
|
|
||||||
self.combotry['values'] = tuple(str(x) for x in range(1, number + 1))
|
|
||||||
self.combotry.grid(column=0, row=1, sticky='N', padx=5, pady=5)
|
|
||||||
self.button = Button(self, text='Valider', command=(self.valid))
|
|
||||||
self.button.grid(column=0, row=2, sticky='S', padx=5, pady=5)
|
|
||||||
self.update()
|
|
||||||
hs = self.winfo_screenheight()
|
|
||||||
w = int(self.winfo_width())
|
|
||||||
h = int(self.winfo_height())
|
|
||||||
ws = self.winfo_screenwidth()
|
|
||||||
hs = self.winfo_screenheight()
|
|
||||||
x = ws / 2 - w / 2
|
|
||||||
y = hs / 2 - h / 2
|
|
||||||
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
|
||||||
if getattr(sys, 'frozen', False):
|
|
||||||
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
|
|
||||||
else:
|
|
||||||
self.iconbitmap('id-card.ico')
|
|
||||||
|
|
||||||
def valid(self):
|
|
||||||
self.parent.page = self.combotry.current()
|
|
||||||
self.destroy()
|
|
||||||
|
|
||||||
|
|
||||||
class OpenScanWin(Toplevel):
|
|
||||||
|
|
||||||
def __init__(self, parent, file, type, nframe=1):
|
|
||||||
super().__init__(parent)
|
|
||||||
self.parent = parent
|
|
||||||
app = OpenScan(self, file, type, nframe)
|
|
||||||
|
|
||||||
class OpenScan(ttk.Frame):
|
|
||||||
def __init__(self, mainframe, fileorig, type, nframe=1, pagenum=0, file=None):
|
|
||||||
""" Initialize the main Frame """
|
|
||||||
if file == None:
|
|
||||||
file = fileorig
|
|
||||||
self.file = file
|
|
||||||
self.fileorig = fileorig
|
|
||||||
self.nframe = nframe
|
|
||||||
self.pagenum = pagenum
|
|
||||||
self.parent = mainframe.parent
|
|
||||||
ttk.Frame.__init__(self, master=mainframe)
|
|
||||||
self.master.title('Ouvrir un scan... (Utilisez la roulette pour zoomer, clic gauche pour déplacer et clic droit pour sélectionner la MRZ)')
|
|
||||||
self.master.resizable(width=False, height=False)
|
|
||||||
hs = self.winfo_screenheight()
|
|
||||||
w = int(self.winfo_screenheight() / 1.5)
|
|
||||||
h = int(self.winfo_screenheight() / 2)
|
|
||||||
ws = self.winfo_screenwidth()
|
|
||||||
hs = self.winfo_screenheight()
|
|
||||||
x = ws / 2 - w / 2
|
|
||||||
y = hs / 2 - h / 2
|
|
||||||
self.master.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
|
||||||
if getattr(sys, 'frozen', False):
|
|
||||||
self.master.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
|
|
||||||
else:
|
|
||||||
self.master.iconbitmap('id-card.ico')
|
|
||||||
self.master.rowconfigure(0, weight=1)
|
|
||||||
self.master.columnconfigure(0, weight=1)
|
|
||||||
self.cadre = CanvasImage(self.master, self.file, type)
|
|
||||||
self.cadre.grid(row=0, column=0)
|
|
||||||
self.master.menubar = Menu(self.master)
|
|
||||||
if type == 1:
|
|
||||||
self.master.menubar.add_command(label='Page précédente', command=(self.pagep))
|
|
||||||
self.master.menubar.add_command(label='Pivoter -90°', command=(self.cadre.rotatemm))
|
|
||||||
self.master.menubar.add_command(label='Pivoter -1°', command=(self.cadre.rotatem))
|
|
||||||
self.master.menubar.add_command(label='Pivoter +1°', command=(self.cadre.rotatep))
|
|
||||||
self.master.menubar.add_command(label='Pivoter +90°', command=(self.cadre.rotatepp))
|
|
||||||
if type == 1:
|
|
||||||
self.master.menubar.add_command(label='Page suivante', command=(self.pages))
|
|
||||||
self.master.config(menu=(self.master.menubar))
|
|
||||||
self.cadre.canvas.bind('<ButtonPress-3>', self.motionprep)
|
|
||||||
self.cadre.canvas.bind('<B3-Motion>', self.motionize)
|
|
||||||
self.cadre.canvas.bind('<ButtonRelease-3>', self.motionend)
|
|
||||||
|
|
||||||
def pages(self):
|
|
||||||
if self.pagenum + 1 < self.nframe:
|
|
||||||
im = Image.open(self.fileorig)
|
|
||||||
im.seek(self.pagenum + 1)
|
|
||||||
newpath = globs.CNIREnv + '\\temp' + str(random.randint(11111, 99999)) + '.tif'
|
|
||||||
im.save(newpath)
|
|
||||||
im.close()
|
|
||||||
self.cadre.destroy()
|
|
||||||
self.__init__(self.master, self.fileorig, 1, self.nframe, self.pagenum + 1, newpath)
|
|
||||||
|
|
||||||
def pagep(self):
|
|
||||||
if self.pagenum - 1 >= 0:
|
|
||||||
im = Image.open(self.fileorig)
|
|
||||||
im.seek(self.pagenum - 1)
|
|
||||||
newpath = globs.CNIREnv + '\\temp' + str(random.randint(11111, 99999)) + '.tif'
|
|
||||||
im.save(newpath)
|
|
||||||
im.close()
|
|
||||||
self.cadre.destroy()
|
|
||||||
self.__init__(self.master, self.fileorig, 1, self.nframe, self.pagenum - 1, newpath)
|
|
||||||
|
|
||||||
def motionprep(self, event):
|
|
||||||
if hasattr(self, 'rect'):
|
|
||||||
self.begx = event.x
|
|
||||||
self.begy = event.y
|
|
||||||
self.ix = self.cadre.canvas.canvasx(event.x)
|
|
||||||
self.iy = self.cadre.canvas.canvasy(event.y)
|
|
||||||
self.cadre.canvas.coords(self.rect, self.cadre.canvas.canvasx(event.x), self.cadre.canvas.canvasy(event.y), self.ix, self.iy)
|
|
||||||
else:
|
|
||||||
self.begx = event.x
|
|
||||||
self.begy = event.y
|
|
||||||
self.ix = self.cadre.canvas.canvasx(event.x)
|
|
||||||
self.iy = self.cadre.canvas.canvasy(event.y)
|
|
||||||
self.rect = self.cadre.canvas.create_rectangle((self.cadre.canvas.canvasx(event.x)), (self.cadre.canvas.canvasy(event.y)), (self.ix), (self.iy), outline='red')
|
|
||||||
|
|
||||||
def motionize(self, event):
|
|
||||||
event.x
|
|
||||||
event.y
|
|
||||||
self.cadre.canvas.coords(self.rect, self.ix, self.iy, self.cadre.canvas.canvasx(event.x), self.cadre.canvas.canvasy(event.y))
|
|
||||||
|
|
||||||
def motionend(self, event):
|
|
||||||
self.endx = event.x
|
|
||||||
self.endy = event.y
|
|
||||||
self.imtotreat = self.cadre.resizedim.crop((min(self.begx, self.endx), min(self.begy, self.endy), max(self.endx, self.begx), max(self.endy, self.begy)))
|
|
||||||
im = self.imtotreat
|
|
||||||
import CNI_pytesseract as pytesseract
|
|
||||||
try:
|
|
||||||
os.environ['PATH'] = globs.CNIREnv + '\\Tesseract-OCR4\\'
|
|
||||||
os.environ['TESSDATA_PREFIX'] = globs.CNIREnv + '\\Tesseract-OCR4\\tessdata'
|
|
||||||
self.text = pytesseract.image_to_string(im, lang='ocrb', boxes=False, config='--psm 6 --oem 0 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890<')
|
|
||||||
except pytesseract.TesseractNotFoundError as e:
|
|
||||||
try:
|
|
||||||
os.remove(globs.CNIREnv + '\\Tesseract-OCR4\\*.*')
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
showerror('Erreur de module OCR', ('Le module OCR localisé en ' + str(os.environ['PATH']) + 'est introuvable. Il sera réinstallé à la prochaine exécution'), parent=self)
|
|
||||||
except pytesseract.TesseractError as e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.master.success = False
|
|
||||||
dialogconf = OpenScanDialog(self.master, self.text)
|
|
||||||
dialogconf.transient(self)
|
|
||||||
dialogconf.grab_set()
|
|
||||||
self.wait_window(dialogconf)
|
|
||||||
if self.master.success:
|
|
||||||
self.master.destroy()
|
|
||||||
|
|
||||||
|
def on_resize(self,event):
|
||||||
|
# determine the ratio of old width/height to new width/height
|
||||||
|
wscale = float(event.width)/self.width
|
||||||
|
hscale = float(event.height)/self.height
|
||||||
|
self.width = event.width
|
||||||
|
self.height = event.height
|
||||||
|
# rescale all the objects tagged with the "all" tag
|
||||||
|
self.scale("all",0,0,wscale,hscale)
|
||||||
|
|
||||||
## Global Handler
|
## Global Handler
|
||||||
launcherWindowCur = LauncherWindow()
|
launcherWindowCur = LauncherWindow()
|
||||||
|
250
src/image.py
250
src/image.py
@ -23,253 +23,3 @@
|
|||||||
********************************************************************************
|
********************************************************************************
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class CanvasImage:
|
|
||||||
|
|
||||||
def __init__(self, placeholder, file, type):
|
|
||||||
""" Initialize the ImageFrame """
|
|
||||||
self.type = type
|
|
||||||
self.angle = 0
|
|
||||||
self.imscale = 1.0
|
|
||||||
self._CanvasImage__delta = 1.3
|
|
||||||
self._CanvasImage__filter = Image.ANTIALIAS
|
|
||||||
self._CanvasImage__previous_state = 0
|
|
||||||
self.path = file
|
|
||||||
self._CanvasImage__imframe = ttk.Frame(placeholder)
|
|
||||||
self.placeholder = placeholder
|
|
||||||
hbar = AutoScrollbar((self._CanvasImage__imframe), orient='horizontal')
|
|
||||||
vbar = AutoScrollbar((self._CanvasImage__imframe), orient='vertical')
|
|
||||||
hbar.grid(row=1, column=0, sticky='we')
|
|
||||||
vbar.grid(row=0, column=1, sticky='ns')
|
|
||||||
self.canvas = Canvas((self._CanvasImage__imframe), highlightthickness=0, xscrollcommand=(hbar.set),
|
|
||||||
yscrollcommand=(vbar.set))
|
|
||||||
self.canvas.grid(row=0, column=0, sticky='nswe')
|
|
||||||
self.canvas.update()
|
|
||||||
hbar.configure(command=(self._CanvasImage__scroll_x))
|
|
||||||
vbar.configure(command=(self._CanvasImage__scroll_y))
|
|
||||||
self.canvas.bind('<Configure>', lambda event: self._CanvasImage__show_image())
|
|
||||||
self.canvas.bind('<ButtonPress-1>', self._CanvasImage__move_from)
|
|
||||||
self.canvas.bind('<B1-Motion>', self._CanvasImage__move_to)
|
|
||||||
self.canvas.bind('<MouseWheel>', self._CanvasImage__wheel)
|
|
||||||
self._CanvasImage__huge = False
|
|
||||||
self._CanvasImage__huge_size = 14000
|
|
||||||
self._CanvasImage__band_width = 1024
|
|
||||||
Image.MAX_IMAGE_PIXELS = 1000000000
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
warnings.simplefilter('ignore')
|
|
||||||
self._CanvasImage__image = Image.open(self.path)
|
|
||||||
self.imwidth, self.imheight = self._CanvasImage__image.size
|
|
||||||
if self.imwidth * self.imheight > self._CanvasImage__huge_size * self._CanvasImage__huge_size:
|
|
||||||
if self._CanvasImage__image.tile[0][0] == 'raw':
|
|
||||||
self._CanvasImage__huge = True
|
|
||||||
self._CanvasImage__offset = self._CanvasImage__image.tile[0][2]
|
|
||||||
self._CanvasImage__tile = [self._CanvasImage__image.tile[0][0],
|
|
||||||
[
|
|
||||||
0, 0, self.imwidth, 0],
|
|
||||||
self._CanvasImage__offset,
|
|
||||||
self._CanvasImage__image.tile[0][3]]
|
|
||||||
self._CanvasImage__min_side = min(self.imwidth, self.imheight)
|
|
||||||
self._CanvasImage__pyramid = [self.smaller()] if self._CanvasImage__huge else [Image.open(self.path)]
|
|
||||||
self._CanvasImage__ratio = max(self.imwidth, self.imheight) / self._CanvasImage__huge_size if self._CanvasImage__huge else 1.0
|
|
||||||
self._CanvasImage__curr_img = 0
|
|
||||||
self._CanvasImage__scale = self.imscale * self._CanvasImage__ratio
|
|
||||||
self._CanvasImage__reduction = 2
|
|
||||||
w, h = self._CanvasImage__pyramid[(-1)].size
|
|
||||||
while w > 512 and h > 512:
|
|
||||||
w /= self._CanvasImage__reduction
|
|
||||||
h /= self._CanvasImage__reduction
|
|
||||||
try:
|
|
||||||
self._CanvasImage__pyramid.append(self._CanvasImage__pyramid[(-1)].resize((int(w), int(h)), self._CanvasImage__filter))
|
|
||||||
except TypeError:
|
|
||||||
showerror(title='Erreur de fichier', message="Image incompatible. Merci d'utiliser une autre image ou de la convertir dans un format standard accepté!", parent=(self.placeholder))
|
|
||||||
self.placeholder.parent.openerrored = True
|
|
||||||
self.placeholder.destroy()
|
|
||||||
self.destroy()
|
|
||||||
return
|
|
||||||
|
|
||||||
self.container = self.canvas.create_rectangle((0, 0, self.imwidth, self.imheight), width=0)
|
|
||||||
self._CanvasImage__show_image()
|
|
||||||
self.canvas.focus_set()
|
|
||||||
|
|
||||||
def rotatem(self):
|
|
||||||
self.angle += 1
|
|
||||||
self._CanvasImage__show_image()
|
|
||||||
|
|
||||||
def rotatep(self):
|
|
||||||
self.angle -= 1
|
|
||||||
self._CanvasImage__show_image()
|
|
||||||
|
|
||||||
def rotatemm(self):
|
|
||||||
self.angle += 90
|
|
||||||
self._CanvasImage__show_image()
|
|
||||||
|
|
||||||
def rotatepp(self):
|
|
||||||
self.angle -= 90
|
|
||||||
self._CanvasImage__show_image()
|
|
||||||
|
|
||||||
def smaller(self):
|
|
||||||
""" Resize image proportionally and return smaller image """
|
|
||||||
w1, h1 = float(self.imwidth), float(self.imheight)
|
|
||||||
w2, h2 = float(self._CanvasImage__huge_size), float(self._CanvasImage__huge_size)
|
|
||||||
aspect_ratio1 = w1 / h1
|
|
||||||
aspect_ratio2 = w2 / h2
|
|
||||||
if aspect_ratio1 == aspect_ratio2:
|
|
||||||
image = Image.new('RGB', (int(w2), int(h2)))
|
|
||||||
k = h2 / h1
|
|
||||||
w = int(w2)
|
|
||||||
else:
|
|
||||||
if aspect_ratio1 > aspect_ratio2:
|
|
||||||
image = Image.new('RGB', (int(w2), int(w2 / aspect_ratio1)))
|
|
||||||
k = h2 / w1
|
|
||||||
w = int(w2)
|
|
||||||
else:
|
|
||||||
image = Image.new('RGB', (int(h2 * aspect_ratio1), int(h2)))
|
|
||||||
k = h2 / h1
|
|
||||||
w = int(h2 * aspect_ratio1)
|
|
||||||
i, j, n = 0, 1, round(0.5 + self.imheight / self._CanvasImage__band_width)
|
|
||||||
while i < self.imheight:
|
|
||||||
band = min(self._CanvasImage__band_width, self.imheight - i)
|
|
||||||
self._CanvasImage__tile[1][3] = band
|
|
||||||
self._CanvasImage__tile[2] = self._CanvasImage__offset + self.imwidth * i * 3
|
|
||||||
self._CanvasImage__image.close()
|
|
||||||
self._CanvasImage__image = Image.open(self.path)
|
|
||||||
self._CanvasImage__image.size = (self.imwidth, band)
|
|
||||||
self._CanvasImage__image.tile = [self._CanvasImage__tile]
|
|
||||||
cropped = self._CanvasImage__image.crop((0, 0, self.imwidth, band))
|
|
||||||
image.paste(cropped.resize((w, int(band * k) + 1), self._CanvasImage__filter), (0, int(i * k)))
|
|
||||||
i += band
|
|
||||||
j += 1
|
|
||||||
|
|
||||||
return image
|
|
||||||
|
|
||||||
def redraw_figures(self):
|
|
||||||
""" Dummy function to redraw figures in the children classes """
|
|
||||||
pass
|
|
||||||
|
|
||||||
def grid(self, **kw):
|
|
||||||
""" Put CanvasImage widget on the parent widget """
|
|
||||||
(self._CanvasImage__imframe.grid)(**kw)
|
|
||||||
self._CanvasImage__imframe.grid(sticky='nswe')
|
|
||||||
self._CanvasImage__imframe.rowconfigure(0, weight=1)
|
|
||||||
self._CanvasImage__imframe.columnconfigure(0, weight=1)
|
|
||||||
|
|
||||||
def __scroll_x(self, *args, **kwargs):
|
|
||||||
""" Scroll canvas horizontally and redraw the image """
|
|
||||||
(self.canvas.xview)(*args)
|
|
||||||
self._CanvasImage__show_image()
|
|
||||||
|
|
||||||
def __scroll_y(self, *args, **kwargs):
|
|
||||||
""" Scroll canvas vertically and redraw the image """
|
|
||||||
(self.canvas.yview)(*args)
|
|
||||||
self._CanvasImage__show_image()
|
|
||||||
|
|
||||||
def __show_image(self):
|
|
||||||
""" Show image on the Canvas. Implements correct image zoom almost like in Google Maps """
|
|
||||||
box_image = self.canvas.coords(self.container)
|
|
||||||
box_canvas = (self.canvas.canvasx(0),
|
|
||||||
self.canvas.canvasy(0),
|
|
||||||
self.canvas.canvasx(self.canvas.winfo_width()),
|
|
||||||
self.canvas.canvasy(self.canvas.winfo_height()))
|
|
||||||
box_img_int = tuple(map(int, box_image))
|
|
||||||
box_scroll = [
|
|
||||||
min(box_img_int[0], box_canvas[0]), min(box_img_int[1], box_canvas[1]),
|
|
||||||
max(box_img_int[2], box_canvas[2]), max(box_img_int[3], box_canvas[3])]
|
|
||||||
if box_scroll[0] == box_canvas[0]:
|
|
||||||
if box_scroll[2] == box_canvas[2]:
|
|
||||||
box_scroll[0] = box_img_int[0]
|
|
||||||
box_scroll[2] = box_img_int[2]
|
|
||||||
if box_scroll[1] == box_canvas[1] and box_scroll[3] == box_canvas[3]:
|
|
||||||
box_scroll[1] = box_img_int[1]
|
|
||||||
box_scroll[3] = box_img_int[3]
|
|
||||||
self.canvas.configure(scrollregion=(tuple(map(int, box_scroll))))
|
|
||||||
x1 = max(box_canvas[0] - box_image[0], 0)
|
|
||||||
y1 = max(box_canvas[1] - box_image[1], 0)
|
|
||||||
x2 = min(box_canvas[2], box_image[2]) - box_image[0]
|
|
||||||
y2 = min(box_canvas[3], box_image[3]) - box_image[1]
|
|
||||||
if int(x2 - x1) > 0:
|
|
||||||
if int(y2 - y1) > 0:
|
|
||||||
if self._CanvasImage__huge:
|
|
||||||
if self._CanvasImage__curr_img < 0:
|
|
||||||
h = int((y2 - y1) / self.imscale)
|
|
||||||
self._CanvasImage__tile[1][3] = h
|
|
||||||
self._CanvasImage__tile[2] = self._CanvasImage__offset + self.imwidth * int(y1 / self.imscale) * 3
|
|
||||||
self._CanvasImage__image.close()
|
|
||||||
self._CanvasImage__image = Image.open(self.path)
|
|
||||||
self._CanvasImage__image.size = (self.imwidth, h)
|
|
||||||
self._CanvasImage__image.tile = [self._CanvasImage__tile]
|
|
||||||
image = self._CanvasImage__image.crop((int(x1 / self.imscale), 0, int(x2 / self.imscale), h))
|
|
||||||
image = self._CanvasImage__pyramid[max(0, self._CanvasImage__curr_img)].crop((
|
|
||||||
int(x1 / self._CanvasImage__scale), int(y1 / self._CanvasImage__scale),
|
|
||||||
int(x2 / self._CanvasImage__scale), int(y2 / self._CanvasImage__scale)))
|
|
||||||
self.resizedim = image.resize((int(x2 - x1), int(y2 - y1)), self._CanvasImage__filter).rotate((self.angle), resample=(Image.BICUBIC), expand=1)
|
|
||||||
imagetk = ImageTk.PhotoImage((self.resizedim), master=(self.placeholder))
|
|
||||||
imageid = self.canvas.create_image((max(box_canvas[0], box_img_int[0])), (max(box_canvas[1], box_img_int[1])),
|
|
||||||
anchor='nw',
|
|
||||||
image=imagetk)
|
|
||||||
self.canvas.lower(imageid)
|
|
||||||
self.canvas.imagetk = imagetk
|
|
||||||
|
|
||||||
def __move_from(self, event):
|
|
||||||
""" Remember previous coordinates for scrolling with the mouse """
|
|
||||||
self.canvas.scan_mark(event.x, event.y)
|
|
||||||
|
|
||||||
def __move_to(self, event):
|
|
||||||
""" Drag (move) canvas to the new position """
|
|
||||||
self.canvas.scan_dragto((event.x), (event.y), gain=1)
|
|
||||||
self._CanvasImage__show_image()
|
|
||||||
|
|
||||||
def outside(self, x, y):
|
|
||||||
""" Checks if the point (x,y) is outside the image area """
|
|
||||||
bbox = self.canvas.coords(self.container)
|
|
||||||
if bbox[0] < x < bbox[2]:
|
|
||||||
if bbox[1] < y < bbox[3]:
|
|
||||||
pass
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __wheel(self, event):
|
|
||||||
""" Zoom with mouse wheel """
|
|
||||||
x = self.canvas.canvasx(event.x)
|
|
||||||
y = self.canvas.canvasy(event.y)
|
|
||||||
if self.outside(x, y):
|
|
||||||
return
|
|
||||||
scale = 1.0
|
|
||||||
if event.delta == -120:
|
|
||||||
if round(self._CanvasImage__min_side * self.imscale) < int(self.placeholder.winfo_screenheight()):
|
|
||||||
return
|
|
||||||
self.imscale /= self._CanvasImage__delta
|
|
||||||
scale /= self._CanvasImage__delta
|
|
||||||
if event.delta == 120:
|
|
||||||
i = min(self.canvas.winfo_width(), self.canvas.winfo_height()) >> 1
|
|
||||||
if i < self.imscale:
|
|
||||||
return
|
|
||||||
self.imscale *= self._CanvasImage__delta
|
|
||||||
scale *= self._CanvasImage__delta
|
|
||||||
k = self.imscale * self._CanvasImage__ratio
|
|
||||||
self._CanvasImage__curr_img = min(-1 * int(math.log(k, self._CanvasImage__reduction)), len(self._CanvasImage__pyramid) - 1)
|
|
||||||
self._CanvasImage__scale = k * math.pow(self._CanvasImage__reduction, max(0, self._CanvasImage__curr_img))
|
|
||||||
self.canvas.scale('all', x, y, scale, scale)
|
|
||||||
self.redraw_figures()
|
|
||||||
self._CanvasImage__show_image()
|
|
||||||
|
|
||||||
def crop(self, bbox):
|
|
||||||
""" Crop rectangle from the image and return it """
|
|
||||||
if self._CanvasImage__huge:
|
|
||||||
band = bbox[3] - bbox[1]
|
|
||||||
self._CanvasImage__tile[1][3] = band
|
|
||||||
self._CanvasImage__tile[2] = self._CanvasImage__offset + self.imwidth * bbox[1] * 3
|
|
||||||
self._CanvasImage__image.close()
|
|
||||||
self._CanvasImage__image = Image.open(self.path)
|
|
||||||
self._CanvasImage__image.size = (self.imwidth, band)
|
|
||||||
self._CanvasImage__image.tile = [self._CanvasImage__tile]
|
|
||||||
return self._CanvasImage__image.crop((bbox[0], 0, bbox[2], band))
|
|
||||||
else:
|
|
||||||
return self._CanvasImage__pyramid[0].crop(bbox)
|
|
||||||
|
|
||||||
def destroy(self):
|
|
||||||
""" ImageFrame destructor """
|
|
||||||
self._CanvasImage__image.close()
|
|
||||||
map(lambda i: i.close, self._CanvasImage__pyramid)
|
|
||||||
del self._CanvasImage__pyramid[:]
|
|
||||||
del self._CanvasImage__pyramid
|
|
||||||
self.canvas.destroy()
|
|
66
src/main.py
66
src/main.py
@ -32,6 +32,9 @@ from tkinter import ttk
|
|||||||
import threading
|
import threading
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import re
|
import re
|
||||||
|
import traceback
|
||||||
|
import cv2
|
||||||
|
import PIL.Image, PIL.ImageTk
|
||||||
|
|
||||||
import ihm # ihm.py
|
import ihm # ihm.py
|
||||||
import logger # logger.py
|
import logger # logger.py
|
||||||
@ -143,13 +146,18 @@ class mainWindow(Tk):
|
|||||||
"INDIC" : self.indic,
|
"INDIC" : self.indic,
|
||||||
}
|
}
|
||||||
|
|
||||||
# The STATUS indicator
|
# The STATUS indicator + image display
|
||||||
self.STATUT = ttk.Labelframe(self, text='Statut')
|
self.STATUT = ttk.Labelframe(self, text='Affichage de documents et statut')
|
||||||
self.STATUT.grid_columnconfigure(0, weight=1)
|
self.STATUT.grid_columnconfigure(0, weight=1)
|
||||||
self.STATUT.grid_rowconfigure(0, weight=1)
|
self.STATUT.grid_rowconfigure(0, weight=1)
|
||||||
self.STATUStxt = Label((self.STATUT), text='', font='Times 24', fg='#FFBF00')
|
self.STATUT.frame = Frame(self.STATUT)
|
||||||
self.STATUStxt.grid(column=0, row=0, padx=0, pady=0, sticky='EWNS')
|
self.STATUT.frame.grid(column=0, row=0, sticky='NSEW')
|
||||||
self.STATUStxt['text'] = 'EN ATTENTE'
|
self.STATUT.frame.grid_columnconfigure(0, weight=1)
|
||||||
|
self.STATUT.frame.grid_rowconfigure(0, weight=1)
|
||||||
|
self.STATUT.ZONE = ihm.ResizeableCanvas(self.STATUT.frame, bg=self["background"])
|
||||||
|
self.STATUT.ZONE.pack(fill="both", expand=True)
|
||||||
|
self.STATUSimg = self.STATUT.ZONE.create_image(0,0, image=None)
|
||||||
|
self.STATUStxt = self.STATUT.ZONE.create_text(0,0, text='', font='Times 24', fill='#FFBF00')
|
||||||
|
|
||||||
# The terminal to enter the MRZ
|
# The terminal to enter the MRZ
|
||||||
self.terminal = ttk.Labelframe(self, text='Terminal de saisie de MRZ complète')
|
self.terminal = ttk.Labelframe(self, text='Terminal de saisie de MRZ complète')
|
||||||
@ -229,7 +237,6 @@ class mainWindow(Tk):
|
|||||||
|
|
||||||
# Make this window resizable and set her size
|
# Make this window resizable and set her size
|
||||||
self.resizable(width=True, height=True)
|
self.resizable(width=True, height=True)
|
||||||
self.minsize(self.winfo_width(), self.winfo_height())
|
|
||||||
self.update()
|
self.update()
|
||||||
w = int(self.winfo_reqwidth())
|
w = int(self.winfo_reqwidth())
|
||||||
h = int(self.winfo_reqheight())
|
h = int(self.winfo_reqheight())
|
||||||
@ -240,6 +247,20 @@ class mainWindow(Tk):
|
|||||||
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
||||||
self.update()
|
self.update()
|
||||||
self.deiconify()
|
self.deiconify()
|
||||||
|
self.minsize(self.winfo_width(), self.winfo_height())
|
||||||
|
|
||||||
|
# Load an image using OpenCV
|
||||||
|
cv_img = cv2.imread("background.png")
|
||||||
|
cv_img = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
|
||||||
|
cv_img = cv2.blur(cv_img, (15, 15))
|
||||||
|
# Get the image dimensions (OpenCV stores image data as NumPy ndarray)
|
||||||
|
height, width = cv_img.shape
|
||||||
|
# Get the image dimensions (OpenCV stores image data as NumPy ndarray)
|
||||||
|
height, width = cv_img.shape
|
||||||
|
# Use PIL (Pillow) to convert the NumPy ndarray to a PhotoImage
|
||||||
|
photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(cv_img))
|
||||||
|
self.statusUpdate("EN ATTENTE", "#FFBF00", photo, setplace=True)
|
||||||
|
|
||||||
|
|
||||||
# Some bindings
|
# Some bindings
|
||||||
self.termtext.bind('<Key>', self.entryValidation)
|
self.termtext.bind('<Key>', self.entryValidation)
|
||||||
@ -247,6 +268,17 @@ class mainWindow(Tk):
|
|||||||
self.speed731text.bind('<Control_R>', self.speedValidation)
|
self.speed731text.bind('<Control_R>', self.speedValidation)
|
||||||
logfile.printdbg('Initialization successful')
|
logfile.printdbg('Initialization successful')
|
||||||
|
|
||||||
|
def statusUpdate(self, msg, color, image=None, setplace=False):
|
||||||
|
if image:
|
||||||
|
self.STATUT.image = image
|
||||||
|
|
||||||
|
self.STATUT.ZONE.itemconfigure(self.STATUSimg, image=(self.STATUT.image))
|
||||||
|
self.STATUT.ZONE.itemconfigure(self.STATUStxt, text=(msg), fill=color)
|
||||||
|
|
||||||
|
if setplace:
|
||||||
|
self.STATUT.ZONE.move(self.STATUSimg, self.STATUT.ZONE.winfo_reqwidth() / 2, self.STATUT.ZONE.winfo_reqheight() / 2)
|
||||||
|
self.STATUT.ZONE.move(self.STATUStxt, self.STATUT.ZONE.winfo_reqwidth() / 2, self.STATUT.ZONE.winfo_reqheight() / 2)
|
||||||
|
|
||||||
def stringValidation(self, keysym):
|
def stringValidation(self, keysym):
|
||||||
# analysis
|
# analysis
|
||||||
# If we must decide the type of the document
|
# If we must decide the type of the document
|
||||||
@ -429,8 +461,13 @@ class mainWindow(Tk):
|
|||||||
|
|
||||||
|
|
||||||
def openingScan(self):
|
def openingScan(self):
|
||||||
pass
|
path = ''
|
||||||
# OPEN A SCAN
|
path = filedialog.askopenfilename(parent=self, title='Ouvrir un scan de CNI...', filetypes=(('TIF files', '*.tif'),
|
||||||
|
('TIF files', '*.tiff'),
|
||||||
|
('JPEG files', '*.jpg'),
|
||||||
|
('JPEG files', '*.jpeg')))
|
||||||
|
self.mrzdetected = ''
|
||||||
|
self.mrzdict = {}
|
||||||
|
|
||||||
def newEntry(self):
|
def newEntry(self):
|
||||||
self.initialize()
|
self.initialize()
|
||||||
@ -524,13 +561,6 @@ class mainWindow(Tk):
|
|||||||
self.termtext.tag_configure("nonconforme", background="red", relief='raised', foreground="white")
|
self.termtext.tag_configure("nonconforme", background="red", relief='raised', foreground="white")
|
||||||
self.compliance = False
|
self.compliance = False
|
||||||
|
|
||||||
if self.compliance == True:
|
|
||||||
self.STATUStxt['text'] = 'CONFORME'
|
|
||||||
self.STATUStxt['fg'] = "green"
|
|
||||||
else:
|
|
||||||
self.STATUStxt['text'] = 'NON CONFORME'
|
|
||||||
self.STATUStxt['fg'] = "red"
|
|
||||||
|
|
||||||
# get the infos
|
# get the infos
|
||||||
docInfos = mrz.getDocInfos(self.mrzDecided, code)
|
docInfos = mrz.getDocInfos(self.mrzDecided, code)
|
||||||
#print(docInfos)
|
#print(docInfos)
|
||||||
@ -547,6 +577,12 @@ class mainWindow(Tk):
|
|||||||
self.infoList[key]['background'] = "red"
|
self.infoList[key]['background'] = "red"
|
||||||
self.infoList[key]['foreground'] = "white"
|
self.infoList[key]['foreground'] = "white"
|
||||||
self.infoList[key]['text'] = "NC"
|
self.infoList[key]['text'] = "NC"
|
||||||
|
self.compliance = False
|
||||||
|
|
||||||
|
if self.compliance == True:
|
||||||
|
self.statusUpdate("CONFORME", "chartreuse2")
|
||||||
|
else:
|
||||||
|
self.statusUpdate("NON-CONFORME","red")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -989,7 +989,7 @@ def getDocInfos(doc, code):
|
|||||||
try:
|
try:
|
||||||
datetime.datetime.strptime(value,"%d/%m/%y")
|
datetime.datetime.strptime(value,"%d/%m/%y")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print(value)
|
#print(value)
|
||||||
if value != "":
|
if value != "":
|
||||||
res[field[0]] = False
|
res[field[0]] = False
|
||||||
else:
|
else:
|
||||||
|
@ -42,6 +42,8 @@ import downloader # downloader.py
|
|||||||
UPDATE_IS_MADE = False
|
UPDATE_IS_MADE = False
|
||||||
UPATH = ' '
|
UPATH = ' '
|
||||||
|
|
||||||
|
launcherWindow = ihm.launcherWindowCur
|
||||||
|
|
||||||
def createShortcut(path, target='', wDir='', icon=''):
|
def createShortcut(path, target='', wDir='', icon=''):
|
||||||
"""
|
"""
|
||||||
Creates a shortcut for a program or an internet link
|
Creates a shortcut for a program or an internet link
|
||||||
@ -127,7 +129,7 @@ def getLatestVersion(credentials):
|
|||||||
|
|
||||||
# Global Handlers
|
# Global Handlers
|
||||||
logfile = logger.logCur
|
logfile = logger.logCur
|
||||||
launcherWindow = ihm.launcherWindowCur
|
|
||||||
|
|
||||||
# First retrieving the urls !
|
# First retrieving the urls !
|
||||||
while True:
|
while True:
|
||||||
@ -194,7 +196,7 @@ def getLatestVersion(credentials):
|
|||||||
def tessInstall(PATH, credentials):
|
def tessInstall(PATH, credentials):
|
||||||
# Global Handlers
|
# Global Handlers
|
||||||
logfile = logger.logCur
|
logfile = logger.logCur
|
||||||
launcherWindow = ihm.launcherWindowCur
|
|
||||||
|
|
||||||
# Verifying that Tesseract is installed
|
# Verifying that Tesseract is installed
|
||||||
if not os.path.exists(PATH + '\\Tesseract-OCR4\\'):
|
if not os.path.exists(PATH + '\\Tesseract-OCR4\\'):
|
||||||
@ -222,7 +224,7 @@ def tessInstall(PATH, credentials):
|
|||||||
def batch(credentials):
|
def batch(credentials):
|
||||||
# Global Handlers
|
# Global Handlers
|
||||||
logfile = logger.logCur
|
logfile = logger.logCur
|
||||||
launcherWindow = ihm.launcherWindowCur
|
|
||||||
|
|
||||||
# Get the latest version of CNIRevelator
|
# Get the latest version of CNIRevelator
|
||||||
finalver, finalurl, finalchecksum = getLatestVersion(credentials)
|
finalver, finalurl, finalchecksum = getLatestVersion(credentials)
|
||||||
@ -300,7 +302,7 @@ def umain():
|
|||||||
|
|
||||||
# Global Handlers
|
# Global Handlers
|
||||||
logfile = logger.logCur
|
logfile = logger.logCur
|
||||||
launcherWindow = ihm.launcherWindowCur
|
|
||||||
|
|
||||||
credentials = downloader.newcredentials()
|
credentials = downloader.newcredentials()
|
||||||
|
|
||||||
@ -308,8 +310,7 @@ def umain():
|
|||||||
logfile.printerr("Credentials Error. No effective update !")
|
logfile.printerr("Credentials Error. No effective update !")
|
||||||
launcherWindow.printmsg('Credentials Error. No effective update !')
|
launcherWindow.printmsg('Credentials Error. No effective update !')
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
launcherWindow = ihm.launcherWindowCur
|
launcherWindow.exit()
|
||||||
launcherWindow.destroy()
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# Cleaner for the old version if detected
|
# Cleaner for the old version if detected
|
||||||
@ -376,7 +377,7 @@ def umain():
|
|||||||
logfile.printerr("An error occured on the thread : " + str(traceback.format_exc()))
|
logfile.printerr("An error occured on the thread : " + str(traceback.format_exc()))
|
||||||
launcherWindow.printmsg('ERROR : ' + str(e))
|
launcherWindow.printmsg('ERROR : ' + str(e))
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
launcherWindow.destroy()
|
launcherWindow.exit()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
@ -386,14 +387,12 @@ def umain():
|
|||||||
logfile.printerr("An error occured. No effective update !")
|
logfile.printerr("An error occured. No effective update !")
|
||||||
launcherWindow.printmsg('An error occured. No effective update !')
|
launcherWindow.printmsg('An error occured. No effective update !')
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
launcherWindow = ihm.launcherWindowCur
|
launcherWindow.exit()
|
||||||
launcherWindow.destroy()
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
except:
|
except:
|
||||||
logfile.printerr("A FATAL ERROR OCCURED : " + str(traceback.format_exc()))
|
logfile.printerr("A FATAL ERROR OCCURED : " + str(traceback.format_exc()))
|
||||||
launcherWindow = ihm.launcherWindowCur
|
launcherWindow.exit()
|
||||||
launcherWindow.destroy()
|
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user