#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# main.py
#
# Copyright 2009-2010 Sandro Mani <manisandro@gmail.com>
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import gtk
import gtkspell
import os
import subprocess
import tempfile
import config
import dialogs
import drawingarea
#import recognize
class gimagereader:
def __init__(self,builder,filename=None):
self.window = builder.get_object("main")
if not self.window:
return
self.window.connect("destroy", gtk.main_quit)
# Some variables
self.builder=builder
self.status=builder.get_object("status_msg")
self.output=builder.get_object("output")
self.curdir=None
self.filename=None
self.lang_count=0
self.lang=None
self.spellchecker=None
self.append_mode=0
# Complete the GUI with some widgets
self.outbuffer=gtk.TextBuffer(None)
self.output.set_buffer(self.outbuffer)
self.langcombo = gtk.combo_box_new_text()
builder.get_object("tb_language").add(self.langcombo)
self.langcombo.show()
self.langcombo.set_sensitive(False)
self.langcombo.connect('changed', self.set_language)
self.appendcombo = gtk.combo_box_new_text()
builder.get_object("tb_appendmode").add(self.appendcombo)
self.appendcombo.append_text("At cursor")
self.appendcombo.append_text("At end")
self.appendcombo.append_text("Replace")
self.appendcombo.set_active(0)
self.appendcombo.show()
self.appendcombo.connect("changed", self.set_append)
self.drawingarea=drawingarea.drawingarea(builder)
# Connect signals
dic = { "on_tb_openimg_clicked" : self.open_image,
"on_tb_zoomin_clicked" : (self.drawingarea.zoom,+0.1),
"on_tb_zoomout_clicked" : (self.drawingarea.zoom,-0.1),
"on_tb_bestzoom_clicked" : self.drawingarea.zoom_fit,
"on_tb_resetzoom_clicked" : self.drawingarea.zoom_reset,
"on_tb_lrotate_clicked" : (self.drawingarea.rotate,+90),
"on_tb_rrotate_clicked" : (self.drawingarea.rotate,-90),
"on_tb_doocr_clicked" : self.doocr,
"on_menu_open_activate" : self.open_image,
"on_menu_config_activate" : self.show_config,
"on_menu_zoomin_activate" : (self.drawingarea.zoom,+0.1),
"on_menu_zoomout_activate" : (self.drawingarea.zoom,-0.1),
"on_menu_bestzoom_activate" : self.drawingarea.zoom_fit,
"on_menu_resetzoom_activate" : self.drawingarea.zoom_reset,
"on_menu_lrotate_activate" : (self.drawingarea.rotate,+90),
"on_menu_rrotate_activate" : (self.drawingarea.rotate,-90),
"on_menu_about_activate" : self.about,
"on_btn_save_clicked": self.out_save,
"on_tb_clearout_clicked" : self.clearoutput,
"output_sel_change" : self.outputselchange,
"on_btn_stripcrlf_clicked" : self.stripcrlf,
"gtk_main_quit" : gtk.main_quit }
builder.connect_signals(dic)
# Load other modules
self.window.show()
self.conf=config.config(builder)
if not self.conf.valid:
self.window.connect('event-after', gtk.main_quit)
else:
self.load_dictionaries()
if filename==None:
self.status.set_text("Open an image to begin...")
self.curdir=os.path.expanduser("~")
else:
self.load_image(filename)
### Open and load images ###
def open_image(self,widget):
filename=dialogs.open_image(self.curdir,self.window)
if filename!="":
self.load_image(filename)
def load_image(self,filename):
self.status.set_text("Loading '"+filename+"'...")
if not self.drawingarea.load(filename):
dialogs.error_dialog('Failed to load image','The file might not be an image or be corrupted.',self.window)
if self.filename==None:
self.status.set_text("Open an image to begin...")
else:
self.status.set_text("Drag a rectangle around the area to recognize, then press 'Recognize'...")
return
self.filename=filename
self.curdir=os.path.dirname(self.filename)
self.window.set_title(os.path.basename(self.filename)+" - gImageReader")
for obj in ("tb_zoomin", "tb_zoomout", "tb_bestzoom", "tb_resetzoom", "tb_rrotate", "tb_lrotate",
"menu_zoomin", "menu_zoomout", "menu_bestzoom", "menu_resetzoom", "menu_rrotate", "menu_lrotate"):
self.builder.get_object(obj).set_sensitive(True)
self.langcombo.set_sensitive(True)
self.clearoutput()
self.status.set_text("Drag a rectangle around the area to recognize, then press 'Recognize'...")
### Recognition ###
def set_append(self,widget):
i = widget.get_active()
if i==-1:
return
self.append_mode=i
def doocr(self,widget):
self.window.set_sensitive(False)
self.status.set_text('Recognizing area, please wait...')
if file==None or os.path.isfile(self.filename)==False:
dialogs.error_dialog('Failed to perform recognition',"The source file '"+self.filename+"' could not be found.",self.window)
self.window.set_sensitive(True)
self.status.set_text("Drag a rectangle around the area to recognize, then press 'Recognize'...")
return
# Create cropped file
sel=self.drawingarea.osel
temptif=tempfile.mkstemp('.tif','tmpocr')[1];
if sel[2]<0:
sel[0]+=sel[2]
sel[2]*=-1
if sel[3]<0:
sel[1]+=sel[3]
sel[3]*=-1
crop=str(int(sel[2]))+"x"+str(int(sel[3]))+"+"+str(int(sel[0]))+"+"+str(int(sel[1]))
p=subprocess.Popen([self.conf.conv_path+"convert",self.filename,"-depth","8","-compress","none","-rotate",str(self.drawingarea.angle),"-crop",crop,temptif], shell=False, stderr=subprocess.PIPE)
out=p.communicate()
if p.returncode!=0 or out[1]!='':
dialogs.error_dialog('Failed to perform recognition',"convert returned:\n"+out[1],self.window)
self.window.set_sensitive(True)
self.status.set_text("Drag a rectangle around the area to recognize, then press 'Recognize'...")
return
# Recognize
tmptxt=tempfile.mkstemp('.txt','tmpocr')[1];
p=subprocess.Popen([self.conf.tess_path+"tesseract",temptif,tmptxt[:-4],"-l",self.lang], shell=False, stderr=subprocess.PIPE)
out=p.communicate()
os.remove(temptif)
if p.returncode!=0 or (out[1]!='' and out[1]!="Tesseract Open Source OCR Engine\n"):
dialogs.error_dialog('Failed to perform recognition',"tesseract returned:\n"+out[1]+"\nThis probabily is a bug in tesseract, retry using an image at a different resolution or by varying the selected region.",self.window)
self.window.set_sensitive(True)
self.status.set_text("Drag a rectangle around the area to recognize, then press 'Recognize'...")
return
outfile = open(tmptxt, "r")
if self.append_mode==0: #at cursor (selection)
iters=self.outbuffer.get_selection_bounds()
if len(iters)!=0:
self.outbuffer.delete(iters[0],iters[1])
self.outbuffer.insert_at_cursor(outfile.read())
elif self.append_mode==1: #at end
self.outbuffer.insert(self.outbuffer.get_end_iter(),outfile.read())
elif self.append_mode==2: #replace
self.outbuffer.set_text(outfile.read())
outfile.close()
os.remove(tmptxt)
if self.spellchecker!=None:
self.spellchecker.recheck_all()
self.builder.get_object("outputbox").show()
self.builder.get_object("hpane").set_position(-1)
self.status.set_text("Drag a rectangle around the area to recognize, then press 'Recognize'...")
self.window.set_sensitive(True)
def outputselchange(self,widget,event):
self.builder.get_object("btn_stripcrlf").set_sensitive(self.outbuffer.get_has_selection()==True)
def stripcrlf(self,widget):
self.output.set_editable(False)
iters=self.outbuffer.get_selection_bounds()
if len(iters)==0:
return #should not happen
txt=self.outbuffer.get_text(iters[0],iters[1])
txt=txt.replace("\n"," ").replace(" "," ")
self.outbuffer.delete(iters[0],iters[1])
self.outbuffer.insert_at_cursor(txt)
self.output.set_editable(True)
self.builder.get_object("btn_stripcrlf").set_sensitive(False)
def out_save(self,widget):
savefile=dialogs.save_text(self.curdir)
if savefile=="":
return
try:
outfile = open(savefile, 'w')
outfile.write(self.outbuffer.get_text(self.outbuffer.get_start_iter(),self.outbuffer.get_end_iter()))
outfile.close()
except:
dialogs.error_dialog("Failed to save output","Check that you have writing permissions in the selected folder.",self.window)
def clearoutput(self,widget=None):
self.outbuffer.set_text("")
self.builder.get_object("outputbox").hide()
### Set and detect languages ###
def show_config(self,widget):
self.conf.show()
self.load_dictionaries()
def load_dictionaries(self):
for i in range(0,self.lang_count):
self.langcombo.remove_text(0)
self.lang_count=0
for lang in (("eng","English"),("fra","French"),("deu","German"),("ita","Italian"),("spa","Spanish"),("nld","Dutch")):
if os.path.isfile(self.conf.dict_path+lang[0]+".unicharset"):
self.lang_count+=1
self.langcombo.append_text(lang[1])
if self.lang_count==0:
if dialogs.question_dialog("No dictionaries found","Do you want to correct the configuration? (Pressing 'No' will quit the program.)",self.window):
self.conf.show()
else:
self.window.connect('event-after', gtk.main_quit)
else:
self.langcombo.set_active(0)
def set_language(self,widget):
list=widget.get_model()
i=widget.get_active()
if i==-1:
return
for lang in (("eng","English","en_US"),("fra","French","fr_FR"),("deu","German","de_DE"),("ita","Italian","it_IT"),("spa","Spanish","es_ES"),("nld","Dutch","nl_NL")):
if list[i][0]==lang[1]:
self.lang=lang[0]
if self.spellchecker!=None:
gtkspell.Spell.detach(self.spellchecker)
self.spellchecker=None
try:
self.spellchecker=gtkspell.Spell(self.output,lang[2])
except:
pass
### About ###
def about(self,widget):
response = self.builder.get_object("aboutdialog").run()
self.builder.get_object("aboutdialog").hide()