class ChangeDetectorApp(QWidget): image1 = "" image2 = "" result_path = "" AppSize = (600, 300) NUMBER_OF_SLICES = 9 def __init__(self): super().__init__() self.AppTitle = "Satellite Images Change Detector" self.initUI() self.setFixedSize(*self.AppSize) self.setWindowTitle(self.AppTitle) self.show() self.output.insertPlainText(">>> Program Started\n") def initUI(self): #set background image of main window oImage = QImage("background.jpg") sImage = oImage.scaled(QSize(*self.AppSize)) palette = QPalette() palette.setBrush(10, QBrush(sImage)) self.setPalette(palette) #settings of image 1 button image1 = QPushButton('First Image', self) image1.setToolTip('Click to choose first photo') image1.resize(image1.sizeHint()) image1.clicked.connect(self.openFile1NameDialog) #settings of image 2 button image2 = QPushButton('Second Image', self) image2.setToolTip('Click to choose second photo') image2.resize(image2.sizeHint()) image2.clicked.connect(self.openFile2NameDialog) #settings of result button self.result = QPushButton('Result path', self) self.result.setToolTip('Click to choose where to save the result') self.result.resize(self.result.sizeHint()) self.result.clicked.connect(self.saveFileDialog) self.result.setEnabled(False) #settings of save result check box self.save_res = QCheckBox('Save Result', self) self.save_res.setToolTip('Click this to save the output result on the disk') self.save_res.stateChanged.connect(self.saveResult) self.save_res.setAutoFillBackground(True) p = self.save_res.palette() p.setColor(self.save_res.backgroundRole(), Qt.white) self.save_res.setPalette(p) #settings of detect button detect = QPushButton('Detect', self) detect.setToolTip('Click to detect changes between inputs') detect.resize(detect.sizeHint()) detect.clicked.connect(self.detectClicked) # creating a plainText box for output messages self.output = QPlainTextEdit(self) #settings of quit button quit = QPushButton('Quit', self) quit.setToolTip('Click to quit the app') quit.resize(quit.sizeHint()) quit.clicked.connect(QApplication.instance().quit) # setting layout of main window resultBox = QHBoxLayout() resultBox.setSpacing(10) resultBox.addWidget(self.result) resultBox.addWidget(self.save_res) leftBox = QVBoxLayout() leftBox.addWidget(image1) leftBox.addWidget(image2) leftBox.addLayout(resultBox) leftBox.addStretch(1) leftBox.addWidget(detect) rightBox = QVBoxLayout() rightBox.addWidget(self.output) rightBox.addStretch(1) rightBox.addWidget(quit) layout = QHBoxLayout() layout.addLayout(leftBox) layout.addStretch(1) layout.addLayout(rightBox) self.setLayout(layout) def detectClicked(self): if self.image1 == "": self.output.insertPlainText("! Error: Enter first image\n") elif self.image2 == "": self.output.insertPlainText("! Error: Enter second image\n") else: self.output.insertPlainText(">>> Detection Started\n") run_detection(self.image1, self.image2, self.NUMBER_OF_SLICES) def openFile1NameDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName(self,"QFileDialog.getOpenFileName()", "","All Files (*);;PNG Files (*.png);;JPG Files (*.jpg);;JPEG Files (*.jpeg)", options=options) if fileName: self.image1 = fileName self.output.insertPlainText(">>> First image is {}\n".format(self.image1)) def openFile2NameDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName(self,"QFileDialog.getOpenFileName()", "","All Files (*);;PNG Files (*.png);;JPG Files (*.jpg);;JPEG Files (*.jpeg)", options=options) if fileName: self.image2 = fileName self.output.insertPlainText(">>> Second image is {}\n".format(self.image2)) def saveFileDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName(self,"QFileDialog.getSaveFileName()","","All Files (*);;PNG Files (*.png)", options=options) if fileName: self.result_path = fileName self.output.insertPlainText(">>> Result save to {}\n".format(self.result_path)) def saveResult(self, state): if state == Qt.Checked: self.result.setEnabled(True) self.result_path = "" else: self.result.setEnabled(False) self.result_path = ""
class _SingleRoiCtrlWidget(QWidget): """_SingleRoiCtrlWidget class. Widget which controls a single ROI. """ # TODO: locked currently is always 0 # (idx, activated, locked, x, y, w, h) where idx starts from 1 roi_geometry_change_sgn = pyqtSignal(object) _pos_validator = QIntValidator(-10000, 10000) _size_validator = QIntValidator(1, 10000) def __init__(self, roi: RectROI, *, mediator=None, with_lock=True, parent=None): super().__init__(parent=parent) self._roi = roi # Here is mediator is a general mediator object which has a slot # 'onRoiGeometryChange'. self._mediator = mediator idx = self._roi.index self._activate_cb = QCheckBox(f"ROI{idx}") palette = self._activate_cb.palette() palette.setColor(palette.WindowText, FColor.mkColor(config['GUI_ROI_COLORS'][idx - 1])) self._activate_cb.setPalette(palette) self._with_lock = with_lock self._lock_cb = QCheckBox("Lock") self._width_le = SmartLineEdit() self._width_le.setValidator(self._size_validator) self._height_le = SmartLineEdit() self._height_le.setValidator(self._size_validator) self._px_le = SmartLineEdit() self._px_le.setValidator(self._pos_validator) self._py_le = SmartLineEdit() self._py_le.setValidator(self._pos_validator) self._line_edits = (self._width_le, self._height_le, self._px_le, self._py_le) self.initUI() self.initConnections() self.disableAllEdit() def initUI(self): layout = QHBoxLayout() layout.addWidget(self._activate_cb) if self._with_lock: layout.addWidget(self._lock_cb) layout.addWidget(QLabel("w: ")) layout.addWidget(self._width_le) layout.addWidget(QLabel("h: ")) layout.addWidget(self._height_le) layout.addWidget(QLabel("x: ")) layout.addWidget(self._px_le) layout.addWidget(QLabel("y: ")) layout.addWidget(self._py_le) self.setLayout(layout) def initConnections(self): self._width_le.value_changed_sgn.connect(self.onRoiSizeEdited) self._height_le.value_changed_sgn.connect(self.onRoiSizeEdited) self._px_le.value_changed_sgn.connect(self.onRoiPositionEdited) self._py_le.value_changed_sgn.connect(self.onRoiPositionEdited) self._roi.sigRegionChangeFinished.connect( self.onRoiGeometryChangeFinished) self._activate_cb.stateChanged.connect(self.onToggleRoiActivation) self._activate_cb.stateChanged.emit(self._activate_cb.checkState()) self._lock_cb.stateChanged.connect(self.onLock) def setLabel(self, text): self._activate_cb.setText(text) @pyqtSlot(int) def onToggleRoiActivation(self, state): if state == Qt.Checked: self._roi.show() self.enableAllEdit() else: self._roi.hide() self.disableAllEdit() x, y = [int(v) for v in self._roi.pos()] w, h = [int(v) for v in self._roi.size()] self.roi_geometry_change_sgn.emit( (self._roi.index, state == Qt.Checked, 0, x, y, w, h)) @pyqtSlot(object) def onRoiPositionEdited(self, value): x, y = [int(v) for v in self._roi.pos()] w, h = [int(v) for v in self._roi.size()] if self.sender() == self._px_le: x = int(self._px_le.text()) elif self.sender() == self._py_le: y = int(self._py_le.text()) # If 'update' == False, the state change will be remembered # but not processed and no signals will be emitted. self._roi.setPos((x, y), update=False) # trigger sigRegionChanged which moves the handler(s) # finish=False -> sigRegionChangeFinished will not emit, which # otherwise triggers infinite recursion self._roi.stateChanged(finish=False) state = self._activate_cb.isChecked() self.roi_geometry_change_sgn.emit( (self._roi.index, state, 0, x, y, w, h)) @pyqtSlot(object) def onRoiSizeEdited(self, value): x, y = [int(v) for v in self._roi.pos()] w, h = [int(v) for v in self._roi.size()] if self.sender() == self._width_le: w = int(self._width_le.text()) elif self.sender() == self._height_le: h = int(self._height_le.text()) # If 'update' == False, the state change will be remembered # but not processed and no signals will be emitted. self._roi.setSize((w, h), update=False) # trigger sigRegionChanged which moves the handler(s) # finish=False -> sigRegionChangeFinished will not emit, which # otherwise triggers infinite recursion self._roi.stateChanged(finish=False) self.roi_geometry_change_sgn.emit( (self._roi.index, self._activate_cb.isChecked(), 0, x, y, w, h)) @pyqtSlot(object) def onRoiGeometryChangeFinished(self, roi): """Connect to the signal from an ROI object.""" x, y = [int(v) for v in roi.pos()] w, h = [int(v) for v in roi.size()] self.updateParameters(x, y, w, h) # inform widgets outside this window self.roi_geometry_change_sgn.emit( (roi.index, self._activate_cb.isChecked(), 0, x, y, w, h)) def notifyRoiParams(self): # fill the QLineEdit(s) and Redis self._roi.sigRegionChangeFinished.emit(self._roi) def reloadRoiParams(self, cfg): state, _, x, y, w, h = [v.strip() for v in cfg.split(',')] self.roi_geometry_change_sgn.disconnect() self._px_le.setText(x) self._py_le.setText(y) self._width_le.setText(w) self._height_le.setText(h) self.roi_geometry_change_sgn.connect( self._mediator.onRoiGeometryChange) self._activate_cb.setChecked(bool(int(state))) def updateParameters(self, x, y, w, h): self.roi_geometry_change_sgn.disconnect() self._px_le.setText(str(x)) self._py_le.setText(str(y)) self._width_le.setText(str(w)) self._height_le.setText(str(h)) self.roi_geometry_change_sgn.connect( self._mediator.onRoiGeometryChange) def setEditable(self, editable): for w in self._line_edits: w.setDisabled(not editable) @pyqtSlot(int) def onLock(self, state): self._roi.setLocked(state == Qt.Checked) self.setEditable(not state == Qt.Checked) def disableAllEdit(self): self.setEditable(False) self._lock_cb.setDisabled(True) def enableAllEdit(self): self._lock_cb.setDisabled(False) self.setEditable(True)