##############################################################################
# ThanCad 0.0.9 "DoesSomething": 2dimensional CAD with raster support for engineers.
#
# Copyright (c) 2001-2009 Thanasis Stamos, August 23, 2009
# URL: http://thancad.sourceforge.net
# e-mail: cyberthanasis@excite.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 2 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 (www.gnu.org/licenses/gpl.html).
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
"""\
ThanCad 0.0.9 "DoesSomething": 2dimensional CAD with raster support for engineers.
This module defines functionality necessary for user interaction in a drawing
window.
"""
import time
from math import pi,sqrt,atan2,hypot
import p_gtkwid, tkSimpleDialog
from thandr import ThanText,ThanPoint
from thantrans import T
from thantkguilowget.thantkconst import *
from thanvar import Canc,ThanScheduler
from thanopt import thancadconf
class ThanTkGuiHighGet:
"Mixin for highlevel getting information from Tk window."
def __init__ (self):
"Initialisation is needed."
self.thanNest = False
self.BSEL = 8
self.BOSN = 15
self.thanSelems = set() # Elements which may be snapped to
self.__externalFilterFunc = None # External filter for the selection routines
def thanGudSetSelExternalFilter(self, func):
"Sets external filter function."
self.__externalFilterFunc = func # External filter for the selection routines
def thanWaitFor(self, mes, state, cc1=None, cc2=None, cc3=None, rr1=None, tt1=None):
"Sets the appropriate state to gui AND command line, and waits for either result."
dc = self.thanCanvas; cmd = self.thanCom
while True:
res, cargo = self.__WaitFor(mes, state, cc1, cc2, cc3, rr1, tt1)
try: nest1 = (res[:1] == "'")
except: nest1 = False
if not nest1: return res, cargo
if self.thanNest:
cmd.thanAppend("A nested command is currently being executed.\n"\
"Please exit this nested command in order to begin a new one\n", "can")
continue
self.thanNest = True
self.__cmdstate = cmd.__dict__.copy()
self.__dcstate = dc.__dict__.copy()
self.__scheduler = self.thanScheduler
self.thanScheduler = ThanScheduler()
cmd.thanCleanup() # Cleanup saved answer in commandline
dc.thanCleanup() # Cleanup gui
cmd.thanAppend("Beginning nested command..\n", "mes")
cmd.thanAppend("%s\n" % res[1:], "com")
cmd.thanBeginCommandNest(res[1:])
cmd.thanAppend("Resuming original command\n", "mes")
cmd.thanCleanup() # Cleanup saved answer in commandline
dc.thanCleanup() # Cleanup gui
cmd.__dict__.update(self.__cmdstate)
dc.__dict__.update(self.__dcstate)
self.thanScheduler = self.__scheduler
del self.__cmdstate, self.__dcstate, self.__scheduler
self.thanNest = False
__READYSTATES = frozenset((THAN_STATE_POINT, THAN_STATE_POINT1,
THAN_STATE_LINE, THAN_STATE_LINE2,
THAN_STATE_RECTANGLE, THAN_STATE_RECTRATIO,
THAN_STATE_MOVE, THAN_STATE_ROADP, THAN_STATE_ROADR,
THAN_STATE_POLAR, THAN_STATE_CIRCLE, THAN_STATE_ARC,
))
def __WaitFor(self, mes, state, cc1=None, cc2=None, cc3=None, rr1=None, tt1=None):
"Sets the appropriate state to gui AND command line, and waits for either result."
dc = self.thanCanvas; cmd = self.thanCom; ct = self.thanCt
strdis = self.thanProj[1].thanUnits.strdis
strang = self.thanProj[1].thanUnits.strang
strcoo = self.thanProj[1].thanUnits.strcoo
dc.thanPrepare(state, cc1, cc2, cc3, rr1, tt1)
while True:
cmd.thanPrompt(mes)
cmd.thanPrepare(state, cc1, cc2, rr1, tt1) # It only sets the state and the ratio
for com1 in self.thanScriptComs:
cmd.thanEnter(com1)
break
while dc.thanState != THAN_STATE_NONE and cmd.thanState != THAN_STATE_NONE: dc.update()
if dc.thanState == THAN_STATE_NONE: # Gui answered
res, cargo = dc.thanLastResult # Get gui result
dc.thanCleanup() # Cleanup saved answer in gui
cmd.thanCleanup() # Cleanup commandline
if res == Canc: return res, cargo
if res == -2: return res, cargo
if res == "c": return "c", cargo
# xx, yy = ct.local2Global(res[0], res[1])
if state in self.__READYSTATES:
cmd.thanCleanup("%s" % strcoo(res))
cmd.thanLastPoint(res) # Save last point for relative coords
return res, cargo
elif state == THAN_STATE_SNAPELEM:
cmd.thanCleanup(" ")
return res, cargo
elif state == THAN_STATE_TEXT:
assert False, "Boy, I would like to see this bug :)"
elif state == THAN_STATE_ZOOMDYNAMIC or state == THAN_STATE_PANDYNAMIC:
return res, cargo
else:
assert False, "Unknown state "+str(state)
else: # Commandline answered
res = cmd.thanLastResult # Get commandline result
try: nest1 = (res[:1] == "'")
except: nest1 = False
if nest1: return res, None
cmd.thanCleanup() # Cleanup saved answer in commandline
dc.thanCleanup() # Cleanup gui
if res == Canc: return res, None
print "__waitfor: cmd answered: type=", type(res), " result=", res
return res, None
#============================================================================
def thanGudCommandBegin(self, com):
"Begins a new command, as if the user entered com via the keyboard."
self.thanCom.thanEnter(com, tags="com")
def thanGudCommandEnd(self, mes=None, mestype="can"):
"Print optional message and reprompt."
if mes != None: self.thanCom.thanAppend("%s\n" % mes, mestype)
self.thanCom.thanPrompt()
self.thanCom.thanWaitingInput = True
def thanGudCommandCan(self, mes=None, mestype="can"):
"Print (at least default) cancel message and reprompt."
from thantkcmd import DEFCAN
if mes == None: mes = DEFCAN
# self.thanCom.thanCleanup(mes)
self.thanCom.thanAppend("%s\n" % mes, mestype)
self.thanCom.thanPrompt()
self.thanCom.thanWaitingInput = True
def thanPrt(self, mes, tag="info1"):
"Print to the command window; this is info, warnings, error etc."
self.thanCom.thanAppend("%s\n" % mes, tag)
#============================================================================
def __getPoint(self, stat1, statonce="", strict=True, options=(), state="", args=()):
"""Gets a point but allows for options which are returned with the first letter (in lower case).
1. Read user response.
2. If user cancelled, return Canc
3. If it is point, return point.
4. If it is one of the options, return the first letter of this option.
5. If we reached here it is not a point and not one of the options. So:
6. If strict==False, return the user response.
7. Otherwise print error message, and go to step 1.
8. statonce is a message that is printed only the first time that this
function asks for input. If the function asks again for input (in
case of error, statonce is not printed).
"""
stat = statonce+stat1
opts = [opt.strip().lower() for opt in options]
nd = self.thanProj[1].thanVar["dimensionality"]
while True:
res, cargo = self.thanWaitFor(stat, state, *args)
if res == Canc: return Canc, "o", None # The user cancelled
istext = True
try: res+"x"
except: istext = False
if not istext:
try: c2 = [float(res[i]) for i in xrange(nd)]
except (IndexError, ValueError, TypeError): pass
else: return c2, "v", cargo # Validated coordinates are returned
res1 = str(res).strip().lower()
n = len(res1)
if n == 0:
if "" in opts: return "", "o", None # Option is returned
else:
for opt in opts:
if res1 == opt[:n]: return opt[0], "o", None # Option is returned
if not strict: return res, "t", cargo # Text other than coordinates or options
if len(opts) == 0: stat = T["Invalid Point. Try again.\n"]
else: stat = T["Invalid point or option. Try again.\n"]
self.thanCom.thanAppend(stat, "can")
stat = stat1
def thanGudGetPoint(self, stat1, statonce="", options=()):
"Gets a point from user with possible options."
return self.__getPoint(stat1, statonce, strict=True, options=options,
state=THAN_STATE_POINT)[0]
def thanGudGetPointOr(self, stat1, statonce="", options=()):
"Gets a point from user with possible options or any other text."
return self.__getPoint(stat1, statonce, strict=False, options=options,
state=THAN_STATE_POINT)
def thanGudGetPoint1(self, stat1, statonce="", options=()):
"Gets a point from user with possible options; use a different cursor."
return self.__getPoint(stat1, statonce, strict=True, options=options,
state=THAN_STATE_POINT1)[0]
def thanGudGetLine(self, c1, stat, statonce="", options=()):
"Gets a line from user beginning at c1, with possible options."
return self.__getPoint(stat, statonce, strict=True, options=options,
state=THAN_STATE_LINE, args=(c1,))[0]
def thanGudGetLine2(self, c1, c2, stat, statonce="", options=()):
"Gets a line dragging 2 lines from user beginning at c1,c2 with possible options."
return self.__getPoint(stat, statonce, strict=True, options=options,
state=THAN_STATE_LINE2, args=(c1,c2))[0]
def thanGudGetRoadP(self, c1, c2, c3, r2, stat, statonce="", options=()):
"Gets a line/arc combination from user, with possible options."
res, typres, cargo = self.__getPoint(stat, statonce, strict=True, options=options,
state=THAN_STATE_ROADP, args=(c1, c2, c3, r2))
return res, cargo
def thanGudGetRoadR(self, c1, c2, c3, r2, stat, statonce="", options=()):
"Gets a line/arc combination from user, with possible options."
while True:
res, typres, cargo = self.__getPoint(stat, statonce, strict=False, options=options,
state=THAN_STATE_ROADR, args=(c1, c2, c3, r2))
if typres == "v": # coordinates
from thanvar import calcRoadNodeR
delta = hypot(res[1]-c2[1], res[0]-c2[0])
ro = calcRoadNodeR(c1[0], c1[1], c2[0], c2[1], c3[0], c3[1], delta)
return ro.R
if typres == "o": return res # Option or Cancel
try:
r = float(res)
if r > 0.0: return r
except ValueError:
pass
if options: statonce = T["Invalid point, positive number or option. Try again.\n"]
else: statonce = T["Invalid point or positive number. Try again.\n"]
self.thanCom.thanAppend(statonce, "can")
statonce = ""
def thanGudGetMovend(self, c1, stat, elems=None, direction=None, statonce="", options=()):
"Gets a vector distance from user, while dragging some elements."
if elems == None: self.thanGudSetSelClone()
else: self.thanGudSetDrag(elems)
return self.__getPoint(stat, statonce, strict=True, options=options,
state=THAN_STATE_MOVE, args=(c1,direction))[0]
def thanGudGetImPoint(self, stat1, image=None, ptol=0, threshold=None, statonce="", options=()):
"""Get a point which is in image, or any image; return image too.
If image==None all active images are searched.
If ptol>0, (ptol is in local coordinates) then a rectangle +=dx, +=dy
where dx=dy=ptol in world coordinates is searched.
if threshold!=None then the point must be part of a curve in image.
"""
strict = True
while True:
cw = self.__getPoint(stat1, statonce, strict=True, options=options,
state=THAN_STATE_POINT)[0]
first = None, cw # cw may be None
try: 0.0+cw[0]+cw[1]
except (IndexError, TypeError): return first # An option, or anything but point if strict==True
if image == None: images = self.thanImages.itervalues()
else: images = [image]
rasterFound = False
for image in images:
for k in xrange(ptol+1):
wk, _ = self.thanCt.local2GlobalRel(k, k)
for dx in -wk,wk:
for dy in -wk,wk:
cw1 = list(cw)
cw1[0] += dx
cw1[1] += dy
try: jx, iy = image.thanGetPixCoor(cw1)
except IndexError: continue
if threshold == None: return image, cw1
if image[jx, iy]: return image, cw1
if not rasterFound:
rasterFound = True
first = image, cw1 # Nearest raster to thepoint the user clicked
if rasterFound: continue
rasterFound = True
if not strict: return first # We didn't find any curve; return the nearest point with image
if rasterFound: statonce = T["No curve was found at point. Try Again.\n"]
else: statonce = T["No raster image found at point. Try again.\n"]
self.thanCom.thanAppend(statonce, "can")
statonce = ""
def thanGudGetPolar(self, cc, r, stat, statonce="", options=()):
"Gets the angle between x-axis and a dragged line of length r beginning at cc."
while True:
res, typres, cargo = self.__getPoint(stat, statonce=statonce, strict=False,
options=options, args=(cc,None,None,r), state=THAN_STATE_POLAR)
if typres == "v": # coordinates
th = atan2(res[1]-cc[1], res[0]-cc[0])
return th
if typres == "o": return res # Option or Cancel
try: th = float(res)
except ValueError: pass
else: return self.thanProj[1].thanUnits.unit2rad(th)
if options: statonce = T["Invalid point, angle or option. Try again.\n"]
else: statonce = T["Invalid point or angle. Try again.\n"]
self.thanCom.thanAppend(statonce, "can")
statonce = ""
def thanGudGetCircle(self, cc, stat, statonce="", options=()):
"Gets a circle from user, centered at cc."
while True:
res, typres, cargo = self.__getPoint(stat, statonce=statonce, strict=False,
options=options, args=(cc,), state=THAN_STATE_CIRCLE)
if typres == "v": # coordinates
print "GUI answered"
r = hypot(res[1]-cc[1], res[0]-cc[0])
print "GUI answered: r=", r
return r
if typres == "o": return res # Option or Cancel
try:
r = float(res)
print "CMD answered: r=", r
if r > 0.0: return r
except ValueError:
pass
if options: statonce = T["Invalid point, positive number or option. Try again.\n"]
else: statonce = T["Invalid point or positive number. Try again.\n"]
self.thanCom.thanAppend(statonce, "can")
statonce = ""
def thanGudGetArc(self, cc, r, theta1, stat, statonce="", options=()):
"Gets a circlular arc from user, centered at cc, radius r and beginning at theta1."
while True:
res, typres, cargo = self.__getPoint(stat, statonce=statonce, strict=False,
options=options, args=(cc,None,None,r,theta1), state=THAN_STATE_ARC)
if typres == "v": # coordinates
th = atan2(res[1]-cc[1], res[0]-cc[0])
return th
if typres == "o": return res # Option or Cancel
try: th = float(res)
except ValueError: pass
else: return self.thanProj[1].thanUnits.unit2rad(th)
if options: statonce = T["Invalid point, angle or option. Try again.\n"]
else: statonce = T["Invalid point or angle. Try again.\n"]
self.thanCom.thanAppend(statonce, "can")
statonce = ""
def thanGudGetRect(self, c1, stat, statonce="", options=(), com=None):
"Gets a rectangle from user, beginning at c1."
return self.__getPoint(stat, statonce, strict=True, options=options,
state=THAN_STATE_RECTANGLE, args=(c1, None, None, None, com))[0]
def thanGudGetRectratio(self, c1, stat, ratio, statonce="", options=()):
"Gets a rectangle from user, beginning at c1 with given ratio height/width."
while True:
res, typres, cargo = self.__getPoint(stat, statonce=statonce, strict=False,
options=options, args=(c1,None,None,None,ratio), state=THAN_STATE_RECTRATIO)
if typres == "v": return res # coordinates
if typres == "o": return res # Option or Cancel
try:
r = float(res)
if r != 0.0:
cc = list(c1)
cc[0] += r
cc[1] += r*ratio
return cc
except ValueError:
pass
if options: statonce = T["Invalid point, nonzero number or option. Try again.\n"]
else: statonce = T["Invalid point or nonzero number. Try again.\n"]
self.thanCom.thanAppend(statonce, "can")
statonce = ""
def __getText(self, stat1, default=None, validate=None, statonce="",
strict=True, options=(), fullopt=False, state=THAN_STATE_TEXT, args=()):
"""Gets a point but allows for options which are returned with the first letter (in lower case).
1. Read user response.
2. If user cancelled, return Canc
3. If it is blank and blank is an option, then return blank.
4. Otherwise if it is blank and there is a default value
set the response equal to this default value.
5. If the response (the real or the default) is equal to an option
return the option.
6. If we reached here we have a nontrivial response from the user
which must be checked if it is legal.
6. If there is not a validate function, the response is legal and return it.
7. Otherwise call the validate function and if validation is ok,
return the validated value.
8. if validation is NOT ok, but strict==False, we dont't mind and so
return the (illegal) response.
9. Otherwise print error message, and go to step 1.
10.statonce is a message that is printed only the first time that this
function asks for input. If the function asks again for input (in
case of error, statonce is not printed).
"""
stat = statonce+stat1
opts = [opt.strip().lower() for opt in options]
while True:
res, cargo = self.thanWaitFor(stat, state, *args)
if res == Canc: return Canc, "o", None # The user cancelled
res1 = str(res).strip().lower()
if res1 == "" and "" in opts: return "", "o", None # Response blank, and blank is an option
if res1 == "" and default != None:
res = default # Use default value
res1 = str(res).strip().lower()
n = len(res1)
if n == 0: # Check for options
if "" in opts: return "", "o", None
else:
for opt in opts:
if res1 == opt[:n]:
if fullopt: return opt, "o", None
else: return opt[0], "o", None
if validate == None: return res, "t", cargo # No validation
val, stat = validate(res, stat1)
if val != None: return val, "v", cargo # Validation ok
if not strict: return res, "t", cargo # We don't mind failed validation
self.thanCom.thanAppend(stat, "can")
stat = stat1
def thanGudGetText(self, stat1, default=None, statonce="", strict=True):
return self.__getText(stat1, default, statonce=statonce, strict=strict)[0]
def thanGudGetText0(self, stat1, default=None, statonce="", strict=True):
"Gets nonblank text from user."
def validate(res, stat1):
if res.strip() != "": return res, None
return None, T["Non blank text is required. Try again.\n"]
return self.__getText(stat1, default, validate, statonce=statonce, strict=strict)[0]
def thanGudGetOpts(self, stat1, default=None, statonce="", fullopt=False, options=()):
"Gets text from user which must be one of the options."
def validate(res, stat1):
return None, T["Invalid option. Try again.\n"]
return self.__getText(stat1, default, validate, statonce=statonce, strict=True,
fullopt=fullopt, options=options)[0]
def thanGudGetYesno(self, stat1, default=None, statonce="", strict=True):
"Gets nonblank text from user."
def validate(res, stat1):
res = res.strip().lower()
if res[:2] == "ye": return True, None
if res[:2] == "no": return False, None
return None, T["Yes or No is required. Try again.\n"]
return self.__getText(stat1, default, validate, statonce=statonce, strict=strict)[0]
def thanGudGetFloat(self, stat1, default=None, statonce="", strict=True,
options=(), state=THAN_STATE_TEXT, args=()):
"Gets a float number from user."
def validate(res, stat1):
try:
val = float(res)
return val, None
except ValueError:
pass
return None, T["A real number is required. Try again.\n"]
return self.__getText(stat1, default, validate, statonce=statonce,
strict=strict, options=options, state=state, args=args)[0]
def thanGudGetPosFloat(self, stat1, default=None, statonce="", strict=True,
options=(), state=THAN_STATE_TEXT, args=()):
"Gets a positive float number from user."
def validate(res, stat1):
try:
val = float(res)
if val > 0.0: return val, None
except ValueError:
pass
return None, T["A positive real number is required. Try again.\n"]
return self.__getText(stat1, default, validate, statonce=statonce,
strict=strict, options=options, state=state, args=args)[0]
def thanGudGetFloat2(self, stat1, default=None, limits=(None, None), statonce="", strict=True):
"Gets a float number from user within limits."
def validate(res, stat1):
try:
val = float(res)
if limits[0] == None and limits[1] == None: return val, None
if limits[0] != None and val >= limits[0]: return val, None
if limits[1] != None and val <= limits[1]: return val, None
if limits[1] <= val <= limits[1]: return val, None
return None, T["Real number out of range. Try again.\n"]
except ValueError:
pass
return None, T["A real number is required. Try again.\n"]
return self.__getText(stat1, default, validate, statonce=statonce, strict=strict)[0]
def thanGudGetInt2(self, stat1, default=None, limits=(None, None), statonce="", strict=True):
"Gets an integer number from user within limits."
def validate(res, stat1):
try:
val = int(res)
if limits[0] == None and limits[1] == None: return val, None
if limits[0] != None and val >= limits[0]: return val, None
if limits[1] != None and val <= limits[1]: return val, None
if limits[1] <= val <= limits[1]: return val, None
return None, T["Integer number out of range. Try again.\n"]
except ValueError:
pass
return None, T["An integer number is required. Try again.\n"]
return self.__getText(stat1, default, validate, statonce=statonce, strict=strict)[0]
def thanGudGetSnapElem(self, stat1, default=None, statonce="", strict=True, options=()):
"Gets a point from user with possible options."
def validate(res, stat1):
"Do not validate anything."
try: res.thanTkGet
except: pass
else: return res, None
return None, T["Invalid selection or option. Try again.\n"]
otypes = thancadconf.thanOsnapModes
otypes1 = otypes.copy()
otypes.clear()
otypes["ena"] = otypes["ele"] = True
self.thanCanvas.thanChs.thanPush(4) # Save previous croshair; set rectangle croshair
res = self.__getText(stat1, default, validate, statonce, strict, options,
state=THAN_STATE_SNAPELEM)[0]
otypes.clear()
otypes.update(otypes1)
self.thanCanvas.thanChs.thanPop() # Restore previous croshair
return res
#===========================================================================
def thanGudGetLayerleaf(self, mes):
"Let the user select a single layer (only leaf layers)."
proj = self.thanProj
lt = proj[1].thanLayerTree
lays = dict((lay.thanGetPathname(), lay) for lay in lt.dilay.itervalues())
names = lays.keys()
names.sort()
win = p_gtkwid.ThanPoplist(self, names, width=50, title=mes)
self.thanTkSetFocus()
if win.result == None: return Canc
return lays[win.result]
def thanGudGetLayerleafs(self, mes):
"Let the user select multiple layers (only leaf layers)."
proj = self.thanProj
lt = proj[1].thanLayerTree
lays = dict((lay.thanGetPathname(), lay) for lay in lt.dilay.itervalues())
names = lays.keys()
names.sort()
import Tkinter
win = p_gtkwid.ThanPoplist(self, names, width=50, selectmode=Tkinter.EXTENDED, title=mes)
self.thanTkSetFocus()
if win.result == None: return Canc
return [lays[n] for n in win.result]
def thanGudGetText1(self, mes, textDefault):
"Accepts a nonblank text via a modal window."
from p_ggen import thanUnicode,thanUnunicode
self.update_idletasks() # Experience showed that there should be no pending
# Tk jobs when we show a modal window.
# _idletasks breaks WinDoze (98?) support. Skotistika
while True:
ans = tkSimpleDialog.askstring("Please enter text", mes,
initialvalue=thanUnicode(textDefault), parent=self)
if ans == None: return Canc
if ans != "": return thanUnunicode(ans)
self.bell()
def thanGudGetFloat1(self, mes, textDefault):
"Accepts a real number via a modal window."
self.update_idletasks() # Experience showed that there should be no pending
# Tk jobs when we show a modal window
# _idletasks breaks WinDoze (98?) support. Skotistika
ans = tkSimpleDialog.askfloat("Please enter a number", mes,
initialvalue=str(textDefault), parent=self)
return ans
def thanGudGetPosFloat1(self, mes, textDefault):
"Accepts a poistive real number via a modal window."
ans = tkSimpleDialog.askfloat("Please enter a positive number", mes,
initialvalue=str(textDefault), minvalue=0.00000001, parent=self)
return ans
#============================================================================
def thanGudGetSel1(self, xa, ya):
"Finds 1 element near xa, ya."
ct = self.thanCt
xa, ya = ct.global2Local(xa, ya)
dc = self.thanCanvas
bpix, hpix = self.BSEL/2, self.BSEL/2
self.thanProj[2].thanCanvas.thanCh.thanDisable()
items = list(dc.find_overlapping(xa-bpix, ya-hpix, xa+bpix, ya+hpix))
items.reverse() # Tkinter places the most recently drawn element to the end of the list
# and the last element is probably the one we want to edit. It is also above the
# previous elements if the coincide
tagel = self.thanProj[1].thanTagel
for item in items:
if dc.type(item) == "image": continue
# if dc.type(item) == "polygon": continue
print "thanGudGetSel1: type=", dc.type(item)
if self.__externalFilterFunc == None: break
titem = dc.gettags(item)[0]
e = tagel[titem]
if self.__externalFilterFunc(e): break # If element satisfies the external filter OK
else:
self.thanProj[2].thanCanvas.thanCh.thanEnable()
return 0, 0 # No element found near xa,ya
# self.thanGudSetSelClear()
dc.dtag("selall", "sel") # Clear tag "sel"
dc.addtag_withtag("sel", item)
self.__filtercros()
return self.__selCount()
def thanGudGetSelWin(self, xa, ya, xb, yb):
"Gets a selection on a given window."
ct = self.thanCt
xa, ya = ct.global2Local(xa, ya)
xb, yb = ct.global2Local(xb, yb)
dc = self.thanCanvas
# self.thanGudSetSelClear()
dc.dtag("selall", "sel") # Clear tag "sel"
self.thanProj[2].thanCanvas.thanCh.thanDisable()
items = dc.addtag_enclosed("sel", xa, ya, xb, yb)
# No need to avoid selecting the raster of ThanImage, because all the raster must be enclosed, in order to be selected
print "filterwin started"; t1 = time.time()
self.__filterwin()
print "filterwin ended:", time.time()-t1
return self.__selCount()
def __selCount(self):
"Updates and counts the selected ThanCad elements."
# cget = self.thanCanvas.itemcget
cget = self.thanCanvas.gettags
tagel = self.thanProj[1].thanTagel
self.thanSel = set(tagel[cget(item)[0]] for item in self.thanCanvas.find_withtag("sel"))
nselect = len(self.thanSel)
nduplic = nselect - len(self.thanSel - self.thanSelall)
self.thanSelall |= self.thanSel
self.thanProj[2].thanCanvas.thanCh.thanEnable()
return nselect, nduplic
def thanGudGetSelOld(self):
"Updates and counts the selected ThanCad elements."
dc = self.thanCanvas
dc.dtag("selall", "sel") # Clear tag "sel"
dc.addtag_withtag("sel", "selold") # Add sel to all previously selected items
dc.addtag_withtag("selall", "sel") # Add selall to all selected items
self.thanSel = self.thanSelold
nselect = len(self.thanSel)
nduplic = nselect - len(self.thanSel - self.thanSelall)
self.thanSelall |= self.thanSel
return nselect, nduplic
def __filterwinold(self):
"""Discards 'compound' items that are not completely within the window.
Some of ThanCad's elements, for now points and texts (and all the
elements with dashed lines in the future), are represented by more
than one Tkinter items. In select window, we must make sure that all
the items of a single compound element are present within the window.
If they are not, the element and its correspondig items are deleted
from the selection.
"""
dc = self.thanCanvas
dc.addtag_withtag("sel1", "sel") # Add sel1 to all items within selection window
# print "elements selected=", dc.find_withtag("sel1")
# print "noncompound elements=", dc.find_withtag("nocomp")
dc.dtag("nocomp", "sel1") # Delete sel1 from items of non-compound elements
items = set(dc.find_withtag("sel1")) # All items of compound elements within selection window
# print "Items compound=", items
while len(items) > 0:
for item in items: break
# titem = dc.itemcget(item, "tags").split()[0]
titem = dc.gettags(item)[0]
items1 = set(dc.find_withtag(titem)) # These are the items of a single ThanCad (compound) element
if not items1 <= items: # If items1 is not subset of items, then the ThanCad element..
dc.dtag(titem, "sel") # .. is removed from selection
items -= items1
# items.difference_update(items1)
dc.dtag("all", "sel1")
if self.__externalFilterFunc != None: self.__filterexternal()
dc.addtag_withtag("selall", "sel") # Add selall to all selected items
def __filterwin(self):
"""Discards 'compound' items that are not completely within the window.
Some of ThanCad's elements, for now points and texts (and all the
elements with dashed lines in the future), are represented by more
than one Tkinter items. In select window, we must make sure that all
the items of a single compound element are present within the window.
If they are not, the element and its correspondig items are deleted
from the selection.
"""
dc = self.thanCanvas
items1 = {}
for item in dc.find_all():
titem = dc.gettags(item)[0]
try: items1[titem].add(item) # These are the items of a single ThanCad (compound) element
except KeyError: items1[titem] = set((item,))
dc.addtag_withtag("sel1", "sel") # Add sel1 to all items within selection window
dc.dtag("nocomp", "sel1") # Delete sel1 from items of non-compound elements
items = set(dc.find_withtag("sel1")) # All items of compound elements within selection window
while len(items) > 0:
for item in items: break
titem = dc.gettags(item)[0]
items2 = items1[titem] # I use this instead of items2=dc.find_withtag(titem), because it is 30 times faster
if not items2 <= items: # If items1 is not subset of items, then the ThanCad element..
dc.dtag(titem, "sel") # .. is removed from selection
items -= items2
dc.dtag("all", "sel1")
if self.__externalFilterFunc != None: self.__filterexternal()
dc.addtag_withtag("selall", "sel") # Add selall to all selected items
def __filterexternal(self):
"Removes elements that do not satisfy external filter."
dc = self.thanCanvas
items = set(dc.find_withtag("sel")) # Find all geometricall selected items
tagel = self.thanProj[1].thanTagel
while len(items) > 0:
for item in items: break
titem = dc.gettags(item)[0]
items1 = set(dc.find_withtag(titem)) # These are the items of a single ThanCad (compound) element
e = tagel[titem]
if not self.__externalFilterFunc(e): # If element does not satisfy the external filter it..
dc.dtag(titem, "sel") # .. is removed from selection
items -= items1
def thanGudGetSelCros(self, xa, ya, xb, yb):
"Gets a selection on a given crossing window."
ct = self.thanCt
xa, ya = ct.global2Local(xa, ya)
xb, yb = ct.global2Local(xb, yb)
dc = self.thanCanvas
# self.thanGudSetSelClear()
dc.dtag("selall", "sel") # Clear tag "sel"
self.thanProj[2].thanCanvas.thanCh.thanDisable()
dc.addtag_overlapping("sel", xa, ya, xb, yb)
# We need to avoid selecting the raster of ThanImage
print "filtercros started"; t1 = time.time()
self.__filtercros()
print "filtercros ended:", time.time()-t1
return self.__selCount()
def __filtercrosold(self):
"""Ensures that 'compound' items partialy within the window, are completely selected.
Some of ThanCad's elements, for now points and texts (and all the
elements with dashed lines in the future), are represented by more
than one Tkinter items. In select crossing, we must make sure that if
one of the items of a single compound element are present within the
window, then all must be selected. If not all selected, the nonselected
items are forced into the selection.
"""
dc = self.thanCanvas
tagseen = set()
dc.addtag_withtag("sel1", "sel") # Add sel1 to all items within selection window
# print "elements selected=", dc.find_withtag("sel1")
# print "noncompound elements=", dc.find_withtag("nocomp")
dc.dtag("nocomp", "sel1") # Delete sel1 from items of non-compound elements
items = dc.find_withtag("sel1") # All items of compound elements within selection window
# print "Items compound=", items
for item in items:
titem = dc.gettags(item)[0]
if titem in tagseen: continue
if dc.type(item) == "image": # Do not consider the raster of ThanImage; ..
dc.dtag(item, "sel") # ..it will be selected only if the bounding rectange is selected..
continue # ..tagseen ensures that the bounding rectangle has not yet been seen
tagseen.add(titem)
dc.addtag_withtag("sel", titem)
dc.dtag("all", "sel1")
if self.__externalFilterFunc != None: self.__filterexternal()
dc.addtag_withtag("selall", "sel") # Add selall to all selected items
def __filtercros(self):
"""Ensures that 'compound' items partialy within the window, are completely selected.
Some of ThanCad's elements, for now points and texts (and all the
elements with dashed lines in the future), are represented by more
than one Tkinter items. In select crossing, we must make sure that if
one of the items of a single compound element are present within the
window, then all must be selected. If not all selected, the nonselected
items are forced into the selection.
"""
dc = self.thanCanvas
items1 = {}
for item in dc.find_all():
titem = dc.gettags(item)[0]
try: items1[titem].add(item) # These are the items of a single ThanCad (compound) element
except KeyError: items1[titem] = set((item,))
items2add = []
tagseen = set()
dc.addtag_withtag("sel1", "sel") # Add sel1 to all items within selection window
dc.dtag("nocomp", "sel1") # Delete sel1 from items of non-compound elements
items = dc.find_withtag("sel1") # All items of compound elements within selection window
for item in items:
titem = dc.gettags(item)[0]
if titem in tagseen: continue
if dc.type(item) == "image": # Do not consider the raster of ThanImage; ..
dc.dtag(item, "sel") # ..it will be selected only if the bounding rectange is selected..
continue # ..tagseen ensures that the bounding rectangle has not yet been seen
tagseen.add(titem)
# dc.addtag_withtag("sel", titem)
items2add.extend(items1[titem])
for item in items2add: dc.addtag_withtag("sel", item)
# dc.addtag_withtag("sel", items2add) # A single addtag_withtag is 30 times faster than many
dc.dtag("all", "sel1")
if self.__externalFilterFunc != None: self.__filterexternal()
dc.addtag_withtag("selall", "sel") # Add selall to all selected items
def thanGudGetSelLayers(self, lays):
"Gets a selection on a given set of layers."
dc = self.thanCanvas
dc.dtag("selall", "sel") # Clear tag "sel"
for lay in lays:
dc.addtag_withtag("sel", lay.thanTag)
dc.addtag_withtag("selall", "sel") # Add selall to all selected items
return self.__selCount()
def prtags(self, stag="all"):
"Print Item tags of all items with tag stag."
dc = self.thanCanvas
print "Item tags of all items with tag '"+stag+"'"
for item in dc.find_withtag(stag):
print item, ":", dc.gettags(item)
if __name__ == "__main__":
print __doc__
|