import os
from tkinter import *
from tkinter.filedialog import *
from  PIL import Image , ImageTk"""
注意:*** 本程序并不保证 box 数据处理得正确,仅供参考.****** 也不是JAVA那个编辑器的替代,这里不处理tiff图片 ****** 没有什么新技术 ****** 没有详细了解 tesseract 的种种规范 ***
#----------------------------------------------
本程序的主要目标
1,可以调整和保存 box 的坐标值。2,呈现 一张jpg或者png图片 对应的 boxfile 。#------------------------------------使用方法-----------------------1 用按钮「浏览/打开」打开图片文件。关联的 .box文件(同名,不同后缀)会自动打开。若找不到相关box文件,有提示信息。程序把box 文件里面的字符和坐标信息读进内部字典 {boxes_data}2 在「修改」输入框输入文字,用于修改ocr识别的字符。按下「替换」按钮,将会改变 {boxes_data} 对应的字符。真正输出一个更改过的文件,要按最下边的「保存」按钮,来把内部字典 {boxes_data}里面的信息写入文件。「 y_什么.box」注* 删除、隐藏,的box 不写入文件3 「字」输入框是用来查找某个识别字符用的。(当然,有可能是“错误”的字符。)通过按下面的「⚲」按钮查找。查找会改变右边图片的显示,故按钮「▢」是用来恢复原来的显示。go 是按canvas绘图id查看。因为本app做了些预备动作...所以都是以6开始。4 拉动滚动条观看图片“隐藏”的部分。也可以用鼠标中间滚轮在图片上,滚动。按住ctrl + 滚轮,可以调整水平方向的显示。<---->5 esc键: 显示或者隐藏 蓝色十字架6 ctrl+鼠标左键拖动 创建新的box7 鼠标左键双击一个box,它将跟着鼠标走,然后到某个地方,点击,就停下8 修改 box 坐标,可以点击滑条前进路上一点点调节,或者按着拖动它。下面几个按钮 [A] /A/ <A> 意味着选择调节x1,y1| 还是|x2,y2; /平移/;<缩小放大>-----------------------------
# 如果想把frame0 搬到frame7,那么请自己动手 ……
"""
root=Tk()
#---------------------------------
East_of_left_part=600 #右边 CANVAS 的开始边界root.wm_title("OCR调整box")
root.wm_minsize(East_of_left_part,600) #设置窗口最小时候的大小
#  总的窗口sizetopwindow_width=1200
topwindow_height=700
root.geometry('%dx%d+%d+%d'%(topwindow_width,topwindow_height,200,100))
root.resizable(width=True, height=False)#-------------------## 基本坐标frame_point_x=[[0,135,466,East_of_left_part],              ##  y0 :y1        第0棑 的分块(横坐标)(配 y0 )[0,180,400,East_of_left_part],         ##  y1 :y2        第1棑 的分块[0,380,East_of_left_part],             ##  y2 :y3        第2棑 的分块[0,East_of_left_part],                 ##  y3 :y4        第3棑 的分块[East_of_left_part ,topwindow_width] ,  ##  y0 :y4:       右边 图片区]frame_point_y=[0,300,385,topwindow_height-50,topwindow_height]## frame_diagonals:各个frame的 坐标列表
frame_diagonals=[]
for i in range(4):match_a=[(x,frame_point_y[i])  for x in frame_point_x[i][:-1]]match_b=[(x,frame_point_y[i+1])for x in frame_point_x[i][1:]]frame_diagonals += tuple(zip(match_a,match_b))frame_diagonals += tuple(zip([East_of_left_part,topwindow_width],[0,topwindow_height])),
#--------def get_width_height_lefttop_of_frame(fr_No):p1,p2=frame_diagonals[fr_No]w=p2[0] - p1[0]h=p2[1] - p1[1]return w,h,p1
#-----------
frm_obj={}
def frm_predo(fr_No): ## 作frame 但还没显示frw,frh,p1=get_width_height_lefttop_of_frame(fr_No)obj= Frame(root,width = frw, height = frh)frm_obj[fr_No]=obj  ## 记录在 frm_obj 字典for f in range(10):frm_predo(f)
#-----
def place_frm(fr_No): ## 把tk"组件"摆放出来obj=frm_obj[fr_No]px,py=get_width_height_lefttop_of_frame(fr_No)[2] # frw ...0,   frh ...1 ,  p1 ...2obj.place(x=px,y=py)#---------------------------------------------公共数据部分------------------------------------------------------------------
# boxes_data 一个记录字符box坐标的字典。在打开box文件后填写内容,现在提前说一下。
#----------------------a_z=[0,0]    # 绘图id [第一个:最后一个] (box序号)注:第一个并不一定是1(1可能是指底图)
boxes_data={}
using   = 1     # box 状态。
waiting = 0      # box 状态。“删除”后的状态。
boxes_been_kiled=[] # 记录被删除的box id,垃圾回收
page_num=0 # 图片内部编号
imge_w,imge_h=0,0 # 图片尺寸。「打开图片时,重新赋值。」
#---------------------------------------------------------------------------------------#-----------------------------------下面是各个框架的填写--------------------------------
#// frame_0 /// frame_0
"""\
1, 显示选择的box识别的字符。
2, 提供修改字符的输入框。
------------------------------------------------------------------"""
fobj0=frm_obj[0]
w0,h0,pf01=get_width_height_lefttop_of_frame(0)#-----------变量----
frame0_char_from_data= StringVar()
frame0_char_to_data= StringVar()
frame0_box_id =  StringVar()def frame0_box_id_get():it = 0 if frame0_box_id.get()=="" else int(frame0_box_id.get())return it
def frame0_box_id_set(n):frame0_box_id.set("" if n==0 else str(n))#-------------
data_char_changed_back =[] # 备份 (修改字符)def frame0_correct4char( ):char_back= frame0_char_from_data.get()change= frame0_char_to_data.get().strip()if change=="":return  #没有输入 (要修改成的字符),则不做下面的工作.it = frame0_box_id_get()if it != -1 :#   # 修改字符 条件'''step 1'''frame0_char_from_data.set(change) #修改上面的ocr显示frame0_char_to_data.set("")       #输入框 清空'''step 2'''boxes_data[it][0]=change #修改字典里的dataframe6_show_MSG .set('字典改写字符 box: '+str(it)+" "+change) #显示消息data_char_changed_back.append((it,char_back)) # 备份#显示 box 区域 对应的 OCR 字符
def  frame0_pass_chr_to_chrTEXT():frame0_char_to_data.set(frame0_char_from_data.get())frame0_ocr_label=Label(fobj0,text="ocr ") ;     frame0_ocr_label.place(x=10,y=30)
frame0_box_id_label= Label(fobj0,textvariable= frame0_box_id,width=6,) ;     frame0_box_id_label.place(x=30,y=30)
frame0_char_label=Label(fobj0,font=("Courtier",22), textvariable=frame0_char_from_data,bg="white",fg="green",width=5) ;frame0_char_label.place(x=10,y=50)
frame0_trButton=Button(fobj0,text = '↓↓',command = frame0_pass_chr_to_chrTEXT,width=2);frame0_trButton.place(x=40,y=94)#输入字符的”Entry"组件。(修改 box 圈住的字符)
def iframe0_correct4char(e):frame0_correct4char()
frame0_correct_label=Label(fobj0,text="修改");frame0_correct_label.place(x=10,y=130)
frame0_chrTEXT=Entry(fobj0,font=("Courtier",26),textvariable=frame0_char_to_data,width=5); frame0_chrTEXT.place(x=10,y=150)
frame0_chrTEXT.bind("<Return>",iframe0_correct4char)
frame0_chrTEXT.bind("<KP_Enter>",iframe0_correct4char)#----------
def change_char():#frame6_show_MSG.set("你点击了 replace 的按钮")frame0_correct4char()def undo_boxfile():#frame6_show_MSG.set("你点击了 undo 的按钮") #if data_char_changed_back !=[]:it,ch= data_char_changed_back.pop()boxes_data[it][0]=chframe6_show_MSG.set('字典改写字符 box: '+str(it)+" "+ch)frame0_udButton=Button(fobj0,text = 'undo',command = undo_boxfile);frame0_udButton.place(x=5,y=202)frame0_rpButton=Button(fobj0,text = '替换',command = change_char,);frame0_rpButton.place(x=60,y=202)#-----------------------
def frame0_change_ocr_show(it):ch=boxes_data[it][0]frame0_char_from_data.set(ch)frame0_box_id_set(it)#// frame_1
"""\
调整box 时的显示窗口。
滑条合二为一: 当按下切换按钮时,分别用来调节 X1<>X2,Y1<>Y2.
------------------------------------------------------------------------------------------------"""fobj1=frm_obj[1]
w1,h1,pf1_1=get_width_height_lefttop_of_frame(1)frm1_scale_12=IntVar() # 或者1 或者2 -- 分别代表着x1y1,x2y2。
frm1_scale_12.set(1)#  放大/调整 box 大小的地方---------
frame1_adjust_box=Canvas(fobj1,bg = '#DEEE84',width = w1, height =h1*0.7+8)
frame1_adjust_box.place(x=0,y=h1*0.05)# x 控制条-----------
"""控制条直接改变的是fram9里的box,并不是frame1的显示"""
def frame1_x1x2scale(e):"""获取当前 box 坐标"""e=int(e)it=frame0_box_id_get()if  boxes_data.get(it,None)==None:returnelse:x1,y1,x2,y2= boxes_data[it][1]"""调整坐标"""if frm1_scale_12.get()==1: # left & topif x1+e < x2 :x1+= eframe4_Xwest.set(x1)elif  frm1_scale_12.get()==2:if x2 + e >x1:x2 += eframe4_Xeast.set(x2)elif  frm1_scale_12.get()==3: #平移x1 += ex2 += eframe4_Xwest.set(x1)frame4_Xeast.set(x2)elif  frm1_scale_12.get()==4: #缩小,但是缩小到宽度为零?另外负数缩小=扩大?if x1 + 2*e < x2 :x1 += ex2 -= eframe4_Xwest.set(x1)frame4_Xeast.set(x2)elif  frm1_scale_12.get()==0:if x1 - 2*e < x2 :x1 -= ex2 += eframe4_Xwest.set(x1)frame4_Xeast.set(x2)if x2>=x1:frame4_Xwidth.set(x2 -x1)"""改变边框"""box=[x1,y1,x2,y2]frame9_change_char_border(it,box)
#------------frame1_BOXleftScale=Scale(fobj1, # 左边from_ = -150,         #设置最小值to = 150,             #设置最大值resolution =1,       #设置步距值orient = HORIZONTAL,   #设置水平方向width=10,#sliderrelief=FLAT,sliderlength=20, #默认是30length=320,font=("Arial",8),#relief=FLAT,#label="x1",command=frame1_x1x2scale)
frame1_BOXleftScale.place(x=0,y=frame_point_y[1]-50)def when_mouse_leave_scale(e):frame1_BOXleftScale.set(0)frame2_BOXtopScale.set(0)if frame0_box_id_get() !=0:frame2_alter_box_coordinate()frame1_BOXleftScale.bind("<Leave>",when_mouse_leave_scale)def refresh_ocr_show(it): #更新显示if it not in boxes_data: return # 可能在删除全部的box后,显示有些问题frame0_change_ocr_show(it)put_img2_canvas(it)put_coordinates_from_dict_to_entrys(it)
#// frame_2
"""\
这一块用于调节某字符的盒子位置竖直偏移量,和高度。
----------------------------------------------------------------------"""
fobj2=frm_obj[2]
w2,h2,pf2_1=get_width_height_lefttop_of_frame(2)
onestep=1
#--------#--------滑条def frame2_y1y2scale(e):"""获取当前 box 坐标"""e=int(e)it=frame0_box_id_get()if  boxes_data.get(it,None)==None:returnelse:x1,y1,x2,y2= boxes_data[it][1]"""调整坐标"""if frm1_scale_12.get()==1:if y1+ e < y2 :y1+= eframe2_Xnorth.set(y1)elif  frm1_scale_12.get()==2:if y2 + e >y1:y2 += eframe2_Xsouth.set(y2)elif  frm1_scale_12.get()==3: #平移y1 += ey2 += eframe2_Xnorth.set(y1)frame2_Xsouth.set(y2)elif  frm1_scale_12.get()==4: #缩小,但是缩小到宽度为零?另外负数缩小=扩大?if y1 + 2*e < y2 :y1 += ey2 -= eframe2_Xnorth.set(y1)frame2_Xsouth.set(y2)elif  frm1_scale_12.get()==0:if y1 - 2*e < y2 :y1 -= ey2 += eframe2_Xnorth.set(y1)frame2_Xsouth.set(y2)if y2>=y1:frame2_Xheight.set(y2 -y1)"""改变边框"""box=[x1,y1,x2,y2]frame9_change_char_border(it,box)frame2_BOXtopScale=Scale(fobj2,from_ = -100  ,      #设置最小值to = 100    ,         #设置最大值resolution = 1,       #设置步距值orient = VERTICAL,  #设置方向width=10,sliderlength=20,length=220,relief=FLAT,font=("Arial",8),#label="y1",command=frame2_y1y2scale)
frame2_BOXtopScale.place(x=10,y=15)
frame2_BOXtopScale.bind("<Leave>",when_mouse_leave_scale)#---- 标签
base_y=h2/5
dy=25
Label(fobj2,text="y1").place(x=w2/2+46,y=base_y+dy+1)
Label(fobj2,text="y2").place(x=w2/2+46,y=base_y+dy*3+1)
#------------ 纵坐标 输入框 [y1,h,y2] -----frame2_Xnorth=IntVar()
frame2_Xsouth=IntVar()
frame2_Xheight=IntVar()def accept_coor(e): # 输入的坐标,要求为数字if str(frame2_Xnorth.get()).isdigit() and  str(frame2_Xsouth.get()).isdigit() and str(frame4_Xwest.get()).isdigit() and  str(frame4_Xeast.get()).isdigit():frame2_alter_box_coordinate()#----------------
fr2y1Et=Entry(fobj2,width= 6,textvariable=frame2_Xnorth);fr2y1Et.place(x=w2/2-10,y=base_y+dy)
Entry(fobj2,width=6,bg="#7EE8E4",textvariable=frame2_Xheight).place(x=w2/2-10,y=base_y+dy*2)
fr2y2Et=Entry(fobj2,width= 6,textvariable=frame2_Xsouth);fr2y2Et.place(x=w2/2-10,y=base_y+dy*3)
#---------------------
fr2y1Et.bind("<Return>",accept_coor)
fr2y2Et.bind("<Return>",accept_coor)def frame2_set_box_coordinate_info(box_data_cvs_str):frame2_Xnorth.set(box_data_cvs_str[1])frame2_Xsouth.set(box_data_cvs_str[3])v=str( int(box_data_cvs_str[3]) - int(box_data_cvs_str[1]) )frame2_Xheight.set(v)
#-------------------def reset_scales():#frame1_BOXleftScale.set(0)frame2_BOXtopScale.set(0)#----------最新坐标值--
def getmostnewco():y1=frame2_Xnorth.get()y2=frame2_Xsouth.get()x1=frame4_Xwest.get()x2=frame4_Xeast.get()return x1,y1,x2,y2#-------- 修改坐标值 to data-----def frame2_alter_box_coordinate():frame6_show_MSG .set(" “修改坐标”")reset_scales()"""修改数据字典"""if boxes_data !={}:box = getmostnewco() # new data from entrys to box_datait=frame0_box_id_get()if it <a_z[0]: returnboxes_data[it][1]=boxrefresh_ocr_show(it)# 改变边框显示#frame9_change_char_border(it,box) # ?有必要吗
#-----------------------------------------
frame2_adapt_button=Button(fobj2,text="修改坐标",relief=RIDGE, #RAISED SUNKEN GROOVE RIDGEcommand=frame2_alter_box_coordinate)frame2_adapt_button.place(x=10,y=h2-50)#/ frame_3 /
"""\
这一块用于保存box文件。生成box文件(在缺乏时)
---------------------------------------------------------------"""
fobj3=frm_obj[3]
w3,h3,pf3_1=get_width_height_lefttop_of_frame(3)
#-------#/ frame_4 /
"""\
这一块用于调节某字符的box水平偏移量,和宽度。
显示:查找信息
---------------------------------------------------------------"""#-------------------横坐标 输入框
fobj4=frm_obj[4]
w4,h4,pf4_1=get_width_height_lefttop_of_frame(4)dx_w4=w4//4 # 布放组件的 位置偏移量frame4_Xwest=IntVar()
frame4_Xwidth=IntVar()
frame4_Xeast=IntVar()fr4x1Et=Entry(fobj4,width= 6,textvariable=frame4_Xwest);fr4x1Et.place(x=20,y=5)
Entry(fobj4,width=6, textvariable= frame4_Xwidth, bg="#7EE8E4").place(x=dx_w4*1+20,y=5)
fr4x2Et=Entry(fobj4,width=6,textvariable=frame4_Xeast);fr4x2Et.place(x=dx_w4*2+20,y=5)
#-------------
fr4x1Et.bind("<Return>",accept_coor)
fr4x2Et.bind("<Return>",accept_coor)#----
def frame4_set_box_coordinate_info(box): # from "box_data" to entryframe4_Xeast.set(box[2])frame4_Xwest.set(box[0])v=str( int(box[2]) - int(box[0]) )frame4_Xwidth.set(v)
#--------------------- 标签Label(fobj4,text="x1",bg="#BFBFBF").place(x=0,y=6)
Label(fobj4,text="x2",bg="#BFBFBF").place(x=dx_w4*3+20,y=6)
#-----------------------------------------模式选择
def frame4_adjm_shift():#frame2_alter_box_coordinate() #在切换时,改变了box_data 字典的数据。没有采取额外的存储空间来保存这些改变。v=frm1_scale_12.get()         #在滑动时,却没有改变字典数据。(现已用鼠标离开 scale 时,捆绑事件,修改坐标)if v==1:s="调整左边界/上边界"elif v==2:s="调整右边界/下边界"elif v==3:s="平移"#elif v==4:#s="缩小"elif v==0:s="扩大"frame6_show_MSG .set("  "+s)frame1_BOXleftScale.set(0) #复位; 数值滑条frame2_BOXtopScale.set(0)
#--
#relief=FLAT, #RAISED SUNKEN GROOVE RIDGE
frame4_adjmode_button_1=Radiobutton(fobj4,text="「A",variable=frm1_scale_12,value=1 , #indicatoron=0,highlightcolor="#67EC29",width=4,command=frame4_adjm_shift)
frame4_adjmode_button_2=Radiobutton(fobj4,text="A」",variable=frm1_scale_12,value=2 ,#indicatoron=0,highlightcolor="#67EC29",width=4,command=frame4_adjm_shift)
frame4_adjmode_button_12=Radiobutton(fobj4,text="/A/",variable=frm1_scale_12,value=3 ,#indicatoron=0,highlightcolor="#67EC29",width=4,command=frame4_adjm_shift)
'''frame4_adjmode_button_na=Radiobutton(fobj4,text="><",variable=frm1_scale_12,value=4 ,#indicatoron=0,highlightcolor="#67EC29",width=4,command=frame4_adjm_shift)'''
frame4_adjmode_button_ex=Radiobutton(fobj4,text="<A>",variable=frm1_scale_12,value=0 ,#indicatoron=0,highlightcolor="#67EC29",width=4,command=frame4_adjm_shift)x4d5=w4/5
h4h2= h4/3+10
frame4_adjmode_button_1.place(x=0,y=h4h2)
frame4_adjmode_button_2.place(x=x4d5,y=h4h2)
frame4_adjmode_button_12.place(x=x4d5*2,y=h4h2)
#frame4_adjmode_button_na.place(x=x4d5*3,y=h4h2)
frame4_adjmode_button_ex.place(x=x4d5*4,y=h4h2)#//   frame_5
"""\
这一块用于查找某个字符的盒子,显示状态、序号,及一些简单操作。
--------------------------------------------------------------------------------------------"""
fobj5=frm_obj[5]
w5,h5,pf5_1=get_width_height_lefttop_of_frame(5)#------------- data ------
frame5_findChr_Entry_t = StringVar()
frame5_find_item_Entry_Sv = StringVar()frame5_Chars_found_inBoxes=[]def frame5_find_item_Entry_Sv_get():f5g=frame5_find_item_Entry_Sv.get().strip()it = 0 if f5g=="" else int(f5g) ;return itdef frame5_find_item_Entry_Sv_set(n):frame5_find_item_Entry_Sv.set("" if n<3 else str(n))#----- 标签和 输入 框 ---frame5_label= Label(fobj5,text = '字',).place(x=10,y=5)
frame5_findChr_Entry=Entry(fobj5,width= 4,bg="#90EE90",textvariable=frame5_findChr_Entry_t);frame5_findChr_Entry.place(x=30,y=5)frame5_label= Label(fobj5,text = '第').place(x=w5/2,y=5)
frame5_find_it_Entry=Entry(fobj5,width=6,bg="#BFBFBF",textvariable=frame5_find_item_Entry_Sv);frame5_find_it_Entry.place(x=w5/2+20,y=5)
#------------捆绑鼠标点击 事件 --(输入使能)def new_find_charinput(e):frame5_findChr_Entry_t.set("")frame5_findChr_Entry.config(state="normal")
frame5_findChr_Entry.bind("<1>",new_find_charinput)def new_find_it_input(e): #-->gotoframe5_find_item_Entry_Sv_set(0)frame5_find_it_Entry.config(state="normal")
frame5_find_it_Entry.bind("<1>",new_find_it_input)#-----------------------
def miscellaneous_show_change(it):refresh_ocr_show(it) # frame0 ,fr1 显示改变frame9_change_view(it )ps=boxes_data[it][1][:2] #调整画布视野位置frame9_view_of_posi(adjust_xy2fit_view(ps))
#----------------------box 填充---辅助画图----------------------------------
def fill_boxes_with_color(i,colo="#FFFFFF",st="gray50"):if isinstance(i,(list,tuple,set)):for j in i:frame9_Canvas.itemconfigure(j,fill=colo,stipple=st)else:frame9_Canvas.itemconfigure(i,fill=colo,stipple=st)
#----------------
def frame5_find():"""如果 frame5_findChr_Entry (结果)不为空,则查找全部 data 里面同样的字符,并在边上采用“选择”颜色。其他未选 boxes 边缘不变,内部 填充白色。如果有寻找结果,那么把结果列表的第一个box 编号 放入 frame0。""""""1 防止键盘再次输入"""frame5_findChr_Entry.config(state="disabled")"""2 查找"""m=frame5_findChr_Entry_t.get().strip()if m =="":return # 没有“需要查找的字符”则退出else:frame5_Chars_found_inBoxes.clear()for i in  boxes_data:     # 遍历字典查询if boxes_data[i][0]==m:frame5_Chars_found_inBoxes.append(i)"""3 遮盖"""if len(frame5_Chars_found_inBoxes)!=0: #在出现目标后,才进行 白板覆盖 “非是目标”unfit=set(boxes_data.keys()) - set(frame5_Chars_found_inBoxes)for i in unfit:fill_boxes_with_color(i,st="")"""4 消息、改变显示"""if frame5_Chars_found_inBoxes !=[]:frame5_find_see_Button.config(text = "▣")frame7_find_brief.set("找到「"+m+"」"+str(len(frame5_Chars_found_inBoxes))+"处"+"\n"+"box"+str(frame5_Chars_found_inBoxes[:2])+"...")change_march_state("find")it=frame5_Chars_found_inBoxes[0]miscellaneous_show_change(it)e=myevent("see")see_aim(e)else:frame7_find_brief.set("没有找到「"+m+"」")def frame5_find_see():if frame5_Chars_found_inBoxes ==[]:returnseet=frame5_find_see_Button["text"]frame5_find_see_Button.config(text = "▢" if seet !="▢" else "▣" )unfit=set(boxes_data.keys()) -set(frame5_Chars_found_inBoxes)if seet=="▢":fill_boxes_with_color(unfit)else:fill_boxes_with_color(set(boxes_data.keys()),colo="")
#---------
march_state=1
def change_march_state(m):global march_stateif m not in ["find","go"]:returnif m=="find":    bgc="#C3E2C3";fgc="#008000";march_state=0 # 用于寻找结果中 《》elif m=="go":   bgc="#BFBFBF";fgc="black";march_state=1     #用于顺序《》frame5_next_button.config(bg=bgc,fg=fgc)frame5_pre_button.config(bg=bgc,fg=fgc)def frame5_fg_preceding(d):global march_stateif march_state==1:#按顺序移动it=frame5_find_item_Entry_Sv_get()+dif it > a_z[0]and it < a_z[1]:frame5_find_item_Entry_Sv_set(it)frame5_goto()elif march_state==0:# 在查找的结果范围内移动it=frame0_box_id_get()p=frame5_Chars_found_inBoxes.index(it)#在结果列表中的位置if p+d in range(len(frame5_Chars_found_inBoxes)):it=frame5_Chars_found_inBoxes[p+d]miscellaneous_show_change(it)def frame5_fg_preceding_KB(e):if e.keysym=="Left":d=-1elif e.keysym=="Right":d=1elif e.keysym=="Up":d=-40elif e.keysym=="Down":d=40frame5_fg_preceding(d)def frame5_goto(): # 数字 和 文字 2个输入框有些冲突,可以互相清除。待完善……"""防止键盘再次输入"""frame5_find_it_Entry.config(state="disabled")it=frame5_find_item_Entry_Sv_get()change_march_state("go")frame7_find_brief.set("")if it != frame0_box_id_get() and it in range(*a_z):miscellaneous_show_change(it)#---捆绑 return 事件---------
def now_find_charinput(e):print(e)if e.keysym=="Return": frame5_find()
frame5_findChr_Entry.bind("<Return>",now_find_charinput)def now_find_it_input(e): #--print(e)if e.keysym=="Return": frame5_goto()
frame5_find_it_Entry.bind("<Return>",now_find_it_input)#-----------------------
dx_w5=w5//5
#------------------- 按钮-------------------------
#--》frame5_find
frame5_find_Button=Button(fobj5, text = '⚲',bg="#C3E2C3",fg="#008000",command = frame5_find);frame5_find_Button.place(x=0,y=h5/2)#---》frame5_find_see
frame5_find_see_Button=Button(fobj5, text = '▢',bg="#C3E2C3",fg="#008000",command = frame5_find_see);frame5_find_see_Button.place(x=dx_w5*1,y=h5/2) # ▢ ▣#------》frame5_fg_preceding
frame5_pre_button=Button(fobj5,text = '⏪',bg="#BFBFBF",command =lambda: frame5_fg_preceding(-1));frame5_pre_button.place(x=dx_w5*2,y=h5/2)#----------》frame5_goto
frame5_go_button=Button(fobj5,text = 'go',bg="#BFBFBF",command = frame5_goto,);frame5_go_button.place(x=dx_w5*3,y=h5/2)#---------》frame5_fg_preceding
frame5_next_button=Button(fobj5,text = '⏩',bg="#BFBFBF",command = lambda: frame5_fg_preceding(1));frame5_next_button.place(x=dx_w5*4,y=h5/2)#/ frame_6 /
"""\
信息区:用于显示一些信息,操作、错误、结果什么的。
------------------------------------------------------------------------"""
#frame6_show_MSG
msg_erro_color="red"
msg_warning_color="yellow"
msg_form_color="#FFE900"
msg_help_color="blue"fobj6=frm_obj[6]
w6,h6,pf6_1=get_width_height_lefttop_of_frame(6)frame6_show_MSG =  StringVar()  # 显示文字
frame6_show_MSG .set("--- you are welcome -----")
frame6_label= Label(fobj6,textvariable= frame6_show_MSG , justify=LEFT,wraplength=w6-20 )
frame6_label.place(x=10,y=10)#/ frame_7 /
"""\
1这一块用于显示字符查找结果
2 可能以后会安排一些“导出”“批量修改”等按钮
------------------------------------------------------------------"""frame9img=Nonefobj7=frm_obj[7]
w7,h7,pf7_1=get_width_height_lefttop_of_frame(7)
#--------------------------------显示查找结果
frame7_find_brief=StringVar()
Label(fobj7,textvariable=frame7_find_brief).place(x=0,y=10)#// frame_8
"""\
(1)这一块前面的输入框用于打开文件。或者输入某些命令。-------------------------------------------------------------------------------"""
# back end
def rearrange_data():global boxes_dataitco=[]for it in boxes_data:box=boxes_data[it][1]twp=restore_box_ocr_xy(box)itco.append((it,twp[:2]))#---x---itco=sorted(itco,key=lambda r:r[1][0])itco=sorted(itco,key=lambda r:r[1][1],reverse= True)itor=[i[0] for i in itco]return itor"""打开相应的 box文件, 读取坐标数据 """
boxes_ocr_data =[]    #   ocr [ct] : [char,box,num]
def open_box_file(filename):global boxes_ocr_databoxes_ocr_data.clear()#(重新打开) 清空。这里应该设置一个询问对话框,如果boxes_ocr_data非空,问要不要保存。p=filename.rfind(".")filename=filename[:p]+".box"try:sf=open(filename,"r",encoding="utf-8") #假如找得到box文件,则添加到字典for info in sf:char, *box, num = info.split(" ")boxes_ocr_data.append([char,*box,num.rstrip()])sf.close()frame6_show_MSG .set("openbox file done")frame6_label.configure(bg="#D9D9D9")except FileNotFoundError:frame6_label.configure(bg=msg_warning_color)frame6_show_MSG .set("找不到文件: "+filename)#---def boxdata2canvas_co(twopoint): # data 里面的坐标(x1,y1,x2,y2)转换成 canvas的坐标(主要是y坐标)#  data 的记录方式以靠近图片的顶部y坐标为大↑。并且 box 对角线取点 [↗] 。#  若不转换 在 PIL crop时会产生问题twopoint=[int(i) for i in twopoint]""" step1  对角2点 从[↗]  改成 [↘] 。即 对调 y1 ,y2。"""tmp=twopoint[1]twopoint[1]=twopoint[3]twopoint[3]=tmp"""step2  ⇅ 坐标转换 。中间 [-] 对称"""twopoint[1]=imge_h-twopoint[1]twopoint[3]=imge_h-twopoint[3]return  twopoint"""根据坐标数据,画出所有 box """def frame8_draw_allBoxes_into_Fr9():global a_z,usinga_z=[0,0];boxes_data.clear()while  len( boxes_ocr_data) >0:char, *box, num = boxes_ocr_data.pop(0)twopoint=boxdata2canvas_co(box)if char =="~": continue # 排除异常的 ocr boxv=frame9_Canvas.create_rectangle( *twopoint,outline = 存在_color, fill="",width = 1, tags ="be") # box图 idboxes_data[v]=[char,twopoint,num,using]n=len(boxes_data)if n !=0:a_z[0]=v-n+1a_z[1]=vnr="boxes [ " +str(a_z[0]) +":"+str(a_z[1])+" ]" #显示id信息 box[第一个:最后一个]frm9_show_bottom_1.set(nr)def restore_box_ocr_xy(p2): #  回到 box 的扫描坐标。return boxdata2canvas_co(p2)def frame0_gerchar_fromboxindex(it): # 取字符char=boxes_data[it][0]return chardef clear_all_in_frame9_fr1_canvas():if frame9_Canvas!="":frame9_Canvas.delete("all")frame1_adjust_box.delete("all")img_orient=""
o_filename =""
def frame8_Open():clear_all_in_frame9_fr1_canvas()global img_orient,o_filename,init_shape_countframe6_show_MSG .set("打开图片---")filename =askopenfilename(title='Open Image', filetypes=[('image', '*.jpg *.png *.gif *.tif')])if filename==() or filename =="": frame6_show_MSG .set("oops---"); return #取消打开(没有相关名字信息返回)o_filename=filenamefrm9_create_canvas()#创建frame_9画布img_orient=Image.open(filename)# 根据图像设置canvas滚动范围global imge_w,imge_himge_w,imge_h=img_orient.sizeframe9_Canvas["scrollregion"]=(0, 0,imge_w, imge_h)#显示图片global photo # 据说是为了不让系统垃圾回收photo=ImageTk.PhotoImage(img_orient)frame9img=frame9_Canvas.create_image(0, 0,anchor=NW,image=photo)#frame9_draw_aim_circle()canvs_draw_crosstangle(2,10,2,10)init_shape_count=frame9_Canvas.find_all()[0] #“套路”图形,如底图,十字架,圆圈之类# 消息显示frame6_label.configure(fg=msg_help_color,bg=msg_form_color)  #显示 (附颜色)消息frame6_show_MSG .set("打开: \n"+filename+"\n \n图像大小:"+str( ( imge_w,imge_h)))# 滚动条frame9_place_Scrollbar()#-----#open_box_file(filename)#-----#frame8_draw_allBoxes_into_Fr9()if a_z[1]>2:root.event_add("<<mov>>","<Left>","<Right>","<Up>","<Down>")root.bind("<<mov>>",frame5_fg_preceding_KB)#-- ---def frame8_save():# 写盘 。文件名以 new 开头if o_filename=="":returnp=o_filename.rfind("/")b=a=""if p != -1:a,b=o_filename[:p],o_filename[p+1:]p = b.rfind(".")b = b[:p]+".box"saveboxfilename= a+"/z_"+bdf=open(a+"/y_"+b,"w")it_order=rearrange_data()for i in it_order:#tg=frame9_Canvas.itemcget(i,"tags")#if tg in ["hd","nw_hd","kl"]:continue # 删除、隐藏,的box 不写入文件ch,box,num,wu=boxes_data[i]if wu == waiting: continue # 排除 -- 没有使用的 「box」twp=restore_box_ocr_xy(box)num=str(num)box=[str(i) for i in twp]box=" ".join(box)m=" ".join((ch,box,num))+"\r\n"df.write(m)df.close()frame6_show_MSG.set(saveboxfilename+"已完成")# interface----------------------------------------
fobj8=frm_obj[8]
w8,h8,pf8_1=get_width_height_lefttop_of_frame(8)frame8_openEntry=Entry(fobj8,text = "path",width= 30)
frame8_openEntry.place(x=20,y=10)frame8_browseButton=Button(fobj8,text = '浏览/打开',command =frame8_Open).place(x=300,y=10)"""存盘 """
#frame8_save_all Button
Button=Button(fobj8,text = '保存',command =frame8_save).place(x=470,y=10)#/ frame_9 / frame_9"""\____________________
这一块用于显示图片和 boxes      )
(1,先在frame8里面打开图片)
2,鼠标左击/右击 box ,边框颜色改变,对应不同box状态。(左击递进,右击退出)删除          : tag="kl": 意味着""删除"该 box, 按下del键隐藏 (无色):tag="hd"可见(红色): tag="be"选择(蓝色): tag="se"   可以修改box圈住的字符(ocr 识别结果)激活(绿色) :tag="ac"   可以修改box的坐标新添 (): tag ="nw"#----------未实现-----移动 a box:
5. shift + 左键拖拉:
--------------------------------"""#mouse_cursors 鼠标的显示形式
cursor_默认=   "arrow"       # ➚ 默认 :
cursor_移动图片="fleur"# ✥ 用于移动图片:
#cursor_复位=   "exchange" # ⮂ :
cursor_选择box=    "hand1"#用于选择box :
cursor_可见box="icon" # ⛋ :
cursor_激活box="dotbox" # ⚀ 激活box :
cursor_新box坐标=   "crosshair" # + 用于设定 新box坐标选择_color="#102AF4" # 蓝
激活_color="#63B83B"  #浅绿
存在_color="#F24157"   #红
新添_color="#FFA500"   #土黄class myevent():def __init__(self,txt="see"):self.keysym= txt
#-----------------------------人工 新添 box-------------
fr9newbox_x,fr9newbox_y,fr9newbox_it=0,0,0def frame9_decide_coord_order(u,v):if u <=v: return u,velse:return v,unewbox_create_stats=0
fr9newbox_it=0
"""control下,鼠标1《点击,移动,松开》对应着《定位、调整、完成》三部分"""
"""这个函数要在创建画布9之后使用"""
def frame9_Create_new_box_at(event):global fr9newbox_x,fr9newbox_y, fr9newbox_it,frame9_Canvas,newbox_create_stats,boxes_been_kiledx,y=win2canvas(event.x,event.y)fr9newbox_x,fr9newbox_y = x,y# 有被删的,则用之;无,则新建if  boxes_been_kiled!=[]:fr9newbox_it= boxes_been_kiled.pop()frame9_Canvas.itemconfig(fr9newbox_it,tags="nw")frame9_Canvas.tag_raise(fr9newbox_it)else:fr9newbox_it=frame9_Canvas.create_rectangle((x, y, x+1,y+1), outline="red", width=2, tags='nw')newbox_create_stats=1
def frame9_Create_new_box_alive(event):if fr9newbox_it!=0:x,y=win2canvas(event.x,event.y)frame9_Canvas.coords(fr9newbox_it,fr9newbox_x, fr9newbox_y,x,y)
def frame9_Create_new_box_settle(event):global fr9newbox_it,boxes_data,using,page_numif fr9newbox_it!=0:frame9_Canvas.itemconfigure(fr9newbox_it,width=1)if  a_z[0]==0:  a_z[0]= fr9newbox_it;page_num=0elif a_z[0]>= init_shape_count +1  : page_num=boxes_data[a_z[1]][-1] # 存在第一个字符 box 记录a_z[1]=fr9newbox_itchar="⯂"x,y=win2canvas(event.x,event.y)a,b,c,d=fr9newbox_x,fr9newbox_y,x,ya,c=frame9_decide_coord_order(a,c)b,d=frame9_decide_coord_order(b,d)#------------画得太小, 则扩大一点----(如果 ctrl B1 点击一下,那么相当于画一个默认尺寸的新box)if c-a <10:  c = a+16if d-b < 10: d = b+16frame9_Canvas.coords(fr9newbox_it,a, b,c,d)#--------------boxes_data[fr9newbox_it]=[char,[a,b,c,d],page_num,using]refresh_ocr_show(fr9newbox_it)fr9newbox_it=0 # 下一轮nr="boxes [ " +str(a_z[0]) +":"+str(a_z[1])+" ]" #显示id信息 box[第一个:最后一个]frm9_show_bottom_1.set(nr)#======== 画布底下的类似状态栏的显示 ==============================
def win2canvas(x,y):x=int(frame9_Canvas.canvasx(x)) #在canvas坐标y=int(frame9_Canvas.canvasy(y))return x,y
def frm9_is_box(event): #判断:鼠标点击de位置 ,是否点中 一个boxglobal imge_hcvs_mx, cvs_my =win2canvas(event.x, event.y)if frame9_Canvas !="":frame6_show_MSG .set("canvas坐标 x y: "+str(cvs_mx)+" , "+str(cvs_my)+"\n"+"boxfile坐标: "+str(cvs_mx)+","+str(imge_h - event.y))b1,bn= a_zif bn==0:return 'not in any box'  , "" ,""for it in range(b1,bn+1):if it in boxes_been_kiled: continue # 删掉的 box,不再讨论x1,y1,x2,y2=frame9_Canvas.bbox(it) #box item 的参数if all((cvs_mx<=x2,    cvs_mx>=x1,   cvs_my in [y1,y2])):   st=" on the border ";             breakelif all((cvs_my<=y2,   cvs_my>=y1,   cvs_my in [x1,x2])):   st=" on the border ";             breakelif all((cvs_mx<x2,    cvs_mx>x1,    cvs_my<y2,cvs_my>y1)):   st=" in the box " ;              breakelse:    st="out the box " ;         continuetypei =frame9_Canvas.type(it)if st=="out the box " and it==bn: # 搜索到最后。st='not in any box';it="";typei=""return st, it,typei"""左击,右击 不断改变box 的状态。"""  ##  隐藏 ↗可见↗选择↗激活
#=============== 送图到 frame_1def crop_amplify(abx,crop_info): # 设定 调整尺寸(canvas :: crop)abx=[int(i) for i in abx]aw,ah=abx[0]-100, abx[1]-100bw,bh=crop_info[2]-crop_info[0]+2,crop_info[3]-crop_info[1]+2if bw*ah>bh*aw:r=aw/bwelse:r= ah/bhreturn int(bw*r), int(bh*r)def put_coordinates_from_dict_to_entrys(it):box= boxes_data[it][1]frame4_set_box_coordinate_info(box) # 往几个坐标 输入框的 注入数值,并显示frame2_set_box_coordinate_info(box)def put_img2_canvas(bxit,cvs=frame1_adjust_box):crop_info= boxes_data[bxit][1]frame1_adjust_box.delete("all")#---------------"""获取截图小块"""global img_orientimx=img_orient.crop(box=tuple(crop_info))#放大abxwh=frame1_adjust_box.cget("width"), frame1_adjust_box.cget("height")amplify_size=crop_amplify(abxwh,crop_info)try:imx=imx.resize(amplify_size, resample=Image.Resampling.BILINEAR, box=None, reducing_gap=None)except AttributeError as ATBER:imx=imx.resize(amplify_size, resample=Image.BILINEAR, box=None, reducing_gap=None)#放进canvasglobal photo_little # 据说是为了不让系统垃圾回收photo_little=ImageTk.PhotoImage(imx)frame9img=frame1_adjust_box.create_image(50, 50,anchor=NW,image=photo_little)#======================
def frm9_mouse_stroke_l(event): #左击global the_moving_itreset_scales()st,it,typei=frm9_is_box(event) #消息\id\图形类型frame6_show_MSG .set("B1 "+st +typei+" "+str(it))if st !='not in any box' and typei=="rectangle":frame5_find_item_Entry_Sv.set(it)char=boxes_data[it][0] # 获取字符,if frame0_box_id_get()!=it:refresh_ocr_show(it)  #更新数据、字符等显示frame9_change_view(it) # 改变“注意力”位置#----------------------------------if frame9_Canvas.itemcget(it,"tags")=="hd":frame9_Canvas.itemconfig(it,tags="be")frame9_Canvas.tag_raise(it)elif frame9_Canvas.itemcget(it,"tags")=="be":frame9_Canvas.itemconfig(it,tags="se",outline=选择_color)#-----------------------------------elif frame9_Canvas.itemcget(it,"tags")=="nw_hd":frame9_Canvas.itemconfig(it,tags="nw")frame9_Canvas.tag_raise(it)elif frame9_Canvas.itemcget(it,"tags")=="nw":frame9_Canvas.itemconfig(it,tags="nw_se",outline=选择_color)#------------------------------------------------------elif "ac" in frame9_Canvas.itemcget(it,"tags"):frame9_Canvas.itemconfig(it,tags="be")frame9_Canvas.itemconfig(it,tags="be",outline=存在_color)a,b,c,d=frame9_Canvas.coords(it)boxes_data[it][1]=[a,b,c,d] #如果不想在移动的过程中更新坐标,那么最后一步必须要更新。the_moving_it=0def frm9_mouse_stroke_r(ev): #右击global the_moving_itst,it,typei=frm9_is_box(ev)frame6_show_MSG .set("B3 "+st +typei+" "+str(it))#print("youji",it,frame9_Canvas.itemcget(it,"tags"))reset_scales()if st !='not in any box' and typei=="rectangle":if frame9_Canvas.itemcget(it,"tags")=="se":frame9_Canvas.itemconfig(it,tags="be",outline=存在_color)elif frame9_Canvas.itemcget(it,"tags")=="be":frame9_Canvas.itemconfig(it,tags="hd")frame9_Canvas.tag_lower(it)#-----------------------------------elif frame9_Canvas.itemcget(it,"tags")=="nw_se":frame9_Canvas.itemconfig(it,tags="nw",outline=存在_color)elif frame9_Canvas.itemcget(it,"tags")=="nw":frame9_Canvas.itemconfig(it,tags="nw_hd")frame9_Canvas.tag_lower(it)#--------------elif "ac" in frame9_Canvas.itemcget(it,"tags"):frame9_Canvas.itemconfig(it,tags="be")frame9_Canvas.itemconfig(it,tags="be",outline=存在_color)a,b,c,d=frame9_Canvas.coords(it)boxes_data[it][1]=[a,b,c,d] #如果不想在移动的过程中更新坐标,那么最后一步必须要更新。the_moving_it=0meet_box=[]
def frm9_where_now(event): #显示鼠标 位置global meet_boxst,it,typei=frm9_is_box(event)if st !='not in any box' :frm9_show_bottom_3.set("□ "+str(it))frame9_Canvas.itemconfig(it,width=2)meet_box.append(it)#if event.state!=4:frame9_Canvas["cursor"]=cursor_激活boxelse:frm9_show_bottom_3.set("")for it in meet_box:frame9_Canvas.itemconfig(it,width=1)#if event.state!=4: frame9_Canvas["cursor"]=cursor_默认it_move_and_release(event)def it_move_and_release(e):if the_moving_it ==0:returnglobal the_dx,the_dy,the_it_w,the_it_hx,y = win2canvas(e.x,e.y)#a,b,c,d=boxes_data[the_moving_it][1]#w,h=c-a,d-bx1,y1 = x + the_dx,  y + the_dyx2,y2 = x1 + the_it_w,  y1 + the_it_hframe9_Canvas.coords(the_moving_it,x1,y1,x2,y2)passthe_moving_it=0
the_dx,the_dy=0,0
the_it_w,the_it_h=0,0
def frm9_box_gowith_mouse(e):# 获取 box idglobal the_moving_it, the_dx,the_dy,the_it_w,the_it_hst,it,tp= frm9_is_box(e)if it =="":returnif it < a_z[0] :return# box 随鼠标移动而移动if frame9_Canvas.itemcget(it,"tags") not in ["hd","kl"]:frame9_Canvas.itemconfig(it,tags="ac",outline=激活_color)the_moving_it =it ## 条件 !=0x,y = win2canvas(e.x,e.y)a,b,c,d=boxes_data[the_moving_it][1]the_dx,the_dy=a-x,b-ythe_it_w,the_it_h= c-a,d-b#-----------------------------------------------------+
fobj9=frm_obj[9]
w9,h9,pf9_1=get_width_height_lefttop_of_frame(9)"""基本容器"""
frame9=fobj9brink_blank=20
bottom_blank=26
frame9_scrollbar = Scrollbar(frame9) #   竖直滚动条
frame9_scrollbar_z = Scrollbar(frame9,orient=HORIZONTAL,width=10) #水平滚动条
#----------------------
def frame9_place_Scrollbar():# 滚动条 set设置时,(内部实现的计算)应该用到 1“内容滚动域”的尺寸,2 滚动条的(路径)尺寸,3 视窗的尺寸# 视窗 > 滚动域  时,可以不用滚动条 ; (视窗/内容滚动域) <1时,将得到一个比率k# 滚动条的尺寸 * k = 滑块的长度; 这样滑块长度的占比,就是 视窗 在 滚动域 上的占比。# 滑块(前沿)走过的位置ds,按照(ds/路径尺寸)百分比,改变(视窗里面的)内容的显示(位置)。px0,py0,pW,pH=frame9_Canvas["scrollregion"].split(" ")"""根据图片尺寸、frame9尺寸, 决定显示滚动"""w9=int(frame9.config()["width"][-1])if int(pH) > h9:frame9_scrollbar.place(x=1,     # 竖直y=0,width= brink_blank,height=h9-bottom_blank-brink_blank)frame9_scrollbar.config(command=frame9_Canvas.yview) #意思是 :后台相响应函数是 frame9_Canvas.yview ,( 调用它来调整Canvas视图)frame9_Canvas.config(yscrollcommand=frame9_scrollbar.set) # 反控if int(pW)>w9:frame9_scrollbar_z.place(x=1, # 水平y= h9- brink_blank- bottom_blank,width= w9,height= brink_blank)frame9_scrollbar_z.config(command=frame9_Canvas.xview)frame9_Canvas.config(xscrollcommand=frame9_scrollbar_z.set)"""容器里的画布"""canvas_w=w9
canvas_h=h9 -bottom_blank - brink_blank  # frame底边留些空白
canvas_leftop_x=20 #左边留些空白 给 scrollbar
canvas_leftop_y=0frame9_Canvas= "" # 画布9 。 global   。创建之后, 类型 为 class 'tkinter.Canvas'
#-----------改变字符边界
def frame9_change_char_border(it,box):frame9_Canvas.coords(it,*box)#-----------
"""(鼠标)调控图片滚动(视野)"""
verti_scl_pos=0
level_scl_pos=0
def frame9_move_vertical(e):global verti_scl_posif verti_scl_pos <1:if e.num==5:verti_scl_pos +=0.05if verti_scl_pos >0:if e.num==4:verti_scl_pos -=0.05frame9_Canvas.yview(MOVETO, verti_scl_pos)
def frame9_move_hori(e):global level_scl_posif level_scl_pos <1:if e.num==5:level_scl_pos +=0.05if level_scl_pos >0:if e.num==4:level_scl_pos -=0.05frame9_Canvas.xview(MOVETO, level_scl_pos)
#-----------------
def adjust_xy2fit_view(point): # 这是为了把选择的「字符」尽量放画布中心去看,所以点击会有点跳动。if frame9_Canvas=="": return pointpx,py=pointww= frame9_Canvas.cget("width")ww=int(ww)wh= frame9_Canvas.cget("height")wh=int(wh)if py < wh/2 :py=0elif py>imge_h - wh/2: py= imge_helse: py= py- wh/2if px< ww/2 :px=0elif px>imge_w - ww/2: px= imge_welse: px= px- ww/2return px,pydef frame9_view_of_posi(point): #这样一般会把字符“ 滚动”到左上角px,py=pointglobal verti_scl_pos,level_scl_poslevel_scl_pos = px/imge_wverti_scl_pos = py/imge_hframe9_Canvas.xview(MOVETO, level_scl_pos) # 滚动画布frame9_Canvas.yview(MOVETO, verti_scl_pos)def frame9_change_view(it):box=boxes_data[it][1]ps=box[:2]#frame9_view_of_posi(adjust_xy2fit_view(ps))#frame9_move_aim_circle(it) # 画圈a,b,c,d =boxcanvas_move_crosstangle(a,b,c,d)
#---------------------------------------------------------------def frame9_width_changed(e): # 适应 拉大的窗口""" 在创建canvas时设定的 width 是观察 canvas某一区域的窗口 宽度,并不是限制canvas的大小。所以可以在 canvas 创建一个大图片(尺寸超过canvas的width) #不知正确否?待考frame本身是也是一个“窗口”,假如它的尺寸大于 canvas 窗口,那么可以看到 canvas 窗口的全貌。(若是,则不必显示滚动条。)否则只能看见一部分。若调整 frame尺寸,则进行显示滚动条判断。这里尽量把两个窗口的尺寸搞成“一致”"""fr9x2=e.widthframe9width= fr9x2 - East_of_left_partframe9.config(width=frame9width) # 这里 e.widget 是 frame9 。if frame9_Canvas !="":frame9_place_Scrollbar()mm=frame9_Canvas.config()["width"]frame9_Canvas.config(width= frame9width-brink_blank)#------------------------------"""为了更好地“锁定”目标 box,方便眼睛查找"""
aimonoff=0
def see_aim(e):global aimonoffif frame9_Canvas!="" :if e.keysym=="Escape" :#it=frame9_Canvas.find_withtag("@")if aimonoff==0:frame9_Canvas.tag_raise(frame9_crosstangle_v1)frame9_Canvas.tag_raise(frame9_crosstangle_v2)frame9_Canvas.tag_raise(frame9_crosstangle_h1)frame9_Canvas.tag_raise(frame9_crosstangle_h2)aimonoff=1else:frame9_Canvas.tag_lower(frame9_crosstangle_v1)frame9_Canvas.tag_lower(frame9_crosstangle_v2)frame9_Canvas.tag_lower(frame9_crosstangle_h1)frame9_Canvas.tag_lower(frame9_crosstangle_h2)aimonoff=0elif e.keysym=="see":if aimonoff==0:frame9_Canvas.tag_raise(2)frame9_Canvas.tag_raise(3)frame9_Canvas.tag_raise(4)frame9_Canvas.tag_raise(5)aimonoff=1#------------------------
def canvs_draw_crosstangle(y1,y2,x1,x2): # 视觉定位十字架global  frame9_crosstangle_v1,frame9_crosstangle_v2,frame9_crosstangle_h1,frame9_crosstangle_h2v1_box = x1,0,x2,y1v2_box = x1,y2,x2,imge_hh1_box = 0,y1, x1,y2h2_box = x2,y1,imge_w,y2frame9_crosstangle_v1= frame9_Canvas.create_rectangle( *v1_box,outline="green",width=1, dash=(4,1,1), fill="blue" ,stipple="gray25")frame9_crosstangle_v2= frame9_Canvas.create_rectangle( *v2_box,outline="green",width=1, dash=(4,1,1), fill="blue", stipple="gray25")frame9_crosstangle_h1= frame9_Canvas.create_rectangle(*h1_box, outline="green",width=1,dash=(4,1,1), fill="blue",  stipple="gray25")frame9_crosstangle_h2= frame9_Canvas.create_rectangle(*h2_box, outline="green",width=1,dash=(4,1,1), fill="blue",  stipple="gray25")for i in [frame9_crosstangle_v1,frame9_crosstangle_v2,frame9_crosstangle_h1,frame9_crosstangle_h2]:frame9_Canvas.tag_lower(i)root.bind("<Escape>",see_aim)def canvas_move_crosstangle(x1,y1,x2,y2):#for j in [,frame9_crosstangle_h2]:v1_box = x1,0,x2,y1v2_box = x1,y2,x2,imge_hh1_box = 0,y1, x1,y2h2_box = x2,y1,imge_w,y2frame9_Canvas.coords(frame9_crosstangle_h1,*h1_box)frame9_Canvas.coords(frame9_crosstangle_h2,*h2_box)frame9_Canvas.coords(frame9_crosstangle_v1,*v1_box)frame9_Canvas.coords(frame9_crosstangle_v2,*v2_box)#------------------------------------xxxxxxxxxx-------------------------def fr9_kill_box(e):# (1)把 相关 box 信息从 <boxes_data> 搬到 <boxes_been_kiled>  ;(2)隐藏显示print("=kill box")global waiting,page_numit= frame0_box_id_get()if it !=0:boxes_been_kiled.append(it)boxes_data[it]=["⯂",[1,9,8,9],page_num,waiting]frame9_Canvas.itemconfig(it,tags="kl")frame9_Canvas.tag_lower(it)# 删除后,显示临近的boxct=1while True:if    it - ct in boxes_data: refresh_ocr_show(it- ct);breakelif  it + ct in boxes_data: refresh_ocr_show(it + ct);breakif it == 0 :break##----------------创建画布----------------------------===上面一大堆,其实是后面添加的,下面才是frm9框架------------------def frm9_create_canvas():global frame9_Canvasif frame9_Canvas!="":  v=frame9_Canvas;del vcanvas_w=w9frame9_Canvas = Canvas(frame9,bg = 'darkgreen', #画布背景颜色:深绿width=w9-brink_blank,height=canvas_h,)frame9_Canvas.place(x=canvas_leftop_x,y=canvas_leftop_y,)frame9_Canvas.event_add('<<sc>>', '<Button-4>','<Button-5>')frame9_Canvas.event_add('<<sch>>', '<Control-Button-4>','<Control-Button-5>')右击="<3>"左击="<1>"左双击="<Double-Button-1>"frame9_Canvas.bind(右击,frm9_mouse_stroke_r)frame9_Canvas.bind(左击,frm9_mouse_stroke_l)frame9_Canvas.bind(左双击,frm9_box_gowith_mouse)frame9_Canvas.bind("<Motion>",frm9_where_now)frame9_Canvas.bind("<<sc>>",frame9_move_vertical)  # 鼠标中轮的上下滚动frame9_Canvas.bind("<<sch>>",frame9_move_hori)  #frame9_Canvas.bind('<Control-Button-1>',frame9_Create_new_box_at)  #frame9_Canvas.bind("<B1-Motion>",frame9_Create_new_box_alive)  #frame9_Canvas.bind("<ButtonRelease-1>",frame9_Create_new_box_settle)  #'''画布中心 '''
centerofcanvas_x=canvas_w//2
centerofcanvas_y=canvas_h//2#----
"""画布底下2个信息显示栏"""
frm9_show_bottom_1=StringVar()
frm9_show_bottom_3=StringVar()HMNx=10
ALLy=h9-25
distance_between_HMN_M=170
distance_between_M_I=150frame9_howmanyboxes_label=Label(frame9, textvariable=frm9_show_bottom_1, fg="black",justify=LEFT)
frame9_howmanyboxes_label.place(x=HMNx, y=ALLy,anchor =NW)
frm9_show_bottom_1.set("wow")frame9_item_label=Label(frame9, textvariable=frm9_show_bottom_3, fg="black",justify=LEFT)
frame9_item_label.place(x=HMNx+distance_between_HMN_M+distance_between_M_I,  y=ALLy,anchor =NW)
frm9_show_bottom_3.set("")#===============================设定frame 的背景色=================================================
for i in [0,1,2]:frm_obj[i].configure(bg="#7F7F7F")place_frm(i)
##--------------
for i in [4]:frm_obj[i].configure(bg="#6D8C7F")place_frm(i)
for i in [3,5,8,6]:#frm_obj[i].configure(bg="#7F7F7F")place_frm(i)
#---------------------------------
frm_obj[7].configure(bg="#ADD8E6")
place_frm(7)
#---------------------------frm_obj[9].configure(bg="#EFE8BA")
frame9.place(x=600,y=0,relwidth=1)
frame9.bind("<Configure>",frame9_width_changed)
root.bind("<Delete>",fr9_kill_box)##-------------------------------------------------------------------------------------------------mainloop()

一个tesseract ocr box 文件查看toy,python相关推荐

  1. python打开一个不存在的文件报错,python中的文件操作(一)

    文件就是把一些数据存储存放起来,可以让程序下一次执行的时候直接使用,而不必重新制作一份,省时省力. python操作文件的步骤 1.将文件抽象成一个python的对象 2.对这个对象,进行读/写的动作 ...

  2. python打不开py文件查看代码,Python打不开.py文件怎么办

    python打不开.py文件的解决办法:首先进入需要打开的py文件目录,打开命令菜单:然后选择命令菜单中的[在此处打开命令窗口],并输入python文件名:**后键入回车.PV1少儿编程网-https ...

  3. python创建一个新的txt文件-如何在python中编辑文本文件并创建一个新的文本文件?...

    我有这样的文本文件:>ENST00000511961.1|ENSG00000013561.13|OTTHUMG00000129660.5|OTTHUMT00000370661.3|RNF14-0 ...

  4. jTessBoxEditor for Tesseract OCR

    jTessBoxEditor下载地址 http://vietocr.sourceforge.net/training.html 以下翻译自安装(解压)后的Readme文件 jTessBoxEditor ...

  5. 好用的Bin文件查看器,J-flash

    工作中,很多地方用到Bin文件,如编译完成后的固件和从MCU的Flash读出来的文件,这时候一个好的Bin文件查看器至关重要.经常我们用STM32自带的STM32 ST-LINK Utility可以直 ...

  6. python文件怎么看代码表_TFRecord文件查看包含的所有Features代码

    TFRecord作为tensorflow中广泛使用的数据格式,它跨平台,省空间,效率高.因为 Tensorflow开发者众多,统一训练时数据的文件格式是一件很有意义的事情,也有助于降低学习成本和迁移成 ...

  7. 使用Python解决对比出两个Excel文件中的不同项并将结果重新写入一个新的Excel文件

    使用Python解决对比出两个Excel文件中的不同项并将结果重新写入一个新的Excel文件 因为有统计成员到会情况的任务,每次汇总时都很麻烦,需要一个个对应腾讯会议导出名单的成员,然后在总表上进行标 ...

  8. python打开一个不存在的文件时-python判断文件是否存在,不存在就创建一个的实例...

    python判断文件是否存在,不存在就创建一个的实例 如下所示: try: f =open("D:/1.txt",'r') f.close() except IOError: f ...

  9. 对于python来说、一个模块就是一个文件-彻底明白Python package和模块

    python 是通过module组织代码的,每一个module就是一个python文件,但是modules是通过package来组织的. 如果我们自己写着玩,有的时候就是一两个Python文件在同级目 ...

最新文章

  1. Plugin Error
  2. 8、Kubernetes核心技术Service
  3. Burp Suite入门笔记
  4. 登录验证---过滤器(Fileter)
  5. 【干货】JS版汉字与拼音互转终极方案,附简单的JS拼音输入法
  6. 共线方程(百度百科)
  7. The Future of Compass ElasticSearch
  8. xampp配置虚拟主机
  9. Q143:FS,物质导数(Material Derivative)
  10. C语言中指针定义的字符串和数组定义的字符串的区别
  11. 微信小程序全国城市搜索(可进行城市中文拼音首字母搜索)
  12. (转)C#中 DirectoryEntry组件应用实例
  13. python字符串、数字
  14. pyvoronoi包 Failed to build pyvoronoi Installing collected packages: pyvoronoi Running setup.py
  15. 计算机系的同学应该有更高的雄心壮志
  16. Frenet坐标系与Cartesian坐标系互转(三):应用示例
  17. C++ 注释风格建议
  18. C#:实现gnome sort 侏儒排序算法(附完整源码)
  19. 线性代数——线性组合、线性空间、基底
  20. linux c开多线程算质数,C语言判断素数(质数)

热门文章

  1. 机器学习 | MATLAB实现BP神经网络newff参数设定(中)
  2. db2 reorg到底需要多少表空间
  3. Android版简历(四)
  4. Unity用代码设置图片的压缩格式(AssetImporter/TextureImporter)
  5. amap 实现获取定位功能(高德api)
  6. echarts X轴文字排列方式总结
  7. opencv 通过标定摄像头测量物体大小_激光三角测量法在工业视觉检测上的应用...
  8. js 将日期转换成时间戳
  9. js时间戳转换为日期时间格式
  10. 超好用的奈飞Netflix客户端:Netflix for Mac