示例#1
0
    def OnPaint(self, evt):
        dc = wx.PaintDC(self)
        #dc.Clear()
        w, h = dc.GetSizeTuple()
        dc.SetPen(wx.RED_PEN)
        cr = ContextFromDC(dc)
        cr.scale(w, h)
        cr.set_source_rgb(1, 0, 1)
        cr.set_line_width(0.002)
        path = self.path.send(cr)
        x, y = self.data.next()

        #dc.DrawLines(numpy.array([x*w,y*h]).T)
        cairo_tools.polyline(cr, x, y)
        cairo_tools.stamp_at(cr, path, x, y)
        cr.stroke()
示例#2
0
class TextEdit(DNApyBaseDrawingClass):
	'''
		new Text editor that allows to display dna and
		complementary dna in a usefull manner
		
		Also it can and should show:
			- features (done, buggy)
			- restriction enzymes (partly)
			- dna modifications (todo)
			- primer (todo)
	'''
	def __init__(self, parent, id):
		
		#parent
		self.parent = parent
		
		# get data
		self.dna 			= genbank.gb.GetDNA()
		self.cdna 			= genbank.gb.GetDNA()

		# settings for the editor 
		self.sY				= 60	# offset top
		self.dX				= 8 	# border left and right
		self.textSpacing 	= 80	# seperation of lines
		self.fontsize 		= 9		# line size
		self.lineGap 		= 6
		
		# interactive cursor
		self.PositionPointer 		= 1
		self.PositionPointerEnd 	= False
		
		# initial minimal height of the editor 
		self.minHeight = 200
		
		# storage dict, to prevent recalculating to much
		self.cairoStorage = {
				'features'	: None, # feature Object
				'fPaths' 	: [], 	# path of alls features as List
				'cursorPath': None,
				'cursor'	: None,
				'enzymes'	: {},	# enzymes object
				'ePaths'	: [],	# enzymes drawn
				'ticks'		: None,	# ticks object --> self.dna
				'tPaths'	: []	# ticks drawn		
		}
		
		
		
		# call window
		DNApyBaseDrawingClass.__init__(self, parent, wx.ID_ANY)

		# bind events
		self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDouble)
		self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
		self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
		self.Bind(wx.EVT_MOTION, self.OnMotion) # drag and drop, selection etc.
		self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPress) #This is important for controlling the input into the editor
		

####### Modify methods from base calss to fit current needs #########

	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="DNA 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.
		"""

		#####################
		# remove buffered features, to force redraw:
		self.cairoStorage['features'] = None
		
		# The Buffer init is done here, to make sure the buffer is always
		# the same size as the Window
		wC, hC = self.parent.GetClientSize()

		wP, hP = self.parent.GetSize()
		self.SetSize(wx.Size(wC, self.minHeight))
		self.parent.SetVirtualSize(wx.Size(wC-30, self.minHeight))
		

		# Make new offscreen bitmap: this bitmap will always have the
		# current drawing in it, so it can be used to save the image to
		# a file, or whatever.
#		if wC != 0 and hC !=0:
#			self._Buffer = wx.EmptyBitmap(wC, self.minHeight)

#		else:
#			self._Buffer = wx.EmptyBitmap(wP, hP)
				
		##########################

		# start dc
		dc = wx.MemoryDC()
		dc.SelectObject(self._Buffer)
		dc.SetBackground(wx.Brush("White"))
		dc.Clear() 
		# make a cairo context
		self.ctx = ContextFromDC(dc)		
		# start pango as a font backend
		self.pango = pangocairo.CairoContext(self.ctx)
		self.pango.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
		
		# reload dna
		self.dna = genbank.gb.GetDNA()
		if self.dna != None:
			self.cdna 	= dna.C(self.dna)
		else:
			self.dna 	= ''	# no dna, if there is nothing
			self.cdna 	= ''	# empty cdna
			
		# render the text
		self.displayText()

		# create colorful features here
		self.drawFeatures()
		
		# draw enzymes
		self.drawEnzymes()
		
		# draw a cursor
		self.drawCursor()
		
		# draw Tics above all!
		self.drawTicks()
	
		
		# end of canvas handling
		dc.SelectObject(wx.NullBitmap) # need to get rid of the MemoryDC before Update() is called.
		self.Refresh()
		self.Update()

		return None
	
	
		
	
	def displayText(self):
		''' main function for the editors text capabilities
			here the text gets positioned and all its color.
			Also the selection highlight is done here'''
		
		# get window size to wrap text
		width, height 	= self.parent.GetVirtualSize()
		if width > 30:
			w = width - self.dX
		else:
			w = width
		# save with for other functions
		self.w = w

		# start layout
		layout 			= self.pango.create_layout()
		layout.set_wrap(pango.WRAP_WORD_CHAR)
		layout.set_width(pango.SCALE * w)
		layout.set_spacing(pango.SCALE *self.textSpacing) 
		layout.set_alignment(pango.ALIGN_LEFT)

		
		# new attr list for the dna
		dnaAttr = pango.AttrList()
		# color
		gray 		= 56 * 65535/255
		fg_color	= pango.AttrForeground(gray,gray,gray, 0, len(self.dna))
		spacing 	= pango.AttrLetterSpacing(1500, 0, len(self.dna))
		# add attr
		dnaAttr.insert(fg_color)
		dnaAttr.insert(spacing)
		# set attr
		layout.set_attributes(dnaAttr)
		
		#set font
		font = pango.FontDescription("monospace normal "+ "1")
		font.set_size(pango.SCALE * self.fontsize)
		layout.set_font_description(font)
		
		# font and color are prepared for the DNA

		
		
		# get selection, if any
		start, end, zero = self.get_selection()
		if end != -1:
			# correct direktion of selection
			if start > end:
				s 	= end - 1
				e 	= start
			else:
				s 	= start - 1
				e	= end
		# make markup selection
		if abs(end-start) >= 0 and zero == -1 and end != -1 :
			senseText = self.dna[0:s] + '<span background="#0082ED">'+self.dna[s:e] + '</span>' + self.dna[e:]
			antiText = self.cdna[0:s] + '<span background="#0082ED">'+self.cdna[s:e] + '</span>' + self.cdna[e:]	
			
		elif abs(end-start) >= 0 and zero == 1 and end != -1:
			senseText = '<span background="#0082ED">'+self.dna[0:s] + '</span>' + self.dna[s:e] + '<span background="#0082ED">'+self.dna[e:] + '</span>'
			antiText = '<span background="#0082ED">'+self.cdna[0:s] + '</span>' + self.cdna[s:e] + '<span background="#0082ED">'+self.cdna[e:] + '</span>'
		else:
			senseText = self.dna
			antiText  = self.cdna
		
		# output dna
		self.ctx.move_to(self.dX,self.sY)
		layout.set_markup(senseText)
		self.pango.update_layout(layout)
		self.pango.show_layout(layout)
		
		# copy sense layout
		self.senseLayout = layout.copy() 

		
		# determine the length and height of a line
		self.lineLength =  layout.get_line(0).length
		x1, y1, w1, h1 	= self.senseLayout.index_to_pos(1)
		x2, y2, w2, h2 	= self.senseLayout.index_to_pos(1+self.lineLength)
		self.lineHeight = abs(y1 - y2) / pango.SCALE
		
		# print anti sense strain
		self.ctx.move_to(self.dX,self.sY + self.fontsize + self.lineGap)
		layout.set_markup(antiText)
		self.pango.update_layout(layout)
		self.pango.show_layout(layout)
		
		# update the height of the context, based on the inserted dna
		w, h = layout.get_pixel_size()
		self.minHeight = h + 2*self.sY
		# resize window
#		w,h = self.GetSize()					# prevents missfomration on resize
#		self.SetSize(wx.Size(w,self.minHeight)) # prevents missfomration on resize
		
		return None
	
	
	def drawFeatures(self):
		features 	= genbank.gb.get_all_feature_positions()
		paths 		= {} # (color, path)
		
		featureheight = 10
		
		if self.cairoStorage['features'] != features :
			# something changed, we have to draw the paths new
			self.drawn_fw_locations = []
			self.drawn_rv_locations = []
			
			# font settings
			# draw the name somewhere at the start
			nameheight = 9 # px
			self.ctx.select_font_face('Arial', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
			self.ctx.set_font_size(nameheight)
			
			for feature in features:
				 
				featuretype, complement, start, finish, name, index = feature
				
				if featuretype != "source":
				
					# get names
					hname 	= self.hitName(name, index)
					name	= self.nameBeautiful(name)
					#get position
					xs, ys, ws, hs = self.senseLayout.index_to_pos(start)
					xf, yf, wf, hf = self.senseLayout.index_to_pos(finish+1)
					# linecount:
					# if ys and yf are not the same, we have feature over multiple lines!
					dy 		= abs(ys - yf) / pango.SCALE
					ySteps 	= dy / self.lineHeight # number of lines!
					xs 		= xs / pango.SCALE
					xf 		= xf / pango.SCALE
					ys		= (ys / pango.SCALE) + self.sY 
					yf		= yf / pango.SCALE
					
					# first sense strain over the text
					if complement == False:
						self.drawn_fw_locations, l = self.find_overlap(self.drawn_fw_locations, (start, finish))				
						level = l * (featureheight+6)
					

						xbearing, ybearing, TextWidth, TextHeight, xadvance, yadvance = self.ctx.text_extents(name)				
						ys = ys  - level - 5
						self.ctx.move_to(xs + 10, ys )
						self.ctx.text_path(name)
						textpath = self.ctx.copy_path()
						self.ctx.new_path()
						# end name
					
						# box for name
						self.ctx.move_to(xs, ys)
						self.ctx.rectangle(xs, ys-featureheight, TextWidth + 20 , nameheight + 5)
					
						# lines above the dna
						while ySteps >= 0:
							if ySteps == 0:
								xE = xf
							else:
								xE = self.w + 5 # TODO: figure out, why we need to add 5 px, to make it look correct
 
							# make a box-path
							self.ctx.move_to(xs, ys)
							self.ctx.rectangle(xs, ys-5, xE-xs , 5)
							
							# the second x start is always 8px, or what we will opt for
							xs = self.dX
							ys = ys + self.lineHeight
							ySteps = ySteps - 1
						
						# path
						fillpath = self.ctx.copy_path()
						self.ctx.new_path()
						# get color
						color = eval(featuretype)['fw'] #get the color of feature (as string)
					else:
						self.drawn_rv_locations, l = self.find_overlap(self.drawn_rv_locations, (start, finish))				
						level = l * (featureheight+6)
						# set y
						ys = ys + 2*self.fontsize + self.lineGap + 9 + level 

						# lines below the dna
						while ySteps >= 0:
							if ySteps == 0:
								xE = xf
							else:
								xE = self.w

							# make the path
							#self.ctx.move_to(xs, ys)
							#self.ctx.line_to(xE, ys)
							self.ctx.move_to(xs, ys)
							self.ctx.rectangle(xs, ys+5, xE-xs , 5)
						
							xs = self.dX
							ys = ys + self.lineHeight
							ySteps = ySteps - 1
						
						# change y:
						ys = ys - self.lineHeight
						# buffer path
						bufferPath = self.ctx.copy_path()
						self.ctx.new_path()
						
						# name at the end:
						xbearing, ybearing, TextWidth, TextHeight, xadvance, yadvance = self.ctx.text_extents(name)	
						self.ctx.move_to(xf - TextWidth - 10, ys + 10 )
						self.ctx.text_path(name)
						textpath = self.ctx.copy_path()
						self.ctx.new_path()
						# end name

						# box for name
						self.ctx.append_path(bufferPath)
						self.ctx.move_to(xf - TextWidth, ys + featureheight)
						self.ctx.rectangle(xf - TextWidth - 20, ys, TextWidth + 20 , nameheight + 5)

						# get path
						fillpath = self.ctx.copy_path()
						self.ctx.new_path()
						# get color
						color = eval(featuretype)['fw'] #get the color of feature (as string)

					
					paths[hname] = (color,fillpath ,textpath)
					self.ctx.new_path() # clear canvas
			# save the paths
			self.cairoStorage['fPaths'] =	paths
			# update storage status
			self.cairoStorage['features'] = features
			
		# draw the beautiful features
		
		for a in self.cairoStorage['fPaths']:
			color, path, tpath = self.cairoStorage['fPaths'][a]
			
			# get color
			assert type(color) == str
			r,g,b = colcol.hex_to_rgb(color)
			r = float(r)/255
			g = float(g)/255
			b = float(b)/255
			
			# append and draw path
			self.ctx.append_path(path)
			self.ctx.set_line_width(1)
			self.ctx.set_source_rgba(r ,g ,b , 1) # Solid color
			#self.ctx.stroke_preserve()
			self.ctx.fill()
			# write text
			self.ctx.append_path(tpath)
			self.ctx.set_line_width(1)
			self.ctx.set_source_rgba(1 ,1 ,1 , 1) # Solid color
			self.ctx.fill()

		return None
	
	
	def drawEnzymes(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()
		
		if e1 != self.cairoStorage['enzymes']:
			nameheight = 9 # px
			self.ctx.select_font_face('Arial', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
			self.ctx.set_font_size(nameheight)
			dna 			= self.dna
			length			= len(dna)
			index			= 0	# same as in plasmid_GUI.py
			enzymesPaths 	= {}
			# loop enzymes ordered dict
			for e in enzymes:
				for site in enzymes[e].restrictionSites:
					name, start, end, cut51, cut52, dnaMatch = site
					startRef 	= start - 1
					hitname 	= self.hitName(name, index)
					
					# get position to draw
					xs, ys, ws, hs = self.senseLayout.index_to_pos(startRef)
					xs = xs / pango.SCALE + self.dX
					ys = ys / pango.SCALE + self.sY
					hs = hs / pango.SCALE
					# move there, but above the line
					y = ys# - hs
					self.ctx.move_to(xs,y)
					# print text as path
					self.ctx.text_path(name)
					textpath = self.ctx.copy_path()
					self.ctx.new_path()
					# make a line, representing the cut
					xC51, yC51, wC51, hC51 	= self.senseLayout.index_to_pos(startRef + enzymes[e].c51)
					xC31, yC31, wC31, hC31 	= self.senseLayout.index_to_pos(startRef + enzymes[e].c31)
					xC51 					= xC51 / pango.SCALE + self.dX
					yC51 					= yC51 / pango.SCALE + self.sY
					tHeight 				= hC51 / pango.SCALE
					xC31 					= xC31 / pango.SCALE +  self.dX
					yC31 					= yC31 / pango.SCALE + self.sY
					
					if yC51 == yC31:
						self.ctx.move_to(xC51, yC51)
						self.ctx.line_to(xC51, yC51 + tHeight + 0.25 * self.lineGap)
						self.ctx.line_to(xC31, yC31 + tHeight + 0.25 * self.lineGap)
						self.ctx.line_to(xC31, yC31 + 2 * tHeight + 0.5* self.lineGap)
					else:
						if yC51 <= yC31:
							lineend 	= self.dX + self.w
							linestart 	= self.dX
						else:
							# switch
							linestart	= self.dX + self.w
							lineend 	= self.dX
							
						# split the drawing in two parts
						self.ctx.move_to(xC51, yC51)
						self.ctx.line_to(xC51, yC51 + tHeight + 0.25 * self.lineGap)
						self.ctx.line_to(lineend, yC51 + tHeight + 0.25 * self.lineGap)
						# part 2
						self.ctx.move_to(linestart, yC31 + tHeight + 0.25 * self.lineGap)
						self.ctx.line_to(xC31, yC31 + tHeight + 0.25 * self.lineGap)
						self.ctx.line_to(xC31, yC31 + tHeight + 0.25 * self.lineGap)
						self.ctx.line_to(xC31, yC31 + 2 * tHeight + 0.5* self.lineGap)
						
					linepath 				= self.ctx.copy_path()
					self.ctx.new_path()
					enzymesPaths[hitname] 	= (textpath, linepath)
					
					# raise index
					index = index + 1
			# update storage
			self.cairoStorage['enzymes'] 	= e1				
			self.cairoStorage['ePaths'] 	= enzymesPaths
		
		# draw enzymes:
		self.ctx.set_line_width(1)
		self.ctx.set_source_rgba(0.6,0.3,0.3 , 1) # Solid color
		for e in self.cairoStorage['ePaths']:
			path, pathc = self.cairoStorage['ePaths'][e]
			self.ctx.append_path(path)
			self.ctx.fill()
			self.ctx.append_path(pathc)
			self.ctx.stroke()
	
	def drawTicks(self):
		''' draw the nice infos about the dna position in each line'''
		nameheight = 9 # px
		self.ctx.set_font_size(nameheight)
		self.ctx.select_font_face('Arial', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
		# number of lines:
		if len(self.dna) != 0 and self.cairoStorage['ticks'] != self.dna:
			# count the lines:
			nLines = math.ceil((len(self.dna)-1) / self.lineLength)
		
			# empty paths:
			self.cairoStorage['tPaths'] = []
			
			n = 0 # counter
			y =  self.sY  -2 #- self.fontsize - 2
			
			# clear paths
			self.ctx.new_path()
			while n <= nLines:
				
				self.ctx.move_to(self.dX + 3, y)
				# make the tick
				text = "%d" % (n *  self.lineLength + 1)
				xbearing, ybearing, TextWidth, TextHeight, xadvance, yadvance = self.ctx.text_extents(text)	
				self.ctx.text_path(text)
				textpath = self.ctx.copy_path()
				self.ctx.new_path()
				# make a box
				boxPath = self.carioRoundBox(self.dX,y  - TextHeight - 2, TextWidth + 6, TextHeight + 4, 0.6)
				self.ctx.new_path()
				
				#save paths
				self.cairoStorage['tPaths'].append((textpath, boxPath))
				
				# raise counter
				y = y + self.lineHeight
				
				n = n + 1
				
			# set storage:
			self.cairoStorage['ticks'] = self.dna
			

		# print the ticks:
		self.ctx.set_line_width(0)

		for i in self.cairoStorage['tPaths']:
			textpath, boxPath = i
			# draw box
			self.ctx.set_source_rgba(1,1,1, 0.6) # white, transparent
			self.ctx.append_path(boxPath)
			self.ctx.fill()
			# draw position
			self.ctx.set_source_rgba(0,0,0,1) # dark black
			self.ctx.append_path(textpath)
			self.ctx.fill()

		return None
	
	def carioRoundBox(self, x, y, width=10, height=10, aspect=0.8):
		''' draw a box with rounded corners '''
		# from http://cairographics.org/samples/rounded_rectangle/
		corner_radius = height / 10.0   

		radius = corner_radius / aspect
		degrees = math.pi / 180.0

		self.ctx.new_sub_path()
		self.ctx.arc( x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees)
		self.ctx.arc( x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees)
		self.ctx.arc(x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees)
		self.ctx.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees)
		self.ctx.close_path()

		path = self.ctx.copy_path()
		self.ctx.new_path()
		return path

	def drawCursor(self):
		''' draw and display cursor in the textfield'''
		indexO 		= self.cairoStorage['cursor']
		index 		= self.PositionPointer
		pos1, pos2, zero = self.get_selection()
		if indexO != index and pos2 == -1:
			index = index - 1 # we save the genetic curors 1-len(), but mean the cursor 0-len()-1
			
			# turn index to x,y
			x, y, w, h 	= self.senseLayout.index_to_pos(index)
			x 			= x / pango.SCALE + self.dX
			y 			= (y / pango.SCALE) + self.sY
			y1 			= y 
			y2 			= y + 0.5 * self.lineHeight
			
			# move cursor if its the line end:
			if self.PositionPointerEnd == True:
				x 		= x + self.dX
			self.ctx.move_to(x,y1)
			self.ctx.line_to(x,y2)
		
			self.cairoStorage['cursorPath'] = self.ctx.copy_path()
			self.ctx.new_path()
		elif pos2 != -1:
			# there is a selection, so no cursor:
			self.cairoStorage['cursorPath'] = None
		
		if self.cairoStorage['cursorPath'] != None:
			# draw cursor:
			self.ctx.append_path(self.cairoStorage['cursorPath'])
			self.ctx.set_line_width(0.3)
			self.ctx.set_source_rgba(0,0,0, 1) # Solid color
			self.ctx.stroke()
	
######################################################



		
	
	def OnLeftDown(self, event):
		# remove selection:
		self.set_dna_selection()
		
		
		# get position
		x, y 				= self.ScreenToClient(wx.GetMousePosition())
		xp 					= (x - self.dX) * pango.SCALE
		yp 					= int(y - self.sY - self.fontsize) * pango.SCALE 
		self.LeftDownIndex 	= None
		index 				= self.senseLayout.xy_to_index(xp,yp)
		
		if index != False:
			# click can be left or right of the center of the char
			if index[1] == 0:
				self.LeftDownIndex = index[0] + 1 
			else:
				self.LeftDownIndex = index[0] + 2
		
		# update cursor
		self.PositionPointer = self.LeftDownIndex
		self.set_cursor_position()
			
		
		event.Skip() #very important to make the event propagate and fulfill its original function

	def OnLeftUp(self, event):
		# get selection
		pos1, pos2, zero 	= self.get_selection()
		if pos2 == -1:
			# reset selection
			#self.set_dna_selection()

			# maybe we cliked on a feature, label or a line?
			hit = self.HitTest()
			if hit != None:
				self.hit = hit
				# we hit one, so we need to draw the selection!
				# wich feature was hit?
				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:
						# 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 
						self.set_dna_selection((start,finish, zero))
						self.set_cursor_position(start)
			else:
				self.hit = None
		
		# update the UI maybe?
		self.update_ownUI()
		self.update_globalUI()
		event.Skip() #very important to make the event propagate and fulfill its original function
	
	def OnLeftDouble(self, event):
		'''doubleclick aktion'''
		# remove selection:
		self.set_dna_selection()
		# if we hit a feature, wen can open a dialog
		hit = self.HitTest()
		if hit != None:
			# wich feature was hit?
			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()
					# update the UI maybe?
					self.update_ownUI()
					self.update_globalUI()

	
	def OnMotion(self, event):
		if event.Dragging() and event.LeftIsDown():
			oldSel = genbank.dna_selection
			
			# get position
			x, y 				= self.ScreenToClient(wx.GetMousePosition())
			xp 					= (x - self.dX) * pango.SCALE
			yp 					= int(y - self.sY - self.fontsize) * pango.SCALE 
			self.LeftMotionIndex 	= None
			index 				=  self.senseLayout.xy_to_index(xp,yp)
			# get motion cursor
			if index != False:
				if index[1] == 0:
					self.LeftMotionIndex = index[0] + 1
				else:
					self.LeftMotionIndex = index[0] + 2 


			# update cursor
			if self.LeftMotionIndex >= self.LeftDownIndex:
				self.PositionPointer = self.LeftMotionIndex + 1
			else:
				self.PositionPointer = self.LeftMotionIndex 
			
			# end of line excepions
			if (self.PositionPointer - 1)  % self.lineLength == 0 and self.PositionPointer >= self.lineLength :
				self.PositionPointerEnd = True
				self.PositionPointer = self.PositionPointer -1
			else:
				self.PositionPointerEnd = False
				
			# get selection
			if self.LeftDownIndex <= self.LeftMotionIndex:
				selection = (self.LeftDownIndex, self.LeftMotionIndex, -1)
			else:
				selection = (self.LeftMotionIndex , self.LeftDownIndex , -1)
			
			if oldSel != selection:
				self.set_dna_selection(selection) #update the varable keeping track of DNA selection
				self.update_ownUI()
				self.update_globalUI()
				
			

			
		event.Skip() #very important to make the event propagate and fulfill its original function


#	def OnRightUp(self, event):
#		event.Skip() #very important to make the event propagate and fulfill its original function


	#### rendering


####################################################################
	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
				
	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 a enzyme'''
		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.cairoStorage['fPaths'], 	# features
				self.cairoStorage['ePaths']]	# 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 or type(path) == tuple:
					path = path[1]
				
				# 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 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 set_cursor_position(self, pointer=False):
		if pointer == False:
			pointer = self.PositionPointer
		else:
			self.PositionPointer = pointer
		# set position of cursor for status bar:
		genbank.cursor_position = pointer


	def set_dna_selection(self, selection = (1,-1, -1)): # standart selection
		'''
		Updates DNA selection.
		'''
		##selection = self.get_selection()
		a, b, none = selection			# new to enable selections over 0
		# if start after end, swap them
		if a > b and b != -1:
			a,b = b,a
		selection = (a, b , -1)
		genbank.dna_selection = selection
		#self.update_globalUI()
		#self.update_ownUI()

	def moveCursor(self, offset, linejump = False):
		### handle cursor movement:
		# offset +-int
		# linejump False/up/down
		
		index = self.PositionPointer
		topology = genbank.gb.gbfile['locus']['topology']
		
		length = len(genbank.gb.GetDNA())

		if linejump == False:
				# check if we moved to a Line end
			# if so, we first do not increase the counter:
			if self.PositionPointerEnd == False and index % self.lineLength == 0:
				self.PositionPointerEnd = True
			elif self.PositionPointerEnd == True and index % self.lineLength == 0:
				self.PositionPointerEnd = False
				index = index + offset
			else:
				index = index + offset
		elif linejump == "up":
			# jump to the last row, but keep index to the left: #TODO, not correctly behaving if jumping between start and end
			index = index - self.lineLength
		elif linejump == "down":
			index = index + self.lineLength
		if topology == 'circular':
			if index < 1:
				index = index + length
			elif index > length:
				index = index - length
		else:
			if index < 1:
				index = 1
			elif index > length - 1:
				index = length - 1

		
		self.PositionPointer = index
		self.set_cursor_position()
		
	def OnKeyPress(self, evt):
		
		key = evt.GetUniChar()

		index = self.PositionPointer
		
		shift = evt.ShiftDown() # is shift down?
		
		if key in [97, 65, 116, 84, 99, 67, 103, 71]: # [a, A, t, T, c, C, g, G']
			start, finish, null = self.get_selection()
			if finish != -1: # if a selection, delete it, then paste
				genbank.gb.Delete(start+1, finish, visible=False)
				self.PositionPointer = start
			if shift == True:
				genbank.gb.Paste(index, chr(key).upper())
			elif shift == False:
				genbank.gb.Paste(index, chr(key).lower())
			
			# move pointer
			self.PositionPointer = self.PositionPointer + 1
			
			if finish != -1:
				self.set_dna_selection()
			self.update_ownUI()
			self.update_globalUI()
			
			


		
		elif key == 8: #backspace
			start, finish, null = self.get_selection()
			if finish != -1: # if a selection, delete it
				genbank.gb.Delete(start+1, finish)
				self.set_dna_selection() # remove selection
				self.PositionPointer = start
				self.update_ownUI()
				self.update_globalUI()

			else:
				genbank.gb.Delete(index-1, index-1)
				self.PositionPointer = self.PositionPointer - 1 # set pointer back
				self.update_ownUI()
				self.update_globalUI()

				#self.stc.SetSelection(start-1, start-1)
		
		elif key == 127: #delete
			start, finish, null = self.get_selection()
			if finish != -1: # if a selection, delete it
				genbank.gb.Delete(start, finish)
				self.PositionPointer = start
			else:
				genbank.gb.Delete(index, index)
			self.set_dna_selection() # remove selection
			self.update_ownUI()
			self.update_globalUI()

		elif key == 314 and shift == False: #left
			self.moveCursor(-1)		
			self.set_dna_selection() # remove selection
			self.update_ownUI()
			self.update_globalUI()

		elif key == 314 and shift == True: #left + select
			start, finish, null = self.get_selection()
			# start a selection
			if finish == -1:
				start = self.PositionPointer - 1
				finish = self.PositionPointer
			if finish == self.PositionPointer:
				self.moveCursor(-1)
				finish = self.PositionPointer
			else:
				self.moveCursor(-1)
				start = self.PositionPointer
			self.set_dna_selection((start, finish, null)) # remove selection
			self.update_ownUI()
			self.update_globalUI()


		elif key == 316 and shift == False: #right
			self.moveCursor(1)#self.PositionPointer = self.PositionPointer + 1
			self.set_dna_selection() # remove selection
			self.update_ownUI()
			self.update_globalUI()

		elif key == 316 and shift == True: #right + select
			start, finish, null = self.get_selection()
			# start a selection
			if finish == -1:
				start = self.PositionPointer
				finish = self.PositionPointer
			
			if finish == self.PositionPointer:
				self.moveCursor(1)
				finish = self.PositionPointer
			else:
				self.moveCursor(1)
				start = self.PositionPointer
			self.set_dna_selection((start, finish, null)) # remove selection
			self.update_ownUI()
			self.update_globalUI()


		elif key == 315 and shift == False: #up
			self.moveCursor(1, "up")
			self.update_ownUI()
			self.update_globalUI()

		elif key == 315 and shift == True: #up + select
			print("select with keyboard")

		elif key == 317 and shift == False: #down
			self.moveCursor(1, "down")
			self.update_ownUI()
			self.update_globalUI()

		elif key == 317 and shift == True: #down + select
			print("select with keyboard")

		#self.set_dna_selection() #update the varable keeping track of DNA selection

		self.set_cursor_position() # udpate cursor position, just in case

##########################
	#def make_outputpopup(self):
	#	'''Creates a popup window in which output can be printed'''
	#	self.outputframe = wx.Frame(None, title="Output Panel") # creation of a Frame with a title
	#	self.output = output.create(self.outputframe, style=wx.VSCROLL|wx.HSCROLL) # creation of a richtextctrl in the frame




#########################################

#	def dna_output(self, featurelist):
#		'''Prints output to the output panel'''
#		self.make_outputpopup()
#		tabtext = str(self.stc.GetPageText(self.stc.GetSelection()))
#		DNA = featurelist[0]
#		self.output.write('%s | DNA in clipboard, %d bp' % (tabtext, len(DNA))+'\n', 'File')
#		self.output.write(DNA+'\n', 'DNA')
#		if len(featurelist) > 1:
#			self.output.write('With features: ', 'Text')
#			for i in range(1, len(featurelist)):
#				feature = featurelist[i]
#				self.output.write(('>%s "%s", ' % (feature['key'], feature['qualifiers'][0].split('=')[1])), 'Text')
#			self.output.write('\n', 'Text')
#		self.outputframe.Show()




################ genbank methods ###############
	def select_all(self):
		'''Select the entire dna sequence'''
		start 	= 1
		finish 	= len(genbank.gb.GetDNA())+1 # TODO figure the coorect way to select. Write it in some dokumentation
		self.set_dna_selection((start, finish, -1))
		#self.set_dna_selection()
		self.update_ownUI()
		self.update_globalUI()

	def get_selection(self):
		'''Gets the text editor selection and adjusts it to DNA locations.'''
		'''start, finish = self.stc.GetSelection()
		if start == finish: #not a selection
			finish = 0
		elif start > finish: #the selection was made backwards
			start, finish = finish, start

		selection = (start+1, finish)'''
		
		return genbank.dna_selection

	def uppercase(self):
		'''Change selection to uppercase'''
		start, finish, zero = self.get_selection()
		if finish == -1:
			raise ValueError('Cannot modify an empty selection')
		else:
			genbank.gb.Upper(start, finish)
			self.update_ownUI()
			self.set_dna_selection()

	def lowercase(self):
		'''Change selection to lowercase'''
		start, finish, zero = self.get_selection()
		if finish == -1:
			raise ValueError('Cannot modify an empty selection')
		else:
			genbank.gb.Lower(start, finish)
			self.update_ownUI()
			self.set_dna_selection()

	def reverse_complement_selection(self):
		'''Reverse-complement current selection'''
		start, finish, zero = self.get_selection()
		if finish == -1:
			raise ValueError('Cannot modify an empty selection')
		else:
			genbank.gb.RCselection(start, finish)
			self.update_ownUI()
			self.update_globalUI()
			self.set_dna_selection()

	def delete(self):
		'''Deletes a selection and updates dna and features'''
		start, finish, zero = self.get_selection()
		if finish == -1:
			raise ValueError('Cannot delete an empty selection')
		else:
			genbank.gb.Delete(start, finish)
			self.update_ownUI()
			self.update_globalUI()
			self.set_dna_selection()

	def cut(self):
		'''Cut DNA and store it in clipboard together with any features present on that DNA'''
		start, finish, zero = self.get_selection()
		if finish == -1:
			raise ValueError('Cannot cut an empty selection')
		else:
			genbank.gb.Cut(start, finish)
			self.set_cursor_position(start)
			self.update_ownUI()
			self.update_globalUI()
			self.set_dna_selection()

	def cut_reverse_complement(self):
		'''Cut reverse complement of DNA and store it in clipboard together with any features present on that DNA'''
		start, finish, zero = self.get_selection()
		if finish == -1:
			raise ValueError('Cannot cut an empty selection')
		else:
			genbank.gb.CutRC(start, finish)
			self.update_ownUI()
			self.update_globalUI()
			self.set_dna_selection()
			
	def paste(self):
		'''Paste DNA and any features present on that DNA'''
		start, finish, zero = self.get_selection()
		if finish == -1:
			pass
		else: #If a selection, remove sequence, reposition pointer
			genbank.gb.Delete(start, finish, visible=False)
			self.set_cursor_position(start)
		genbank.gb.RichPaste(genbank.cursor_position)
		self.update_ownUI()
		self.update_globalUI()
		self.set_dna_selection() # reset selection

	def paste_reverse_complement(self):
		'''Paste reverse complement of DNA and any features present on that DNA'''
		start, finish, zero = self.get_selection()
		if finish == -1:
			pass
		else: #If a selection, remove sequence, reposition pointer
			genbank.gb.Delete(start, finish, visible=False)
			self.set_cursor_position(start)
		genbank.gb.PasteRC(genbank.cursor_position)
		self.update_ownUI()
		self.update_globalUI()
		self.set_dna_selection() # reset selection

	def copy(self):
		'''Copy DNA and features into clipboard'''
		start, finish, zero = self.get_selection()

		if finish == -1:
			raise ValueError('Cannot copy an empty selection')
		else:
			#genbank.gb.Copy(start, finish)

			# try the new copy:
			genbank.gb.RichCopy(start, finish)

	def copy_reverse_complement(self):
		'''Copy reverse complement of DNA'''
		start, finish, zero = self.get_selection()
		if finish == -1:
			raise ValueError('Cannot copy an empty selection')
		else:
			genbank.gb.CopyRC(start, finish)


#######################################################

####### Protein functions #######
	def translate_output(self, protein, DNA, info):
		'''Generate output in the output.panel'''
#		tabtext = str(self.stc.GetPageText(self.stc.GetSelection()))
		self.make_outputpopup()
		self.output.write('Translate %s\n' % (info), 'File')
		self.output.write(('%d AA from %d bases, %d bases left untranslated' % (len(protein), len(DNA), len(DNA)%3))+'\n', 'Text')
		self.output.write(protein, 'Protein')
		self.outputframe.Show()

	def translate_selection(self):
		'''Translate selected DNA'''
		start, finish = self.get_selection()
		if finish == -1:
			raise ValueError('Cannot translate an empty selection')
		else:
			DNA = genbank.gb.GetDNA(start, finish)
			protein = dna.Translate(DNA)
			self.translate_output(protein, DNA, 'leading strand')

	def translate_selection_reverse_complement(self):
		'''Translate reverse-complement of selected DNA'''
		start, finish = self.get_selection()
		if finish == -1:
			raise ValueError('Cannot translate an empty selection')
		else:
			DNA = genbank.gb.GetDNA(start, finish)
			protein = dna.Translate(dna.RC(DNA))
			self.translate_output(protein, DNA, 'complement strand')

#update this one...
	def translate_feature(self):
		'''Translate specified feature'''
		feature = genbank.gb.allgbfeatures[2]
		DNA = genbank.gb.getdnaforgbfeature(feature[4])
		protein = dna.Translate(DNA)
		self.translate_output(protein, DNA, 'feature "%s"' % feature[4][7:])


################ other functions ###############################


	def OnCloseWindow(self, e):
		self.close_all("")
		foo=self.GetSize()  ###except for the window size of file
		if(self.IsMaximized()==0):
			file=open(files['size'], "w")
			file.write(str(foo[0])+"\n"+str(foo[1]))
			file.close()
		self.Destroy()
		self.update_ownUI() #refresh everything


	def mouse_position(self, event):
		'''Get which features are at a given position'''
		xposition, yposition = self.stc.ScreenToClient(wx.GetMousePosition())
		if xposition > 1 and yposition > 1:

			mposition = self.stc.CharPositionFromPoint(xposition, yposition)


#			#which feature corresponds to this pos?
			Feature = genbank.gb.get_featurename_for_pos(mposition)
			return mposition, Feature
		else:
			return None, None
示例#3
0
class TextEdit(DNApyBaseDrawingClass):
    '''
		new Text editor that allows to display dna and
		complementary dna in a usefull manner
		
		Also it can and should show:
			- features (done, buggy)
			- restriction enzymes (partly)
			- dna modifications (todo)
			- primer (todo)
	'''
    def __init__(self, parent, id):

        #parent
        self.parent = parent

        # get data
        self.dna = genbank.gb.GetDNA()
        self.cdna = genbank.gb.GetDNA()

        # settings for the editor
        self.sY = 60  # offset top
        self.dX = 8  # border left and right
        self.textSpacing = 80  # seperation of lines
        self.fontsize = 9  # line size
        self.lineGap = 6

        # interactive cursor
        self.PositionPointer = 1
        self.PositionPointerEnd = False

        # initial minimal height of the editor
        self.minHeight = 200

        # storage dict, to prevent recalculating to much
        self.cairoStorage = {
            'features': None,  # feature Object
            'fPaths': [],  # path of alls features as List
            'cursorPath': None,
            'cursor': None,
            'enzymes': {},  # enzymes object
            'ePaths': [],  # enzymes drawn
            'ticks': None,  # ticks object --> self.dna
            'tPaths': []  # ticks drawn		
        }

        # call window
        DNApyBaseDrawingClass.__init__(self, parent, wx.ID_ANY)

        # bind events
        self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDouble)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_MOTION,
                  self.OnMotion)  # drag and drop, selection etc.
        self.Bind(
            wx.EVT_KEY_DOWN, self.OnKeyPress
        )  #This is important for controlling the input into the editor

####### Modify methods from base calss to fit current needs #########

    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="DNA 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.
		"""

        #####################
        # remove buffered features, to force redraw:
        self.cairoStorage['features'] = None

        # The Buffer init is done here, to make sure the buffer is always
        # the same size as the Window
        wC, hC = self.parent.GetClientSize()

        wP, hP = self.parent.GetSize()
        self.SetSize(wx.Size(wC, self.minHeight))
        self.parent.SetVirtualSize(wx.Size(wC - 30, self.minHeight))

        # Make new offscreen bitmap: this bitmap will always have the
        # current drawing in it, so it can be used to save the image to
        # a file, or whatever.
        #		if wC != 0 and hC !=0:
        #			self._Buffer = wx.EmptyBitmap(wC, self.minHeight)

        #		else:
        #			self._Buffer = wx.EmptyBitmap(wP, hP)

        ##########################

        # start dc
        dc = wx.MemoryDC()
        dc.SelectObject(self._Buffer)
        dc.SetBackground(wx.Brush("White"))
        dc.Clear()
        # make a cairo context
        self.ctx = ContextFromDC(dc)
        # start pango as a font backend
        self.pango = pangocairo.CairoContext(self.ctx)
        self.pango.set_antialias(cairo.ANTIALIAS_SUBPIXEL)

        # reload dna
        self.dna = genbank.gb.GetDNA()
        if self.dna != None:
            self.cdna = dna.C(self.dna)
        else:
            self.dna = ''  # no dna, if there is nothing
            self.cdna = ''  # empty cdna

        # render the text
        self.displayText()

        # create colorful features here
        self.drawFeatures()

        # draw enzymes
        self.drawEnzymes()

        # draw a cursor
        self.drawCursor()

        # draw Tics above all!
        self.drawTicks()

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

        return None

    def displayText(self):
        ''' main function for the editors text capabilities
			here the text gets positioned and all its color.
			Also the selection highlight is done here'''

        # get window size to wrap text
        width, height = self.parent.GetVirtualSize()
        if width > 30:
            w = width - self.dX
        else:
            w = width
        # save with for other functions
        self.w = w

        # start layout
        layout = self.pango.create_layout()
        layout.set_wrap(pango.WRAP_WORD_CHAR)
        layout.set_width(pango.SCALE * w)
        layout.set_spacing(pango.SCALE * self.textSpacing)
        layout.set_alignment(pango.ALIGN_LEFT)

        # new attr list for the dna
        dnaAttr = pango.AttrList()
        # color
        gray = 56 * 65535 / 255
        fg_color = pango.AttrForeground(gray, gray, gray, 0, len(self.dna))
        spacing = pango.AttrLetterSpacing(1500, 0, len(self.dna))
        # add attr
        dnaAttr.insert(fg_color)
        dnaAttr.insert(spacing)
        # set attr
        layout.set_attributes(dnaAttr)

        #set font
        font = pango.FontDescription("monospace normal " + "1")
        font.set_size(pango.SCALE * self.fontsize)
        layout.set_font_description(font)

        # font and color are prepared for the DNA

        # get selection, if any
        start, end, zero = self.get_selection()
        if end != -1:
            # correct direktion of selection
            if start > end:
                s = end - 1
                e = start
            else:
                s = start - 1
                e = end
        # make markup selection
        if abs(end - start) >= 0 and zero == -1 and end != -1:
            senseText = self.dna[
                0:s] + '<span background="#0082ED">' + self.dna[
                    s:e] + '</span>' + self.dna[e:]
            antiText = self.cdna[
                0:s] + '<span background="#0082ED">' + self.cdna[
                    s:e] + '</span>' + self.cdna[e:]

        elif abs(end - start) >= 0 and zero == 1 and end != -1:
            senseText = '<span background="#0082ED">' + self.dna[
                0:s] + '</span>' + self.dna[
                    s:e] + '<span background="#0082ED">' + self.dna[
                        e:] + '</span>'
            antiText = '<span background="#0082ED">' + self.cdna[
                0:s] + '</span>' + self.cdna[
                    s:e] + '<span background="#0082ED">' + self.cdna[
                        e:] + '</span>'
        else:
            senseText = self.dna
            antiText = self.cdna

        # output dna
        self.ctx.move_to(self.dX, self.sY)
        layout.set_markup(senseText)
        self.pango.update_layout(layout)
        self.pango.show_layout(layout)

        # copy sense layout
        self.senseLayout = layout.copy()

        # determine the length and height of a line
        self.lineLength = layout.get_line(0).length
        x1, y1, w1, h1 = self.senseLayout.index_to_pos(1)
        x2, y2, w2, h2 = self.senseLayout.index_to_pos(1 + self.lineLength)
        self.lineHeight = abs(y1 - y2) / pango.SCALE

        # print anti sense strain
        self.ctx.move_to(self.dX, self.sY + self.fontsize + self.lineGap)
        layout.set_markup(antiText)
        self.pango.update_layout(layout)
        self.pango.show_layout(layout)

        # update the height of the context, based on the inserted dna
        w, h = layout.get_pixel_size()
        self.minHeight = h + 2 * self.sY
        # resize window
        #		w,h = self.GetSize()					# prevents missfomration on resize
        #		self.SetSize(wx.Size(w,self.minHeight)) # prevents missfomration on resize

        return None

    def drawFeatures(self):
        features = genbank.gb.get_all_feature_positions()
        paths = {}  # (color, path)

        featureheight = 10

        if self.cairoStorage['features'] != features:
            # something changed, we have to draw the paths new
            self.drawn_fw_locations = []
            self.drawn_rv_locations = []

            # font settings
            # draw the name somewhere at the start
            nameheight = 9  # px
            self.ctx.select_font_face('Arial', cairo.FONT_SLANT_NORMAL,
                                      cairo.FONT_WEIGHT_NORMAL)
            self.ctx.set_font_size(nameheight)

            for feature in features:

                featuretype, complement, start, finish, name, index = feature

                if featuretype != "source":

                    # get names
                    hname = self.hitName(name, index)
                    name = self.nameBeautiful(name)
                    #get position
                    xs, ys, ws, hs = self.senseLayout.index_to_pos(start)
                    xf, yf, wf, hf = self.senseLayout.index_to_pos(finish + 1)
                    # linecount:
                    # if ys and yf are not the same, we have feature over multiple lines!
                    dy = abs(ys - yf) / pango.SCALE
                    ySteps = dy / self.lineHeight  # number of lines!
                    xs = xs / pango.SCALE
                    xf = xf / pango.SCALE
                    ys = (ys / pango.SCALE) + self.sY
                    yf = yf / pango.SCALE

                    # first sense strain over the text
                    if complement == False:
                        self.drawn_fw_locations, l = self.find_overlap(
                            self.drawn_fw_locations, (start, finish))
                        level = l * (featureheight + 6)

                        xbearing, ybearing, TextWidth, TextHeight, xadvance, yadvance = self.ctx.text_extents(
                            name)
                        ys = ys - level - 5
                        self.ctx.move_to(xs + 10, ys)
                        self.ctx.text_path(name)
                        textpath = self.ctx.copy_path()
                        self.ctx.new_path()
                        # end name

                        # box for name
                        self.ctx.move_to(xs, ys)
                        self.ctx.rectangle(xs, ys - featureheight,
                                           TextWidth + 20, nameheight + 5)

                        # lines above the dna
                        while ySteps >= 0:
                            if ySteps == 0:
                                xE = xf
                            else:
                                xE = self.w + 5  # TODO: figure out, why we need to add 5 px, to make it look correct

                            # make a box-path
                            self.ctx.move_to(xs, ys)
                            self.ctx.rectangle(xs, ys - 5, xE - xs, 5)

                            # the second x start is always 8px, or what we will opt for
                            xs = self.dX
                            ys = ys + self.lineHeight
                            ySteps = ySteps - 1

                        # path
                        fillpath = self.ctx.copy_path()
                        self.ctx.new_path()
                        # get color
                        color = eval(featuretype)[
                            'fw']  #get the color of feature (as string)
                    else:
                        self.drawn_rv_locations, l = self.find_overlap(
                            self.drawn_rv_locations, (start, finish))
                        level = l * (featureheight + 6)
                        # set y
                        ys = ys + 2 * self.fontsize + self.lineGap + 9 + level

                        # lines below the dna
                        while ySteps >= 0:
                            if ySteps == 0:
                                xE = xf
                            else:
                                xE = self.w

                            # make the path
                            #self.ctx.move_to(xs, ys)
                            #self.ctx.line_to(xE, ys)
                            self.ctx.move_to(xs, ys)
                            self.ctx.rectangle(xs, ys + 5, xE - xs, 5)

                            xs = self.dX
                            ys = ys + self.lineHeight
                            ySteps = ySteps - 1

                        # change y:
                        ys = ys - self.lineHeight
                        # buffer path
                        bufferPath = self.ctx.copy_path()
                        self.ctx.new_path()

                        # name at the end:
                        xbearing, ybearing, TextWidth, TextHeight, xadvance, yadvance = self.ctx.text_extents(
                            name)
                        self.ctx.move_to(xf - TextWidth - 10, ys + 10)
                        self.ctx.text_path(name)
                        textpath = self.ctx.copy_path()
                        self.ctx.new_path()
                        # end name

                        # box for name
                        self.ctx.append_path(bufferPath)
                        self.ctx.move_to(xf - TextWidth, ys + featureheight)
                        self.ctx.rectangle(xf - TextWidth - 20, ys,
                                           TextWidth + 20, nameheight + 5)

                        # get path
                        fillpath = self.ctx.copy_path()
                        self.ctx.new_path()
                        # get color
                        color = eval(featuretype)[
                            'fw']  #get the color of feature (as string)

                    paths[hname] = (color, fillpath, textpath)
                    self.ctx.new_path()  # clear canvas
            # save the paths
            self.cairoStorage['fPaths'] = paths
            # update storage status
            self.cairoStorage['features'] = features

        # draw the beautiful features

        for a in self.cairoStorage['fPaths']:
            color, path, tpath = self.cairoStorage['fPaths'][a]

            # get color
            assert type(color) == str
            r, g, b = colcol.hex_to_rgb(color)
            r = float(r) / 255
            g = float(g) / 255
            b = float(b) / 255

            # append and draw path
            self.ctx.append_path(path)
            self.ctx.set_line_width(1)
            self.ctx.set_source_rgba(r, g, b, 1)  # Solid color
            #self.ctx.stroke_preserve()
            self.ctx.fill()
            # write text
            self.ctx.append_path(tpath)
            self.ctx.set_line_width(1)
            self.ctx.set_source_rgba(1, 1, 1, 1)  # Solid color
            self.ctx.fill()

        return None

    def drawEnzymes(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()

        if e1 != self.cairoStorage['enzymes']:
            nameheight = 9  # px
            self.ctx.select_font_face('Arial', cairo.FONT_SLANT_NORMAL,
                                      cairo.FONT_WEIGHT_NORMAL)
            self.ctx.set_font_size(nameheight)
            dna = self.dna
            length = len(dna)
            index = 0  # same as in plasmid_GUI.py
            enzymesPaths = {}
            # loop enzymes ordered dict
            for e in enzymes:
                for site in enzymes[e].restrictionSites:
                    name, start, end, cut51, cut52, dnaMatch = site
                    startRef = start - 1
                    hitname = self.hitName(name, index)

                    # get position to draw
                    xs, ys, ws, hs = self.senseLayout.index_to_pos(startRef)
                    xs = xs / pango.SCALE + self.dX
                    ys = ys / pango.SCALE + self.sY
                    hs = hs / pango.SCALE
                    # move there, but above the line
                    y = ys  # - hs
                    self.ctx.move_to(xs, y)
                    # print text as path
                    self.ctx.text_path(name)
                    textpath = self.ctx.copy_path()
                    self.ctx.new_path()
                    # make a line, representing the cut
                    xC51, yC51, wC51, hC51 = self.senseLayout.index_to_pos(
                        startRef + enzymes[e].c51)
                    xC31, yC31, wC31, hC31 = self.senseLayout.index_to_pos(
                        startRef + enzymes[e].c31)
                    xC51 = xC51 / pango.SCALE + self.dX
                    yC51 = yC51 / pango.SCALE + self.sY
                    tHeight = hC51 / pango.SCALE
                    xC31 = xC31 / pango.SCALE + self.dX
                    yC31 = yC31 / pango.SCALE + self.sY

                    if yC51 == yC31:
                        self.ctx.move_to(xC51, yC51)
                        self.ctx.line_to(xC51,
                                         yC51 + tHeight + 0.25 * self.lineGap)
                        self.ctx.line_to(xC31,
                                         yC31 + tHeight + 0.25 * self.lineGap)
                        self.ctx.line_to(
                            xC31, yC31 + 2 * tHeight + 0.5 * self.lineGap)
                    else:
                        if yC51 <= yC31:
                            lineend = self.dX + self.w
                            linestart = self.dX
                        else:
                            # switch
                            linestart = self.dX + self.w
                            lineend = self.dX

                        # split the drawing in two parts
                        self.ctx.move_to(xC51, yC51)
                        self.ctx.line_to(xC51,
                                         yC51 + tHeight + 0.25 * self.lineGap)
                        self.ctx.line_to(lineend,
                                         yC51 + tHeight + 0.25 * self.lineGap)
                        # part 2
                        self.ctx.move_to(linestart,
                                         yC31 + tHeight + 0.25 * self.lineGap)
                        self.ctx.line_to(xC31,
                                         yC31 + tHeight + 0.25 * self.lineGap)
                        self.ctx.line_to(xC31,
                                         yC31 + tHeight + 0.25 * self.lineGap)
                        self.ctx.line_to(
                            xC31, yC31 + 2 * tHeight + 0.5 * self.lineGap)

                    linepath = self.ctx.copy_path()
                    self.ctx.new_path()
                    enzymesPaths[hitname] = (textpath, linepath)

                    # raise index
                    index = index + 1
            # update storage
            self.cairoStorage['enzymes'] = e1
            self.cairoStorage['ePaths'] = enzymesPaths

        # draw enzymes:
        self.ctx.set_line_width(1)
        self.ctx.set_source_rgba(0.6, 0.3, 0.3, 1)  # Solid color
        for e in self.cairoStorage['ePaths']:
            path, pathc = self.cairoStorage['ePaths'][e]
            self.ctx.append_path(path)
            self.ctx.fill()
            self.ctx.append_path(pathc)
            self.ctx.stroke()

    def drawTicks(self):
        ''' draw the nice infos about the dna position in each line'''
        nameheight = 9  # px
        self.ctx.set_font_size(nameheight)
        self.ctx.select_font_face('Arial', cairo.FONT_SLANT_NORMAL,
                                  cairo.FONT_WEIGHT_NORMAL)
        # number of lines:
        if len(self.dna) != 0 and self.cairoStorage['ticks'] != self.dna:
            # count the lines:
            nLines = math.ceil((len(self.dna) - 1) / self.lineLength)

            # empty paths:
            self.cairoStorage['tPaths'] = []

            n = 0  # counter
            y = self.sY - 2  #- self.fontsize - 2

            # clear paths
            self.ctx.new_path()
            while n <= nLines:

                self.ctx.move_to(self.dX + 3, y)
                # make the tick
                text = "%d" % (n * self.lineLength + 1)
                xbearing, ybearing, TextWidth, TextHeight, xadvance, yadvance = self.ctx.text_extents(
                    text)
                self.ctx.text_path(text)
                textpath = self.ctx.copy_path()
                self.ctx.new_path()
                # make a box
                boxPath = self.carioRoundBox(self.dX, y - TextHeight - 2,
                                             TextWidth + 6, TextHeight + 4,
                                             0.6)
                self.ctx.new_path()

                #save paths
                self.cairoStorage['tPaths'].append((textpath, boxPath))

                # raise counter
                y = y + self.lineHeight

                n = n + 1

            # set storage:
            self.cairoStorage['ticks'] = self.dna

        # print the ticks:
        self.ctx.set_line_width(0)

        for i in self.cairoStorage['tPaths']:
            textpath, boxPath = i
            # draw box
            self.ctx.set_source_rgba(1, 1, 1, 0.6)  # white, transparent
            self.ctx.append_path(boxPath)
            self.ctx.fill()
            # draw position
            self.ctx.set_source_rgba(0, 0, 0, 1)  # dark black
            self.ctx.append_path(textpath)
            self.ctx.fill()

        return None

    def carioRoundBox(self, x, y, width=10, height=10, aspect=0.8):
        ''' draw a box with rounded corners '''
        # from http://cairographics.org/samples/rounded_rectangle/
        corner_radius = height / 10.0

        radius = corner_radius / aspect
        degrees = math.pi / 180.0

        self.ctx.new_sub_path()
        self.ctx.arc(x + width - radius, y + radius, radius, -90 * degrees,
                     0 * degrees)
        self.ctx.arc(x + width - radius, y + height - radius, radius,
                     0 * degrees, 90 * degrees)
        self.ctx.arc(x + radius, y + height - radius, radius, 90 * degrees,
                     180 * degrees)
        self.ctx.arc(x + radius, y + radius, radius, 180 * degrees,
                     270 * degrees)
        self.ctx.close_path()

        path = self.ctx.copy_path()
        self.ctx.new_path()
        return path

    def drawCursor(self):
        ''' draw and display cursor in the textfield'''
        indexO = self.cairoStorage['cursor']
        index = self.PositionPointer
        pos1, pos2, zero = self.get_selection()
        if indexO != index and pos2 == -1:
            index = index - 1  # we save the genetic curors 1-len(), but mean the cursor 0-len()-1

            # turn index to x,y
            x, y, w, h = self.senseLayout.index_to_pos(index)
            x = x / pango.SCALE + self.dX
            y = (y / pango.SCALE) + self.sY
            y1 = y
            y2 = y + 0.5 * self.lineHeight

            # move cursor if its the line end:
            if self.PositionPointerEnd == True:
                x = x + self.dX
            self.ctx.move_to(x, y1)
            self.ctx.line_to(x, y2)

            self.cairoStorage['cursorPath'] = self.ctx.copy_path()
            self.ctx.new_path()
        elif pos2 != -1:
            # there is a selection, so no cursor:
            self.cairoStorage['cursorPath'] = None

        if self.cairoStorage['cursorPath'] != None:
            # draw cursor:
            self.ctx.append_path(self.cairoStorage['cursorPath'])
            self.ctx.set_line_width(0.3)
            self.ctx.set_source_rgba(0, 0, 0, 1)  # Solid color
            self.ctx.stroke()

######################################################

    def OnLeftDown(self, event):
        # remove selection:
        self.set_dna_selection()

        # get position
        x, y = self.ScreenToClient(wx.GetMousePosition())
        xp = (x - self.dX) * pango.SCALE
        yp = int(y - self.sY - self.fontsize) * pango.SCALE
        self.LeftDownIndex = None
        index = self.senseLayout.xy_to_index(xp, yp)

        if index != False:
            # click can be left or right of the center of the char
            if index[1] == 0:
                self.LeftDownIndex = index[0] + 1
            else:
                self.LeftDownIndex = index[0] + 2

        # update cursor
        self.PositionPointer = self.LeftDownIndex
        self.set_cursor_position()

        event.Skip(
        )  #very important to make the event propagate and fulfill its original function

    def OnLeftUp(self, event):
        # get selection
        pos1, pos2, zero = self.get_selection()
        if pos2 == -1:
            # reset selection
            #self.set_dna_selection()

            # maybe we cliked on a feature, label or a line?
            hit = self.HitTest()
            if hit != None:
                self.hit = hit
                # we hit one, so we need to draw the selection!
                # wich feature was hit?
                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:
                        # 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
                        self.set_dna_selection((start, finish, zero))
                        self.set_cursor_position(start)
            else:
                self.hit = None

        # update the UI maybe?
        self.update_ownUI()
        self.update_globalUI()
        event.Skip(
        )  #very important to make the event propagate and fulfill its original function

    def OnLeftDouble(self, event):
        '''doubleclick aktion'''
        # remove selection:
        self.set_dna_selection()
        # if we hit a feature, wen can open a dialog
        hit = self.HitTest()
        if hit != None:
            # wich feature was hit?
            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()
                    # update the UI maybe?
                    self.update_ownUI()
                    self.update_globalUI()

    def OnMotion(self, event):
        if event.Dragging() and event.LeftIsDown():
            oldSel = genbank.dna_selection

            # get position
            x, y = self.ScreenToClient(wx.GetMousePosition())
            xp = (x - self.dX) * pango.SCALE
            yp = int(y - self.sY - self.fontsize) * pango.SCALE
            self.LeftMotionIndex = None
            index = self.senseLayout.xy_to_index(xp, yp)
            # get motion cursor
            if index != False:
                if index[1] == 0:
                    self.LeftMotionIndex = index[0] + 1
                else:
                    self.LeftMotionIndex = index[0] + 2

            # update cursor
            if self.LeftMotionIndex >= self.LeftDownIndex:
                self.PositionPointer = self.LeftMotionIndex + 1
            else:
                self.PositionPointer = self.LeftMotionIndex

            # end of line excepions
            if (
                    self.PositionPointer - 1
            ) % self.lineLength == 0 and self.PositionPointer >= self.lineLength:
                self.PositionPointerEnd = True
                self.PositionPointer = self.PositionPointer - 1
            else:
                self.PositionPointerEnd = False

            # get selection
            if self.LeftDownIndex <= self.LeftMotionIndex:
                selection = (self.LeftDownIndex, self.LeftMotionIndex, -1)
            else:
                selection = (self.LeftMotionIndex, self.LeftDownIndex, -1)

            if oldSel != selection:
                self.set_dna_selection(
                    selection
                )  #update the varable keeping track of DNA selection
                self.update_ownUI()
                self.update_globalUI()

        event.Skip(
        )  #very important to make the event propagate and fulfill its original function

#	def OnRightUp(self, event):
#		event.Skip() #very important to make the event propagate and fulfill its original function

#### rendering

####################################################################

    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

    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 a enzyme'''
        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.cairoStorage['fPaths'],  # features
            self.cairoStorage['ePaths']
        ]  # 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 or type(path) == tuple:
                    path = path[1]

                # 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 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 set_cursor_position(self, pointer=False):
        if pointer == False:
            pointer = self.PositionPointer
        else:
            self.PositionPointer = pointer
        # set position of cursor for status bar:
        genbank.cursor_position = pointer

    def set_dna_selection(self, selection=(1, -1, -1)):  # standart selection
        '''
		Updates DNA selection.
		'''
        ##selection = self.get_selection()
        a, b, none = selection  # new to enable selections over 0
        # if start after end, swap them
        if a > b and b != -1:
            a, b = b, a
        selection = (a, b, -1)
        genbank.dna_selection = selection
        #self.update_globalUI()
        #self.update_ownUI()

    def moveCursor(self, offset, linejump=False):
        ### handle cursor movement:
        # offset +-int
        # linejump False/up/down

        index = self.PositionPointer
        topology = genbank.gb.gbfile['locus']['topology']

        length = len(genbank.gb.GetDNA())

        if linejump == False:
            # check if we moved to a Line end
            # if so, we first do not increase the counter:
            if self.PositionPointerEnd == False and index % self.lineLength == 0:
                self.PositionPointerEnd = True
            elif self.PositionPointerEnd == True and index % self.lineLength == 0:
                self.PositionPointerEnd = False
                index = index + offset
            else:
                index = index + offset
        elif linejump == "up":
            # jump to the last row, but keep index to the left: #TODO, not correctly behaving if jumping between start and end
            index = index - self.lineLength
        elif linejump == "down":
            index = index + self.lineLength
        if topology == 'circular':
            if index < 1:
                index = index + length
            elif index > length:
                index = index - length
        else:
            if index < 1:
                index = 1
            elif index > length - 1:
                index = length - 1

        self.PositionPointer = index
        self.set_cursor_position()

    def OnKeyPress(self, evt):

        key = evt.GetUniChar()

        index = self.PositionPointer

        shift = evt.ShiftDown()  # is shift down?

        if key in [97, 65, 116, 84, 99, 67, 103,
                   71]:  # [a, A, t, T, c, C, g, G']
            start, finish, null = self.get_selection()
            if finish != -1:  # if a selection, delete it, then paste
                genbank.gb.Delete(start + 1, finish, visible=False)
                self.PositionPointer = start
            if shift == True:
                genbank.gb.Paste(index, chr(key).upper())
            elif shift == False:
                genbank.gb.Paste(index, chr(key).lower())

            # move pointer
            self.PositionPointer = self.PositionPointer + 1

            if finish != -1:
                self.set_dna_selection()
            self.update_ownUI()
            self.update_globalUI()

        elif key == 8:  #backspace
            start, finish, null = self.get_selection()
            if finish != -1:  # if a selection, delete it
                genbank.gb.Delete(start + 1, finish)
                self.set_dna_selection()  # remove selection
                self.PositionPointer = start
                self.update_ownUI()
                self.update_globalUI()

            else:
                genbank.gb.Delete(index - 1, index - 1)
                self.PositionPointer = self.PositionPointer - 1  # set pointer back
                self.update_ownUI()
                self.update_globalUI()

                #self.stc.SetSelection(start-1, start-1)

        elif key == 127:  #delete
            start, finish, null = self.get_selection()
            if finish != -1:  # if a selection, delete it
                genbank.gb.Delete(start, finish)
                self.PositionPointer = start
            else:
                genbank.gb.Delete(index, index)
            self.set_dna_selection()  # remove selection
            self.update_ownUI()
            self.update_globalUI()

        elif key == 314 and shift == False:  #left
            self.moveCursor(-1)
            self.set_dna_selection()  # remove selection
            self.update_ownUI()
            self.update_globalUI()

        elif key == 314 and shift == True:  #left + select
            start, finish, null = self.get_selection()
            # start a selection
            if finish == -1:
                start = self.PositionPointer - 1
                finish = self.PositionPointer
            if finish == self.PositionPointer:
                self.moveCursor(-1)
                finish = self.PositionPointer
            else:
                self.moveCursor(-1)
                start = self.PositionPointer
            self.set_dna_selection((start, finish, null))  # remove selection
            self.update_ownUI()
            self.update_globalUI()

        elif key == 316 and shift == False:  #right
            self.moveCursor(
                1)  #self.PositionPointer = self.PositionPointer + 1
            self.set_dna_selection()  # remove selection
            self.update_ownUI()
            self.update_globalUI()

        elif key == 316 and shift == True:  #right + select
            start, finish, null = self.get_selection()
            # start a selection
            if finish == -1:
                start = self.PositionPointer
                finish = self.PositionPointer

            if finish == self.PositionPointer:
                self.moveCursor(1)
                finish = self.PositionPointer
            else:
                self.moveCursor(1)
                start = self.PositionPointer
            self.set_dna_selection((start, finish, null))  # remove selection
            self.update_ownUI()
            self.update_globalUI()

        elif key == 315 and shift == False:  #up
            self.moveCursor(1, "up")
            self.update_ownUI()
            self.update_globalUI()

        elif key == 315 and shift == True:  #up + select
            print("select with keyboard")

        elif key == 317 and shift == False:  #down
            self.moveCursor(1, "down")
            self.update_ownUI()
            self.update_globalUI()

        elif key == 317 and shift == True:  #down + select
            print("select with keyboard")

        #self.set_dna_selection() #update the varable keeping track of DNA selection

        self.set_cursor_position()  # udpate cursor position, just in case

##########################
#def make_outputpopup(self):
#	'''Creates a popup window in which output can be printed'''
#	self.outputframe = wx.Frame(None, title="Output Panel") # creation of a Frame with a title
#	self.output = output.create(self.outputframe, style=wx.VSCROLL|wx.HSCROLL) # creation of a richtextctrl in the frame

#########################################

#	def dna_output(self, featurelist):
#		'''Prints output to the output panel'''
#		self.make_outputpopup()
#		tabtext = str(self.stc.GetPageText(self.stc.GetSelection()))
#		DNA = featurelist[0]
#		self.output.write('%s | DNA in clipboard, %d bp' % (tabtext, len(DNA))+'\n', 'File')
#		self.output.write(DNA+'\n', 'DNA')
#		if len(featurelist) > 1:
#			self.output.write('With features: ', 'Text')
#			for i in range(1, len(featurelist)):
#				feature = featurelist[i]
#				self.output.write(('>%s "%s", ' % (feature['key'], feature['qualifiers'][0].split('=')[1])), 'Text')
#			self.output.write('\n', 'Text')
#		self.outputframe.Show()

################ genbank methods ###############

    def select_all(self):
        '''Select the entire dna sequence'''
        start = 1
        finish = len(
            genbank.gb.GetDNA()
        ) + 1  # TODO figure the coorect way to select. Write it in some dokumentation
        self.set_dna_selection((start, finish, -1))
        #self.set_dna_selection()
        self.update_ownUI()
        self.update_globalUI()

    def get_selection(self):
        '''Gets the text editor selection and adjusts it to DNA locations.'''
        '''start, finish = self.stc.GetSelection()
		if start == finish: #not a selection
			finish = 0
		elif start > finish: #the selection was made backwards
			start, finish = finish, start

		selection = (start+1, finish)'''

        return genbank.dna_selection

    def uppercase(self):
        '''Change selection to uppercase'''
        start, finish, zero = self.get_selection()
        if finish == -1:
            raise ValueError('Cannot modify an empty selection')
        else:
            genbank.gb.Upper(start, finish)
            self.update_ownUI()
            self.set_dna_selection()

    def lowercase(self):
        '''Change selection to lowercase'''
        start, finish, zero = self.get_selection()
        if finish == -1:
            raise ValueError('Cannot modify an empty selection')
        else:
            genbank.gb.Lower(start, finish)
            self.update_ownUI()
            self.set_dna_selection()

    def reverse_complement_selection(self):
        '''Reverse-complement current selection'''
        start, finish, zero = self.get_selection()
        if finish == -1:
            raise ValueError('Cannot modify an empty selection')
        else:
            genbank.gb.RCselection(start, finish)
            self.update_ownUI()
            self.update_globalUI()
            self.set_dna_selection()

    def delete(self):
        '''Deletes a selection and updates dna and features'''
        start, finish, zero = self.get_selection()
        if finish == -1:
            raise ValueError('Cannot delete an empty selection')
        else:
            genbank.gb.Delete(start, finish)
            self.update_ownUI()
            self.update_globalUI()
            self.set_dna_selection()

    def cut(self):
        '''Cut DNA and store it in clipboard together with any features present on that DNA'''
        start, finish, zero = self.get_selection()
        if finish == -1:
            raise ValueError('Cannot cut an empty selection')
        else:
            genbank.gb.Cut(start, finish)
            self.set_cursor_position(start)
            self.update_ownUI()
            self.update_globalUI()
            self.set_dna_selection()

    def cut_reverse_complement(self):
        '''Cut reverse complement of DNA and store it in clipboard together with any features present on that DNA'''
        start, finish, zero = self.get_selection()
        if finish == -1:
            raise ValueError('Cannot cut an empty selection')
        else:
            genbank.gb.CutRC(start, finish)
            self.update_ownUI()
            self.update_globalUI()
            self.set_dna_selection()

    def paste(self):
        '''Paste DNA and any features present on that DNA'''
        start, finish, zero = self.get_selection()
        if finish == -1:
            pass
        else:  #If a selection, remove sequence, reposition pointer
            genbank.gb.Delete(start, finish, visible=False)
            self.set_cursor_position(start)
        genbank.gb.RichPaste(genbank.cursor_position)
        self.update_ownUI()
        self.update_globalUI()
        self.set_dna_selection()  # reset selection

    def paste_reverse_complement(self):
        '''Paste reverse complement of DNA and any features present on that DNA'''
        start, finish, zero = self.get_selection()
        if finish == -1:
            pass
        else:  #If a selection, remove sequence, reposition pointer
            genbank.gb.Delete(start, finish, visible=False)
            self.set_cursor_position(start)
        genbank.gb.PasteRC(genbank.cursor_position)
        self.update_ownUI()
        self.update_globalUI()
        self.set_dna_selection()  # reset selection

    def copy(self):
        '''Copy DNA and features into clipboard'''
        start, finish, zero = self.get_selection()

        if finish == -1:
            raise ValueError('Cannot copy an empty selection')
        else:
            #genbank.gb.Copy(start, finish)

            # try the new copy:
            genbank.gb.RichCopy(start, finish)

    def copy_reverse_complement(self):
        '''Copy reverse complement of DNA'''
        start, finish, zero = self.get_selection()
        if finish == -1:
            raise ValueError('Cannot copy an empty selection')
        else:
            genbank.gb.CopyRC(start, finish)

#######################################################

####### Protein functions #######

    def translate_output(self, protein, DNA, info):
        '''Generate output in the output.panel'''
        #		tabtext = str(self.stc.GetPageText(self.stc.GetSelection()))
        self.make_outputpopup()
        self.output.write('Translate %s\n' % (info), 'File')
        self.output.write(('%d AA from %d bases, %d bases left untranslated' %
                           (len(protein), len(DNA), len(DNA) % 3)) + '\n',
                          'Text')
        self.output.write(protein, 'Protein')
        self.outputframe.Show()

    def translate_selection(self):
        '''Translate selected DNA'''
        start, finish = self.get_selection()
        if finish == -1:
            raise ValueError('Cannot translate an empty selection')
        else:
            DNA = genbank.gb.GetDNA(start, finish)
            protein = dna.Translate(DNA)
            self.translate_output(protein, DNA, 'leading strand')

    def translate_selection_reverse_complement(self):
        '''Translate reverse-complement of selected DNA'''
        start, finish = self.get_selection()
        if finish == -1:
            raise ValueError('Cannot translate an empty selection')
        else:
            DNA = genbank.gb.GetDNA(start, finish)
            protein = dna.Translate(dna.RC(DNA))
            self.translate_output(protein, DNA, 'complement strand')

#update this one...

    def translate_feature(self):
        '''Translate specified feature'''
        feature = genbank.gb.allgbfeatures[2]
        DNA = genbank.gb.getdnaforgbfeature(feature[4])
        protein = dna.Translate(DNA)
        self.translate_output(protein, DNA, 'feature "%s"' % feature[4][7:])

################ other functions ###############################

    def OnCloseWindow(self, e):
        self.close_all("")
        foo = self.GetSize()  ###except for the window size of file
        if (self.IsMaximized() == 0):
            file = open(files['size'], "w")
            file.write(str(foo[0]) + "\n" + str(foo[1]))
            file.close()
        self.Destroy()
        self.update_ownUI()  #refresh everything

    def mouse_position(self, event):
        '''Get which features are at a given position'''
        xposition, yposition = self.stc.ScreenToClient(wx.GetMousePosition())
        if xposition > 1 and yposition > 1:

            mposition = self.stc.CharPositionFromPoint(xposition, yposition)

            #			#which feature corresponds to this pos?
            Feature = genbank.gb.get_featurename_for_pos(mposition)
            return mposition, Feature
        else:
            return None, None
示例#4
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()