class ErasingPanel: # copied for ideas for the morph panel def __init__(self, target, erase_radius=128): self.busy = True self.erase_radius = erase_radius self.target = weakref.ref(target) self.erase_rad_edit = None self.widget = None self.busy = False def set_erase_radius(self, erase_rad_edit): self.busy = True self.erase_radius = erase_rad_edit if self.erase_rad_edit != None: self.erase_rad_edit.setValue(erase_rad_edit) self.busy = False def get_widget(self): if self.widget == None: from PyQt4 import QtCore, QtGui, Qt self.widget = QtGui.QWidget() vbl = QtGui.QVBoxLayout(self.widget) vbl.setMargin(0) vbl.setSpacing(6) vbl.setObjectName("vbl") hbl = QtGui.QHBoxLayout() hbl.addWidget(QtGui.QLabel("Erase Radius:")) from eman2_gui.valslider import ValSlider self.erase_rad_edit = ValSlider(None, (0.0, 1000.0), "") self.erase_rad_edit.setValue(int(self.erase_radius)) self.erase_rad_edit.setEnabled(True) hbl.addWidget(self.erase_rad_edit) self.unerase = QtGui.QCheckBox("Unerase") self.unerase.setChecked(False) vbl.addLayout(hbl) vbl.addWidget(self.unerase) QtCore.QObject.connect(self.erase_rad_edit, QtCore.SIGNAL("sliderReleased"), self.new_erase_radius) QtCore.QObject.connect(self.unerase, QtCore.SIGNAL("clicked(bool)"), self.unerase_checked) return self.widget def new_erase_radius(self, erase_rad_edit): if self.busy: return self.target().set_erase_radius(erase_rad_edit) def unerase_checked(self, val): if self.busy: return self.target().toggle_unerase(val)
class GUIctfsim(QtGui.QWidget): module_closed = QtCore.pyqtSignal() def __init__(self, application, apix=1.0, voltage=300.0, cs=4.1, ac=10.0, samples=256, apply=None): """CTF simulation dialog """ try: from eman2_gui.emimage2d import EMImage2DWidget except: print("Cannot import EMAN image GUI objects (EMImage2DWidget)") sys.exit(1) try: from eman2_gui.emplot2d import EMPlot2DWidget except: print( "Cannot import EMAN plot GUI objects (is matplotlib installed?)" ) sys.exit(1) self.app = weakref.ref(application) self.df_voltage = voltage self.df_apix = apix self.df_cs = cs self.df_ac = ac self.df_samples = samples self.img = None if apply == None: self.apply = None self.applyim = None else: self.apply = EMData(apply, 0) self.df_apix = self.apply["apix_x"] print("A/pix reset to ", self.df_apix) self.applyim = EMImage2DWidget(application=self.app()) QtGui.QWidget.__init__(self, None) self.setWindowIcon(QtGui.QIcon(get_image_directory() + "ctf.png")) self.data = [] self.curset = 0 self.plotmode = 0 self.guiim = EMImage2DWidget(application=self.app()) self.guiiminit = True # a flag that's used to auto resize the first time the gui's set_data function is called self.guiplot = EMPlot2DWidget(application=self.app()) # self.guirealim=EMImage2DWidget(application=self.app()) # This will show the original particle images self.guiim.mousedown.connect(self.imgmousedown) self.guiim.mousedrag.connect(self.imgmousedrag) self.guiim.mouseup.connect(self.imgmouseup) self.guiplot.mousedown.connect(self.plotmousedown) self.guiim.mmode = "app" # This object is itself a widget we need to set up self.hbl = QtGui.QHBoxLayout(self) self.hbl.setMargin(0) self.hbl.setSpacing(6) self.hbl.setObjectName("hbl") # plot list and plot mode combobox self.vbl2 = QtGui.QVBoxLayout() self.setlist = MyListWidget(self) self.setlist.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.vbl2.addWidget(self.setlist) self.splotmode = QtGui.QComboBox(self) self.splotmode.addItem("Amplitude") self.splotmode.addItem("Intensity") self.splotmode.addItem("Int w sum") self.splotmode.addItem("Amp w sum") self.vbl2.addWidget(self.splotmode) self.hbl.addLayout(self.vbl2) # ValSliders for CTF parameters self.vbl = QtGui.QVBoxLayout() self.vbl.setMargin(0) self.vbl.setSpacing(6) self.vbl.setObjectName("vbl") self.hbl.addLayout(self.vbl) #self.samp = ValSlider(self,(0,5.0),"Amp:",0) #self.vbl.addWidget(self.samp) self.imginfo = QtGui.QLabel("Info", self) self.vbl.addWidget(self.imginfo) self.sdefocus = ValSlider(self, (0, 5), "Defocus:", 0, 90) self.vbl.addWidget(self.sdefocus) self.sbfactor = ValSlider(self, (0, 1600), "B factor:", 100, 90) self.vbl.addWidget(self.sbfactor) self.sdfdiff = ValSlider(self, (0, 1), "DF Diff:", 0, 90) self.vbl.addWidget(self.sdfdiff) self.sdfang = ValSlider(self, (0, 180), "Df Angle:", 0, 90) self.vbl.addWidget(self.sdfang) self.sampcont = ValSlider(self, (0, 100), "% AC", 0, 90) self.vbl.addWidget(self.sampcont) self.sphase = ValSlider(self, (0, 1), "Phase/pi", 0, 90) self.vbl.addWidget(self.sphase) self.sapix = ValSlider(self, (.2, 10), "A/Pix:", 2, 90) self.vbl.addWidget(self.sapix) self.svoltage = ValSlider(self, (0, 1000), "Voltage (kV):", 0, 90) self.vbl.addWidget(self.svoltage) self.scs = ValSlider(self, (0, 5), "Cs (mm):", 0, 90) self.vbl.addWidget(self.scs) self.ssamples = ValSlider(self, (32, 1024), "# Samples:", 0, 90) self.ssamples.setIntonly(True) self.vbl.addWidget(self.ssamples) self.hbl_buttons = QtGui.QHBoxLayout() self.newbut = QtGui.QPushButton("New") self.hbl_buttons.addWidget(self.newbut) self.vbl.addLayout(self.hbl_buttons) self.on_new_but() self.sdefocus.valueChanged.connect(self.newCTF) self.sbfactor.valueChanged.connect(self.newCTF) self.sdfdiff.valueChanged.connect(self.newCTF) self.sdfang.valueChanged.connect(self.newCTF) self.sapix.valueChanged.connect(self.newCTF) self.sampcont.valueChanged.connect(self.newCTFac) self.sphase.valueChanged.connect(self.newCTFpha) self.svoltage.valueChanged.connect(self.newCTF) self.scs.valueChanged.connect(self.newCTF) self.ssamples.valueChanged.connect(self.newCTF) self.setlist.currentRowChanged[int].connect(self.newSet) self.setlist.keypress.connect(self.listkey) self.splotmode.currentIndexChanged[int].connect(self.newPlotMode) self.newbut.clicked[bool].connect(self.on_new_but) self.resize( 720, 380 ) # figured these values out by printing the width and height in resize event E2loadappwin("e2ctfsim", "main", self) E2loadappwin("e2ctfsim", "image", self.guiim.qt_parent) # E2loadappwin("e2ctf","realimage",self.guirealim.qt_parent) E2loadappwin("e2ctfsim", "plot", self.guiplot.qt_parent) self.setWindowTitle("CTF") def listkey(self, event): 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.01) elif event.key() == Qt.Key_Right: self.sdefocus.setValue(self.sdefocus.getValue() + 0.01) elif event.key() == Qt.Key_R: self.on_recall_params() def on_new_but(self): ctf = EMAN2Ctf() ctf.defocus = 1.0 ctf.bfactor = 100.0 ctf.voltage = self.df_voltage ctf.apix = self.df_apix ctf.cs = self.df_cs ctf.ac = self.df_ac ctf.samples = self.df_samples self.data.append((str(len(self.setlist) + 1), ctf)) self.curset = len(self.data) self.update_data() def show_guis(self): if self.guiim != None: self.app().show_specific(self.guiim) if self.applyim != None: self.app().show_specific(self.applyim) if self.guiplot != None: self.app().show_specific(self.guiplot) #if self.guirealim != None: #self.app().show_specific(self.guirealim) self.show() def closeEvent(self, event): # QtGui.QWidget.closeEvent(self,event) # self.app.app.closeAllWindows() E2saveappwin("e2ctf", "main", self) if self.guiim != None: E2saveappwin("e2ctf", "image", self.guiim.qt_parent) self.app().close_specific(self.guiim) self.guiim = None if self.applyim != None: self.app().close_specific(self.applyim) self.applyim = None if self.guiplot != None: E2saveappwin("e2ctf", "plot", self.guiplot.qt_parent) self.app().close_specific(self.guiplot) #if self.guirealim != None: #E2saveappwin("e2ctf","realimage",self.guirealim.qt_parent) #self.app().close_specific(self.guirealim) event.accept() self.app().close_specific(self) self.module_closed.emit( ) # this signal is important when e2ctf is being used by a program running its own event loop def update_data(self): """This will make sure the various widgets properly show the current data sets""" self.setlist.clear() for i, j in enumerate(self.data): self.setlist.addItem(j[0]) self.setlist.setCurrentRow(self.curset) def update_plot(self): if self.guiplot == None: return # it's closed/not visible for d in range(len(self.data)): ctf = self.data[d][1] ds = old_div(1.0, (ctf.apix * 2.0 * ctf.samples)) s = arange(0, ds * ctf.samples, ds) curve = array(ctf.compute_1d(len(s) * 2, ds, Ctf.CtfType.CTF_AMP)) if self.plotmode == 1 or self.plotmode == 2: curve = curve**2 if self.plotmode == 2 or self.plotmode == 3: if d == 0: avg = curve[:] else: if len(curve) != len(avg): print( "Number of samples must be fixed to compute an average ({})" .format(d + 1)) else: avg += curve self.guiplot.set_data((s, curve), self.data[d][0], d == 0, True, color=d + 1) if self.plotmode in (2, 3): self.guiplot.set_data((s, avg), "Sum", False, True, color=0) self.guiplot.setAxisParms("s (1/$\AA$)", "CTF") ctf.compute_2d_complex(self.img, Ctf.CtfType.CTF_AMP, None) self.guiim.set_data(self.img) if self.applyim != None: applyf = self.apply.do_fft() ctfmul = applyf.copy() ctf.compute_2d_complex(ctfmul, Ctf.CtfType.CTF_AMP) ctfsgn = applyf.copy() ctf.compute_2d_complex(ctfsgn, Ctf.CtfType.CTF_SIGN) applyf.mult(ctfmul) apply2 = applyf.do_ift() apply2.mult( 5.0 ) # roughly compensate for contrast reduction so apply comparable applyf.mult(ctfsgn) apply3 = applyf.do_ift() apply3.mult(5.0) self.applyim.set_data([apply2, apply3, self.apply]) def newSet(self, val=0): "called when a new data set is selected from the list" self.curset = val self.sdefocus.setValue(self.data[val][1].defocus, True) self.sbfactor.setValue(self.data[val][1].bfactor, True) self.sapix.setValue(self.data[val][1].apix, True) self.sampcont.setValue(self.data[val][1].ampcont, True) self.sphase.setValue(old_div(self.data[val][1].get_phase(), pi), True) self.svoltage.setValue(self.data[val][1].voltage, True) self.scs.setValue(self.data[val][1].cs, True) self.sdfdiff.setValue(self.data[val][1].dfdiff, True) self.sdfang.setValue(self.data[val][1].dfang, True) self.ssamples.setValue(self.data[val][1].samples, True) # make new image if necessary if self.img == None or self.img["ny"] != self.data[val][1].samples: self.img = EMData(self.data[val][1].samples + 2, self.data[val][1].samples) self.img.to_zero() self.img.set_complex(1) self.guiim.set_data(self.img) # self.imginfo.setText("%s particles SNR = %s"%(ptcl,ssnr)) #if self.guiim != None: ## print self.data #self.guiim.set_data(self.data[val][4]) #if self.guiiminit: #self.guiim.optimally_resize() #self.guiiminit = False #self.guiim.updateGL() #self.update_plot() # print "self.data[val]=",self.data[val][0].split('#')[-1] self.guiim.qt_parent.setWindowTitle("e2ctfsim - 2D FFT - " + self.data[val][0]) # self.guirealim.qt_parent.setWindowTitle("e2ctf - "+self.data[val][0].split('#')[-1]) self.guiplot.qt_parent.setWindowTitle("e2ctfsim - Plot ") #n=EMUtil.get_image_count(self.data[val][0]) #if n>1: #self.ptcldata=EMData.read_images(self.data[val][0],range(0,min(20,n))) #im=sum(self.ptcldata) #im.mult(1.0/len(self.ptcldata)) #self.ptcldata.insert(0,im) #self.guirealim.set_data(self.ptcldata) #else : self.guirealim.set_data([EMData()]) self.update_plot() def newPlotMode(self, mode): self.plotmode = mode self.update_plot() def newCTF(self): # print traceback.print_stack() self.data[self.curset][1].defocus = self.sdefocus.value self.data[self.curset][1].bfactor = self.sbfactor.value self.data[self.curset][1].dfdiff = self.sdfdiff.value self.data[self.curset][1].dfang = self.sdfang.value self.data[self.curset][1].apix = self.sapix.value self.data[self.curset][1].ampcont = self.sampcont.value # self.data[self.curset][1].set_phase(self.sphase.value)*pi self.data[self.curset][1].voltage = self.svoltage.value self.data[self.curset][1].cs = self.scs.value self.data[self.curset][1].samples = self.ssamples.value if self.img == None or self.img["ny"] != self.ssamples.value: self.img = EMData(self.ssamples.value + 2, self.ssamples.value) self.img.to_zero() self.img.set_complex(1) self.guiim.set_data(self.img) self.update_plot() def newCTFac(self): # print traceback.print_stack() self.data[self.curset][1].ampcont = self.sampcont.value self.sphase.setValue( old_div(self.data[self.curset][1].get_phase(), pi), True) if self.img == None or self.img["ny"] != self.ssamples.value: self.img = EMData(self.ssamples.value + 2, self.ssamples.value) self.img.to_zero() self.img.set_complex(1) self.guiim.set_data(self.img) self.update_plot() def newCTFpha(self): # print traceback.print_stack() self.data[self.curset][1].set_phase(self.sphase.value * pi) self.sampcont.setValue(self.data[self.curset][1].ampcont, True) if self.img == None or self.img["ny"] != self.ssamples.value: self.img = EMData(self.ssamples.value + 2, self.ssamples.value) self.img.to_zero() self.img.set_complex(1) self.guiim.set_data(self.img) self.update_plot() def imgmousedown(self, event): m = self.guiim.scr_to_img((event.x(), event.y())) #self.guiim.add_shape("cen",["rect",.9,.9,.4,x0,y0,x0+2,y0+2,1.0]) def imgmousedrag(self, event): m = self.guiim.scr_to_img((event.x(), event.y())) # box deletion when shift held down #if event.modifiers()&Qt.ShiftModifier: #for i,j in enumerate(self.boxes): def imgmouseup(self, event): m = self.guiim.scr_to_img((event.x(), event.y())) def plotmousedown(self, event): m = self.guiim.scr_to_img((event.x(), event.y())) def run(self): """If you make your own application outside of this object, you are free to use your own local app.exec_(). This is a convenience for ctf-only programs.""" self.app.exec_() # E2saveappwin("boxer","imagegeom",self.guiim) # try: # E2setappval("boxer","imcontrol",self.guiim.inspector.isVisible()) # if self.guiim.inspector.isVisible() : E2saveappwin("boxer","imcontrolgeom",self.guiim.inspector) # except : E2setappval("boxer","imcontrol",False) return
class GUIctf(QtGui.QWidget): def __init__(self, application, data): """Implements the CTF fitting dialog using various EMImage and EMPlot2D widgets 'data' is a list of (filename,ctf,im_1d,bg_1d,im_2d,bg_2d) """ try: from eman2_gui.emimage2d import EMImage2DWidget except: ERROR("Cannot import EMAN image GUI objects (EMImage2DWidget)") return try: from eman2_gui.emplot2d import EMPlot2DWidget except: ERROR( "Cannot import EMAN plot GUI objects (is matplotlib installed?)" ) return self.app = weakref.ref(application) QtGui.QWidget.__init__(self, None) self.setWindowIcon(QtGui.QIcon(get_image_directory() + "ctf.png")) self.data = data self.curset = 0 self.plotmode = 0 self.guiim = EMImage2DWidget(application=self.app()) self.guiplot = EMPlot2DWidget(application=self.app()) self.guiim.mousedown.connect(self.imgmousedown) self.guiim.mousedrag.connect(self.imgmousedrag) self.guiim.mouseup.connect(self.imgmouseup) self.guiplot.mousedown.connect(self.plotmousedown) self.guiim.mmode = "app" # This object is itself a widget we need to set up self.hbl = QtGui.QHBoxLayout(self) self.hbl.setMargin(0) self.hbl.setSpacing(6) self.hbl.setObjectName("hbl") # plot list and plot mode combobox self.vbl2 = QtGui.QVBoxLayout() self.setlist = QtGui.QListWidget(self) self.setlist.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.vbl2.addWidget(self.setlist) self.splotmode = QtGui.QComboBox(self) self.splotmode.addItem("Bgsub & fit") self.splotmode.addItem("Ptcl & BG power") self.splotmode.addItem("SNR") self.splotmode.addItem("Smoothed SNR") self.splotmode.addItem("Integrated SNR") self.splotmode.addItem("Total CTF") self.vbl2.addWidget(self.splotmode) self.hbl.addLayout(self.vbl2) # ValSliders for CTF parameters self.vbl = QtGui.QVBoxLayout() self.vbl.setMargin(0) self.vbl.setSpacing(6) self.vbl.setObjectName("vbl") self.hbl.addLayout(self.vbl) #self.samp = ValSlider(self,(0,5.0),"Amp:",0) #self.vbl.addWidget(self.samp) self.sdefocus = ValSlider(self, (0, 5), "Defocus:", 0, 90) self.vbl.addWidget(self.sdefocus) self.sbfactor = ValSlider(self, (0, 1600), "B factor:", 0, 90) self.vbl.addWidget(self.sbfactor) self.sampcont = ValSlider(self, (0, 100), "% AC", 0, 90) self.vbl.addWidget(self.sampcont) # self.sapix=ValSlider(self,(.2,10),"A/Pix:",2,90) # self.vbl.addWidget(self.sapix) self.svoltage = ValSlider(self, (0, 500), "Voltage (kV):", 0, 90) self.vbl.addWidget(self.svoltage) self.scs = ValSlider(self, (0, 5), "Cs (mm):", 0, 90) self.vbl.addWidget(self.scs) self.hbl_buttons = QtGui.QHBoxLayout() self.saveparms = QtGui.QPushButton("Save parms") self.recallparms = QtGui.QPushButton("Recall") self.output = QtGui.QPushButton("Output") self.hbl_buttons.addWidget(self.saveparms) self.hbl_buttons.addWidget(self.recallparms) self.hbl_buttons2 = QtGui.QHBoxLayout() self.hbl_buttons2.addWidget(self.output) self.vbl.addLayout(self.hbl_buttons) self.vbl.addLayout(self.hbl_buttons2) self.sdefocus.valueChanged.connect(self.newCTF) self.sbfactor.valueChanged.connect(self.newCTF) # QtCore.QObject.connect(self.sapix, QtCore.SIGNAL("valueChanged"), self.newCTF) self.sampcont.valueChanged.connect(self.newCTF) self.svoltage.valueChanged.connect(self.newCTF) self.scs.valueChanged.connect(self.newCTF) self.setlist.currentRowChanged[int].connect(self.newSet) self.splotmode.currentIndexChanged[int].connect(self.newPlotMode) self.saveparms.clicked[bool].connect(self.on_save_params) self.recallparms.clicked[bool].connect(self.on_recall_params) self.output.clicked[bool].connect(self.on_output) self.update_data() self.update_data() self.resize( 460, 380 ) # figured these values out by printing the width and height in resize event self.setWindowTitle("CTF") def on_save_params(self): if len(self.setlist.selectedItems()) == 0: return val = self.curset name = str(self.setlist.item(val).text()) name = base_name(name) # if not db_check_dict(name): # print "error, the db doesn't exist:",name # db_parms = db_open_dict("bdb:e2ctf.parms") ctf = self.data[val][1].to_string() output = [] for i, val in enumerate(self.data[val]): # ignore i == 0 it's just the filename if i > 1: output.append(val) elif i == 1: output.append(ctf) db_parms[name] = output def on_recall_params(self): if len(self.setlist.selectedItems()) == 0: return val = self.curset name = str(self.setlist.item(val).text()) data = [name] name = base_name(name) db_parms = db_open_dict("bdb:e2ctf.parms") if name not in db_parms: sxprint("error, ctf parameters do not exist for:", name) # data.extend(db_parms[name]) ctf = EMAN2Ctf() ctf.from_string(data[1]) data[1] = ctf self.data[val] = data self.newSet(self.curset) # def get_output_params(self): def on_output(self): from eman2_gui.emsprworkflow import E2CTFOutputTaskGeneral self.form = E2CTFOutputTaskGeneral() self.form.run_form() def show_guis(self): if self.guiim != None: self.app().show_specific(self.guiim) if self.guiplot != None: self.app().show_specific(self.guiplot) self.app().show_specific(self) def closeEvent(self, event): # QtGui.QWidget.closeEvent(self,event) # self.app.app.closeAllWindows() if self.guiim != None: self.app().close_specific(self.guiim) self.guiim = None if self.guiplot != None: self.app().close_specific(self.guiplot) event.accept() def newData(self, data): self.data = data self.update_data() def update_data(self): """This will make sure the various widgets properly show the current data sets""" self.setlist.clear() for i, j in enumerate(self.data): self.setlist.addItem(j[0]) self.setlist.setCurrentRow(self.curset) def update_plot(self): val = self.curset ctf = self.data[val][1] ds = self.data[val][1].dsbg s = [ds * i for i in range(len(ctf.background))] if self.plotmode == 1: self.guiplot.set_data((s, self.data[val][2]), "fg", True, True) self.guiplot.set_data((s, self.data[val][3]), "bg") self.guiplot.setAxisParms("s (1/A)", "Intensity (a.u)") elif self.plotmode == 0: bgsub = [ self.data[val][2][i] - self.data[val][3][i] for i in range(len(self.data[val][2])) ] self.guiplot.set_data((s, bgsub), "fg-bg", True, True) fit = ctf.compute_1d(len(s) * 2, ds, Ctf.CtfType.CTF_AMP) # The fit curve sf = sfact(s, "ribosome", "nono") fit = [sf[i] * fit[i]**2 for i in range(len(s)) ] # squared * a generic structure factor # auto-amplitude for b-factor adjustment rto, nrto = 0, 0 for i in range(int(.04 / ds) + 1, min(int(0.15 / ds), len(s) - 1)): if bgsub[i] > 0: #rto+=fit[i]**2/fabs(bgsub[i]) #nrto+=fit[i] #rto+=fit[i]**2 #nrto+=bgsub[i]**2 rto += fit[i] nrto += fabs(bgsub[i]) if nrto == 0: rto = 1.0 else: rto /= nrto fit = [fit[i] / rto for i in range(len(s))] self.guiplot.set_data((s, fit), "fit") self.guiplot.setAxisParms("s (1/A)", "Intensity (a.u)") elif self.plotmode == 2: snr = ctf.compute_1d(len(s) * 2, ds, Ctf.CtfType.CTF_SNR) # The snr curve self.guiplot.set_data((s, snr[:len(s)]), "snr", True) self.guiplot.setAxisParms("s (1/A)", "SNR (intensity ratio)") elif self.plotmode == 3: snr = ctf.compute_1d(len(s) * 2, ds, Ctf.CtfType.CTF_SNR) # The snr curve self.guiplot.set_data((s, snr[:len(s)]), "snr", True) ssnr = ctf.compute_1d(len(s) * 2, ds, Ctf.CtfType.CTF_SNR_SMOOTH) # The fit curve self.guiplot.set_data((s, ssnr[:len(s)]), "ssnr") self.guiplot.setAxisParms("s (1/A)", "SNR (intensity ratio)") elif self.plotmode == 4: snr = ctf.compute_1d(len(s) * 2, ds, Ctf.CtfType.CTF_SNR) # The snr curve for i in range(1, len(snr)): snr[i] = snr[i] * i + snr[i - 1] # integrate SNR*s # for i in range(1,len(snr)): snr[i]/=snr[-1] # normalize for i in range(1, len(snr)): snr[i] /= len( snr ) # this way the relative quality of images can be compared self.guiplot.set_data((s, snr[:len(s)]), "snr", True) self.guiplot.setAxisParms("s (1/A)", "Integrated SNR") elif self.plotmode == 5: inten = [ fabs(i) for i in ctf.compute_1d(len(s) * 2, ds, Ctf.CtfType.CTF_AMP) ] # The snr curve self.guiplot.set_data((s, inten[:len(s)]), "single", True) all = [0 for i in inten] for st in self.data: sxprint(st) inten = [ fabs(i) for i in st[1].compute_1d( len(s) * 2, ds, Ctf.CtfType.CTF_AMP) ] for i in range(len(all)): all[i] += inten[i] self.guiplot.set_data((s, all[:len(s)]), "total") #bgsub=[self.data[val][2][i]-self.data[val][3][i] for i in range(len(self.data[val][2]))] #self.guiplot.set_data("fg-bg",(s,bgsub),True,True) #fit=[bgsub[i]/sfact(s[i]) for i in range(len(s))] # squared * a generic structure factor #self.guiplot.set_data("fit",(s,fit)) def newSet(self, val): "called when a new data set is selected from the list" self.curset = val self.sdefocus.setValue(self.data[val][1].defocus, True) self.sbfactor.setValue(self.data[val][1].bfactor, True) # self.sapix.setValue(self.data[val][1].apix) self.sampcont.setValue(self.data[val][1].ampcont, True) self.svoltage.setValue(self.data[val][1].voltage, True) self.scs.setValue(self.data[val][1].cs, True) self.guiim.set_data(self.data[val][4]) self.update_plot() def newPlotMode(self, mode): self.plotmode = mode self.update_plot() def newCTF(self): self.data[self.curset][1].defocus = self.sdefocus.value self.data[self.curset][1].bfactor = self.sbfactor.value # self.data[self.curset][1].apix=self.sapix.value self.data[self.curset][1].ampcont = self.sampcont.value self.data[self.curset][1].voltage = self.svoltage.value self.data[self.curset][1].cs = self.scs.value self.update_plot() def imgmousedown(self, event): m = self.guiim.scr_to_img((event.x(), event.y())) #self.guiim.add_shape("cen",["rect",.9,.9,.4,x0,y0,x0+2,y0+2,1.0]) def imgmousedrag(self, event): m = self.guiim.scr_to_img((event.x(), event.y())) # box deletion when shift held down #if event.modifiers()&Qt.ShiftModifier: #for i,j in enumerate(self.boxes): def imgmouseup(self, event): m = self.guiim.scr_to_img((event.x(), event.y())) def plotmousedown(self, event): m = self.guiim.scr_to_img((event.x(), event.y())) def run(self): """If you make your own application outside of this object, you are free to use your own local app.exec_(). This is a convenience for ctf-only programs.""" self.app.exec_() # E2saveappwin("boxer","imagegeom",self.guiim) # try: # E2setappval("boxer","imcontrol",self.guiim.inspector.isVisible()) # if self.guiim.inspector.isVisible() : E2saveappwin("boxer","imcontrolgeom",self.guiim.inspector) # except : E2setappval("boxer","imcontrol",False) return
class EMTomoBoxer(QtGui.QMainWindow): """This class represents the EMTomoBoxer application instance. """ keypress = QtCore.pyqtSignal(QtGui.QKeyEvent) module_closed = QtCore.pyqtSignal() def __init__(self,application,options,datafile): QtGui.QWidget.__init__(self) self.initialized=False self.app=weakref.ref(application) self.options=options self.yshort=False self.apix=options.apix self.currentset=0 self.shrink=1#options.shrink self.setWindowTitle("Main Window (e2spt_boxer.py)") if options.mode=="3D": self.boxshape="circle" else: self.boxshape="rect" # Menu Bar self.mfile=self.menuBar().addMenu("File") self.mfile_open=self.mfile.addAction("Open") self.mfile_read_boxloc=self.mfile.addAction("Read Box Coord") self.mfile_save_boxloc=self.mfile.addAction("Save Box Coord") self.mfile_save_boxes_stack=self.mfile.addAction("Save Boxes as Stack") self.mfile_quit=self.mfile.addAction("Quit") self.setCentralWidget(QtGui.QWidget()) self.gbl = QtGui.QGridLayout(self.centralWidget()) # relative stretch factors self.gbl.setColumnStretch(0,1) self.gbl.setColumnStretch(1,4) self.gbl.setColumnStretch(2,0) self.gbl.setRowStretch(1,1) self.gbl.setRowStretch(0,4) # 3 orthogonal restricted projection views self.xyview = EMImage2DWidget() self.gbl.addWidget(self.xyview,0,1) self.xzview = EMImage2DWidget() self.gbl.addWidget(self.xzview,1,1) self.zyview = EMImage2DWidget() self.gbl.addWidget(self.zyview,0,0) # Select Z for xy view self.wdepth = QtGui.QSlider() self.gbl.addWidget(self.wdepth,1,2) ### Control panel area in upper left corner self.gbl2 = QtGui.QGridLayout() self.gbl.addLayout(self.gbl2,1,0) #self.wxpos = QtGui.QSlider(Qt.Horizontal) #self.gbl2.addWidget(self.wxpos,0,0) #self.wypos = QtGui.QSlider(Qt.Vertical) #self.gbl2.addWidget(self.wypos,0,3,6,1) # box size self.wboxsize=ValBox(label="Box Size:",value=0) self.gbl2.addWidget(self.wboxsize,2,0,1,2) # max or mean #self.wmaxmean=QtGui.QPushButton("MaxProj") #self.wmaxmean.setCheckable(True) #self.gbl2.addWidget(self.wmaxmean,3,0) # number slices self.wnlayers=QtGui.QSpinBox() self.wnlayers.setMinimum(1) self.wnlayers.setMaximum(256) self.wnlayers.setValue(1) self.gbl2.addWidget(self.wnlayers,3,1) # Local boxes in side view self.wlocalbox=QtGui.QCheckBox("Limit Side Boxes") self.gbl2.addWidget(self.wlocalbox,3,0) self.wlocalbox.setChecked(True) # scale factor self.wscale=ValSlider(rng=(.1,2),label="Sca:",value=1.0) self.gbl2.addWidget(self.wscale,4,0,1,2) # 2-D filters self.wfilt = ValSlider(rng=(0,150),label="Filt:",value=0.0) self.gbl2.addWidget(self.wfilt,5,0,1,2) self.curbox=-1 self.boxes=[] # array of box info, each is (x,y,z,...) self.boxesimgs=[] # z projection of each box self.xydown=self.xzdown=self.zydown=None self.firsthbclick = None # coordinate display self.wcoords=QtGui.QLabel("X: " + str(self.get_x()) + "\t\t" + "Y: " + str(self.get_y()) + "\t\t" + "Z: " + str(self.get_z())) self.gbl2.addWidget(self.wcoords, 1, 0, 1, 2) # file menu self.mfile_open.triggered[bool].connect(self.menu_file_open) self.mfile_read_boxloc.triggered[bool].connect(self.menu_file_read_boxloc) self.mfile_save_boxloc.triggered[bool].connect(self.menu_file_save_boxloc) self.mfile_save_boxes_stack.triggered[bool].connect(self.save_boxes) self.mfile_quit.triggered[bool].connect(self.menu_file_quit) # all other widgets self.wdepth.valueChanged[int].connect(self.event_depth) self.wnlayers.valueChanged[int].connect(self.event_nlayers) self.wboxsize.valueChanged.connect(self.event_boxsize) #self.wmaxmean.clicked[bool].connect(self.event_projmode) self.wscale.valueChanged.connect(self.event_scale) self.wfilt.valueChanged.connect(self.event_filter) self.wlocalbox.stateChanged[int].connect(self.event_localbox) self.xyview.mousemove.connect(self.xy_move) self.xyview.mousedown.connect(self.xy_down) self.xyview.mousedrag.connect(self.xy_drag) self.xyview.mouseup.connect(self.xy_up) self.xyview.mousewheel.connect(self.xy_wheel) self.xyview.signal_set_scale.connect(self.xy_scale) self.xyview.origin_update.connect(self.xy_origin) self.xzview.mousedown.connect(self.xz_down) self.xzview.mousedrag.connect(self.xz_drag) self.xzview.mouseup.connect(self.xz_up) self.xzview.signal_set_scale.connect(self.xz_scale) self.xzview.origin_update.connect(self.xz_origin) self.zyview.mousedown.connect(self.zy_down) self.zyview.mousedrag.connect(self.zy_drag) self.zyview.mouseup.connect(self.zy_up) self.zyview.signal_set_scale.connect(self.zy_scale) self.zyview.origin_update.connect(self.zy_origin) self.xyview.keypress.connect(self.key_press) self.datafilename=datafile self.basename=base_name(datafile) p0=datafile.find('__') if p0>0: p1=datafile.rfind('.') self.filetag=datafile[p0:p1] if self.filetag[-1]!='_': self.filetag+='_' else: self.filetag="__" data=EMData(datafile) self.set_data(data) # Boxviewer subwidget (details of a single box) self.boxviewer=EMBoxViewer() #self.app().attach_child(self.boxviewer) # Boxes Viewer (z projections of all boxes) self.boxesviewer=EMImageMXWidget() #self.app().attach_child(self.boxesviewer) self.boxesviewer.show() self.boxesviewer.set_mouse_mode("App") self.boxesviewer.setWindowTitle("Particle List") self.boxesviewer.rzonce=True self.setspanel=EMTomoSetsPanel(self) self.optionviewer=EMTomoBoxerOptions(self) self.optionviewer.add_panel(self.setspanel,"Sets") self.optionviewer.show() # Average viewer shows results of background tomographic processing # self.averageviewer=EMAverageViewer(self) #self.averageviewer.show() self.boxesviewer.mx_image_selected.connect(self.img_selected) self.jsonfile=info_name(datafile) info=js_open_dict(self.jsonfile) self.sets={} self.boxsize={} if "class_list" in info: clslst=info["class_list"] for k in sorted(clslst.keys()): if type(clslst[k])==dict: self.sets[int(k)]=str(clslst[k]["name"]) self.boxsize[int(k)]=int(clslst[k]["boxsize"]) else: self.sets[int(k)]=str(clslst[k]) self.boxsize[int(k)]=boxsize clr=QtGui.QColor self.setcolors=[clr("blue"),clr("green"),clr("red"),clr("cyan"),clr("purple"),clr("orange"), clr("yellow"),clr("hotpink"),clr("gold")] self.sets_visible={} if "boxes_3d" in info: box=info["boxes_3d"] for i,b in enumerate(box): #### X-center,Y-center,Z-center,method,[score,[class #]] bdf=[0,0,0,"manual",0.0, 0] for j,bi in enumerate(b): bdf[j]=bi if bdf[5] not in list(self.sets.keys()): clsi=int(bdf[5]) self.sets[clsi]="particles_{:02d}".format(clsi) self.boxsize[clsi]=boxsize self.boxes.append(bdf) ###### this is the new (2018-09) metadata standard.. ### now we use coordinates at full size from center of tomogram so it works for different binning and clipping ### have to make it compatible with older versions though.. if "apix_unbin" in info: self.apix_unbin=info["apix_unbin"] self.apix_cur=apix=data["apix_x"] for b in self.boxes: b[0]=b[0]/apix*self.apix_unbin+data["nx"]//2 b[1]=b[1]/apix*self.apix_unbin+data["ny"]//2 b[2]=b[2]/apix*self.apix_unbin+data["nz"]//2 for k in self.boxsize.keys(): self.boxsize[k]=self.boxsize[k]/apix*self.apix_unbin else: self.apix_unbin=-1 info.close() if len(self.sets)==0: self.new_set("particles_00") self.sets_visible[list(self.sets.keys())[0]]=0 self.currentset=sorted(self.sets.keys())[0] self.setspanel.update_sets() self.wboxsize.setValue(self.get_boxsize()) print(self.sets) for i in range(len(self.boxes)): self.update_box(i) self.update_all() self.initialized=True def set_data(self,data): self.data=data self.apix=data["apix_x"] self.datasize=(data["nx"],data["ny"],data["nz"]) self.wdepth.setRange(0,self.datasize[2]-1) self.boxes=[] self.curbox=-1 self.wdepth.setValue(old_div(self.datasize[2],2)) if self.initialized: self.update_all() def eraser_width(self): return int(self.optionviewer.eraser_radius.getValue()) def get_cube(self,x,y,z, centerslice=False, boxsz=-1): """Returns a box-sized cube at the given center location""" if boxsz<0: bs=self.get_boxsize() else: bs=boxsz if centerslice: bz=1 else: bz=bs if ((x<-bs//2) or (y<-bs//2) or (z<-bz//2) or (x>self.data["nx"]+bs//2) or (y>self.data["ny"]+bs//2) or (z>self.data["nz"]+bz//2) ): r=EMData(bs,bs,bz) else: r=self.data.get_clip(Region(x-bs//2,y-bs//2,z-bz//2,bs,bs,bz)) if self.apix!=0 : r["apix_x"]=self.apix r["apix_y"]=self.apix r["apix_z"]=self.apix #if options.normproc: #r.process_inplace(options.normproc) return r def get_slice(self,n,xyz): """Reads a slice either from a file or the preloaded memory array. xyz is the axis along which 'n' runs, 0=x (yz), 1=y (xz), 2=z (xy)""" if xyz==0: r=self.data.get_clip(Region(n,0,0,1,self.datasize[1],self.datasize[2])) r.set_size(self.datasize[1],self.datasize[2],1) elif xyz==1: r=self.data.get_clip(Region(0,n,0,self.datasize[0],1,self.datasize[2])) r.set_size(self.datasize[0],self.datasize[2],1) else: r=self.data.get_clip(Region(0,0,n,self.datasize[0],self.datasize[1],1)) if self.apix!=0 : r["apix_x"]=self.apix r["apix_y"]=self.apix r["apix_z"]=self.apix return r def event_boxsize(self): if self.get_boxsize()==int(self.wboxsize.getValue()): return self.boxsize[self.currentset]=int(self.wboxsize.getValue()) cb=self.curbox self.initialized=False for i in range(len(self.boxes)): if self.boxes[i][5]==self.currentset: self.update_box(i) self.update_box(cb) self.initialized=True self.update_all() def event_projmode(self,state): """Projection mode can be simple average (state=False) or maximum projection (state=True)""" self.update_all() def event_scale(self,newscale): self.xyview.set_scale(newscale) self.xzview.set_scale(newscale) self.zyview.set_scale(newscale) def event_depth(self): if self.initialized: self.update_xy() def event_nlayers(self): self.update_all() def event_filter(self): self.update_all() def event_localbox(self,tog): self.update_sides() def get_boxsize(self, clsid=-1): if clsid<0: return int(self.boxsize[self.currentset]) else: try: ret= int(self.boxsize[clsid]) except: print("No box size saved for {}..".format(clsid)) ret=32 return ret def nlayers(self): return int(self.wnlayers.value()) def depth(self): return int(self.wdepth.value()) def scale(self): return self.wscale.getValue() def get_x(self): return self.get_coord(0) def get_y(self): return self.get_coord(1) def get_z(self): return self.depth() def get_coord(self, coord_index): if len(self.boxes) > 1: if self.curbox: return self.boxes[self.curbox][coord_index] else: return self.boxes[-1][coord_index] else: return 0 def menu_file_open(self,tog): QtGui.QMessageBox.warning(None,"Error","Sorry, in the current version, you must provide a file to open on the command-line.") def load_box_yshort(self, boxcoords): if options.yshort: return [boxcoords[0], boxcoords[2], boxcoords[1]] else: return boxcoords def menu_file_read_boxloc(self): fsp=str(QtGui.QFileDialog.getOpenFileName(self, "Select output text file")) f=file(fsp,"r") for b in f: b2=[old_div(int(float(i)),self.shrink) for i in b.split()[:3]] bdf=[0,0,0,"manual",0.0, self.currentset] for j in range(len(b2)): bdf[j]=b2[j] self.boxes.append(bdf) self.update_box(len(self.boxes)-1) f.close() def menu_file_save_boxloc(self): shrinkf=self.shrink #jesus fsp=str(QtGui.QFileDialog.getSaveFileName(self, "Select output text file")) out=file(fsp,"w") for b in self.boxes: out.write("%d\t%d\t%d\n"%(b[0]*shrinkf,b[1]*shrinkf,b[2]*shrinkf)) out.close() def save_boxes(self, clsid=[]): if len(clsid)==0: defaultname="ptcls.hdf" else: defaultname="_".join([self.sets[i] for i in clsid])+".hdf" name,ok=QtGui.QInputDialog.getText( self, "Save particles", "Filename suffix:", text=defaultname) if not ok: return name=self.filetag+str(name) if name[-4:].lower()!=".hdf" : name+=".hdf" if self.options.mode=="3D": dr="particles3d" is2d=False else: dr="particles" is2d=True if not os.path.isdir(dr): os.mkdir(dr) fsp=os.path.join(dr,self.basename)+name print("Saving {} particles to {}".format(self.options.mode, fsp)) if os.path.isfile(fsp): print("{} exist. Overwritting...".format(fsp)) os.remove(fsp) progress = QtGui.QProgressDialog("Saving", "Abort", 0, len(self.boxes),None) boxsz=-1 for i,b in enumerate(self.boxes): if len(clsid)>0: if int(b[5]) not in clsid: continue #img=self.get_cube(b[0],b[1],b[2]) bs=self.get_boxsize(b[5]) if boxsz<0: boxsz=bs else: if boxsz!=bs: print("Inconsistant box size in the particles to save.. Using {:d}..".format(boxsz)) bs=boxsz sz=[s//2 for s in self.datasize] img=self.get_cube(b[0], b[1], b[2], centerslice=is2d, boxsz=bs) if is2d==False: img.process_inplace('normalize') img["ptcl_source_image"]=self.datafilename img["ptcl_source_coord"]=(b[0]-sz[0], b[1]-sz[1], b[2]-sz[2]) if is2d==False: #### do not invert contrast for 2D images img.mult(-1) img.write_image(fsp,-1) progress.setValue(i+1) if progress.wasCanceled(): break def menu_file_quit(self): self.close() def transform_coords(self, point, xform): xvec = xform.get_matrix() return [xvec[0]*point[0] + xvec[4]*point[1] + xvec[8]*point[2] + xvec[3], xvec[1]*point[0] + xvec[5]*point[1] + xvec[9]*point[2] + xvec[7], xvec[2]*point[0] + xvec[6]*point[1] + xvec[10]*point[2] + xvec[11]] def get_averager(self): """returns an averager of the appropriate type for generating projection views""" #if self.wmaxmean.isChecked() : return Averagers.get("minmax",{"max":1}) return Averagers.get("mean") def update_sides(self): """updates xz and yz views due to a new center location""" #print "\n\n\n\n\nIn update sides, self.datafile is", self.datafile #print "\n\n\n\n" if self.data==None: return if self.curbox==-1 : x=self.datasize[0]//2 y=self.datasize[1]//2 z=0 else: x,y,z=self.boxes[self.curbox][:3] self.cury=y self.curx=x # update shape display if self.wlocalbox.isChecked(): xzs=self.xzview.get_shapes() for i in range(len(self.boxes)): bs=self.get_boxsize(self.boxes[i][5]) if self.boxes[i][1]<self.cury+old_div(bs,2) and self.boxes[i][1]>self.cury-old_div(bs,2) and self.boxes[i][5] in self.sets_visible: xzs[i][0]=self.boxshape else: xzs[i][0]="hidden" zys=self.zyview.get_shapes() for i in range(len(self.boxes)): bs=self.get_boxsize(self.boxes[i][5]) if self.boxes[i][0]<self.curx+old_div(bs,2) and self.boxes[i][0]>self.curx-old_div(bs,2) and self.boxes[i][5] in self.sets_visible: zys[i][0]=self.boxshape else: zys[i][0]="hidden" else : xzs=self.xzview.get_shapes() zys=self.zyview.get_shapes() for i in range(len(self.boxes)): bs=self.get_boxsize(self.boxes[i][5]) if self.boxes[i][5] in self.sets_visible: xzs[i][0]=self.boxshape zys[i][0]=self.boxshape else: xzs[i][0]="hidden" zys[i][0]="hidden" self.xzview.shapechange=1 self.zyview.shapechange=1 # yz avgr=self.get_averager() for x in range(x-(self.nlayers()//2),x+((self.nlayers()+1)//2)): slc=self.get_slice(x,0) avgr.add_image(slc) av=avgr.finish() if not self.yshort: av.process_inplace("xform.transpose") if self.wfilt.getValue()!=0.0: av.process_inplace("filter.lowpass.gauss",{"cutoff_freq":old_div(1.0,self.wfilt.getValue()),"apix":self.apix}) self.zyview.set_data(av) # xz avgr=self.get_averager() for y in range(y-old_div(self.nlayers(),2),y+old_div((self.nlayers()+1),2)): slc=self.get_slice(y,1) avgr.add_image(slc) av=avgr.finish() if self.wfilt.getValue()!=0.0: av.process_inplace("filter.lowpass.gauss",{"cutoff_freq":old_div(1.0,self.wfilt.getValue()),"apix":self.apix}) self.xzview.set_data(av) def update_xy(self): """updates xy view due to a new slice range""" #print "\n\n\n\n\nIn update_xy, self.datafile is", self.datafile #print "\n\n\n\n" if self.data==None: return # Boxes should also be limited by default in the XY view if len(self.boxes) > 0: zc=self.wdepth.value() #print "The current depth is", self.wdepth.value() xys=self.xyview.get_shapes() for i in range(len(self.boxes)): bs=self.get_boxsize(self.boxes[i][5]) zdist=abs(self.boxes[i][2] - zc) if self.options.mode=="3D": zthr=bs/2 xys[i][6]=bs//2-zdist else: zthr=1 if zdist < zthr and self.boxes[i][5] in self.sets_visible: xys[i][0]=self.boxshape else : xys[i][0]="hidden" self.xyview.shapechange=1 #if self.wmaxmean.isChecked(): #avgr=Averagers.get("minmax",{"max":1}) #else: avgr=Averagers.get("mean") slc=EMData() for z in range(self.wdepth.value()-self.nlayers()//2,self.wdepth.value()+(self.nlayers()+1)//2): slc=self.get_slice(z,2) avgr.add_image(slc) av=avgr.finish() #print "\n\nIn update xy, av and type are", av, type(av) if self.wfilt.getValue()!=0.0: av.process_inplace("filter.lowpass.gauss",{"cutoff_freq":old_div(1.0,self.wfilt.getValue()),"apix":self.apix}) if self.initialized: self.xyview.set_data(av, keepcontrast=True) else: self.xyview.set_data(av) def update_all(self): """redisplay of all widgets""" #print "\n\n\n\n\nIn update all, self.datafile is", self.datafile #print "\n\n\n\n" if self.data==None: return self.update_xy() self.update_sides() self.update_boximgs() def update_coords(self): self.wcoords.setText("X: " + str(self.get_x()) + "\t\t" + "Y: " + str(self.get_y()) + "\t\t" + "Z: " + str(self.get_z())) def inside_box(self,n,x=-1,y=-1,z=-1): """Checks to see if a point in image coordinates is inside box number n. If any value is negative, it will not be checked.""" box=self.boxes[n] if box[5] not in self.sets_visible: return False bs=self.get_boxsize(box[5])/2 if self.options.mode=="3D": rr=(x>=0)*((box[0]-x)**2) + (y>=0)*((box[1]-y) **2) + (z>=0)*((box[2]-z)**2) else: rr=(x>=0)*((box[0]-x)**2) + (y>=0)*((box[1]-y) **2) + (z>=0)*(box[2]!=z)*(1e3*bs**2) return rr<=bs**2 def do_deletion(self, delids): kpids=[i for i,b in enumerate(self.boxes) if i not in delids] self.boxes=[self.boxes[i] for i in kpids] self.boxesimgs=[self.boxesimgs[i] for i in kpids] self.xyview.shapes={i:self.xyview.shapes[k] for i,k in enumerate(kpids)} self.xzview.shapes={i:self.xzview.shapes[k] for i,k in enumerate(kpids)} self.zyview.shapes={i:self.zyview.shapes[k] for i,k in enumerate(kpids)} #print self.boxes, self.xyview.get_shapes() self.curbox=-1 self.update_all() def del_box(self,n): """Delete an existing box by replacing the deleted box with the last box. A bit funny, but otherwise update after deletion is REALLY slow.""" # print "del ",n if n<0 or n>=len(self.boxes): return if self.boxviewer.get_data(): self.boxviewer.set_data(None) self.curbox=-1 self.do_deletion([n]) def update_box(self,n,quiet=False): """After adjusting a box, call this""" # print "upd ",n,quiet try: box=self.boxes[n] except IndexError: return bs2=self.get_boxsize(box[5])//2 color=self.setcolors[box[5]%len(self.setcolors)].getRgbF() if self.options.mode=="3D": self.xyview.add_shape(n,EMShape(["circle",color[0],color[1],color[2],box[0],box[1],bs2,2])) self.xzview.add_shape(n,EMShape(["circle",color[0],color[1],color[2],box[0],box[2],bs2,2])) self.zyview.add_shape(n,EMShape(("circle",color[0],color[1],color[2],box[2],box[1],bs2,2))) else: self.xyview.add_shape(n,EMShape(["rect",color[0],color[1],color[2], box[0]-bs2,box[1]-bs2,box[0]+bs2,box[1]+bs2,2])) self.xzview.add_shape(n,EMShape(["rect",color[0],color[1],color[2], box[0]-bs2,box[2]-1,box[0]+bs2,box[2]+1,2])) self.zyview.add_shape(n,EMShape(["rect",color[0],color[1],color[2], box[2]-1,box[1]-bs2,box[2]+1,box[1]+bs2,2])) if self.depth()!=box[2]: self.wdepth.setValue(box[2]) else: self.xyview.update() if self.initialized: self.update_sides() # For speed, we turn off updates while dragging a box around. Quiet is set until the mouse-up if not quiet: # Get the cube from the original data (normalized) proj=self.get_cube(box[0], box[1], box[2], centerslice=True, boxsz=self.get_boxsize(box[5])) proj.process_inplace("normalize") for i in range(len(self.boxesimgs),n+1): self.boxesimgs.append(None) self.boxesimgs[n]=proj mm=[m for im,m in enumerate(self.boxesimgs) if self.boxes[im][5] in self.sets_visible] if self.initialized: self.SaveJson() if self.initialized: self.update_boximgs() if n!=self.curbox: self.boxesviewer.set_selected((n,),True) self.curbox=n self.update_coords() def update_boximgs(self): self.boxids=[im for im,m in enumerate(self.boxesimgs) if self.boxes[im][5] in self.sets_visible] self.boxesviewer.set_data([self.boxesimgs[i] for i in self.boxids]) self.boxesviewer.update() return def img_selected(self,event,lc): #print "sel",lc[0] lci=self.boxids[lc[0]] if event.modifiers()&Qt.ShiftModifier: self.del_box(lci) else: self.update_box(lci) if self.curbox>=0 : box=self.boxes[self.curbox] self.xyview.scroll_to(box[0],box[1]) self.xzview.scroll_to(None,box[2]) self.zyview.scroll_to(box[2],None) self.currentset=box[5] self.setspanel.initialized=False self.setspanel.update_sets() def del_region_xy(self, x=-1, y=-1, z=-1, rad=-1): if rad<0: rad=self.eraser_width() delids=[] for i,b in enumerate(self.boxes): if b[5] not in self.sets_visible: continue if (x>=0)*(b[0]-x)**2 + (y>=0)*(b[1]-y)**2 +(z>=0)*(b[2]-z)**2 < rad**2: delids.append(i) self.do_deletion(delids) def xy_down(self,event): x,y=self.xyview.scr_to_img((event.x(),event.y())) x,y=int(x),int(y) z=int(self.get_z()) self.xydown=None if x<0 or y<0 : return # no clicking outside the image (on 2 sides) if self.optionviewer.erasercheckbox.isChecked(): self.del_region_xy(x,y) return for i in range(len(self.boxes)): if self.inside_box(i,x,y,z): if event.modifiers()&Qt.ShiftModifier: self.del_box(i) self.firsthbclick = None else: self.xydown=(i,x,y,self.boxes[i][0],self.boxes[i][1]) self.update_box(i) break else: # if x>self.get_boxsize()/2 and x<self.datasize[0]-self.get_boxsize()/2 and y>self.get_boxsize()/2 and y<self.datasize[1]-self.get_boxsize()/2 and self.depth()>self.get_boxsize()/2 and self.depth()<self.datasize[2]-self.get_boxsize()/2 : if not event.modifiers()&Qt.ShiftModifier: self.boxes.append(([x,y,self.depth(), 'manual', 0.0, self.currentset])) self.xydown=(len(self.boxes)-1,x,y,x,y) # box #, x down, y down, x box at down, y box at down self.update_box(self.xydown[0]) if self.curbox>=0: box=self.boxes[self.curbox] self.xzview.scroll_to(None,box[2]) self.zyview.scroll_to(box[2],None) def xy_drag(self,event): x,y=self.xyview.scr_to_img((event.x(),event.y())) x,y=int(x),int(y) if self.optionviewer.erasercheckbox.isChecked(): self.del_region_xy(x,y) self.xyview.eraser_shape=EMShape(["circle",1,1,1,x,y,self.eraser_width(),2]) self.xyview.shapechange=1 self.xyview.update() return if self.xydown==None : return dx=x-self.xydown[1] dy=y-self.xydown[2] self.boxes[self.xydown[0]][0]=dx+self.xydown[3] self.boxes[self.xydown[0]][1]=dy+self.xydown[4] self.update_box(self.curbox,True) def xy_up (self,event): if self.xydown!=None: self.update_box(self.curbox) self.xydown=None def xy_wheel (self,event): if event.delta() > 0: #self.wdepth.setValue(self.wdepth.value()+4) self.wdepth.setValue(self.wdepth.value()+1) #jesus elif event.delta() < 0: #self.wdepth.setValue(self.wdepth.value()-4) self.wdepth.setValue(self.wdepth.value()-1) #jesus def xy_scale(self,news): "xy image view has been rescaled" self.wscale.setValue(news) #self.xzview.set_scale(news,True) #self.zyview.set_scale(news,True) def xy_origin(self,newor): "xy origin change" xzo=self.xzview.get_origin() self.xzview.set_origin(newor[0],xzo[1],True) zyo=self.zyview.get_origin() self.zyview.set_origin(zyo[0],newor[1],True) def xy_move(self,event): if self.optionviewer.erasercheckbox.isChecked(): x,y=self.xyview.scr_to_img((event.x(),event.y())) #print x,y self.xyview.eraser_shape=EMShape(["circle",1,1,1,x,y,self.eraser_width(),2]) self.xyview.shapechange=1 self.xyview.update() else: self.xyview.eraser_shape=None def xz_down(self,event): x,z=self.xzview.scr_to_img((event.x(),event.y())) x,z=int(x),int(z) y=int(self.get_y()) self.xzdown=None if x<0 or z<0 : return # no clicking outside the image (on 2 sides) if self.optionviewer.erasercheckbox.isChecked(): return for i in range(len(self.boxes)): if (not self.wlocalbox.isChecked() and self.inside_box(i,x,y,z)) or self.inside_box(i,x,self.cury,z) : if event.modifiers()&Qt.ShiftModifier: self.del_box(i) self.firsthbclick = None else : self.xzdown=(i,x,z,self.boxes[i][0],self.boxes[i][2]) self.update_box(i) break else: if not event.modifiers()&Qt.ShiftModifier: self.boxes.append(([x,self.cury,z, 'manual', 0.0, self.currentset])) self.xzdown=(len(self.boxes)-1,x,z,x,z) # box #, x down, y down, x box at down, y box at down self.update_box(self.xzdown[0]) if self.curbox>=0 : box=self.boxes[self.curbox] self.xyview.scroll_to(None,box[1]) self.zyview.scroll_to(box[2],None) def xz_drag(self,event): if self.xzdown==None : return x,z=self.xzview.scr_to_img((event.x(),event.y())) x,z=int(x),int(z) dx=x-self.xzdown[1] dz=z-self.xzdown[2] self.boxes[self.xzdown[0]][0]=dx+self.xzdown[3] self.boxes[self.xzdown[0]][2]=dz+self.xzdown[4] self.update_box(self.curbox,True) def xz_up (self,event): if self.xzdown!=None: self.update_box(self.curbox) self.xzdown=None def xz_scale(self,news): "xy image view has been rescaled" self.wscale.setValue(news) #self.xyview.set_scale(news,True) #self.zyview.set_scale(news,True) def xz_origin(self,newor): "xy origin change" xyo=self.xyview.get_origin() self.xyview.set_origin(newor[0],xyo[1],True) #zyo=self.zyview.get_origin() #self.zyview.set_origin(zyo[0],newor[1],True) def zy_down(self,event): z,y=self.zyview.scr_to_img((event.x(),event.y())) z,y=int(z),int(y) x=int(self.get_x()) self.xydown=None if z<0 or y<0 : return # no clicking outside the image (on 2 sides) for i in range(len(self.boxes)): if (not self.wlocalbox.isChecked() and self.inside_box(i,x,y,z)) or self.inside_box(i,self.curx,y,z): if event.modifiers()&Qt.ShiftModifier: self.del_box(i) self.firsthbclick = None else : self.zydown=(i,z,y,self.boxes[i][2],self.boxes[i][1]) self.update_box(i) break else: if not event.modifiers()&Qt.ShiftModifier: ########### self.boxes.append(([self.curx,y,z, 'manual', 0.0, self.currentset])) self.zydown=(len(self.boxes)-1,z,y,z,y) # box #, x down, y down, x box at down, y box at down self.update_box(self.zydown[0]) if self.curbox>=0 : box=self.boxes[self.curbox] self.xyview.scroll_to(box[0],None) self.xzview.scroll_to(None,box[2]) def zy_drag(self,event): if self.zydown==None : return z,y=self.zyview.scr_to_img((event.x(),event.y())) z,y=int(z),int(y) dz=z-self.zydown[1] dy=y-self.zydown[2] self.boxes[self.zydown[0]][2]=dz+self.zydown[3] self.boxes[self.zydown[0]][1]=dy+self.zydown[4] self.update_box(self.curbox,True) def zy_up (self,event): if self.zydown!=None: self.update_box(self.curbox) self.zydown=None def zy_scale(self,news): "xy image view has been rescaled" self.wscale.setValue(news) #self.xyview.set_scale(news,True) #self.xzview.set_scale(news,True) def zy_origin(self,newor): "xy origin change" xyo=self.xyview.get_origin() self.xyview.set_origin(xyo[0],newor[1],True) #xzo=self.xzview.get_origin() #self.xzview.set_origin(xzo[0],newor[1],True) def set_current_set(self, name): #print "set current", name name=parse_setname(name) self.currentset=name self.wboxsize.setValue(self.get_boxsize()) self.update_all() return def hide_set(self, name): name=parse_setname(name) if name in self.sets_visible: self.sets_visible.pop(name) if self.initialized: self.update_all() self.update_boximgs() return def show_set(self, name): name=parse_setname(name) self.sets_visible[name]=0 #self.currentset=name self.wboxsize.setValue(self.get_boxsize()) if self.initialized: self.update_all() self.update_boximgs() return def delete_set(self, name): name=parse_setname(name) ## idx to keep delids=[i for i,b in enumerate(self.boxes) if b[5]==int(name)] self.do_deletion(delids) if name in self.sets_visible: self.sets_visible.pop(name) if name in self.sets: self.sets.pop(name) if name in self.boxsize: self.boxsize.pop(name) self.curbox=-1 self.update_all() return def rename_set(self, oldname, newname): name=parse_setname(oldname) if name in self.sets: self.sets[name]=newname return def new_set(self, name): for i in range(len(self.sets)+1): if i not in self.sets: break self.sets[i]=name self.sets_visible[i]=0 if self.options.mode=="3D": self.boxsize[i]=32 else: self.boxsize[i]=64 return def save_set(self): self.save_boxes(list(self.sets_visible.keys())) return def key_press(self,event): if event.key() == 96: self.wdepth.setValue(self.wdepth.value()+1) elif event.key() == 49: self.wdepth.setValue(self.wdepth.value()-1) else: self.keypress.emit(event) def SaveJson(self): info=js_open_dict(self.jsonfile) sx,sy,sz=(self.data["nx"]//2,self.data["ny"]//2,self.data["nz"]//2) if "apix_unbin" in info: bxs=[] for b0 in self.boxes: b=[ (b0[0]-sx)*self.apix_cur/self.apix_unbin, (b0[1]-sy)*self.apix_cur/self.apix_unbin, (b0[2]-sz)*self.apix_cur/self.apix_unbin, b0[3], b0[4], b0[5] ] bxs.append(b) bxsz={} for k in self.boxsize.keys(): bxsz[k]=self.boxsize[k]*self.apix_cur/self.apix_unbin else: bxs=self.boxes bxsz=self.boxsize info["boxes_3d"]=bxs clslst={} for key in list(self.sets.keys()): clslst[int(key)]={ "name":self.sets[key], "boxsize":int(bxsz[key]), } info["class_list"]=clslst info.close() def closeEvent(self,event): print("Exiting") self.SaveJson() self.boxviewer.close() self.boxesviewer.close() self.optionviewer.close() self.xyview.close() self.xzview.close() self.zyview.close() self.module_closed.emit() # this signal is important when e2ctf is being used by a program running its own event loop
class EMHelloWorldInspector(QtWidgets.QWidget): def __init__(self, target): QtWidgets.QWidget.__init__(self, None) self.target = target self.vbl = QtWidgets.QVBoxLayout(self) self.vbl.setContentsMargins(0, 0, 0, 0) self.vbl.setSpacing(6) self.vbl.setObjectName("vbl") self.hbl = QtWidgets.QHBoxLayout() self.hbl.setContentsMargins(0, 0, 0, 0) self.hbl.setSpacing(6) self.hbl.setObjectName("hbl") self.vbl.addLayout(self.hbl) self.vbl2 = QtWidgets.QVBoxLayout() self.vbl2.setContentsMargins(0, 0, 0, 0) self.vbl2.setSpacing(6) self.vbl2.setObjectName("vbl2") self.hbl.addLayout(self.vbl2) self.wiretog = QtWidgets.QPushButton("Wire") self.wiretog.setCheckable(1) self.vbl2.addWidget(self.wiretog) self.lighttog = QtWidgets.QPushButton("Light") self.lighttog.setCheckable(1) self.vbl2.addWidget(self.lighttog) self.tabwidget = QtWidgets.QTabWidget() self.maintab = None self.tabwidget.addTab(self.get_main_tab(), "Main") self.tabwidget.addTab(self.get_GL_tab(), "GL") self.vbl.addWidget(self.tabwidget) self.n3_showing = False self.scale.valueChanged.connect(target.set_scale) self.az.valueChanged.connect(self.slider_rotate) self.alt.valueChanged.connect(self.slider_rotate) self.phi.valueChanged.connect(self.slider_rotate) self.cbb.currentIndexChanged[str].connect(target.setColor) self.src.currentIndexChanged[str].connect(self.set_src) self.x_trans.valueChanged[double].connect(target.set_cam_x) self.y_trans.valueChanged[double].connect(target.set_cam_y) self.z_trans.valueChanged[double].connect(target.set_cam_z) self.wiretog.toggled[bool].connect(target.toggle_wire) self.lighttog.toggled[bool].connect(target.toggle_light) self.glcontrast.valueChanged.connect(target.set_GL_contrast) self.glbrightness.valueChanged.connect(target.set_GL_brightness) def get_GL_tab(self): self.gltab = QtWidgets.QWidget() gltab = self.gltab gltab.vbl = QtWidgets.QVBoxLayout(self.gltab) gltab.vbl.setContentsMargins(0, 0, 0, 0) gltab.vbl.setSpacing(6) gltab.vbl.setObjectName("Main") self.glcontrast = ValSlider(gltab, (1.0, 5.0), "GLShd:") self.glcontrast.setObjectName("GLShade") self.glcontrast.setValue(1.0) gltab.vbl.addWidget(self.glcontrast) self.glbrightness = ValSlider(gltab, (-1.0, 0.0), "GLBst:") self.glbrightness.setObjectName("GLBoost") self.glbrightness.setValue(0.1) self.glbrightness.setValue(0.0) gltab.vbl.addWidget(self.glbrightness) return gltab def get_main_tab(self): if (self.maintab == None): self.maintab = QtWidgets.QWidget() maintab = self.maintab maintab.vbl = QtWidgets.QVBoxLayout(self.maintab) maintab.vbl.setContentsMargins(0, 0, 0, 0) maintab.vbl.setSpacing(6) maintab.vbl.setObjectName("Main") self.scale = ValSlider(maintab, (0.01, 30.0), "Zoom:") self.scale.setObjectName("scale") self.scale.setValue(1.0) maintab.vbl.addWidget(self.scale) self.hbl_color = QtWidgets.QHBoxLayout() self.hbl_color.setContentsMargins(0, 0, 0, 0) self.hbl_color.setSpacing(6) self.hbl_color.setObjectName("Material") maintab.vbl.addLayout(self.hbl_color) self.color_label = QtWidgets.QLabel() self.color_label.setText('Material') self.hbl_color.addWidget(self.color_label) self.cbb = QtWidgets.QComboBox(maintab) self.hbl_color.addWidget(self.cbb) self.hbl_trans = QtWidgets.QHBoxLayout() self.hbl_trans.setContentsMargins(0, 0, 0, 0) self.hbl_trans.setSpacing(6) self.hbl_trans.setObjectName("Trans") maintab.vbl.addLayout(self.hbl_trans) self.x_label = QtWidgets.QLabel() self.x_label.setText('x') self.hbl_trans.addWidget(self.x_label) self.x_trans = QtWidgets.QDoubleSpinBox(self) self.x_trans.setMinimum(-10000) self.x_trans.setMaximum(10000) self.x_trans.setValue(0.0) self.hbl_trans.addWidget(self.x_trans) self.y_label = QtWidgets.QLabel() self.y_label.setText('y') self.hbl_trans.addWidget(self.y_label) self.y_trans = QtWidgets.QDoubleSpinBox(maintab) self.y_trans.setMinimum(-10000) self.y_trans.setMaximum(10000) self.y_trans.setValue(0.0) self.hbl_trans.addWidget(self.y_trans) self.z_label = QtWidgets.QLabel() self.z_label.setText('z') self.hbl_trans.addWidget(self.z_label) self.z_trans = QtWidgets.QDoubleSpinBox(maintab) self.z_trans.setMinimum(-10000) self.z_trans.setMaximum(10000) self.z_trans.setValue(0.0) self.hbl_trans.addWidget(self.z_trans) self.hbl_src = QtWidgets.QHBoxLayout() self.hbl_src.setContentsMargins(0, 0, 0, 0) self.hbl_src.setSpacing(6) self.hbl_src.setObjectName("hbl") maintab.vbl.addLayout(self.hbl_src) self.label_src = QtWidgets.QLabel() self.label_src.setText('Rotation Convention') self.hbl_src.addWidget(self.label_src) self.src = QtWidgets.QComboBox(maintab) self.load_src_options(self.src) self.hbl_src.addWidget(self.src) # set default value -1 ensures that the val slider is updated the first time it is created self.az = ValSlider(self, (-360.0, 360.0), "az", -1) self.az.setObjectName("az") maintab.vbl.addWidget(self.az) self.alt = ValSlider(self, (-180.0, 180.0), "alt", -1) self.alt.setObjectName("alt") maintab.vbl.addWidget(self.alt) self.phi = ValSlider(self, (-360.0, 360.0), "phi", -1) self.phi.setObjectName("phi") maintab.vbl.addWidget(self.phi) self.current_src = EULER_EMAN return self.maintab def set_xy_trans(self, x, y): self.x_trans.setValue(x) self.y_trans.setValue(y) def set_xyz_trans(self, x, y, z): self.x_trans.setValue(x) self.y_trans.setValue(y) self.z_trans.setValue(z) def set_translate_scale(self, xscale, yscale, zscale): self.x_trans.setSingleStep(xscale) self.y_trans.setSingleStep(yscale) self.z_trans.setSingleStep(zscale) def update_rotations(self, t3d): convention = str(self.src.currentText()) #FIXME: Transform.get_rotation() wants a string sometimes and a EulerType other times try: rot = t3d.get_rotation(str(self.src_map[convention])) except Exception as e: #doing a quick fix print(e) print( "Developers: This catches a large range of exceptions... a better way surely exists" ) rot = t3d.get_rotation(self.src_map[convention]) if (self.src_map[convention] == EULER_SPIN): self.n3.setValue(rot[self.n3.getLabel()], True) self.az.setValue(rot[self.az.getLabel()], True) self.alt.setValue(rot[self.alt.getLabel()], True) self.phi.setValue(rot[self.phi.getLabel()], True) def slider_rotate(self): self.target.load_rotation(self.get_current_rotation()) def get_current_rotation(self): convention = self.src.currentText() rot = {} if (self.current_src == EULER_SPIN): rot[self.az.getLabel()] = self.az.getValue() n1 = self.alt.getValue() n2 = self.phi.getValue() n3 = self.n3.getValue() norm = sqrt(n1 * n1 + n2 * n2 + n3 * n3) n1 /= norm n2 /= norm n3 /= norm rot[self.alt.getLabel()] = n1 rot[self.phi.getLabel()] = n2 rot[self.n3.getLabel()] = n3 else: rot[self.az.getLabel()] = self.az.getValue() rot[self.alt.getLabel()] = self.alt.getValue() rot[self.phi.getLabel()] = self.phi.getValue() return Transform(self.current_src, rot) def set_src(self, val): t3d = self.get_current_rotation() if (self.n3_showing): self.vbl.removeWidget(self.n3) self.n3.deleteLater() self.n3_showing = False self.az.setRange(-360, 360) self.alt.setRange(-180, 180) self.phi.setRange(-360, 660) if (self.src_map[str(val)] == EULER_SPIDER): self.az.setLabel('phi') self.alt.setLabel('theta') self.phi.setLabel('psi') elif (self.src_map[str(val)] == EULER_EMAN): self.az.setLabel('az') self.alt.setLabel('alt') self.phi.setLabel('phi') elif (self.src_map[str(val)] == EULER_IMAGIC): self.az.setLabel('alpha') self.alt.setLabel('beta') self.phi.setLabel('gamma') elif (self.src_map[str(val)] == EULER_XYZ): self.az.setLabel('xtilt') self.alt.setLabel('ytilt') self.phi.setLabel('ztilt') elif (self.src_map[str(val)] == EULER_MRC): self.az.setLabel('phi') self.alt.setLabel('theta') self.phi.setLabel('omega') elif (self.src_map[str(val)] == EULER_SPIN): self.az.setLabel('omega') self.alt.setRange(-1, 1) self.phi.setRange(-1, 1) self.alt.setLabel('n1') self.phi.setLabel('n2') self.n3 = ValSlider(self, (-360.0, 360.0), "n3", -1) self.n3.setRange(-1, 1) self.n3.setObjectName("n3") self.vbl.addWidget(self.n3) self.n3.valueChanged.connect(self.slider_rotate) self.n3_showing = True self.current_src = self.src_map[str(val)] self.update_rotations(t3d) def load_src_options(self, widgit): self.load_src() for i in self.src_strings: widgit.addItem(i) # read src as 'supported rotation conventions' def load_src(self): # supported_rot_conventions src_flags = [] src_flags.append(EULER_EMAN) src_flags.append(EULER_SPIDER) src_flags.append(EULER_IMAGIC) src_flags.append(EULER_MRC) src_flags.append(EULER_SPIN) src_flags.append(EULER_XYZ) self.src_strings = [] self.src_map = {} for i in src_flags: self.src_strings.append(str(i)) self.src_map[str(i)] = i def setColors(self, colors, current_color): a = 0 for i in colors: self.cbb.addItem(i) if (i == current_color): self.cbb.setCurrentIndex(a) a += 1 def set_scale(self, newscale): self.scale.setValue(newscale)