def setupTabs(self): """ Setup the various tabs in the AddressWidget. """ groups = ["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ"] for group in groups: proxyModel = QSortFilterProxyModel(self) proxyModel.setSourceModel(self.tableModel) proxyModel.setDynamicSortFilter(True) tableView = QTableView() tableView.setModel(proxyModel) tableView.setSortingEnabled(True) tableView.setSelectionBehavior(QAbstractItemView.SelectRows) tableView.horizontalHeader().setStretchLastSection(True) tableView.verticalHeader().hide() tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) tableView.setSelectionMode(QAbstractItemView.SingleSelection) # This here be the magic: we use the group name (e.g. "ABC") to # build the regex for the QSortFilterProxyModel for the group's # tab. The regex will end up looking like "^[ABC].*", only # allowing this tab to display items where the name starts with # "A", "B", or "C". Notice that we set it to be case-insensitive. re = QRegularExpression("^[{}].*".format(group)) assert re.isValid() re.setPatternOptions(QRegularExpression.CaseInsensitiveOption) proxyModel.setFilterRegularExpression(re) proxyModel.setFilterKeyColumn(0) # Filter on the "name" column proxyModel.sort(0, Qt.AscendingOrder) # This prevents an application crash (see: http://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash) viewselectionmodel = tableView.selectionModel() tableView.selectionModel().selectionChanged.connect(self.selectionChanged) self.addTab(tableView, group)
def parseMCodes(cls, command: str) -> List[int]: re = QRegularExpression("[Mm]0*(\\d+)") codes = [] pos = 0 match = re.match(command, pos) while match.hasMatch(): codes.append(int(match.captured(1))) pos += match.capturedLength() match = re.match(command, pos) return codes
def __init__(self, document): super().__init__(document) self.rules = [] # keywords f = QTextCharFormat() f.setFontWeight(QFont.Bold) f.setForeground(Qt.darkBlue) for kw in keyword.kwlist: self.rules.append((QRegularExpression(rf"\b{kw}\b"), f)) # numerals f = QTextCharFormat() f.setForeground(Qt.blue) self.rules.append((QRegularExpression("[0-9]+"), f)) # strings f = QTextCharFormat() f.setForeground(Qt.darkCyan) self.rules.append((QRegularExpression('"[^"]*"'), f)) self.rules.append((QRegularExpression("'[^']*'"), f))
def __init__(self, parent): super().__init__(parent) keywordFormat = QTextCharFormat() commentFormat = QTextCharFormat() stringFormat = QTextCharFormat() singleQuotedStringFormat = QTextCharFormat() self.highlightingRules = [] # keyword brush = QBrush(Qt.blue, Qt.SolidPattern) keywordFormat.setForeground(brush) keywordFormat.setFontWeight(QFont.Bold) keywords = keyword.kwlist for word in keywords: pattern = QRegularExpression("\\b" + word + "\\b") rule = HighlightingRule(pattern, keywordFormat) self.highlightingRules.append(rule) # comment brush = QBrush(Qt.green, Qt.SolidPattern) pattern = QRegularExpression("#[^\n]*") commentFormat.setForeground(brush) rule = HighlightingRule(pattern, commentFormat) self.highlightingRules.append(rule) # string brush = QBrush(Qt.red, Qt.SolidPattern) pattern = QRegularExpression("\".*\"") stringFormat.setForeground(brush) rule = HighlightingRule(pattern, stringFormat) self.highlightingRules.append(rule) # singleQuotedString pattern = QRegularExpression("\'.*\'") singleQuotedStringFormat.setForeground(brush) rule = HighlightingRule(pattern, singleQuotedStringFormat) self.highlightingRules.append(rule)
def filterRegExpChanged(self): syntax_nr = self.filterSyntaxComboBox.currentData() pattern = self.filterPatternLineEdit.text() if syntax_nr == WILDCARD: pattern = QRegularExpression.wildcardToRegularExpression(pattern) elif syntax_nr == FIXED_STRING: pattern = QRegularExpression.escape(pattern) regExp = QRegularExpression(pattern) if not self.filterCaseSensitivityCheckBox.isChecked(): options = regExp.patternOptions() options |= QRegularExpression.CaseInsensitiveOption regExp.setPatternOptions(options) self.proxyModel.setFilterRegularExpression(regExp)
class GcodePreprocessorUtils: ''' ''' re_speed = QRegularExpression("[Ff]([0-9.]+)") rx1_comment_parenthesis = QRegularExpression("\\(+[^\\(]*\\)+") #[rx2_comment_commapoint = QRegularExpression(";.*") re_comment = QRegularExpression("(\\([^\\(\\)]*\\)|[^].*)") re_truncate_decimals = QRegularExpression("(\\d*\\.\\d*)") M_PI = math.acos(-1) @classmethod def overrideSpeed(cls, command: str, speed: float, original: float = None) -> str: ''' Searches the command string for an 'f' and replaces the speed value between the 'f' and the next space with a percentage of that speed. In that way all speed values become a ratio of the provided speed and don't get overridden with just a fixed speed. ''' match = cls.re_speed.match(command) if match.hasMatch(): command = "F%d" % float(match.captured(1)) / 100 * speed # BUG: original does not comes back if original: original = float(match.captured(1)) return command # return command, original @classmethod def removeComment(cls, command: str) -> str: ''' Removes any comments within parentheses or beginning with a semi-colon. ''' res = command #Remove any comments within ( parentheses ) using regex "\([^\(]*\)" match = cls.rx1_comment_parenthesis.match(command) if match.hasMatch(): #comment = match.captured(0) idx = match.capturedStart() len = match.capturedLength() res = res[:idx] + res[idx + len:] # Remove any comment beginning with ';' using regex ";.*" if ';' in res: idx = res.index(";") res = res[:idx] return res.strip() @classmethod def parseComment(cls, command: str) -> str: ''' Searches for a comment in the input string and returns the first match. ''' match = cls.re_comment.match(command) if match.hasMatch(): return match.captured(1) return "" @classmethod def truncateDecimals(cls, length: int, command: str) -> str: res = command pos = 0 match = cls.re_truncate_decimals.match(res, pos) while match.hasMatch(): pos = match.capturedStart() len = match.capturedLength() newNum = "%.*f" % (length, float(match.captured(1))) res = res[:pos] + newNum + res[pos + len:] pos += len(newNum) + 1 match = cls.re_truncate_decimals.match(res, pos) return res @classmethod def removeAllWhitespace(cls, command: str) -> str: #rx = QRegularExpression("\\s") #return command.remove(rx) return command.replace(" ", "") @classmethod def parseCodes(cls, args: List[str], code: str) -> List[float]: l = [] for s in args: if len(s) > 0 and s[0].upper() == code: l.append(float(s[1:])) return l @classmethod def parseGCodes(cls, command: str) -> List[int]: re = QRegularExpression("[Gg]0*(\\d+)") codes = [] pos = 0 match = re.match(command, pos) while match.hasMatch(): codes.append(int(match.captured(1))) pos += match.capturedLength() match = re.match(command, pos) return codes @classmethod def parseMCodes(cls, command: str) -> List[int]: re = QRegularExpression("[Mm]0*(\\d+)") codes = [] pos = 0 match = re.match(command, pos) while match.hasMatch(): codes.append(int(match.captured(1))) pos += match.capturedLength() match = re.match(command, pos) return codes @classmethod def updatePointWithCommand(cls, command: str, initial: QVector3D, absoluteMode: bool) -> QVector3D: ''' Update a point given the arguments of a command. ''' if command.__class__.__name__ == 'str': l = cls.splitCommand(command) return cls.updatePointWithCommand(l, initial, absoluteMode) else: return cls.updatePointWithCommand_FromStringList( command, initial, absoluteMode) @classmethod def updatePointWithCommand_FromStringList(cls, commandArgs: List[str], initial: QVector3D, absoluteMode: bool) -> QVector3D: ''' Update a point given the arguments of a command, using a pre-parsed list. ''' x = qQNaN() y = qQNaN() z = qQNaN() c = "" for command in commandArgs: if len(command) > 0: #c = command.upper().toLatin1() c = command[0].upper() if c == 'X': x = float(command[1:]) elif c == 'Y': y = float(command[1:]) elif c == 'Z': z = float(command[1:]) return cls.updatePointWithCommand_FromVector3D(initial, x, y, z, absoluteMode) @classmethod def updatePointWithCommand_FromVector3D(cls, initial: QVector3D, x: float, y: float, z: float, absoluteMode: bool) -> QVector3D: ''' Update a point given the new coordinates. ''' newPoint = QVector3D(initial.x(), initial.y(), initial.z()) if absoluteMode: if not qIsNaN(x): newPoint.setX(x) if not qIsNaN(y): newPoint.setY(y) if not qIsNaN(z): newPoint.setZ(z) else: if not qIsNaN(x): newPoint.setX(newPoint.x() + x) if not qIsNaN(y): newPoint.setY(newPoint.y() + y) if not qIsNaN(z): newPoint.setZ(newPoint.z() + z) return newPoint @classmethod def updateCenterWithCommand(cls, commandArgs: List[str], initial: QVector3D, nextPoint: QVector3D, absoluteIJKMode: bool, clockwise: bool) -> QVector3D: i = qQNaN() j = qQNaN() k = qQNaN() r = qQNaN() c = "" for t in commandArgs: if len(t) > 0: # c = t[0].upper().toLatin1() c = t[0].upper() if c == 'I': i = float(t[1:]) elif c == 'J': j = float(t[1:]) elif c == 'K': k = float(t[1:]) elif c == 'R': r = float(t[1:]) if qIsNaN(i) and qIsNaN(j) and qIsNaN(k): return cls.convertRToCenter(initial, nextPoint, r, absoluteIJKMode, clockwise) return cls.updatePointWithCommand_FromVector3D(initial, i, j, k, absoluteIJKMode) @classmethod def generateG1FromPoints(cls, start: QVector3D, end: QVector3D, absoluteMode: bool, precision: int) -> str: sb = "G1" if absoluteMode: if not qIsNaN(end.x()): sb.append("X" + "%.*f" % (precision, end.x())) if not qIsNaN(end.y()): sb.append("Y" + "%.*f" % (precision, end.y())) if not qIsNaN(end.z()): sb.append("Z" + "%.*f" % (precision, end.z())) else: if not qIsNaN(end.x()): sb.append("X" + "%.*f" % (precision, end.x() - start.x())) if not qIsNaN(end.y()): sb.append("Y" + "%.*f" % (precision, end.y() - start.y())) if not qIsNaN(end.z()): sb.append("Z" + "%.*f" % (precision, end.z() - start.z())) return sb @classmethod def splitCommand(cls, command: str) -> List[str]: ''' Splits a gcode command by each word/argument, doesn't care about spaces. This command is about the same speed as the string.split(" ") command, but might be a little faster using precompiled regex. ''' l = [] readNumeric = False sb = "" # NO UNICODE STUFF #ba = command.encode(encoding="latin_1") #cmd = ba.decode() # Direct access to string data for c in command: if readNumeric and not c.isdigit() and c != '.': readNumeric = False l.append(sb) sb = "" if c.isalpha(): sb += c elif c.isdigit() or c == '.' or c == '-': sb += c readNumeric = True elif c.isalpha(): sb += c if len(sb) > 0: l.append(sb) return l @classmethod def parseCoord(cls, argList: List[str], c: str) -> float: ''' TODO: Replace everything that uses this with a loop that loops through the string and creates a hash with all the values. ''' for t in argList: if len(t) > 0 and t[0].upper() == c: return float(t[1:]) return qQNaN() @classmethod def convertRToCenter(cls, start: QVector3D, end: QVector3D, radius: float, absoluteIJK: bool, clockwise: bool) -> QVector3D: R = radius center = QVector3D() x = end.x() - start.x() y = end.y() - start.y() h_x2_div_d = 4 * R * R - x * x - y * y if h_x2_div_d < 0: print("Error computing arc radius.") h_x2_div_d = (-math.sqrt(h_x2_div_d)) / math.hypot(x, y) if not clockwise: h_x2_div_d = -h_x2_div_d # Special message from gcoder to software for which radius # should be used. if R < 0: h_x2_div_d = -h_x2_div_d # TODO: Places that use this need to run ABS on radius. radius = -radius offsetX = 0.5 * (x - (y * h_x2_div_d)) offsetY = 0.5 * (y + (x * h_x2_div_d)) if not absoluteIJK: center.setX(start.x() + offsetX) center.setY(start.y() + offsetY) else: center.setX(offsetX) center.setY(offsetY) return center @classmethod def getAngle(cls, start: QVector3D, end: QVector3D) -> float: ''' Return the angle in radians when going from start to end. ''' deltaX = end.x() - start.x() deltaY = end.y() - start.y() angle = 0.0 if deltaX != 0: # prevent div by 0 # it helps to know what quadrant you are in if deltaX > 0 and deltaY >= 0: # 0 - 90 angle = math.atan(deltaY / deltaX) elif deltaX < 0 and deltaY >= 0: # 90 to 180 angle = cls.M_PI - math.fabs(math.atan(deltaY / deltaX)) elif deltaX < 0 and deltaY < 0: # 180 - 270 angle = cls.M_PI + math.fabs(math.atan(deltaY / deltaX)) elif deltaX > 0 and deltaY < 0: # 270 - 360 angle = cls.M_PI * 2 - math.fabs(math.atan(deltaY / deltaX)) else: # 90 deg if deltaY > 0: angle = cls.M_PI / 2.0 #270 deg else: angle = cls.M_PI * 3.0 / 2.0 return angle @classmethod def calculateSweep(cls, startAngle: float, endAngle: float, isCw: bool) -> float: sweep = 0.0 # Full circle if startAngle == endAngle: sweep = cls.M_PI * 2 #Arcs else: # Account for full circles and end angles of 0/360 if endAngle == 0: endAngle = cls.M_PI * 2 # Calculate distance along arc. if (not isCw) and endAngle < startAngle: sweep = (cls.M_PI * 2 - startAngle) + endAngle elif isCw and endAngle > startAngle: sweep = (cls.M_PI * 2 - endAngle) + startAngle else: sweep = math.fabs(endAngle - startAngle) return sweep @classmethod def generatePointsAlongArcBDring(cls, plane: PointSegment.Plane, start: QVector3D, end: QVector3D, center: QVector3D, clockwise: bool, R: float, minArcLength: float, arcPrecision: float, LAST_ARG: Any) -> List[QVector3D]: ''' DUMMY DISPATCH ''' if isinstance(LAST_ARG, bool): return cls.generatePointsAlongArcBDring_Arc( plane, start, end, center, clockwise, R, minArcLength, arcPrecision, LAST_ARG) else: return cls.generatePointsAlongArcBDring_Num( plane, start, end, center, clockwise, R, minArcLength, arcPrecision, LAST_ARG) @classmethod def generatePointsAlongArcBDring_Arc( cls, plane: PointSegment.Plane, start: QVector3D, end: QVector3D, center: QVector3D, clockwise: bool, R: float, minArcLength: float, arcPrecision: float, arcDegreeMode: bool) -> List[QVector3D]: ''' Generates the points along an arc including the start and end points. ''' radius = R # Rotate vectors according to plane m = QMatrix4x4() m.setToIdentity() if plane == PointSegment.Plane.XY: pass elif plane == PointSegment.Plane.ZX: m.rotate(90, 1.0, 0.0, 0.0) elif plane == PointSegment.Plane.YZ: m.rotate(-90, 0.0, 1.0, 0.0) start = m * start end = m * end center = m * center # Check center if qIsNaN(center.length()): return [] # Calculate radius if necessary. if radius == 0: radius = math.sqrt( math.pow((start.x() - center.x(), 2.0) + math.pow(end.y() - center.y(), 2.0))) startAngle = cls.getAngle(center, start) endAngle = cls.getAngle(center, end) sweep = cls.calculateSweep(startAngle, endAngle, clockwise) # Convert units. arcLength = sweep * radius numPoints = 0 if arcDegreeMode and arcPrecision > 0: numPoints = max(1.0, sweep / (cls.M_PI * arcPrecision / 180)) else: if arcPrecision <= 0 and minArcLength > 0: arcPrecision = minArcLength numPoints = math.ceil(arcLength / arcPrecision) return cls.generatePointsAlongArcBDring_Num(plane, start, end, center, clockwise, radius, startAngle, sweep, numPoints) @classmethod def generatePointsAlongArcBDring_Num(cls, plane: PointSegment.Plane, p1: QVector3D, p2: QVector3D, center: QVector3D, isCw: bool, radius: float, startAngle: float, sweep: float, numPoints: int) -> List[QVector3D]: ''' Generates the points along an arc including the start and end points. ''' # Prepare rotation matrix to restore plane m = QMatrix4x4() m.setToIdentity() if plane == PointSegment.plane.XY: pass elif plane == PointSegment.plane.ZX: m.rotate(-90, 1.0, 0.0, 0.0) elif plane == PointSegment.plane.YZ: m.rotate(90, 0.0, 1.0, 0.0) lineEnd = QVector3D(p2.x(), p2.y(), p1.z()) segments = [] angle = 0.0 # Calculate radius if necessary. if radius == 0: radius = math.sqrt( math.pow((p1.x() - center.x()), 2.0) + math.pow((p1.y() - center.y()), 2.0)) zIncrement = (p2.z() - p1.z()) / numPoints for i in range(numPoints): if isCw: angle = (startAngle - i * sweep / numPoints) else: angle = (startAngle + i * sweep / numPoints) if angle >= cls.M_PI * 2: angle = angle - cls.M_PI * 2 lineEnd.setX(math.cos(angle) * radius + center.x()) lineEnd.setY(math.sin(angle) * radius + center.y()) lineEnd.setZ(lineEnd.z() + zIncrement) segments.append(m * lineEnd) segments.append(m * p2) return segments
def setFilter(self, search: str) -> None: self.filtermodel.setFilterRegularExpression( QRegularExpression(search, QRegularExpression.CaseInsensitiveOption))
def init_layout(self): """ Initialize the layout for the DICOM View tab. Add the view widget and the slider in the layout. Add the whole container 'tab2_view' as a tab in the main page. """ # Initialise a DrawROIWindow if platform.system() == 'Darwin': self.stylesheet_path = "res/stylesheet.qss" else: self.stylesheet_path = "res/stylesheet-win-linux.qss" stylesheet = open(resource_path(self.stylesheet_path)).read() window_icon = QIcon() window_icon.addPixmap(QPixmap(resource_path("res/images/icon.ico")), QIcon.Normal, QIcon.Off) self.draw_roi_window_instance.setObjectName("DrawRoiWindowInstance") self.draw_roi_window_instance.setWindowIcon(window_icon) # Creating a form box to hold all buttons and input fields self.draw_roi_window_input_container_box = QFormLayout() self.draw_roi_window_input_container_box. \ setObjectName("DrawRoiWindowInputContainerBox") self.draw_roi_window_input_container_box. \ setLabelAlignment(Qt.AlignLeft) # Create a label for denoting the ROI name self.roi_name_label = QLabel() self.roi_name_label.setObjectName("ROINameLabel") self.roi_name_line_edit = QLineEdit() # Create an input box for ROI name self.roi_name_line_edit.setObjectName("ROINameLineEdit") self.roi_name_line_edit.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.roi_name_line_edit.resize( self.roi_name_line_edit.sizeHint().width(), self.roi_name_line_edit.sizeHint().height()) self.roi_name_line_edit.setEnabled(False) self.draw_roi_window_input_container_box. \ addRow(self.roi_name_label, self.roi_name_line_edit) # Create horizontal box to store image slice number and backward, # forward buttons self.image_slice_number_box = QHBoxLayout() self.image_slice_number_box.setObjectName("ImageSliceNumberBox") # Create a label for denoting the Image Slice Number self.image_slice_number_label = QLabel() self.image_slice_number_label.setObjectName("ImageSliceNumberLabel") self.image_slice_number_box.addWidget(self.image_slice_number_label) # Create a line edit for containing the image slice number self.image_slice_number_line_edit = QLineEdit() self.image_slice_number_line_edit. \ setObjectName("ImageSliceNumberLineEdit") self.image_slice_number_line_edit. \ setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.image_slice_number_line_edit.resize( self.image_slice_number_line_edit.sizeHint().width(), self.image_slice_number_line_edit.sizeHint().height()) self.image_slice_number_line_edit.setCursorPosition(0) self.image_slice_number_line_edit.setEnabled(False) self.image_slice_number_box. \ addWidget(self.image_slice_number_line_edit) # Create a button to move backward to the previous image self.image_slice_number_move_backward_button = QPushButton() self.image_slice_number_move_backward_button. \ setObjectName("ImageSliceNumberMoveBackwardButton") self.image_slice_number_move_backward_button.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.image_slice_number_move_backward_button.resize(QSize(24, 24)) self.image_slice_number_move_backward_button.clicked. \ connect(self.onBackwardClicked) icon_move_backward = QtGui.QIcon() icon_move_backward.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/backward_slide_icon.png'))) self.image_slice_number_move_backward_button.setIcon( icon_move_backward) self.image_slice_number_box. \ addWidget(self.image_slice_number_move_backward_button) # Create a button to move forward to the next image self.image_slice_number_move_forward_button = QPushButton() self.image_slice_number_move_forward_button. \ setObjectName("ImageSliceNumberMoveForwardButton") self.image_slice_number_move_forward_button.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.image_slice_number_move_forward_button.resize(QSize(24, 24)) self.image_slice_number_move_forward_button.clicked. \ connect(self.onForwardClicked) icon_move_forward = QtGui.QIcon() icon_move_forward.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/forward_slide_icon.png'))) self.image_slice_number_move_forward_button.setIcon(icon_move_forward) self.image_slice_number_box. \ addWidget(self.image_slice_number_move_forward_button) self.draw_roi_window_input_container_box. \ addRow(self.image_slice_number_box) # Create a horizontal box for containing the zoom function self.draw_roi_window_viewport_zoom_box = QHBoxLayout() self.draw_roi_window_viewport_zoom_box.setObjectName( "DrawRoiWindowViewportZoomBox") # Create a label for zooming self.draw_roi_window_viewport_zoom_label = QLabel() self.draw_roi_window_viewport_zoom_label. \ setObjectName("DrawRoiWindowViewportZoomLabel") # Create an input box for zoom factor self.draw_roi_window_viewport_zoom_input = QLineEdit() self.draw_roi_window_viewport_zoom_input. \ setObjectName("DrawRoiWindowViewportZoomInput") self.draw_roi_window_viewport_zoom_input. \ setText("{:.2f}".format(self.zoom * 100) + "%") self.draw_roi_window_viewport_zoom_input.setCursorPosition(0) self.draw_roi_window_viewport_zoom_input.setEnabled(False) self.draw_roi_window_viewport_zoom_input. \ setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.draw_roi_window_viewport_zoom_input.resize( self.draw_roi_window_viewport_zoom_input.sizeHint().width(), self.draw_roi_window_viewport_zoom_input.sizeHint().height()) # Create 2 buttons for zooming in and out # Zoom In Button self.draw_roi_window_viewport_zoom_in_button = QPushButton() self.draw_roi_window_viewport_zoom_in_button. \ setObjectName("DrawRoiWindowViewportZoomInButton") self.draw_roi_window_viewport_zoom_in_button.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.draw_roi_window_viewport_zoom_in_button.resize(QSize(24, 24)) self.draw_roi_window_viewport_zoom_in_button. \ setProperty("QPushButtonClass", "zoom-button") icon_zoom_in = QtGui.QIcon() icon_zoom_in.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/zoom_in_icon.png'))) self.draw_roi_window_viewport_zoom_in_button.setIcon(icon_zoom_in) self.draw_roi_window_viewport_zoom_in_button.clicked. \ connect(self.onZoomInClicked) # Zoom Out Button self.draw_roi_window_viewport_zoom_out_button = QPushButton() self.draw_roi_window_viewport_zoom_out_button. \ setObjectName("DrawRoiWindowViewportZoomOutButton") self.draw_roi_window_viewport_zoom_out_button.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.draw_roi_window_viewport_zoom_out_button.resize(QSize(24, 24)) self.draw_roi_window_viewport_zoom_out_button. \ setProperty("QPushButtonClass", "zoom-button") icon_zoom_out = QtGui.QIcon() icon_zoom_out.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/zoom_out_icon.png'))) self.draw_roi_window_viewport_zoom_out_button.setIcon(icon_zoom_out) self.draw_roi_window_viewport_zoom_out_button.clicked. \ connect(self.onZoomOutClicked) self.draw_roi_window_viewport_zoom_box. \ addWidget(self.draw_roi_window_viewport_zoom_label) self.draw_roi_window_viewport_zoom_box. \ addWidget(self.draw_roi_window_viewport_zoom_input) self.draw_roi_window_viewport_zoom_box. \ addWidget(self.draw_roi_window_viewport_zoom_out_button) self.draw_roi_window_viewport_zoom_box. \ addWidget(self.draw_roi_window_viewport_zoom_in_button) self.draw_roi_window_input_container_box. \ addRow(self.draw_roi_window_viewport_zoom_box) self.init_cursor_radius_change_box() # Create field to toggle two options: Keep empty pixel or fill empty # pixel when using draw cursor self.toggle_keep_empty_pixel_box = QHBoxLayout() self.toggle_keep_empty_pixel_label = QLabel() self.toggle_keep_empty_pixel_label. \ setObjectName("ToggleKeepEmptyPixelLabel") # Create input for min pixel size self.toggle_keep_empty_pixel_combo_box = QComboBox() self.toggle_keep_empty_pixel_combo_box.addItems(["Off", "On"]) self.toggle_keep_empty_pixel_combo_box.setCurrentIndex(0) self.toggle_keep_empty_pixel_combo_box.setEnabled(False) self.toggle_keep_empty_pixel_combo_box. \ setObjectName("ToggleKeepEmptyPixelComboBox") self.toggle_keep_empty_pixel_combo_box. \ setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.toggle_keep_empty_pixel_combo_box.resize( self.toggle_keep_empty_pixel_combo_box.sizeHint().width(), self.toggle_keep_empty_pixel_combo_box.sizeHint().height()) self.toggle_keep_empty_pixel_combo_box.currentIndexChanged.connect( self.toggle_keep_empty_pixel_box_index_changed) self.toggle_keep_empty_pixel_box. \ addWidget(self.toggle_keep_empty_pixel_label) self.toggle_keep_empty_pixel_box. \ addWidget(self.toggle_keep_empty_pixel_combo_box) self.draw_roi_window_input_container_box. \ addRow(self.toggle_keep_empty_pixel_box) # Create a horizontal box for transect and draw button self.draw_roi_window_transect_draw_box = QHBoxLayout() self.draw_roi_window_transect_draw_box. \ setObjectName("DrawRoiWindowTransectDrawBox") # Create a transect button self.image_slice_number_transect_button = QPushButton() self.image_slice_number_transect_button. \ setObjectName("ImageSliceNumberTransectButton") self.image_slice_number_transect_button.setSizePolicy( QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)) self.image_slice_number_transect_button.resize( self.image_slice_number_transect_button.sizeHint().width(), self.image_slice_number_transect_button.sizeHint().height()) self.image_slice_number_transect_button.clicked. \ connect(self.transect_handler) icon_transect = QtGui.QIcon() icon_transect.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/transect_icon.png'))) self.image_slice_number_transect_button.setIcon(icon_transect) self.draw_roi_window_transect_draw_box. \ addWidget(self.image_slice_number_transect_button) # Create a bounding box button self.image_slice_number_box_draw_button = QPushButton() self.image_slice_number_box_draw_button. \ setObjectName("ImageSliceNumberBoxDrawButton") self.image_slice_number_box_draw_button.setSizePolicy( QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.image_slice_number_box_draw_button.resize( self.image_slice_number_box_draw_button.sizeHint().width(), self.image_slice_number_box_draw_button.sizeHint().height()) self.image_slice_number_box_draw_button.clicked. \ connect(self.onBoxDrawClicked) icon_box_draw = QtGui.QIcon() icon_box_draw.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/draw_bound_icon.png'))) self.image_slice_number_box_draw_button.setIcon(icon_box_draw) self.draw_roi_window_transect_draw_box. \ addWidget(self.image_slice_number_box_draw_button) # Create a draw button self.image_slice_number_draw_button = QPushButton() self.image_slice_number_draw_button. \ setObjectName("ImageSliceNumberDrawButton") self.image_slice_number_draw_button.setSizePolicy( QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)) self.image_slice_number_draw_button.resize( self.image_slice_number_draw_button.sizeHint().width(), self.image_slice_number_draw_button.sizeHint().height()) self.image_slice_number_draw_button.clicked.connect(self.onDrawClicked) icon_draw = QtGui.QIcon() icon_draw.addPixmap( QtGui.QPixmap(resource_path('res/images/btn-icons/draw_icon.png'))) self.image_slice_number_draw_button.setIcon(icon_draw) self.draw_roi_window_transect_draw_box. \ addWidget(self.image_slice_number_draw_button) self.draw_roi_window_input_container_box. \ addRow(self.draw_roi_window_transect_draw_box) # Create a contour preview button self.row_preview_layout = QtWidgets.QHBoxLayout() self.button_contour_preview = QtWidgets.QPushButton("Preview contour") self.button_contour_preview.clicked.connect(self.onPreviewClicked) self.row_preview_layout.addWidget(self.button_contour_preview) self.draw_roi_window_input_container_box. \ addRow(self.row_preview_layout) icon_preview = QtGui.QIcon() icon_preview.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/preview_icon.png'))) self.button_contour_preview.setIcon(icon_preview) # Create input line edit for alpha value self.label_alpha_value = QtWidgets.QLabel("Alpha value:") self.input_alpha_value = QtWidgets.QLineEdit("0.2") self.input_alpha_value. \ setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.input_alpha_value.resize( self.input_alpha_value.sizeHint().width(), self.input_alpha_value.sizeHint().height()) self.input_alpha_value.setValidator( QRegularExpressionValidator( QRegularExpression("^[0-9]*[.]?[0-9]*$"))) self.draw_roi_window_input_container_box. \ addRow(self.label_alpha_value, self.input_alpha_value) # Create a label for denoting the max internal hole size self.internal_hole_max_label = QLabel() self.internal_hole_max_label.setObjectName("InternalHoleLabel") # Create input for max internal hole size self.internal_hole_max_line_edit = QLineEdit() self.internal_hole_max_line_edit.setObjectName("InternalHoleInput") self.internal_hole_max_line_edit. \ setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.internal_hole_max_line_edit.resize( self.internal_hole_max_line_edit.sizeHint().width(), self.internal_hole_max_line_edit.sizeHint().height()) self.internal_hole_max_line_edit.setValidator( QRegularExpressionValidator( QRegularExpression("^[0-9]*[.]?[0-9]*$"))) self.draw_roi_window_input_container_box.addRow( self.internal_hole_max_label, self.internal_hole_max_line_edit) # Create a label for denoting the isthmus width size self.isthmus_width_max_label = QLabel() self.isthmus_width_max_label.setObjectName("IsthmusWidthLabel") # Create input for max isthmus width size self.isthmus_width_max_line_edit = QLineEdit() self.isthmus_width_max_line_edit.setObjectName("IsthmusWidthInput") self.isthmus_width_max_line_edit.setSizePolicy( QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.isthmus_width_max_line_edit.resize( self.isthmus_width_max_line_edit.sizeHint().width(), self.isthmus_width_max_line_edit.sizeHint().height()) self.isthmus_width_max_line_edit.setValidator( QRegularExpressionValidator( QRegularExpression("^[0-9]*[.]?[0-9]*$"))) self.draw_roi_window_input_container_box.addRow( self.isthmus_width_max_label, self.isthmus_width_max_line_edit) # Create a label for denoting the minimum pixel density self.min_pixel_density_label = QLabel() self.min_pixel_density_label.setObjectName("MinPixelDensityLabel") # Create input for min pixel size self.min_pixel_density_line_edit = QLineEdit() self.min_pixel_density_line_edit.setObjectName("MinPixelDensityInput") self.min_pixel_density_line_edit.setSizePolicy( QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.min_pixel_density_line_edit.resize( self.min_pixel_density_line_edit.sizeHint().width(), self.min_pixel_density_line_edit.sizeHint().height()) self.min_pixel_density_line_edit.setValidator( QRegularExpressionValidator( QRegularExpression("^[0-9]*[.]?[0-9]*$"))) self.draw_roi_window_input_container_box.addRow( self.min_pixel_density_label, self.min_pixel_density_line_edit) # Create a label for denoting the minimum pixel density self.max_pixel_density_label = QLabel() self.max_pixel_density_label.setObjectName("MaxPixelDensityLabel") # Create input for min pixel size self.max_pixel_density_line_edit = QLineEdit() self.max_pixel_density_line_edit.setObjectName("MaxPixelDensityInput") self.max_pixel_density_line_edit. \ setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.max_pixel_density_line_edit.resize( self.max_pixel_density_line_edit.sizeHint().width(), self.max_pixel_density_line_edit.sizeHint().height()) self.max_pixel_density_line_edit.setValidator( QRegularExpressionValidator( QRegularExpression("^[0-9]*[.]?[0-9]*$"))) self.draw_roi_window_input_container_box.addRow( self.max_pixel_density_label, self.max_pixel_density_line_edit) # Create a button to clear the draw self.draw_roi_window_instance_action_reset_button = QPushButton() self.draw_roi_window_instance_action_reset_button. \ setObjectName("DrawRoiWindowInstanceActionClearButton") self.draw_roi_window_instance_action_reset_button.setSizePolicy( QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)) reset_button = self.draw_roi_window_instance_action_reset_button self.draw_roi_window_instance_action_reset_button.resize( reset_button.sizeHint().width(), reset_button.sizeHint().height()) self.draw_roi_window_instance_action_reset_button.clicked. \ connect(self.onResetClicked) self.draw_roi_window_instance_action_reset_button. \ setProperty("QPushButtonClass", "fail-button") icon_clear_roi_draw = QtGui.QIcon() icon_clear_roi_draw.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/reset_roi_draw_icon.png'))) self.draw_roi_window_instance_action_reset_button. \ setIcon(icon_clear_roi_draw) self.draw_roi_window_input_container_box. \ addRow(self.draw_roi_window_instance_action_reset_button) # Create a horizontal box for saving and cancel the drawing self.draw_roi_window_cancel_save_box = QHBoxLayout() self.draw_roi_window_cancel_save_box. \ setObjectName("DrawRoiWindowCancelSaveBox") # Create an exit button to cancel the drawing # Add a button to go back/exit from the application self.draw_roi_window_instance_cancel_button = QPushButton() self.draw_roi_window_instance_cancel_button. \ setObjectName("DrawRoiWindowInstanceCancelButton") self.draw_roi_window_instance_cancel_button.setSizePolicy( QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)) self.draw_roi_window_instance_cancel_button.resize( self.draw_roi_window_instance_cancel_button.sizeHint().width(), self.draw_roi_window_instance_cancel_button.sizeHint().height()) self.draw_roi_window_instance_cancel_button. \ setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.draw_roi_window_instance_cancel_button.clicked. \ connect(self.onCancelButtonClicked) self.draw_roi_window_instance_cancel_button. \ setProperty("QPushButtonClass", "fail-button") icon_cancel = QtGui.QIcon() icon_cancel.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/cancel_icon.png'))) self.draw_roi_window_instance_cancel_button.setIcon(icon_cancel) self.draw_roi_window_cancel_save_box. \ addWidget(self.draw_roi_window_instance_cancel_button) # Create a save button to save all the changes self.draw_roi_window_instance_save_button = QPushButton() self.draw_roi_window_instance_save_button. \ setObjectName("DrawRoiWindowInstanceSaveButton") self.draw_roi_window_instance_save_button.setSizePolicy( QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)) self.draw_roi_window_instance_save_button.resize( self.draw_roi_window_instance_save_button.sizeHint().width(), self.draw_roi_window_instance_save_button.sizeHint().height()) self.draw_roi_window_instance_save_button. \ setProperty("QPushButtonClass", "success-button") icon_save = QtGui.QIcon() icon_save.addPixmap( QtGui.QPixmap(resource_path('res/images/btn-icons/save_icon.png'))) self.draw_roi_window_instance_save_button.setIcon(icon_save) self.draw_roi_window_instance_save_button.clicked. \ connect(self.onSaveClicked) self.draw_roi_window_cancel_save_box. \ addWidget(self.draw_roi_window_instance_save_button) self.draw_roi_window_input_container_box. \ addRow(self.draw_roi_window_cancel_save_box) # Creating a horizontal box to hold the ROI view and slider self.draw_roi_window_instance_view_box = QHBoxLayout() self.draw_roi_window_instance_view_box. \ setObjectName("DrawRoiWindowInstanceViewBox") # Add View and Slider into horizontal box self.draw_roi_window_instance_view_box.addWidget(self.dicom_view) # Create a widget to hold the image slice box self.draw_roi_window_instance_view_widget = QWidget() self.draw_roi_window_instance_view_widget.setObjectName( "DrawRoiWindowInstanceActionWidget") self.draw_roi_window_instance_view_widget.setLayout( self.draw_roi_window_instance_view_box) # Create a horizontal box for containing the input fields and the # viewport self.draw_roi_window_main_box = QHBoxLayout() self.draw_roi_window_main_box.setObjectName("DrawRoiWindowMainBox") self.draw_roi_window_main_box. \ addLayout(self.draw_roi_window_input_container_box, 1) self.draw_roi_window_main_box. \ addWidget(self.draw_roi_window_instance_view_widget, 11) # Create a new central widget to hold the vertical box layout self.draw_roi_window_instance_central_widget = QWidget() self.draw_roi_window_instance_central_widget. \ setObjectName("DrawRoiWindowInstanceCentralWidget") self.draw_roi_window_instance_central_widget.setLayout( self.draw_roi_window_main_box) self.retranslate_ui(self.draw_roi_window_instance) self.draw_roi_window_instance.setStyleSheet(stylesheet) self.draw_roi_window_instance. \ setCentralWidget(self.draw_roi_window_instance_central_widget) QtCore.QMetaObject.connectSlotsByName(self.draw_roi_window_instance)
def __init__(self, parent=None): self.bool_exp = QRegularExpression('^(true)|(false)$') assert self.bool_exp.isValid() self.bool_exp.setPatternOptions(QRegularExpression.CaseInsensitiveOption) self.byteArray_exp = QRegularExpression(r'^[\x00-\xff]*$') assert self.byteArray_exp.isValid() self.char_exp = QRegularExpression('^.$') assert self.char_exp.isValid() pattern = r'^[+-]?\d+$' self.int_exp = QRegularExpression(pattern) assert self.int_exp.isValid() pattern = r'^\(([0-9]*),([0-9]*),([0-9]*),([0-9]*)\)$' self.color_exp = QRegularExpression(pattern) assert self.color_exp.isValid() pattern = r'^\((-?[0-9]*),(-?[0-9]*)\)$' self.point_exp = QRegularExpression(pattern) assert self.point_exp.isValid() pattern = r'^\((-?[0-9]*),(-?[0-9]*),(-?[0-9]*),(-?[0-9]*)\)$' self.rect_exp = QRegularExpression(pattern) assert self.rect_exp.isValid() self.size_exp = QRegularExpression(self.point_exp) date_pattern = '([0-9]{,4})-([0-9]{,2})-([0-9]{,2})' self.date_exp = QRegularExpression('^{}$'.format(date_pattern)) assert self.date_exp.isValid() time_pattern = '([0-9]{,2}):([0-9]{,2}):([0-9]{,2})' self.time_exp = QRegularExpression('^{}$'.format(time_pattern)) assert self.time_exp.isValid() pattern = '^{}T{}$'.format(date_pattern, time_pattern) self.dateTime_exp = QRegularExpression(pattern) assert self.dateTime_exp.isValid()
class TypeChecker: def __init__(self, parent=None): self.bool_exp = QRegularExpression('^(true)|(false)$') assert self.bool_exp.isValid() self.bool_exp.setPatternOptions(QRegularExpression.CaseInsensitiveOption) self.byteArray_exp = QRegularExpression(r'^[\x00-\xff]*$') assert self.byteArray_exp.isValid() self.char_exp = QRegularExpression('^.$') assert self.char_exp.isValid() pattern = r'^[+-]?\d+$' self.int_exp = QRegularExpression(pattern) assert self.int_exp.isValid() pattern = r'^\(([0-9]*),([0-9]*),([0-9]*),([0-9]*)\)$' self.color_exp = QRegularExpression(pattern) assert self.color_exp.isValid() pattern = r'^\((-?[0-9]*),(-?[0-9]*)\)$' self.point_exp = QRegularExpression(pattern) assert self.point_exp.isValid() pattern = r'^\((-?[0-9]*),(-?[0-9]*),(-?[0-9]*),(-?[0-9]*)\)$' self.rect_exp = QRegularExpression(pattern) assert self.rect_exp.isValid() self.size_exp = QRegularExpression(self.point_exp) date_pattern = '([0-9]{,4})-([0-9]{,2})-([0-9]{,2})' self.date_exp = QRegularExpression('^{}$'.format(date_pattern)) assert self.date_exp.isValid() time_pattern = '([0-9]{,2}):([0-9]{,2}):([0-9]{,2})' self.time_exp = QRegularExpression('^{}$'.format(time_pattern)) assert self.time_exp.isValid() pattern = '^{}T{}$'.format(date_pattern, time_pattern) self.dateTime_exp = QRegularExpression(pattern) assert self.dateTime_exp.isValid() def type_from_text(self, text): if self.bool_exp.match(text).hasMatch(): return bool if self.int_exp.match(text).hasMatch(): return int return None def create_validator(self, value, parent): if isinstance(value, bool): return QRegularExpressionValidator(self.bool_exp, parent) if isinstance(value, float): return QDoubleValidator(parent) if isinstance(value, int): return QIntValidator(parent) if isinstance(value, QByteArray): return QRegularExpressionValidator(self.byteArray_exp, parent) if isinstance(value, QColor): return QRegularExpressionValidator(self.color_exp, parent) if isinstance(value, QDate): return QRegularExpressionValidator(self.date_exp, parent) if isinstance(value, QDateTime): return QRegularExpressionValidator(self.dateTime_exp, parent) if isinstance(value, QTime): return QRegularExpressionValidator(self.time_exp, parent) if isinstance(value, QPoint): return QRegularExpressionValidator(self.point_exp, parent) if isinstance(value, QRect): return QRegularExpressionValidator(self.rect_exp, parent) if isinstance(value, QSize): return QRegularExpressionValidator(self.size_exp, parent) return None def from_string(self, text, original_value): if isinstance(original_value, QColor): match = self.color_exp.match(text) return QColor(min(int(match.captured(1)), 255), min(int(match.captured(2)), 255), min(int(match.captured(3)), 255), min(int(match.captured(4)), 255)) if isinstance(original_value, QDate): value = QDate.fromString(text, Qt.ISODate) return value if value.isValid() else None if isinstance(original_value, QDateTime): value = QDateTime.fromString(text, Qt.ISODate) return value if value.isValid() else None if isinstance(original_value, QTime): value = QTime.fromString(text, Qt.ISODate) return value if value.isValid() else None if isinstance(original_value, QPoint): match = self.point_exp.match(text) return QPoint(int(match.captured(1)), int(match.captured(2))) if isinstance(original_value, QRect): match = self.rect_exp.match(text) return QRect(int(match.captured(1)), int(match.captured(2)), int(match.captured(3)), int(match.captured(4))) if isinstance(original_value, QSize): match = self.size_exp.match(text) return QSize(int(match.captured(1)), int(match.captured(2))) if isinstance(original_value, list): return text.split(',') return type(original_value)(text)
class GCodeSyntaxHighlightDelegate(QtWidgets.QStyledItemDelegate): ''' Html of QTextDocument can be painted Note: QPlainTextEdit document FAILS to be painted... ''' HTML_STYLES = { 'comment': "color:darkGreen;font-style:italic", 'keyword': "color:blue;font-weight:bold", 'numbers': "color:brown", 'numbersZ': "color:magenta", } # The rules rules = [ # From ';' until a newline (r';[^\n]*', 0, HTML_STYLES['comment']), # Positions Numeric literals (r'\b[XY][+-]?[0-9]+(?:\.[0-9]+)?\b', 0, HTML_STYLES['numbers']), (r'\b[Z][+-]?[0-9]+(?:\.[0-9]+)?\b', 0, HTML_STYLES['numbersZ']), # Others Numeric literals (r'\b[GSMF][0-9]+?\b', 0, HTML_STYLES['keyword']) ] # Build a QRegularExpression for each pattern RULES = [(QRegularExpression(pat), index, style) for (pat, index, style) in rules] def __init__(self, parent): ''' ''' super().__init__(parent) def paint(self, painter: QtGui.QPainter, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex): painter.save() options = QtWidgets.QStyleOptionViewItem(option) self.initStyleOption(options, index) painter.translate(options.rect.left(), options.rect.top()) doc = QtGui.QTextDocument() doc.setHtml(self.makeHtml(index.data())) doc.drawContents(painter) painter.restore() def makeHtml(self, text: str): ''' ''' html_parts = {} for expression, nth, style in self.RULES: nth = 0 match = expression.match(text, offset=0) index = match.capturedStart() while index >= 0: # We actually want the index of the nth match index = match.capturedStart(nth) length = match.capturedLength(nth) ####self.setFormat(index, length, format) html_parts[index] = '<span style="%s">%s</span>' % ( style, text[index:index + length]) # check the rest of the string match = expression.match(text, offset=index + length) index = match.capturedStart() # sorted by index keys = list(html_parts.keys()) keys.sort() htmls = [] for key in keys: htmls.append(html_parts[key]) html = "<p>" + " ".join(htmls) + "</p>" #print("------------------------------------------") #print("TEXT -> ", text) #print("HTML -> ", html) return html
def init_layout(self): """ Initialize the layout for the DICOM View tab. Add the view widget and the slider in the layout. Add the whole container 'tab2_view' as a tab in the main page. """ # Initialise a ManipulateROIWindow if platform.system() == 'Darwin': self.stylesheet_path = "res/stylesheet.qss" else: self.stylesheet_path = "res/stylesheet-win-linux.qss" stylesheet = open(resource_path(self.stylesheet_path)).read() window_icon = QIcon() window_icon.addPixmap(QPixmap(resource_path("res/images/icon.ico")), QIcon.Normal, QIcon.Off) self.manipulate_roi_window_instance.setObjectName( "ManipulateRoiWindowInstance") self.manipulate_roi_window_instance.setWindowIcon(window_icon) # Creating a form box to hold all buttons and input fields self.manipulate_roi_window_input_container_box = QFormLayout() self.manipulate_roi_window_input_container_box.setObjectName( "ManipulateRoiWindowInputContainerBox") self.manipulate_roi_window_input_container_box.setLabelAlignment( Qt.AlignLeft) # Create a label for denoting the first ROI name self.first_roi_name_label = QLabel() self.first_roi_name_label.setObjectName("FirstROINameLabel") self.first_roi_name_dropdown_list = QComboBox() # Create an dropdown list for ROI name self.first_roi_name_dropdown_list.setObjectName( "FirstROINameDropdownList") self.first_roi_name_dropdown_list.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Minimum) self.first_roi_name_dropdown_list.resize( self.first_roi_name_dropdown_list.sizeHint().width(), self.first_roi_name_dropdown_list.sizeHint().height()) self.first_roi_name_dropdown_list.activated.connect( self.update_selected_rois) self.manipulate_roi_window_input_container_box.addRow( self.first_roi_name_label, self.first_roi_name_dropdown_list) # Create a label for denoting the operation self.operation_name_label = QLabel() self.operation_name_label.setObjectName("OperationNameLabel") self.operation_name_dropdown_list = QComboBox() # Create an dropdown list for operation name self.operation_name_dropdown_list.setObjectName( "OperationNameDropdownList") self.operation_name_dropdown_list.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Minimum) self.operation_name_dropdown_list.resize( self.operation_name_dropdown_list.sizeHint().width(), self.operation_name_dropdown_list.sizeHint().height()) self.operation_name_dropdown_list.activated.connect( self.operation_changed) self.manipulate_roi_window_input_container_box.addRow( self.operation_name_label, self.operation_name_dropdown_list) # Create a label for denoting the second ROI name self.second_roi_name_label = QLabel() self.second_roi_name_label.setObjectName("SecondROINameLabel") self.second_roi_name_label.setVisible(False) self.second_roi_name_dropdown_list = QComboBox() # Create an dropdown list for ROI name self.second_roi_name_dropdown_list.setObjectName( "SecondROINameDropdownList") self.second_roi_name_dropdown_list.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Minimum) self.second_roi_name_dropdown_list.resize( self.second_roi_name_dropdown_list.sizeHint().width(), self.second_roi_name_dropdown_list.sizeHint().height()) self.second_roi_name_dropdown_list.setVisible(False) self.second_roi_name_dropdown_list.activated.connect( self.update_selected_rois) self.manipulate_roi_window_input_container_box.addRow( self.second_roi_name_label, self.second_roi_name_dropdown_list) # Create a label for denoting the margin self.margin_label = QLabel() self.margin_label.setObjectName("MarginLabel") self.margin_label.setVisible(False) # Create input for the new ROI name self.margin_line_edit = QLineEdit() self.margin_line_edit.setObjectName("MarginInput") self.margin_line_edit.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.margin_line_edit.resize(self.margin_line_edit.sizeHint().width(), self.margin_line_edit.sizeHint().height()) self.margin_line_edit.setVisible(False) self.margin_line_edit.setValidator( QRegularExpressionValidator( QRegularExpression("^[0-9]*[.]?[0-9]*$"))) self.manipulate_roi_window_input_container_box.addRow( self.margin_label, self.margin_line_edit) # Create a label for denoting the new ROI name self.new_roi_name_label = QLabel() self.new_roi_name_label.setObjectName("NewROINameLabel") # Create input for the new ROI name self.new_roi_name_line_edit = QLineEdit() self.new_roi_name_line_edit.setObjectName("NewROINameInput") self.new_roi_name_line_edit.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.new_roi_name_line_edit.resize( self.new_roi_name_line_edit.sizeHint().width(), self.new_roi_name_line_edit.sizeHint().height()) self.manipulate_roi_window_input_container_box.addRow( self.new_roi_name_label, self.new_roi_name_line_edit) # Create a spacer between inputs and buttons spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) spacer.setFocusPolicy(Qt.NoFocus) self.manipulate_roi_window_input_container_box.addRow(spacer) # Create a warning message when missing inputs self.warning_message = QWidget() self.warning_message.setContentsMargins(8, 5, 8, 5) warning_message_layout = QHBoxLayout() warning_message_layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignLeft) warning_message_icon = QLabel() warning_message_icon.setPixmap( QtGui.QPixmap( resource_path("res/images/btn-icons/alert_icon.png"))) warning_message_layout.addWidget(warning_message_icon) self.warning_message_text = QLabel() self.warning_message_text.setStyleSheet("color: red") warning_message_layout.addWidget(self.warning_message_text) self.warning_message.setLayout(warning_message_layout) self.warning_message.setVisible(False) self.manipulate_roi_window_input_container_box.addRow( self.warning_message) # Create a draw button self.manipulate_roi_window_instance_draw_button = QPushButton() self.manipulate_roi_window_instance_draw_button.setObjectName( "ManipulateRoiWindowInstanceDrawButton") self.manipulate_roi_window_instance_draw_button.setSizePolicy( QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)) self.manipulate_roi_window_instance_draw_button.resize( self.manipulate_roi_window_instance_draw_button.sizeHint().width(), self.manipulate_roi_window_instance_draw_button.sizeHint().height( )) self.manipulate_roi_window_instance_draw_button.setCursor( QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.manipulate_roi_window_instance_draw_button.clicked.connect( self.onDrawButtonClicked) self.manipulate_roi_window_input_container_box.addRow( self.manipulate_roi_window_instance_draw_button) # Create a horizontal box for saving and cancel the drawing self.manipulate_roi_window_cancel_save_box = QHBoxLayout() self.manipulate_roi_window_cancel_save_box.setObjectName( "ManipulateRoiWindowCancelSaveBox") # Create an exit button to cancel the drawing # Add a button to go back/exit from the application self.manipulate_roi_window_instance_cancel_button = QPushButton() self.manipulate_roi_window_instance_cancel_button.setObjectName( "ManipulateRoiWindowInstanceCancelButton") self.manipulate_roi_window_instance_cancel_button.setSizePolicy( QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)) self.manipulate_roi_window_instance_cancel_button.resize( self.manipulate_roi_window_instance_cancel_button.sizeHint().width( ), self.manipulate_roi_window_instance_cancel_button.sizeHint(). height()) self.manipulate_roi_window_instance_cancel_button.setCursor( QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.manipulate_roi_window_instance_cancel_button.clicked.connect( self.onCancelButtonClicked) self.manipulate_roi_window_instance_cancel_button.setProperty( "QPushButtonClass", "fail-button") icon_cancel = QtGui.QIcon() icon_cancel.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/cancel_icon.png'))) self.manipulate_roi_window_instance_cancel_button.setIcon(icon_cancel) self.manipulate_roi_window_cancel_save_box.addWidget( self.manipulate_roi_window_instance_cancel_button) # Create a save button to save all the changes self.manipulate_roi_window_instance_save_button = QPushButton() self.manipulate_roi_window_instance_save_button.setObjectName( "ManipulateRoiWindowInstanceSaveButton") self.manipulate_roi_window_instance_save_button.setSizePolicy( QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)) self.manipulate_roi_window_instance_save_button.resize( self.manipulate_roi_window_instance_save_button.sizeHint().width(), self.manipulate_roi_window_instance_save_button.sizeHint().height( )) self.manipulate_roi_window_instance_save_button.setProperty( "QPushButtonClass", "success-button") icon_save = QtGui.QIcon() icon_save.addPixmap( QtGui.QPixmap(resource_path('res/images/btn-icons/save_icon.png'))) self.manipulate_roi_window_instance_save_button.setIcon(icon_save) self.manipulate_roi_window_instance_save_button.clicked.connect( self.onSaveClicked) self.manipulate_roi_window_cancel_save_box.addWidget( self.manipulate_roi_window_instance_save_button) self.manipulate_roi_window_input_container_box.addRow( self.manipulate_roi_window_cancel_save_box) # Creating a horizontal box to hold the ROI view and the preview self.manipulate_roi_window_instance_view_box = QHBoxLayout() self.manipulate_roi_window_instance_view_box.setObjectName( "ManipulateRoiWindowInstanceViewBoxes") # Font for the ROI view and preview's labels font = QFont() font.setBold(True) font.setPixelSize(20) # Creating the ROI view self.ROI_view_box_layout = QVBoxLayout() self.ROI_view_box_label = QLabel() self.ROI_view_box_label.setFont(font) self.ROI_view_box_label.setAlignment(Qt.AlignHCenter) self.ROI_view_box_layout.addWidget(self.ROI_view_box_label) self.ROI_view_box_layout.addWidget(self.dicom_view) self.ROI_view_box_widget = QWidget() self.ROI_view_box_widget.setLayout(self.ROI_view_box_layout) # Creating the preview self.preview_box_layout = QVBoxLayout() self.preview_box_label = QLabel() self.preview_box_label.setFont(font) self.preview_box_label.setAlignment(Qt.AlignHCenter) self.preview_box_layout.addWidget(self.preview_box_label) self.preview_box_layout.addWidget(self.dicom_preview) self.preview_box_widget = QWidget() self.preview_box_widget.setLayout(self.preview_box_layout) # Add View and Slider into horizontal box self.manipulate_roi_window_instance_view_box.addWidget( self.ROI_view_box_widget) self.manipulate_roi_window_instance_view_box.addWidget( self.preview_box_widget) # Create a widget to hold the image slice box self.manipulate_roi_window_instance_view_widget = QWidget() self.manipulate_roi_window_instance_view_widget.setObjectName( "ManipulateRoiWindowInstanceActionWidget") self.manipulate_roi_window_instance_view_widget.setLayout( self.manipulate_roi_window_instance_view_box) # Create a horizontal box for containing the input fields and the # viewports self.manipulate_roi_window_main_box = QHBoxLayout() self.manipulate_roi_window_main_box.setObjectName( "ManipulateRoiWindowMainBox") self.manipulate_roi_window_main_box.addLayout( self.manipulate_roi_window_input_container_box, 1) self.manipulate_roi_window_main_box.addWidget( self.manipulate_roi_window_instance_view_widget, 11) # Create a new central widget to hold the horizontal box layout self.manipulate_roi_window_instance_central_widget = QWidget() self.manipulate_roi_window_instance_central_widget.setObjectName( "ManipulateRoiWindowInstanceCentralWidget") self.manipulate_roi_window_instance_central_widget.setLayout( self.manipulate_roi_window_main_box) self.retranslate_ui(self.manipulate_roi_window_instance) self.manipulate_roi_window_instance.setStyleSheet(stylesheet) self.manipulate_roi_window_instance.setCentralWidget( self.manipulate_roi_window_instance_central_widget) QtCore.QMetaObject.connectSlotsByName( self.manipulate_roi_window_instance)
def setupUi(self): """ Constructs the GUI and sets the limit of each input field. """ # Create a vertical Widget to hold Vertical Layout self.vertical_layout_widget = QWidget() self.vertical_layout = QtWidgets.QVBoxLayout() # Create a Widget and set layout to a GridLayout self.gridLayoutWidget = QWidget() self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget) self.gridLayout.setSizeConstraint(QLayout.SetDefaultConstraint) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setVerticalSpacing(0) # Create horizontal spacer in the middle of the grid hspacer = QtWidgets.QSpacerItem(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.gridLayout.addItem(hspacer, 0, 5, 16, 1) # Labels self.fixed_image_label = QLabel("Fixed Image: ") fixed_image_sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred) fixed_image_sizePolicy.setHorizontalStretch(0) fixed_image_sizePolicy.setVerticalStretch(0) fixed_image_sizePolicy.setHeightForWidth( self.fixed_image_label.sizePolicy().hasHeightForWidth()) self.fixed_image_label.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) self.fixed_image_placeholder \ = QLabel("This is a placeholder for fixed image") self.fixed_image_placeholder.setWordWrap(False) self.fixed_image_placeholder.setText(str(self.fixed_image)) self.fixed_image_placeholder.setMaximumSize(200, 50) self.moving_image_label = QLabel("Moving Image: ") moving_image_label_sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) moving_image_label_sizePolicy.setHorizontalStretch(0) moving_image_label_sizePolicy.setVerticalStretch(0) moving_image_label_sizePolicy.setHeightForWidth( self.moving_image_label.sizePolicy().hasHeightForWidth()) self.moving_image_label.setSizePolicy(moving_image_label_sizePolicy) self.moving_image_placeholder = QLabel("This is a placeholder") self.moving_image_placeholder.setWordWrap(False) self.moving_image_placeholder.setText(str(self.moving_image)) self.moving_image_placeholder.setMaximumSize(200, 50) self.gridLayout.addWidget(self.fixed_image_label, 0, 0) self.gridLayout.addWidget(self.fixed_image_placeholder, 0, 1) self.gridLayout.addWidget(self.moving_image_label, 0, 2) self.gridLayout.addWidget(self.moving_image_placeholder, 0, 3) # Default Numbers self.default_numbers_label = QLabel("Default Numbers") self.default_numbers_label.setAlignment(Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.gridLayout.addWidget(self.default_numbers_label, 1, 0) self.default_number_spinBox = QSpinBox(self.gridLayoutWidget) self.default_number_spinBox.setRange(-2147483648, 2147483647) self.default_number_spinBox.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.default_number_spinBox.setToolTip( "Default voxel value. Defaults to -1000.") self.gridLayout.addWidget(self.default_number_spinBox, 1, 1) # Final Interp self.interp_order_label = QLabel("Final Interp") self.interp_order_label.setAlignment(Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.gridLayout.addWidget(self.interp_order_label, 2, 0) self.interp_order_spinbox = QSpinBox(self.gridLayoutWidget) self.interp_order_spinbox.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.interp_order_spinbox.setToolTip("The final interpolation order.") self.gridLayout.addWidget(self.interp_order_spinbox, 2, 1) # Metric self.metric_label = QLabel("Metric") self.metric_label.setAlignment(QtCore.Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.gridLayout.addWidget(self.metric_label, 3, 0) self.metric_comboBox = QComboBox() self.metric_comboBox.addItem("correlation") self.metric_comboBox.addItem("mean_squares") self.metric_comboBox.addItem("mattes_mi") self.metric_comboBox.addItem("joint_hist_mi") self.metric_comboBox.setToolTip( "The metric to be optimised during image registration.") self.gridLayout.addWidget(self.metric_comboBox, 3, 1) # Number of Iterations self.no_of_iterations_label = QLabel("Number of Iterations") self.no_of_iterations_label.setAlignment(Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.gridLayout.addWidget(self.no_of_iterations_label, 4, 0) self.no_of_iterations_spinBox = QSpinBox(self.gridLayoutWidget) self.no_of_iterations_spinBox.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.no_of_iterations_spinBox.setRange(0, 100) self.no_of_iterations_spinBox.setToolTip( "Number of iterations in each multi-resolution step.") self.gridLayout.addWidget(self.no_of_iterations_spinBox, 4, 1) # Shrink Factor self.shrink_factor_label = QLabel("Shrink Factor") self.shrink_factor_label.setAlignment(Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.gridLayout.addWidget(self.shrink_factor_label, 5, 0) self.shrink_factor_qLineEdit = QLineEdit() self.shrink_factor_qLineEdit.resize( self.shrink_factor_qLineEdit.sizeHint().width(), self.shrink_factor_qLineEdit.sizeHint().height()) self.shrink_factor_qLineEdit.setValidator( QRegularExpressionValidator( QRegularExpression("^[0-9]*[,]?[0-9]*[,]?[0-9]"))) self.shrink_factor_qLineEdit.setToolTip( "The multi-resolution downsampling factors. Can be up to three " "integer elements in an array. Example [8, 2, 1]") self.gridLayout.addWidget(self.shrink_factor_qLineEdit, 5, 1) # Optimiser self.optimiser_label = QLabel("Optimiser") self.optimiser_label.setAlignment(Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.gridLayout.addWidget(self.optimiser_label, 1, 2) self.optimiser_comboBox = QComboBox(self.gridLayoutWidget) self.optimiser_comboBox.addItem("lbfgsb") self.optimiser_comboBox.addItem("gradient_descent") self.optimiser_comboBox.addItem("gradient_descent_line_search") self.optimiser_comboBox.setToolTip( "The optimiser algorithm used for image registration.") self.gridLayout.addWidget(self.optimiser_comboBox, 1, 3) # Reg Method self.reg_method_label = QLabel("Reg Method") self.reg_method_label.setAlignment(QtCore.Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.gridLayout.addWidget(self.reg_method_label, 2, 2) self.reg_method_comboBox = QComboBox() self.reg_method_comboBox.addItem("translation") self.reg_method_comboBox.addItem("rigid") self.reg_method_comboBox.addItem("similarity") self.reg_method_comboBox.addItem("affine") self.reg_method_comboBox.addItem("scaleversor") self.reg_method_comboBox.addItem("scaleskewversor") self.reg_method_comboBox.setToolTip( "The linear transformation model to be used for image " "registration.") self.gridLayout.addWidget(self.reg_method_comboBox, 2, 3) # Sampling Rate self.sampling_rate_label = QLabel("Sampling Rate") self.sampling_rate_label.setAlignment(Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.gridLayout.addWidget(self.sampling_rate_label, 3, 2) self.sampling_rate_spinBox = QDoubleSpinBox(self.gridLayoutWidget) self.sampling_rate_spinBox.setMinimum(0) self.sampling_rate_spinBox.setMaximum(1) self.sampling_rate_spinBox.setSingleStep(0.01) self.sampling_rate_spinBox.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.sampling_rate_spinBox.setToolTip("The fraction of voxels sampled " "during each iteration.") self.gridLayout.addWidget(self.sampling_rate_spinBox, 3, 3) # Smooth Sigmas self.smooth_sigma_label = QLabel("Smooth Sigma") self.smooth_sigma_label.setAlignment(Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.gridLayout.addWidget(self.smooth_sigma_label, 4, 2) self.smooth_sigmas_qLineEdit = QLineEdit() self.smooth_sigmas_qLineEdit.resize( self.smooth_sigmas_qLineEdit.sizeHint().width(), self.smooth_sigmas_qLineEdit.sizeHint().height()) self.smooth_sigmas_qLineEdit.setValidator( QRegularExpressionValidator( QRegularExpression("^[0-9]*[,]?[0-9]*[,]?[0-9]"))) self.smooth_sigmas_qLineEdit.setToolTip( "The multi-resolution smoothing kernal scale (Gaussian). Can be " "up to three integer elements in an array. Example [4, 2, 1]") self.gridLayout.addWidget(self.smooth_sigmas_qLineEdit, 4, 3) # Label to hold warning labels. self.warning_label = QLabel() # Button for fast mode self.fast_mode_button = QtWidgets.QPushButton("Fast Mode") self.fast_mode_button.setCursor( QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.fast_mode_button.clicked.connect(self.set_fast_mode) # Add Widgets to the vertical layout self.vertical_layout.addWidget(self.fast_mode_button) self.vertical_layout.addWidget(self.gridLayoutWidget) self.vertical_layout.addWidget(self.warning_label) # Set layout of frame to the gridlayout widget self.auto_image_fusion_frame.setLayout(self.vertical_layout)