Move all GUI-related stuff from OCREngine to GUIProxy.

This commit is contained in:
Andrey Golovizin 2014-09-11 12:16:38 +02:00
parent 8e4eb7d152
commit 4e35d56696
5 changed files with 113 additions and 40 deletions

View file

@ -28,10 +28,16 @@ from PyQt4 import QtCore
QtCore.signal = QtCore.pyqtSignal QtCore.signal = QtCore.pyqtSignal
QtCore.slot = QtCore.pyqtSlot QtCore.slot = QtCore.pyqtSlot
from PyQt4.QtCore import (
QThread,
)
from PyQt4.QtGui import ( from PyQt4.QtGui import (
qApp,
QApplication, QApplication,
) )
from .guiproxy import GUIProxy
from .window import MainWindow from .window import MainWindow
from .ocrengine import OCREngine from .ocrengine import OCREngine
@ -51,6 +57,18 @@ def load_entry_point(group, name):
raise ValueError('Entry point {} in group {} not found'.format(name, group)) raise ValueError('Entry point {} in group {} not found'.format(name, group))
class WorkerThread(QThread):
def __init__(self, ocr, quit=False):
super().__init__()
self.ocr = ocr
self.quit = quit
def run(self):
self.ocr.recognize()
if self.quit:
qApp.quit()
def main(): def main():
app = QApplication(sys.argv) app = QApplication(sys.argv)
@ -58,19 +76,21 @@ def main():
QApplication.setApplicationName("PixelOCR"); QApplication.setApplicationName("PixelOCR");
args = parser.parse_args() args = parser.parse_args()
gui_proxy = GUIProxy()
ocr = OCREngine( ocr = OCREngine(
args.filename, args.filename,
ui=gui_proxy,
skip=args.skip, skip=args.skip,
limit=args.limit, limit=args.limit,
quit=args.quit,
output_format=load_entry_point('pixelocr.formatting', args.output_format).load()(), output_format=load_entry_point('pixelocr.formatting', args.output_format).load()(),
) )
app.aboutToQuit.connect(ocr.save_glyphdb) app.aboutToQuit.connect(ocr.save_glyphdb)
ocr_thread = WorkerThread(ocr, quit=args.quit)
win = MainWindow(ocr) win = MainWindow(ocr)
win.glyphEntered.connect(ocr.give_help) win.glyphEntered.connect(gui_proxy.give_help)
win.show() win.show()
ocr.start() ocr_thread.start()
signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL)
sys.exit(app.exec_()) sys.exit(app.exec_())

55
pixelocr/gui/guiproxy.py Normal file
View file

@ -0,0 +1,55 @@
# Copyright (C) 2014 Andrey Golovizin
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from queue import Queue
from PyQt4.QtCore import (
signal,
slot,
QObject,
)
from PyQt4.QtGui import (
qApp,
)
from ..ui import BaseUI
from ..page import Page, Glyph
class GUIProxy(QObject, BaseUI):
unknownGlyph = signal([Glyph, bool, bool])
pageChanged = signal([Page])
def __init__(self):
super().__init__()
self.help_queue = Queue()
def turn_page(self, page):
self.pageChanged.emit(page)
def process_events(self):
qApp.processEvents()
def ask_for_help(self, unknown_glyph):
self.unknownGlyph.emit(unknown_glyph, self.last_style.bold, self.last_style.italic)
return self.receive_help()
def give_help(self, *args):
self.help_queue.put(args)
def receive_help(self):
return self.help_queue.get()

View file

@ -17,35 +17,22 @@
import itertools import itertools
from glob import glob from glob import glob
from os import path from os import path
from queue import Queue
from PyQt4.QtCore import (
signal,
slot,
QThread,
)
from PyQt4.QtGui import (
qApp
)
from .. import formatting from .. import formatting
from ..image import Image from ..image import Image
from ..page import Page, Glyph, Space from ..page import Page, Space
from ..glyphdb import GlyphDB, SPACE, NEWLINE from ..glyphdb import GlyphDB, SPACE, NEWLINE
class OCREngine(QThread): class OCREngine(object):
SPACE_WIDTH = 15 SPACE_WIDTH = 15
unknownGlyph = signal([Glyph, bool, bool])
pageChanged = signal([Page])
def __init__(self, dirname, skip=0, limit=None, quit=False, output_format='text'): def __init__(self, dirname, ui, skip=0, limit=None, output_format='text'):
super().__init__() super().__init__()
self.dirname = dirname self.dirname = dirname
self.ui = ui
self.filenames = sorted(glob(path.join(dirname, '*.png')))[skip:skip + limit if limit else None] self.filenames = sorted(glob(path.join(dirname, '*.png')))[skip:skip + limit if limit else None]
self.glyphdb = GlyphDB(path.join(self.dirname, 'glyphdb.pickle')) self.glyphdb = GlyphDB(path.join(self.dirname, 'glyphdb.pickle'))
self.help_queue = Queue()
self.quit = quit
self.output_format = output_format self.output_format = output_format
self.last_style = (False, False, (255, 255, 255)) # FIXME get rid of hardcoded value self.last_style = (False, False, (255, 255, 255)) # FIXME get rid of hardcoded value
@ -55,15 +42,10 @@ class OCREngine(QThread):
def load_page(self, filename): def load_page(self, filename):
return Page(Image.fromfile(filename), filename) return Page(Image.fromfile(filename), filename)
def run(self):
self.recognize()
if self.quit:
qApp.quit()
def recognize(self): def recognize(self):
for filename in self.filenames: for filename in self.filenames:
page = self.load_page(filename) page = self.load_page(filename)
self.pageChanged.emit(page) self.ui.turn_page(page)
page_text = self.recognize_page(page) page_text = self.recognize_page(page)
print(page_text) print(page_text)
with open(filename + self.output_format.suffix, 'w') as page_text_file: with open(filename + self.output_format.suffix, 'w') as page_text_file:
@ -80,24 +62,15 @@ class OCREngine(QThread):
yield NEWLINE yield NEWLINE
def recognize_glyph(self, glyph): def recognize_glyph(self, glyph):
qApp.processEvents() self.ui.process_events()
if isinstance(glyph, Space): if isinstance(glyph, Space):
return SPACE return SPACE
try: try:
glyph_data = self.glyphdb[glyph] glyph_data = self.glyphdb[glyph]
except KeyError: except KeyError:
text, bold, italic = self.ask_for_help(glyph) text, bold, italic = self.ui.ask_for_help(glyph)
glyph_data = self.glyphdb.add_glyph(glyph, text, bold, italic) glyph_data = self.glyphdb.add_glyph(glyph, text, bold, italic)
self.last_style = glyph_data.style self.last_style = glyph_data.style
return glyph_data return glyph_data
def ask_for_help(self, unknown_glyph):
self.unknownGlyph.emit(unknown_glyph, self.last_style.bold, self.last_style.italic)
return self.receive_help()
def give_help(self, *args):
self.help_queue.put(args)
def receive_help(self):
return self.help_queue.get()

View file

@ -59,9 +59,9 @@ class MainWindow(QMainWindow):
self.glyphEdit.glyphEntered.connect(self.unknownGlyphEntered) self.glyphEdit.glyphEntered.connect(self.unknownGlyphEntered)
self.glyphEdit.glyphEntered.connect(self.pageScene.clearHighlight) self.glyphEdit.glyphEntered.connect(self.pageScene.clearHighlight)
self.glyphEdit.glyphEntered.connect(self.glyphDBEdit.updateData) self.glyphEdit.glyphEntered.connect(self.glyphDBEdit.updateData)
ocr.pageChanged.connect(self.pageScene.setPage) ocr.ui.pageChanged.connect(self.pageScene.setPage)
ocr.pageChanged.connect(self.showPageTitle) ocr.ui.pageChanged.connect(self.showPageTitle)
ocr.unknownGlyph.connect(self.unknownGlyph) ocr.ui.unknownGlyph.connect(self.unknownGlyph)
self.page.setFocusProxy(self.glyphEdit) self.page.setFocusProxy(self.glyphEdit)
layout = QVBoxLayout(centralWidget) layout = QVBoxLayout(centralWidget)

25
pixelocr/ui.py Normal file
View file

@ -0,0 +1,25 @@
# Copyright (C) 2014 Andrey Golovizin
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class BaseUI(object):
def ask_for_help(self, unknown_glyph):
raise NotImplementedError
def turn_page(self, page):
pass
def process_events(self):
pass