PKipMH;kRRedocuments/process.py# -*- coding: utf-8 -*- import os import re from tempfile import NamedTemporaryFile from subprocess import check_call from shutil import copyfile from PyQt5.QtCore import QObject, pyqtSignal import edocuments class Process(QObject): progress = pyqtSignal(int, str, str, dict) cancel = False def process( self, names, filename=None, destination_filename=None, in_extention=None, get_content=False): cmds = edocuments.config.get("cmds", {}) out_ext = in_extention original_filename = filename if destination_filename is None: destination_filename = filename for no, name in enumerate(names): cmd = cmds.get(name) if cmd is None: raise "Missing command '%s' in `cmds`" % name if isinstance(cmd, str): cmd = dict(cmd=cmd) if cmd.get('type') == 'rename': destination_filename = self._rename(cmd, destination_filename) else: if 'out_ext' in cmd: out_ext = cmd['out_ext'] inplace = cmd.get('inplace', False) cmd_cmd = cmd.get('cmd') if inplace: out_name = filename else: if out_ext is None: out_name = NamedTemporaryFile(mode='w+b').name else: out_name = NamedTemporaryFile( mode='w+b', suffix='.' + out_ext ).name params = {} if filename is not None: params["in"] = "'%s'" % filename.replace("'", "'\"'\"'") if not inplace: params["out"] = "'%s'" % out_name.replace("'", "'\"'\"'") try: cmd_cmd = cmd_cmd.format(**params) except: print("Error in {name}: {cmd}, with {params}".format( name=name, cmd=cmd_cmd, params=params)) raise if self.cancel is True: print(555) return None, None print("{name}: {cmd}".format(name=name, cmd=cmd_cmd)) self.progress.emit(no, name, cmd_cmd, cmd) check_call(cmd_cmd, shell=True) filename = out_name if get_content: content = None if os.path.exists(filename): with open(filename) as f: content = f.read() if original_filename is None or original_filename != filename: os.unlink(filename) return content, out_ext else: if original_filename is not None and original_filename != filename: os.unlink(original_filename) if out_ext is not None: destination_filename = "%s.%s" % (re.sub( r"\.[a-z0-9A-Z]{2,5}$", "", destination_filename ), out_ext) if filename != destination_filename: directory = os.path.dirname(destination_filename) if not os.path.exists(directory): os.makedirs(directory) copyfile(filename, destination_filename) os.unlink(filename) return destination_filename, out_ext def _rename(self, cmd, destination_filename): from_re = cmd.get('from') to_re = cmd.get('to') if cmd.get('format') in ['upper', 'lower']: def format_term(term): if cmd.get('format') == 'upper': return term.upper() else: return term.lower() to_re = lambda m: format_term(m.group(0)) return re.sub(from_re, to_re, destination_filename) def destination_filename(self, names, filename, extension=None): cmds = edocuments.config.get("cmds", {}) for name in names: cmd = cmds.get(name) if cmd is None: raise "Missing command '%s' in `cmds`" % name if isinstance(cmd, str): cmd = {} if cmd.get('type') == 'rename': filename = self._rename(cmd, filename) else: if 'out_ext' in cmd: extension = cmd['out_ext'] if extension is not None: filename = "%s.%s" % (re.sub( r"\.[a-z0-9A-Z]{2,5}$", "", filename ), extension) return filename, extension PK>HOedocuments/label_dialog.py# -*- coding: utf-8 -*- from PyQt5.QtWidgets import QDialog from PyQt5.QtGui import QPixmap from edocuments.ui.label_dialog import Ui_Dialog class Dialog(QDialog): def __init__(self): super().__init__() self.ui = Ui_Dialog() self.ui.setupUi(self) def set_image(self, image_filename): size = 800 pixmap = QPixmap(image_filename) if pixmap.width() > pixmap.height(): if pixmap.width() > size: pixmap = pixmap.scaledToWidth(size) else: if pixmap.height() > size: pixmap = pixmap.scaledToHeight(size) self.ui.label.setPixmap(pixmap) self.ui.label.setMask(pixmap.mask()) self.ui.label.show() PKMJH`edocuments/colorize.py# -*- coding: utf-8 -*- BLACK = 0 RED = 1 GREEN = 2 YELLOW = 3 BLUE = 4 MAGENTA = 5 CYAN = 6 WHITE = 7 def colorize(text, color): return "\x1b[01;3%im%s\x1b[0m" % (color, text) PKKMHdGW; ; edocuments/index.py# -*- coding: utf-8 -*- import os from pathlib import Path from whoosh.index import create_in, open_dir from whoosh.fields import Schema, ID, TEXT, STORED from whoosh.qparser import QueryParser from whoosh.query import Term import edocuments class Index: def __init__(self): self.directory = os.path.join(edocuments.root_folder, '.index') self.dirty = False schema = Schema( path_id=ID(stored=True, unique=True), path=TEXT(stored=True), content=TEXT(stored=True), date=STORED ) self.parser_path = QueryParser("path_id", schema) self.parser_content = QueryParser("content", schema) if not os.path.exists(self.directory): os.makedirs(self.directory) self.index = create_in(self.directory, schema) else: self.index = open_dir(self.directory) self.writer = self.index.writer() def get_nb(self, filename): filename = edocuments.short_path(filename) with self.index.searcher() as searcher: return len(searcher.search(Term("path_id", filename))) # TODO: update # http://pythonhosted.org//Whoosh/indexing.html#updating-documents def add(self, filename, text): date = Path(filename).stat().st_mtime filename = edocuments.short_path(filename) if self.get_nb(filename) == 0: self.writer.add_document( path_id=filename, path=filename, content="%s\n%s" % (filename, text), date=date) self.dirty = True def save(self): if self.dirty: print('Saving index.') self.writer.commit(optimize=True) self.writer = self.index.writer() def search(self, text): with self.index.searcher() as searcher: query = self.parser_content.parse(text) results = searcher.search(query, terms=True, limit=200) return [{ 'path': r.get('path'), 'content': r.get('content'), 'highlight': r.highlights( 'path' if 'path_in' in r.matched_terms() else 'content' ), } for r in results] _index = None def index(): global _index if _index is None: _index = Index() return _index PKaDMH^iwwedocuments/__init__.py# -*- coding: utf-8 -*- import os import sys import re import shutil import subprocess from pathlib import Path from threading import Thread from multiprocessing import Pool from yaml import load from argparse import ArgumentParser from bottle import mako_template from autoupgrade import AutoUpgrade from PyQt5.QtCore import QSettings from PyQt5.QtWidgets import QApplication, QMessageBox from edocuments.main_widget import MainWindow CONFIG_FILENAME = "edocuments.yaml" if 'APPDATA' in os.environ: CONFIG_PATH = os.path.join(os.environ['APPDATA'], CONFIG_FILENAME) elif 'XDG_CONFIG_HOME' in os.environ: CONFIG_PATH = os.path.join(os.environ['XDG_CONFIG_HOME'], CONFIG_FILENAME) else: CONFIG_PATH = os.path.join(os.environ['HOME'], '.config', CONFIG_FILENAME) config = {} root_folder = None settings = None main_window = None pool = None def short_path(filename): global root_folder if filename[:len(root_folder)] == root_folder: return filename[len(root_folder):] return filename def long_path(filename): global root_folder if len(filename) == 0 or filename[0] != '/': return os.path.join(root_folder, filename) return filename def gui_main(): global config, root_folder, settings, main_window, pool with open(CONFIG_PATH) as f: config = load(f.read()) root_folder = os.path.expanduser(config.get("root_folder")) if root_folder[-1] != '/': root_folder += '/' settings = QSettings("org", "edocuments") pool = Pool(config.get('nb_process', 8)) app = QApplication(sys.argv) main_window = MainWindow() if settings.value("geometry") is not None: main_window.restoreGeometry(settings.value("geometry")) if settings.value("state") is not None: main_window.restoreState(settings.value("state")) t = Thread(target=autoupgrade) t.start() main_window.show() app.exec() settings.setValue("geometry", main_window.saveGeometry()) settings.setValue("state", main_window.saveState()) settings.sync() def autoupgrade(): au = AutoUpgrade('edocuments') if au.check(): msg = QMessageBox(main_window) msg.setWindowTitle("eDocuments - Upgrade") msg.setText("A new version is available") msg.setInformativeText("Do you want to do anupdate and restart?") msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) ret = msg.exec() if ret == QMessageBox.Yes: au.upgrade(dependencies=True) au.restart() def cmd_main(): parser = ArgumentParser( description='eDocuments - a simple and productive personal documents ' 'library.', prog=sys.argv[0] ) parser.add_argument( '--install', action='store_true', help='Install the application icon, the required packages, ' 'and default config file', ) parser.add_argument( '--lang3', default='eng', metavar='LANG', help='the language used by the OCR', ) parser.add_argument( '--list-available-lang3', action='store_true', help='List the available language used by the OCR.', ) options = parser.parse_args() if options.list_available_lang3: if Path('/usr/bin/apt-cache').exists(): result = subprocess.check_output([ '/usr/bin/apt-cache', 'search', 'tesseract-ocr-']) result = str(result)[1:].strip("'") result = result.replace('\\n', '\n') result = re.sub( '\ntesseract-ocr-all - [^\n]* packages\n', '', result, flags=re.MULTILINE) result = re.sub(r'tesseract-ocr-', '', result) result = re.sub(r' - tesseract-ocr language files ', ' ', result) print(result) else: exit('Works only on Debian base OS') if options.install: if not Path(os.path.expanduser( '~/.local/share/applications')).exists(): os.makedirs(os.path.expanduser('~/.local/share/applications')) ressource_dir = os.path.join(os.path.dirname(os.path.dirname( os.path.abspath(__file__))), 'ressources') shutil.copyfile( os.path.join(ressource_dir, 'edocuments.desktop'), os.path.expanduser( '~/.local/share/applications/edocuments.desktop') ) shutil.copyfile( os.path.join(ressource_dir, 'edocuments.png'), os.path.expanduser('~/.local/share/applications/edocuments.png') ) config = mako_template( os.path.join(ressource_dir, 'config.yaml'), lang=options.lang3 ) with open( os.path.expanduser('~/.config/edocuments.yaml'), 'w' ) as file_open: file_open.write(config) if Path('/usr/bin/apt-get').exists(): subprocess.check_call([ 'sudo', 'apt-get', 'install', 'python3-pyqt5', 'sane-utils', 'imagemagick', 'tesseract-ocr', 'tesseract-ocr-' + options.lang3, 'optipng']) else: print( 'WARNING: the package installation works only on Debian ' 'base OS') PKqMH V4&4&edocuments/main_widget.py# -*- coding: utf-8 -*- import os import sys import re import pathlib import traceback from threading import Thread from subprocess import call from datetime import datetime, timedelta from pathlib import Path from PyQt5.Qt import Qt from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QMainWindow, QFileDialog, \ QErrorMessage, QMessageBox, QProgressDialog, QListWidgetItem import edocuments from edocuments.process import Process from edocuments.index import index from edocuments.ui.main import Ui_MainWindow from edocuments.label_dialog import Dialog class MainWindow(QMainWindow): scan_end = pyqtSignal(str) scan_error = pyqtSignal(str) update_update_library_progress = pyqtSignal(int, str) def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.process = Process() self.ui.scan_comments.setText(edocuments.config.get("scan_comments")) default_index = 0 for s in edocuments.config.get("scans", []): if s.get("default") is True: default_index = self.ui.scan_type.count() self.ui.scan_type.addItem(s.get("name"), s) self.ui.scan_type.setCurrentIndex(default_index) self.ui.scan_browse.clicked.connect(self.scan_browse) self.ui.scan_to.returnPressed.connect(self.scan_start) self.ui.scan_start.clicked.connect(self.scan_start) self.ui.open.clicked.connect(self.open_selected) self.ui.open_folder.clicked.connect(self.open_folder) self.image_dialog = Dialog() self.scan_end.connect(self.end_scan) self.scan_error.connect(self.on_scan_error) self.update_update_library_progress.connect( self.on_update_update_library_progress) self.ui.search_text.textChanged.connect(self.search) self.ui.search_result_list.itemSelectionChanged.connect( self.selection_change) self.ui.library_update.triggered.connect(self.update_library) self.process.progress.connect(self.on_progress) def open_selected(self): item = self.ui.search_result_list.currentItem() if item is not None: cmd = edocuments.config.get('open_cmd').split(' ') cmd.append(edocuments.long_path(item.result.get('path'))) call(cmd) def open_folder(self): item = self.ui.search_result_list.currentItem() if item is not None: cmd = edocuments.config.get('open_cmd').split(' ') cmd.append(os.path.dirname( edocuments.long_path(item.result.get('path')))) call(cmd) def selection_change(self): item = self.ui.search_result_list.currentItem() if item is not None: self.ui.search_result_text.document().setHtml( item.result.get('highlight')) else: self.ui.search_result_text.document().setHtml('') def search(self, text): model = self.ui.search_result_list.model() model.removeRows(0, model.rowCount()) for result in index().search(self.ui.search_text.text()): item = QListWidgetItem(result['path'], self.ui.search_result_list) item.result = result def scan_browse(self, event): filename = QFileDialog.getSaveFileName( self, "Scan to", directory=self.filename() )[0] filename = re.sub(r"\.[a-z0-9A-Z]{2,5}$", "", filename) filename = edocuments.short_path(filename) self.ui.scan_to.setText(filename) def update_library(self): self.update_library_progress = QProgressDialog( "Scanning...", None, 0, 100, self) self.update_library_progress.setWindowTitle('Updating the library...') self.update_library_progress.setLabelText('Browsing the files...') self.update_library_progress.setWindowModality(Qt.WindowModal) self.update_library_progress.show() t = Thread(target=self._do_update_library) t.start() def on_update_update_library_progress(self, pos, text): self.update_library_progress.setValue(pos) self.update_library_progress.setLabelText(text) def _do_update_library(self): todo = [] for conv in edocuments.config.get('to_txt'): cmds = conv.get("cmds") for filename in Path(edocuments.root_folder).rglob( "*." + conv.get('extension')): if index().get_nb(str(filename)) == 0: todo.append((str(filename), cmds)) self.update_update_library_progress.emit( 0, 'Browsing the files (%i)...' % len(todo)) nb = len(todo) results = edocuments.pool.imap_unordered(_to_txt, todo) interval = timedelta( seconds=edocuments.config.get('save_interval', 60)) last_save = datetime.now() nb_error = 0 no = 0 self.update_update_library_progress.emit( 0, 'Parsing the files %i/%i.' % (no, nb)) for filename, text in results: no += 1 self.update_update_library_progress.emit( no * 100 / nb, 'Parsing the files %i/%i.' % (no, nb)) print("%i/%i" % (no, nb)) if text is False: nb_error += 1 else: index().add(filename, text) if datetime.now() - last_save > interval: index().save() last_save = datetime.now() index().save() if nb_error != 0: self.scan_error.emit("Finished with %i errors" % nb_error) def filename(self): return edocuments.long_path(self.ui.scan_to.text()) def scan_start(self, event=None): if pathlib.Path(self.filename()).is_dir(): err = QErrorMessage(self) err.setWindowTitle("eDocuments - Error") err.showMessage("The destination is a directory!") return destination, extension = self.process.destination_filename( self.ui.scan_type.currentData().get("cmds"), self.filename() ) if pathlib.Path(destination).is_file(): msg = QMessageBox(self) msg.setWindowTitle("Scanning...") msg.setText("The destination file already exists") msg.setInformativeText("Do you want to overwrite it?") msg.setStandardButtons( QMessageBox.Ok | QMessageBox.Cancel | QMessageBox.Open) ret = msg.exec() if ret == QMessageBox.Ok: self._scan() elif ret == QMessageBox.Open: cmd = edocuments.config.get('open_cmd').split(' ') cmd.append(destination) call(cmd) else: self._scan() def _scan(self): cmds = self.ui.scan_type.currentData().get("cmds") self.progress = QProgressDialog( "Scanning...", "Cancel", 0, len(cmds), self) self.progress.setWindowTitle("Scanning...") self.progress.setWindowModality(Qt.WindowModal) self.progress.show() t = Thread(target=self._do_scan) t.start() def on_progress(self, no, name, cmd_cmd, cmd): if self.progress is not None: self.progress.setValue(no) self.progress.setLabelText(cmd.get('display', '')) if self.progress.wasCanceled() is True: print("Cancel") self.process.cancel = True self.statusBar().showMessage(cmd_cmd) def _do_scan(self): cmds = self.ui.scan_type.currentData().get("cmds") try: filename, extension = self.process.process( cmds, destination_filename=self.filename(), ) except: self.scan_error.emit(str(sys.exc_info()[0])) raise if filename is None: return self.scan_end.emit(filename) cmds = self.ui.scan_type.currentData().get("postprocess", []) try: filename, extension = self.process.process( cmds, filename=filename, destination_filename=self.filename(), in_extention=extension, ) conv = [ c for c in edocuments.config.get('to_txt') if c['extension'] == extension ] if len(conv) >= 1: conv = conv[0] cmds = conv.get("cmds") try: text, extension = self.process.process( cmds, filename=filename, get_content=True, ) index().add(filename, text) except: self.scan_error.emit(str(sys.exc_info()[0])) raise except: self.scan_error.emit(str(sys.exc_info()[0])) raise def end_scan(self, filename): self.progress.hide() self.image_dialog.set_image(filename) self.image_dialog.exec() self.ui.scan_to.setText(re.sub( ' ([0-9]{1,3})$', lambda m: ' ' + str(int(m.group(1)) + 1), self.ui.scan_to.text() )) def on_scan_error(self, error): print('Error: %s' % error) err = QErrorMessage(self) err.setWindowTitle("eDocuments - scan error") err.showMessage(error) def _to_txt(job): filename, cmds = job try: text, extension = Process().process( cmds, filename=str(filename), get_content=True, ) if text is None: text = '' return filename, text except: traceback.print_exc() return filename, False PK>H"..edocuments/ui/label_dialog.py# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'edocuments/ui/label_dialog.ui' # # Created by: PyQt5 UI code generator 5.4.2 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") Dialog.resize(194, 70) self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) self.verticalLayout.setObjectName("verticalLayout") self.label = QtWidgets.QLabel(Dialog) self.label.setText("") self.label.setObjectName("label") self.verticalLayout.addWidget(self.label) self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.verticalLayout.addWidget(self.buttonBox) self.retranslateUi(Dialog) self.buttonBox.accepted.connect(Dialog.accept) self.buttonBox.rejected.connect(Dialog.reject) QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(_translate("Dialog", "Dialog")) PKLHedocuments/ui/main.py# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'edocuments/ui/main.ui' # # Created by: PyQt5 UI code generator 5.4.2 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(583, 525) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../../ressources/edocuments.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) MainWindow.setWindowIcon(icon) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout.setObjectName("verticalLayout") self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) self.tabWidget.setEnabled(True) self.tabWidget.setObjectName("tabWidget") self.search = QtWidgets.QWidget() self.search.setObjectName("search") self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.search) self.verticalLayout_4.setObjectName("verticalLayout_4") self.search_text = QtWidgets.QLineEdit(self.search) self.search_text.setObjectName("search_text") self.verticalLayout_4.addWidget(self.search_text) self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint) self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.search_result_list = QtWidgets.QListWidget(self.search) self.search_result_list.setObjectName("search_result_list") self.horizontalLayout_2.addWidget(self.search_result_list) self.verticalLayout_5 = QtWidgets.QVBoxLayout() self.verticalLayout_5.setObjectName("verticalLayout_5") self.search_result_text = QtWidgets.QTextBrowser(self.search) self.search_result_text.setObjectName("search_result_text") self.verticalLayout_5.addWidget(self.search_result_text) self.horizontalWidget = QtWidgets.QWidget(self.search) self.horizontalWidget.setObjectName("horizontalWidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalWidget) self.horizontalLayout.setObjectName("horizontalLayout") spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.open_folder = QtWidgets.QPushButton(self.horizontalWidget) self.open_folder.setObjectName("open_folder") self.horizontalLayout.addWidget(self.open_folder) self.open = QtWidgets.QPushButton(self.horizontalWidget) self.open.setObjectName("open") self.horizontalLayout.addWidget(self.open) self.verticalLayout_5.addWidget(self.horizontalWidget) self.horizontalLayout_2.addLayout(self.verticalLayout_5) self.horizontalLayout_2.setStretch(0, 1) self.verticalLayout_4.addLayout(self.horizontalLayout_2) self.tabWidget.addTab(self.search, "") self.scan = QtWidgets.QWidget() self.scan.setObjectName("scan") self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.scan) self.verticalLayout_2.setObjectName("verticalLayout_2") self.horizontalLayout_3 = QtWidgets.QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.label = QtWidgets.QLabel(self.scan) self.label.setObjectName("label") self.horizontalLayout_3.addWidget(self.label) self.scan_type = QtWidgets.QComboBox(self.scan) self.scan_type.setObjectName("scan_type") self.horizontalLayout_3.addWidget(self.scan_type) self.verticalLayout_2.addLayout(self.horizontalLayout_3) self.direct = QtWidgets.QGroupBox(self.scan) self.direct.setObjectName("direct") self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.direct) self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.scan_to = QtWidgets.QLineEdit(self.direct) self.scan_to.setObjectName("scan_to") self.horizontalLayout_5.addWidget(self.scan_to) self.scan_browse = QtWidgets.QPushButton(self.direct) self.scan_browse.setObjectName("scan_browse") self.horizontalLayout_5.addWidget(self.scan_browse) self.scan_start = QtWidgets.QPushButton(self.direct) self.scan_start.setObjectName("scan_start") self.horizontalLayout_5.addWidget(self.scan_start) self.verticalLayout_2.addWidget(self.direct) self.groupBox = QtWidgets.QGroupBox(self.scan) self.groupBox.setObjectName("groupBox") self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox) self.verticalLayout_6.setObjectName("verticalLayout_6") self.scan_comments = QtWidgets.QLabel(self.groupBox) self.scan_comments.setText("") self.scan_comments.setObjectName("scan_comments") self.verticalLayout_6.addWidget(self.scan_comments) self.verticalLayout_2.addWidget(self.groupBox) spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_2.addItem(spacerItem1) self.tabWidget.addTab(self.scan, "") self.verticalLayout.addWidget(self.tabWidget) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 583, 24)) self.menubar.setObjectName("menubar") self.menuUpdate_library = QtWidgets.QMenu(self.menubar) self.menuUpdate_library.setObjectName("menuUpdate_library") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.library_update = QtWidgets.QAction(MainWindow) self.library_update.setObjectName("library_update") self.library_reset = QtWidgets.QAction(MainWindow) self.library_reset.setObjectName("library_reset") self.menuUpdate_library.addAction(self.library_update) self.menuUpdate_library.addAction(self.library_reset) self.menubar.addAction(self.menuUpdate_library.menuAction()) self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "eDocuments - a Simple and Productive Personal Documents Library")) self.open_folder.setText(_translate("MainWindow", "Open folder")) self.open.setText(_translate("MainWindow", "Open")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.search), _translate("MainWindow", "Search")) self.label.setText(_translate("MainWindow", "Scan type")) self.direct.setTitle(_translate("MainWindow", "Scan")) self.scan_browse.setText(_translate("MainWindow", "Browse...")) self.scan_start.setText(_translate("MainWindow", "Scan")) self.groupBox.setTitle(_translate("MainWindow", "Comments")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.scan), _translate("MainWindow", "Scan")) self.menuUpdate_library.setTitle(_translate("MainWindow", "&Library")) self.library_update.setText(_translate("MainWindow", "&Update")) self.library_reset.setText(_translate("MainWindow", "Reset")) PK=Hedocuments/ui/__init__.pyPKÈMH99*edocuments-0.8.0.dist-info/DESCRIPTION.rstA sample and productive personal documents library * Scan your documents * Auto rotate * index them on the file name and on the content (OCR) * Mange your pdf * index them on the file name and on the content * Search in your library `Sources `_ PKÈMHY?]]+edocuments-0.8.0.dist-info/entry_points.txt[console_scripts] edocuments-cmd = edocuments:cmd_main edocuments-gui = edocuments:gui_main PKÈMH5|(edocuments-0.8.0.dist-info/metadata.json{"classifiers": ["Programming Language :: Python :: 3"], "extensions": {"python.commands": {"wrap_console": {"edocuments-cmd": "edocuments:cmd_main", "edocuments-gui": "edocuments:gui_main"}}, "python.details": {"contacts": [{"email": "stephane.brunner@gmail.com", "name": "St\u00e9phane Brunner", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/sbrunner/edocuments/"}}, "python.exports": {"console_scripts": {"edocuments-cmd": "edocuments:cmd_main", "edocuments-gui": "edocuments:gui_main"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["simple", "productive", "personal", "documents", "library", "scan", "index", "search"], "metadata_version": "2.0", "name": "edocuments", "run_requires": [{"requires": ["Mako", "PyYAML", "autoupgrade", "bottle"]}], "summary": "eDocuments - a simple and productive personal documents library", "test_requires": [{"requires": ["Mako", "PyYAML", "autoupgrade", "bottle"]}], "version": "0.8.0"}PKÈMH^x (edocuments-0.8.0.dist-info/top_level.txtedocuments PKÈMH''\\ edocuments-0.8.0.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any PKÈMH}`&&#edocuments-0.8.0.dist-info/METADATAMetadata-Version: 2.0 Name: edocuments Version: 0.8.0 Summary: eDocuments - a simple and productive personal documents library Home-page: https://github.com/sbrunner/edocuments/ Author: Stéphane Brunner Author-email: stephane.brunner@gmail.com License: UNKNOWN Keywords: simple productive personal documents library scan index search Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Requires-Dist: Mako Requires-Dist: PyYAML Requires-Dist: autoupgrade Requires-Dist: bottle A sample and productive personal documents library * Scan your documents * Auto rotate * index them on the file name and on the content (OCR) * Mange your pdf * index them on the file name and on the content * Search in your library `Sources `_ PKÈMH#88!edocuments-0.8.0.dist-info/RECORDedocuments/__init__.py,sha256=MCg0OfCdzYZ-Xmq__L0mEsVYKv2X1NhdWCLUrQEokq8,5239 edocuments/colorize.py,sha256=1xfk4jozdL_nfDoZ4mwQer8vMdw9xxfvWR8DpfON_ww,184 edocuments/index.py,sha256=8T12_cu0T-nGlfce9j2NKZOXktYDDF8mmhMvTkRsUP8,2363 edocuments/label_dialog.py,sha256=5gKnx_3ov0YnNO2yTieT_ojGfrhLyXtYCY6KXmwnaqI,736 edocuments/main_widget.py,sha256=WQFupkXcdY55q45QTLGQmcjbLWMj29P7W42cHuxHiTY,9780 edocuments/process.py,sha256=vSPYyerDCi89MXqQXYynjFCibGo0QHXN1D71PGDSjlY,4690 edocuments/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 edocuments/ui/label_dialog.py,sha256=Xwrsrq7chUq55Lk7OI-AqYWI0GV_2e_BhyiwuSiY0w0,1326 edocuments/ui/main.py,sha256=iksclp8uHgM7jr8nUxsrJaS489hcEkb-b_WQXoh-1Gs,7699 edocuments-0.8.0.dist-info/DESCRIPTION.rst,sha256=NNmNRtKhPkR7y8ziKTDcRske1amQTOFlUSC3wpyqXPw,313 edocuments-0.8.0.dist-info/METADATA,sha256=Dng3fZrz9Z6rkErOOGptPZQQTsULGDUSMURN0OcvDlE,806 edocuments-0.8.0.dist-info/RECORD,, edocuments-0.8.0.dist-info/WHEEL,sha256=JTb7YztR8fkPg6aSjc571Q4eiVHCwmUDlX8PhuuqIIE,92 edocuments-0.8.0.dist-info/entry_points.txt,sha256=P_eXij8vRbfFmwuMKSwtV99Zv4h1wZBwN1mq_ajJUkc,93 edocuments-0.8.0.dist-info/metadata.json,sha256=eQyj8sqGM_fwi4_5XJ4w3D568LM0TBVkPYW-bxzA8cU,1019 edocuments-0.8.0.dist-info/top_level.txt,sha256=yvF7f7apfBqVzBbWZQP6Pp2iqiVAHWruuNkQ7SDS2mg,11 PKipMH;kRRedocuments/process.pyPK>HOedocuments/label_dialog.pyPKMJH`edocuments/colorize.pyPKKMHdGW; ; edocuments/index.pyPKaDMH^iwwedocuments/__init__.pyPKqMH V4&4&4edocuments/main_widget.pyPK>H".. [edocuments/ui/label_dialog.pyPKLHt`edocuments/ui/main.pyPK=H~edocuments/ui/__init__.pyPKÈMH99*~edocuments-0.8.0.dist-info/DESCRIPTION.rstPKÈMHY?]]+redocuments-0.8.0.dist-info/entry_points.txtPKÈMH5|(edocuments-0.8.0.dist-info/metadata.jsonPKÈMH^x (Yedocuments-0.8.0.dist-info/top_level.txtPKÈMH''\\ edocuments-0.8.0.dist-info/WHEELPKÈMH}`&&#Dedocuments-0.8.0.dist-info/METADATAPKÈMH#88!edocuments-0.8.0.dist-info/RECORDPK"