def appendMaterial(self, item): for i in self.materiallist: if item[0] == i[0]: error = QErrorMessage(self) error.setWindowTitle("提示") error.showMessage("{}已存在,请删除原来的再添加".format(item[0])) error.show() return try: self.materiallist.append(item) self.showTable() except Exception as ret: print(ret)
def login(self): """登录函数""" acc = self.lineEdit.text() pas = self.lineEdit_2.text() if acc and pas: if CheckLogin(acc, pas).isHave(): self.login_signal.emit() return # 登录失败提示 error = QErrorMessage(self) error.setWindowTitle("提示") error.showMessage("登录失败,请正确输入后重试!") error.show()
def checkText(self): if not self.textLine.text().isalpha(): message = QErrorMessage(self) message.showMessage('Invalid Entry! Try Again.') message.setWindowTitle('Warning') message.setWindowIcon(QIcon('error.png')) else: location = self.textLine.text() url = weather_url + location global json json = requests.get(url).json() try: json['message'] except Exception: self.obj = ShowWeather() else: message = QErrorMessage(self) message.showMessage('Invalid City Name! Try Again.') message.setWindowTitle('Warning') message.setWindowIcon(QIcon('error.png'))
listener = create_listener() except socket.error: # Good singleinstance is correct (on UNIX) otherinstance = True else: # On windows only singleinstance can be trusted otherinstance = True if iswindows else False if not otherinstance and not opts.shutdown_running_calibre: return run_gui(opts, args, listener, app, gui_debug=gui_debug) communicate(opts, args) return 0 if __name__ == '__main__': try: sys.exit(main()) except Exception as err: if not iswindows: raise tb = traceback.format_exc() from PyQt5.Qt import QErrorMessage logfile = os.path.join(os.path.expanduser('~'), 'calibre.log') if os.path.exists(logfile): log = open(logfile).read().decode('utf-8', 'ignore') d = QErrorMessage() d.showMessage(('<b>Error:</b>%s<br><b>Traceback:</b><br>' '%s<b>Log:</b><br>%s')%(unicode(err), unicode(tb).replace('\n', '<br>'), log.replace('\n', '<br>')))
otherinstance = True else: # On windows only singleinstance can be trusted otherinstance = True if iswindows else False if not otherinstance and not opts.shutdown_running_calibre: return run_gui(opts, args, listener, app, gui_debug=gui_debug) communicate(opts, args) return 0 if __name__ == '__main__': try: sys.exit(main()) except Exception as err: if not iswindows: raise tb = traceback.format_exc() from PyQt5.Qt import QErrorMessage logfile = os.path.join(os.path.expanduser('~'), 'calibre.log') if os.path.exists(logfile): log = open(logfile).read().decode('utf-8', 'ignore') d = QErrorMessage() d.showMessage(('<b>Error:</b>%s<br><b>Traceback:</b><br>' '%s<b>Log:</b><br>%s')%(unicode(err), unicode(tb).replace('\n', '<br>'), log.replace('\n', '<br>')))
class ExportKarmaDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.model = None self.setModal(modal) self.setWindowTitle("Export Karma annotations") lo = QVBoxLayout(self) lo.setContentsMargins(10, 10, 10, 10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="Filename:", dialog_label="Karma annotations filename", default_suffix="ann", file_types="Karma annotations (*.ann)") lo.addWidget(self.wfile) # selected sources checkbox self.wsel = QCheckBox("selected sources only", self) lo.addWidget(self.wsel) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(5, 5, 5, 5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) self.wokbtn.clicked.connect(self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) cancelbtn.clicked.connect(self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals self.wfile.valid.connect(self.wokbtn.setEnabled) # internal state self.qerrmsg = QErrorMessage(self) self._model_filename = None def setModel(self, model): self.model = model # set the default annotations filename, whenever a new model filename is set filename = self.model.filename() if filename and filename != self._model_filename: self._model_filename = filename self.wfile.setFilename(os.path.splitext(filename)[0] + ".ann") def accept(self): """Tries to export annotations, and closes the dialog if successful.""" try: filename = self.wfile.filename() if os.path.exists(filename) and QMessageBox.question( self, "Exporting Karma annotations", "<P>Overwrite the file %s?</P>" % filename, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) != QMessageBox.Yes: return f = open(self.wfile.filename(), "wt") f.write('COORD W\nPA STANDARD\nCOLOR GREEN\nFONT hershey12\n') # source list if self.wsel.isChecked(): sources = [src for src in self.model.sources if src.selected] else: sources = self.model.sources # calculate basis size for crosses (TODO: replace min_size with something more sensible, as this value is in degrees) brightnesses = [ abs(src.brightness()) for src in sources if src.brightness() != 0 ] min_bright = brightnesses and min(brightnesses) min_size = 0.01 # loop over sources busy = BusyIndicator() for src in sources: ra = src.pos.ra / DEG dec = src.pos.dec / DEG # figure out source size if src.brightness() and min_bright: ysize = (math.log10(abs(src.brightness())) - math.log10(min_bright) + 1) * min_size else: ysize = min_size xsize = ysize / (math.cos(src.pos.dec) or 1) # figure out source style style, label = self.model.getSourcePlotStyle(src) if style: f.write('# %s\n' % src.name) # write symbol for source f.write('COLOR %s\n' % style.symbol_color) if style.symbol == "plus": f.write('CROSS %.12f %.12f %f %f\n' % (ra, dec, xsize, ysize)) elif style.symbol == "cross": f.write('CROSS %.12f %.12f %f %f 45\n' % (ra, dec, ysize, ysize)) elif style.symbol == "circle": f.write('CIRCLE %.12f %.12f %f\n' % (ra, dec, ysize)) elif style.symbol == "dot": f.write('DOT %.12f %.12f\n' % (ra, dec)) elif style.symbol == "square": f.write('CBOX %.12f %.12f %f %f\n' % (ra, dec, xsize, ysize)) elif style.symbol == "diamond": f.write('CBOX %.12f %.12f %f %f 45\n' % (ra, dec, xsize, ysize)) # write label if label: f.write('FONT hershey%d\n' % (style.label_size * 2)) f.write('COLOR %s\n' % style.label_color) f.write('TEXT %.12f %.12f %s\n' % (ra, dec, label)) f.close() except IOError as err: busy.reset_cursor() self.qerrmsg.showMessage( "Error writing Karma annotations file %s: %s" % (filename, str(err))) return busy.reset_cursor() self.parent().showMessage( "Wrote Karma annotations for %d sources to file %s" % (len(sources), filename)) return QDialog.accept(self)
def onSave(self): """导出保存""" ccompany = self.lineEdit.text().strip() cman = self.lineEdit_2.text().strip() cphone = self.lineEdit_3.text().replace('-', '').strip() caddress = self.lineEdit_4.text().strip() csigndate = self.lineEdit_6.text() cgive = self.lineEdit_7.text() cpaydate = self.lineEdit_11.text() myname = self.lineEdit_8.text().strip() myphone = self.lineEdit_9.text().replace('-', '') myaddres = self.lineEdit_10.text().strip() cpaymethod = self.comboBox.currentText() ctotalprice = self.lineEdit_5.text() cmaterials = "" for i in self.materiallist: cmaterials += "{}x{}x{};".format(i[0], i[1], i[2]) cmaterials = cmaterials[:-1] print(cmaterials) if ccompany and cman and cphone and caddress and csigndate and cgive and cpaydate and myname and myphone and myaddres and cpaymethod and ctotalprice and cmaterials: cid = "H{}{}".format(csigndate.replace('-', ''), getToday(csigndate)) print(cid) dd = [cid, cmaterials, ctotalprice, cpaymethod, ccompany, cman, cphone, caddress, csigndate, cgive, myname, myphone, myaddres, cpaydate] try: sc = SaveContract(dd) sc.saveContract() # 制作合同 f = open("resource/static/contractmodel.html", 'r', encoding="utf-8") m = f.read() f.close() temp = "" cmaterials = cmaterials.split("x")[0] l = [cid, cmaterials, ctotalprice, cpaymethod, ccompany, cman, cphone, caddress, csigndate, cgive, cpaydate] n = 0 for i in m: if i == "{": temp += "{}".format(l[n]) n += 1 else: temp += i with open('resource/static/contractres.html', 'w', encoding='utf-8') as f: f.write(temp) print("制作完成") # 初始化 self.materiallist = list() self.showTable() self.lineEdit.clear() self.lineEdit_2.clear() self.lineEdit_3.clear() self.lineEdit_4.clear() self.lineEdit_5.clear() self.lineEdit_6.clear() self.lineEdit_7.clear() self.lineEdit_8.clear() self.lineEdit_9.clear() self.lineEdit_10.clear() self.lineEdit_11.clear() self.previewpane = PreviewPane("resource/static/contractres.html") self.previewpane.show() except Exception as ret: print(ret) else: mes = QErrorMessage(self) mes.setWindowTitle("提示") mes.showMessage("请填写完整信息!") mes.show()
except socket.error: # Good singleinstance is correct (on UNIX) otherinstance = True else: # On windows only singleinstance can be trusted otherinstance = True if iswindows else False if not otherinstance and not opts.shutdown_running_calibre: return run_gui(opts, args, listener, app, gui_debug=gui_debug) communicate(opts, args) return 0 if __name__ == "__main__": try: sys.exit(main()) except Exception as err: if not iswindows: raise tb = traceback.format_exc() from PyQt5.Qt import QErrorMessage logfile = os.path.join(os.path.expanduser("~"), "calibre.log") if os.path.exists(logfile): log = open(logfile).read().decode("utf-8", "ignore") d = QErrorMessage() d.showMessage( ("<b>Error:</b>%s<br><b>Traceback:</b><br>" "%s<b>Log:</b><br>%s") % (unicode(err), unicode(tb).replace("\n", "<br>"), log.replace("\n", "<br>")) )
class RestoreImageDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.model = None self.setModal(modal) self.setWindowTitle("Restore model into image") lo = QVBoxLayout(self) lo.setContentsMargins(10, 10, 10, 10) lo.setSpacing(5) # file selector self.wfile_in = FileSelector(self, label="Input FITS file:", dialog_label="Input FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile_in) self.wfile_out = FileSelector(self, label="Output FITS file:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.AnyFile) lo.addWidget(self.wfile_out) # beam size lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lo1.addWidget(QLabel("Restoring beam FWHM, major axis:", self)) self.wbmaj = QLineEdit(self) lo1.addWidget(self.wbmaj) lo1.addWidget(QLabel("\" minor axis:", self)) self.wbmin = QLineEdit(self) lo1.addWidget(self.wbmin) lo1.addWidget(QLabel("\" P.A.:", self)) self.wbpa = QLineEdit(self) lo1.addWidget(self.wbpa) lo1.addWidget(QLabel("\u00B0", self)) for w in self.wbmaj, self.wbmin, self.wbpa: w.setValidator(QDoubleValidator(self)) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wfile_psf = FileSelector( self, label="Set restoring beam by fitting PSF image:", dialog_label="PSF FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo1.addSpacing(32) lo1.addWidget(self.wfile_psf) # selection only self.wselonly = QCheckBox("restore selected model sources only", self) lo.addWidget(self.wselonly) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setContentsMargins(5, 5, 5, 5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) self.wokbtn.clicked.connect(self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) cancelbtn.clicked.connect(self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals self.wfile_in.filenameSelected.connect(self._fileSelected) self.wfile_in.filenameSelected.connect(self._inputFileSelected) self.wfile_out.filenameSelected.connect(self._fileSelected) self.wfile_psf.filenameSelected.connect(self._psfFileSelected) # internal state self.qerrmsg = QErrorMessage(self) def setModel(self, model): nsel = len([src for src in model.sources if src.selected]) self.wselonly.setVisible(nsel > 0 and nsel < len(model.sources)) self.model = model self._fileSelected(None) def _fileSelected(self, filename): self.wokbtn.setEnabled( bool(self.wfile_in.filename() and self.wfile_out.filename())) def _inputFileSelected(self, filename): if filename: try: header = pyfits.open(filename)[0].header except Exception as err: self.qerrmsg.showMessage("Error reading FITS file %s: %s" % (filename, str(err))) self.wfile_in.setFilename("") return # try to get beam extents gx, gy, grot = [ header.get(x, None) for x in ('BMAJ', 'BMIN', 'BPA') ] if all([x is not None for x in (gx, gy, grot)]): # if beam size is already set, ask before overwriting print([ str(x.text()) for x in (self.wbmaj, self.wbmin, self.wbpa) ]) if any([bool(str(x.text())) for x in (self.wbmaj, self.wbmin, self.wbpa)]) and \ QMessageBox.question(self, "Set restoring beam", "Also reset restoring beam size from this FITS file?", QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes: return self.wbmaj.setText("%.2f" % (gx * 3600)) self.wbmin.setText("%.2f" % (gy * 3600)) self.wbpa.setText("%.2f" % grot) def _psfFileSelected(self, filename): busy = BusyIndicator() filename = str(filename) self.parent().showMessage("Fitting gaussian to PSF file %s" % filename) try: bmaj, bmin, pa = [x / DEG for x in Imaging.fitPsf(filename)] except Exception as err: busy.reset_cursor() self.qerrmsg.showMessage("Error fitting PSF file %s: %s" % (filename, str(err))) return bmaj *= 3600 * Imaging.FWHM bmin *= 3600 * Imaging.FWHM self.wbmaj.setText(str(bmaj)) self.wbmin.setText(str(bmin)) self.wbpa.setText(str(pa)) busy.reset_cursor() def accept(self): """Tries to restore the image, and closes the dialog if successful.""" # get list of sources to restore sources = self.model.sources sel_sources = [src for src in sources if src.selected] if len(sel_sources) > 0 and len(sel_sources) < len( sources) and self.wselonly.isChecked(): sources = sel_sources if not sources: self.qerrmsg.showMessage("No sources to restore.") return busy = BusyIndicator() # get filenames infile = self.wfile_in.filename() outfile = self.wfile_out.filename() self.parent().showMessage( "Restoring %d model sources to image %s, writing to %s" % (len(sources), infile, outfile)) # read fits file try: input_hdu = pyfits.open(infile)[0] except Exception as err: busy.reset_cursor() self.qerrmsg.showMessage("Error reading FITS file %s: %s" % (infile, str(err))) return # get beam sizes try: bmaj = float(str(self.wbmaj.text())) bmin = float(str(self.wbmin.text())) pa = float(str(self.wbpa.text()) or "0") except Exception as err: busy.reset_cursor() self.qerrmsg.showMessage("Invalid beam size specified") return bmaj = bmaj / (Imaging.FWHM * 3600) * DEG bmin = bmin / (Imaging.FWHM * 3600) * DEG pa = pa * DEG # restore try: Imaging.restoreSources(input_hdu, sources, bmaj, bmin, pa) except Exception as err: busy.reset_cursor() self.qerrmsg.showMessage("Error restoring model into image: %s" % str(err)) return # save fits file try: input_hdu.writeto(outfile, overwrite=True) except Exception as err: busy.reset_cursor() self.qerrmsg.showMessage("Error writing FITS file %s: %s" % (outfile, str(err))) return self.parent().loadImage(outfile) busy.reset_cursor() return QDialog.accept(self)
class MainWindow(QMainWindow): isUpdated = pyqtSignal(bool) hasSkyModel = pyqtSignal(bool) hasSelection = pyqtSignal(bool) modelChanged = pyqtSignal(object) closing = pyqtSignal() signalShowMessage = pyqtSignal([str, int], [str]) signalShowErrorMessage = pyqtSignal([str], [str, int]) ViewModelColumns = [ "name", "RA", "Dec", "type", "Iapp", "I", "Q", "U", "V", "RM", "spi", "shape" ] def __init__(self, parent, max_width=None, max_height=None, hide_on_close=False): QMainWindow.__init__(self, parent) self.signalShowMessage.connect(self.showMessage, type=Qt.QueuedConnection) self.signalShowErrorMessage.connect(self.showErrorMessage, type=Qt.QueuedConnection) self.setWindowIcon(pixmaps.tigger_starface.icon()) self._currier = PersistentCurrier() self.hide() # init column constants for icol, col in enumerate(self.ViewModelColumns): setattr(self, "Column%s" % col.capitalize(), icol) # init GUI self.setWindowTitle("Tigger") self.setWindowIcon(QIcon(pixmaps.purr_logo.pm())) # central widget setup self.cw = QWidget(self) # The actual min width of the control dialog is ~396 self._ctrl_dialog_min_size = 400 # approx value # The actual min width of the profile/zoom windows is ~256 self._profile_and_zoom_widget_min_size = 300 # approx value # set usable screen space (90% of available) self.max_width = max_width self.max_height = max_height self.setCentralWidget(self.cw) cwlo = QVBoxLayout(self.cw) cwlo.setContentsMargins(5, 5, 5, 5) # make splitter spl1 = self._splitter1 = QSplitter(Qt.Vertical, self.cw) spl1.setOpaqueResize(False) cwlo.addWidget(spl1) # Create listview of LSM entries self.tw = SkyModelTreeWidget(spl1) self.tw.hide() # split bottom pane spl2 = self._splitter2 = QSplitter(Qt.Horizontal, spl1) spl2.setOpaqueResize(False) self._skyplot_stack = QWidget(spl2) self._skyplot_stack_lo = QVBoxLayout(self._skyplot_stack) self._skyplot_stack_lo.setContentsMargins(0, 0, 0, 0) # add plot self.skyplot = SkyModelPlotter(self._skyplot_stack, self) self.skyplot.resize(128, 128) self.skyplot.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) self._skyplot_stack_lo.addWidget(self.skyplot, 1000) self.skyplot.hide() self.skyplot.imagesChanged.connect(self._imagesChanged) self.skyplot.setupShowMessages(self.signalShowMessage) self.skyplot.setupShowErrorMessages(self.signalShowErrorMessage) self._grouptab_stack = QWidget(spl2) self._grouptab_stack_lo = lo = QVBoxLayout(self._grouptab_stack) self._grouptab_stack_lo.setContentsMargins(0, 0, 0, 0) # add groupings table self.grouptab = ModelGroupsTable(self._grouptab_stack) self.grouptab.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.hasSkyModel.connect(self.grouptab.setEnabled) lo.addWidget(self.grouptab, 1000) lo.addStretch(1) self.grouptab.hide() # add image controls -- parentless for now (setLayout will reparent them anyway) self.imgman = ImageManager() self.imgman.setMainWindow(self) self.imgman.setShowMessageSignal(self.signalShowMessage) self.imgman.setShowErrorMessageSignal(self.signalShowErrorMessage) self.skyplot.setImageManager(self.imgman) self.imgman.imagesChanged.connect(self._imagesChanged) # enable status line self.statusBar().show() # Create and populate main menu menubar = self.menuBar() # File menu file_menu = menubar.addMenu("&File") qa_open = file_menu.addAction("&Open model...", self._openFileCallback, Qt.CTRL + Qt.Key_O) qa_merge = file_menu.addAction("&Merge in model...", self._mergeFileCallback, Qt.CTRL + Qt.SHIFT + Qt.Key_O) self.hasSkyModel.connect(qa_merge.setEnabled) file_menu.addSeparator() qa_save = file_menu.addAction("&Save model", self.saveFile, Qt.CTRL + Qt.Key_S) self.isUpdated.connect(qa_save.setEnabled) qa_save_as = file_menu.addAction("Save model &as...", self.saveFileAs) self.hasSkyModel.connect(qa_save_as.setEnabled) qa_save_selection_as = file_menu.addAction("Save selection as...", self.saveSelectionAs) self.hasSelection.connect(qa_save_selection_as.setEnabled) file_menu.addSeparator() qa_close = file_menu.addAction("&Close model", self.closeFile, Qt.CTRL + Qt.Key_W) self.hasSkyModel.connect(qa_close.setEnabled) qa_quit = file_menu.addAction("Quit", self.close, Qt.CTRL + Qt.Key_Q) # Image menu menubar.addMenu(self.imgman.getMenu()) # Plot menu menubar.addMenu(self.skyplot.getMenu()) # LSM Menu em = QMenu("&LSM", self) self._qa_em = menubar.addMenu(em) self._qa_em.setVisible(False) self.hasSkyModel.connect(self._qa_em.setVisible) self._column_view_menu = QMenu("&Show columns", self) self._qa_cv_menu = em.addMenu(self._column_view_menu) em.addSeparator() em.addAction("Select &all", self._selectAll, Qt.CTRL + Qt.Key_A) em.addAction("U&nselect all", self._unselectAll, Qt.CTRL + Qt.Key_N) em.addAction("&Invert selection", self._selectInvert, Qt.CTRL + Qt.Key_I) em.addAction("Select b&y attribute...", self._showSourceSelector, Qt.CTRL + Qt.Key_Y) em.addSeparator() qa_add_tag = em.addAction("&Tag selection...", self.addTagToSelection, Qt.CTRL + Qt.Key_T) self.hasSelection.connect(qa_add_tag.setEnabled) qa_del_tag = em.addAction("&Untag selection...", self.removeTagsFromSelection, Qt.CTRL + Qt.Key_U) self.hasSelection.connect(qa_del_tag.setEnabled) qa_del_sel = em.addAction("&Delete selection", self._deleteSelection) self.hasSelection.connect(qa_del_sel.setEnabled) # Tools menu tm = self._tools_menu = QMenu("&Tools", self) self._qa_tm = menubar.addMenu(tm) self._qa_tm.setVisible(False) self.hasSkyModel.connect(self._qa_tm.setVisible) # Help menu menubar.addSeparator() hm = self._help_menu = menubar.addMenu("&Help") hm.addAction("&About...", self._showAboutDialog) self._about_dialog = None # message handlers self.qerrmsg = QErrorMessage(self) # set initial state self.setAcceptDrops(True) self.model = None self.filename = None self._display_filename = None self._open_file_dialog = self._merge_file_dialog = self._save_as_dialog = self._save_sel_as_dialog = self._open_image_dialog = None self.isUpdated.emit(False) self.hasSkyModel.emit(False) self.hasSelection.emit(False) self._exiting = False # set initial layout self._current_layout = None self.setLayout(self.LayoutEmpty) dprint(1, "init complete") # layout identifiers LayoutEmpty = "empty" LayoutImage = "image" LayoutImageModel = "model" LayoutSplit = "split" def _getFilenamesFromDropEvent(self, event): """Checks if drop event is valid (i.e. contains a local URL to a FITS file), and returns list of filenames contained therein.""" dprint(1, "drop event:", event.mimeData().text()) if not event.mimeData().hasUrls(): dprint(1, "drop event: no urls") return None filenames = [] for url in event.mimeData().urls(): name = str(url.toLocalFile()) dprint(2, "drop event: name is", name) if name and Images.isFITS(name): filenames.append(name) dprint(2, "drop event: filenames are", filenames) return filenames def dragEnterEvent(self, event): if self._getFilenamesFromDropEvent(event): dprint(1, "drag-enter accepted") event.acceptProposedAction() else: dprint(1, "drag-enter rejected") def dropEvent(self, event): busy = None filenames = self._getFilenamesFromDropEvent(event) dprint(1, "dropping", filenames) if filenames: event.acceptProposedAction() busy = BusyIndicator() for name in filenames: self.imgman.loadImage(name) if busy is not None: busy.reset_cursor() def saveSizes(self): if self._current_layout is not None: dprint(1, "saving sizes for layout", self._current_layout) # save main window size and splitter dimensions sz = self.size() Config.set('%s-main-window-width' % self._current_layout, sz.width()) Config.set('%s-main-window-height' % self._current_layout, sz.height()) for spl, name in ((self._splitter1, "splitter1"), (self._splitter2, "splitter2")): ssz = spl.sizes() for i, sz in enumerate(ssz): Config.set( '%s-%s-size%d' % (self._current_layout, name, i), sz) def loadSizes(self): if self._current_layout is not None: dprint(1, "loading sizes for layout", self._current_layout) # get main window size and splitter dimensions w = Config.getint('%s-main-window-width' % self._current_layout, 0) h = Config.getint('%s-main-window-height' % self._current_layout, 0) dprint(2, "window size is", w, h) if not (w and h): return None self.resize(QSize(w, h)) for spl, name in (self._splitter1, "splitter1"), (self._splitter2, "splitter2"): ssz = [ Config.getint( '%s-%s-size%d' % (self._current_layout, name, i), -1) for i in (0, 1) ] dprint(2, "splitter", name, "sizes", ssz) if all([sz >= 0 for sz in ssz]): spl.setSizes(ssz) else: return None return True def setLayout(self, layout): """Changes the current window layout. Restores sizes etc. from config file.""" if self._current_layout is layout: return dprint(1, "switching to layout", layout) # save sizes to config file self.saveSizes() # remove imgman widget from all layouts for lo in self._skyplot_stack_lo, self._grouptab_stack_lo: if lo.indexOf(self.imgman) >= 0: lo.removeWidget(self.imgman) # assign it to appropriate parent and parent's layout if layout is self.LayoutImage: lo = self._skyplot_stack_lo self.setMaximumSize(self.max_width, self.max_height) self.setBaseSize(self.max_width, self.max_height) size_policy = QSizePolicy() size_policy.setVerticalPolicy(QSizePolicy.Minimum) size_policy.setHorizontalPolicy(QSizePolicy.Expanding) self.setSizePolicy(size_policy) # set central widget size - workaround for bug #164 # self.cw.setFixedSize(self.max_width - self._ctrl_dialog_min_size - self._profile_and_zoom_widget_min_size, self.max_height) # self.cw.setGeometry(0, self.max_width - self._ctrl_dialog_min_size - self._profile_and_zoom_widget_min_size / 2, # self.max_width - self._ctrl_dialog_min_size - self._profile_and_zoom_widget_min_size, self.max_height) elif layout is self.LayoutEmpty: lo = self._skyplot_stack_lo else: lo = self._grouptab_stack_lo self.imgman.setParent(lo.parentWidget()) lo.addWidget(self.imgman, 0) # show/hide panels if layout is self.LayoutEmpty: self.tw.hide() self.grouptab.hide() # self.skyplot.show() elif layout is self.LayoutImage: self.tw.hide() self.grouptab.hide() self.skyplot.show() # setup dockable state from config file if Config.getbool('livezoom-show'): self.skyplot._livezoom.setVisible(True) self.skyplot._dockable_livezoom.setVisible(True) self.addDockWidget(Qt.LeftDockWidgetArea, self.skyplot._dockable_livezoom) if Config.getbool('liveprofile-show'): self.skyplot._liveprofile.setVisible(True) self.skyplot._dockable_liveprofile.setVisible(True) self.addDockWidget(Qt.LeftDockWidgetArea, self.skyplot._dockable_liveprofile) # resize dock areas widget_list = self.findChildren(QDockWidget) size_list = [] result = [] for widget in widget_list: if not isinstance(widget.bind_widget, ImageControlDialog): size_list.append(widget.bind_widget.width()) result.append(widget) dprint(2, f"{widget} width {widget.width()}") dprint( 2, f"{widget} bind_widget width {widget.bind_widget.width()}" ) if isinstance(widget.bind_widget, LiveImageZoom): widget.bind_widget.setMinimumWidth(widget.width()) widget_list = result # resize dock areas self.resizeDocks(widget_list, size_list, Qt.Horizontal) elif layout is self.LayoutImageModel: self.tw.show() self.grouptab.show() self.skyplot.show() # reload sizes self._current_layout = layout if not self.loadSizes(): dprint(1, "no sizes loaded, setting defaults") if layout is self.LayoutEmpty: self.resize(QSize(512, 256)) elif layout is self.LayoutImage: self.resize(QSize(512, 512)) self._splitter2.setSizes([512, 0]) elif layout is self.LayoutImageModel: self.resize(QSize(1024, 512)) self._splitter1.setSizes([256, 256]) self._splitter2.setSizes([256, 256]) def enableUpdates(self, enable=True): """Enables updates of the child widgets. Usually called after startup is completed (i.e. all data loaded)""" self.skyplot.enableUpdates(enable) if enable: if self.model: self.setLayout(self.LayoutImageModel) elif self.imgman.getImages(): self.setLayout(self.LayoutImage) else: self.setLayout(self.LayoutEmpty) self.show() def _showAboutDialog(self): if not self._about_dialog: self._about_dialog = AboutDialog.AboutDialog(self) self._about_dialog.show() def addTool(self, name, callback): """Adds a tool to the Tools menu""" self._tools_menu.addAction( name, self._currier.curry(self._callTool, callback)) def _callTool(self, callback): callback(self, self.model) def _imagesChanged(self): """Called when the set of loaded images has changed""" if self.imgman.getImages(): if self._current_layout is self.LayoutEmpty: self.setLayout(self.LayoutImage) else: if not self.model: self.setLayout(self.LayoutEmpty) def _selectAll(self): if not self.model: return busy = BusyIndicator() for src in self.model.sources: src.selected = True self.model.emitSelection(self) busy.reset_cursor() def _unselectAll(self): if not self.model: return busy = BusyIndicator() for src in self.model.sources: src.selected = False self.model.emitSelection(self) busy.reset_cursor() def _selectInvert(self): if not self.model: return busy = BusyIndicator() for src in self.model.sources: src.selected = not src.selected self.model.emitSelection(self) busy.reset_cursor() def _deleteSelection(self): unselected = [src for src in self.model.sources if not src.selected] nsel = len(self.model.sources) - len(unselected) if QMessageBox.question( self, "Delete selection", """<P>Really deleted %d selected source(s)? %d unselected sources will remain in the model.</P>""" % (nsel, len(unselected)), QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel) != QMessageBox.Ok: return self.model.setSources(unselected) self.signalShowMessage[str].emit("""Deleted %d sources""" % nsel) self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self) def _showSourceSelector(self): TigGUI.Tools.source_selector.show_source_selector(self, self.model) def _updateModelSelection(self, num, origin=None): """Called when the model selection has been updated.""" self.hasSelection.emit(bool(num)) import Tigger.Models.Formats _formats = [f[1] for f in Tigger.Models.Formats.listFormatsFull()] _load_file_types = [(doc, ["*" + ext for ext in extensions], load) for load, save, doc, extensions in _formats if load] _save_file_types = [(doc, ["*" + ext for ext in extensions], save) for load, save, doc, extensions in _formats if save] def showMessage(self, msg, time=3000): self.statusBar().showMessage(msg, time) def showErrorMessage(self, msg, time=3000): self.qerrmsg.showMessage(msg) def loadImage(self, filename): return self.imgman.loadImage(filename) def setModel(self, model): if model is not None: self.modelChanged.emit(model) if model: self.model = model self.hasSkyModel.emit(True) self.hasSelection.emit(False) self.isUpdated.emit(False) self.model.enableSignals() self.model.connect("updated", self._indicateModelUpdated) self.model.connect("selected", self._updateModelSelection) # pass to children self.tw.setModel(self.model) self.grouptab.setModel(self.model) self.skyplot.setModel(self.model) # add items to View menu self._column_view_menu.clear() self.tw.addColumnViewActionsTo(self._column_view_menu) else: self.model = None self.setWindowTitle("Tigger") self.hasSelection.emit(False) self.isUpdated.emit(False) self.hasSkyModel.emit(False) self.tw.clear() self.grouptab.clear() self.skyplot.setModel(None) def _openFileCallback(self): if not self._open_file_dialog: filters = ";;".join([ "%s (%s)" % (name, " ".join(patterns)) for name, patterns, func in self._load_file_types ]) dialog = self._open_file_dialog = QFileDialog( self, "Open sky model", ".", filters) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setModal(True) dialog.filesSelected['QStringList'].connect(self.openFile) self._open_file_dialog.exec_() return def _mergeFileCallback(self): if not self._merge_file_dialog: filters = ";;".join([ "%s (%s)" % (name, " ".join(patterns)) for name, patterns, func in self._load_file_types ]) dialog = self._merge_file_dialog = QFileDialog( self, "Merge in sky model", ".", filters) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setModal(True) dialog.filesSelected['QStringList'].connect( self._currier.curry(self.openFile, merge=True)) self._merge_file_dialog.exec_() return def openFile(self, _filename=None, _format=None, _merge=False, _show=True): # check that we can close existing model if not _merge and not self._canCloseExistingModel(): return False if isinstance(_filename, QStringList): _filename = _filename[0] _filename = str(_filename) # try to determine the file type filetype, import_func, export_func, doc = Tigger.Models.Formats.resolveFormat( _filename, _format) if import_func is None: self.signalShowErrorMessage.emit( """Error loading model file %s: unknown file format""" % _filename) return # try to load the specified file busy = BusyIndicator() self.signalShowMessage.emit( """Reading %s file %s""" % (filetype, _filename), 3000) QApplication.flush() try: model = import_func(_filename) model.setFilename(_filename) except: busy.reset_cursor() self.signalShowErrorMessage.emit( """Error loading '%s' file %s: %s""" % (filetype, _filename, str(sys.exc_info()[1]))) return else: # set the layout if _show: self.setLayout(self.LayoutImageModel) # add to content if _merge and self.model: self.model.addSources(model.sources) self.signalShowMessage.emit( """Merged in %d sources from '%s' file %s""" % (len(model.sources), filetype, _filename), 3000) self.model.emitUpdate(SkyModel.SkyModel.UpdateAll) else: print("""Loaded %d sources from '%s' file %s""" % (len(model.sources), filetype, _filename)) self.signalShowMessage.emit( """Loaded %d sources from '%s' file %s""" % (len(model.sources), filetype, _filename), 3000) self._display_filename = os.path.basename(_filename) self.setModel(model) self._indicateModelUpdated(updated=False) # only set self.filename if an export function is available for this format. Otherwise set it to None, so that trying to save # the file results in a save-as operation (so that we don't save to a file in an unsupported format). self.filename = _filename if export_func else None finally: busy.reset_cursor() def closeEvent(self, event): dprint(1, "closing") self._exiting = True self.saveSizes() if not self.closeFile(): self._exiting = False event.ignore() return self.skyplot.close() self.imgman.close() self.closing.emit() dprint(1, "invoking os._exit(0)") os._exit(0) QMainWindow.closeEvent(self, event) def _canCloseExistingModel(self): # save model if modified if self.model and self._model_updated: res = QMessageBox.question( self, "Closing sky model", "<P>Model has been modified, would you like to save the changes?</P>", QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Save) if res == QMessageBox.Cancel: return False elif res == QMessageBox.Save: if not self.saveFile(confirm=False, overwrite=True): return False # unload model images, unless we are already exiting anyway if not self._exiting: self.imgman.unloadModelImages() return True def closeFile(self): if not self._canCloseExistingModel(): return False # close model self._display_filename = None self.setModel(None) # set the layout self.setLayout(self.LayoutImage if self.imgman.getTopImage() else self. LayoutEmpty) return True def saveFile(self, filename=None, confirm=False, overwrite=True, non_native=False): """Saves file using the specified 'filename'. If filename is None, uses current filename, if that is not set, goes to saveFileAs() to open dialog and get a filename. If overwrite=False, will ask for confirmation before overwriting an existing file. If non_native=False, will ask for confirmation before exporting in non-native format. If confirm=True, will ask for confirmation regardless. Returns True if saving succeeded, False on error (or if cancelled by user). """ if isinstance(filename, QStringList): filename = filename[0] filename = (filename and str(filename)) or self.filename if filename is None: return self.saveFileAs() else: warning = '' # try to determine the file type filetype, import_func, export_func, doc = Tigger.Models.Formats.resolveFormat( filename, None) if export_func is None: self.signalShowErrorMessage.emit( """Error saving model file %s: unsupported output format""" % filename) return if os.path.exists(filename) and not overwrite: warning += "<P>The file already exists and will be overwritten.</P>" if filetype != 'Tigger' and not non_native: warning += """<P>Please note that you are exporting the model using the external format '%s'. Source types, tags and other model features not supported by this format will be omitted during the export.</P>""" % filetype # get confirmation if confirm or warning: dialog = QMessageBox.warning if warning else QMessageBox.question if dialog(self, "Saving sky model", "<P>Save model to %s?</P>%s" % (filename, warning), QMessageBox.Save | QMessageBox.Cancel, QMessageBox.Save) != QMessageBox.Save: return False busy = BusyIndicator() try: export_func(self.model, filename) self.model.setFilename(filename) except: busy.reset_cursor() self.signalShowErrorMessage.emit( """Error saving model file %s: %s""" % (filename, str(sys.exc_info()[1]))) return False else: self.signalShowMessage.emit( """Saved model to file %s""" % filename, 3000) self._display_filename = os.path.basename(filename) self._indicateModelUpdated(updated=False) self.filename = filename return True finally: busy.reset_cursor() def saveFileAs(self, filename=None): """Saves file using the specified 'filename'. If filename is None, opens dialog to get a filename. Returns True if saving succeeded, False on error (or if cancelled by user). """ if filename is None: if not self._save_as_dialog: filters = ";;".join([ "%s (%s)" % (name, " ".join(patterns)) for name, patterns, func in self._save_file_types ]) dialog = self._save_as_dialog = QFileDialog( self, "Save sky model", ".", filters) dialog.setDefaultSuffix(ModelHTML.DefaultExtension) dialog.setFileMode(QFileDialog.AnyFile) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setOption(QFileDialog.DontConfirmOverwrite, True) dialog.setModal(True) dialog.filesSelected['QStringList'].connect(self.saveFileAs) return self._save_as_dialog.exec_() == QDialog.Accepted # filename supplied, so save return self.saveFile(filename, confirm=False) def saveSelectionAs(self, filename=None, force=False): if not self.model: return if filename is None: if not self._save_sel_as_dialog: filters = ";;".join([ "%s (%s)" % (name, " ".join(patterns)) for name, patterns, func in self._save_file_types ]) dialog = self._save_sel_as_dialog = QFileDialog( self, "Save sky model", ".", filters) dialog.setDefaultSuffix(ModelHTML.DefaultExtension) dialog.setFileMode(QFileDialog.AnyFile) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setOption(QFileDialog.DontConfirmOverwrite, False) dialog.setModal(True) dialog.filesSelected['QStringList'].connect( self.saveSelectionAs) return self._save_sel_as_dialog.exec_() == QDialog.Accepted # save selection if isinstance(filename, QStringList): filename = filename[0] filename = str(filename) selmodel = self.model.copy() sources = [src for src in self.model.sources if src.selected] if not sources: self.signalShowErrorMessage.emit( """You have not selected any sources to save.""") return # try to determine the file type filetype, import_func, export_func, doc = Tigger.Models.Formats.resolveFormat( filename, None) if export_func is None: self.signalShowErrorMessage.emit( """Error saving model file %s: unsupported output format""" % filename) return busy = BusyIndicator() try: export_func(self.model, filename, sources=sources) except: busy.reset_cursor() self.signalShowErrorMessage.emit( """Error saving selection to model file %s: %s""" % (filename, str(sys.exc_info()[1]))) return False else: self.signalShowMessage.emit( """Wrote %d selected source%s to file %s""" % (len(selmodel.sources), "" if len(selmodel.sources) == 1 else "s", filename), 3000) finally: busy.reset_cursor() pass def addTagToSelection(self): if not hasattr(self, '_add_tag_dialog'): self._add_tag_dialog = Widgets.AddTagDialog(self, modal=True) self._add_tag_dialog.setTags(self.model.tagnames) self._add_tag_dialog.setValue(True) if self._add_tag_dialog.exec_() != QDialog.Accepted: return tagname, value = self._add_tag_dialog.getTag() if tagname is None or value is None: return None dprint(1, "tagging selected sources with", tagname, value) # tag selected sources for src in self.model.sources: if src.selected: src.setAttribute(tagname, value) # If tag is not new, set a UpdateSelectionOnly flag on the signal dprint(1, "adding tag to model") self.model.addTag(tagname) dprint(1, "recomputing totals") self.model.getTagGrouping(tagname).computeTotal(self.model.sources) dprint(1, "emitting update signal") what = SkyModel.SkyModel.UpdateSourceContent + SkyModel.SkyModel.UpdateTags + SkyModel.SkyModel.UpdateSelectionOnly self.model.emitUpdate(what, origin=self) def removeTagsFromSelection(self): if not hasattr(self, '_remove_tag_dialog'): self._remove_tag_dialog = Widgets.SelectTagsDialog( self, modal=True, caption="Remove Tags", ok_button="Remove") # get set of all tags in selected sources tags = set() for src in self.model.sources: if src.selected: tags.update(src.getTagNames()) if not tags: return tags = list(tags) tags.sort() # show dialog self._remove_tag_dialog.setTags(tags) if self._remove_tag_dialog.exec_() != QDialog.Accepted: return tags = self._remove_tag_dialog.getSelectedTags() if not tags: return # ask for confirmation plural = (len(tags) > 1 and "s") or "" if QMessageBox.question( self, "Removing tags", "<P>Really remove the tag%s '%s' from selected sources?</P>" % (plural, "', '".join(tags)), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) != QMessageBox.Yes: return # remove the tags for src in self.model.sources: if src.selected: for tag in tags: src.removeAttribute(tag) # update model self.model.scanTags() self.model.initGroupings() # emit signal what = SkyModel.SkyModel.UpdateSourceContent + SkyModel.SkyModel.UpdateTags + SkyModel.SkyModel.UpdateSelectionOnly self.model.emitUpdate(what, origin=self) def _indicateModelUpdated(self, what=None, origin=None, updated=True): """Marks model as updated.""" self._model_updated = updated self.isUpdated.emit(updated) if self.model: self.setWindowTitle("Tigger - %s%s" % ((self._display_filename or "(unnamed)", " (modified)" if updated else "")))