def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) # 获取屏幕尺寸相关信息 self.ag = QApplication.desktop().availableGeometry() self.maxWidth = self.ag.width() self.maxHeight = self.ag.height() self.real = QApplication.desktop().geometry() self.windowPreState = None self.initWidth = 1280 self.initHeight = 760 # 全局存储当前屏幕参数 ctx.put(Key.WINDOW_INIT_SIZE, [self.initWidth, self.initHeight]) ctx.put(Key.SCREEN_AVAIL_SIZE, [self.maxWidth, self.maxHeight]) ctx.put(Key.SCREEN_REAL_SIZE, [self.real.width(), self.real.height()]) # 头部标题栏 self.head = Pane(self) # 条码输入框 self.barcode = EditText() # 流程展示控件 self.resultBox = QLabel() # 显示最终结果控件 self.displayBox = TextView() # 摄像头开启控件, 在这里只新建对象,不要将其添加到窗口中 # 因为图层遮挡,到后面得不到鼠标事件 self.cameraIcon = ImageView()
def __init__(self, data, parent=None, liveplot=None): # Init Feature Feature.__init__(self, 'Cell Selection', data, parent, liveplot, display_name_label=False) self.data_manager = data self.data_manager.cell_selection = self self.setMinimumSize(350, 450) self.allowEditROI = True self.disabled_roi_handles = [] self.preview_mode = True self.show_user_roi = False self.ellipse_mode = True self.update_on_ellipse_mode_change = True # data self.input = {'source': None, 'roi_ellipse_mode': None} self.output = {'cell': None, 'cell mean': None, 'roi': None} # view / plot self.imv = ImageView() self.imv.setMinimumHeight(450) self.layout.addWidget(self.imv) self.imv.view.mouseClickEvent = lambda ev: self.imvViewMouseClickEvent( self.imv.view, ev) self.imv.view.raiseContextMenu = lambda ev: self.imvViewRaiseContextMenu( self.imv.view, ev) self.imv.view.getContextMenus = lambda ev=None: self.imvViewGetContextMenus( self.imv.view, ev) self.imv.view.preview_mode = self.imv.view.menu.addAction( 'live preview mode') self.imv.view.preview_mode.setCheckable(True) self.imv.view.preview_mode.setChecked(self.preview_mode) self.imv.view.preview_mode.triggered.connect(self.switchPreviewMode) self.imv.view.ellipse_mode = self.imv.view.menu.addAction( 'ellipse mode') self.imv.view.ellipse_mode.setCheckable(True) self.imv.view.ellipse_mode.setChecked(self.ellipse_mode) self.connectEllipseMode() # methods self.addMethod('ROI', cs_roi_params, self.calculateROI) self.initMethodUI() self.initParametersUI() self.show() """ the image must be displayed once such that the roicurve is created and can be shown even if the user loads a non-TIF source before he loads a TIF source. """ self.imv.setImage(np.zeros((1, 10, 10)), xvals=np.arange(10)) self.imv.clear()
def createHead(self): self.head.resize(self.initWidth, MainWindow.HEAD_HEIGHT) self.head.move(0, 0) innerLayout = QHBoxLayout() innerLayout.layout().setContentsMargins(0, 0, 0, 0) self.head.setLayout(innerLayout) adjust = TextView("校准") adjust.setObjectName("adjust") adjust.setFixedSize(MainWindow.RIGHT_LABEL_WIDTH, MainWindow.HEAD_HEIGHT) adjust.setAlignment(Qt.AlignCenter) adjust.setMousePressCallback(self.clickedEvent) # 右边 # 隐藏窗口控件 dis = ImageView() dis.setFixedSize(MainWindow.RIGHT_LABEL_WIDTH, MainWindow.HEAD_HEIGHT) dis.setObjectName("dis") dis.setCallback(self.clickedEvent) # 最大最小化窗口控件 maxi = ImageView() maxi.setFixedSize(MainWindow.RIGHT_LABEL_WIDTH, MainWindow.HEAD_HEIGHT) maxi.setObjectName("maxi") maxi.setCallback(self.clickedEvent) # 关闭窗口控件 close = ImageView() close.setFixedSize(MainWindow.RIGHT_LABEL_WIDTH, MainWindow.HEAD_HEIGHT) close.setObjectName("close") close.setCallback(self.clickedEvent) close.setDoubleClick(self.doubleClickEvent) innerLayout.addStretch(0) innerLayout.addWidget(adjust) innerLayout.addWidget(dis) innerLayout.addWidget(maxi) innerLayout.addWidget(close)
def createHead(self): head = QWidget(self) head.setObjectName("head") head.resize(self.w, self.headHeight) head.move(0, 0) head.setContentsMargins(0, 0, 0, 0) headInnerBox = QHBoxLayout() headInnerBox.setContentsMargins(0, 0, 0, 0) head.setLayout(headInnerBox) leftSpace = QLabel() leftSpace.resize(10, self.headHeight) headInnerBox.addWidget(leftSpace) title = TextView() title.setObjectName("title") title.setText("校准框(请将机台对准框)") headInnerBox.addWidget(title) # 隐藏窗口控件 dis = ImageView() dis.setFixedSize(CalibrationActivity.__IMAGE_WIDTH, self.headHeight) dis.setObjectName("dis") dis.setCallback(self.clickedEvent) # 关闭窗口控件 close = ImageView() close.setFixedSize(CalibrationActivity.__IMAGE_WIDTH, self.headHeight) close.setObjectName("close") close.setCallback(self.clickedEvent) headInnerBox.addStretch(0) headInnerBox.addWidget(dis) headInnerBox.addWidget(close)
def createHead(self): self.head.resize(self.initWidth, MainWindow.HEAD_HEIGHT) self.head.move(0, 0) innerLayout = QHBoxLayout() innerLayout.layout().setContentsMargins(0, 0, 0, 0) # 间隔 leftSpace = QLabel("") leftSpace.setFixedSize(0, 10) innerLayout.addWidget(leftSpace) # 左边图标 icon = QLabel() icon.setFixedSize(25, 25) icon.setObjectName("icon") # 项目名称 name = QLabel("AI") name.setObjectName("projectName") # 版本号 version = QLabel("v1.2021.01.09") version.setObjectName("version") innerLayout.addWidget(icon) innerLayout.addWidget(name) innerLayout.addWidget(version) innerLayout.addStretch(1) innerLayout.setSpacing(10) innerLayout.setAlignment(Qt.AlignHCenter) self.head.setLayout(innerLayout) # 右边 # 隐藏窗口控件 dis = ImageView() dis.setFixedSize(MainWindow.RIGHT_LABEL_WIDTH, MainWindow.HEAD_HEIGHT) dis.setObjectName("dis") dis.setCallback(self.clickedEvent) # 最大最小化窗口控件 maxi = ImageView() maxi.setFixedSize(MainWindow.RIGHT_LABEL_WIDTH, MainWindow.HEAD_HEIGHT) maxi.setObjectName("maxi") maxi.setCallback(self.clickedEvent) # 关闭窗口控件 close = ImageView() close.setFixedSize(MainWindow.RIGHT_LABEL_WIDTH, MainWindow.HEAD_HEIGHT) close.setObjectName("close") close.setCallback(self.clickedEvent) innerLayout.addStretch(0) innerLayout.addWidget(dis) innerLayout.addWidget(maxi) innerLayout.addWidget(close)
class MainWindow(QWidget): # 头部的高度 HEAD_HEIGHT = 40 # 头部下面的线条高度 LINE_HEIGHT = 1 RIGHT_LABEL_WIDTH = 60 # 默认的背景颜色 BG_COLOR = "#0C172D" # 摄像头图片容器的尺寸 CAMERA_ICON_SIZE = QSize(50, 50) # 边界间隔 PADDING = 10 def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) # 获取屏幕尺寸相关信息 self.ag = QApplication.desktop().availableGeometry() self.maxWidth = self.ag.width() self.maxHeight = self.ag.height() self.real = QApplication.desktop().geometry() self.windowPreState = None self.initWidth = 1280 self.initHeight = 760 # 全局存储当前屏幕参数 ctx.put(Key.WINDOW_INIT_SIZE, [self.initWidth, self.initHeight]) ctx.put(Key.SCREEN_AVAIL_SIZE, [self.maxWidth, self.maxHeight]) ctx.put(Key.SCREEN_REAL_SIZE, [self.real.width(), self.real.height()]) # 头部标题栏 self.head = Pane(self) # 条码输入框 self.barcode = EditText() # 流程展示控件 self.resultBox = QLabel() # 显示最终结果控件 self.displayBox = TextView() # 摄像头开启控件, 在这里只新建对象,不要将其添加到窗口中 # 因为图层遮挡,到后面得不到鼠标事件 self.cameraIcon = ImageView() def initStyle(self): self.setObjectName("window") self.resize(self.initWidth, self.initHeight) self.setWindowTitle(Global.project_name) self.setWindowFlags(Qt.FramelessWindowHint) # self.setStyleSheet(style) def display(self): self.initStyle() self.createHead() self.createLine() self.createBody() self.show() def createHead(self): self.head.resize(self.initWidth, MainWindow.HEAD_HEIGHT) self.head.move(0, 0) innerLayout = QHBoxLayout() innerLayout.layout().setContentsMargins(0, 0, 0, 0) # 间隔 leftSpace = QLabel("") leftSpace.setFixedSize(0, 10) innerLayout.addWidget(leftSpace) # 左边图标 icon = QLabel() icon.setFixedSize(25, 25) icon.setObjectName("icon") # 项目名称 name = QLabel("AI") name.setObjectName("projectName") # 版本号 version = QLabel("v1.2021.01.09") version.setObjectName("version") innerLayout.addWidget(icon) innerLayout.addWidget(name) innerLayout.addWidget(version) innerLayout.addStretch(1) innerLayout.setSpacing(10) innerLayout.setAlignment(Qt.AlignHCenter) self.head.setLayout(innerLayout) # 右边 # 隐藏窗口控件 dis = ImageView() dis.setFixedSize(MainWindow.RIGHT_LABEL_WIDTH, MainWindow.HEAD_HEIGHT) dis.setObjectName("dis") dis.setCallback(self.clickedEvent) # 最大最小化窗口控件 maxi = ImageView() maxi.setFixedSize(MainWindow.RIGHT_LABEL_WIDTH, MainWindow.HEAD_HEIGHT) maxi.setObjectName("maxi") maxi.setCallback(self.clickedEvent) # 关闭窗口控件 close = ImageView() close.setFixedSize(MainWindow.RIGHT_LABEL_WIDTH, MainWindow.HEAD_HEIGHT) close.setObjectName("close") close.setCallback(self.clickedEvent) innerLayout.addStretch(0) innerLayout.addWidget(dis) innerLayout.addWidget(maxi) innerLayout.addWidget(close) def createLine(self): line = QWidget(self) line.resize(self.initWidth, MainWindow.LINE_HEIGHT) line.move(0, MainWindow.HEAD_HEIGHT) line.setObjectName("line") def createBody(self): body = QWidget(self) body.resize( self.initWidth, self.initHeight - MainWindow.HEAD_HEIGHT - MainWindow.LINE_HEIGHT) body.move(0, MainWindow.HEAD_HEIGHT + MainWindow.LINE_HEIGHT) # body.setStyleSheet("background-color: #DA81F5;") # 左边盒子宽度 leftBoxWidth = 400 # 盒子高度 boxHeight = 700 # padding padding = 10 # 条码输入框高度 barcodeHeight = 30 # 流程信息展示区高度 displayBoxHeight = 450 bannerHeight = 100 # 右边区域宽度 = 窗口宽度 - 左边盒子宽度 - 左边盒子距离窗口左边的间隔 - 右边盒子和左边盒子的间隔 - 右边和距离窗口右边的间隔 rightBoxWidth = self.initWidth - leftBoxWidth - padding - padding - padding # ############左边区域################# leftBox = QWidget(body) leftBox.resize(leftBoxWidth, boxHeight) leftBox.move(padding, padding) leftBox.setObjectName("leftBox") # leftBox.setStyleSheet("background-color: #CCECDA;") # 内部垂直布局 leftBoxInnerBox = QVBoxLayout() leftBoxInnerBox.setSpacing(10) leftBox.setLayout(leftBoxInnerBox) # 条码输入框 self.barcode.setParent(leftBox) self.barcode.setObjectName("barcode") self.barcode.setMaximumSize(leftBoxWidth, barcodeHeight) # self.barcode.setCallback(self.on_edit_change) leftBoxInnerBox.addWidget(self.barcode) # 流程展示区 self.displayBox.setParent(leftBox) self.displayBox.resize(leftBoxWidth, displayBoxHeight) # self.displayBox.setMaximumHeight(displayBoxHeight) # self.displayBox.setMinimumHeight(displayBoxHeight) self.displayBox.setObjectName("displayBox") leftBoxInnerBox.addWidget(self.displayBox) self.displayBox.setAlignment(Qt.AlignTop) # effect = QGraphicsDropShadowEffect(self) # effect.setBlurRadius(12) # effect.setOffset(5, -5) # effect.setColor(Qt.red) # displayBox.setGraphicsEffect(effect) self.displayBox.setText("1.获取到条码.\n2.开始识别...\n3.识别结果: OK.") self.displayBox.append("4.结束!") for i in range(30): self.displayBox.append("{}.动作!".format(i)) # 显示最终结果的地方 self.resultBox.setParent(leftBox) self.resultBox.setObjectName("resultBox") self.resultBox.resize( leftBoxWidth, boxHeight - displayBoxHeight - barcodeHeight - 20) self.resultBox.setMaximumSize( leftBoxWidth, boxHeight - displayBoxHeight - barcodeHeight - 20) self.resultBox.setMinimumHeight(boxHeight - displayBoxHeight - barcodeHeight - 20) self.resultBox.setText("OK") self.resultBox.setAlignment(Qt.AlignCenter) self.resultBox.setPalette(QPalette(QColor(255, 0, 0, 1))) leftBoxInnerBox.addWidget(self.resultBox) # ############右边区域################# rightBox = QWidget(body) rightBox.resize(rightBoxWidth, boxHeight) rightBox.move(leftBoxWidth + padding + padding, padding) rightBox.setObjectName("rightBox") # rightBox.setStyleSheet("background-color: #E5E9BF;") # 左边区域内部垂直布局 rightInnerBox = QVBoxLayout() rightInnerBox.setAlignment(Qt.AlignCenter) rightInnerBox.setSpacing(padding) rightBox.setLayout(rightInnerBox) # 横幅 banner = QLabel("Kamiyong Welcome You!") banner.setObjectName("banner") banner.setMaximumSize(rightBoxWidth - 20, bannerHeight) banner.setMinimumSize(rightBoxWidth - 20, bannerHeight) rightInnerBox.addWidget(banner) image = QLabel() image.setObjectName("image") image.setMinimumSize( rightBoxWidth - 20, boxHeight - bannerHeight - padding - padding - padding) rightInnerBox.addWidget(image) # 摄像头图标, 在这里添加到窗口中 # 这样改控件就在所有的图层最上面,就不会被其他控件遮挡而导致事件失效的问题 self.cameraIcon.setParent(self) self.cameraIcon.setCallback(self.clickedEvent) self.cameraIcon.setObjectName("cameraIcon") self.cameraIcon.setFixedSize(MainWindow.CAMERA_ICON_SIZE.width(), MainWindow.CAMERA_ICON_SIZE.height()) self.cameraIcon.move( self.initWidth - MainWindow.CAMERA_ICON_SIZE.width() - MainWindow.PADDING, MainWindow.HEAD_HEIGHT + MainWindow.LINE_HEIGHT + MainWindow.PADDING) def resizeEvent(self, event): """ 窗口尺寸变化事件 :param event: {@link QEvent} :return: no return """ # print("MainWindow Size[w=", self.width(), ", h=", self.height(), "]") self.head.change(self.width(), MainWindow.HEAD_HEIGHT) def getBarcodeEditText(self): """ 获取到条码输入框实例 :return: {@link QEditText} """ return self.barcode def getDisplayBox(self): """ 获取流程展示控件 :return: {@link QLabel} """ return self.displayBox def getResultBox(self): """ 获取结果显示控件 :return: {@link QLabel} """ return self.resultBox def clickedEvent(self, obj: QWidget): name = obj.objectName() if name == "dis": # 最小化窗口 print("dis is clicked.") self.setWindowState(Qt.WindowMinimized) elif name == "maxi": # 最大化窗口点击事件 print("maxi is clicked.") elif name == "close": # 关闭窗口点击事件 print("close is clicked.") self.close() elif name == "cameraIcon": print("cameraIcon is clicked.")
class CellSelection(Feature): def __init__(self, data, parent=None, liveplot=None): # Init Feature Feature.__init__(self, 'Cell Selection', data, parent, liveplot, display_name_label=False) self.data_manager = data self.data_manager.cell_selection = self self.setMinimumSize(350, 450) self.allowEditROI = True self.disabled_roi_handles = [] self.preview_mode = True self.show_user_roi = False self.ellipse_mode = True self.update_on_ellipse_mode_change = True # data self.input = {'source': None, 'roi_ellipse_mode': None} self.output = {'cell': None, 'cell mean': None, 'roi': None} # view / plot self.imv = ImageView() self.imv.setMinimumHeight(450) self.layout.addWidget(self.imv) self.imv.view.mouseClickEvent = lambda ev: self.imvViewMouseClickEvent( self.imv.view, ev) self.imv.view.raiseContextMenu = lambda ev: self.imvViewRaiseContextMenu( self.imv.view, ev) self.imv.view.getContextMenus = lambda ev=None: self.imvViewGetContextMenus( self.imv.view, ev) self.imv.view.preview_mode = self.imv.view.menu.addAction( 'live preview mode') self.imv.view.preview_mode.setCheckable(True) self.imv.view.preview_mode.setChecked(self.preview_mode) self.imv.view.preview_mode.triggered.connect(self.switchPreviewMode) self.imv.view.ellipse_mode = self.imv.view.menu.addAction( 'ellipse mode') self.imv.view.ellipse_mode.setCheckable(True) self.imv.view.ellipse_mode.setChecked(self.ellipse_mode) self.connectEllipseMode() # methods self.addMethod('ROI', cs_roi_params, self.calculateROI) self.initMethodUI() self.initParametersUI() self.show() """ the image must be displayed once such that the roicurve is created and can be shown even if the user loads a non-TIF source before he loads a TIF source. """ self.imv.setImage(np.zeros((1, 10, 10)), xvals=np.arange(10)) self.imv.clear() def show(self): Feature.show(self) self.refreshROIVisibleState() self.imv.getRoiPlot().enableAutoRange(axis=pg.ViewBox.XYAxes, enable=True) def refreshROIVisibleState(self): roi = self.methods['ROI'].getParametersGUI('roi') rect_roi = self.methods['ROI'].getParametersGUI('rect_roi') if not self.show_user_roi: if roi is not None: roi.hide() if rect_roi is not None: rect_roi.hide() elif self.ellipse_mode: if rect_roi is not None: rect_roi.hide() else: if roi is not None: roi.hide() def updateParametersUI(self): Feature.updateParametersUI(self) self.refreshROIVisibleState() def switchPreviewMode(self, checked): self.preview_mode = checked self.disconnectUserROISignals() self.connectUserROISignals() def connectEllipseMode(self): self.imv.view.ellipse_mode.triggered.connect(self.switchEllipseMode) def disconnectEllipseMode(self): try: self.imv.view.ellipse_mode.triggered.disconnect() except: pass def switchEllipseMode(self, checked): self.ellipse_mode = checked if self.ellipse_mode: show = 'roi' hide = 'rect_roi' else: show = 'rect_roi' hide = 'roi' self.methods['ROI'].getParametersGUI(show).show() self.methods['ROI'].getParametersGUI(hide).hide() if self.update_on_ellipse_mode_change: if not self.ellipse_mode: # if we just changed to rectangle mode: set angle to 0, pos and size and update graphics afterwards, # such that the pos and size are set correctly (rounded). but prevent calculation, because # setObjectAttributes does it pos, size, _ = self.methods['ROI'].parameters['roi'] rect_roi = self.methods['ROI'].getParametersGUI('rect_roi') self.disconnectUserROISignals() rect_roi.setPos(pos) rect_roi.setSize(size) rect_roi.setAngle(0) self.connectUserROISignals() self.updateROI(prevent_calculation=True) self.data.setObjectAttributes(self.data.object_selection, {'ellipse_mode': self.ellipse_mode}) def imvViewGetContextMenus(self, view, ev=None): return view.menu def imvViewRaiseContextMenu(self, view, ev): # return if we are not in user mode if not self.show_user_roi: return menu = view.getContextMenus() pos = ev.screenPos() menu.popup(QtCore.QPoint(pos.x(), pos.y())) return True def imvViewMouseClickEvent(self, view, ev): if ev.button() == QtCore.Qt.RightButton: if view.raiseContextMenu(ev): ev.accept() def initParametersUI(self): pos, size, angle = self.methods['ROI'].parameters['roi'] pen = pg.mkPen(color=colors.alternative) roi = self.methods['ROI'].initROI( self.imv, pos, size, angle, pen, updateFunc=self.updateROIOnlyProcessed, releaseFunc=self.updateROIAll) roi.setZValue(20) pos = (math.floor(pos[0]), math.floor(pos[1])) size = (math.ceil(size[0]), math.ceil(size[1])) rect_roi = self.methods['ROI'].initRectROI( self.imv, pos, size, pen, updateFunc=self.updateROIOnlyProcessed, releaseFunc=self.updateROIAll) rect_roi.setZValue(20) rect_roi.hide() self.updateParametersUI() def inputConfiguration(self): source = self.input['source'] if source is not None: if source.filetype == 'tif': if not np.array_equal(self.imv.image, source.getData()): self.imv.setImage(source.getData(), xvals=source.frameRange()) self.imv.showPlot() if not (source.start <= self.imv.timeLine.value() <= source.end): self.imv.timeLine.setValue(source.start) self.show_user_roi = True else: self.imv.clear() self.imv.showPlot() self.imv.roiCurve.setData(source.frameRange(), source.getData()) self.imv.timeLine.hide() self.show_user_roi = False self.refreshROIVisibleState() self.imv.getRoiPlot().enableAutoRange(axis=pg.ViewBox.XYAxes, enable=True) ellipse_mode = self.input['roi_ellipse_mode'] if ellipse_mode is not None: # set attribute s.t. we do not update. the reason behind that is that inputConfiguration should never update. self.update_on_ellipse_mode_change = False self.disconnectEllipseMode() self.imv.view.ellipse_mode.setChecked(ellipse_mode) self.switchEllipseMode(ellipse_mode) self.connectEllipseMode() self.update_on_ellipse_mode_change = True else: self.imv.hidePlot() self.imv.clear() self.show_user_roi = False self.getUserROI().hide() def updateROIAll(self): self.updateROI(only_processed=False) def updateROIOnlyProcessed(self): self.updateROI(only_processed=True) def updateROI(self, only_processed=False, prevent_calculation=False): if self.ellipse_mode: ellipse_roi = self.methods['ROI'].getParametersGUI('roi') self.methods['ROI'].parameters['roi'] = (ellipse_roi.pos(), ellipse_roi.size(), ellipse_roi.angle()) else: rect_roi = self.methods['ROI'].getParametersGUI('rect_roi') pos = rect_roi.pos() size = rect_roi.size() pos = (round(pos[0]), round(pos[1])) size = (round(size[0]), round(size[1])) self.methods['ROI'].parameters['roi'] = (pos, size, 0) if not prevent_calculation: self.update( stop_after_processing=only_processed and self.preview_mode) def calculateROI(self, source, roi_ellipse_mode, roi): if source.filetype == 'tif': img = self.imv.getProcessedImage() if roi_ellipse_mode: ellipse_roi = self.methods['ROI'].getParametersGUI('roi') pos, size, angle = self.methods['ROI'].parameters['roi'] self.disconnectUserROISignals() ellipse_roi.setPos(pos) ellipse_roi.setSize(size) ellipse_roi.setAngle(angle) self.connectUserROISignals() cell = ellipse_roi.getArrayRegion(img.view(np.ndarray), self.imv.imageItem, axes=(1, 2)) else: rect_roi = self.methods['ROI'].getParametersGUI('rect_roi') pos, size, _ = self.methods['ROI'].parameters['roi'] self.disconnectUserROISignals() rect_roi.setPos(pos) rect_roi.setSize(size) rect_roi.setAngle(0) self.connectUserROISignals() _slice = rect_roi.getArraySlice(img.view(np.ndarray), self.imv.imageItem, axes=(1, 2)) cell = img[_slice[0]] # Get ROI mean data cell_mean = cell.mean(axis=(1, 2)) # read roi params out of saved params pos, size, angle = self.methods['ROI'].parameters['roi'] return { 'cell': cell, 'cell mean': cell_mean, 'roi': (cell_mean, pos, size, angle) } else: return {'cell': None, 'cell mean': None, 'roi': None} def editROI(self, roi_index): if self.allowEditROI and self.input['source'].filetype == 'tif': method = self.getMethod() pos, size, angle = method.parameters['roi'] cell_mean = self.output['cell mean'] cell = self.output['cell'] edit_data = { 'cell_mean': cell_mean, 'cell': cell, 'pos': pos, 'angle': angle, 'size': size } self.data_manager.setObjectAttributes( roi_index, edit_data, prevent_object_manager_refresh=True, prevent_roiview_refresh=True) def updateLivePlot(self): pass def getUserROI(self): param_name = 'roi' if self.ellipse_mode else 'rect_roi' return self.methods['ROI'].getParametersGUI(param_name) def disconnectUserROISignals(self): roi_view = self.getUserROI() try: roi_view.sigRegionChanged.disconnect() except: pass roi_view.sigRegionChangeFinished.disconnect() def connectUserROISignals(self): roi_view = self.getUserROI() if self.preview_mode: roi_view.sigRegionChanged.connect(self.updateROIOnlyProcessed) roi_view.sigRegionChangeFinished.connect(self.updateROIAll)
class MovementCorrectionDialog(QtWidgets.QDialog): """ Dialog that shows the Movement Correction Feature. Dialog consists of: - the feature view (two ComboBoxes that control the ImageView content) - a control area (save as file, save and cancel button) - two ImageViews that show the correction and a comparison """ def __init__(self, parent, data_manager): QtWidgets.QDialog.__init__(self, parent, flags=QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.setModal(True) self.setWindowTitle('Movement Correction') self.data_manager = data_manager self.last_confirmed_parameter = None layout = QtWidgets.QGridLayout() self.setLayout(layout) self.feature_view = QtWidgets.QWidget(self) self.feature_view.setMinimumWidth(290) self.controls = QtWidgets.QWidget(self) controls_layout = QtWidgets.QVBoxLayout() self.controls.setLayout(controls_layout) save_tif_btn = QtWidgets.QPushButton('Save Correction as File', self) save_tif_btn.clicked.connect(self.saveAsFile) save_btn = QtWidgets.QPushButton('Save Correction in NOSA', self) save_btn.clicked.connect(self.save) cancel_btn = QtWidgets.QPushButton('Cancel', self) cancel_btn.clicked.connect(self.cancel) controls_layout.addWidget(save_tif_btn) controls_layout.addWidget(save_btn) controls_layout.addWidget(cancel_btn) layout.addWidget(self.feature_view, 0, 0) layout.addWidget(self.controls, 0, 1) corrected_label = QtWidgets.QLabel('Corrected') corrected_label.setAlignment(QtCore.Qt.AlignHCenter) corrected_font = corrected_label.font() corrected_font.setPointSize(corrected_font.pointSize() + 8) corrected_label.setFont(corrected_font) layout.addWidget(corrected_label, 1, 0) compare_label = QtWidgets.QLabel('Compare') compare_label.setAlignment(QtCore.Qt.AlignHCenter) compare_font = compare_label.font() compare_font.setPointSize(compare_font.pointSize() + 8) compare_label.setFont(compare_font) layout.addWidget(compare_label, 1, 1) self.corrected_sublabel = QtWidgets.QLabel('') self.corrected_sublabel.setAlignment(QtCore.Qt.AlignHCenter) layout.addWidget(self.corrected_sublabel, 2, 0) self.compare_sublabel = QtWidgets.QLabel('') self.compare_sublabel.setAlignment(QtCore.Qt.AlignHCenter) layout.addWidget(self.compare_sublabel, 2, 1) def timeChanged(self, ind, time, plot): """ Called when the index of an ImageView has changed. Sets the index of the other ImageView to the same. """ if self.liveplot.getImageItem( ).image is None or self.compare_plot.getImageItem().image is None: return if plot == self.liveplot: self.compare_plot.setCurrentIndex(ind) else: self.liveplot.setCurrentIndex(ind) def show(self): """ Closes (rejects) the Dialog (self) and returns if there is no source selected. If there is a source selected, the correct MovementCorrection Feature is shown, others are hidden. The ImageViews are created and connected with the Feature. The input of the Feature is set and Feature is updated such that the ImageViews are set. """ QtWidgets.QDialog.show(self) if self.data_manager.source_selection is None: self.done(QtWidgets.QDialog.Rejected) return for i in range(len(self.data_manager.sources)): mc_ = self.data_manager.movement_corrections[i] if i == self.data_manager.source_selection: if mc_ is None: self.done(QtWidgets.QDialog.Rejected) return self.mc = mc_ # self.mc.show() is called later! settings must be done first. else: if mc_ is not None: mc_.hide() src = self.data_manager.sources[self.data_manager.source_selection] self.liveplot = ImageView(self) removeExportFromContextMenu(self.liveplot.scene.contextMenu) self.liveplot.setMinimumSize(300, 300) self.compare_plot = ImageView(self) removeExportFromContextMenu(self.compare_plot.scene.contextMenu) self.compare_plot.setMinimumSize(300, 300) self.liveplot.sigTimeChanged.connect( lambda ind, time, plot=self.liveplot: self.timeChanged( ind, time, plot)) self.compare_plot.sigTimeChanged.connect( lambda ind, time, plot=self.compare_plot: self.timeChanged( ind, time, plot)) self.layout().addWidget(self.liveplot, 3, 0) self.layout().addWidget(self.compare_plot, 3, 1) self.mc.liveplot = self.liveplot self.mc.compare_plot = self.compare_plot self.mc.correction_label = self.corrected_sublabel self.mc.compare_label = self.compare_sublabel self.mc.input['source'] = src self.mc.inputConfiguration() # we dont want to calculate the current correction (has not been changed) correction_ind = 0 correction_string = self.mc.methods[ 'Movement Correction'].getParameters()['correction'] for index, (option) in enumerate(self.mc.options): if correction_string == option[0]: correction_ind = index break if correction_ind != 0: self.mc.calculated[correction_ind] = src.getData() self.mc.show() self.mc.update(updateDependend=False) # save the parameter the mc has self.last_confirmed_parameter = correction_string def done(self, r): """ Closes both ImageViews nicely. Clears the calculated list and the feature output. """ self.compare_plot.close() self.liveplot.close() self.layout().removeWidget(self.compare_plot) self.layout().removeWidget(self.liveplot) self.mc.calculated = [None for _ in self.mc.calculated] self.mc.clearData() self.mc = None QtWidgets.QDialog.done(self, r) def save(self): """ Does nothing if self is not visible. If self is visible, the corrected data of the currently selected source is set to the output of the MovementCorrection Feature. The Dialog (self) is closed (accepted). All Pipelines for the source will be refreshed. """ if self.isVisible(): selected_source = self.data_manager.sources[ self.data_manager.source_selection] selected_source.setCorrectedData(self.mc.output['source']) self.done(QtWidgets.QDialog.Accepted) object_indices_for_this_source = [ i for i, o in enumerate(self.data_manager.objects) if o.source is selected_source ] for index in object_indices_for_this_source: # refresh pipeline for all objects belonging to the source. only make cc when refreshing the last pipeline. self.data_manager.refreshPipeline( object_index=index, start_with_feature=self.data_manager.cell_selection, ignore_cross_correlation=index != max(object_indices_for_this_source)) def cancel(self): """ Does nothing if self is not visible. If self is visible, resets the parameter of the mc and closes (rejects) the Dialog (self). """ if self.isVisible(): self.mc.methods['Movement Correction'].parameters[ 'correction'] = self.last_confirmed_parameter self.done(QtWidgets.QDialog.Rejected) def saveAsFile(self): """ Saves the corrected image sequence as a TIF. User has to choose a directory where to save the file. Does nothing if there is no correction. """ if self.mc is None or not self.isVisible(): return corrected = self.mc.output['source'] if corrected is None: return filename, ok = QtWidgets.QFileDialog.getSaveFileName( self, 'Save Corrected as File', directory='', filter='(*.tiff)') if ok and filename.endswith('.tiff'): self.data_manager.progressDialog() self.data_manager.progress_dialog.setMinimum(0) self.data_manager.progress_dialog.setMaximum(0) def saveFileWork(): tifffile.imwrite(filename, corrected, dtype=np.float32) def saveFileCallback(): self.data_manager.progress_dialog.setMaximum(1) self.data_manager.progress_dialog.setValue(1) save_file_worker = Worker(work=saveFileWork, callback=saveFileCallback) QtCore.QThreadPool.globalInstance().start(save_file_worker)
def show(self): """ Closes (rejects) the Dialog (self) and returns if there is no source selected. If there is a source selected, the correct MovementCorrection Feature is shown, others are hidden. The ImageViews are created and connected with the Feature. The input of the Feature is set and Feature is updated such that the ImageViews are set. """ QtWidgets.QDialog.show(self) if self.data_manager.source_selection is None: self.done(QtWidgets.QDialog.Rejected) return for i in range(len(self.data_manager.sources)): mc_ = self.data_manager.movement_corrections[i] if i == self.data_manager.source_selection: if mc_ is None: self.done(QtWidgets.QDialog.Rejected) return self.mc = mc_ # self.mc.show() is called later! settings must be done first. else: if mc_ is not None: mc_.hide() src = self.data_manager.sources[self.data_manager.source_selection] self.liveplot = ImageView(self) removeExportFromContextMenu(self.liveplot.scene.contextMenu) self.liveplot.setMinimumSize(300, 300) self.compare_plot = ImageView(self) removeExportFromContextMenu(self.compare_plot.scene.contextMenu) self.compare_plot.setMinimumSize(300, 300) self.liveplot.sigTimeChanged.connect( lambda ind, time, plot=self.liveplot: self.timeChanged( ind, time, plot)) self.compare_plot.sigTimeChanged.connect( lambda ind, time, plot=self.compare_plot: self.timeChanged( ind, time, plot)) self.layout().addWidget(self.liveplot, 3, 0) self.layout().addWidget(self.compare_plot, 3, 1) self.mc.liveplot = self.liveplot self.mc.compare_plot = self.compare_plot self.mc.correction_label = self.corrected_sublabel self.mc.compare_label = self.compare_sublabel self.mc.input['source'] = src self.mc.inputConfiguration() # we dont want to calculate the current correction (has not been changed) correction_ind = 0 correction_string = self.mc.methods[ 'Movement Correction'].getParameters()['correction'] for index, (option) in enumerate(self.mc.options): if correction_string == option[0]: correction_ind = index break if correction_ind != 0: self.mc.calculated[correction_ind] = src.getData() self.mc.show() self.mc.update(updateDependend=False) # save the parameter the mc has self.last_confirmed_parameter = correction_string