Beispiel #1
0
class EMEulerExplorer(EM3DSymModel,Animator):

	def mousePressEvent(self,event):
		if self.events_mode == "inspect":
			self.current_hit = self.get_hit(event)
			if self.current_hit == None:
				EM3DSymModel.mousePressEvent(self,event)
		else:
			EM3DSymModel.mousePressEvent(self,event)

	def mouseReleaseEvent(self,event):
		if self.events_mode == "inspect":
			if self.current_hit != None:
				self.updateGL() # there needs to be a clear or something  in order for the picking to work. This is  bit of hack but our rendering function doesn't take long anyhow
				hit = self.get_hit(event)
				if hit == self.current_hit:
					self.emit(QtCore.SIGNAL("point_selected"),self.current_hit,event)
			else:
				#EM3DSymModel.mouseReleaseEvent(self,event)
				EM3DModel.mouseReleaseEvent(self, event) #behavior in EM3DSymModel is not what we want (needed in sibling classes?)

			self.current_hit = None
		else:
				#EM3DSymModel.mouseReleaseEvent(self,event)
				EM3DModel.mouseReleaseEvent(self, event) #behavior in EM3DSymModel is not what we want (needed in sibling classes?)


	def mouseMoveEvent(self,event):
		if self.events_mode == "inspect" and self.current_hit:
			pass
		else:
			EM3DSymModel.mouseMoveEvent(self,event)

	def get_hit(self,event):
		v = self.vdtools.wview.tolist()
		self.get_gl_widget().makeCurrent() # prevents a stack underflow
#		x = event.x()
#		y = v[-1]-event.y()
#		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT )
#		vals = self.render(color_picking=True)
#		glFlush()
#		vv = glReadPixels(x,y,1,1,GL_RGB,GL_FLOAT)
#		reslt = Vec3f(float(vv[0][0][0]),float(vv[0][0][1]),float(vv[0][0][2]))
#		for i,val in enumerate(vals):
##			print val,reslt,(reslt-val).length(),vv[0][0]
#			if (reslt-val).length() < 0.01:
#				print i
##				print (reslt-val).length()
#				return i
#		print vv
#
		# the problem with this approach is that depth testing is not part of picking
		sb = [0 for i in xrange(0,512)]
		glSelectBuffer(512)
		glRenderMode(GL_SELECT)
		glInitNames()
		glMatrixMode(GL_PROJECTION)
		glPushMatrix()
		glLoadIdentity()
		gluPickMatrix(event.x(),v[-1]-event.y(),5,5,v)
		self.get_gl_widget().load_perspective()
		glMatrixMode(GL_MODELVIEW)
		glInitNames()
		self.render()
		glMatrixMode(GL_PROJECTION)
		glPopMatrix()
		glMatrixMode(GL_MODELVIEW)
		glFlush()

		intersection = None
		hits = list(glRenderMode(GL_RENDER))
		for hit in hits:
			a,b,c=hit
			if len(c) > 0:
				intersection = c[0]-1
				break

		return intersection


	def keyPressEvent(self,event):

		if event.key() == Qt.Key_F1:
			self.display_web_help("http://blake.bcm.edu/emanwiki/EMAN2/Programs/e2eulerxplor")
		elif event.key() == Qt.Key_F :
			if self.flatten>0 : self.flatten=0.0
			else: self.flatten=1.0
			self.generate_current_display_list(True)
			self.updateGL()
		else:
			EM3DSymModel.keyPressEvent(self,event)

	def __init__(self, gl_widget=None, auto=True,sparse_mode=False, file_name = ""):
		self.current_hit = None
		self.events_mode_list = ["navigate", "inspect"]
		self.events_mode = self.events_mode_list[1]


		self.init_lock = True # a lock indicated that we are still in the __init__ function
		self.au_data = None # This will be a dictionary, keys will be refinement directories, values will be something like available iterations for visual study
		if auto: # this a flag that tells the eulerxplorer to search for refinement data and automatically add elements to the inspector, if so
			self.gen_refinement_data()

		EM3DSymModel.__init__(self, gl_widget, eulerfilename=file_name)
		Animator.__init__(self)
		self.height_scale = 8.0 # This is a value used in EM3DSymModel which scales the height of the displayed cylinders - I made it 8 because it seemed fine. The user can change it anyhow
		self.projection_file = None  # This is a string - the name of the projection images file
		self.average_file = None # This is a string - the name of the class averages file
		self.proj_class_viewer = None # This will be an EMImageMXWidget that shows the class and/or projection
		self.particle_viewer = None  # This will be an EMImageMXWidget that shows the particles in a class
		self.clsdb = None # I think this will become redundant - it used to be the old database that stores which particles are in a class, but now that's stored in the header
		self.particle_file = None # This will be a string - the name of the file that has the particle files in it. This might be made redundant with the new approach
		self.alignment_file = None # This will be a string - the name of the file containing the alignment parameters - this is essential if you we want to show the aligned particles
		self.refine_dir = None # This will be a string - the name of the current refinement directory that is being studied
		self.dx = None # This is an EMData object storing the x shifts of the alignments for all particles. Generated by e2classaverage
		self.dy = None # This is an EMData object storing the y shifts of the alignments for all particles. Generated by e2classaverage
		self.da = None# This is an EMData object storing the angle of the alignments for all particles. Generated by e2classaverage
		self.dflip = None # This is an EMData object storing whether or not tthe alignment involved a flip, for all particles. Generated by e2classaverage
		self.classes = None # This is an EMData object storing which class(es) a particle belongs to. Generated by e2classaverage
		self.inclusions = None # This is and EMDAta storing a boolean that indicates the particle was actually included in the final average. Generated by e2classaverage

		self.average = None # This the class average itself, an EMData object
		self.projection = None # This is the projection itelse, an EMData object
		self.class_idx = None # This is the idx of the current class being studied in the interface

		self.previous_len = -1 # To keep track of the number of class averages that were previously viewable. This helps to make sure we can switch to the same class average in the context of a different refinement iteration
		self.mirror_eulers = False
		if sparse_mode:
			self.mirror_eulers = True # If True the drawn Eulers are are also rendered on the opposite side of the sphere - see EM3DSymModel.make_sym_dl_lis

		# Grab the symmetry from the workflow database if possible
		sym = "c1"
		if js_check_dict("refine_01/0_refine_parms.json"):
			try: sym = str(js_open_dict("refine_01/0_refine_parms.json")["sym"])
			except: pass

		# Have to tell the EM3DSymModel that there is a new sym
		self.set_symmetry(sym)

		# this object will have
		if self.au_data != None:
			combo_entries = self.au_data.keys()
			combo_entries.sort()
			combo_entries.reverse()

			if len(combo_entries) > 0:
				au = combo_entries[0]
				cls = self.au_data[au][0][0]
				self.au_selected(au,cls)
				self.mirror_eulers = True

		self.init_lock = False
		self.force_update=True # Force a display udpdate in EMImage3DSymModule

		QtCore.QObject.connect(self, QtCore.SIGNAL("point_selected"), self.au_point_selected)

	def __del__(self):
		EM3DSymModel.__del__(self) # this is here for documentation purposes - beware that the del function is important

	def initializeGL(self):
		glEnable(GL_NORMALIZE)

	def generate_current_display_list(self,force=False):
		'''
		Redefinition of EMImage3DSymModule.generate_current_display_list

		'''
		if self.init_lock: return 0
		if self.au_data == None or len(self.au_data) == 0:
			EM3DSymModel.generate_current_display_list(self,force)

		self.init_basic_shapes()
		if self.nomirror == True : val = 0
		else: val = 1
		self.trace_great_arcs(self.sym_object.get_asym_unit_points(val))
		self.trace_great_triangles(val)

		self.eulers = self.specified_eulers
		if self.eulers == None:	return 0

#		if not self.colors_specified: self.point_colors = []
#		else: self.point_colors = self.specified_colors
#		self.points = []
#		for i in self.eulers:
#			p = i.transpose()*Vec3f(0,0,self.radius)
#			self.points.append(p)
#			if not self.colors_specified: self.point_colors.append((0.34615, 0.3143, 0.0903,1))

		self.make_sym_dl_list(self.eulers)
		return 1
	def get_data_dims(self):
		return (2*self.radius,2*self.radius,2*self.radius)

	def width(self): return 2*self.radius
	def height(self): return 2*self.radius
	def depth(self): return 2*self.radius

	def gen_refinement_data(self):
		dirs,files = get_files_and_directories()

		dirs.sort()
		for i in range(len(dirs)-1,-1,-1):
			if len(dirs[i]) != 9:
				dirs.pop(i)
			elif dirs[i][:7] != "refine_":
				dirs.pop(i)
			else:
				try: int(dirs[i][7:])
				except: dirs.pop(i)

		self.dirs = dirs
		print dirs

		self.au_data = {}
		for dir in self.dirs:
			d = self.check_refine_db_dir(dir)
			if len(d) != 0 and len(d[dir]) != 0: self.au_data.update(d)

	def check_refine_db_dir(self,dir,s1="classes",s2=None,s3="cls_result",s4="threed",s5="projections"):
		# s2 used to be class_indices
		names = [s1,s2,s3,s4,s5]
		data = {}
		data[dir] = []
		register_js_name = "{}/0_refine_parms.json".format(dir)

		files=os.listdir(dir)
		try:
			nums=[int(i[7:9]) for i in files if "threed" in i and "even" not in i and "odd" not in i]
			maxnum=max(nums)
		except :
			print "Nothing in ",dir
			return {}

		for i in xrange(1,maxnum+1):
			exte="_{:02d}_even.hdf".format(i)
			exto="_{:02d}_odd.hdf".format(i)
			data[dir].append([sadd(dir,s1,exte),sadd(dir,s2,exte),sadd(dir,s3,exte),sadd(dir,s4,exte),sadd(dir,s5,exte)])
			data[dir].append([sadd(dir,s1,exto),sadd(dir,s2,exto),sadd(dir,s3,exto),sadd(dir,s4,exto),sadd(dir,s5,exto)])

		return data

	def set_projection_file(self,projection_file): self.projection_file = projection_file
	def get_inspector(self):
		if not self.inspector :
			if (self.au_data == None or len(self.au_data) == 0) and self.mirror_eulers == False: #self.mirror_eulers thing is a little bit of a hack, it's tied to the sparse_mode flag in the init function, which is used by euler_display in EMAN2.py
				self.inspector=EMAsymmetricUnitInspector(self,True,True)
			else:
				self.inspector=EMAsymmetricUnitInspector(self)
			QtCore.QObject.connect(self.inspector,QtCore.SIGNAL("au_selected"),self.au_selected)
		return self.inspector


	def au_selected(self,refine_dir,cls):
		self.refine_dir = refine_dir
		get_application().setOverrideCursor(Qt.BusyCursor)
		data = []
		for d in self.au_data[refine_dir]:
			if d[0] == cls:
				data = d;
				break

		if len(data) == 0:
			error("error, no data for %s %s, returning" %(refine_dir,cls))
#			print "error, no data for",au,cls,"returning"
			self.events_handlers["inspect"].reset()
			get_application().setOverrideCursor(Qt.ArrowCursor)
			return

		try :
			self.particle_file=js_open_dict(refine_dir+"/0_refine_parms.json")["input"]
		except:
			error("No data in "+refine_dir )
			self.events_handlers["inspect"].reset()
			get_application().setOverrideCursor(Qt.ArrowCursor)
			return

		self.average_file = cls
		self.projection_file = data[4]
		self.alignment_file = data[2]
		self.clsdb = data[1]

		self.dx = None
		self.dy = None
		self.da = None
		self.dflip = None
		self.classes = None

		eulers = get_eulers_from(self.average_file)
		#s = Symmetries.get("d7")
		#eulers = s.gen_orientations("rand",{"n":EMUtil.get_image_count(self.average_file)})

		self.specify_eulers(eulers)
		#from emimagemx import EMDataListCache
		#a = EMData.read_images(self.average_file)
		#a = [test_image() for i in range(EMUtil.get_image_count(self.average_file))]
		#print len(a),len(eulers)
		#b = [a[i].set_attr("xform.projection",eulers[i]) for i in range(len(eulers))]
		#b = [a[i].set_attr("ptcl_repr",1) for i in range(len(eulers))]

		self.set_emdata_list_as_data(EMLightWeightParticleCache.from_file(self.average_file),"ptcl_repr")
		#self.set_emdata_list_as_data(EMDataListCache(self.average_file),"ptcl_repr")
#		self.set_emdata_list_as_data(a,"ptcl_repr")
		self.force_update = True
		self.au_point_selected(self.class_idx,None)
		# if we have the same number of Eulers we can update everything
#		if self.previous_len == len(eulers) : self.events_handlers["inspect"].repeat_event()
#		else:self.events_handlers["inspect"].reset()
		self.previous_len = len(eulers)
		if not self.init_lock:self.updateGL()
		get_application().setOverrideCursor(Qt.ArrowCursor)

	def __get_file_headers(self,filename):
		headers = []
		n = EMUtil.get_image_count(filename)
		for i in range(n):
			e = EMData()
			e.read_image(filename,i,True)
			headers.append(e)
		return headers

	def au_point_selected(self,i,event=None):
		if i == None:
			if event != None and event.modifiers()&Qt.ShiftModifier:
				if self.special_euler != None:
					self.special_euler = None
					if not self.init_lock:self.regen_dl()
			return
#		self.arc_anim_points = None
		self.projection = None
		if self.euler_data:
#			db = db_open_dict(self.average_file)
#			a = db.get(i)
#			print a["nx"]
#			print self.average_file,i
#			self.average = EMData(self.average_file,i)
#			self.average["nx"]
			self.average = self.euler_data[i]#
			self.projection = EMData(self.projection_file,self.average.get_attr("projection_image_idx"))
			self.average.process_inplace("normalize.toimage",{"to":self.projection})
			try:
				self.class_idx = self.average.get_attr("projection_image_idx")
				print "%d (%d)"%(self.class_idx,self.average["ptcl_repr"])
			except:
				self.class_idx = -1
		else: return

		#if self.projection  == None and self.average == None: return
		first = False
		if self.proj_class_viewer == None:
			first = True
			self.proj_class_viewer = EMImageMXWidget(data=None,application=get_application())
#			self.proj_class_viewer = EMImage2DWidget(image=None,application=get_application())
			QtCore.QObject.connect(self.proj_class_viewer,QtCore.SIGNAL("module_closed"),self.on_mx_view_closed)
#			self.proj_class_viewer.set_mouse_mode("App" )
			QtCore.QObject.connect(self.proj_class_viewer,QtCore.SIGNAL("mx_image_selected"), self.mx_image_selected)
			get_application().show_specific(self.proj_class_viewer)

			self.proj_class_single = EMImage2DWidget(image=None,application=get_application())
			QtCore.QObject.connect(self.proj_class_single,QtCore.SIGNAL("module_closed"),self.on_mx_view_closed)
#			QtCore.QObject.connect(self.proj_class_single,QtCore.SIGNAL("mx_image_selected"), self.mx_image_selected)
			get_application().show_specific(self.proj_class_single)

		disp = []
		if self.projection != None: disp.append(self.projection)
		if self.average != None and self.projection!=None:
			# ok, this really should be put into its own processor
			#dataf = self.projection.do_fft()
			#apix=self.projection["apix_x"]
			#curve = dataf.calc_radial_dist(dataf["ny"], 0, 0.5,True)
			#curve=[i/(dataf["nx"]*dataf["ny"])**2 for i in curve]
			#xcurve=[i/(apix*2.0*dataf["ny"]) for i in range(len(curve))]
			#xyd=XYData()
			#xyd.set_xy_list(xcurve,curve)
			#filt=self.average.process("filter.setstrucfac",{"apix":apix,"strucfac":xyd})
			#filt.process_inplace("normalize.toimage",{"to":self.average})
			self.projection["apix_x"]=self.average["apix_x"]
			self.projection["apix_y"]=self.average["apix_y"]
			self.projection["apix_z"]=self.average["apix_z"]
			filt=self.projection.process("threshold.notzero")
			filt.mult(self.average)
			filt.process_inplace("filter.matchto",{"to":self.projection})

			disp.append(filt)

		if self.average!=None:
			disp.append(self.average)

		self.proj_class_viewer.set_data(disp)
		self.proj_class_single.set_data(disp)

		self.proj_class_viewer.updateGL()
		self.proj_class_single.updateGL()
		if self.particle_viewer != None:
			self.mx_image_selected(None,None)
		if first: self.proj_class_viewer.optimally_resize()

		if i != self.special_euler:
			self.special_euler = i
			self.force_update = True

		if not self.init_lock: self.updateGL()


	def on_mx_view_closed(self):
		self.proj_class_viewer = None
		self.proj_class_single = None

	def on_particle_mx_view_closed(self):
		self.particle_viewer = None

	def animation_done_event(self,animation):
		pass

	def alignment_time_animation(self,transforms):
		if len(transforms) < 2: return
		animation = OrientationListAnimation(self,transforms,self.radius)
		self.register_animatable(animation)

	def particle_selected(self,event,lc):
		if lc != None:
			d = lc[3]
			ptcl_idx = d["Img #"]
			data = self.au_data[self.refine_dir]
			prj = []
			cls_result = []
			for l in data:
				for s in l:
					stag = base_name(s)

					if len(stag) > 11 and stag[:11] == "projections":
						prj.append(s)
					elif len(stag) > 10 and stag[:10] == "cls_result":
						cls_result.append(s)

			transforms = []
			if len(prj) != len(cls_result): RunTimeError("The number of cls_result files does not match the number of projection files?")

			e = EMData()
			for i,cr in enumerate(cls_result):
				r = Region(0,ptcl_idx,1,1)
				e.read_image(cr,0,False,r)
				p = int(e.get(0))
				e.read_image(prj[i],p,True)
				transforms.append(e["xform.projection"])

			self.alignment_time_animation(transforms)

	def mx_image_selected(self,event,lc):
#		self.arc_anim_points = None
		get_application().setOverrideCursor(Qt.BusyCursor)
		if lc != None: self.sel = lc[0]

		if self.average != None:
			included = []
			if self.average.has_attr("class_ptcl_idxs"):
				included = self.average["class_ptcl_idxs"]
			excluded = []
			if self.average.has_attr("exc_class_ptcl_idxs"):
				excluded = self.average["exc_class_ptcl_idxs"]

			all = included + excluded
			#all.sort()

			bdata = []
			data = []
			idx_included = []
			running_idx = 0
			from emimagemx import ApplyAttribute
			for val in included:
				bdata.append([self.particle_file,val,[ApplyAttribute("Img #",val)]])
				idx_included.append(running_idx)
				running_idx += 1

			idx_excluded = []
			for val in excluded:
				bdata.append([self.particle_file,val,[ApplyAttribute("Img #",val)]])
				idx_excluded.append(running_idx)
				running_idx += 1

			data = EMLightWeightParticleCache(bdata)

			first = False
			if self.particle_viewer == None:
				first = True
				self.particle_viewer = EMImageMXWidget(data=None,application=get_application())
				self.particle_viewer.set_mouse_mode("App" )
				QtCore.QObject.connect(self.particle_viewer,QtCore.SIGNAL("module_closed"),self.on_particle_mx_view_closed)
				QtCore.QObject.connect(self.particle_viewer,QtCore.SIGNAL("mx_image_selected"), self.particle_selected)
				get_application().show_specific(self.particle_viewer)


			self.check_images_in_memory()

			if self.sel== 0 or self.alignment_file == None:
				self.particle_viewer.set_data(data)
			else:

				for i,[name,idx,f] in enumerate(bdata):
					index = -1
					if self.classes.get_xsize() == 1:
						index = 0 # just assume it's the first one - this is potentially fatal assumption, but in obscure situations only
					else:
						for j in range(self.classes.get_xsize()):
							if int(self.classes.get(j,idx)) == self.class_idx:
								index = j
								break
					if index == -1:
						print "couldn't find"
						get_application().setOverrideCursor(Qt.ArrowCursor)
						return

					x = self.dx.get(index,idx)
					y = self.dy.get(index,idx)
					a = self.da.get(index,idx)
					m = self.dflip.get(index,idx)

					t = Transform({"type":"2d","alpha":a,"mirror":int(m)})
					t.set_trans(x,y)
					from emimagemx import ApplyTransform
					f.append(ApplyTransform(t))
					#data[i].transform(t)
				self.particle_viewer.set_data(data)


			if first:
				self.particle_viewer.updateGL()
				self.particle_viewer.optimally_resize()

			self.particle_viewer.clear_sets(False)
			self.particle_viewer.enable_set("Excluded",idx_excluded,True,False)
			self.particle_viewer.enable_set("Included",idx_included,False,False)
			self.particle_viewer.updateGL()

			get_application().setOverrideCursor(Qt.ArrowCursor)

			self.updateGL()

	def check_images_in_memory(self):
		if self.alignment_file != None:
			if self.dx == None:
				self.dx = EMData(self.alignment_file,2)
			if self.dy == None:
				self.dy = EMData(self.alignment_file,3)
			if self.da == None:
				self.da = EMData(self.alignment_file,4)
			if self.dflip == None:
				self.dflip  = EMData(self.alignment_file,5)
			if self.classes == None:
				self.classes  = EMData(self.alignment_file,0)
			if self.inclusions == None:
				self.inclusions  = EMData(self.alignment_file,1)


	def set_events_mode(self,mode):
		if not mode in self.events_mode_list:
			print "error, unknown events mode", mode
			return

		else:
			self.events_mode = mode

	def closeEvent(self,event):
		if self.inspector !=None: self.inspector.close()
		if self.proj_class_viewer !=None: self.proj_class_viewer.close()
		if self.proj_class_single !=None: self.proj_class_single.close()
		if self.particle_viewer != None: self.particle_viewer.close()
		get_application().close_specific(self)
		self.emit(QtCore.SIGNAL("module_closed")) # this signal is
Beispiel #2
0
class EMClassPtclTool(QtGui.QWidget):
	"""This class is a tab widget for inspecting particles within class-averages"""

	def __init__(self,extrafiles=None):
		QtGui.QWidget.__init__(self)
		self.vbl = QtGui.QVBoxLayout(self)

		self.extrafiles=extrafiles

		# A listwidget for selecting which class-average file we're looking at
		self.wclassfilel=QtGui.QLabel("Class-average File:")
		self.vbl.addWidget(self.wclassfilel)

		self.wfilesel=QtGui.QListWidget()
		self.vbl.addWidget(self.wfilesel)
		self.vbl.addSpacing(5)

		# A widget containing the current particle filename, editable by the user
		# If edited it will also impact set generation !
		self.wptclfilel=QtGui.QLabel("Particle Data File:")
		self.vbl.addWidget(self.wptclfilel)

		self.wptclfile=QtGui.QComboBox(self)
		self.vbl.addWidget(self.wptclfile)
		self.vbl.addSpacing(5)

		# Selection tools
		self.wselectg=QtGui.QGroupBox("Class Selection",self)
		self.wselectg.setFlat(False)
		self.vbl.addWidget(self.wselectg)
		self.vbl.addSpacing(5)

		self.gbl0=QtGui.QGridLayout(self.wselectg)

		self.wselallb=QtGui.QPushButton("All")
		self.gbl0.addWidget(self.wselallb,0,0)

		self.wselnoneb=QtGui.QPushButton("Clear")
		self.gbl0.addWidget(self.wselnoneb,0,1)

		self.wselrangeb=QtGui.QPushButton("Range")
		self.gbl0.addWidget(self.wselrangeb,1,0)

		self.wselinvertb=QtGui.QPushButton("Invert")
		self.gbl0.addWidget(self.wselinvertb,0,2)

		self.wsel3db=QtGui.QPushButton("From 3D")
		self.gbl0.addWidget(self.wsel3db,1,2)

		self.wprocessg=QtGui.QGroupBox("Process results",self)
		self.wprocessg.setFlat(False)
		self.vbl.addWidget(self.wprocessg)

		self.vbl2=QtGui.QVBoxLayout(self.wprocessg)

		self.wselused=CheckBox(None,"Included Ptcls",1,100)
		self.vbl2.addWidget(self.wselused)

		self.wselunused=CheckBox(None,"Excluded Ptcls",1,100)
		self.vbl2.addWidget(self.wselunused)

		# Mark particles in selected classes as bad
		self.wmarkbut=QtGui.QPushButton("Mark as Bad")
		self.vbl2.addWidget(self.wmarkbut)

		# Mark particles in selected classes as good
		self.wmarkgoodbut=QtGui.QPushButton("Mark as Good")
		self.vbl2.addWidget(self.wmarkgoodbut)

		# Make a new set from selected classes
		self.wmakebut=QtGui.QPushButton("Make New Set")
		self.vbl2.addWidget(self.wmakebut)
#		self.wmakebut.setEnabled(False)

		# Save list
		self.wsavebut=QtGui.QPushButton("Save Particle List")
		self.vbl2.addWidget(self.wsavebut)

		# Save micrograph dereferenced lists
		self.wsaveorigbut=QtGui.QPushButton("Save CCD-based List")
		self.vbl2.addWidget(self.wsaveorigbut)


		QtCore.QObject.connect(self.wfilesel,QtCore.SIGNAL("itemSelectionChanged()"),self.fileUpdate)
		QtCore.QObject.connect(self.wptclfile,QtCore.SIGNAL("currentIndexChanged(int)"),self.ptclChange)
		QtCore.QObject.connect(self.wselallb,QtCore.SIGNAL("clicked(bool)"),self.selAllClasses)
		QtCore.QObject.connect(self.wselnoneb,QtCore.SIGNAL("clicked(bool)"),self.selNoClasses)
		QtCore.QObject.connect(self.wselrangeb,QtCore.SIGNAL("clicked(bool)"),self.selRangeClasses)
		QtCore.QObject.connect(self.wselinvertb,QtCore.SIGNAL("clicked(bool)"),self.selInvertClasses)
		QtCore.QObject.connect(self.wsel3db,QtCore.SIGNAL("clicked(bool)"),self.sel3DClasses)
		QtCore.QObject.connect(self.wmakebut,QtCore.SIGNAL("clicked(bool)"),self.makeNewSet)
		QtCore.QObject.connect(self.wmarkbut,QtCore.SIGNAL("clicked(bool)"),self.markBadPtcl)
		QtCore.QObject.connect(self.wmarkgoodbut,QtCore.SIGNAL("clicked(bool)"),self.markGoodPtcl)
		QtCore.QObject.connect(self.wsavebut,QtCore.SIGNAL("clicked(bool)"),self.savePtclNum)
		QtCore.QObject.connect(self.wsaveorigbut,QtCore.SIGNAL("clicked(bool)"),self.saveOrigPtclNum)

		# View windows, one for class-averages, one for good particles and one for bad particles
		self.vclasses=None
		self.vgoodptcl=None
		self.vbadptcl=None

		self.updateFiles()

	def makeNewSet(self,x):
		"Makes a new particle set based on the selected class-averages"
		setname=QtGui.QInputDialog.getText(None,"Set Name","Please specify the name for the set. If you specify an existing set, new particles will be added to the end")
		if setname[1]==False : return
		else: setname=setname[0]
		if setname[-4:]!=".lst" : setname=setname+".lst"
		if not "/" in setname : setname="sets/"+setname

		lst=LSXFile(self.curPtclFile())		# lst file for dereferenceing
		lstout=LSXFile(setname)
		include=[]
		# iterate over each particle from each marked class-average
		for n in self.curPtclIter(self.wselused.getValue(),self.wselunused.getValue()):
			try :
				orign,origfile,comment=lst.read(n)			# the original file/number dereferenced from the LST file
			except:
				QtGui.QMessageBox.warning(self,"Error !","The data_source '%s' does not follow EMAN2.1 project conventions. Cannot find raw particles for set."%srcfile)
				return

			include.append((origfile,orign,comment))		# build a list so we can sort by frame
		
		# write the new set
		for i in sorted(include) : lstout.write(-1,i[1],i[0],i[2])

	def markBadPtcl(self,x):
		"Mark particles from the selected class-averages as bad in the set interface"

		r=QtGui.QMessageBox.question(None,"Are you sure ?","WARNING: There is no undo for this operation. It will  mark all particles associated with the selected class-averages as bad. Are you sure you want to proceed ?",QtGui.QMessageBox.Yes|QtGui.QMessageBox.Cancel)
		if r==QtGui.QMessageBox.Cancel : return

		lst=LSXFile(self.curPtclFile())		# lst file for dereferenceing
		ptcls={}						# dictionary keyed by original frame filename with list of selected particle #s
		# iterate over each particle from each marked class-average
		for n in self.curPtclIter(self.wselused.getValue(),self.wselunused.getValue()):
			try :
				orign,origfile,comment=lst.read(n)
			except:
				QtGui.QMessageBox.warning(self,"Error !","The data_source '%s' does not follow EMAN2.1 project conventions. Cannot find raw particles for set."%srcfile)
				return

			try: ptcls[origfile].append(orign)		# try to add to a list for an existing filename
			except: ptcls[origfile]=[orign]			# creates the list for this filename if it's new

		#now mark the particles as bad
		newbad=0
		totbad=0
		for origfile in ptcls:
			js=js_open_dict(info_name(origfile))	# get the info dict for this file

			try: sets=js["sets"]
			except: sets={"bad_particles":[]}
			try: badset=set(sets["bad_particles"])
			except: badset=set()

			try:
				newset=list(set(ptcls[origfile])|badset)
				sets["bad_particles"]=newset	# update the set of bad particles for this image file
				js["sets"]=sets
				totbad+=len(badset)
				newbad+=len(newset)-len(badset)
			except:
				print "Error setting bad particles in ",origfile
				
		print newbad, " new particles marked as bad. Total of ",totbad," in affected micrographs"

	def markGoodPtcl(self,x):
		"Mark particles from the selected class-averages as good in the set interface"

		r=QtGui.QMessageBox.question(None,"Are you sure ?","WARNING: There is no undo for this operation. It will un-mark all particles associated with the selected class-averages as bad. Are you sure you want to proceed ?",QtGui.QMessageBox.Yes|QtGui.QMessageBox.Cancel)
		if r==QtGui.QMessageBox.Cancel : return

		lst=LSXFile(self.curPtclFile())		# lst file for dereferenceing
		ptcls={}						# dictionary keyed by original frame filename with list of selected particle #s
		# iterate over each particle from each marked class-average
		for n in self.curPtclIter(self.wselused.getValue(),self.wselunused.getValue()):
			try :
				orign,origfile,comment=lst.read(n)
			except:
				QtGui.QMessageBox.warning(self,"Error !","The data_source '%s' does not follow EMAN2.1 project conventions. Cannot find raw particles for set."%srcfile)
				return

			try: ptcls[origfile].append(orign)		# try to add to a list for an existing filename
			except: ptcls[origfile]=[orign]			# creates the list for this filename if it's new

		#now mark the particles as good
		badafter=0
		badbefore=0
		for origfile in ptcls:
			js=js_open_dict(info_name(origfile))	# get the info dict for this file
			try:
				badset=set(js["sets"]["bad_particles"])
				js["sets"]["bad_particles"]=list(badset-set(ptcls[origfile]))	# update the set of bad particles for this image file
			except:
				pass		# since marking as good is the same as removing from the bad list, if there is no bad list, there is nothing to do

			try: sets=js["sets"]
			except: continue	# if no current bad particles, nothing to mark good
			try: badset=sets["bad_particles"]
			except: continue

			try:
				newset=list(badset-set(ptcls[origfile]))
				sets["bad_particles"]=newset	# update the set of bad particles for this image file
				js["sets"]=sets
				badbefore+=len(badset)
				badafter+=len(newset)
			except:
				continue
		
		print badbefore," bad particles before processing, now ",badafter

	def savePtclNum(self,x):
		"Saves a list of particles from marked classes into a text file"

		filename=QtGui.QInputDialog.getText(None,"Filename","Please enter a filename for the particle list. The file will contain the particle number (within the particle file) for each particle associated with a selected class-average.")
		if filename[1]==False or filename[0]=="" : return

		out=file(filename[0],"w")
		for i in self.curPtclIter(self.wselused.getValue(),self.wselunused.getValue()): out.write("%d\n"%i)
		out.close()

	def saveOrigPtclNum(self,x):
		"Saves a file containing micrograph-dereferenced particles"
		filename=QtGui.QInputDialog.getText(None,"Filename","Please enter a filename for the particle list. The file will contain particle number and image file, one per line. Image files will be referenced back to the original per-CCD frame stacks.")
		if filename[1]==False or filename[0]=="" : return

		lst=LSXFile(self.curPtclFile())		# lst file for dereferenceing
		include=[]
		# iterate over each particle from each marked class-average
		for n in self.curPtclIter(self.wselused.getValue(),self.wselunused.getValue()):
			try :
				orign,origfile,comment=lst.read(n)			# the original file/number dereferenced from the LST file
			except:
				QtGui.QMessageBox.warning(self,"Error !","The data_source '%s' does not follow EMAN2.1 project conventions. Cannot find raw particles for set."%srcfile)
				return

			include.append((origfile,orign,comment))		# build a list so we can sort by frame
		
		# write the output file
		out=file(filename,"w")
		for i in sorted(include) : out.write("{}\t{}\n".format(i[1],i[0]))
		out=None

	def selAllClasses(self,x):
		"Mark all classes as selected"
		self.vclasses.all_set()

	def selNoClasses(self,x):
		"Clear selection"
		self.vclasses.clear_set()

	def selRangeClasses(self,x):
		"Select a range of images (ask the user for the range)"
		rng=QtGui.QInputDialog.getText(None,"Select Range","Enter the range of particle values as first-last (inclusive). Merges with existing selection.")
		if rng[1]==False : return

		try:
			x0,x1=rng[0].split("-")
			x0=int(x0)
			x1=int(x1)+1
		except:
			QtGui.QMessageBox.warning(self,"Error !","Invalid range specified. Use: min-max")
			return

		self.vclasses.subset_set(range(x0,x1))

	def selInvertClasses(self,x):
		"Inverts the current selection set"
		self.vclasses.invert_set()

	def sel3DClasses(self,x):
		"Select a range of images based on those used in a 3-D reconstruction associated with this classes file. Removes current selection first."

		f=self.curFile()
		if not '#classes_' in f :
			QtGui.QMessageBox.warning(self,"Error !","A classes_xx file from a refine_xx directory is not currently selected")
			return

		# construct the path to the threed_xx file
		num=f.split("_")[-1]
		pre=f.split("#")[0]
		d3path="%s#threed_%s"%(pre,num)
		try:
			a=EMData(d3path,0,True)
			goodptcl=a["threed_ptcl_idxs"]
		except:
			QtGui.QMessageBox.warning(self,"Error !","Cannot read classes from "+d3path)
			return

		self.vclasses.clear_set()
		self.vclasses.subset_set(goodptcl)

	def ptclChange(self,value):
		"Called when a change of particle data file occurs to zero out the display"
		try:
			self.vgoodptcl.set_data(None)
			self.vbadptcl.set_data(None)
		except:
			pass

	def updateFiles(self):
		"Updates the list of classes files"
		subdir=sorted([i for i in os.listdir(e2getcwd()) if "r2d_" in i or "relion2d_" in i or "refine_" in i or "multi_" in i])
		for d in subdir:
			try: dbs=os.listdir(d)
			except: continue
			dbs.sort()
			for db in dbs:
				if "classes_" in db or "allrefs_" in db :
					self.wfilesel.addItem("%s/%s"%(d,db))

		for f in self.extrafiles:
			self.wfilesel.addItem(f)

		dbs=os.listdir("sets")
		dbs.sort()
		for db in dbs:
			self.wptclfile.addItem("sets/"+db)

	def curPtclIter(self,included=True,excluded=True):
		"This is a generator function which yields n (in self.curPtclFile()) for all particles associated with selected classes"
		for ci in self.curSet():
			try :
				c=EMData(self.curFile(),ci,True)		# read header for current class average
				if included :
					incl=c["class_ptcl_idxs"]
					if isinstance(incl,int) : incl=[incl]		# This should not happen, but seems to sometimes for some reason
					for i in incl:
						yield(i)
				if excluded and c.has_attr("exc_class_ptcl_idxs"):
					excl=c["exc_class_ptcl_idxs"]
					if isinstance(excl,int) : excl=[excl]		# This should not happen, but seems to sometimes for some reason
					for i in excl:
						yield(i)
			except:
				print "Problem with class %d (%s). Skipping"%(ci,self.curFile())
				traceback.print_exc()
				continue

	def curFile(self):
		"return the currently selected file as a readable path"
		return str(self.wfilesel.item(self.wfilesel.currentRow()).text())		# text of the currently selected item

	def curSet(self):
		"return a set (integers) of the currently selected class-averages"
		
		return self.vclasses.get_set("evalptcl")

	def curPtclFile(self):
		"return the particle file associated with the currently selected classes file"
		return str(self.wptclfile.currentText())		# text of the currently selected item


	def fileUpdate(self):
		"Called when the user selects a file from the list or need to completely refresh display"

		QtGui.qApp.setOverrideCursor(Qt.BusyCursor)

		if self.vclasses==None :
			self.vclasses=EMImageMXWidget()
			self.vclasses.set_mouse_mode("App")
			QtCore.QObject.connect(self.vclasses,QtCore.SIGNAL("mx_image_selected"),self.classSelect)
			QtCore.QObject.connect(self.vclasses,QtCore.SIGNAL("mx_image_double"),self.classDouble)

		self.vclasses.set_title("Classes")


#		self.classes=EMData.read_images(self.curFile())
		self.vclasses.set_data(self.curFile(),self.curFile())
#		self.vclasses.set_single_active_set("selected")		# This makes the 'set' representing the selected class-averages current
		self.vclasses.set_mouse_mode("App")
		self.vclasses.enable_set("evalptcl",[])

		# This makes sure the particle file is in the list of choices and is selected
		try:
			ptclfile=EMData(self.curFile(),0,True)["class_ptcl_src"]
			i=self.wptclfile.findText(ptclfile)
			if i==-1 :
				self.wptclfile.insertItem(0,ptclfile)
				self.wptclfile.setCurrentIndex(0)
			else:
				self.wptclfile.setCurrentIndex(i)
		except:
			QtGui.QMessageBox.warning(self,"Error !","This image does not appear to be a class average. (No class_ptcl_src, etc.)")
			ptclfile="None"


		# Make sure our display widgets exist
		if self.vgoodptcl==None :
			self.vgoodptcl=EMImageMXWidget()
		self.vgoodptcl.set_title("Included Particles")

		if self.vbadptcl==None :
			self.vbadptcl=EMImageMXWidget()
		self.vbadptcl.set_title("Excluded Particles")

		self.vclasses.show()
		self.vgoodptcl.show()
		self.vbadptcl.show()

		QtGui.qApp.setOverrideCursor(Qt.ArrowCursor)

	def classSelect(self,event,lc):
		"Single clicked class particle. lc=(img#,x,y,image_dict)"

		QtGui.qApp.setOverrideCursor(Qt.BusyCursor)
		ptclfile=self.curPtclFile()
		try:
			ptclgood=lc[3]["class_ptcl_idxs"]
			self.vgoodptcl.set_data(EMData.read_images(ptclfile,ptclgood))
		except:
			QtGui.QMessageBox.warning(self,"Error !","This image does not appear to be a class average. (No class_ptcl_src, etc.)")
			QtGui.qApp.setOverrideCursor(Qt.ArrowCursor)
			return
		try:
			ptclbad=lc[3]["exc_class_ptcl_idxs"]
			self.vbadptcl.set_data(EMData.read_images(ptclfile,ptclbad))
		except:
			ptclbad=[]
			self.vbadptcl.set_data(None)

		self.vgoodptcl.show()
		self.vbadptcl.show()
		QtGui.qApp.setOverrideCursor(Qt.ArrowCursor)

	def classDouble(self,event,lc):
		self.vclasses.image_set_associate(lc[0],update_gl=True)

	def closeEvent(self,event):
		try :
			self.vclasses.commit_sets()
			self.vclasses.close()
		except: pass
		try : self.vgoodptcl.close()
		except: pass
		try : self.vbadptcl.close()
		except: pass

		QtGui.QWidget.closeEvent(self, event)
Beispiel #3
0
	def __new__(cls,data=None,old=None,app=None,force_2d=False,force_plot=False,filename="",replace=True):
		"""This will create a new EMImage* object depending on the type of 'data'. If
		old= is provided, and of the appropriate type, it will be used rather than creating
		a new instance.
		"""
		
		if isinstance(data,EMData) and data.get_size()==0: raise RuntimeError("Can not display an EMData object that has no pixels")
		
		from EMAN2 import remove_directories_from_name
		if force_plot and force_2d:
			# ok this sucks but it suffices for the time being
			print "Error, the force_plot and force_2d options are mutually exclusive"
			return None
		
		if force_plot or (isinstance(data,EMData) and data.get_zsize()==1 and data.get_ysize()==1):
			from emplot2d import EMPlot2DWidget
			if old:
				if isinstance(old,EMPlot2DWidget) :
					old.set_data(data,remove_directories_from_name(filename),replace)
					return old
			widget = EMPlot2DWidget(application=app)
			widget.set_data(data,remove_directories_from_name(filename),replace)
			return widget	
		elif force_2d or (isinstance(data,EMData) and data.get_zsize()==1):
			from emimage2d import EMImage2DWidget
			if old:
				if isinstance(old,EMImage2DWidget) :
					old.set_data(data,filename)
					return old
			widget = EMImage2DWidget(application=app)
			widget.set_data(data,filename)
			return widget
		elif isinstance(data,EMData):
			if isinstance(old,EMScene3D): widget = old
			else: widget = EMScene3D()
			data = EMDataItem3D(data, transform=Transform())
			#data.setSelectedItem(True)
			isosurface = EMIsosurface(data, transform=Transform())
			widget.insertNewNode(os.path.basename(filename), data, parentnode=widget)
			widget.insertNewNode("Iso", isosurface, parentnode=data)
			return widget

		elif isinstance(data,list) and isinstance(data[0],EMData):
			from emimagemx import EMImageMXWidget
			if old:
				if isinstance(old,EMImageMXWidget) :
					old.set_data(data,filename)
					return old
			widget = EMImageMXWidget(application=app)
			widget.set_data(data,filename)
			return widget
		elif isinstance(data,list):
			from emplot3d import EMPlot3DWidgetNew
			if (isinstance(data[0],list) or isinstance(data[0],tuple)) and len(data) > 2:
				if old:
					if isinstance(old,EMPlot3DWidgetNew) :
						old.set_data(data,remove_directories_from_name(filename),replace)
						return old
						
				widget = EMPlot3DWidgetNew()
				widget.set_data(data,remove_directories_from_name(filename),replace)
				return widget	
			else:
				from emplot2d import EMPlot2DWidget
				if old:
					if isinstance(old,EMPlot2DWidget) :
						old.set_data(data,remove_directories_from_name(filename),replace)
						return old
				widget = EMPlot2DWidget(application=app)
				widget.set_data(data,remove_directories_from_name(filename),replace)
				return widget	
		else:
			raise Exception,"data must be a single EMData object or a list of EMData objects"
Beispiel #4
0
class EMClassPtclTool(QtGui.QWidget):
    """This class is a tab widget for inspecting particles within class-averages"""
    def __init__(self, extrafiles=None):
        QtGui.QWidget.__init__(self)
        self.vbl = QtGui.QVBoxLayout(self)

        self.extrafiles = extrafiles

        # A listwidget for selecting which class-average file we're looking at
        self.wclassfilel = QtGui.QLabel("Class-average File:")
        self.vbl.addWidget(self.wclassfilel)

        self.wfilesel = QtGui.QListWidget()
        self.vbl.addWidget(self.wfilesel)
        self.vbl.addSpacing(5)

        # A widget containing the current particle filename, editable by the user
        # If edited it will also impact set generation !
        self.wptclfilel = QtGui.QLabel("Particle Data File:")
        self.vbl.addWidget(self.wptclfilel)

        self.wptclfile = QtGui.QComboBox(self)
        self.vbl.addWidget(self.wptclfile)
        self.vbl.addSpacing(5)

        # Selection tools
        self.wselectg = QtGui.QGroupBox("Class Selection", self)
        self.wselectg.setFlat(False)
        self.vbl.addWidget(self.wselectg)
        self.vbl.addSpacing(5)

        self.gbl0 = QtGui.QGridLayout(self.wselectg)

        self.wselallb = QtGui.QPushButton("All")
        self.gbl0.addWidget(self.wselallb, 0, 0)

        self.wselnoneb = QtGui.QPushButton("Clear")
        self.gbl0.addWidget(self.wselnoneb, 0, 1)

        self.wselrangeb = QtGui.QPushButton("Range")
        self.gbl0.addWidget(self.wselrangeb, 1, 0)

        self.wselinvertb = QtGui.QPushButton("Invert")
        self.gbl0.addWidget(self.wselinvertb, 0, 2)

        self.wsel3db = QtGui.QPushButton("From 3D")
        self.gbl0.addWidget(self.wsel3db, 1, 2)

        self.wprocessg = QtGui.QGroupBox("Process results", self)
        self.wprocessg.setFlat(False)
        self.vbl.addWidget(self.wprocessg)

        self.vbl2 = QtGui.QVBoxLayout(self.wprocessg)

        self.wselused = CheckBox(None, "Included Ptcls", 1, 100)
        self.vbl2.addWidget(self.wselused)

        self.wselunused = CheckBox(None, "Excluded Ptcls", 1, 100)
        self.vbl2.addWidget(self.wselunused)

        # Mark particles in selected classes as bad
        self.wmarkbut = QtGui.QPushButton("Mark as Bad")
        self.vbl2.addWidget(self.wmarkbut)

        # Mark particles in selected classes as good
        self.wmarkgoodbut = QtGui.QPushButton("Mark as Good")
        self.vbl2.addWidget(self.wmarkgoodbut)

        # Make a new set from selected classes
        self.wmakebut = QtGui.QPushButton("Make New Set")
        self.vbl2.addWidget(self.wmakebut)
        #		self.wmakebut.setEnabled(False)

        # Save list
        self.wsavebut = QtGui.QPushButton("Save Particle List")
        self.vbl2.addWidget(self.wsavebut)

        # Save micrograph dereferenced lists
        self.wsaveorigbut = QtGui.QPushButton("Save CCD-based List")
        self.vbl2.addWidget(self.wsaveorigbut)

        QtCore.QObject.connect(self.wfilesel,
                               QtCore.SIGNAL("itemSelectionChanged()"),
                               self.fileUpdate)
        QtCore.QObject.connect(self.wptclfile,
                               QtCore.SIGNAL("currentIndexChanged(int)"),
                               self.ptclChange)
        QtCore.QObject.connect(self.wselallb, QtCore.SIGNAL("clicked(bool)"),
                               self.selAllClasses)
        QtCore.QObject.connect(self.wselnoneb, QtCore.SIGNAL("clicked(bool)"),
                               self.selNoClasses)
        QtCore.QObject.connect(self.wselrangeb, QtCore.SIGNAL("clicked(bool)"),
                               self.selRangeClasses)
        QtCore.QObject.connect(self.wselinvertb,
                               QtCore.SIGNAL("clicked(bool)"),
                               self.selInvertClasses)
        QtCore.QObject.connect(self.wsel3db, QtCore.SIGNAL("clicked(bool)"),
                               self.sel3DClasses)
        QtCore.QObject.connect(self.wmakebut, QtCore.SIGNAL("clicked(bool)"),
                               self.makeNewSet)
        QtCore.QObject.connect(self.wmarkbut, QtCore.SIGNAL("clicked(bool)"),
                               self.markBadPtcl)
        QtCore.QObject.connect(self.wmarkgoodbut,
                               QtCore.SIGNAL("clicked(bool)"),
                               self.markGoodPtcl)
        QtCore.QObject.connect(self.wsavebut, QtCore.SIGNAL("clicked(bool)"),
                               self.savePtclNum)
        QtCore.QObject.connect(self.wsaveorigbut,
                               QtCore.SIGNAL("clicked(bool)"),
                               self.saveOrigPtclNum)

        # View windows, one for class-averages, one for good particles and one for bad particles
        self.vclasses = None
        self.vgoodptcl = None
        self.vbadptcl = None

        self.updateFiles()

    def makeNewSet(self, x):
        "Makes a new particle set based on the selected class-averages"
        setname = QtGui.QInputDialog.getText(
            None, "Set Name",
            "Please specify the name for the set. If you specify an existing set, new particles will be added to the end"
        )
        if setname[1] == False: return
        else: setname = setname[0]
        if setname[-4:] != ".lst": setname = setname + ".lst"
        if not "/" in setname: setname = "sets/" + setname

        lst = LSXFile(self.curPtclFile())  # lst file for dereferenceing
        lstout = LSXFile(setname)
        include = []
        # iterate over each particle from each marked class-average
        for n in self.curPtclIter(self.wselused.getValue(),
                                  self.wselunused.getValue()):
            try:
                orign, origfile, comment = lst.read(
                    n
                )  # the original file/number dereferenced from the LST file
            except:
                QtGui.QMessageBox.warning(
                    self, "Error !",
                    "The data_source '%s' does not follow EMAN2.1 project conventions. Cannot find raw particles for set."
                    % srcfile)
                return

            include.append((origfile, orign,
                            comment))  # build a list so we can sort by frame

        # write the new set
        for i in sorted(include):
            lstout.write(-1, i[1], i[0], i[2])

    def markBadPtcl(self, x):
        "Mark particles from the selected class-averages as bad in the set interface"

        r = QtGui.QMessageBox.question(
            None, "Are you sure ?",
            "WARNING: There is no undo for this operation. It will  mark all particles associated with the selected class-averages as bad. Are you sure you want to proceed ?",
            QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel)
        if r == QtGui.QMessageBox.Cancel: return

        lst = LSXFile(self.curPtclFile())  # lst file for dereferenceing
        ptcls = {
        }  # dictionary keyed by original frame filename with list of selected particle #s
        # iterate over each particle from each marked class-average
        for n in self.curPtclIter(self.wselused.getValue(),
                                  self.wselunused.getValue()):
            try:
                orign, origfile, comment = lst.read(n)
            except:
                QtGui.QMessageBox.warning(
                    self, "Error !",
                    "The data_source '%s' does not follow EMAN2.1 project conventions. Cannot find raw particles for set."
                    % srcfile)
                return

            try:
                ptcls[origfile].append(
                    orign)  # try to add to a list for an existing filename
            except:
                ptcls[origfile] = [
                    orign
                ]  # creates the list for this filename if it's new

        #now mark the particles as bad
        newbad = 0
        totbad = 0
        for origfile in ptcls:
            js = js_open_dict(
                info_name(origfile))  # get the info dict for this file

            try:
                sets = js["sets"]
            except:
                sets = {"bad_particles": []}
            try:
                badset = set(sets["bad_particles"])
            except:
                badset = set()

            try:
                newset = list(set(ptcls[origfile]) | badset)
                sets[
                    "bad_particles"] = newset  # update the set of bad particles for this image file
                js["sets"] = sets
                totbad += len(badset)
                newbad += len(newset) - len(badset)
            except:
                print "Error setting bad particles in ", origfile

            js_close_dict(info_name(origfile))
        print newbad, " new particles marked as bad. Total of ", totbad, " in affected micrographs"

    def markGoodPtcl(self, x):
        "Mark particles from the selected class-averages as good in the set interface"

        r = QtGui.QMessageBox.question(
            None, "Are you sure ?",
            "WARNING: There is no undo for this operation. It will un-mark all particles associated with the selected class-averages as bad. Are you sure you want to proceed ?",
            QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel)
        if r == QtGui.QMessageBox.Cancel: return

        lst = LSXFile(self.curPtclFile())  # lst file for dereferenceing
        ptcls = {
        }  # dictionary keyed by original frame filename with list of selected particle #s
        # iterate over each particle from each marked class-average
        for n in self.curPtclIter(self.wselused.getValue(),
                                  self.wselunused.getValue()):
            try:
                orign, origfile, comment = lst.read(n)
            except:
                QtGui.QMessageBox.warning(
                    self, "Error !",
                    "The data_source '%s' does not follow EMAN2.1 project conventions. Cannot find raw particles for set."
                    % srcfile)
                return

            try:
                ptcls[origfile].append(
                    orign)  # try to add to a list for an existing filename
            except:
                ptcls[origfile] = [
                    orign
                ]  # creates the list for this filename if it's new

        #now mark the particles as good
        badafter = 0
        badbefore = 0
        for origfile in ptcls:
            js = js_open_dict(
                info_name(origfile))  # get the info dict for this file
            try:
                badset = set(js["sets"]["bad_particles"])
                js["sets"]["bad_particles"] = list(
                    badset - set(ptcls[origfile])
                )  # update the set of bad particles for this image file
            except:
                pass  # since marking as good is the same as removing from the bad list, if there is no bad list, there is nothing to do

            try:
                sets = js["sets"]
            except:
                continue  # if no current bad particles, nothing to mark good
            try:
                badset = sets["bad_particles"]
            except:
                continue

            try:
                newset = list(badset - set(ptcls[origfile]))
                sets[
                    "bad_particles"] = newset  # update the set of bad particles for this image file
                js["sets"] = sets
                badbefore += len(badset)
                badafter += len(newset)
            except:
                continue

        print badbefore, " bad particles before processing, now ", badafter

    def savePtclNum(self, x):
        "Saves a list of particles from marked classes into a text file"

        filename = QtGui.QInputDialog.getText(
            None, "Filename",
            "Please enter a filename for the particle list. The file will contain the particle number (within the particle file) for each particle associated with a selected class-average."
        )
        if filename[1] == False or filename[0] == "": return

        out = file(filename[0], "w")
        for i in self.curPtclIter(self.wselused.getValue(),
                                  self.wselunused.getValue()):
            out.write("%d\n" % i)
        out.close()

    def saveOrigPtclNum(self, x):
        "Saves a file containing micrograph-dereferenced particles"
        filename = QtGui.QInputDialog.getText(
            None, "Filename",
            "Please enter a filename for the particle list. The file will contain particle number and image file, one per line. Image files will be referenced back to the original per-CCD frame stacks."
        )
        if filename[1] == False or filename[0] == "": return

        lst = LSXFile(self.curPtclFile())  # lst file for dereferenceing
        include = []
        # iterate over each particle from each marked class-average
        for n in self.curPtclIter(self.wselused.getValue(),
                                  self.wselunused.getValue()):
            try:
                orign, origfile, comment = lst.read(
                    n
                )  # the original file/number dereferenced from the LST file
            except:
                QtGui.QMessageBox.warning(
                    self, "Error !",
                    "The data_source '%s' does not follow EMAN2.1 project conventions. Cannot find raw particles for set."
                    % srcfile)
                return

            include.append((origfile, orign,
                            comment))  # build a list so we can sort by frame

        # write the output file
        out = file(filename, "w")
        for i in sorted(include):
            out.write("{}\t{}\n".format(i[1], i[0]))
        out = None

    def selAllClasses(self, x):
        "Mark all classes as selected"
        self.vclasses.all_set()

    def selNoClasses(self, x):
        "Clear selection"
        self.vclasses.clear_set()

    def selRangeClasses(self, x):
        "Select a range of images (ask the user for the range)"
        rng = QtGui.QInputDialog.getText(
            None, "Select Range",
            "Enter the range of particle values as first-last (inclusive). Merges with existing selection."
        )
        if rng[1] == False: return

        try:
            x0, x1 = rng[0].split("-")
            x0 = int(x0)
            x1 = int(x1) + 1
        except:
            QtGui.QMessageBox.warning(self, "Error !",
                                      "Invalid range specified. Use: min-max")
            return

        self.vclasses.subset_set(range(x0, x1))

    def selInvertClasses(self, x):
        "Inverts the current selection set"
        self.vclasses.invert_set()

    def sel3DClasses(self, x):
        "Select a range of images based on those used in a 3-D reconstruction associated with this classes file. Removes current selection first."

        f = self.curFile()
        if not '#classes_' in f:
            QtGui.QMessageBox.warning(
                self, "Error !",
                "A classes_xx file from a refine_xx directory is not currently selected"
            )
            return

        # construct the path to the threed_xx file
        num = f.split("_")[-1]
        pre = f.split("#")[0]
        d3path = "%s#threed_%s" % (pre, num)
        try:
            a = EMData(d3path, 0, True)
            goodptcl = a["threed_ptcl_idxs"]
        except:
            QtGui.QMessageBox.warning(self, "Error !",
                                      "Cannot read classes from " + d3path)
            return

        self.vclasses.clear_set()
        self.vclasses.subset_set(goodptcl)

    def ptclChange(self, value):
        "Called when a change of particle data file occurs to zero out the display"
        try:
            self.vgoodptcl.set_data(None)
            self.vbadptcl.set_data(None)
        except:
            pass

    def updateFiles(self):
        "Updates the list of classes files"
        subdir = sorted([
            i for i in os.listdir(e2getcwd())
            if "r2d_" in i or "relion2d_" in i or "refine_" in i
            or "multi_" in i or "multinoali_" in i
        ])
        for d in subdir:
            try:
                dbs = os.listdir(d)
            except:
                continue
            dbs.sort()
            for db in dbs:
                if "classes_" in db or "allrefs_" in db:
                    self.wfilesel.addItem("%s/%s" % (d, db))

        for f in self.extrafiles:
            self.wfilesel.addItem(f)

        dbs = os.listdir("sets")
        dbs.sort()
        for db in dbs:
            self.wptclfile.addItem("sets/" + db)

    def curPtclIter(self, included=True, excluded=True):
        "This is a generator function which yields n (in self.curPtclFile()) for all particles associated with selected classes"
        for ci in self.curSet():
            try:
                c = EMData(self.curFile(), ci,
                           True)  # read header for current class average
                if included:
                    incl = c["class_ptcl_idxs"]
                    if isinstance(incl, int):
                        incl = [
                            incl
                        ]  # This should not happen, but seems to sometimes for some reason
                    for i in incl:
                        yield (i)
                if excluded and c.has_attr("exc_class_ptcl_idxs"):
                    excl = c["exc_class_ptcl_idxs"]
                    if isinstance(excl, int):
                        excl = [
                            excl
                        ]  # This should not happen, but seems to sometimes for some reason
                    for i in excl:
                        yield (i)
            except:
                print "Problem with class %d (%s). Skipping" % (ci,
                                                                self.curFile())
                traceback.print_exc()
                continue

    def curFile(self):
        "return the currently selected file as a readable path"
        return str(self.wfilesel.item(self.wfilesel.currentRow()).text()
                   )  # text of the currently selected item

    def curSet(self):
        "return a set (integers) of the currently selected class-averages"

        return self.vclasses.get_set("evalptcl")

    def curPtclFile(self):
        "return the particle file associated with the currently selected classes file"
        return str(self.wptclfile.currentText()
                   )  # text of the currently selected item

    def fileUpdate(self):
        "Called when the user selects a file from the list or need to completely refresh display"

        QtGui.qApp.setOverrideCursor(Qt.BusyCursor)

        if self.vclasses == None:
            self.vclasses = EMImageMXWidget()
            self.vclasses.set_mouse_mode("App")
            QtCore.QObject.connect(self.vclasses,
                                   QtCore.SIGNAL("mx_image_selected"),
                                   self.classSelect)
            QtCore.QObject.connect(self.vclasses,
                                   QtCore.SIGNAL("mx_image_double"),
                                   self.classDouble)

        self.vclasses.set_title("Classes")

        #		self.classes=EMData.read_images(self.curFile())
        self.vclasses.set_data(self.curFile(), self.curFile())
        #		self.vclasses.set_single_active_set("selected")		# This makes the 'set' representing the selected class-averages current
        self.vclasses.set_mouse_mode("App")
        self.vclasses.enable_set("evalptcl", [])

        # This makes sure the particle file is in the list of choices and is selected
        try:
            ptclfile = EMData(self.curFile(), 0, True)["class_ptcl_src"]
            i = self.wptclfile.findText(ptclfile)
            if i == -1:
                self.wptclfile.insertItem(0, ptclfile)
                self.wptclfile.setCurrentIndex(0)
            else:
                self.wptclfile.setCurrentIndex(i)
        except:
            QtGui.QMessageBox.warning(
                self, "Error !",
                "This image does not appear to be a class average. (No class_ptcl_src, etc.)"
            )
            ptclfile = "None"

        # Make sure our display widgets exist
        if self.vgoodptcl == None:
            self.vgoodptcl = EMImageMXWidget()
        self.vgoodptcl.set_title("Included Particles")

        if self.vbadptcl == None:
            self.vbadptcl = EMImageMXWidget()
        self.vbadptcl.set_title("Excluded Particles")

        self.vclasses.show()
        self.vgoodptcl.show()
        self.vbadptcl.show()

        QtGui.qApp.setOverrideCursor(Qt.ArrowCursor)

    def classSelect(self, event, lc):
        "Single clicked class particle. lc=(img#,x,y,image_dict)"

        QtGui.qApp.setOverrideCursor(Qt.BusyCursor)
        ptclfile = self.curPtclFile()
        try:
            ptclgood = lc[3]["class_ptcl_idxs"]
            self.vgoodptcl.set_data(EMData.read_images(ptclfile, ptclgood))
        except:
            QtGui.QMessageBox.warning(
                self, "Error !",
                "This image does not appear to be a class average. (No class_ptcl_src, etc.)"
            )
            QtGui.qApp.setOverrideCursor(Qt.ArrowCursor)
            return
        try:
            ptclbad = lc[3]["exc_class_ptcl_idxs"]
            self.vbadptcl.set_data(EMData.read_images(ptclfile, ptclbad))
        except:
            ptclbad = []
            self.vbadptcl.set_data(None)

        self.vgoodptcl.show()
        self.vbadptcl.show()
        QtGui.qApp.setOverrideCursor(Qt.ArrowCursor)

    def classDouble(self, event, lc):
        self.vclasses.image_set_associate(lc[0], update_gl=True)

    def closeEvent(self, event):
        try:
            self.vclasses.commit_sets()
            self.vclasses.close()
        except:
            pass
        try:
            self.vgoodptcl.close()
        except:
            pass
        try:
            self.vbadptcl.close()
        except:
            pass

        QtGui.QWidget.closeEvent(self, event)
Beispiel #5
0
class EMEulerExplorer(EM3DSymModel,Animator):

	def mousePressEvent(self,event):
		if self.events_mode == "inspect":
			self.current_hit = self.get_hit(event)
			if self.current_hit == None:
				EM3DSymModel.mousePressEvent(self,event)
		else:
			EM3DSymModel.mousePressEvent(self,event)

	def mouseReleaseEvent(self,event):
		if self.events_mode == "inspect":
			if self.current_hit != None:
				self.updateGL() # there needs to be a clear or something  in order for the picking to work. This is  bit of hack but our rendering function doesn't take long anyhow
				hit = self.get_hit(event)
				if hit == self.current_hit:
					self.emit(QtCore.SIGNAL("point_selected"),self.current_hit,event)
			else:
				#EM3DSymModel.mouseReleaseEvent(self,event)
				EM3DModel.mouseReleaseEvent(self, event) #behavior in EM3DSymModel is not what we want (needed in sibling classes?)

			self.current_hit = None
		else:
				#EM3DSymModel.mouseReleaseEvent(self,event)
				EM3DModel.mouseReleaseEvent(self, event) #behavior in EM3DSymModel is not what we want (needed in sibling classes?)


	def mouseMoveEvent(self,event):
		if self.events_mode == "inspect" and self.current_hit:
			pass
		else:
			EM3DSymModel.mouseMoveEvent(self,event)

	def get_hit(self,event):
		v = self.vdtools.wview.tolist()
		self.get_gl_widget().makeCurrent() # prevents a stack underflow
#		x = event.x()
#		y = v[-1]-event.y()
#		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT )
#		vals = self.render(color_picking=True)
#		glFlush()
#		vv = glReadPixels(x,y,1,1,GL_RGB,GL_FLOAT)
#		reslt = Vec3f(float(vv[0][0][0]),float(vv[0][0][1]),float(vv[0][0][2]))
#		for i,val in enumerate(vals):
##			print val,reslt,(reslt-val).length(),vv[0][0]
#			if (reslt-val).length() < 0.01:
#				print i
##				print (reslt-val).length()
#				return i
#		print vv
#
		# the problem with this approach is that depth testing is not part of picking
		sb = [0 for i in xrange(0,512)]
		glSelectBuffer(512)
		glRenderMode(GL_SELECT)
		glInitNames()
		glMatrixMode(GL_PROJECTION)
		glPushMatrix()
		glLoadIdentity()
		gluPickMatrix(event.x(),v[-1]-event.y(),5,5,v)
		self.get_gl_widget().load_perspective()
		glMatrixMode(GL_MODELVIEW)
		glInitNames()
		self.render()
		glMatrixMode(GL_PROJECTION)
		glPopMatrix()
		glMatrixMode(GL_MODELVIEW)
		glFlush()

		intersection = None
		hits = list(glRenderMode(GL_RENDER))
		for hit in hits:
			a,b,c=hit
			if len(c) > 0:
				intersection = c[0]-1
				break

		return intersection


	def keyPressEvent(self,event):

		if event.key() == Qt.Key_F1:
			self.display_web_help("http://blake.bcm.edu/emanwiki/EMAN2/Programs/e2eulerxplor")
		elif event.key() == Qt.Key_F :
			if self.flatten>0 : self.flatten=0.0
			else: self.flatten=1.0
			self.generate_current_display_list(True)
			self.updateGL()
		else:
			EM3DSymModel.keyPressEvent(self,event)

	def __init__(self, gl_widget=None, auto=True,sparse_mode=False, file_name = "", read_from=None):
		self.current_hit = None
		self.events_mode_list = ["navigate", "inspect"]
		self.events_mode = self.events_mode_list[1]

		self.init_lock = True # a lock indicated that we are still in the __init__ function
		self.au_data = None # This will be a dictionary, keys will be refinement directories, values will be something like available iterations for visual study
		if len(read_from)==0:
			read_from=None
		self.readfrom=read_from
		if self.readfrom:
			
			file_name=""
			auto=False
			datalst=[]
			for rr in self.readfrom:
				datalst.append([rr,None, None, None,rr])
			self.au_data={"data":datalst}

		if auto: # this a flag that tells the eulerxplorer to search for refinement data and automatically add elements to the inspector, if so
			self.gen_refinement_data()
		
		EM3DSymModel.__init__(self, gl_widget, eulerfilename=file_name)
		Animator.__init__(self)
		self.height_scale = 8.0 # This is a value used in EM3DSymModel which scales the height of the displayed cylinders - I made it 8 because it seemed fine. The user can change it anyhow
		self.projection_file = None  # This is a string - the name of the projection images file
		self.average_file = None # This is a string - the name of the class averages file
		self.proj_class_viewer = None # This will be an EMImageMXWidget that shows the class and/or projection
		self.particle_viewer = None  # This will be an EMImageMXWidget that shows the particles in a class
		self.clsdb = None # I think this will become redundant - it used to be the old database that stores which particles are in a class, but now that's stored in the header
		self.particle_file = None # This will be a string - the name of the file that has the particle files in it. This might be made redundant with the new approach
		self.alignment_file = None # This will be a string - the name of the file containing the alignment parameters - this is essential if you we want to show the aligned particles
		self.refine_dir = None # This will be a string - the name of the current refinement directory that is being studied
		self.dx = None # This is an EMData object storing the x shifts of the alignments for all particles. Generated by e2classaverage
		self.dy = None # This is an EMData object storing the y shifts of the alignments for all particles. Generated by e2classaverage
		self.da = None# This is an EMData object storing the angle of the alignments for all particles. Generated by e2classaverage
		self.dflip = None # This is an EMData object storing whether or not tthe alignment involved a flip, for all particles. Generated by e2classaverage
		self.classes = None # This is an EMData object storing which class(es) a particle belongs to. Generated by e2classaverage
		self.inclusions = None # This is and EMDAta storing a boolean that indicates the particle was actually included in the final average. Generated by e2classaverage

		self.average = None # This the class average itself, an EMData object
		self.projection = None # This is the projection itelse, an EMData object
		self.class_idx = None # This is the idx of the current class being studied in the interface

		self.previous_len = -1 # To keep track of the number of class averages that were previously viewable. This helps to make sure we can switch to the same class average in the context of a different refinement iteration
		self.mirror_eulers = False
		if sparse_mode:
			self.mirror_eulers = True # If True the drawn Eulers are are also rendered on the opposite side of the sphere - see EM3DSymModel.make_sym_dl_lis

		# Grab the symmetry from the workflow database if possible
		sym = "c1"
		if js_check_dict("refine_01/0_refine_parms.json"):
			try: sym = str(js_open_dict("refine_01/0_refine_parms.json")["sym"])
			except: pass

		# Have to tell the EM3DSymModel that there is a new sym
		self.set_symmetry(sym)

		# this object will have
		if self.au_data != None:
			combo_entries = self.au_data.keys()
			combo_entries.sort()
			combo_entries.reverse()

			if len(combo_entries) > 0:
				au = combo_entries[0]
				cls = self.au_data[au][0][0]
				self.au_selected(au,cls)
				self.mirror_eulers = True

		self.init_lock = False
		self.force_update=True # Force a display udpdate in EMImage3DSymModule

		QtCore.QObject.connect(self, QtCore.SIGNAL("point_selected"), self.au_point_selected)

	def __del__(self):
		EM3DSymModel.__del__(self) # this is here for documentation purposes - beware that the del function is important

	def initializeGL(self):
		glEnable(GL_NORMALIZE)

	def generate_current_display_list(self,force=False):
		'''
		Redefinition of EMImage3DSymModule.generate_current_display_list

		'''
		if self.init_lock: return 0
		if self.au_data == None or len(self.au_data) == 0:
			EM3DSymModel.generate_current_display_list(self,force)

		self.init_basic_shapes()
		if self.nomirror == True : val = 0
		else: val = 1
		self.trace_great_arcs(self.sym_object.get_asym_unit_points(val))
		self.trace_great_triangles(val)

		self.eulers = self.specified_eulers
		if self.eulers == None:	return 0

#		if not self.colors_specified: self.point_colors = []
#		else: self.point_colors = self.specified_colors
#		self.points = []
#		for i in self.eulers:
#			p = i.transpose()*Vec3f(0,0,self.radius)
#			self.points.append(p)
#			if not self.colors_specified: self.point_colors.append((0.34615, 0.3143, 0.0903,1))

		self.make_sym_dl_list(self.eulers)
		return 1
	def get_data_dims(self):
		return (2*self.radius,2*self.radius,2*self.radius)

	def width(self): return 2*self.radius
	def height(self): return 2*self.radius
	def depth(self): return 2*self.radius

	def gen_refinement_data(self):
		dirs,files = get_files_and_directories()

		dirs.sort()
		for i in range(len(dirs)-1,-1,-1):
			if dirs[i][:7] != "refine_" and dirs[i][:6]!="multi_" and dirs[i][:11]!="multinoali_":
				dirs.pop(i)
			else:
				try: int(dirs[i][7:])
				except: dirs.pop(i)

		self.dirs = dirs
		print(dirs)

		self.au_data = {}
		for dir in self.dirs:
			d = self.check_refine_db_dir(dir)
			if len(d) != 0 and len(d[dir]) != 0: self.au_data.update(d)

	def check_refine_db_dir(self,dir,s1="classes",s2=None,s3="cls_result",s4="threed",s5="projections"):
		# s2 used to be class_indices
		names = [s1,s2,s3,s4,s5]
		data = {}
		data[dir] = []
		register_js_name = "{}/0_refine_parms.json".format(dir)

		files=os.listdir(dir)
		
		try:
			nums=[int(i[7:9]) for i in files if "threed" in i and "even" not in i and "odd" not in i]
			maxnum=max(nums)
		except :
			print("Nothing in ",dir)
			return {}

		for i in xrange(1,maxnum+1):
			exte="_{:02d}_even.hdf".format(i)
			exto="_{:02d}_odd.hdf".format(i)
			data[dir].append([sadd(dir,s1,exte),sadd(dir,s2,exte),sadd(dir,s3,exte),sadd(dir,s4,exte),sadd(dir,s5,exte)])
			data[dir].append([sadd(dir,s1,exto),sadd(dir,s2,exto),sadd(dir,s3,exto),sadd(dir,s4,exto),sadd(dir,s5,exto)])

		return data

	def set_projection_file(self,projection_file): self.projection_file = projection_file
	def get_inspector(self):
		if not self.inspector :
			if (self.au_data == None or len(self.au_data) == 0) and self.mirror_eulers == False: #self.mirror_eulers thing is a little bit of a hack, it's tied to the sparse_mode flag in the init function, which is used by euler_display in EMAN2.py
				self.inspector=EMAsymmetricUnitInspector(self,True,True)
			else:
				self.inspector=EMAsymmetricUnitInspector(self)
			QtCore.QObject.connect(self.inspector,QtCore.SIGNAL("au_selected"),self.au_selected)
		return self.inspector


	def au_selected(self,refine_dir,cls):
		self.refine_dir = refine_dir
		get_application().setOverrideCursor(Qt.BusyCursor)
		data = []
		for d in self.au_data[refine_dir]:
			if d[0] == cls:
				data = d;
				break

		if len(data) == 0:
			error("error, no data for %s %s, returning" %(refine_dir,cls))
#			print "error, no data for",au,cls,"returning"
			self.events_handlers["inspect"].reset()
			get_application().setOverrideCursor(Qt.ArrowCursor)
			return
		
		if not self.readfrom:
			try :
				self.particle_file=js_open_dict(refine_dir+"/0_refine_parms.json")["input"]
			except:
				error("No data in "+refine_dir )
				self.events_handlers["inspect"].reset()
				get_application().setOverrideCursor(Qt.ArrowCursor)
				return

		self.average_file = cls
		self.projection_file = data[4]
		self.alignment_file = data[2]
		self.clsdb = data[1]

		self.dx = None
		self.dy = None
		self.da = None
		self.dflip = None
		self.classes = None

		eulers = get_eulers_from(self.average_file)
		#s = Symmetries.get("d7")
		#eulers = s.gen_orientations("rand",{"n":EMUtil.get_image_count(self.average_file)})

		self.specify_eulers(eulers)
		#from emimagemx import EMDataListCache
		#a = EMData.read_images(self.average_file)
		#a = [test_image() for i in range(EMUtil.get_image_count(self.average_file))]
		#print len(a),len(eulers)
		#b = [a[i].set_attr("xform.projection",eulers[i]) for i in range(len(eulers))]
		#b = [a[i].set_attr("ptcl_repr",1) for i in range(len(eulers))]

		self.set_emdata_list_as_data(EMLightWeightParticleCache.from_file(self.average_file),"ptcl_repr")
		#self.set_emdata_list_as_data(EMDataListCache(self.average_file),"ptcl_repr")
#		self.set_emdata_list_as_data(a,"ptcl_repr")
		self.force_update = True
		self.au_point_selected(self.class_idx,None)
		# if we have the same number of Eulers we can update everything
#		if self.previous_len == len(eulers) : self.events_handlers["inspect"].repeat_event()
#		else:self.events_handlers["inspect"].reset()
		self.previous_len = len(eulers)
		if not self.init_lock:self.updateGL()
		get_application().setOverrideCursor(Qt.ArrowCursor)

	def __get_file_headers(self,filename):
		headers = []
		n = EMUtil.get_image_count(filename)
		for i in range(n):
			e = EMData()
			e.read_image(filename,i,True)
			headers.append(e)
		return headers

	def au_point_selected(self,i,event=None):
		if self.readfrom:
			return
		if i == None:
			if event != None and event.modifiers()&Qt.ShiftModifier:
				if self.special_euler != None:
					self.special_euler = None
					if not self.init_lock:self.regen_dl()
			return
#		self.arc_anim_points = None
		self.projection = None
		if self.euler_data:
#			db = db_open_dict(self.average_file)
#			a = db.get(i)
#			print a["nx"]
#			print self.average_file,i
#			self.average = EMData(self.average_file,i)
#			self.average["nx"]
			self.average = self.euler_data[i]#
			self.projection = EMData(self.projection_file,self.average.get_attr("projection_image_idx"))
			self.average.process_inplace("normalize.toimage",{"to":self.projection})
			try:
				self.class_idx = self.average.get_attr("projection_image_idx")
				print("%d (%d)"%(self.class_idx,self.average["ptcl_repr"]))
			except:
				self.class_idx = -1
		else: return

		#if self.projection  == None and self.average == None: return
		first = False
		if self.proj_class_viewer == None:
			first = True
			self.proj_class_viewer = EMImageMXWidget(data=None,application=get_application())
#			self.proj_class_viewer = EMImage2DWidget(image=None,application=get_application())
			QtCore.QObject.connect(self.proj_class_viewer,QtCore.SIGNAL("module_closed"),self.on_mx_view_closed)
#			self.proj_class_viewer.set_mouse_mode("App" )
			QtCore.QObject.connect(self.proj_class_viewer,QtCore.SIGNAL("mx_image_selected"), self.mx_image_selected)
			get_application().show_specific(self.proj_class_viewer)

			self.proj_class_single = EMImage2DWidget(image=None,application=get_application())
			QtCore.QObject.connect(self.proj_class_single,QtCore.SIGNAL("module_closed"),self.on_mx_view_closed)
#			QtCore.QObject.connect(self.proj_class_single,QtCore.SIGNAL("mx_image_selected"), self.mx_image_selected)
			get_application().show_specific(self.proj_class_single)

		disp = []
		if self.projection != None: disp.append(self.projection)
		if self.average != None and self.projection!=None:
			# ok, this really should be put into its own processor
			#dataf = self.projection.do_fft()
			#apix=self.projection["apix_x"]
			#curve = dataf.calc_radial_dist(dataf["ny"], 0, 0.5,True)
			#curve=[i/(dataf["nx"]*dataf["ny"])**2 for i in curve]
			#xcurve=[i/(apix*2.0*dataf["ny"]) for i in range(len(curve))]
			#xyd=XYData()
			#xyd.set_xy_list(xcurve,curve)
			#filt=self.average.process("filter.setstrucfac",{"apix":apix,"strucfac":xyd})
			#filt.process_inplace("normalize.toimage",{"to":self.average})
			self.projection["apix_x"]=self.average["apix_x"]
			self.projection["apix_y"]=self.average["apix_y"]
			self.projection["apix_z"]=self.average["apix_z"]
			filt=self.projection.process("threshold.notzero")
			filt.mult(self.average)
			filt.process_inplace("filter.matchto",{"to":self.projection})

			disp.append(filt)

		if self.average!=None:
			disp.append(self.average)

		self.proj_class_viewer.set_data(disp)
		self.proj_class_single.set_data(disp)

		self.proj_class_viewer.updateGL()
		self.proj_class_single.updateGL()
		if self.particle_viewer != None:
			self.mx_image_selected(None,None)
		if first: self.proj_class_viewer.optimally_resize()

		if i != self.special_euler:
			self.special_euler = i
			self.force_update = True

		if not self.init_lock: self.updateGL()


	def on_mx_view_closed(self):
		self.proj_class_viewer = None
		self.proj_class_single = None

	def on_particle_mx_view_closed(self):
		self.particle_viewer = None

	def animation_done_event(self,animation):
		pass

	def alignment_time_animation(self,transforms):
		if len(transforms) < 2: return
		animation = OrientationListAnimation(self,transforms,self.radius)
		self.register_animatable(animation)

	def particle_selected(self,event,lc):
		if lc != None:
			d = lc[3]
			ptcl_idx = d["Img #"]
			data = self.au_data[self.refine_dir]
			prj = []
			cls_result = []
			for l in data:
				for s in l:
					stag = base_name(s)

					if len(stag) > 11 and stag[:11] == "projections":
						prj.append(s)
					elif len(stag) > 10 and stag[:10] == "cls_result":
						cls_result.append(s)

			transforms = []
			if len(prj) != len(cls_result): RunTimeError("The number of cls_result files does not match the number of projection files?")

			e = EMData()
			for i,cr in enumerate(cls_result):
				r = Region(0,ptcl_idx,1,1)
				e.read_image(cr,0,False,r)
				p = int(e.get(0))
				e.read_image(prj[i],p,True)
				transforms.append(e["xform.projection"])

			self.alignment_time_animation(transforms)

	def mx_image_selected(self,event,lc):
#		self.arc_anim_points = None
		get_application().setOverrideCursor(Qt.BusyCursor)
		if lc != None: self.sel = lc[0]

		if self.average != None:
			included = []
			if self.average.has_attr("class_ptcl_idxs"):
				included = self.average["class_ptcl_idxs"]
			excluded = []
			if self.average.has_attr("exc_class_ptcl_idxs"):
				excluded = self.average["exc_class_ptcl_idxs"]

			all = included + excluded
			#all.sort()

			bdata = []
			data = []
			idx_included = []
			running_idx = 0
			from emimagemx import ApplyAttribute
			for val in included:
				bdata.append([self.particle_file,val,[ApplyAttribute("Img #",val)]])
				idx_included.append(running_idx)
				running_idx += 1

			idx_excluded = []
			for val in excluded:
				bdata.append([self.particle_file,val,[ApplyAttribute("Img #",val)]])
				idx_excluded.append(running_idx)
				running_idx += 1

			data = EMLightWeightParticleCache(bdata)

			first = False
			if self.particle_viewer == None:
				first = True
				self.particle_viewer = EMImageMXWidget(data=None,application=get_application())
				self.particle_viewer.set_mouse_mode("App" )
				QtCore.QObject.connect(self.particle_viewer,QtCore.SIGNAL("module_closed"),self.on_particle_mx_view_closed)
				QtCore.QObject.connect(self.particle_viewer,QtCore.SIGNAL("mx_image_selected"), self.particle_selected)
				get_application().show_specific(self.particle_viewer)


			self.check_images_in_memory()

			if self.sel== 0 or self.alignment_file == None:
				self.particle_viewer.set_data(data)
			else:

				for i,[name,idx,f] in enumerate(bdata):
					index = -1
					if self.classes.get_xsize() == 1:
						index = 0 # just assume it's the first one - this is potentially fatal assumption, but in obscure situations only
					else:
						for j in range(self.classes.get_xsize()):
							if int(self.classes.get(j,idx)) == self.class_idx:
								index = j
								break
					if index == -1:
						print("couldn't find")
						get_application().setOverrideCursor(Qt.ArrowCursor)
						return

					x = self.dx.get(index,idx)
					y = self.dy.get(index,idx)
					a = self.da.get(index,idx)
					m = self.dflip.get(index,idx)

					t = Transform({"type":"2d","alpha":a,"mirror":int(m)})
					t.set_trans(x,y)
					from emimagemx import ApplyTransform
					f.append(ApplyTransform(t))
					#data[i].transform(t)
				self.particle_viewer.set_data(data)


			if first:
				self.particle_viewer.updateGL()
				self.particle_viewer.optimally_resize()

			self.particle_viewer.clear_sets(False)
			self.particle_viewer.enable_set("Excluded",idx_excluded,True,False)
			self.particle_viewer.enable_set("Included",idx_included,False,False)
			self.particle_viewer.updateGL()

			get_application().setOverrideCursor(Qt.ArrowCursor)

			self.updateGL()

	def check_images_in_memory(self):
		if self.alignment_file != None:
			if self.dx == None:
				self.dx = EMData(self.alignment_file,2)
			if self.dy == None:
				self.dy = EMData(self.alignment_file,3)
			if self.da == None:
				self.da = EMData(self.alignment_file,4)
			if self.dflip == None:
				self.dflip  = EMData(self.alignment_file,5)
			if self.classes == None:
				self.classes  = EMData(self.alignment_file,0)
			if self.inclusions == None:
				self.inclusions  = EMData(self.alignment_file,1)


	def set_events_mode(self,mode):
		if not mode in self.events_mode_list:
			print("error, unknown events mode", mode)
			return

		else:
			self.events_mode = mode

	def closeEvent(self,event):
		if self.inspector !=None: self.inspector.close()
		if self.proj_class_viewer !=None: self.proj_class_viewer.close()
		if self.proj_class_single !=None: self.proj_class_single.close()
		if self.particle_viewer != None: self.particle_viewer.close()
		get_application().close_specific(self)
		self.emit(QtCore.SIGNAL("module_closed")) # this signal is
Beispiel #6
0
	def __new__(cls,filename,application,force_plot=False,force_2d=False,old=None):
		
		file_type = Util.get_filename_ext(filename)
		em_file_type = EMUtil.get_image_ext_type(file_type)
		if not file_exists(filename): return None
		
		if force_plot and force_2d:
			# ok this sucks but it suffices for the time being
			print "Error, the force_plot and force_2d options are mutually exclusive"
			return None
		
		if force_plot:
			from emplot2d import EMPlot2DWidget
			if isinstance(old,EMPlot2DWidget): widget = old
			else: widget = EMPlot2DWidget(application=application)
			widget.set_data_from_file(filename)
			return widget
		
		if em_file_type != IMAGE_UNKNOWN or filename[:4] == "bdb:":
			n = EMUtil.get_image_count(filename)
			nx,ny,nz = gimme_image_dimensions3D(filename)
			if n > 1 and nz == 1: 
				if force_2d:
					a = EMData()
					data=a.read_images(filename)
				else:
					data = None # This is like a flag - the ImageMXWidget only needs the file name
			elif nz == 1:
				data = [EMData(filename,0)]
			else:
				data = EMData()
				data.read_image(filename,0,not force_2d)		# This should be 3-D. We read the header-only here
				data = [data]
				
			if data != None and len(data) == 1: data = data[0]
			
			if force_2d or isinstance(data,EMData) and data.get_zsize()==1:
				if isinstance(data,list) or data.get_ysize() != 1:
					from emimage2d import EMImage2DWidget
					if isinstance(old,EMImage2DWidget): widget = old
					else: widget= EMImage2DWidget(application=application)
				else:
					from emplot2d import EMPlot2DWidget
					if isinstance(old,EMPlot2DWidget): widget = old
					else: widget = EMPlot2DWidget(application=application)
					widget.set_data_from_file(filename)
					return widget
			elif isinstance(data,EMData):
				if isinstance(old,EMScene3D): widget = old
				else: widget = EMScene3D()
#				print n,data
				for ii in xrange(n):
					data=EMData(filename,ii)
					datai = EMDataItem3D(data, transform=Transform())
					widget.insertNewNode(os.path.basename(filename), datai, parentnode=widget)
					isosurface = EMIsosurface(datai, transform=Transform())
					widget.insertNewNode("Iso", isosurface, parentnode=datai)
				return widget
				
			elif data == None or isinstance(data,list):
				from emimagemx import EMImageMXWidget
				if isinstance(old,EMImageMXWidget): widget = old
				else: widget = EMImageMXWidget(application=application)
				data = filename
			else: 
				print filename
				raise # weirdness, this should never happen
			widget.set_data(data,filename)
			return widget
		else:
			from emplot2d import EMPlot2DWidget
			if isinstance(old,EMPlot2DWidget): widget = old
			else: widget = EMPlot2DWidget(application=application)
			widget.set_data_from_file(filename)
			return widget
Beispiel #7
0
class ParticlesWindow:
	def __init__(self, rctwidget):
		self.rctwidget = rctwidget
		self.window=EMImageMXWidget(application=self.rctwidget.parent_window)
		self.window.set_display_values(["tilt","PImg#"])
		self.window.set_mouse_mode("App")
		self.window.setWindowTitle("Particles")
		self.window.optimally_resize()

		self.connect_signals()
		self.listsofparts = []
		self.numlists = 0
		self.closed = False
	
	def addlist(self, name):
		data = []
		data.append(name)
		data.append(0)
		data.append([])
		self.listsofparts.append(data)
		self.numlists = len(self.listsofparts)
		
	def update_particles(self, particles, idx):
		#print self.listsofparts[idx][0]
		# reset the relevent list of particles
		self.listsofparts[idx][1] = len(particles)
		self.listsofparts[idx][2] = particles

		# get the number of lists and the minimum number of particles in a given list..
		listlength = 1e308	
		for lst in self.listsofparts:
			listlength = min(listlength, lst[1])
		
		i = 0
		self.totparts = []
		for part in xrange(listlength):	
			for lst in xrange(self.numlists):
				self.listsofparts[lst][2][part].set_attr("tilt", self.listsofparts[lst][0])
				self.listsofparts[lst][2][part].set_attr("PImg#", part)
				self.totparts.append(self.listsofparts[lst][2][part])
				i += 1	
				
		if self.totparts != []:
			self.window.set_data(self.totparts)
			self.window.updateGL()
			
	def connect_signals(self):
		QtCore.QObject.connect(self.window,QtCore.SIGNAL("mx_image_selected"),self.box_selected)
		QtCore.QObject.connect(self.window,QtCore.SIGNAL("mx_mousedrag"),self.box_moved)
		QtCore.QObject.connect(self.window,QtCore.SIGNAL("mx_mouseup"),self.box_released)
		QtCore.QObject.connect(self.window,QtCore.SIGNAL("mx_boxdeleted"),self.box_image_deleted)
		QtCore.QObject.connect(self.window,QtCore.SIGNAL("module_closed"),self.module_closed)
			
	def box_selected(self,event,lc):
		if lc == None or lc[0] == None: return
		self.moving_box_data = [event.x(),event.y(),lc[0]]
	
	def box_moved(self,event,scale):
		winidx = self.moving_box_data[2] % self.numlists
		ppidx = int(self.moving_box_data[2]/self.numlists)
		if self.moving_box_data:
			dx = 0.2*(event.x() - self.moving_box_data[0])
			dy = 0.2*(self.moving_box_data[1] - event.y())
			self.rctwidget.windowlist[winidx].boxes.move_box(ppidx,dx,dy)
			self.rctwidget.windowlist[winidx].update_mainwin()
			self.rctwidget.windowlist[winidx].update_particles()
		
	def box_released(self, event,lc):
		pass
		
	def box_image_deleted(self,event,lc):
		if lc == None or lc[0] == None: return
		
		#delete all particle pairs
		ppidx = int(lc[0]/self.numlists)
		for i,window in enumerate(self.rctwidget.windowlist):
			window.boxes.remove_box(ppidx,self.rctwidget.boxsize)
			window.update_mainwin()
			window.update_particles()
		
	def module_closed(self):
		E2saveappwin("e2rctboxer","particles",self.window.qt_parent)
		pass
Beispiel #8
0
    def __new__(cls,
                data=None,
                old=None,
                app=None,
                force_2d=False,
                force_plot=False,
                filename="",
                replace=True):
        """This will create a new EMImage* object depending on the type of 'data'. If
		old= is provided, and of the appropriate type, it will be used rather than creating
		a new instance.
		"""

        if isinstance(data, EMData) and data.get_size() == 0:
            raise RuntimeError(
                "Can not display an EMData object that has no pixels")

        from EMAN2 import remove_directories_from_name
        if force_plot and force_2d:
            # ok this sucks but it suffices for the time being
            print(
                "Error, the force_plot and force_2d options are mutually exclusive"
            )
            return None

        if force_plot or (isinstance(data, EMData) and data.get_zsize() == 1
                          and data.get_ysize() == 1):
            from emplot2d import EMPlot2DWidget
            if old:
                if isinstance(old, EMPlot2DWidget):
                    old.set_data(data, remove_directories_from_name(filename),
                                 replace)
                    return old
            widget = EMPlot2DWidget(application=app)
            widget.set_data(data, remove_directories_from_name(filename),
                            replace)
            return widget
        elif force_2d or (isinstance(data, EMData) and data.get_zsize() == 1):
            from emimage2d import EMImage2DWidget
            if old:
                if isinstance(old, EMImage2DWidget):
                    old.set_data(data, filename)
                    return old
            widget = EMImage2DWidget(application=app)
            widget.set_data(data, filename)
            return widget
        elif isinstance(data, EMData):
            if isinstance(old, EMScene3D): widget = old
            else: widget = EMScene3D()
            data = EMDataItem3D(data, transform=Transform())
            #data.setSelectedItem(True)
            isosurface = EMIsosurface(data, transform=Transform())
            widget.insertNewNode(os.path.basename(filename),
                                 data,
                                 parentnode=widget)
            widget.insertNewNode("Iso", isosurface, parentnode=data)
            return widget

        elif isinstance(data, list) and isinstance(data[0], EMData):
            from emimagemx import EMImageMXWidget
            if old:
                if isinstance(old, EMImageMXWidget):
                    old.set_data(data, filename)
                    return old
            widget = EMImageMXWidget(application=app)
            widget.set_data(data, filename)
            return widget
        elif isinstance(data, list):
            from emplot3d import EMPlot3DWidgetNew
            if (isinstance(data[0], list)
                    or isinstance(data[0], tuple)) and len(data) > 2:
                if old:
                    if isinstance(old, EMPlot3DWidgetNew):
                        old.set_data(data,
                                     remove_directories_from_name(filename),
                                     replace)
                        return old

                widget = EMPlot3DWidgetNew()
                widget.set_data(data, remove_directories_from_name(filename),
                                replace)
                return widget
            else:
                from emplot2d import EMPlot2DWidget
                if old:
                    if isinstance(old, EMPlot2DWidget):
                        old.set_data(data,
                                     remove_directories_from_name(filename),
                                     replace)
                        return old
                widget = EMPlot2DWidget(application=app)
                widget.set_data(data, remove_directories_from_name(filename),
                                replace)
                return widget
        else:
            raise Exception(
                "data must be a single EMData object or a list of EMData objects"
            )
Beispiel #9
0
    def __new__(cls,
                filename,
                application,
                force_plot=False,
                force_2d=False,
                old=None):

        file_type = Util.get_filename_ext(filename)
        em_file_type = EMUtil.get_image_ext_type(file_type)
        if not file_exists(filename): return None

        if force_plot and force_2d:
            # ok this sucks but it suffices for the time being
            print(
                "Error, the force_plot and force_2d options are mutually exclusive"
            )
            return None

        if force_plot:
            from emplot2d import EMPlot2DWidget
            if isinstance(old, EMPlot2DWidget): widget = old
            else: widget = EMPlot2DWidget(application=application)
            widget.set_data_from_file(filename)
            return widget

        if em_file_type != IMAGE_UNKNOWN or filename[:4] == "bdb:":
            n = EMUtil.get_image_count(filename)
            nx, ny, nz = gimme_image_dimensions3D(filename)
            if n > 1 and nz == 1:
                if force_2d:
                    a = EMData()
                    data = a.read_images(filename)
                else:
                    data = None  # This is like a flag - the ImageMXWidget only needs the file name
            elif nz == 1:
                data = [EMData(filename, 0)]
            else:
                data = EMData()
                data.read_image(
                    filename, 0, not force_2d
                )  # This should be 3-D. We read the header-only here
                data = [data]

            if data != None and len(data) == 1: data = data[0]

            if force_2d or isinstance(data, EMData) and data.get_zsize() == 1:
                if isinstance(data, list) or data.get_ysize() != 1:
                    from emimage2d import EMImage2DWidget
                    if isinstance(old, EMImage2DWidget): widget = old
                    else: widget = EMImage2DWidget(application=application)
                else:
                    from emplot2d import EMPlot2DWidget
                    if isinstance(old, EMPlot2DWidget): widget = old
                    else: widget = EMPlot2DWidget(application=application)
                    widget.set_data_from_file(filename)
                    return widget
            elif isinstance(data, EMData):
                if isinstance(old, EMScene3D): widget = old
                else: widget = EMScene3D()
                #				print n,data
                for ii in xrange(n):
                    data = EMData(filename, ii)
                    datai = EMDataItem3D(data, transform=Transform())
                    widget.insertNewNode(os.path.basename(filename),
                                         datai,
                                         parentnode=widget)
                    isosurface = EMIsosurface(datai, transform=Transform())
                    widget.insertNewNode("Iso", isosurface, parentnode=datai)
                return widget

            elif data == None or isinstance(data, list):
                from emimagemx import EMImageMXWidget
                if isinstance(old, EMImageMXWidget): widget = old
                else: widget = EMImageMXWidget(application=application)
                data = filename
            else:
                print(filename)
                raise  # weirdness, this should never happen
            widget.set_data(data, filename)
            return widget
        else:
            from emplot2d import EMPlot2DWidget
            if isinstance(old, EMPlot2DWidget): widget = old
            else: widget = EMPlot2DWidget(application=application)
            widget.set_data_from_file(filename)
            return widget
Beispiel #10
0
class GUIBoxer(QtGui.QWidget):
	# Dictionary of autopickers
	# to add a new one, provide name:(Qt_setup_function,picker_execution_function)
	# Qt_setup_function(self,empty_grid_layout)
	# picker_execution_function(self,...

	aboxmodes = [ ("by Ref","auto_ref",boxerByRef), ("Gauss","auto_gauss",boxerGauss) ]
	boxcolors = { "selected":(0.9,0.9,0.9), "manual":(0,0,0), "refgood":(0,0.8,0), "refbad":(0.8,0,0), "unknown":[.4,.4,.1], "auto_ref":(.1,.1,.4), "auto_gauss":(.4,.1,.4) }
	
	def __init__(self,imagenames,voltage=None,apix=None,cs=None,ac=10.0,box=256,ptcl=200):
		"""The 'new' e2boxer interface.
		"""

		QtGui.QWidget.__init__(self,None)
#		self.setWindowIcon(QtGui.QIcon(get_image_directory() + "ctf.png"))

		self.data=None
		self.curfilename = None				# current selected file for boxing
		self.filenames=imagenames			# list of available filenames
		self.micrograph=None
		self.boxes=None

		self.defaultvoltage=voltage
		self.defaultapix=apix
		self.defaultcs=cs
		self.defaultac=ac
		
		self.db = None						# open JSON file for current image

		self.wimage=EMImage2DWidget()
		self.wimage.setWindowTitle("Micrograph")

		self.wparticles=EMImageMXWidget()
		self.wparticles.setWindowTitle("Particles")
		
		self.wrefs=EMImageMXWidget()
		self.wrefs.setWindowTitle("Box Refs")
		
		self.wbadrefs=EMImageMXWidget()
		self.wbadrefs.setWindowTitle("Bad Box Refs")


		#self.wfft=EMImage2DWidget()
		#self.wfft.setWindowTitle("e2evalimage - 2D FFT")

		#self.wplot=EMPlot2DWidget()
		#self.wplot.setWindowTitle("e2evalimage - Plot")

		self.wimage.connect(self.wimage,QtCore.SIGNAL("mousedown"),self.imgmousedown)
		self.wimage.connect(self.wimage,QtCore.SIGNAL("mousedrag"),self.imgmousedrag)
		self.wimage.connect(self.wimage,QtCore.SIGNAL("mouseup")  ,self.imgmouseup)
		self.wparticles.connect(self.wparticles,QtCore.SIGNAL("mousedown"),self.ptclmousedown)
		self.wparticles.connect(self.wparticles,QtCore.SIGNAL("mousedrag"),self.ptclmousedrag)
		self.wparticles.connect(self.wparticles,QtCore.SIGNAL("mouseup")  ,self.ptclmouseup)
		self.wrefs.connect(self.wparticles,QtCore.SIGNAL("mousedown"),self.refmousedown)
		self.wrefs.connect(self.wparticles,QtCore.SIGNAL("mousedrag"),self.refmousedrag)
		self.wrefs.connect(self.wparticles,QtCore.SIGNAL("mouseup")  ,self.refmouseup)

		self.wimage.mmode="app"
		self.wparticles.mmode="app"

		# This object is itself a widget we need to set up
		self.gbl = QtGui.QGridLayout(self)
		self.gbl.setMargin(8)
		self.gbl.setSpacing(6)

		# Micrograph list
		self.setlist=QtGui.QListWidget(self)
		self.setlist.setSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Expanding)
		for i in imagenames:
			self.setlist.addItem(i)
		self.gbl.addWidget(self.setlist,0,0,12,2)

		self.setlist.connect(self.setlist,QtCore.SIGNAL("currentRowChanged(int)"),self.newSet)
		self.setlist.connect(self.setlist,QtCore.SIGNAL("keypress"),self.listKey)
		
		# Mouse Modes
		self.mmode="manual"
		self.boxmm=QtGui.QGroupBox("Mouse Mode",self)
		self.boxmm.setFlat(False)
		self.gbl.addWidget(self.boxmm,0,2,3,2)
		
		self.hbl0=QtGui.QHBoxLayout(self.boxmm)
		
		self.bmmanual=QtGui.QPushButton("Manual")
		self.bmmanual.setToolTip("Manual selection of particles. No impact on autoselection.")
		self.bmmanual.setAutoExclusive(True)
		self.bmmanual.setCheckable(True)
		self.bmmanual.setChecked(True)
		self.hbl0.addWidget(self.bmmanual)
		
		self.bmdel=QtGui.QPushButton("Delete")
		self.bmdel.setToolTip("Delete particles from any mode. Can also shift-click in other mouse modes.")
		self.bmdel.setAutoExclusive(True)
		self.bmdel.setCheckable(True)
		self.hbl0.addWidget(self.bmdel)
		
		self.bmgref=QtGui.QPushButton("Good Refs")
		self.bmgref.setToolTip("Identify some good particles. Available to all autoboxers.")
		self.bmgref.setAutoExclusive(True)
		self.bmgref.setCheckable(True)
		self.hbl0.addWidget(self.bmgref)

		self.bmbref=QtGui.QPushButton("Bad Refs")
		self.bmbref.setToolTip("Identify regions which should not be selected as particles.")
		self.bmbref.setAutoExclusive(True)
		self.bmbref.setCheckable(True)
		self.hbl0.addWidget(self.bmbref)

		QtCore.QObject.connect(self.bmmanual,QtCore.SIGNAL("clicked(bool)"),self.setMouseManual)
		QtCore.QObject.connect(self.bmdel,QtCore.SIGNAL("clicked(bool)"),self.setMouseDel)
		QtCore.QObject.connect(self.bmgref,QtCore.SIGNAL("clicked(bool)"),self.setMouseGoodRef)
		QtCore.QObject.connect(self.bmbref,QtCore.SIGNAL("clicked(bool)"),self.setMouseBadRef)

		# Global parameters
		self.boxparm=QtGui.QGroupBox("Parameters",self)
		self.boxparm.setFlat(False)
		self.gbl.addWidget(self.boxparm,3,2,3,3)
		
		self.gbl1=QtGui.QGridLayout(self.boxparm)
		self.gbl1.setMargin(8)
		self.gbl1.setSpacing(6)
		
		self.vbbsize = ValBox(label="Box Size:",value=box)
		self.gbl1.addWidget(self.vbbsize,0,0)
		
		self.vbbpsize = ValBox(label="Ptcl Size:",value=ptcl)
		self.gbl1.addWidget(self.vbbpsize,0,1)
		
		self.vbbapix = ValBox(label="A/pix:",value=apix)
		self.gbl1.addWidget(self.vbbapix,0,2)
		
		self.vbvoltage = ValBox(label="Voltage:",value=voltage)
		self.gbl1.addWidget(self.vbvoltage,1,0)

		self.vbbac = ValBox(label="% AC:",value=ac)
		self.gbl1.addWidget(self.vbbac,1,1)
		
		self.vbcs = ValBox(label="Cs:",value=cs)
		self.gbl1.addWidget(self.vbcs,1,2)


		# Autoboxing Tabs
		self.autolbl = QtGui.QLabel("Autoboxing Methods:")
		self.gbl.addWidget(self.autolbl,7,2)
		self.autotab = QtGui.QTabWidget()
		self.gbl.addWidget(self.autotab,8,2,6,3)

		self.bautobox = QtGui.QPushButton("Autobox")
		self.gbl.addWidget(self.bautobox,7,4)
		
		QtCore.QObject.connect(self.bautobox,QtCore.SIGNAL("clicked(bool)"),self.doAutoBox)
		
		# Individual tabs from Dictionary
		self.abwid=[]
		for name,bname,cls in GUIBoxer.aboxmodes:
			w=QtGui.QWidget()
			gl=QtGui.QGridLayout(w)
			self.abwid.append((w,gl))
			cls.setup_gui(gl)
			self.autotab.addTab(w,name)
			
		
		self.setWindowTitle("e2boxer21 - Control Panel")

		self.wimage.show()
#		self.wfft.show()
#		self.wplot.show()
		E2loadappwin("e2boxer21","main",self)
		E2loadappwin("e2boxer21","image",self.wimage.qt_parent)
		E2loadappwin("e2boxer21","particles",self.wparticles.qt_parent)
		E2loadappwin("e2boxer21","refs",self.wrefs.qt_parent)
		E2loadappwin("e2boxer21","badrefs",self.wbadrefs.qt_parent)

		self.newSet(0)




	#QWidget *firstPageWidget = new QWidget;
    #QWidget *secondPageWidget = new QWidget;
    #QWidget *thirdPageWidget = new QWidget;

    #QStackedWidget *stackedWidget = new QStackedWidget;
    #stackedWidget->addWidget(firstPageWidget);
    #stackedWidget->addWidget(secondPageWidget);
    #stackedWidget->addWidget(thirdPageWidget);

    #QVBoxLayout *layout = new QVBoxLayout;
    #layout->addWidget(stackedWidget);
    #setLayout(layout);
		
		 #QComboBox *pageComboBox = new QComboBox;
    #pageComboBox->addItem(tr("Page 1"));
    #pageComboBox->addItem(tr("Page 2"));
    #pageComboBox->addItem(tr("Page 3"));
    #connect(pageComboBox, SIGNAL(activated(int)),stackedWidget, SLOT(setCurrentIndex(int)));

		#self.lboxmode=QtGui.QLabel("Mode:",self)
		#self.gbl.addWidget(self.lboxmode,10,0)

		#self.sboxmode=QtGui.QComboBox(self)
		#self.sboxmode.addItem("Manual")
		#self.sboxmode.addItem("Reference")
		#self.sboxmode.setCurrentIndex(1)
		#self.gbl.addWidget(self.sboxmode,10,1)

		#self.lanmode=QtGui.QLabel("Annotate:",self)
		#self.gbl.addWidget(self.lanmode,12,0)

		#self.sanmode=QtGui.QComboBox(self)
		#self.sanmode.addItem("Box")
		#self.sanmode.addItem("Box+dot")
		#self.sanmode.addItem("Circle")
		#self.sanmode.addItem("None")
		#self.gbl.addWidget(self.sanmode,12,1)

		#self.sdefocus=ValSlider(self,(0,5),"Defocus:",0.0,90)
		#self.gbl.addWidget(self.sdefocus,0,2,1,3)

		#self.squality=ValSlider(self,(0,9),"Quality (0-9):",0,90)
		#self.squality.setIntonly(True)

		#self.gbl.addWidget(self.squality,6,2,1,3)

		#self.brefit=QtGui.QPushButton("Autobox")
		#self.gbl.addWidget(self.brefit,7,2)

		#self.bclrauto=QtGui.QPushButton("Clear Auto")
		#self.gbl.addWidget(self.bclrauto,7,3)

		#self.bclrall=QtGui.QPushButton("Clear All")
		#self.gbl.addWidget(self.bclrall,7,4)

		#self.sapix=ValBox(self,(0,500),"A/pix:",1.0,90)
		#if self.defaultapix!=None : self.sapix.setValue(self.defaultapix)
		#self.gbl.addWidget(self.sapix,10,2)

		#self.svoltage=ValBox(self,(0,500),"Voltage (kV):",200,90)
		#if self.defaultvoltage!=None : self.svoltage.setValue(self.defaultvoltage)
		#self.gbl.addWidget(self.svoltage,11,2)

		#self.scs=ValBox(self,(0,5),"Cs (mm):",4.1,90)
		#if self.defaultcs!=None : self.scs.setValue(self.defaultcs)
		#self.gbl.addWidget(self.scs,12,2)

		#self.sboxsize=ValBox(self,(0,500),"Box Size:",256,90)
		#self.sboxsize.intonly=True
		#self.gbl.addWidget(self.sboxsize,13,2)

		#self.sptclsize=ValBox(self,(0,500),"Ptcl Size:",256,90)
		#self.sptclsize.intonly=True
		#self.gbl.addWidget(self.sptclsize,14,2)

		#QtCore.QObject.connect(self.sdefocus, QtCore.SIGNAL("valueChanged"), self.newCTF)
		#QtCore.QObject.connect(self.sapix, QtCore.SIGNAL("valueChanged"), self.newCTF)
		#QtCore.QObject.connect(self.svoltage, QtCore.SIGNAL("valueChanged"), self.newCTF)
		#QtCore.QObject.connect(self.scs, QtCore.SIGNAL("valueChanged"), self.newCTF)
		#QtCore.QObject.connect(self.sboxsize, QtCore.SIGNAL("valueChanged"), self.newBox)
##		QtCore.QObject.connect(self.soversamp, QtCore.SIGNAL("valueChanged"), self.newBox)
		#QtCore.QObject.connect(self.squality,QtCore.SIGNAL("valueChanged"),self.newQualityFactor)
		#QtCore.QObject.connect(self.setlist,QtCore.SIGNAL("currentRowChanged(int)"),self.newSet)
		#QtCore.QObject.connect(self.setlist,QtCore.SIGNAL("keypress"),self.listkey)
		#QtCore.QObject.connect(self.sboxmode,QtCore.SIGNAL("currentIndexChanged(int)"),self.newBoxMode)

		#self.resize(720,380) # figured these values out by printing the width and height in resize event

		#### This section is responsible for background updates
		#self.busy=False
		#self.needupdate=True
		#self.needredisp=False
		#self.procthread=None
		#self.errors=None		# used to communicate errors back from the reprocessing thread

		#self.timer=QTimer()
		#QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.timeOut)
		#self.timer.start(100)

#		self.recalc()

	def setMouseManual(self,x):
		self.mmode="manual"
	
	def setMouseDel(self,x):
		self.mmode="del"
	
	def setMouseGoodRef(self,x):
		self.mmode="refgood"
	
	def setMouseBadRef(self,x):
		self.mmode="refbad"
		

	def imgmousedown(self,event) :
		m=self.wimage.scr_to_img((event.x(),event.y()))
		boxsize2=self.vbbsize.getValue()//2
		ptclsize=self.vbbpsize.getValue()

		self.curbox=0
		# check to see if click was inside an existing box, in which case we move it
		for i in self.boxes:
			if abs(m.x-i[0])<boxsize2 and abs(m.y-i[1])<boxsize2 : 
				self.curbox=i
				break
		else :
			# Create a new box
			if self.mmode=="del" : return	 # This is for creating a new box, so clearly not desirable in delete mode
			self.curbox=len(self.boxes)
			self.boxes.append((m[0],m[1],self.mmode))
			self.__addBox(self.curbox,self.boxes[-1])
			
			
				
		#self.guiim.add_shape("cen",["rect",.9,.9,.4,x0,y0,x0+2,y0+2,1.0])

	def imgmousedrag(self,event) :
		if self.calcmode==0:
			m=self.wimage.scr_to_img((event.x(),event.y()))
			parms=self.parms[self.curset]
			parms[2]=(m[0]-parms[0]/2,m[1]-parms[0]/2)
			self.needredisp=True
			self.recalc()
			

		# box deletion when shift held down
		#if event.modifiers()&Qt.ShiftModifier:
			#for i,j in enumerate(self.boxes):

	def imgmouseup(self,event) :
		m=self.wimage.scr_to_img((event.x(),event.y()))
		if self.calcmode==1:
			parms=self.parms[self.curset]
			nx=self.data["nx"]/parms[0]-1
			grid=int((m[0]-parms[0]/2)/parms[0])+int((m[1]-parms[0]/2)/parms[0])*nx
			if grid in parms[3] : parms[3].remove(grid)
			else: parms[3].add(grid)
			self.needredisp=True
			self.recalc()

	def ptclmousedown(self,event) :
		return
	
	def ptclmousedrag(self,event) :
		return

	def ptclmouseup(self,event) :
		return

	def refmousedown(self,event) :
		return
	
	def refmousedrag(self,event) :
		return

	def refmouseup(self,event) :
		return

	def newSet(self,val):
		"called when a new data set is selected from the list"

		first=True
		newfilename=str(self.setlist.item(val).text())
		if newfilename==self.curfilename : return

		# Write the current image parameters to the database
		if self.curfilename!=None and self.boxes!=None :
			self.save_boxes()
			first=False


		self.micrograph=load_micrograph(newfilename)
		self.wimage.set_data(self.micrograph)
		if first : E2loadappwin("e2boxer21","image",self.wimage.qt_parent)
		self.curfilename=newfilename
		self.restore_boxes()

	def save_boxes(self):
		js=js_open_dict(info_name(self.curfilename))
		js["boxes"]=self.boxes
		
	def restore_boxes(self):
		# first we restore the list of box locations
		js=js_open_dict(info_name(self.curfilename))
		try: self.boxes=js["boxes"]
		except: self.boxes=[]
		boxsize=self.vbbsize.getValue()
		ptclsize=self.vbbpsize.getValue()
		micro=self.wimage.get_data()
		
		self.wimage.del_shapes()
		if len(self.boxes)==0 : 
			self.wparticles.set_data([])
			return
		
		# Then we extract the actual boxed out particles
		goodrefs=[]
		badrefs=[]
		self.particles=[]
		for i,box in enumerate(self.boxes):
			self.__addBox(i,box)

			# extract the data
			boxim=self.micrograph.get_clip(Region(box[0]-boxsize//2,box[1]-boxsize//2,boxsize,boxsize))
			boxim["ptcl_source_coord"]=(box[0],box[1])
			if box[2]=="refgood":
				goodrefs.append(boxim)
			elif box[2]=="refbad":
				badrefs.append(boxim)
			else:
				self.particles.append(boxim)
		
		# finally redisplay as appropriate
		self.wimage.update()
		self.wparticles.set_data(self.particles)
		if len(self.particles)>0 : self.wparticles.show()
				
		if len(goodrefs)+len(badrefs)!=0:
			self.goodrefs=goodrefs
			self.badrefs=badrefs
			self.wrefs.set_data(self.goodrefs)
			self.wbadrefs.set_data(self.badrefs)
			
			if len(self.goodrefs)>0 : self.wrefs.show()
			if len(self.badrefs)>0 : self.wbadrefs.show()

	def __addBox(self,i,box):
		"""takes the number of the box in self.boxes and the (x,y,mode) tuple and displays it"""
		# Display the actual box
		boxsize=self.vbbsize.getValue()
		ptclsize=self.vbbpsize.getValue()
		try: color=self.boxcolors[box[2]]
		except: color=self.boxcolors["unknown"]
		self.wimage.add_shape("box{}".format(i),EMShape(("rect",color[0],color[1],color[2],box[0]-boxsize//2,box[1]-boxsize//2,box[0]+boxsize//2,box[1]+boxsize//2,2)))
		self.wimage.add_shape("cir{}".format(i),EMShape(("circle",color[0],color[1],color[2],box[0],box[1],ptclsize/2,1.5)))

		

	def listKey(self,event):
		pass

		#if event.key()>=Qt.Key_0 and event.key()<=Qt.Key_9 :
			#q=int(event.key())-Qt.Key_0
			#self.squality.setValue(q)
		#elif event.key() == Qt.Key_Left:
			#self.sdefocus.setValue(self.sdefocus.getValue()-0.03)
		#elif event.key() == Qt.Key_Right:
			#self.sdefocus.setValue(self.sdefocus.getValue()+0.03)
		#elif event.key()==Qt.Key_I :
			#self.doImport()
		#elif event.key()==Qt.Key_U :
			#self.unImport()

		
	def doAutoBox(self,b):
		"""Autobox button pressed, find the right algorithm and call it"""
		
		name,bname,fn1=self.aboxmodes[self.autotab.currentIndex()]
		
		print name," called"

	def closeEvent(self,event):
#		QtGui.QWidget.closeEvent(self,event)
		E2saveappwin("e2boxer21","main",self)
		E2saveappwin("e2boxer21","image",self.wimage.qt_parent)
		E2saveappwin("e2boxer21","particles",self.wparticles.qt_parent)
		E2saveappwin("e2boxer21","refs",self.wrefs.qt_parent)
		E2saveappwin("e2boxer21","badrefs",self.wbadrefs.qt_parent)

		#self.writeCurParm()
		event.accept()
		QtGui.qApp.exit(0)
Beispiel #11
0
class GUIBoxer(QtGui.QWidget):
    # Dictionary of autopickers
    # to add a new one, provide name:(Qt_setup_function,picker_execution_function)
    # Qt_setup_function(self,empty_grid_layout)
    # picker_execution_function(self,...

    aboxmodes = [("by Ref", "auto_ref", boxerByRef),
                 ("Gauss", "auto_gauss", boxerGauss)]
    boxcolors = {
        "selected": (0.9, 0.9, 0.9),
        "manual": (0, 0, 0),
        "refgood": (0, 0.8, 0),
        "refbad": (0.8, 0, 0),
        "unknown": [.4, .4, .1],
        "auto_ref": (.1, .1, .4),
        "auto_gauss": (.4, .1, .4)
    }

    def __init__(self,
                 imagenames,
                 voltage=None,
                 apix=None,
                 cs=None,
                 ac=10.0,
                 box=256,
                 ptcl=200):
        """The 'new' e2boxer interface.
		"""

        QtGui.QWidget.__init__(self, None)
        #		self.setWindowIcon(QtGui.QIcon(get_image_directory() + "ctf.png"))

        self.data = None
        self.curfilename = None  # current selected file for boxing
        self.filenames = imagenames  # list of available filenames
        self.micrograph = None
        self.boxes = None

        self.defaultvoltage = voltage
        self.defaultapix = apix
        self.defaultcs = cs
        self.defaultac = ac

        self.db = None  # open JSON file for current image

        self.wimage = EMImage2DWidget()
        self.wimage.setWindowTitle("Micrograph")

        self.wparticles = EMImageMXWidget()
        self.wparticles.setWindowTitle("Particles")

        self.wrefs = EMImageMXWidget()
        self.wrefs.setWindowTitle("Box Refs")

        self.wbadrefs = EMImageMXWidget()
        self.wbadrefs.setWindowTitle("Bad Box Refs")

        #self.wfft=EMImage2DWidget()
        #self.wfft.setWindowTitle("e2evalimage - 2D FFT")

        #self.wplot=EMPlot2DWidget()
        #self.wplot.setWindowTitle("e2evalimage - Plot")

        self.wimage.connect(self.wimage, QtCore.SIGNAL("mousedown"),
                            self.imgmousedown)
        self.wimage.connect(self.wimage, QtCore.SIGNAL("mousedrag"),
                            self.imgmousedrag)
        self.wimage.connect(self.wimage, QtCore.SIGNAL("mouseup"),
                            self.imgmouseup)
        self.wparticles.connect(self.wparticles, QtCore.SIGNAL("mousedown"),
                                self.ptclmousedown)
        self.wparticles.connect(self.wparticles, QtCore.SIGNAL("mousedrag"),
                                self.ptclmousedrag)
        self.wparticles.connect(self.wparticles, QtCore.SIGNAL("mouseup"),
                                self.ptclmouseup)
        self.wrefs.connect(self.wparticles, QtCore.SIGNAL("mousedown"),
                           self.refmousedown)
        self.wrefs.connect(self.wparticles, QtCore.SIGNAL("mousedrag"),
                           self.refmousedrag)
        self.wrefs.connect(self.wparticles, QtCore.SIGNAL("mouseup"),
                           self.refmouseup)

        self.wimage.mmode = "app"
        self.wparticles.mmode = "app"

        # This object is itself a widget we need to set up
        self.gbl = QtGui.QGridLayout(self)
        self.gbl.setMargin(8)
        self.gbl.setSpacing(6)

        # Micrograph list
        self.setlist = QtGui.QListWidget(self)
        self.setlist.setSizePolicy(QtGui.QSizePolicy.Preferred,
                                   QtGui.QSizePolicy.Expanding)
        for i in imagenames:
            self.setlist.addItem(i)
        self.gbl.addWidget(self.setlist, 0, 0, 12, 2)

        self.setlist.connect(self.setlist,
                             QtCore.SIGNAL("currentRowChanged(int)"),
                             self.newSet)
        self.setlist.connect(self.setlist, QtCore.SIGNAL("keypress"),
                             self.listKey)

        # Mouse Modes
        self.mmode = "manual"
        self.boxmm = QtGui.QGroupBox("Mouse Mode", self)
        self.boxmm.setFlat(False)
        self.gbl.addWidget(self.boxmm, 0, 2, 3, 2)

        self.hbl0 = QtGui.QHBoxLayout(self.boxmm)

        self.bmmanual = QtGui.QPushButton("Manual")
        self.bmmanual.setToolTip(
            "Manual selection of particles. No impact on autoselection.")
        self.bmmanual.setAutoExclusive(True)
        self.bmmanual.setCheckable(True)
        self.bmmanual.setChecked(True)
        self.hbl0.addWidget(self.bmmanual)

        self.bmdel = QtGui.QPushButton("Delete")
        self.bmdel.setToolTip(
            "Delete particles from any mode. Can also shift-click in other mouse modes."
        )
        self.bmdel.setAutoExclusive(True)
        self.bmdel.setCheckable(True)
        self.hbl0.addWidget(self.bmdel)

        self.bmgref = QtGui.QPushButton("Good Refs")
        self.bmgref.setToolTip(
            "Identify some good particles. Available to all autoboxers.")
        self.bmgref.setAutoExclusive(True)
        self.bmgref.setCheckable(True)
        self.hbl0.addWidget(self.bmgref)

        self.bmbref = QtGui.QPushButton("Bad Refs")
        self.bmbref.setToolTip(
            "Identify regions which should not be selected as particles.")
        self.bmbref.setAutoExclusive(True)
        self.bmbref.setCheckable(True)
        self.hbl0.addWidget(self.bmbref)

        QtCore.QObject.connect(self.bmmanual, QtCore.SIGNAL("clicked(bool)"),
                               self.setMouseManual)
        QtCore.QObject.connect(self.bmdel, QtCore.SIGNAL("clicked(bool)"),
                               self.setMouseDel)
        QtCore.QObject.connect(self.bmgref, QtCore.SIGNAL("clicked(bool)"),
                               self.setMouseGoodRef)
        QtCore.QObject.connect(self.bmbref, QtCore.SIGNAL("clicked(bool)"),
                               self.setMouseBadRef)

        # Global parameters
        self.boxparm = QtGui.QGroupBox("Parameters", self)
        self.boxparm.setFlat(False)
        self.gbl.addWidget(self.boxparm, 3, 2, 3, 3)

        self.gbl1 = QtGui.QGridLayout(self.boxparm)
        self.gbl1.setMargin(8)
        self.gbl1.setSpacing(6)

        self.vbbsize = ValBox(label="Box Size:", value=box)
        self.gbl1.addWidget(self.vbbsize, 0, 0)

        self.vbbpsize = ValBox(label="Ptcl Size:", value=ptcl)
        self.gbl1.addWidget(self.vbbpsize, 0, 1)

        self.vbbapix = ValBox(label="A/pix:", value=apix)
        self.gbl1.addWidget(self.vbbapix, 0, 2)

        self.vbvoltage = ValBox(label="Voltage:", value=voltage)
        self.gbl1.addWidget(self.vbvoltage, 1, 0)

        self.vbbac = ValBox(label="% AC:", value=ac)
        self.gbl1.addWidget(self.vbbac, 1, 1)

        self.vbcs = ValBox(label="Cs:", value=cs)
        self.gbl1.addWidget(self.vbcs, 1, 2)

        # Autoboxing Tabs
        self.autolbl = QtGui.QLabel("Autoboxing Methods:")
        self.gbl.addWidget(self.autolbl, 7, 2)
        self.autotab = QtGui.QTabWidget()
        self.gbl.addWidget(self.autotab, 8, 2, 6, 3)

        self.bautobox = QtGui.QPushButton("Autobox")
        self.gbl.addWidget(self.bautobox, 7, 4)

        QtCore.QObject.connect(self.bautobox, QtCore.SIGNAL("clicked(bool)"),
                               self.doAutoBox)

        # Individual tabs from Dictionary
        self.abwid = []
        for name, bname, cls in GUIBoxer.aboxmodes:
            w = QtGui.QWidget()
            gl = QtGui.QGridLayout(w)
            self.abwid.append((w, gl))
            cls.setup_gui(gl)
            self.autotab.addTab(w, name)

        self.setWindowTitle("e2boxer21 - Control Panel")

        self.wimage.show()
        #		self.wfft.show()
        #		self.wplot.show()
        E2loadappwin("e2boxer21", "main", self)
        E2loadappwin("e2boxer21", "image", self.wimage.qt_parent)
        E2loadappwin("e2boxer21", "particles", self.wparticles.qt_parent)
        E2loadappwin("e2boxer21", "refs", self.wrefs.qt_parent)
        E2loadappwin("e2boxer21", "badrefs", self.wbadrefs.qt_parent)

        self.newSet(0)

    #QWidget *firstPageWidget = new QWidget;
    #QWidget *secondPageWidget = new QWidget;
    #QWidget *thirdPageWidget = new QWidget;

    #QStackedWidget *stackedWidget = new QStackedWidget;
    #stackedWidget->addWidget(firstPageWidget);
    #stackedWidget->addWidget(secondPageWidget);
    #stackedWidget->addWidget(thirdPageWidget);

    #QVBoxLayout *layout = new QVBoxLayout;
    #layout->addWidget(stackedWidget);
    #setLayout(layout);

    #QComboBox *pageComboBox = new QComboBox;
    #pageComboBox->addItem(tr("Page 1"));
    #pageComboBox->addItem(tr("Page 2"));
    #pageComboBox->addItem(tr("Page 3"));
    #connect(pageComboBox, SIGNAL(activated(int)),stackedWidget, SLOT(setCurrentIndex(int)));

    #self.lboxmode=QtGui.QLabel("Mode:",self)
    #self.gbl.addWidget(self.lboxmode,10,0)

    #self.sboxmode=QtGui.QComboBox(self)
    #self.sboxmode.addItem("Manual")
    #self.sboxmode.addItem("Reference")
    #self.sboxmode.setCurrentIndex(1)
    #self.gbl.addWidget(self.sboxmode,10,1)

    #self.lanmode=QtGui.QLabel("Annotate:",self)
    #self.gbl.addWidget(self.lanmode,12,0)

    #self.sanmode=QtGui.QComboBox(self)
    #self.sanmode.addItem("Box")
    #self.sanmode.addItem("Box+dot")
    #self.sanmode.addItem("Circle")
    #self.sanmode.addItem("None")
    #self.gbl.addWidget(self.sanmode,12,1)

    #self.sdefocus=ValSlider(self,(0,5),"Defocus:",0.0,90)
    #self.gbl.addWidget(self.sdefocus,0,2,1,3)

    #self.squality=ValSlider(self,(0,9),"Quality (0-9):",0,90)
    #self.squality.setIntonly(True)

    #self.gbl.addWidget(self.squality,6,2,1,3)

    #self.brefit=QtGui.QPushButton("Autobox")
    #self.gbl.addWidget(self.brefit,7,2)

    #self.bclrauto=QtGui.QPushButton("Clear Auto")
    #self.gbl.addWidget(self.bclrauto,7,3)

    #self.bclrall=QtGui.QPushButton("Clear All")
    #self.gbl.addWidget(self.bclrall,7,4)

    #self.sapix=ValBox(self,(0,500),"A/pix:",1.0,90)
    #if self.defaultapix!=None : self.sapix.setValue(self.defaultapix)
    #self.gbl.addWidget(self.sapix,10,2)

    #self.svoltage=ValBox(self,(0,500),"Voltage (kV):",200,90)
    #if self.defaultvoltage!=None : self.svoltage.setValue(self.defaultvoltage)
    #self.gbl.addWidget(self.svoltage,11,2)

    #self.scs=ValBox(self,(0,5),"Cs (mm):",4.1,90)
    #if self.defaultcs!=None : self.scs.setValue(self.defaultcs)
    #self.gbl.addWidget(self.scs,12,2)

    #self.sboxsize=ValBox(self,(0,500),"Box Size:",256,90)
    #self.sboxsize.intonly=True
    #self.gbl.addWidget(self.sboxsize,13,2)

    #self.sptclsize=ValBox(self,(0,500),"Ptcl Size:",256,90)
    #self.sptclsize.intonly=True
    #self.gbl.addWidget(self.sptclsize,14,2)

    #QtCore.QObject.connect(self.sdefocus, QtCore.SIGNAL("valueChanged"), self.newCTF)
    #QtCore.QObject.connect(self.sapix, QtCore.SIGNAL("valueChanged"), self.newCTF)
    #QtCore.QObject.connect(self.svoltage, QtCore.SIGNAL("valueChanged"), self.newCTF)
    #QtCore.QObject.connect(self.scs, QtCore.SIGNAL("valueChanged"), self.newCTF)
    #QtCore.QObject.connect(self.sboxsize, QtCore.SIGNAL("valueChanged"), self.newBox)
##		QtCore.QObject.connect(self.soversamp, QtCore.SIGNAL("valueChanged"), self.newBox)
#QtCore.QObject.connect(self.squality,QtCore.SIGNAL("valueChanged"),self.newQualityFactor)
#QtCore.QObject.connect(self.setlist,QtCore.SIGNAL("currentRowChanged(int)"),self.newSet)
#QtCore.QObject.connect(self.setlist,QtCore.SIGNAL("keypress"),self.listkey)
#QtCore.QObject.connect(self.sboxmode,QtCore.SIGNAL("currentIndexChanged(int)"),self.newBoxMode)

#self.resize(720,380) # figured these values out by printing the width and height in resize event

#### This section is responsible for background updates
#self.busy=False
#self.needupdate=True
#self.needredisp=False
#self.procthread=None
#self.errors=None		# used to communicate errors back from the reprocessing thread

#self.timer=QTimer()
#QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.timeOut)
#self.timer.start(100)

#		self.recalc()

    def setMouseManual(self, x):
        self.mmode = "manual"

    def setMouseDel(self, x):
        self.mmode = "del"

    def setMouseGoodRef(self, x):
        self.mmode = "refgood"

    def setMouseBadRef(self, x):
        self.mmode = "refbad"

    def imgmousedown(self, event):
        m = self.wimage.scr_to_img((event.x(), event.y()))
        boxsize2 = self.vbbsize.getValue() // 2
        ptclsize = self.vbbpsize.getValue()

        self.curbox = 0
        # check to see if click was inside an existing box, in which case we move it
        for i in self.boxes:
            if abs(m.x - i[0]) < boxsize2 and abs(m.y - i[1]) < boxsize2:
                self.curbox = i
                break
        else:
            # Create a new box
            if self.mmode == "del":
                return  # This is for creating a new box, so clearly not desirable in delete mode
            self.curbox = len(self.boxes)
            self.boxes.append((m[0], m[1], self.mmode))
            self.__addBox(self.curbox, self.boxes[-1])

        #self.guiim.add_shape("cen",["rect",.9,.9,.4,x0,y0,x0+2,y0+2,1.0])

    def imgmousedrag(self, event):
        if self.calcmode == 0:
            m = self.wimage.scr_to_img((event.x(), event.y()))
            parms = self.parms[self.curset]
            parms[2] = (m[0] - parms[0] / 2, m[1] - parms[0] / 2)
            self.needredisp = True
            self.recalc()

        # box deletion when shift held down
        #if event.modifiers()&Qt.ShiftModifier:
        #for i,j in enumerate(self.boxes):

    def imgmouseup(self, event):
        m = self.wimage.scr_to_img((event.x(), event.y()))
        if self.calcmode == 1:
            parms = self.parms[self.curset]
            nx = self.data["nx"] / parms[0] - 1
            grid = int((m[0] - parms[0] / 2) / parms[0]) + int(
                (m[1] - parms[0] / 2) / parms[0]) * nx
            if grid in parms[3]: parms[3].remove(grid)
            else: parms[3].add(grid)
            self.needredisp = True
            self.recalc()

    def ptclmousedown(self, event):
        return

    def ptclmousedrag(self, event):
        return

    def ptclmouseup(self, event):
        return

    def refmousedown(self, event):
        return

    def refmousedrag(self, event):
        return

    def refmouseup(self, event):
        return

    def newSet(self, val):
        "called when a new data set is selected from the list"

        first = True
        newfilename = str(self.setlist.item(val).text())
        if newfilename == self.curfilename: return

        # Write the current image parameters to the database
        if self.curfilename != None and self.boxes != None:
            self.save_boxes()
            first = False

        self.micrograph = load_micrograph(newfilename)
        self.wimage.set_data(self.micrograph)
        if first: E2loadappwin("e2boxer21", "image", self.wimage.qt_parent)
        self.curfilename = newfilename
        self.restore_boxes()

    def save_boxes(self):
        js = js_open_dict(info_name(self.curfilename))
        js["boxes"] = self.boxes

    def restore_boxes(self):
        # first we restore the list of box locations
        js = js_open_dict(info_name(self.curfilename))
        try:
            self.boxes = js["boxes"]
        except:
            self.boxes = []
        boxsize = self.vbbsize.getValue()
        ptclsize = self.vbbpsize.getValue()
        micro = self.wimage.get_data()

        self.wimage.del_shapes()
        if len(self.boxes) == 0:
            self.wparticles.set_data([])
            return

        # Then we extract the actual boxed out particles
        goodrefs = []
        badrefs = []
        self.particles = []
        for i, box in enumerate(self.boxes):
            self.__addBox(i, box)

            # extract the data
            boxim = self.micrograph.get_clip(
                Region(box[0] - boxsize // 2, box[1] - boxsize // 2, boxsize,
                       boxsize))
            boxim["ptcl_source_coord"] = (box[0], box[1])
            if box[2] == "refgood":
                goodrefs.append(boxim)
            elif box[2] == "refbad":
                badrefs.append(boxim)
            else:
                self.particles.append(boxim)

        # finally redisplay as appropriate
        self.wimage.update()
        self.wparticles.set_data(self.particles)
        if len(self.particles) > 0: self.wparticles.show()

        if len(goodrefs) + len(badrefs) != 0:
            self.goodrefs = goodrefs
            self.badrefs = badrefs
            self.wrefs.set_data(self.goodrefs)
            self.wbadrefs.set_data(self.badrefs)

            if len(self.goodrefs) > 0: self.wrefs.show()
            if len(self.badrefs) > 0: self.wbadrefs.show()

    def __addBox(self, i, box):
        """takes the number of the box in self.boxes and the (x,y,mode) tuple and displays it"""
        # Display the actual box
        boxsize = self.vbbsize.getValue()
        ptclsize = self.vbbpsize.getValue()
        try:
            color = self.boxcolors[box[2]]
        except:
            color = self.boxcolors["unknown"]
        self.wimage.add_shape(
            "box{}".format(i),
            EMShape(("rect", color[0], color[1], color[2],
                     box[0] - boxsize // 2, box[1] - boxsize // 2,
                     box[0] + boxsize // 2, box[1] + boxsize // 2, 2)))
        self.wimage.add_shape(
            "cir{}".format(i),
            EMShape(("circle", color[0], color[1], color[2], box[0], box[1],
                     ptclsize / 2, 1.5)))

    def listKey(self, event):
        pass

        #if event.key()>=Qt.Key_0 and event.key()<=Qt.Key_9 :
        #q=int(event.key())-Qt.Key_0
        #self.squality.setValue(q)
        #elif event.key() == Qt.Key_Left:
        #self.sdefocus.setValue(self.sdefocus.getValue()-0.03)
        #elif event.key() == Qt.Key_Right:
        #self.sdefocus.setValue(self.sdefocus.getValue()+0.03)
        #elif event.key()==Qt.Key_I :
        #self.doImport()
        #elif event.key()==Qt.Key_U :
        #self.unImport()

    def doAutoBox(self, b):
        """Autobox button pressed, find the right algorithm and call it"""

        name, bname, fn1 = self.aboxmodes[self.autotab.currentIndex()]

        print name, " called"

    def closeEvent(self, event):
        #		QtGui.QWidget.closeEvent(self,event)
        E2saveappwin("e2boxer21", "main", self)
        E2saveappwin("e2boxer21", "image", self.wimage.qt_parent)
        E2saveappwin("e2boxer21", "particles", self.wparticles.qt_parent)
        E2saveappwin("e2boxer21", "refs", self.wrefs.qt_parent)
        E2saveappwin("e2boxer21", "badrefs", self.wbadrefs.qt_parent)

        #self.writeCurParm()
        event.accept()
        QtGui.qApp.exit(0)