示例#1
0
文件: Document.py 项目: R4md4c/Gamra
 def Render(self,dc):
     self.Clip[2:] = list(dc.GetSizeTuple())
     ctx = ContextFromDC(dc)
     ctx.translate(-self.Clip[0], -self.Clip[1])
     ctx.scale(self.Zoom,self.Zoom)
     
     self.Border.Apply(ctx)
     self.DrawAll(ctx, self.Objects)
     self.DrawAll(ctx, self.ToolObjects)
示例#2
0
class drawPlasmid(DNApyBaseDrawingClass):
	''' This class handle the drawing of a plasmid'''
	'''
		it should draw:
		- Plasmid 
		- Features
		- feature names
		- RestriktionSites
		- Names of Restriction sites
		- Selection
		- higlights

		It has to Calculate in this order:
		- The Size of the Plasmid
		- The angular positon of the features
		- If the Label of a feature can be drawn inside
		- the angular positions of all Restrictionsites
		- The arrangements of the labels
			- featurelabels
			- Restriktionsite Labels

		Possible Interactions:
		- select dna
			- drag and drop select
			- click on feature
		- zoom in and out
		- double click feature

		Each cycle must be something like this:

		1. Check if we have to calcaulate the length of the features
			self.calc.fl = True
			if true: check if this changes anything for the other labels (do labels have to be drawn outside or inside compared to before)
		2. Check if we have to calcaulate Label Positions again:
			self.calc.lp = true
		3. If self.calc.lp and fl == False:
			Just redraw the previous plasmid
		   else:
			recalculate the desired stuff

		x. Highlight and interaction'''
	def __init__(self, parent, id):
	
		# object to store our cacluations over time
		self.plasmidstore 		= plasmidstore()		
		self.radius 			= 25								# cairo unit
		self.radiusI 			= self.radius - 0.013 * self.radius	# cairo unit
		self.radiusO 			= self.radius + 0.013 * self.radius	# cairo unit
		self.radiusLabels 		= self.radius + 17 					# cairo unit
		self.arrowWidth 		= 1.8 								# cairo unit
		self.labelHeight 		= 12 								# pixel
		

		self.Highlight 			= None								# store who to highlight
		self.enzymeGap			= "-|-"								# seperator to make unique hitname less unique
		
		#self.EnzymeMonitor = Monitor()
		
		# start the window
		DNApyBaseDrawingClass.__init__(self, parent, wx.ID_ANY)
		
		# bind events
		self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
		self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
		self.Bind(wx.EVT_MOTION, self.OnMotion)
		self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDouble)


		
	
		
		return None
	
	def update_globalUI(self):
		'''
		Method should be modified as to update other panels in response to changes in own panel.
		'''
		MSG_CHANGE_TEXT = "change.text"
		pub.sendMessage(MSG_CHANGE_TEXT, text="Plasmid view says update!")
	
	def update_ownUI(self):
		"""
		This would get called if the drawing needed to change, for whatever reason.

		The idea here is that the drawing is based on some data generated
		elsewhere in the system. If that data changes, the drawing needs to
		be updated.

		This code re-draws the buffer, then calls Update, which forces a paint event.
		"""	

		# update selection from editor
		self.updateSelection() 
		self.updatePosition()
		
		# start the calculations if any.
		self.checkFeatureCalculations() 		# changed features may change labels also
		self.checkEnzymeCalculations() 			# check for restriction enzymes
		self.checkLabelCalculations() 			# changed features may change labels also
		
		# check if we even have to redraw anything:

		dc = wx.MemoryDC()
		dc.SelectObject(self._Buffer)

		dc.SetBackground(wx.Brush("White"))	# start the DC and clear it
		dc.Clear() 				# make sure you clear the bitmap!
		self.ctx = ContextFromDC(dc)		# load it into cairo
		
		# seletion starts here
		self.checkSelectionCalculations()		# check and make a selection arc

		
		# glyphs
		#self.pango = pangocairo.CairoContext(self.ctx)
		#self.pango.set_antialias(cairo.ANTIALIAS_SUBPIXEL)

		self.draw()

		dc.SelectObject(wx.NullBitmap) # need to get rid of the MemoryDC before Update() is called.
		self.Refresh()
		self.Update()

		return None
	
	def set_dna_selection(self, selection):
		'''Receives requests for DNA selection and then sends it.'''
		assert type(selection) == tuple, 'Error, dna selection must be a tuple'
		selection = (int(selection[0]), int(selection[1]), int(selection[2]))
		genbank.dna_selection = selection
		self.plasmidstore.interaction["selection"] = selection
		self.update_globalUI()
	


	def set_cursor_position(self, position):
		# set position of cursor for status bar:
		genbank.cursor_position 					= position 
		self.plasmidstore.interaction["position"] 	= position

	def updateSelection(self):
		''' get selection from editor and update own store '''
		selection =  genbank.dna_selection
		assert type(selection) == tuple, 'Error, dna selection must be a tuple'
		selection = (int(selection[0]), int(selection[1]), int(selection[2]))
		self.plasmidstore.interaction["selection"] = selection
	
	def updatePosition(self):
		''' get position from editor and update own store '''
		position 									=  genbank.cursor_position
		self.plasmidstore.interaction["position"] 	= position

	def hitName(self, name, index):
		# unique and reproducible id for each feature
		name 	= self.nameBeautiful(name)
		seq 	= "%s%s" % (name, index)
		name 	= ''.join(seq.split())
		return name
	
	def HitTest(self):
		'''Tests whether the mouse is over any feature or label'''
		hit 	= None


		# get the mouse positions
		x, y 	= self.ScreenToClient(wx.GetMousePosition())
		x2, y2 	= self.ctx.device_to_user(x,y)

		# list of all the paths:
		loop = [self.plasmidstore.drawnfeatures, 	# features
				self.plasmidstore.drawnlabels, 		# featurelabes
				self.plasmidstore.labelBoxes,		# featurelabels
				self.plasmidstore.drawnlabelsE,  	# enzymes
				self.plasmidstore.labelBoxesE]		# enzymes
		# loop over all possible paths to find the one we have under the mouse
		for paths in loop:
			for i in paths:
				path = paths[i]
				if type(path) == list:
					path = path[0]
				
				# load the path
				self.ctx.append_path(path)
				self.ctx.set_line_width(0.1) # reduce linewith, so we can have nice clicking detection
				inFill 		= self.ctx.in_fill(x2,y2)
				inStroke 	= self.ctx.in_stroke(x2,y2)
				# check if this path is hit
				if inFill == True or inStroke == True:
					hit = i
				self.ctx.new_path()

		return hit
	
	def HitTestSelection(self):
		'''Check if the selection area (area where selection is possible) was hit'''

		# get the mouse position
		x, y 	= self.ScreenToClient(wx.GetMousePosition())
		x2, y2 	= self.ctx.device_to_user(x,y)

		self.ctx.append_path(self.plasmidstore.interaction["markerArea1"])
		inFill1 = self.ctx.in_fill(x2,y2)
		self.ctx.stroke()

		self.ctx.append_path(self.plasmidstore.interaction["markerArea2"])
		inFill2 = self.ctx.in_fill(x2,y2)
		self.ctx.stroke()

		# check if the path is hit for selection
		if inFill1 == True and inFill2 == False:
			return True
		else:
			return False
	
	def saveSelection(self, start=0, finish=0, zero=-1, featureHit=False):

		# feature to highlight or drag and drop?
		if featureHit != False:
			featurelist             = genbank.gb.get_all_feature_positions()
			for i in range(0,len(featurelist)):
				# load all the feature infos
				featuretype, complement, start, finish, name, index = featurelist[i]

				hName = self.hitName(name, index)
				if featureHit == hName:
					# a feature with start smaller then finish, is a featur laying over zero
					if start > finish:
						zero = 1 # 1 --> feature starts left and ends right of +1
					# set selection for real!
					self.set_dna_selection((start,finish, zero))
		else:
			self.set_dna_selection((start,finish, zero))
		
		return None
	
	def nameBeautiful(self, name):
		# remove any '"' in front or at the end
		if name[0:1] == '"':
			name = name[1:]
		if name[-1:] == '"':
			name = name[:-1]
		return name

	def find_overlap(self, drawn_locations, new_range):
		'''
		Takes two ranges and determines whether the new range has overlaps with the old one.
		If there are overlaps the overlap locations are returned.
		This is used when drawing features. If two features overlap I want them drawn on different levels.
		'''
		assert type(drawn_locations) == list
		assert type(new_range) == tuple

		if drawn_locations == []:
			drawn_locations.append([new_range])
			return drawn_locations, 0
		else:
			i = 0
			while i < len(drawn_locations):
				overlap_found = False
				for n in range(0,len(drawn_locations[i])):
					if drawn_locations[i][n][0]<=new_range[0]<=drawn_locations[i][n][1] or drawn_locations[i][n][0]<=new_range[1]<=drawn_locations[i][n][1]: #if they overlap
						overlap_found = True
					elif new_range[0]<=drawn_locations[i][n][0]<=new_range[1] or new_range[0]<=drawn_locations[i][n][1]<=new_range[1]: #if they overlap
						overlap_found = True
				if overlap_found == False:
					drawn_locations[i].append(new_range)
					return drawn_locations, i
					break
				elif i+1==len(drawn_locations):
					drawn_locations.append([new_range])
					return drawn_locations, i+1
					break
				i += 1
	
	
	############### Inteaction with mosue and keyboard ################
	def OnLeftUp(self, event):
		''' handle left ouseclick up'''
		# get selection
		pos1, pos2, zero 	= self.plasmidstore.interaction["selection"]
		if self.plasmidstore.interaction["leftDown"] == True and pos2 != -1:
			# selection is finish
			self.plasmidstore.interaction["leftDown"] = False
			# save the mouse position
			x, y 		= self.ScreenToClient(wx.GetMousePosition())
			x2, y2 		= self.ctx.device_to_user(x,y)
			pos 		= self.cartesian2position(x2,y2)
			
			self.plasmidstore.interaction["selection"] = (pos1, pos, zero)

		elif pos2 == -1:
			# reset selection
			self.plasmidstore.interaction["selection"] = (1, -1, -1)

			# maybe we cliked on a feature, label or a line?
			hit = self.HitTest()
			if hit != None:
				self.plasmidstore.interaction["hit"] = hit
				# we hit one, so we need to draw the selection!
				# wich feature was hit?
				self.saveSelection(1, -1, -1, hit)
			else:
				self.plasmidstore.interaction["hit"] = None
		
		# update the UI maybe?
		self.update_ownUI()

	
	def OnLeftDown(self, event):
		# remove the position cursor:
		self.set_cursor_position(0)
		
		selection = self.HitTestSelection()
		if selection:
			# save the mouse position
			x, y = self.ScreenToClient(wx.GetMousePosition())
			x2, y2 = self.ctx.device_to_user(x,y)
			pos = self.cartesian2position(x2,y2)
			self.plasmidstore.interaction["leftDown"] = True
			self.plasmidstore.interaction["selection"] = (pos, -1, -1)
		else:
			# remove selection
			self.plasmidstore.interaction["hit"] = None
			self.saveSelection(1, -1, -1, False)
		
		return None
		
		
	def OnLeftDouble(self, event):
		'''When left button is double clicked, launch the feature edit dialog.'''
		hit = self.HitTest() #this does not get the "true" feature index. Some featues are split and this is an index that accounts for that.
		if hit is not False:
			# get the index of this feature
			featurelist             = genbank.gb.get_all_feature_positions()
			for i in range(0,len(featurelist)):
				# load all the feature infos
				featuretype, complement, start, finish, name, index = featurelist[i]
				hName = self.hitName(name, index)
				if hit == hName:
					genbank.feature_selection = copy.copy(index)

			dlg = featureedit_GUI.FeatureEditDialog(None, 'Edit Feature') # creation of a dialog with a title
			dlg.ShowModal()
			dlg.Center()
		
		
	def OnMotion(self, event):
		'''When mouse is moved with the left button down determine the DNA selection from angle generated at mouse down and mouse move event.'''
		
		
		if event.Dragging() and event.LeftIsDown() and self.plasmidstore.interaction["leftDown"] == True:
			# save the mouse position
			x, y 				= self.ScreenToClient(wx.GetMousePosition())
			x2, y2 				= self.ctx.device_to_user(x,y)
			pos 				= self.cartesian2position(x2,y2)
			pos1, pos2, zero 	= self.plasmidstore.interaction["selection"]
			self.plasmidstore.interaction["selection"] = (pos1, pos, zero)
			
			# if we have a sudden increase, we might have moven over zero
			if abs(pos2 - pos) > self.dnaLength/2 and pos2 != -1:
				zero = zero * -1 # turn zero around
				self.plasmidstore.interaction["selection"] = (pos1, pos, zero)

			#set genebank selection
			self.saveSelection(pos1, pos, zero)
			self.update_ownUI()



		

		# check if something is beneath the mousecursor
		oldhit 	= self.Highlight
		hit 	= self.HitTest()
		# save the state
		self.Highlight = hit
		# only update after change
		if oldhit != hit:
			self.update_ownUI()

				
		
		
		#elif len(self.selectionDrawing) < 2: # only hover position, if none is selected --> maybe use two variables to make both possible?
			# no feature to highlight, but maybe selection hover?
		#	self.Highlight = None


			# check if we are in the "hover area"
		#	inArea = self.HitTestMarkerArea()

		#	if inArea == True:
		#		# get the mouse position
		#		x, y = self.ScreenToClient(wx.GetMousePosition())
		#		x2, y2 = self.ctx.device_to_user(x,y)
		#		self.selectionDrawing = [] # empty it
		#		self.selectionDrawing.append(self.cartesian2radial(x2,y2))
		#		self.update_ownUI()
		#	elif oldhit != hit:
		#		self.update_ownUI()
		#	elif len(self.selectionDrawing) == 1: # TODO
		#		self.selectionDrawing = [] # empty it so the hover line wont be seen all the time
		#		self.update_ownUI()




	
	
	############### Setting methods for converting stuff ##############

	def radial2cartesian(self,radius, angle, cx=0,cy=0): # angle in radial!
		radius = float(radius)
		angle  = float(angle)
		x = radius * math.cos(angle) + cx
		y = radius * math.sin(angle) + cy
		return x,y

	def cartesian2radial(self,x, y):
		x 		= -x # cairo somehow swaps the x axis
		diff 	=  -math.radians(90)	# remove some radians, because 0 is on top
		r 		= math.atan2(x,y) + 1 * math.pi + diff
		return r

	def position2angle(self, position):
		radMult        	= float(2*math.pi/self.dnaLength)
		angle = (position - 0.5 * self.dnaLength) * radMult + math.pi/2
		return angle
		
	def position2cartesian(self, position, radius):
		angle = self.position2angle(position)
		x,y = self.radial2cartesian(radius, angle)
		return x, y
	
	def cartesian2position(self, x,y):
		angle 	= self.cartesian2radial(x,y)
		radMult	= float(2*math.pi/self.dnaLength)
		pos 	= 0.5 * self.dnaLength  + (angle - math.pi/2)/radMult 
		pos 	= int(round(pos, 0))
		return pos
	


	def merge_dicts(self, *dict_args):
		'''
		Given any number of dicts, shallow copy and merge into a new dict,
		precedence goes to key value pairs in latter dicts.
		'''
		result = {}
		for dictionary in dict_args:
		    result.update(dictionary)
		return result


	########################################
	# caclcualting functions
	def checkFeatureCalculations(self):
		''' this functions takes the current positions and calcualtions
			of the features and checks fast if it has to recalculated anything'''
		redrawfeatures = False

		
		
		
		# compare the features to the previously calculated ones
		featuresOld = self.plasmidstore.features
		featuresNew = genbank.gb.get_all_feature_positions()
		
		if featuresNew != None:
			# get the length once
			self.dnaLength 			= float(len(genbank.gb.GetDNA()))
		else:
			self.dnaLength 			= 1 # or 0 but this might cause problems if there is a division x/length

		# compare old and new
		if featuresOld != featuresNew:

			# print ("DO recalc features")
			# we only have to redraw if the length, the color or direction changed
			# calculate and save all the features
			allFeatures = {} # storing list
			# some basic info we have to update:
			
			self.drawn_fw_locations = []
			self.drawn_rv_locations = []
			

			labelFeature = [] 	# labels for features outside of the arrow
								# save as [[middle, name, index, hitname, y],[...]]

			
			# loop the features and get the path
			for feature in 	featuresNew:
				featuretype, complement, start, finish, name, index = feature
				hitname 	= self.hitName(name, index)
				
				# do not display the annoying source
				if featuretype != "source": 
					# path creation
					allFeatures[hitname] = [self.cairoFeature(feature), featuretype]
				
					# label handling
					length 		= self.dnaLength
					middle 		= start + (finish-start)/2 % length
				
					name		= self.nameBeautiful(name)
					y 			= None # to be populated
					labelFeature.append([middle, name, index, hitname, y])
				
			# save the new paths in the fancy storing class for further processing
			self.plasmidstore.drawnfeatures = allFeatures
			
			# save label drawn outside as text
			self.plasmidstore.LoF = labelFeature

		
		


		self.plasmidstore.features = featuresNew # everything is over and the new are the current (old)
		return redrawfeatures
		
		
	def cairoFeature(self, feature):
		featurepath = None
		featuretype, complement, start, finish, name, index = feature
		name 	= self.nameBeautiful(name)
		hname 	= self.hitName(name, index)
		

		
		# create the correct path for foreward and reverse
		if complement == False:
			self.drawn_fw_locations, levelMult = self.find_overlap(self.drawn_fw_locations, (start, finish))
			arrowMult 	= 1 # to account for the different direction of addition
			radiusAdd	= levelMult * (self.arrowWidth + 0.5) + 0.4
			radiusO 	= self.radiusO + radiusAdd + self.arrowWidth
			radiusI 	= self.radiusO + radiusAdd
			
			
		else:
			self.drawn_rv_locations, levelMult = self.find_overlap(self.drawn_rv_locations, (start, finish))		
			
			arrowMult 	= -1 # to account for the different direction of addition
			radiusAdd	= levelMult * (self.arrowWidth + 0.5) + 0.4
			radiusO 	= self.radiusI - radiusAdd - self.arrowWidth
			radiusI 	= self.radiusI - radiusAdd
			
		# calculate the radians for end and start
		s = self.position2angle(start)
		e = self.position2angle(finish)

		
		# large features get a arrow head:
		arrowHead = False
		if abs(e - s) > math.radians(5): # more then x degrees
			arrowHead = True
		
		
		if arrowHead == True and complement == False:
			# arrow head has x radians
			r 		= radiusI + arrowMult * self.arrowWidth/2
			xA, yA 	= self.radial2cartesian(r, e)
			e 		= e - math.radians(2) # new end
			
			self.ctx.new_path()
			self.ctx.arc(0,0,radiusI, s,e)
		
			# draw arrow head sometimes:
			self.ctx.line_to(xA, yA)
			#self.ctx.move_to(cx1, cy1)
			self.ctx.arc_negative(0,0,radiusO, e,s)
			self.ctx.close_path()
			featurepath  = self.ctx.copy_path()
			self.ctx.new_path() # clear canvas
		elif arrowHead == True and complement == True:
			# arrow head has x radians
			r 		= radiusI + arrowMult * self.arrowWidth/2
			xA, yA 	= self.radial2cartesian(r, s)
			s 		= s + math.radians(2) # new "start"
			
			self.ctx.new_path()
			self.ctx.arc_negative(0,0,radiusI, e,s)
			self.ctx.line_to(xA, yA)
			self.ctx.arc(0,0,radiusO, s,e)
			self.ctx.close_path()
			featurepath  = self.ctx.copy_path()
			self.ctx.new_path() # clear canvas
		else:
			self.ctx.new_path()
			self.ctx.arc(0,0,radiusI, s,e)
			self.ctx.arc_negative(0,0,radiusO, e,s)
			self.ctx.close_path()
			featurepath  = self.ctx.copy_path()
			self.ctx.new_path() # clear canvas
		
		# save the outer radius to allow draing of the lines:
		if complement == False:
			self.plasmidstore.radiusOuter[hname] = radiusO
		else:
			self.plasmidstore.radiusOuter[hname] = radiusI
		
		return featurepath
	
	
	
	def caironameLabels(self, labels, site, kind):
		''' create paths for labels given'''
		
		if kind == "features":
			self.ctx.select_font_face('Arial', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
		else:
			self.ctx.select_font_face('Arial', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
		self.ctx.set_font_size(self.lH)
		textPaths			= {}
		linePaths 			= {}
		underlinePaths 		= {}
		boxPaths			= {}
		
		shift = 4 # shift the labels closer to the plasmid, as the have a bigger radius
		
		
		for l in labels:
			name 	= l[1]
			y 		= l[4]
			if kind == "enzymes":
				name2		= "%s%s" %(name, self.enzymeGap)
			else:
				name2		= name
			hname 	= self.hitName(name2, l[2])

					
			xbearing, ybearing, TextWidth, TextHeight, xadvance, yadvance = self.ctx.text_extents(name)

			if site == "left":
				x = - math.cos(math.asin(y/self.radiusLabels)) * self.radiusLabels - TextWidth + shift
			else:
				x = math.cos(math.asin(y/self.radiusLabels)) * self.radiusLabels - shift
			self.ctx.move_to(x, y);
			self.ctx.text_path(name)
						
			# save label:
			path		 = self.ctx.copy_path()
			textPaths[hname] = path
			self.ctx.new_path() # clear canvas
			
			# make a box behind the text, so we can select more easys:
			self.ctx.rectangle(x, y - self.lH, TextWidth, self.lH)
			boxPaths[hname] = self.ctx.copy_path()
			self.ctx.new_path()
			
			# featurs have a underline:
			if kind == "features":
				self.ctx.move_to(x, y)
				self.ctx.line_to(x + TextWidth, y)
				path		 = self.ctx.copy_path()
				underlinePaths[hname] = path
				self.ctx.new_path() # clear canvas
			
			# make a line from text to plasmid:
			if site == "left":
				x = x + TextWidth
			else:
				x = x
			
			# get the radius to where the line should lead:
			try:
				r = self.plasmidstore.radiusOuter[hname]
			except:
				r = self.radiusI
					
			y1 = y - self.lH/2 + 0.3
			self.ctx.move_to(x, y1)
			x2, y2 = self.position2cartesian(l[0], self.radiusO + 7)
			x3, y3 = self.position2cartesian(l[0], r)
			self.ctx.line_to(x2,y2)
			self.ctx.line_to(x3,y3)
			linePaths[hname] = self.ctx.copy_path() # save line path
			self.ctx.new_path() # clear canvas
			
			
		return textPaths, linePaths, boxPaths, underlinePaths

	def checkLabelCalculations(self):
		''' this functions takes the current positions and calcualtions
			of the labels and checks fast if it has to recalculated anything'''
		# labels to handle:
		LoF = self.plasmidstore.LoF
		LoE = self.plasmidstore.LoE
		
		# check if the labels changed, onyl recalculate everything, if they did
		if LoF != self.plasmidstore.LoFold or LoE != self.plasmidstore.LoEold:
			# print("DO recalculate label")
			# sort labels for their middle position:
			LoF = sorted(LoF, key=lambda x: x[0]) 
			LoE = sorted(LoE, key=lambda x: x[0])
		
			# group the labels
			# TODO, should be interactive anyway
		
			# now split in two sites:
			half = self.dnaLength/2
			# Features
			LoF1 = [] # first half
			LoF2 = [] # second half
			LoF1I = [] # indexing
			LoF2I = [] # indexing
			for l in LoF:
				if l[0] < half:
					LoF1.append(l)
					LoF1I.append(l[3])
				else:
					LoF2.append(l)
					LoF2I.append(l[3])
			# Enzymes
			LoE1 = [] # first half
			LoE2 = [] # second half
			LoE1I = [] # indexing
			LoE2I = [] # indexing
			for l in LoE:
				if l[0] < half:
					LoE1.append(l)
					LoE1I.append(l[3])
				else:
					LoE2.append(l)
					LoE2I.append(l[3])
			

		
			# then check the numbers
			LoF1, LoE1 = self.checkNumberOfLabels(LoF1, LoE1)
			
			# arrange and draw labels:
			Labels1 = self.arrangeLabels(LoF1, LoE1, -1)
			Labels2 = self.arrangeLabels(LoF2, LoE2, +1)
			
			# sperate into features and enzymes... 
			# this must be, so we can order them together, but color them
			# different
			LabelsFeatures1 	= []
			LabelsFeatures2 	= []
			LabelsEnzymes1	 	= []
			LabelsEnzymes2 		= []
			for label in Labels1:
				hit = label[3]
				if hit in LoF1I:
					LabelsFeatures1.append(label)
				if hit in LoE1I:
					LabelsEnzymes1.append(label)
			for label in Labels2:
				hit = label[3]
				if hit in LoF2I:
					LabelsFeatures2.append(label)
				if hit in LoE2I:
					LabelsEnzymes2.append(label)
			# done serperating the labels agai				

			
			# FEATURES: make the paths for features
			laP1, liP1, bP1, UlP1	= self.caironameLabels(LabelsFeatures1, "right", "features")
			laP2, liP2, bP2, UlP2  	= self.caironameLabels(LabelsFeatures2, "left", "features")
			# FEATURES: save paths for displaying
			self.plasmidstore.drawnlabels 			= self.merge_dicts(laP1, laP2) 
			self.plasmidstore.drawnLLines 			= self.merge_dicts(liP1, liP2) 
			self.plasmidstore.labelBoxes 			= self.merge_dicts(bP1, bP2) 
			self.plasmidstore.labelULine 			= self.merge_dicts(UlP1, UlP2) 
			
			
			
			# ENZYMES: make the paths for enzymes
			laP1, liP1, bP1, UlP1	= self.caironameLabels(LabelsEnzymes1, "right", "enzymes")
			laP2, liP2, bP2, UlP2 	= self.caironameLabels(LabelsEnzymes2, "left", "enzymes")
			# ENZYMES: save paths for displaying
			self.plasmidstore.drawnlabelsE 			= self.merge_dicts(laP1, laP2) 
			self.plasmidstore.drawnLLinesE 			= self.merge_dicts(liP1, liP2) 
			self.plasmidstore.labelBoxesE 			= self.merge_dicts(bP1, bP2) 

			
			# save the new status quo of the labels 
			self.plasmidstore.LoFold = self.plasmidstore.LoF
			self.plasmidstore.LoEold = self.plasmidstore.LoE
			
			return None
		else:
			# print("NOT recalculate label")
			return None

	
	def checkNumberOfLabels(self, LoF=None, LoE=None):
		''' this function is called before label sorting'''
		
	
		# determine height of label displaying area
		height = self.radius * 2.2 
		# divide by the height of each label
		lH = self.ctx.device_to_user_distance(self.labelHeight,0)[0] # label height
		lH = round(lH, 3)
		self.lH = lH # for other functions to use
		# round down, to make it work
		nL = math.trunc(height/lH)
	
		# number of labels to handle
		nFL = len(LoF)
		nEL = len(LoE)
	
		if nFL + nEL <= nL:
			# no problem here
			return LoF, LoE # return feature Label and Text Label
		elif nFL <= nL:
			# reduce the nTL so, that it may work.
			# reduce most populated areas
			# print("group some Restrictionsite labels agressivly")
			# call function and check again if we can fit everything in
			# if it did work--> fine
			# else show it anyway --> or figure something out
			return LoF, LoE # return feature Label and Text Label
		elif nFL > nL:
			# we have to many feature labels.
			# so we hide all TL and remove the labels of the smallest features!
			# print("delete some Restrictionsite labels")
			return LoF, LoE # return feature Label and Text Label
		else:
			# print("check Number of labels: undefined")
			return LoF, LoE # return feature Label and Text Label
	

	#def groupLabels(self, labels):
	#	''' restrictionenzymelabels can be grouped 
	#		as they somtimes occure at the very same place '''
	#	lastPos = None
	#	i		= 0
	#	for l in labels:
	#		pos = l[0]
	#
	#		if pos == lastPos:
	#			# two labels at the very same position
	#			# we merge the names
	#			cname 	= l[1]
	#			oname 	= labels[i][1]
	#			name 	= "%s, %s"
	#		i = i + 1
	#	# we can group labels of same type and same position
	#	return None

	def groupAgressivly(self):
		# we can group some close restrictionlabels 
		# and only show the position indicating by a number
		return None

	def arrangeLabels(self, F, E, index):
		''' this function does the actual positioning of all labels'''
		
		# now they are equal
		# enzymes, features etc: merge
		Labels = F + E
		Labels = sorted(Labels, key=lambda x: x[0]) 
		
		# find the most horiziontal one
		horzI = None
		horzN = self.dnaLength
		if index == -1:
			target = self.dnaLength / 4 * 1
		else:
			target = self.dnaLength / 4 * 3

		i = 0
		# start by putting the initial position
		for l in Labels:
			# append a new entry with the current y coordinate:
			x,y = self.position2cartesian(l[0], self.radiusLabels - 2)
			l[4] =  round(y, 3)
			
			# most horizontal one:
			if abs(l[0] - target) < horzN:
				horzN = abs(l[0] - target) 	# save new differenze
				horzI = i					# save index
			
			i = i + 1
		
		
		
		# create series for loop for better positioning
		# this way we start positioning in the hotizontal position and 
		# arrange the labels in both directions more accurate
		h 		= 1
		loop 	= [horzI]
		newI 	= horzI
		switch 	= 1
		both	= True
		while h < len(Labels):
			if newI > 0 and newI < len(Labels)- 1 and both == True:
				newI = newI + switch * h
				switch = -1 * switch
			elif newI == 0:
				switch = 1
				newI = newI + switch * h
				both = False
			elif newI == len(Labels) - 1:
				switch = -1
				newI = newI + switch * h
				both = False
			else:
				newI = newI + switch * 1

			loop.append(newI)
			h = h + 1

		# now iterate n times and check if x[i] overlaps with either x[i+1] or x[i-1]
		# this is the key element of the label positioning algorythm
		# n is critical for performance, higher n = more steps
		# dy is the step size in px
		dy 			= self.ctx.device_to_user_distance(5,0)[0] # amount of movement in pixel
		i 			= 0 		# just a counter
		n 			= 100		# n of steps
		overlapp 	= 1			# initial overlap, so it is not 0
		while i < n and overlapp > 0:

			overlapp = 0
			#for l in Labels:
			for a in loop:
				l = Labels[a]
				# for those labels with one upper and lower label
				y = l[4]			# y
				U = y + self.lH		# upper border
				L = y				# lower border
				
				# get the correct upper and lower bound of the adjacend features or the end of the radius
				if index > 0:
					if a == 0:
						# next label
						nL = +self.radiusLabels
					else:
						# next label
						nextLabel = Labels[a - 1]
						nL = nextLabel[4]		# next lower
						
					if a == len(Labels)-1:
						lU = -self.radiusLabels
					else: 
						# previous label
						prevLabel = Labels[a + 1]
						lU = prevLabel[4] + self.lH # last upper
				else:
					if a == len(Labels)-1:
						# next label
						nL = self.radiusLabels
					else:
						# next label
						nextLabel = Labels[a + 1]
						nL = nextLabel[4]		# next lower
						
					if a == 0:
						lU = -self.radiusLabels
					else: 
						# previous label
						prevLabel = Labels[a - 1]
						lU = prevLabel[4] + self.lH # last upper
				
				
				
				# check upper bound
				dU = U - nL
				# check lower bound 
				dL = lU - L
	

				# move the item up or down, according to its overlapping
				if dU > 0 or dL > 0:
					
					# we need to move somewhere
					if dU > 0 and dL <= 0:
						# move down
						y = y - dy

						# raise the overlap
						overlapp = overlapp + dU
					elif dU <= 0 and dL > 0:
						# we need to move up
						y = y + dy

						# raise the overlap
						overlapp = overlapp + dL
					elif dU > 0 and dL > 0:
						# we need to make them even
						y = y + (dU - (dU + dL)/2)

						# raise the overlap
						overlapp = overlapp + dU + dL
					
					# prevent to large y
					if y > self.radiusLabels:
						y = self.radiusLabels
					elif y < -self.radiusLabels:
						y = - self.radiusLabels
					

					# save y:
					l[4] = y
	
			# print("DO reposition labels, Round: ", i)
			i = i + 1
	
		# after n cycles stop calculations and return the positions
		return Labels

	
	
	def checkEnzymeCalculations(self):
		
		# load enzymes
		enzymes 	=  genbank.gb.restrictionEnzymes.selection
		# create hash of selection and DNA
		hashable 	= "%s%s" % (frozenset(enzymes), genbank.gb.gbfile['dna'])
		e1 			= hashlib.md5(hashable).hexdigest()

		labels = []

		if e1 !=self.plasmidstore.enzymes:
			# we have new enzymes
			index = 0
			for e in enzymes:
				for site in enzymes[e].restrictionSites:
					name, start, end, cut51, cut52, dnaMatch = site
					length 		= self.dnaLength
					middle 		= start + (end-start)/2 % length
					name		= self.nameBeautiful(name)
					name2		= "%s%s" %(name, self.enzymeGap)
					hitname 	= self.hitName(name2, index)
					y 			= None # to be populated
					labels.append([middle, name, index, hitname, y])
					
					index = index + 1
			self.plasmidstore.enzymes= e1
			self.plasmidstore.LoE = labels
		
		return None
	
	
	def checkSelectionCalculations(self):
		''' only chnage the arc if we changed something '''
		# selection
		selOld 		= self.plasmidstore.interaction["selectionOld"]
		sel 		= self.plasmidstore.interaction["selection"]

		
		if sel != selOld:
			if sel[1] == -1:
				# remove selection arc
				self.plasmidstore.drawnSelection = None
			else:
				# draw selection arc
				pos1, pos2, zero = sel

				a1 = self.position2angle(pos1)
				a2 = self.position2angle(pos2)
				
				if (pos1 < pos2 and zero == -1) or (pos2 < pos1 and zero == 1):
					self.ctx.arc(0,0,self.radius,a1,a2)
				else :
					self.ctx.arc_negative(0,0,self.radius,a1,a2)
				path = self.ctx.copy_path() # save line path
				self.ctx.new_path() 		# clear canvas
				self.plasmidstore.drawnSelection = path
			self.plasmidstore.interaction["selectionOld"] = sel
		
		
		# draw the line for indicating the position
		position 	= self.plasmidstore.interaction["position"]
		positionOld	= self.plasmidstore.interaction["positionOld"]

		if position != positionOld:
			if position < self.dnaLength and position > 0:
				# get the coordinates for the line
				x1, y1 = self.position2cartesian(position, self.radiusI - 1)
				x2, y2 = self.position2cartesian(position, self.radiusO + 1)
				# draw the actual line
				self.ctx.move_to(x1,y1)
				self.ctx.line_to(x2,y2)
				# copy, save and delete
				path = self.ctx.copy_path()
				self.plasmidstore.drawnPosition = path
				self.ctx.new_path() 		
				# save settings
				
			else:
				self.plasmidstore.drawnPosition = None
		
		self.plasmidstore.interaction["positionOld"] = position
		return None
	
	############################################
	# drawing functions

	def draw(self):
		''' # print the stored elements on th canvas'''
		# prepare the canvas
		width, height = self.GetVirtualSize()
		if width < height:
			ratio = float(width)/float(height)
			# only resize the canvas if software is loaded. This prevents error messages
			if width != 0 and height != 0 and width > 100 and height > 100:
				self.ctx.scale(width, height*ratio) # Normalizing the canvas
				self.ctx.scale(0.01, 0.01) 			# make it 100*ratiox100
				self.ctx.translate (50,50/ratio) 		# set center to 0,0
		if width >= height:
			ratio = float(height)/float(width)
			# only resize the canvas if software is loaded. This prevents error messages
			if width != 0 and height != 0 and width > 100 and height > 100:
				self.ctx.scale(width*ratio, height) # Normalizing the canvas
				self.ctx.scale(0.01, 0.01) 			# make it 100*ratiox100
				self.ctx.translate (50/ratio,50) 		# set center to 0,0
		self.drawFeatures()
		self.drawLabels()
		self.drawSelection()
		return None

	def drawFeatures(self):
		# print("DO output path feature")
		# only resize the canvas if software is loaded. This prevents error messages

		# draw the helper plain white circle to make selection possible:
		self.ctx.arc(0,0,self.radiusO+1,0,2*math.pi)		# outer circle
		self.ctx.set_source_rgb (1, 1, 1) 				# white
		self.ctx.set_line_width (0)						# no line
		self.plasmidstore.interaction["markerArea1"] = self.ctx.copy_path()	# copy for later
		self.ctx.new_path()								# remove

		self.ctx.arc(0,0,self.radiusI-1,0,2*math.pi)		# inner circle
		self.ctx.set_source_rgb (1,1,1)					# Solid white
		self.ctx.set_line_width (0)						# no line
		self.plasmidstore.interaction["markerArea2"] = self.ctx.copy_path()	# copy for later
		self.ctx.new_path()								# remove

		# draw the plasmid (two circles with different radius)
		radius=10
		self.ctx.arc(0,0,self.radiusO,0, 2*math.pi)		# circle
		self.ctx.set_source_rgb (0, 0, 0) 			# Solid color
		self.ctx.set_line_width (0.24)				# line width
		self.ctx.stroke()					# stroke only no fill!

		# inner plasmid
		self.ctx.arc(0,0,self.radiusI,0,2*math.pi)		# inner circle
		self.ctx.stroke()					# stroke the same color and settings


		# draw the buffered featrues:
		i  = 0
		for a in self.plasmidstore.drawnfeatures:
			path 		= self.plasmidstore.drawnfeatures[a][0]
			# get color
			featuretype = self.plasmidstore.drawnfeatures[a][1]
			color = eval(featuretype)['fw'] #get the color of feature (as string)
			assert type(color) == str
			r,g,b = colcol.hex_to_rgb(color)
			r = float(r)/255
			g = float(g)/255
			b = float(b)/255

			# put the path on the canvas and fill
			#self.ctx.new_path()
			self.ctx.append_path(path)
			self.ctx.set_source_rgba (r,g,b,1.0) # Solid color
			self.ctx.fill_preserve()
			
			# outline for the feature that is beneeth the mouse:
			if a == self.Highlight:
				#self.ctx.set_source_rgb (1, 0, 0)
				self.ctx.set_line_width(0.3)
				self.ctx.stroke()
			else:
				# remove path
				self.ctx.new_path()
			
			i = i + 1
		
		return None



	def drawLabels(self):
		''' function to put the labels on the canvas'''
		
		
		# FEATURES
		self.ctx.set_source_rgb (0, 0, 0) 			# Solid color
		self.ctx.set_line_width(0.1)
		# draw feature label names
		for a in self.plasmidstore.drawnlabels:
			path = self.plasmidstore.drawnlabels[a]
			self.ctx.append_path(path)
			# darker color for the feature that is beneeth the mouse:
			if a == self.Highlight:
				self.ctx.set_source_rgba (0, 0, 0, 1)
			else:
				self.ctx.set_source_rgba (0, 0, 0, 0.7)
			self.ctx.fill()
		
		# draw the lines
		for a in self.plasmidstore.drawnLLines:
			path = self.plasmidstore.drawnLLines[a]
			self.ctx.append_path(path)
			
			# darker color for the feature that is beneeth the mouse:
			if a == self.Highlight:
				self.ctx.set_source_rgba (0, 0, 0, 1)
			else:
				self.ctx.set_source_rgba (0, 0, 0, 0.3)
			self.ctx.stroke()
			
		# draw the underlines
		#for a in self.plasmidstore.labelULine:
		#	path = self.plasmidstore.labelULine[a]
		#	self.ctx.append_path(path)
		#	self.ctx.set_line_width(2)
		#	
		#	# darker color for the feature that is beneeth the mouse:
		#	if a == self.Highlight:
		##		self.ctx.set_source_rgba (0, 0, 0, 1)
		#	else:
		#		self.ctx.set_source_rgba (0, 0, 0, 0.3)
		#	self.ctx.stroke()
		
		
		# ENZYMES
		self.ctx.set_source_rgb (0, 0, 0) 			# Solid color
		self.ctx.set_line_width(0.1)
		# draw feature label names
		for a in self.plasmidstore.drawnlabelsE:
			path = self.plasmidstore.drawnlabelsE[a]
			self.ctx.append_path(path)
			# darker color for the feature that is beneeth the mouse:
			if self.Highlight != None and a.split(self.enzymeGap)[0] == self.Highlight.split(self.enzymeGap)[0]:
				self.ctx.set_source_rgba (0, 0, 0, 1)
			else:
				self.ctx.set_source_rgba (0, 0, 0, 0.7)
			self.ctx.fill()
		
		# draw the lines
		for a in self.plasmidstore.drawnLLinesE:
			path = self.plasmidstore.drawnLLinesE[a]
			self.ctx.append_path(path)
			
			# darker color for the feature that is beneeth the mouse:
			if self.Highlight != None and a.split(self.enzymeGap)[0] == self.Highlight.split(self.enzymeGap)[0]:
				self.ctx.set_source_rgba (0, 0, 0, 1)
			else:
				self.ctx.set_source_rgba (0, 0, 0, 0.3)
			self.ctx.stroke()
		
	def drawSelection(self):
		''' draw arc for selection '''
		if self.plasmidstore.drawnSelection != None:
			#0082ED
			r,g,b = colcol.hex_to_rgb('#0082ED') 

			r = float(r)/255
			g = float(g) /255
			b = float(b) /255

			self.ctx.set_source_rgba (r,g,b, 0.6) 			# Solid color
			self.ctx.set_line_width(3)
			path = self.plasmidstore.drawnSelection
			self.ctx.append_path(path)
			self.ctx.stroke()
		
		if self.plasmidstore.drawnSelection == None and self.plasmidstore.drawnPosition != None:
			self.ctx.set_source_rgba (0,0,0) 			# Solid color
			self.ctx.set_line_width(0.2)
			path = self.plasmidstore.drawnPosition
			self.ctx.append_path(path)
			self.ctx.stroke()