def text_size(text, font: QFont) -> 'SizeF': font_metrics = QFontMetrics(font) lines = text.split('\n') width = max(map(font_metrics.width, lines)) height = font_metrics.height() * len(lines) return SizeF(width, height)
def strRect(s, fontMetrics: QtGui.QFontMetrics): #baseWidth = fontMetrics.boundingRect(s).width() baseWidth = fontMetrics.horizontalAdvance(s) sWidth = baseWidth # - fontMetrics.leftBearing(s[0]) #sWidth -= fontMetrics.rightBearing(s[-1]) r = QtCore.QRect(0, 0, sWidth, fontMetrics.height()) return r
def setModelData(self, editor: '_IntervalWidget', model: QAbstractItemModel, index: QModelIndex) -> None: datetimes: List[QDateTime] datetimes, byDate, byTime = editor.getOptions() # Do some validation errors = list() if len(datetimes) < 2: errors.append(('e1', 'Error: at least one range must be defined')) if any([a >= b for a, b in zip(datetimes, datetimes[1:])]): errors.append( ('e2', 'Error: datetime points must be strictly increasing')) if errors: editor.handleErrors(errors) # Avoid setting options and leave editor open return options = ([ pd.Timestamp(date.toPython(), tz='UTC') for date in datetimes ], byDate, byTime) model.setData(index, options, Qt.EditRole) # Resize rows. This assumes that the TableView is the delegate parent f = QFontMetrics(QFont()) rowHeight = f.height() * len(options[0]) table: QTableView = self.parent() table.setRowHeight(index.row(), rowHeight) # Close editor. Works because it's the delegate that tells the view to close it with this signal self.closeEditor.emit(self.w, QStyledItemDelegate.NoHint)
class ApolloGroupHeading(QWidget): def __init__(self, parent, text, *args, **kwargs): super().__init__(parent, *args, **kwargs) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) self.text = text self.font_metrics = QFontMetrics(self.font()) self.text_width = self.font_metrics.width(self.text) self.text_height = self.font_metrics.height() def sizeHint(self): return QSize(self.text_width, self.text_height) def paintEvent(self, event): p = QPainter(self) left_edge = self.width() // 2 - self.text_width // 2 right_edge = self.width() // 2 + self.text_width // 2 p.drawText(left_edge, self.text_height, self.text) x0 = 0 x1 = left_edge - 4 x2 = right_edge + 4 x3 = self.width() - 1 y0 = self.height() // 2 + 2 y1 = self.height() p.drawLine(x0, y0, x1, y0) p.drawLine(x2, y0, x3, y0) p.drawLine(x0, y0, x0, y1) p.drawLine(x3, y0, x3, y1) p.end()
def setFont(self, qFont): qFont.setStyleHint(QFont.Monospace) qFont.setFixedPitch(True) fontMetrics = QFontMetrics(qFont) self.setTabStopWidth(self.__tabStop * fontMetrics.width(' ')) self.__lineHeight = fontMetrics.height() * self.__lineHeightMultiplier super().setFont(qFont) self.__setLineHeight()
def drawHeader(self): """Draw logo/copyright in the header""" pHeight = 90 pMargin = 15 icon_path = cm.DIR_ICONS + "app.png" self.header_lbl.setMinimumHeight(pHeight) self.header_lbl.setFrameShape(QFrame.StyledPanel) self.header_lbl.setContentsMargins(0, 0, 0, 0) pixmap = QPixmap(450, pHeight) pixmap.fill(Qt.transparent) iconY = (pHeight - 64) / 2 logoRect = QRect(pMargin, iconY, 64, 64) painter = QPainter(pixmap) painter.setBrush(QBrush(Qt.red)) painter.drawPixmap( logoRect, QPixmap(icon_path).scaled( logoRect.width(), logoRect.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation, ), ) titleRect = QRect(logoRect.right() + 10, iconY, 200, pHeight) font = QFont() font.setBold(True) font.setPixelSize(16) painter.setFont(font) painter.setPen(QPen(QApplication.instance().palette().text().color())) painter.drawText(titleRect, Qt.AlignTop, "Cutevariant") font_metrics = QFontMetrics(font) font.setBold(False) font.setPixelSize(12) painter.setFont(font) painter.setPen(QPen(Qt.darkGray)) titleRect.setY(titleRect.y() + font_metrics.height()) painter.drawText( titleRect, Qt.AlignTop, f"Version %s\nGPL3 Copyright (C) 2018-2020\nLabsquare.org" % __version__, ) self.header_lbl.setPixmap(pixmap) # Painting is finished ! # Avoid Segfault: # QPaintDevice: Cannot destroy paint device that is being painted painter.end()
def __init__(self, text, position, attachment_point, *args, **kwargs): super(TextItem, self).__init__(*args, **kwargs) self.text = text self.angle = kwargs.pop('angle') self.position = position self.font = QFont('Arial', 5) metrics = QFontMetrics(self.font) self.w = metrics.width(self.text) self.h = metrics.height() self.attachment_point = attachment_point
def text_changed(self): t = self.toPlainText() fm = QFontMetrics(self.font()) text_width = fm.width(self.get_longest_line(t)) text_width = text_width+25 text_height = fm.height()*(t.count('\n')+1)+12 self.setFixedWidth(text_width if text_width > self.base_width else self.base_width) self.setFixedHeight(text_height if text_height > self.base_height else self.base_height) self.parent_node_instance.update_shape()
def add_item(self, item_str: str): """ 向导航栏中添加项目(代表一个选项卡),添加之前会查重 :param item_str:项目名称(显示出来的文字) :return:None """ if not item_str: return for key, value in self._m_item_maps.items(): if value[0] == item_str: return # 如果存在同名item,则返回 f = QFont() f.setPointSize(self._m_item_font_size) fm = QFontMetrics(f) text_width = fm.width(item_str) text_height = fm.height() item_count = len(self._m_item_maps) if item_count > 0: if self._m_orientation == Qt.Horizontal: top_left = QPointF(self._m_total_text_width, 0) self._m_total_text_width += text_width + self._m_space bottom_right = QPointF(self._m_total_text_width, self._m_total_text_height) else: top_left = QPointF(0, self._m_total_text_height) self._m_total_text_height += text_height + self._m_space bottom_right = QPointF(self._m_total_text_width, self._m_total_text_height) self._m_item_maps[item_count] = [item_str, QRectF(top_left, bottom_right)] else: if self._m_orientation == Qt.Horizontal: # 水平方向,水平各占1个space, 竖直占1个space self._m_total_text_width = text_width + self._m_space self._m_total_text_height = text_height + self._m_space else: # 竖直方向, 水平各占2个space, 竖直占一个space self._m_total_text_width = text_width + 2 * self._m_space self._m_total_text_height = text_height + self._m_space top_left = QPointF(0.0, 0.0) bottom_right = QPointF(self._m_total_text_width, self._m_total_text_height) self._m_item_maps[item_count] = [item_str, QRectF(top_left, bottom_right)] self.setMinimumSize(self._m_total_text_width, self._m_total_text_height) if self._m_fixed: if self._m_orientation == Qt.Horizontal: self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) # 固定高度 else: self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) # 固定宽度 if len(self._m_item_maps): self._m_start_rect = QRectF(self._m_item_maps[0][1]) self.update()
def __init__(self, parameter): super().__init__() metrics = QFontMetrics(TEXT_FONT) self.min_height = metrics.height() self.setFont(TEXT_FONT) self.default_text = parameter.default self.textChanged.connect(self.on_update) self.highlighter = JSONHighlighter(self.document()) self.setText(parameter.default)
def resize_to_content(self, lines): if len(lines) == 0: lines.append('') # resize properly fm = QFontMetrics(self.font()) text_width = fm.width(lines[0]+'__') text_width = text_width+20 # some buffer text_height = fm.height()*(len(lines))+12 # also some vertical buffer self.setFixedWidth(text_width if text_width > self.base_width else self.base_width) self.setFixedHeight(text_height if text_height > self.base_height else self.base_height) self.parent_node_instance.update_shape()
def createDropTextPixmap(self): pixmap = QPixmap(481, 300) pixmap.fill(QColor("#333333")) painter = QPainter(pixmap) font = QFont("Arial") font.setPixelSize(28) font.setBold(True) fm = QFontMetrics(font) painter.setFont(font) painter.setPen(QPen(QColor("#888888"), 1)) text = "Drop the tileset image here" x = (pixmap.width()-fm.width(text))/2 y = (pixmap.height()+fm.height())/2 painter.drawText(x, y, text) del painter return pixmap
def compute_art_layout_size(self) -> Tuple[int, int]: """ Computes the size of art_layout which prints ASCII art. Returns: Pair of art_layout's width and height. """ art_width = int(self.__get_property(self.__art_layout, "width").read()) art_height = int( self.__get_property(self.__art_layout, "height").read()) fm = QFontMetrics( self.__get_property(self.__art_layout, "font").read()) char_width = art_width // fm.averageCharWidth() char_height = art_height // fm.height() return char_width, char_height
def paintEvent(self, event): sineTable = [ 0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38 ] metrics = QFontMetrics(self.font()) x = (self.width() - metrics.horizontalAdvance(self.text)) / 2 y = (self.height() + metrics.ascent() - metrics.descent()) / 2 color = QColor() painter = QPainter(self) for i in range(len(self.text)): index = (self.step + i) % 16 color.setHsv((15 - index) * 16, 255, 191) painter.setPen(color) painter.drawText(x, y - ((sineTable[index] * metrics.height()) / 400), str(self.text[i])) x += metrics.horizontalAdvance(self.text[i])
def _adjust_item_size(self) -> None: """ 调整Item大小 :return: """ if self._m_fixed: return item_count = len(self._m_item_maps) if self._m_orientation == Qt.Horizontal: add_width = 1.0 * (self.width() - self._m_total_text_width) / item_count add_height = 1.0 * (self.height() - self._m_total_text_height) else: add_width = 1.0 * (self.width() - self._m_total_text_width) add_height = 1.0 * (self.height() - self._m_total_text_height) / item_count dx = dy = 0.0 for key, value in self._m_item_maps.items(): # f = QFont() fm = QFontMetrics(self._m_item_font) text_width = fm.width(value[0]) text_height = fm.height() if self._m_orientation == Qt.Horizontal: topLeft = QPointF(dx, 0) dx += text_width + self._m_space + add_width dy = self._m_total_text_height + add_height else: topLeft = QPointF(0, dy) dx = self._m_total_text_width + add_width dy += text_height + self._m_space + add_height bottomRight = QPointF(dx, dy) text_rect = QRectF(topLeft, bottomRight) self._m_item_maps[key] = [value[0], QRectF(text_rect)] if key == self._m_current_index: self._m_start_rect = text_rect self._m_stop_rect = text_rect self.update()
def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None): super().__init__(layer=layer, targetImage=targetImage, parent=parent) # options self.options = None # exposure slider self.sliderExp = QbLUeSlider(Qt.Horizontal) self.sliderExp.setStyleSheet(QbLUeSlider.bLueSliderDefaultBWStylesheet) self.sliderExp.setRange(-20, 20) self.sliderExp.setSingleStep(1) expLabel = QbLUeLabel() expLabel.setMaximumSize(150, 30) expLabel.setText("Exposure Correction") expLabel.doubleClicked.connect( lambda: self.sliderExp.setValue(self.defaultExpCorrection)) self.expValue = QbLUeLabel() font = self.expValue.font() metrics = QFontMetrics(font) w = metrics.width("1000 ") h = metrics.height() self.expValue.setMinimumSize(w, h) self.expValue.setMaximumSize(w, h) # exp change/released slot def f(): self.expValue.setText( str("{:+.1f}".format(self.sliderExp.value() * self.defaultStep))) if self.sliderExp.isSliderDown() or (self.expCorrection == self.sliderExp.value() * self.defaultStep): return try: self.sliderExp.valueChanged.disconnect() self.sliderExp.sliderReleased.disconnect() except RuntimeError: pass self.expCorrection = self.sliderExp.value() * self.defaultStep self.dataChanged.emit() self.sliderExp.valueChanged.connect(f) self.sliderExp.sliderReleased.connect(f) self.sliderExp.valueChanged.connect(f) self.sliderExp.sliderReleased.connect(f) # layout l = QVBoxLayout() l.setAlignment(Qt.AlignTop) l.addWidget(expLabel) hl = QHBoxLayout() hl.addWidget(self.expValue) hl.addWidget(self.sliderExp) l.addLayout(hl) l.setContentsMargins(20, 0, 20, 25) # left, top, right, bottom self.setLayout(l) self.adjustSize() self.setWhatsThis("""<b>Exposure Correction</b> Multiplicative correction in the linear sRGB color space.<br> Unit is the diaphragm stop.<br> """) # end setWhatsThis self.setDefaults()
def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None): super().__init__(layer=layer, targetImage=targetImage, parent=parent) # connect layer selectionChanged signal self.layer.selectionChanged.sig.connect(self.updateLayer) self.kernelCategory = filterIndex.UNSHARP # options self.optionList = [ 'Unsharp Mask', 'Sharpen', 'Gaussian Blur', 'Surface Blur' ] filters = [ filterIndex.UNSHARP, filterIndex.SHARPEN, filterIndex.BLUR1, filterIndex.SURFACEBLUR ] self.filterDict = dict( zip(self.optionList, filters)) # filters is not a dict: don't use UDict here self.listWidget1 = optionsWidget(options=self.optionList, exclusive=True, changed=self.dataChanged) # set initial selection to unsharp mask self.listWidget1.checkOption(self.optionList[0]) # sliders self.sliderRadius = QbLUeSlider(Qt.Horizontal) self.sliderRadius.setRange(1, 50) self.sliderRadius.setSingleStep(1) self.radiusLabel = QLabel() self.radiusLabel.setMaximumSize(150, 30) self.radiusLabel.setText("Radius") self.radiusValue = QLabel() font = self.radiusValue.font() metrics = QFontMetrics(font) w = metrics.width("1000 ") h = metrics.height() self.radiusValue.setMinimumSize(w, h) self.radiusValue.setMaximumSize(w, h) self.sliderAmount = QbLUeSlider(Qt.Horizontal) self.sliderAmount.setRange(0, 100) self.sliderAmount.setSingleStep(1) self.amountLabel = QLabel() self.amountLabel.setMaximumSize(150, 30) self.amountLabel.setText("Amount") self.amountValue = QLabel() font = self.radiusValue.font() metrics = QFontMetrics(font) w = metrics.width("1000 ") h = metrics.height() self.amountValue.setMinimumSize(w, h) self.amountValue.setMaximumSize(w, h) self.toneValue = QLabel() self.toneLabel = QLabel() self.toneLabel.setMaximumSize(150, 30) self.toneLabel.setText("Sigma") self.sliderTone = QbLUeSlider(Qt.Horizontal) self.sliderTone.setRange(0, 100) self.sliderTone.setSingleStep(1) font = self.radiusValue.font() metrics = QFontMetrics(font) w = metrics.width("1000 ") h = metrics.height() self.toneValue.setMinimumSize(w, h) self.toneValue.setMaximumSize(w, h) # value change/done slot def formUpdate(): self.radiusValue.setText(str('%d ' % self.sliderRadius.value())) self.amountValue.setText(str('%d ' % self.sliderAmount.value())) self.toneValue.setText(str('%d ' % self.sliderTone.value())) if self.sliderRadius.isSliderDown( ) or self.sliderAmount.isSliderDown( ) or self.sliderTone.isSliderDown(): return try: for slider in [ self.sliderRadius, self.sliderAmount, self.sliderTone ]: slider.valueChanged.disconnect() slider.sliderReleased.disconnect() except RuntimeError: pass self.tone = self.sliderTone.value() self.radius = self.sliderRadius.value() self.amount = self.sliderAmount.value() self.dataChanged.emit() for slider in [ self.sliderRadius, self.sliderAmount, self.sliderTone ]: slider.valueChanged.connect(formUpdate) slider.sliderReleased.connect(formUpdate) for slider in [self.sliderRadius, self.sliderAmount, self.sliderTone]: slider.valueChanged.connect(formUpdate) slider.sliderReleased.connect(formUpdate) # layout l = QVBoxLayout() l.addWidget(self.listWidget1) hl = QHBoxLayout() hl.addWidget(self.radiusLabel) hl.addWidget(self.radiusValue) hl.addWidget(self.sliderRadius) l.addLayout(hl) hl = QHBoxLayout() hl.addWidget(self.amountLabel) hl.addWidget(self.amountValue) hl.addWidget(self.sliderAmount) l.addLayout(hl) hl = QHBoxLayout() hl.addWidget(self.toneLabel) hl.addWidget(self.toneValue) hl.addWidget(self.sliderTone) l.addLayout(hl) l.setContentsMargins(20, 0, 20, 25) # left, top, right, bottom self.setLayout(l) self.setDefaults() self.setWhatsThis(""" <b>Unsharp Mask</b> and <b>Sharpen Mask</b> are used to sharpen an image. Unsharp Mask usually gives best results.<br> <b>Gaussian Blur</b> and <b>Surface Blur</b> are used to blur an image.<br> In contrast to Gaussian Blur, Surface Blur preserves edges and reduces noise, but it may be slow.<br> It is possible to <b>limit the effect of a filter to a rectangular region of the image</b> by drawing a selection rectangle on the layer with the marquee (rectangle) tool.<br> Ctrl Click <b>clears the selection</b><br> """) # end setWhatsThis
def __init__(self, parent): super().__init__(parent) self.img = None # graphic form to show : it # should correspond to the currently selected layer self.currentWin = None # mouse click event self.clicked.connect(self.viewClicked) # set behavior and styles self.setSelectionBehavior(QAbstractItemView.SelectRows) delegate = itemDelegate(parent=self) self.setItemDelegate(delegate) ic1 = QImage(":/images/resources/eye-icon.png") ic2 = QImage(":/images/resources/eye-icon-strike.png") delegate.px1 = QPixmap.fromImage(ic1) delegate.px2 = QPixmap.fromImage(ic2) ic1.invertPixels() ic2.invertPixels() delegate.inv_px1 = QPixmap.fromImage(ic1) delegate.inv_px2 = QPixmap.fromImage(ic2) self.setIconSize(QSize(20, 15)) self.verticalHeader().setMinimumSectionSize(-1) self.verticalHeader().setDefaultSectionSize( self.verticalHeader().minimumSectionSize()) self.horizontalHeader().setMinimumSectionSize(40) self.horizontalHeader().setDefaultSectionSize(40) # drag and drop self.setDragDropMode(QAbstractItemView.DragDrop) self.setDefaultDropAction(Qt.MoveAction) self.setDragDropOverwriteMode(False) self.setDragEnabled(True) self.setAcceptDrops(True) self.setDropIndicatorShown(True) ################################ # layer property GUI : # preview, blending mode, opacity, mask color ################################ # Preview option # We should use a QListWidget or a custom optionsWidget # (cf. utils.py) : adding it to QVBoxLayout with mode # Qt.AlignBottom does not work. self.previewOptionBox = QCheckBox('Preview') self.previewOptionBox.setMaximumSize(100, 30) # View/Preview changed slot def m(state): # state : Qt.Checked Qt.UnChecked if self.img is None: return useThumb = (state == Qt.Checked) if useThumb == self.img.useThumb: return self.img.useThumb = useThumb window.updateStatus() self.img.cacheInvalidate() try: QApplication.setOverrideCursor( Qt.WaitCursor ) # TODO 18/04/18 waitcursor already called by applytostack QApplication.processEvents() # update the whole stack self.img.layersStack[0].applyToStack() self.img.onImageChanged() finally: QApplication.restoreOverrideCursor() QApplication.processEvents() self.previewOptionBox.stateChanged.connect(m) self.previewOptionBox.setChecked(True) # m is not triggered # title titleLabel = QLabel('Layer') titleLabel.setMaximumSize(100, 30) # opacity slider self.opacitySlider = QbLUeSlider(Qt.Horizontal) self.opacitySlider.setStyleSheet( QbLUeSlider.bLueSliderDefaultBWStylesheet) self.opacitySlider.setTickPosition(QSlider.TicksBelow) self.opacitySlider.setRange(0, 100) self.opacitySlider.setSingleStep(1) self.opacitySlider.setSliderPosition(100) self.opacityValue = QLabel() font = self.opacityValue.font() metrics = QFontMetrics(font) w = metrics.width("100 ") h = metrics.height() self.opacityValue.setMinimumSize(w, h) self.opacityValue.setMaximumSize(w, h) self.opacityValue.setText('100 ') # opacity value changed event handler def f1(): self.opacityValue.setText(str('%d ' % self.opacitySlider.value())) # opacity slider released event handler def f2(): try: layer = self.img.getActiveLayer() layer.setOpacity(self.opacitySlider.value()) layer.applyToStack() self.img.onImageChanged() except AttributeError: return self.opacitySlider.valueChanged.connect(f1) self.opacitySlider.sliderReleased.connect(f2) # mask color slider self.maskLabel = QLabel('Mask Color') maskSlider = QbLUeSlider(Qt.Horizontal) maskSlider.setStyleSheet(QbLUeSlider.bLueSliderDefaultBWStylesheet) maskSlider.setTickPosition(QSlider.TicksBelow) maskSlider.setRange(0, 100) maskSlider.setSingleStep(1) maskSlider.setSliderPosition(100) self.maskSlider = maskSlider self.maskValue = QLabel() font = self.maskValue.font() metrics = QFontMetrics(font) w = metrics.width("100 ") h = metrics.height() self.maskValue.setMinimumSize(w, h) self.maskValue.setMaximumSize(w, h) self.maskValue.setText('100 ') # masks are disbled by default self.maskLabel.setEnabled(False) self.maskSlider.setEnabled(False) self.maskValue.setEnabled(False) # mask value changed event handler def g1(): self.maskValue.setText(str('%d ' % self.maskSlider.value())) # mask slider released event handler def g2(): try: layer = self.img.getActiveLayer() layer.setColorMaskOpacity(self.maskSlider.value() * 255.0 / 100.0) layer.applyToStack() self.img.onImageChanged() except AttributeError: return self.maskSlider.valueChanged.connect(g1) self.maskSlider.sliderReleased.connect(g2) # blending mode combo box compLabel = QLabel() compLabel.setText("Blend") self.compositionModeDict = OrderedDict([ ('Normal', QPainter.CompositionMode_SourceOver), ('Plus', QPainter.CompositionMode_Plus), ('Multiply', QPainter.CompositionMode_Multiply), ('Screen', QPainter.CompositionMode_Screen), ('Overlay', QPainter.CompositionMode_Overlay), ('Darken', QPainter.CompositionMode_Darken), ('Lighten', QPainter.CompositionMode_Lighten), ('Color Dodge', QPainter.CompositionMode_ColorDodge), ('Color Burn', QPainter.CompositionMode_ColorBurn), ('Hard Light', QPainter.CompositionMode_HardLight), ('Soft Light', QPainter.CompositionMode_SoftLight), ('Difference', QPainter.CompositionMode_Difference), ('Exclusion', QPainter.CompositionMode_Exclusion), # Type of previous modes is QPainter.CompositionMode (Shiboken enum-type). # Next additional modes are not implemented by QPainter: ('Luminosity', -1), ('color', -2) ]) self.blendingModeCombo = QComboBox() for key in self.compositionModeDict: self.blendingModeCombo.addItem(key, self.compositionModeDict[key]) # combo box item changed slot def g(ind): layer = self.img.getActiveLayer() s = self.blendingModeCombo.currentText() newMode = self.compositionModeDict[str(s)] if newMode == layer.compositionMode: return layer.compositionMode = newMode layer.applyToStack() self.img.onImageChanged() self.blendingModeCombo.currentIndexChanged.connect(g) # layout l = QVBoxLayout() l.setAlignment(Qt.AlignTop) hl0 = QHBoxLayout() hl0.addWidget(titleLabel) hl0.addStretch(1) hl0.addWidget(self.previewOptionBox) l.addLayout(hl0) hl = QHBoxLayout() hl.addWidget(QLabel('Opacity')) hl.addWidget(self.opacityValue) hl.addWidget(self.opacitySlider) l.addLayout(hl) hl1 = QHBoxLayout() hl1.addWidget(self.maskLabel) hl1.addWidget(self.maskValue) hl1.addWidget(self.maskSlider) l.addLayout(hl1) l.setContentsMargins(0, 0, 10, 0) # left, top, right, bottom hl2 = QHBoxLayout() hl2.addWidget(compLabel) hl2.addWidget(self.blendingModeCombo) l.addLayout(hl2) for layout in [hl, hl1, hl2]: layout.setContentsMargins(5, 0, 0, 0) # this layout must be added to the propertyWidget object loaded from blue.ui : # we postpone it to in blue.py, after loading the main form. self.propertyLayout = l # shortcut actions self.actionDup = QAction('Duplicate layer', None) self.actionDup.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_J)) self.addAction(self.actionDup) def dup(): row = self.selectedIndexes()[0].row() # Stack index index = len(self.img.layersStack) - row - 1 layer = self.img.layersStack[index] if layer.isAdjustLayer(): return # add new layer to stack and set it to active self.img.dupLayer(index=index) # update layer view self.setLayers(self.img) self.actionDup.triggered.connect(dup) self.setWhatsThis("""<b>Layer Stack</b><br> To <b>toggle the layer visibility</b> click on the Eye icon.<br> To <b>add a mask</b> use the context menu to enable it and paint pixels with the Mask/Unmask tools in the left pane.<br> For <b>color mask<b/b>: <br> green pixels are masked,<br> red pixels are unmasked.<br> Other colors correspond to partially masked pixels.<br> Note that upper visible layers slow down mask edition.<br> """) # end of setWhatsThis
def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None): super().__init__(layer=layer, targetImage=targetImage, parent=parent) self.setMinimumSize(axeSize, axeSize+100) # contrast spline viewer self.contrastForm = None # options optionList1, optionNames1 = ['Multi-Mode', 'CLAHE'], ['Multi-Mode', 'CLAHE'] self.listWidget1 = optionsWidget(options=optionList1, optionNames=optionNames1, exclusive=True, changed=lambda: self.dataChanged.emit()) self.listWidget1.checkOption(self.listWidget1.intNames[0]) self.listWidget1.setStyleSheet("QListWidget {border: 0px;} QListWidget::item {border: 0px; padding-left: 0px;}") optionList2, optionNames2 = ['High', 'manualCurve'], ['Preserve Highlights', 'Show Contrast Curve'] def optionList2Change(item): if item.internalName == 'High': # force to recalculate the spline self.layer.autoSpline = True self.dataChanged.emit() self.listWidget2 = optionsWidget(options=optionList2, optionNames=optionNames2, exclusive=False, changed=optionList2Change) self.listWidget2.checkOption(self.listWidget2.intNames[0]) self.listWidget2.setStyleSheet("QListWidget {border: 0px;} QListWidget::item {border: 0px; padding-left: 0px;}") self.options = UDict((self.listWidget1.options, self.listWidget2.options)) # contrast slider self.sliderContrast = QbLUeSlider(Qt.Horizontal) self.sliderContrast.setStyleSheet(QbLUeSlider.bLueSliderDefaultIBWStylesheet) self.sliderContrast.setRange(0, 10) self.sliderContrast.setSingleStep(1) contrastLabel = QbLUeLabel() contrastLabel.setMaximumSize(150, 30) contrastLabel.setText("Contrast Level") contrastLabel.doubleClicked.connect(lambda: self.sliderContrast.setValue(self.contrast2Slider(self.contrastDefault))) self.contrastValue = QLabel() font = self.contrastValue.font() metrics = QFontMetrics(font) w = metrics.width("100") h = metrics.height() self.contrastValue.setMinimumSize(w, h) self.contrastValue.setMaximumSize(w, h) self.contrastValue.setText(str("{:d}".format(self.sliderContrast.value()))) # contrast changed event handler. def contrastUpdate(value): self.contrastValue.setText(str("{:d}".format(self.sliderContrast.value()))) # move not yet terminated or value not modified if self.sliderContrast.isSliderDown() or self.slider2Contrast(value) == self.contrastCorrection: return self.sliderContrast.valueChanged.disconnect() self.sliderContrast.sliderReleased.disconnect() self.contrastCorrection = self.slider2Contrast(self.sliderContrast.value()) # force to recalculate the spline self.layer.autoSpline = True self.dataChanged.emit() self.sliderContrast.valueChanged.connect(contrastUpdate) self.sliderContrast.sliderReleased.connect(lambda: contrastUpdate(self.sliderContrast.value())) self.sliderContrast.valueChanged.connect(contrastUpdate) self.sliderContrast.sliderReleased.connect(lambda: contrastUpdate(self.sliderContrast.value())) # saturation slider self.sliderSaturation = QbLUeSlider(Qt.Horizontal) self.sliderSaturation.setStyleSheet(QbLUeSlider.bLueSliderDefaultColorStylesheet) self.sliderSaturation.setRange(0, 100) self.sliderSaturation.setSingleStep(1) saturationLabel = QbLUeLabel() saturationLabel.setMaximumSize(150, 30) saturationLabel.setText("Saturation") saturationLabel.doubleClicked.connect(lambda: self.sliderSaturation.setValue(self.saturation2Slider(self.saturationDefault))) self.saturationValue = QLabel() font = self.saturationValue.font() metrics = QFontMetrics(font) w = metrics.width("100") h = metrics.height() self.saturationValue.setMinimumSize(w, h) self.saturationValue.setMaximumSize(w, h) self.saturationValue.setText(str("{:+d}".format(self.sliderContrast.value()))) # saturation changed event handler def saturationUpdate(value): self.saturationValue.setText(str("{:+d}".format(int(self.slidersaturation2User(self.sliderSaturation.value()))))) # move not yet terminated or value not modified if self.sliderSaturation.isSliderDown() or self.slider2Saturation(value) == self.satCorrection: return self.sliderSaturation.valueChanged.disconnect() self.sliderSaturation.sliderReleased.disconnect() self.satCorrection = self.slider2Saturation(self.sliderSaturation.value()) self.dataChanged.emit() self.sliderSaturation.valueChanged.connect(saturationUpdate) self.sliderSaturation.sliderReleased.connect(lambda: saturationUpdate(self.sliderSaturation.value())) self.sliderSaturation.valueChanged.connect(saturationUpdate) self.sliderSaturation.sliderReleased.connect(lambda: saturationUpdate(self.sliderSaturation.value())) # brightness slider self.sliderBrightness = QbLUeSlider(Qt.Horizontal) self.sliderBrightness.setStyleSheet(QbLUeSlider.bLueSliderDefaultBWStylesheet) self.sliderBrightness.setRange(0, 100) self.sliderBrightness.setSingleStep(1) brightnessLabel = QbLUeLabel() brightnessLabel.setMaximumSize(150, 30) brightnessLabel.setText("Brightness") brightnessLabel.doubleClicked.connect(lambda: self.sliderBrightness.setValue(self.brightness2Slider(self.brightnessDefault))) self.brightnessValue = QLabel() font = self.brightnessValue.font() metrics = QFontMetrics(font) w = metrics.width("100") h = metrics.height() self.brightnessValue.setMinimumSize(w, h) self.brightnessValue.setMaximumSize(w, h) self.brightnessValue.setText(str("{:+d}".format(self.sliderContrast.value()))) # brightness changed event handler def brightnessUpdate(value): self.brightnessValue.setText(str("{:+d}".format(int(self.sliderBrightness2User(self.sliderBrightness.value()))))) # move not yet terminated or value not modified if self.sliderBrightness.isSliderDown() or self.slider2Brightness(value) == self.brightnessCorrection: return self.sliderBrightness.valueChanged.disconnect() self.sliderBrightness.sliderReleased.disconnect() self.brightnessCorrection = self.slider2Brightness(self.sliderBrightness.value()) self.dataChanged.emit() self.sliderBrightness.valueChanged.connect(brightnessUpdate) self.sliderBrightness.sliderReleased.connect(lambda: brightnessUpdate(self.sliderBrightness.value())) self.sliderBrightness.valueChanged.connect(brightnessUpdate) self.sliderBrightness.sliderReleased.connect(lambda: brightnessUpdate(self.sliderBrightness.value())) # attributes initialized in setDefaults, declared here # for the sake of correctness self.contrastCorrection = None # range self.satCorrection = None # range -0.5..0.5 self.brightnessCorrection = None # range -0.5..0.5 # layout l = QVBoxLayout() l.setAlignment(Qt.AlignTop) gb1 = QGroupBox() gb1.setStyleSheet("QGroupBox {border: 1px solid gray; border-radius: 4px}") l1 = QVBoxLayout() ct = QLabel() ct.setText('Contrast') l.setAlignment(Qt.AlignTop) l1.addWidget(ct) l1.addWidget(self.listWidget1) gb1.setLayout(l1) l.addWidget(gb1) l.addWidget(self.listWidget2) l.addWidget(contrastLabel) hl = QHBoxLayout() hl.addWidget(self.contrastValue) hl.addWidget(self.sliderContrast) l.addLayout(hl) l.addWidget(brightnessLabel) hl3 = QHBoxLayout() hl3.addWidget(self.brightnessValue) hl3.addWidget(self.sliderBrightness) l.addLayout(hl3) l.addWidget(saturationLabel) hl2 = QHBoxLayout() hl2.addWidget(self.saturationValue) hl2.addWidget(self.sliderSaturation) l.addLayout(hl2) self.setLayout(l) self.adjustSize() self.setStyleSheet("QListWidget, QLabel {font : 7pt;}") self.setDefaults() self.setWhatsThis( """<b>Contrast Brightness Saturation</b><br> <b>Contrast</b> is enhanced using one of these two methods:<br> - <b>CLAHE</b> : increases the local contrast.<br> - <b>Multi-Mode</b> : increases the local contrast and the contrast between regions of the image.<br> For both methods the contrast slider controls the level of the correction.<br> With Multi-Mode enabled, use the option <b>Show Contrast Curve</b> to edit the correction curve and check <b>Preserve Highlights</b> for softer highlights.<br> <b>Brightness</b> and <b>Saturation</b> corrections are non linear to limit clipping.<br> Sliders are <b>reset</b> to their default value by double clicking the name of the slider.<br> """ ) # end setWhatsThis
def __init__(self, axeSize=500, layer=None, parent=None): super(noiseForm, self).__init__(parent=parent) self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.setMinimumSize(axeSize, axeSize) self.setAttribute(Qt.WA_DeleteOnClose) # link back to image layer # using weak ref for back links if type(layer) in weakref.ProxyTypes: self.layer = layer else: self.layer = weakref.proxy(layer) # attribute initialized in setDefaults # defined here for the sake of correctness self.noiseCorrection = 0 # options optionList = ['Wavelets', 'Bilateral', 'NLMeans'] self.listWidget1 = optionsWidget( options=optionList, exclusive=True, changed=lambda: self.dataChanged.emit(True)) self.listWidget1.checkOption(self.listWidget1.intNames[0]) self.options = self.listWidget1.options # threshold slider self.sliderThr = QbLUeSlider(Qt.Horizontal) self.sliderThr.setStyleSheet(QbLUeSlider.bLueSliderDefaultBWStylesheet) self.sliderThr.setTickPosition(QSlider.TicksBelow) self.sliderThr.setRange(0, 10) self.sliderThr.setSingleStep(1) self.sliderThr.valueChanged.connect(self.thrUpdate) self.sliderThr.sliderReleased.connect(lambda: self.thrUpdate( self.sliderThr.value())) # signal has no parameter) self.thrLabel = QLabel() self.thrLabel.setMaximumSize(150, 30) self.thrLabel.setText("level") self.thrValue = QLabel() font = self.thrValue.font() metrics = QFontMetrics(font) w = metrics.width("0000") h = metrics.height() self.thrValue.setMinimumSize(w, h) self.thrValue.setMaximumSize(w, h) self.thrValue.setText( str("{:.0f}".format(self.slider2Thr(self.sliderThr.value())))) # self.dataChanged.connect(self.updateLayer) # self.setStyleSheet("QListWidget, QLabel {font : 7pt;}") # layout l = QVBoxLayout() #l.setAlignment(Qt.AlignBottom) l.addWidget(self.listWidget1) hl1 = QHBoxLayout() hl1.addWidget(self.thrLabel) hl1.addWidget(self.thrValue) hl1.addWidget(self.sliderThr) l.addLayout(hl1) l.setContentsMargins(20, 0, 20, 25) # left, top, right, bottom #l.setContentsMargins(10, 10, 10, 10) # left, top, right, bottom self.setLayout(l) self.adjustSize() self.setDefaults() self.setWhatsThis("""<b>Noise Reduction</b><br> <b>Bilateral Filtering</b> is the fastest method.<br> <b>NLMeans</b> (Non Local Means) and <b>Wavelets</b> are slower, but they usually give better results.<br> It is possible to <b>limit the application of all methods to a rectangular region of the image</b> by drawing a selection rectangle on the layer with the marquee tool.<br> Ctrl-Click to <b>clear the selection</b><br> """) # end of setWhatsThis
def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None): super().__init__(targetImage=targetImage, axeSize=axeSize, layer=layer, parent=parent) graphicsScene = self.scene() # connect layer selectionChanged signal self.layer.selectionChanged.sig.connect(self.updateLayer) # Init curves dSplineItem = activeBSpline(axeSize, period=axeSize, yZero=-3 * axeSize // 4) graphicsScene.addItem(dSplineItem) dSplineItem.setVisible(True) dSplineItem.initFixedPoints() self.dSplineItemB = dSplineItem graphicsScene.dSplineItemB = dSplineItem text = graphicsScene.addText('Hue (0-360)') text.setDefaultTextColor(Qt.white) text.setPos(-40, 10) text = graphicsScene.addText('delta H') text.setDefaultTextColor(Qt.white) text.setPos(-40, -self.axeSize // 4) text = graphicsScene.addText('delta B') text.setDefaultTextColor(Qt.white) text.setPos(-40, -(self.axeSize * 3) // 4) dSplineItem = activeBSpline(axeSize, period=axeSize, yZero=-axeSize // 4) graphicsScene.addItem(dSplineItem) dSplineItem.setVisible(True) dSplineItem.initFixedPoints() self.dSplineItemH = dSplineItem graphicsScene.dSplineItemH = dSplineItem # init 3D LUT self.LUT = DeltaLUT3D((34, 32, 32)) self.marker = activeMarker.fromTriangle(parent=self.dSplineItemB) self.marker.setPos(0, 0) # -(axeSize * 3) // 4) self.marker.setMoveRange(QRect(0, 0, axeSize, 0)) self.scene().addItem(self.marker) def showPos(e, x, y): self.markerLabel.setText("%d" % (x * 360 // axeSize)) self.marker.onMouseMove = showPos self.markerLabel = QLabel() font = self.markerLabel.font() metrics = QFontMetrics(font) w = metrics.width("0000") h = metrics.height() self.markerLabel.setMinimumSize(w, h) self.markerLabel.setMaximumSize(w, h) self.sliderSat = QbLUeSlider(Qt.Horizontal) self.sliderSat.setMinimumWidth(200) def satUpdate(value): self.satValue.setText(str("{:d}".format(value))) # move not yet terminated or values not modified if self.sliderSat.isSliderDown() or value == self.satThr: return try: self.sliderSat.valueChanged.disconnect() self.sliderSat.sliderReleased.disconnect() except RuntimeError: pass self.satThr = value self.dataChanged.emit() self.sliderSat.valueChanged.connect(satUpdate) self.sliderSat.sliderReleased.connect( lambda: satUpdate(self.sliderSat.value())) self.sliderSat.valueChanged.connect(satUpdate) self.sliderSat.sliderReleased.connect( lambda: satUpdate(self.sliderSat.value())) self.satValue = QLabel() font = self.markerLabel.font() metrics = QFontMetrics(font) w = metrics.width("0000") h = metrics.height() self.satValue.setMinimumSize(w, h) self.satValue.setMaximumSize(w, h) # layout gl = QGridLayout() gl.addWidget(QLabel('Hue '), 0, 0) gl.addWidget(self.markerLabel, 0, 1) gl.addWidget(QLabel('Sat Thr '), 1, 0) gl.addWidget(self.satValue, 1, 1) gl.addWidget(self.sliderSat, 1, 2, 4, 1) self.addCommandLayout(gl) self.setDefaults() self.setWhatsThis("""<b>3D LUT Shift HSV</b><br> All pixel colors are changed by the specific hue and brightness shifts corresponding to their hue.<br> x-axis represents hue values from 0 to 360. The upper curve shows brightness multiplicative shifts (initially 1) and the lower curve hue additive shifts (initially 0). <br> Each curve is controlled by bump triangles.<br> To <b>add a bump triangle</b> to the curve click anywhere on the curve. To <b>remove the triangle</b> click on any vertex.<br> Drag the triangle vertices to move the bump along the x-axis and to change its height and orientation. Use the <b> Sat Thr</b> slider to preserve low saturated colors.<br> To <b>set the Hue Value Marker</b> Ctrl+click on the image.<br> To limit the shift corrections to a region of the image select the desired area with the rectangular marquee tool.<br> <b>Zoom</b> the curves with the mouse wheel.<br> """)
def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex): """Apply graphical formatting to each item in each displayed column in the view""" brush = QBrush() pen = QPen() font = QFont() if option.state & QStyle.State_Selected: text_color = option.palette.color(QPalette.Normal, QPalette.BrightText) else: text_color = option.palette.color(QPalette.Normal, QPalette.Text) is_selected = option.state & QStyle.State_Selected # Default theme color pen.setColor(text_color) field_name = self.field_name(index).lower() value = self.value(index) # Colour bases (default color is the one of the current theme) if (field_name == "ref" or field_name == "alt") and (value in ("A", "C", "G", "T") and not is_selected): pen.setColor( self.BASE_COLOR.get(value, option.palette.color(QPalette.WindowText))) if field_name == "impact" and not is_selected: font.setBold(True) pen.setColor( self.IMPACT_COLOR.get(value, self.IMPACT_COLOR["MODIFIER"])) if field_name == "gene" and not is_selected: pen.setColor("#6a9fca") if field_name == "classification": icon = self.ACMG_ICON.get(str(value), self.ACMG_ICON["0"]) self.draw_icon(painter, option.rect, icon) return if field_name == "favorite": icon = self.FAV_ICON.get(int(value), self.FAV_ICON[0]) self.draw_icon(painter, option.rect, icon) return if field_name == "hgvs_c": font.setBold(True) m = re.search(r"([cnm]\..+)", str(value)) if m: value = m.group(1) if field_name == "hgvs_p": font.setBold(True) m = re.search(r"(p\..+)", str(value)) if m: value = m.group(1) if re.match(r"sample\[.+\]\.gt", field_name): icon = self.GENOTYPE_ICONS.get(int(value), self.GENOTYPE_ICONS[-1]) self.draw_icon(painter, option.rect, icon) return if field_name == "consequence": values = str(self.value(index)).split("&") metrics = QFontMetrics(font) x = option.rect.x() + 5 # y = option.rect.center().y() for value in values: width = metrics.width(value) height = metrics.height() rect = QRect(x, 0, width + 15, height + 10) rect.moveCenter(option.rect.center()) rect.moveLeft(x) painter.setFont(font) painter.setClipRect(option.rect, Qt.IntersectClip) painter.setBrush( QBrush(QColor(self.SO_COLOR.get(value, "#90d4f7")))) painter.setPen(Qt.NoPen) painter.drawRoundedRect(rect, 3, 3) painter.setPen(QPen(QColor("white"))) painter.drawText(rect, Qt.AlignCenter | Qt.AlignVCenter, value) x += width + 20 painter.setClipping(False) return if field_name == "rsid": self.draw_url(painter, option.rect, value, QUrl("http://www.google.fr"), index) return painter.setBrush(brush) painter.setPen(pen) painter.setFont(font) painter.drawText(option.rect, option.displayAlignment, value)
def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None, mainForm=None): super().__init__(parent=parent) defaultRadius = 10 defaultTone = 100.0 defaultAmount = 50.0 self.radius = defaultRadius self.tone = defaultTone self.amount = defaultAmount self.targetImage = targetImage self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.setMinimumSize(axeSize, axeSize) self.setAttribute(Qt.WA_DeleteOnClose) self.img = targetImage # link back to image layer self.layer = weakProxy(layer) """ # using weak ref for back links if type(layer) in weakref.ProxyTypes: self.layer = layer else: self.layer = weakref.proxy(layer) """ self.mainForm = mainForm self.kernelCategory = filterIndex.UNSHARP # options self.optionList = [ 'Unsharp Mask', 'Sharpen', 'Gaussian Blur', 'Surface Blur' ] filters = [ filterIndex.UNSHARP, filterIndex.SHARPEN, filterIndex.BLUR1, filterIndex.SURFACEBLUR ] self.filterDict = dict(zip(self.optionList, filters)) self.listWidget1 = optionsWidget(options=self.optionList, exclusive=True, changed=self.dataChanged) # set initial selection to unsharp mask item = self.listWidget1.checkOption(self.optionList[0]) # sliders self.sliderRadius = QbLUeSlider(Qt.Horizontal) #self.sliderRadius.setTickPosition(QSlider.TicksBelow) self.sliderRadius.setRange(1, 50) self.sliderRadius.setSingleStep(1) self.radiusLabel = QLabel() self.radiusLabel.setMaximumSize(150, 30) self.radiusLabel.setText("Radius") self.radiusValue = QLabel() font = self.radiusValue.font() metrics = QFontMetrics(font) w = metrics.width("1000 ") h = metrics.height() self.radiusValue.setMinimumSize(w, h) self.radiusValue.setMaximumSize(w, h) self.sliderAmount = QbLUeSlider(Qt.Horizontal) #self.sliderAmount.setTickPosition(QSlider.TicksBelow) self.sliderAmount.setRange(0, 100) self.sliderAmount.setSingleStep(1) self.amountLabel = QLabel() self.amountLabel.setMaximumSize(150, 30) self.amountLabel.setText("Amount") self.amountValue = QLabel() font = self.radiusValue.font() metrics = QFontMetrics(font) w = metrics.width("1000 ") h = metrics.height() self.amountValue.setMinimumSize(w, h) self.amountValue.setMaximumSize(w, h) self.toneValue = QLabel() self.toneLabel = QLabel() self.toneLabel.setMaximumSize(150, 30) self.toneLabel.setText("Sigma") self.sliderTone = QbLUeSlider(Qt.Horizontal) #self.sliderTone.setTickPosition(QSlider.TicksBelow) self.sliderTone.setRange(0, 100) self.sliderTone.setSingleStep(1) font = self.radiusValue.font() metrics = QFontMetrics(font) w = metrics.width("1000 ") h = metrics.height() self.toneValue.setMinimumSize(w, h) self.toneValue.setMaximumSize(w, h) # layout l = QVBoxLayout() l.setAlignment(Qt.AlignBottom) l.addWidget(self.listWidget1) hl = QHBoxLayout() hl.addWidget(self.radiusLabel) hl.addWidget(self.radiusValue) hl.addWidget(self.sliderRadius) l.addLayout(hl) hl = QHBoxLayout() hl.addWidget(self.amountLabel) hl.addWidget(self.amountValue) hl.addWidget(self.sliderAmount) l.addLayout(hl) hl = QHBoxLayout() hl.addWidget(self.toneLabel) hl.addWidget(self.toneValue) hl.addWidget(self.sliderTone) l.addLayout(hl) l.setContentsMargins(20, 0, 20, 25) # left, top, right, bottom self.setLayout(l) # value changed event handler def sliderUpdate(): self.radiusValue.setText(str('%d ' % self.sliderRadius.value())) self.amountValue.setText(str('%d ' % self.sliderAmount.value())) self.toneValue.setText(str('%d ' % self.sliderTone.value())) # value done event handler def formUpdate(): sR, sA, sT = self.sliderRadius.isEnabled( ), self.sliderAmount.isEnabled(), self.sliderTone.isEnabled() self.sliderRadius.setEnabled(False) self.sliderAmount.setEnabled(False) self.sliderTone.setEnabled(False) self.tone = self.sliderTone.value() self.radius = self.sliderRadius.value() self.amount = self.sliderAmount.value() sliderUpdate() self.dataChanged.emit() self.sliderRadius.setEnabled(sR) self.sliderAmount.setEnabled(sA) self.sliderTone.setEnabled(sT) self.sliderRadius.valueChanged.connect(sliderUpdate) self.sliderRadius.sliderReleased.connect(formUpdate) self.sliderAmount.valueChanged.connect(sliderUpdate) self.sliderAmount.sliderReleased.connect(formUpdate) self.sliderTone.valueChanged.connect(sliderUpdate) self.sliderTone.sliderReleased.connect(formUpdate) self.dataChanged.connect(self.updateLayer) # init self.sliderRadius.setValue(defaultRadius) self.sliderAmount.setValue(defaultAmount) self.sliderTone.setValue(defaultTone) self.enableSliders() sliderUpdate() self.setWhatsThis(""" <b>Unsharp Mask</b> and <b>Sharpen Mask</b> are used to sharpen an image.<br> <b>Gaussian Blur</b> and <b>Surface Blur</b> are used to blur an image.<br> In contrast to Gaussian Blur, Surface Blur preserves edges and reduces noise.<br> It is possible to <b>limit the effect of a filter to a rectangular region of the image</b> by drawing a selection rectangle on the layer with the marquee tool.<br> Ctrl-Click to <b>clear the selection</b><br> """) # end setWhatsThis
def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None): super().__init__(layer=layer, targetImage=targetImage, parent=parent) self.tempCorrection = 6500 self.tintCorrection = 1.0 self.defaultTemp = sRGBWP # ref temperature D65 self.defaultTint = 0 # options optionList, optionNames = ['Photo Filter', 'Chromatic Adaptation' ], ['Photo Filter', 'Chromatic Adaptation'] self.listWidget1 = optionsWidget( options=optionList, optionNames=optionNames, exclusive=True, changed=lambda: self.dataChanged.emit()) self.listWidget1.checkOption(self.listWidget1.intNames[1]) self.options = self.listWidget1.options # temp slider self.sliderTemp = QbLUeSlider(Qt.Horizontal) self.sliderTemp.setStyleSheet( QbLUeSlider.bLueSliderDefaultIColorStylesheet) self.sliderTemp.setRange( 17, 100 ) # 250) # valid range for spline approximation is 1667..25000, cf. colorConv.temperature2xyWP self.sliderTemp.setSingleStep(1) tempLabel = QbLUeLabel() tempLabel.setMaximumSize(150, 30) tempLabel.setText("Filter Temperature") tempLabel.doubleClicked.connect(lambda: self.sliderTemp.setValue( self.temp2Slider(self.defaultTemp))) self.tempValue = QLabel() font = self.tempValue.font() metrics = QFontMetrics(font) w = metrics.width("00000") h = metrics.height() self.tempValue.setMinimumSize(w, h) self.tempValue.setMaximumSize(w, h) self.tempValue.setText( str("{:d}".format(self.sliderTemp2User(self.sliderTemp.value())))) # tint slider self.sliderTint = QbLUeSlider(Qt.Horizontal) self.sliderTint.setStyleSheet( QbLUeSlider.bLueSliderDefaultMGColorStylesheet) self.sliderTint.setRange( 0, 100 ) # 250) # valid range for spline approximation is 1667..25000, cf. colorConv.temperature2xyWP self.sliderTint.setSingleStep(1) self.tintLabel = QbLUeLabel() self.tintLabel.setMaximumSize(150, 30) self.tintLabel.setText("Tint") self.tintLabel.doubleClicked.connect(lambda: self.sliderTint.setValue( self.tint2Slider(self.defaultTint))) self.tintValue = QLabel() font = self.tintValue.font() metrics = QFontMetrics(font) w = metrics.width("0000") h = metrics.height() self.tintValue.setMinimumSize(w, h) self.tintValue.setMaximumSize(w, h) self.tintValue.setText( str("{:d}".format(self.sliderTint2User(self.sliderTint.value())))) # temp change slot def tempUpdate(value): self.tempValue.setText( str("{:d}".format(self.sliderTemp2User(value)))) # move not yet terminated or values not modified if self.sliderTemp.isSliderDown() or self.slider2Temp( value) == self.tempCorrection: return try: self.sliderTemp.valueChanged.disconnect() self.sliderTemp.sliderReleased.disconnect() except RuntimeError: pass self.tempCorrection = self.slider2Temp(value) self.dataChanged.emit() self.sliderTemp.valueChanged.connect(tempUpdate) self.sliderTemp.sliderReleased.connect( lambda: tempUpdate(self.sliderTemp.value())) # tint change slot def tintUpdate(value): self.tintValue.setText( str("{:d}".format(self.sliderTint2User(value)))) # move not yet terminated or values not modified if self.sliderTint.isSliderDown() or self.slider2Tint( value) == self.tintCorrection: return try: self.sliderTint.valueChanged.disconnect() self.sliderTint.sliderReleased.disconnect() except RuntimeError: pass self.tintCorrection = self.slider2Tint(value) self.dataChanged.emit() self.sliderTint.valueChanged.connect(tintUpdate) self.sliderTint.sliderReleased.connect( lambda: tintUpdate(self.sliderTint.value())) self.sliderTemp.valueChanged.connect(tempUpdate) self.sliderTemp.sliderReleased.connect( lambda: tempUpdate(self.sliderTemp.value())) self.sliderTint.valueChanged.connect(tintUpdate) self.sliderTint.sliderReleased.connect( lambda: tintUpdate(self.sliderTint.value())) # layout l = QVBoxLayout() l.setAlignment(Qt.AlignTop) l.addWidget(self.listWidget1) l.addWidget(tempLabel) hl = QHBoxLayout() hl.addWidget(self.tempValue) hl.addWidget(self.sliderTemp) l.addLayout(hl) l.addWidget(self.tintLabel) hl1 = QHBoxLayout() hl1.addWidget(self.tintValue) hl1.addWidget(self.sliderTint) l.addLayout(hl1) self.setLayout(l) self.adjustSize() self.setDefaults() self.setWhatsThis("""<b>Color Temperature</b><br> <b>Photo Filter</b> uses the multiply blending mode to mimic a warming or cooling filter put in front of the camera lens. The luminosity of the resulting image is corrected.<br> <b>Chromatic Adaptation</b> uses multipliers in the linear sRGB color space to adjust <b>temperature</b> and <b>tint</b>. """) # end of setWhatsThis
def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None): super().__init__(layer=layer, targetImage=targetImage, parent=parent) self.tempCorrection = 6500 self.tintCorrection = 1.0 self.filterColor = QColor(255, 255, 255) self.defaultTemp = sRGBWP # ref temperature D65 self.defaultTint = 0 # options optionList, optionNames = [ 'Color Filter', 'Photo Filter', 'Chromatic Adaptation' ], ['Color Filter', 'Photo Filter', 'Chromatic Adaptation'] self.listWidget1 = optionsWidget( options=optionList, optionNames=optionNames, exclusive=True, changed=lambda: self.dataChanged.emit()) self.listWidget1.checkOption(self.listWidget1.intNames[0]) self.options = self.listWidget1.options # link to app color dialog self.colorChooser = self.parent().colorChooser # color viewer self.colorLabel = QLabel() self.colorLabel.setMaximumSize(50, 50) # color chooser button self.colorChooserBtn = QbLUePushButton('Select Filter Color') self.colorChooserBtn.clicked.connect(self.showColorChooser) # temp slider self.sliderTemp = QbLUeSlider(Qt.Horizontal) self.sliderTemp.setStyleSheet( QbLUeSlider.bLueSliderDefaultIColorStylesheet) self.sliderTemp.setRange( 17, 100 ) # 250) # valid range for spline approximation is 1667..25000, cf. colorConv.temperature2xyWP self.sliderTemp.setSingleStep(1) self.tempLabel = QbLUeLabel() self.tempLabel.setMaximumSize(150, 30) self.tempLabel.setText("Filter Temperature") self.tempLabel.doubleClicked.connect(lambda: self.sliderTemp.setValue( self.temp2Slider(self.defaultTemp))) self.tempValue = QLabel() font = self.tempValue.font() metrics = QFontMetrics(font) w = metrics.width("00000") h = metrics.height() self.tempValue.setMinimumSize(w, h) self.tempValue.setMaximumSize(w, h) self.tempValue.setText( str("{:d}".format(self.sliderTemp2User(self.sliderTemp.value())))) # tint slider self.sliderTint = QbLUeSlider(Qt.Horizontal) self.sliderTint.setStyleSheet( QbLUeSlider.bLueSliderDefaultMGColorStylesheet) self.sliderTint.setRange( 0, 100 ) # 250) # valid range for spline approximation is 1667..25000, cf. colorConv.temperature2xyWP self.sliderTint.setSingleStep(1) self.tintLabel = QbLUeLabel() self.tintLabel.setMaximumSize(150, 30) self.tintLabel.setText("Tint") self.tintLabel.doubleClicked.connect(lambda: self.sliderTint.setValue( self.tint2Slider(self.defaultTint))) self.tintValue = QLabel() font = self.tintValue.font() metrics = QFontMetrics(font) w = metrics.width("0000") h = metrics.height() self.tintValue.setMinimumSize(w, h) self.tintValue.setMaximumSize(w, h) self.tintValue.setText( str("{:d}".format(self.sliderTint2User(self.sliderTint.value())))) self.sliderTemp.valueChanged.connect(self.tempUpdate) self.sliderTemp.sliderReleased.connect( lambda: self.tempUpdate(self.sliderTemp.value())) self.sliderTint.valueChanged.connect(self.tintUpdate) self.sliderTint.sliderReleased.connect( lambda: self.tintUpdate(self.sliderTint.value())) # layout l = QVBoxLayout() l.setAlignment(Qt.AlignTop) l.addWidget(self.listWidget1) hl2 = QHBoxLayout() hl2.addWidget(self.colorLabel) hl2.addWidget(self.colorChooserBtn) l.addLayout(hl2) l.addWidget(self.tempLabel) hl = QHBoxLayout() hl.addWidget(self.tempValue) hl.addWidget(self.sliderTemp) l.addLayout(hl) l.addWidget(self.tintLabel) hl1 = QHBoxLayout() hl1.addWidget(self.tintValue) hl1.addWidget(self.sliderTint) l.addLayout(hl1) self.setLayout(l) self.adjustSize() self.setDefaults() self.setWhatsThis( """<b> Color Filter</b> and <b>Photo Filter</b> use the multiply blending mode to mimic a warming or cooling filter put in front of the camera lens. The luminosity of the resulting image is corrected.<br> <b>Chromatic Adaptation</b> uses multipliers in the linear sRGB color space to adjust <b>temperature</b> and <b>tint</b>. """) # end of setWhatsThis
class scrollTextLabel(QLabel): deletesig = Signal() def __init__(self, text, Rect, scale, speed, line, color, bold, parent=None): super(scrollTextLabel, self).__init__(parent) if color == 'red': self.color = QColor(231, 0, 18, 255) # red elif color == 'white': self.color = QColor(255, 255, 246, 255) # white elif color == 'Grass': self.color = QColor(144, 195, 32, 255) # Grass elif color == 'Blue': self.color = QColor(0, 160, 234, 255) # Blue if bold: self.font = QFont("Helvetica", scale, QFont.Bold) # 20 25 30 粗體 else: self.font = QFont("Helvetica", scale) self.txt = text self.speed = speed # between 50 ~ 120 self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags( Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint) # 隱藏 FramelessWindow self.metrics = QFontMetrics(self.font) self.setFixedWidth(self.metrics.width(self.txt) + 10) self.setFixedHeight(self.metrics.height() + 5) # self.move(Rect.x() + Rect.width() * 0.97, Rect.y() + 50 * line) self.setFocusPolicy(Qt.NoFocus) self.hide() self.anim = QPropertyAnimation(self, 'pos') self.anim.setDuration(self.speed * 100) self.anim.setStartValue(QPoint(Rect.x() + Rect.width() * 0.95, Rect.y() + 50 * line)) self.anim.setEndValue(QPoint(-self.width() + Rect.x(), Rect.y() + 50 * line)) self.anim.setEasingCurve(QEasingCurve.Linear) self.show() self.repaint() self.anim.start() self.anim.finished.connect(self.sendslot) def sendslot(self): self.hide() def paintEvent(self, event): painter = QPainter(self) painter.save() path = QPainterPath() painter.setFont(self.font) painter.setRenderHint(QPainter.Antialiasing) pen = QPen(QColor(0, 0, 0, 230)) pen_width = 3 pen.setWidth(pen_width) len = self.metrics.width(self.txt) w = self.width() px = (len - w) / 2 if px < 0: px = -px py = (self.height() - self.metrics.height()) / 2 + self.metrics.ascent() if py < 0: py = -py path.addText(px + 2, py + 2, self.font, self.txt) painter.strokePath(path, pen) painter.drawPath(path) painter.fillPath(path, QBrush(self.color)) painter.restore()
def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None, mainForm=None): super().__init__(parent=parent) #self.targetImage = targetImage self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.setMinimumSize(axeSize, axeSize) self.setAttribute(Qt.WA_DeleteOnClose) # link back to image layer # using weak ref for back links if type(layer) in weakref.ProxyTypes: self.layer = layer else: self.layer = weakref.proxy(layer) # options self.options = None # exposure slider self.sliderExp = QbLUeSlider(Qt.Horizontal) self.sliderExp.setStyleSheet(QbLUeSlider.bLueSliderDefaultBWStylesheet) self.sliderExp.setTickPosition(QSlider.TicksBelow) self.sliderExp.setRange(-20, 20) self.sliderExp.setSingleStep(1) expLabel = QbLUeLabel() expLabel.setMaximumSize(150, 30) expLabel.setText("Exposure Correction") expLabel.doubleClicked.connect( lambda: self.sliderExp.setValue(self.defaultExpCorrection)) self.expValue = QbLUeLabel() font = self.expValue.font() metrics = QFontMetrics(font) w = metrics.width("1000 ") h = metrics.height() self.expValue.setMinimumSize(w, h) self.expValue.setMaximumSize(w, h) # exp done event handler def f(): self.sliderExp.setEnabled(False) self.expValue.setText( str("{:+.1f}".format(self.sliderExp.value() * self.DefaultStep))) self.onUpdateExposure(self.layer, self.sliderExp.value() * self.DefaultStep) self.sliderExp.setEnabled(True) # exp value changed slot def g(): self.expValue.setText( str("{:+.1f}".format(self.sliderExp.value() * self.DefaultStep))) self.sliderExp.valueChanged.connect(g) self.sliderExp.sliderReleased.connect(f) self.sliderExp.setValue(self.defaultExpCorrection / self.DefaultStep) self.expValue.setText(str("{:+.1f}".format(self.defaultExpCorrection))) #layout l = QVBoxLayout() l.setAlignment(Qt.AlignTop) l.addWidget(expLabel) hl = QHBoxLayout() hl.addWidget(self.expValue) hl.addWidget(self.sliderExp) l.addLayout(hl) l.setContentsMargins(20, 0, 20, 25) # left, top, right, bottom #l.addStretch(1) self.setLayout(l) self.adjustSize() self.setWhatsThis("""<b>Exposure Correction</b> Multiplicative correction in the linear sRGB color space.<br> """) # end setWhatsThis
def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None): super().__init__(layer=layer, targetImage=targetImage, parent=parent) ####################################### # Libraw correspondences: # rgb_xyz_matrix is libraw cam_xyz # camera_whitebalance is libraw cam_mul # daylight_whitebalance is libraw pre_mul # dng correspondences: # ASSHOTNEUTRAL tag value is (X,Y,Z) = 1 / rawpyObj.camera_whitebalance ########################################## rawpyObj = layer.parentImage.rawImage # constants and as shot values self.XYZ2CameraMatrix = rawpyObj.rgb_xyz_matrix[:3, :] self.XYZ2CameraInverseMatrix = np.linalg.inv(self.XYZ2CameraMatrix) # initial post processing multipliers (as shot) m1, m2, m3, m4 = rawpyObj.camera_whitebalance self.asShotMultipliers = ( m1 / m2, 1.0, m3 / m2, m4 / m2 ) # normalization is mandatory : for nef files white balance is around 256 self.asShotTemp, self.asShotTint = multipliers2TemperatureAndTint( *1 / np.array(self.asShotMultipliers[:3]), self.XYZ2CameraMatrix) self.rawMultipliers = self.asShotMultipliers # rawpyObj.camera_whitebalance # = 1/(dng ASSHOTNEUTRAL tag value) self.sampleMultipliers = False self.samples = [] ######################################## # XYZ-->Camera conversion matrix: # Last row is zero for RGB cameras (cf. rawpy and libraw docs). # type ndarray, shape (4,3) ######################################### # attributes initialized in setDefaults, declared here for the sake of correctness self.tempCorrection, self.tintCorrection, self.expCorrection, self.highCorrection,\ self.contCorrection, self.satCorrection, self.brCorrection = [None] * 7 # contrast spline vie (initialized by setContrastSpline) self.contrastForm = None # tone spline view (initialized by setToneSpline) self.toneForm = None # dock containers for contrast and tome forms self.dockC, self.dockT = None, None # options optionList0, optionNames0 = ['Auto Brightness', 'Preserve Highlights' ], ['Auto Expose', 'Preserve Highlights'] optionList1, optionNames1 = ['Auto WB', 'Camera WB', 'User WB' ], ['Auto', 'Camera (As Shot)', 'User'] optionList2, optionNames2 = [ 'cpLookTable', 'cpToneCurve', 'manualCurve' ], [ 'Use Camera Profile Look Table', 'Show Tone Curves', 'Show Contrast Curve' ] self.listWidget1 = optionsWidget( options=optionList0, optionNames=optionNames0, exclusive=False, changed=lambda: self.dataChanged.emit(1)) self.listWidget2 = optionsWidget( options=optionList1, optionNames=optionNames1, exclusive=True, changed=lambda: self.dataChanged.emit(1)) self.listWidget3 = optionsWidget( options=optionList2, optionNames=optionNames2, exclusive=False, changed=lambda: self.dataChanged.emit(2)) self.options = UDict( (self.listWidget1.options, self.listWidget2.options, self.listWidget3.options)) # display the 'as shot' temperature item = self.listWidget2.item(1) item.setText(item.text() + ' : %d' % self.asShotTemp) # temperature slider self.sliderTemp = QbLUeSlider(Qt.Horizontal) self.sliderTemp.setStyleSheet( QbLUeSlider.bLueSliderDefaultColorStylesheet) self.sliderTemp.setRange(0, 100) self.sliderTemp.setSingleStep(1) self.tempLabel = QLabel() self.tempLabel.setText("Temp") self.tempValue = QLabel() font = self.tempValue.font() metrics = QFontMetrics(font) w = metrics.width("10000") h = metrics.height() self.tempValue.setMinimumSize(w, h) self.tempValue.setMaximumSize(w, h) self.tempValue.setText( str("{:.0f}".format(self.slider2Temp(self.sliderTemp.value())))) self.sliderTemp.valueChanged.connect( self.tempUpdate) # signal send new value as parameter self.sliderTemp.sliderReleased.connect(lambda: self.tempUpdate( self.sliderTemp.value())) # signal pass no parameter # tint slider self.sliderTint = QbLUeSlider(Qt.Horizontal) self.sliderTint.setStyleSheet( QbLUeSlider.bLueSliderDefaultIMGColorStylesheet) self.sliderTint.setRange(0, 150) self.sliderTint.setSingleStep(1) self.tintLabel = QLabel() self.tintLabel.setText("Tint") self.tintValue = QLabel() font = self.tempValue.font() metrics = QFontMetrics(font) w = metrics.width("100") h = metrics.height() self.tintValue.setMinimumSize(w, h) self.tintValue.setMaximumSize(w, h) self.tintValue.setText( str("{:.0f}".format(self.sliderTint2User( self.sliderTint.value())))) self.sliderTint.valueChanged.connect(self.tintUpdate) self.sliderTint.sliderReleased.connect(lambda: self.tintUpdate( self.sliderTint.value())) # signal pass no parameter) ###################### # From libraw and dcraw sources: # Exposure and brightness are curve transformations. # Exposure curve is y = alpha*x, with cubic root ending; it is applied before demosaicing. # Brightness is (similar to) y = x**alpha and part of gamma transformation from linear sRGB to RGB. # Exposure and brightness both dilate the histogram towards highlights. # Exposure dilatation is uniform (homothety), brightness dilataion is # maximum for the midtones and the highlghts are preserved. # As a consequence, normal workflow begins with the adjustment of exposure, # to fill the entire range of the histogram and to adjust the highlights. Next, # one adjusts the brightness to put the midtones at the level we want them to be. # Cf. https://www.cambridgeincolour.com/forums/thread653.htm ##################### # profile combo self.dngDict = self.setCameraProfilesCombo() # cameraProfilesCombo index changed event handler def cameraProfileUpdate(value): self.dngDict = self.cameraProfilesCombo.itemData(value) if self.options['cpToneCurve']: toneCurve = dngProfileToneCurve( self.dngDict.get('ProfileToneCurve', [])) self.toneForm.baseCurve = [ QPointF(x * axeSize, -y * axeSize) for x, y in zip(toneCurve.dataX, toneCurve.dataY) ] self.toneForm.update() # recompute as shot temp and tint using new profile self.asShotTemp, self.asShotTint = multipliers2TemperatureAndTint( *1 / np.array(self.asShotMultipliers[:3]), self.XYZ2CameraMatrix, dngDict=self.dngDict ) # TODO 6/12/19 added keyword dngDict validate # display updated as shot temp item = self.listWidget2.item(1) item.setText(item.text().split(":")[0] + ': %d' % self.asShotTemp) # invalidate cache self.layer.bufCache_HSV_CV32 = None self.dataChanged.emit(2) # 2 = no postprocessing self.cameraProfilesCombo.currentIndexChanged.connect( cameraProfileUpdate) # denoising combo self.denoiseCombo = QComboBox() items = OrderedDict([('Off', 0), ('Medium', 1), ('Full', 2)]) for key in items: self.denoiseCombo.addItem(key, items[key]) # denoiseCombo index changed event handler def denoiseUpdate(value): self.denoiseValue = self.denoiseCombo.itemData(value) self.dataChanged.emit(1) self.denoiseCombo.currentIndexChanged.connect(denoiseUpdate) # overexposed area restoration self.overexpCombo = QComboBox() items = OrderedDict([('Clip', 0), ('Ignore', 1), ('Blend', 2), ('Reconstruct', 3)]) for key in items: self.overexpCombo.addItem(key, items[key]) # overexpCombo index changed event handler def overexpUpdate(value): self.overexpValue = self.overexpCombo.itemData(value) self.dataChanged.emit(1) self.overexpCombo.currentIndexChanged.connect(overexpUpdate) # exp slider self.sliderExp = QbLUeSlider(Qt.Horizontal) self.sliderExp.setStyleSheet(QbLUeSlider.bLueSliderDefaultBWStylesheet) self.sliderExp.setRange(0, 100) self.sliderExp.setSingleStep(1) self.expLabel = QLabel() self.expLabel.setText("Exp.") self.expValue = QLabel() font = self.expValue.font() metrics = QFontMetrics(font) w = metrics.width("+1.0") h = metrics.height() self.expValue.setMinimumSize(w, h) self.expValue.setMaximumSize(w, h) self.expValue.setText( str("{:.1f}".format(self.slider2Exp(self.sliderExp.value())))) # exp done event handler def expUpdate(value): self.expValue.setText( str("{:+.1f}".format( self.sliderExp2User(self.sliderExp.value())))) # move not yet terminated or value not modified if self.sliderExp.isSliderDown() or self.slider2Exp( value) == self.expCorrection: return try: self.sliderExp.valueChanged.disconnect() self.sliderExp.sliderReleased.disconnect() except RuntimeError: pass # rawpy: expCorrection range is -2.0...3.0, boiling down to exp_shift range 2**(-2)=0.25...2**3=8.0 self.expCorrection = self.slider2Exp(self.sliderExp.value()) self.dataChanged.emit(1) self.sliderExp.valueChanged.connect( expUpdate) # send new value as parameter self.sliderExp.sliderReleased.connect(lambda: expUpdate( self.sliderExp.value())) # signal pass no parameter self.sliderExp.valueChanged.connect( expUpdate) # send new value as parameter self.sliderExp.sliderReleased.connect(lambda: expUpdate( self.sliderExp.value())) # signal pass no parameter # brightness slider brSlider = QbLUeSlider(Qt.Horizontal) brSlider.setRange(1, 101) self.sliderExp.setSingleStep(1) brSlider.setStyleSheet(QbLUeSlider.bLueSliderDefaultBWStylesheet) self.sliderBrightness = brSlider brLabel = QLabel() brLabel.setText("Bright.") self.brValue = QLabel() font = self.expValue.font() metrics = QFontMetrics(font) w = metrics.width("+99") h = metrics.height() self.brValue.setMinimumSize(w, h) self.brValue.setMaximumSize(w, h) self.brValue.setText( str("{:+d}".format( int(self.brSlider2User(self.sliderBrightness.value()))))) # brightness done event handler def brUpdate(value): self.brValue.setText( str("{:+d}".format( int(self.brSlider2User(self.sliderBrightness.value()))))) # move not yet terminated or value not modified if self.sliderBrightness.isSliderDown() or self.slider2Br( value) == self.brCorrection: return try: self.sliderBrightness.valueChanged.disconnect() self.sliderBrightness.sliderReleased.disconnect() except RuntimeError: pass self.brCorrection = self.slider2Br(self.sliderBrightness.value()) self.dataChanged.emit(1) self.sliderBrightness.sliderReleased.connect( lambda: brUpdate(self.sliderBrightness.value())) self.sliderBrightness.valueChanged.connect( brUpdate) # send new value as parameter self.sliderBrightness.valueChanged.connect( brUpdate) # send new value as parameter self.sliderBrightness.sliderReleased.connect( lambda: brUpdate(self.sliderBrightness.value())) # contrast slider self.sliderCont = QbLUeSlider(Qt.Horizontal) self.sliderCont.setStyleSheet( QbLUeSlider.bLueSliderDefaultBWStylesheet) self.sliderCont.setRange(0, 20) self.sliderCont.setSingleStep(1) self.contLabel = QLabel() self.contLabel.setText("Cont.") self.contValue = QLabel() font = self.contValue.font() metrics = QFontMetrics(font) w = metrics.width("100") h = metrics.height() self.contValue.setMinimumSize(w, h) self.contValue.setMaximumSize(w, h) self.contValue.setText( str("{:.0f}".format(self.slider2Cont(self.sliderCont.value())))) # cont done event handler def contUpdate(value): self.contValue.setText( str("{:.0f}".format(self.slider2Cont( self.sliderCont.value())))) # move not yet terminated or value not modified if self.sliderCont.isSliderDown() or self.slider2Cont( value) == self.tempCorrection: return try: self.sliderCont.valueChanged.disconnect() self.sliderCont.sliderReleased.disconnect() except RuntimeError: pass self.contCorrection = self.slider2Cont(self.sliderCont.value()) self.contValue.setText(str("{:+d}".format(self.contCorrection))) # force to recalculate the spline self.layer.autoSpline = True self.dataChanged.emit( 3) # no postprocessing and no camera profile stuff self.sliderCont.valueChanged.connect( contUpdate) # send new value as parameter self.sliderCont.sliderReleased.connect(lambda: contUpdate( self.sliderCont.value())) # signal has no parameter self.sliderCont.valueChanged.connect( contUpdate) # send new value as parameter self.sliderCont.sliderReleased.connect(lambda: contUpdate( self.sliderCont.value())) # signal has no parameter # saturation slider self.sliderSat = QbLUeSlider(Qt.Horizontal) self.sliderSat.setStyleSheet( QbLUeSlider.bLueSliderDefaultColorStylesheet) self.sliderSat.setRange(0, 100) self.sliderSat.setSingleStep(1) satLabel = QLabel() satLabel.setText("Sat.") self.satValue = QLabel() font = self.satValue.font() metrics = QFontMetrics(font) w = metrics.width("+10") h = metrics.height() self.satValue.setMinimumSize(w, h) self.satValue.setMaximumSize(w, h) self.satValue.setText( str("{:+d}".format(self.slider2Sat(self.sliderSat.value())))) """sat done event handler""" def satUpdate(value): self.satValue.setText( str("{:+d}".format(self.slider2Sat(self.sliderSat.value())))) # move not yet terminated or value not modified if self.sliderSat.isSliderDown() or self.slider2Sat( value) == self.satCorrection: return try: self.sliderSat.valueChanged.disconnect() self.sliderSat.sliderReleased.disconnect() except RuntimeError: pass self.satCorrection = self.slider2Sat(self.sliderSat.value()) self.dataChanged.emit( 3) # no post processing and no camera profile stuff self.sliderSat.valueChanged.connect( satUpdate) # send new value as parameter self.sliderSat.sliderReleased.connect(lambda: satUpdate( self.sliderSat.value())) # signal has no parameter self.sliderSat.valueChanged.connect( satUpdate) # send new value as parameter self.sliderSat.sliderReleased.connect(lambda: satUpdate( self.sliderSat.value())) # signal has no parameter # layout l = QVBoxLayout() l.addWidget(self.listWidget3) hl01 = QHBoxLayout() hl01.addWidget(QLabel('Camera Profile')) hl01.addWidget(self.cameraProfilesCombo) l.addLayout(hl01) hl0 = QHBoxLayout() hl0.addWidget(QLabel('Denoising')) hl0.addWidget(self.denoiseCombo) l.addLayout(hl0) hl00 = QHBoxLayout() hl00.addWidget(QLabel('Overexp. Restoration')) hl00.addWidget(self.overexpCombo) l.addLayout(hl00) hl1 = QHBoxLayout() hl1.addWidget(self.expLabel) hl1.addWidget(self.expValue) hl1.addWidget(self.sliderExp) l.addLayout(hl1) hl8 = QHBoxLayout() hl8.addWidget(brLabel) hl8.addWidget(self.brValue) hl8.addWidget(self.sliderBrightness) l.addLayout(hl8) l.addWidget(self.listWidget1) vl1 = QVBoxLayout() vl1.addWidget(self.listWidget2) gb1 = QGroupBox() gb1.setTitle('White Balance') hl2 = QHBoxLayout() hl2.addWidget(self.tempLabel) hl2.addWidget(self.tempValue) hl2.addWidget(self.sliderTemp) hl3 = QHBoxLayout() hl3.addWidget(self.tintLabel) hl3.addWidget(self.tintValue) hl3.addWidget(self.sliderTint) vl1.addLayout(hl2) vl1.addLayout(hl3) gb1.setLayout(vl1) l.addWidget(gb1) hl4 = QHBoxLayout() hl4.addWidget(self.contLabel) hl4.addWidget(self.contValue) hl4.addWidget(self.sliderCont) hl7 = QHBoxLayout() hl7.addWidget(satLabel) hl7.addWidget(self.satValue) hl7.addWidget(self.sliderSat) # separator sep = QFrame() sep.setFrameShape(QFrame.HLine) sep.setFrameShadow(QFrame.Sunken) l.addWidget(sep) l.addLayout(hl4) l.addLayout(hl7) l.addStretch(1) self.setLayout(l) self.adjustSize() self.setDefaults() self.setWhatsThis("""<b>Development of raw files</b><br> <b>Default settings</b> are a good starting point.<br> A <b>Tone Curve</b> is applied to the raw image prior to postprocessing.<br> The cuvre can be edited by checking the option <b>Show Tone Curve</b>; this option works best with manual exposure.<br> <b>Contrast</b> correction is based on an automatic algorithm well suited to multi-mode histograms.<br> <b>Brightness, Contrast</b> and <b>Saturation</b> levels</b> are adjustable with the correponding sliders.<br> The <b>Contrast Curve</b> can be edited manually by checking the option <b>Show Contrast Curve</b>.<br> Uncheck <b>Auto Expose</b> to adjust the exposure manually.<br> The <b>OverExp. Rest.</b> slider controls the mode of restoration of overexposed areas. Valid values are 0 to 3 (0=clip;1=unclip;2=blend;3=rebuild); (with Auto Exposed checked the mode is clip).<br> """) # end of setWhatsThis
def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None, mainForm=None): super().__init__(parent=parent) self.tempCorrection = 6500 self.tintCorrection = 1.0 self.setStyleSheet( 'QRangeSlider * {border: 0px; padding: 0px; margin: 0px}') self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.setMinimumSize(axeSize, axeSize) self.setAttribute(Qt.WA_DeleteOnClose) # link back to image layer self.layer = weakProxy(layer) """ # using weak ref for back links if type(layer) in weakref.ProxyTypes: self.layer = layer else: self.layer = weakref.proxy(layer) """ self.defaultTemp = sRGBWP # ref temperature D65 self.defaultTint = 0 # options optionList, optionNames = ['Photo Filter', 'Chromatic Adaptation' ], ['Photo Filter', 'Chromatic Adaptation'] self.listWidget1 = optionsWidget( options=optionList, optionNames=optionNames, exclusive=True, changed=lambda: self.dataChanged.emit()) self.listWidget1.checkOption(self.listWidget1.intNames[1]) self.options = self.listWidget1.options # temp slider self.sliderTemp = QbLUeSlider(Qt.Horizontal) self.sliderTemp.setStyleSheet( QbLUeSlider.bLueSliderDefaultIColorStylesheet) self.sliderTemp.setRange( 17, 100 ) # 250) # valid range for spline approximation is 1667..25000, cf. colorConv.temperature2xyWP self.sliderTemp.setSingleStep(1) tempLabel = QbLUeLabel() tempLabel.setMaximumSize(150, 30) tempLabel.setText("Color temperature") tempLabel.doubleClicked.connect(lambda: self.sliderTemp.setValue( self.temp2Slider(self.defaultTemp))) self.tempValue = QLabel() font = self.tempValue.font() metrics = QFontMetrics(font) w = metrics.width("0000") h = metrics.height() self.tempValue.setMinimumSize(w, h) self.tempValue.setMaximumSize(w, h) self.tempValue.setText( str("{:d}".format(self.sliderTemp2User(self.sliderTemp.value())))) # tint slider self.sliderTint = QbLUeSlider(Qt.Horizontal) self.sliderTint.setStyleSheet( QbLUeSlider.bLueSliderDefaultMGColorStylesheet) self.sliderTint.setRange( 0, 100 ) # 250) # valid range for spline approximation is 1667..25000, cf. colorConv.temperature2xyWP self.sliderTint.setSingleStep(1) tintLabel = QbLUeLabel() tintLabel.setMaximumSize(150, 30) tintLabel.setText("Tint") tintLabel.doubleClicked.connect(lambda: self.sliderTint.setValue( self.tint2Slider(self.defaultTint))) self.tintValue = QLabel() font = self.tintValue.font() metrics = QFontMetrics(font) w = metrics.width("0000") h = metrics.height() self.tintValue.setMinimumSize(w, h) self.tintValue.setMaximumSize(w, h) self.tintValue.setText( str("{:d}".format(self.sliderTint2User(self.sliderTint.value())))) # temp change event handler def tempUpdate(value): self.tempValue.setText( str("{:d}".format(self.sliderTemp2User(value)))) # move not yet terminated or values not modified if self.sliderTemp.isSliderDown() or self.slider2Temp( value) == self.tempCorrection: return self.sliderTemp.valueChanged.disconnect() self.sliderTemp.sliderReleased.disconnect() self.tempCorrection = self.slider2Temp(value) self.dataChanged.emit() self.sliderTemp.valueChanged.connect(tempUpdate) self.sliderTemp.sliderReleased.connect( lambda: tempUpdate(self.sliderTemp.value())) # tint change event handler def tintUpdate(value): self.tintValue.setText( str("{:d}".format(self.sliderTint2User(value)))) # move not yet terminated or values not modified if self.sliderTint.isSliderDown() or self.slider2Tint( value) == self.tintCorrection: return self.sliderTint.valueChanged.disconnect() self.sliderTint.sliderReleased.disconnect() self.tintCorrection = self.slider2Tint(value) self.dataChanged.emit() self.sliderTint.valueChanged.connect(tintUpdate) self.sliderTint.sliderReleased.connect( lambda: tintUpdate(self.sliderTint.value())) self.sliderTemp.valueChanged.connect(tempUpdate) self.sliderTemp.sliderReleased.connect( lambda: tempUpdate(self.sliderTemp.value())) self.sliderTint.valueChanged.connect(tintUpdate) self.sliderTint.sliderReleased.connect( lambda: tintUpdate(self.sliderTint.value())) # layout l = QVBoxLayout() l.setAlignment(Qt.AlignTop) l.addWidget(self.listWidget1) l.addWidget(tempLabel) hl = QHBoxLayout() hl.addWidget(self.tempValue) hl.addWidget(self.sliderTemp) l.addLayout(hl) l.addWidget(tintLabel) hl1 = QHBoxLayout() hl1.addWidget(self.tintValue) hl1.addWidget(self.sliderTint) l.addLayout(hl1) self.setLayout(l) self.adjustSize() self.dataChanged.connect( self.updateLayer) # TODO move to setDefaults 3/12/18 self.setStyleSheet("QListWidget, QLabel {font : 7pt;}") self.setDefaults() self.setWhatsThis("""<b>Color Temperature</b><br> <b>Photo Filter</b> uses the multiply blending mode to mimic a color filter in front of the camera lens.<br> <b>Chromatic Adaptation</b> uses multipliers in the linear sRGB color space to adjust <b>temperature</b> and <b>tint</b>. """) # end of setWhatsThis