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
b77283a3ae
commit
d93b4fe893
@ -6,6 +6,7 @@
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
@ -63,7 +64,10 @@ def main():
|
||||
if globs.CNIRNewVersion:
|
||||
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() ****')
|
||||
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('*** CNIRevelator LOGFILE. Goodbye World ! ***')
|
||||
|
BIN
src/background.png
Normal file
BIN
src/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
@ -26,12 +26,13 @@ import os
|
||||
|
||||
# CNIRevelator version
|
||||
verType = "alpha"
|
||||
version = [3, 0, 5]
|
||||
version = [3, 0, 6]
|
||||
verstring_full = "{}.{}.{} {}".format(version[0], version[1], version[2], verType)
|
||||
verstring = "{}.{}".format(version[0], version[1])
|
||||
debug = True
|
||||
|
||||
changelog = "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" + \
|
||||
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.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.2 \nMise-à-jour mineure avec les corrections suivantes :\n- Changement d'icône de l'exécutable\n- Correction d'un bug affectant le logging\n- Correction d'un bug affectant la détection de documents\n- Et autres modifications mineures\n\n" + \
|
||||
|
212
src/ihm.py
212
src/ihm.py
@ -27,9 +27,12 @@ from tkinter import *
|
||||
from tkinter.messagebox import *
|
||||
from tkinter import filedialog
|
||||
from tkinter import ttk
|
||||
import cv2
|
||||
import PIL.Image, PIL.ImageTk
|
||||
|
||||
import logger # logger.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"]
|
||||
@ -108,35 +111,50 @@ class LauncherWindow(Tk):
|
||||
def __init__(self):
|
||||
# Initialize the tkinter main class
|
||||
Tk.__init__(self)
|
||||
self.configure(bg=globs.CNIRLColor)
|
||||
self.resizable(width=False, height=False)
|
||||
self.queue = []
|
||||
|
||||
# Setting up the geometry
|
||||
ws = self.winfo_screenwidth()
|
||||
hs = self.winfo_screenheight()
|
||||
wheight = hs /4
|
||||
wwidth = ws /4
|
||||
wheight = 274
|
||||
wwidth = 480
|
||||
# Centering
|
||||
x = ws / 2 - wwidth / 2
|
||||
y = hs / 2 - wheight / 2
|
||||
self.geometry('%dx%d+%d+%d' % (wwidth, wheight, x, y))
|
||||
|
||||
# Creating objects
|
||||
self.mainCanvas = Canvas(self, width=wwidth, height=wheight*9/10, bg=globs.CNIRLColor, highlightthickness=0)
|
||||
self.pBarZone = Canvas(self, width=wwidth, height=wheight/10, bg=globs.CNIRLColor)
|
||||
# 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.create_image(wwidth /2, wheight /2, image=self.photo)
|
||||
|
||||
self.progressBar = ttk.Progressbar(self.pBarZone, orient=HORIZONTAL, length=wwidth-10, mode='determinate')
|
||||
# Column
|
||||
self.mainCanvas.grid_rowconfigure(0, weight=1, minsize=(wheight / 10 * 9))
|
||||
self.mainCanvas.grid_rowconfigure(1, weight=1, minsize=(wheight / 10 * 1))
|
||||
|
||||
self.mainCanvas.create_text((wwidth / 2), (wheight / 3), text=(globs.CNIRName), font='Helvetica 30', fill='white')
|
||||
self.mainCanvas.create_text((wwidth / 2), (wheight / 2), text="version " + (globs.verstring_full), font='Helvetica 8', fill='white')
|
||||
self.msg = self.mainCanvas.create_text((wwidth / 2), (wheight / 1.20), text='Booting up...', font='Helvetica 9', fill='white')
|
||||
|
||||
#self.pBarZone = Frame(self.mainCanvas, width=wwidth, height=wheight/10)
|
||||
self.update()
|
||||
|
||||
self.progressBar = ttk.Progressbar(self.mainCanvas, orient=HORIZONTAL, length=wwidth, mode='determinate')
|
||||
|
||||
self.wm_title(globs.CNIRName)
|
||||
|
||||
# Centering
|
||||
x = ws / 2 - wwidth / 2
|
||||
y = hs / 2 - wheight / 2
|
||||
self.geometry('%dx%d+%d+%d' % (wwidth, wheight, x, y))
|
||||
self.mainCanvas.grid()
|
||||
self.pBarZone.grid()
|
||||
self.progressBar.grid()
|
||||
self.mainCanvas.grid(row=0)
|
||||
self.update()
|
||||
self.progressBar.grid(row=1, sticky='S')
|
||||
|
||||
|
||||
if getattr(sys, 'frozen', False):
|
||||
@ -152,6 +170,9 @@ class LauncherWindow(Tk):
|
||||
def printmsg(self, msg):
|
||||
self.mainCanvas.itemconfigure(self.msg, text=(msg))
|
||||
|
||||
def exit(self):
|
||||
self.after(1000, self.destroy)
|
||||
|
||||
class AutoScrollbar(ttk.Scrollbar):
|
||||
|
||||
def set(self, lo, hi):
|
||||
@ -167,156 +188,21 @@ class AutoScrollbar(ttk.Scrollbar):
|
||||
def place(self, **kw):
|
||||
raise TclError('Cannot use place with the widget ' + self.__class__.__name__)
|
||||
|
||||
class OpenPageDialog(Toplevel):
|
||||
|
||||
def __init__(self, parent, number):
|
||||
super().__init__(parent)
|
||||
self.parent = parent
|
||||
self.title("Choisir la page à afficher de l'image selectionnée")
|
||||
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()
|
||||
class ResizeableCanvas(Canvas):
|
||||
def __init__(self,parent,**kwargs):
|
||||
Canvas.__init__(self,parent,**kwargs)
|
||||
self.bind("<Configure>", self.on_resize)
|
||||
self.height = self.winfo_reqheight()
|
||||
self.width = self.winfo_reqwidth()
|
||||
|
||||
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
|
||||
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()
|
85
src/main.py
85
src/main.py
@ -32,6 +32,9 @@ from tkinter import ttk
|
||||
import threading
|
||||
from datetime import datetime
|
||||
import re
|
||||
import traceback
|
||||
import cv2
|
||||
import PIL.Image, PIL.ImageTk
|
||||
|
||||
import ihm # ihm.py
|
||||
import logger # logger.py
|
||||
@ -55,11 +58,13 @@ class mainWindow(Tk):
|
||||
self.Tags = []
|
||||
self.compliance = True
|
||||
|
||||
# Get the screen size
|
||||
# Hide during construction
|
||||
self.withdraw()
|
||||
|
||||
# Get the screen size and center
|
||||
ws = self.winfo_screenwidth()
|
||||
hs = self.winfo_screenheight()
|
||||
logfile.printdbg('Launching main window with resolution' + str(ws) + 'x' + str(hs))
|
||||
self.grid()
|
||||
|
||||
# Configuring the size of each part of the window
|
||||
self.grid_columnconfigure(0, weight=1, minsize=(ws / 2 * 0.3333333333333333))
|
||||
@ -141,13 +146,18 @@ class mainWindow(Tk):
|
||||
"INDIC" : self.indic,
|
||||
}
|
||||
|
||||
# The STATUS indicator
|
||||
self.STATUT = ttk.Labelframe(self, text='Statut')
|
||||
# The STATUS indicator + image display
|
||||
self.STATUT = ttk.Labelframe(self, text='Affichage de documents et statut')
|
||||
self.STATUT.grid_columnconfigure(0, weight=1)
|
||||
self.STATUT.grid_rowconfigure(0, weight=1)
|
||||
self.STATUStxt = Label((self.STATUT), text='', font='Times 24', fg='#FFBF00')
|
||||
self.STATUStxt.grid(column=0, row=0, padx=0, pady=0, sticky='EWNS')
|
||||
self.STATUStxt['text'] = 'EN ATTENTE'
|
||||
self.STATUT.frame = Frame(self.STATUT)
|
||||
self.STATUT.frame.grid(column=0, row=0, sticky='NSEW')
|
||||
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
|
||||
self.terminal = ttk.Labelframe(self, text='Terminal de saisie de MRZ complète')
|
||||
@ -201,7 +211,6 @@ class mainWindow(Tk):
|
||||
self.terminal.grid(column=0, row=2, sticky='EWNS', columnspan=2, padx=5, pady=5)
|
||||
self.terminal2.grid(column=0, row=1, sticky='EWNS', columnspan=2, padx=5, pady=5)
|
||||
self.monitor.grid(column=2, row=1, sticky='EWNS', columnspan=1, rowspan=2, padx=5, pady=5)
|
||||
self.update()
|
||||
|
||||
# What is a window without a menu bar ?
|
||||
menubar = Menu(self)
|
||||
@ -228,22 +237,48 @@ class mainWindow(Tk):
|
||||
|
||||
# Make this window resizable and set her size
|
||||
self.resizable(width=True, height=True)
|
||||
self.minsize(self.winfo_width(), self.winfo_height())
|
||||
w = int(self.winfo_width())
|
||||
h = int(self.winfo_height())
|
||||
self.update()
|
||||
w = int(self.winfo_reqwidth())
|
||||
h = int(self.winfo_reqheight())
|
||||
ws = self.winfo_screenwidth()
|
||||
hs = self.winfo_screenheight()
|
||||
x = ws / 2 - w / 2
|
||||
y = hs / 2 - h / 2
|
||||
x = (ws - w)/2
|
||||
y = (hs - h)/2
|
||||
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
||||
self.update()
|
||||
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
|
||||
self.termtext.bind('<Key>', self.entryValidation)
|
||||
self.termtext.bind('<<Paste>>', self.pasteValidation)
|
||||
self.speed731text.bind('<Control_R>', self.speedValidation)
|
||||
self.update()
|
||||
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):
|
||||
# analysis
|
||||
# If we must decide the type of the document
|
||||
@ -426,8 +461,13 @@ class mainWindow(Tk):
|
||||
|
||||
|
||||
def openingScan(self):
|
||||
pass
|
||||
# OPEN A SCAN
|
||||
path = ''
|
||||
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):
|
||||
self.initialize()
|
||||
@ -521,13 +561,6 @@ class mainWindow(Tk):
|
||||
self.termtext.tag_configure("nonconforme", background="red", relief='raised', foreground="white")
|
||||
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
|
||||
docInfos = mrz.getDocInfos(self.mrzDecided, code)
|
||||
#print(docInfos)
|
||||
@ -544,6 +577,12 @@ class mainWindow(Tk):
|
||||
self.infoList[key]['background'] = "red"
|
||||
self.infoList[key]['foreground'] = "white"
|
||||
self.infoList[key]['text'] = "NC"
|
||||
self.compliance = False
|
||||
|
||||
if self.compliance == True:
|
||||
self.statusUpdate("CONFORME", "chartreuse2")
|
||||
else:
|
||||
self.statusUpdate("NON-CONFORME","red")
|
||||
|
||||
return
|
||||
|
||||
|
@ -989,7 +989,7 @@ def getDocInfos(doc, code):
|
||||
try:
|
||||
datetime.datetime.strptime(value,"%d/%m/%y")
|
||||
except ValueError:
|
||||
print(value)
|
||||
#print(value)
|
||||
if value != "":
|
||||
res[field[0]] = False
|
||||
else:
|
||||
|
@ -42,6 +42,8 @@ import downloader # downloader.py
|
||||
UPDATE_IS_MADE = False
|
||||
UPATH = ' '
|
||||
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
def createShortcut(path, target='', wDir='', icon=''):
|
||||
"""
|
||||
Creates a shortcut for a program or an internet link
|
||||
@ -79,6 +81,47 @@ def exitProcess(arg):
|
||||
process.terminate()
|
||||
sys.exit(arg)
|
||||
|
||||
def runPowershell(scriptblock, cwd=os.getcwd()):
|
||||
"""
|
||||
Executes a powershell command
|
||||
"""
|
||||
log.debug("Running PowerShell Block:\r\n%s", scriptblock)
|
||||
log.debug("Current Directory: %s\r\n" % cwd)
|
||||
psProc = subprocess.Popen([r'C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe',
|
||||
'-ExecutionPolicy', 'Bypass',
|
||||
'-noprofile',
|
||||
'-c', '-',],
|
||||
cwd=cwd,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdoutdata, stderrdata = psProc.communicate(scriptblock)
|
||||
|
||||
if stdoutdata:
|
||||
log.debug("Script Output:\r\n%s" % stdoutdata)
|
||||
elif not stderrdata:
|
||||
log.debug("Script completed succssfully (no stdout/stderr).")
|
||||
if stderrdata:
|
||||
log.error("Script Error:\r\n%s" % stderrdata)
|
||||
|
||||
return stdoutdata, stderrdata
|
||||
|
||||
|
||||
def getCertificates(server_list, location="LocalMachine", store="My"):
|
||||
"""
|
||||
Returns the json data of all installed certificates
|
||||
"""
|
||||
cmd = '''
|
||||
$sb = { ls Cert:\%s\%s | Select Subject,ThumbPrint }
|
||||
$Servers = '%s' | ConvertFrom-Json
|
||||
|
||||
Invoke-Command -ComputerName $Servers -ScriptBlock $sb -Authentication Negotiate | ConvertTo-Json -Depth 999
|
||||
''' % (location, store, json.dumps(server_list))
|
||||
stdoutdata, stderrdata = runPowershell(cmd)
|
||||
return json.loads(stdoutdata)
|
||||
|
||||
|
||||
|
||||
def getLatestVersion(credentials):
|
||||
"""
|
||||
Returns the latest version of the software
|
||||
@ -86,7 +129,7 @@ def getLatestVersion(credentials):
|
||||
|
||||
# Global Handlers
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
|
||||
# First retrieving the urls !
|
||||
while True:
|
||||
@ -153,7 +196,7 @@ def getLatestVersion(credentials):
|
||||
def tessInstall(PATH, credentials):
|
||||
# Global Handlers
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
|
||||
# Verifying that Tesseract is installed
|
||||
if not os.path.exists(PATH + '\\Tesseract-OCR4\\'):
|
||||
@ -181,7 +224,7 @@ def tessInstall(PATH, credentials):
|
||||
def batch(credentials):
|
||||
# Global Handlers
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
|
||||
# Get the latest version of CNIRevelator
|
||||
finalver, finalurl, finalchecksum = getLatestVersion(credentials)
|
||||
@ -259,7 +302,7 @@ def umain():
|
||||
|
||||
# Global Handlers
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
|
||||
credentials = downloader.newcredentials()
|
||||
|
||||
@ -267,8 +310,7 @@ def umain():
|
||||
logfile.printerr("Credentials Error. No effective update !")
|
||||
launcherWindow.printmsg('Credentials Error. No effective update !')
|
||||
time.sleep(2)
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
launcherWindow.destroy()
|
||||
launcherWindow.exit()
|
||||
return 0
|
||||
|
||||
# Cleaner for the old version if detected
|
||||
@ -335,7 +377,7 @@ def umain():
|
||||
logfile.printerr("An error occured on the thread : " + str(traceback.format_exc()))
|
||||
launcherWindow.printmsg('ERROR : ' + str(e))
|
||||
time.sleep(3)
|
||||
launcherWindow.destroy()
|
||||
launcherWindow.exit()
|
||||
return 1
|
||||
|
||||
if success:
|
||||
@ -345,14 +387,12 @@ def umain():
|
||||
logfile.printerr("An error occured. No effective update !")
|
||||
launcherWindow.printmsg('An error occured. No effective update !')
|
||||
time.sleep(2)
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
launcherWindow.destroy()
|
||||
launcherWindow.exit()
|
||||
return 0
|
||||
|
||||
except:
|
||||
logfile.printerr("A FATAL ERROR OCCURED : " + str(traceback.format_exc()))
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
launcherWindow.destroy()
|
||||
launcherWindow.exit()
|
||||
sys.exit(2)
|
||||
return 2
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user