# Pigment.O is a Krita plugin and it is a Color Picker and Color Mixer. # Copyright ( C ) 2020 Ricardo Jeremias. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # ( at your option ) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . #region Imports #################################################################### # Python Modules import os import math import time import subprocess import zipfile # Krita Module from krita import * # PyQt5 Modules from PyQt5 import QtWidgets, QtCore, QtGui, uic # Pigment.O Modules from .pigment_o_constants import * from .pigment_o_calculations import ( Geometry, Convert, ) #endregion #region Shared ##################################################################### # ZIP def Read_Zip( self, location, tan_range, space, shape ): try: qpixmap_list = [] if zipfile.is_zipfile( location ): archive = zipfile.ZipFile( location, "r" ) for i in range( 0, tan_range+1 ): # Mode Sensitive name = f"{ space }_{ shape }_{ str( i ).zfill( 3 ) }.png" # Read File archive_open = archive.open( name ) archive_read = archive_open.read() # Image image = QImage() image.loadFromData( archive_read ) qpixmap = QPixmap().fromImage( image ) qpixmap_list.append( qpixmap ) return qpixmap_list except Exception as e: try:QtCore.qDebug( f"Pigment.O ERROR | { e }" ) except:pass # Colors def krita_theme( self ): theme = QApplication.palette().color( QPalette.Window ).value() if theme > 128: self.color_1 = QColor( "#191919" ) # Dark self.color_2 = QColor( "#e5e5e5" ) # Light else: self.color_1 = QColor( "#e5e5e5" ) # Light self.color_2 = QColor( "#191919" ) # Dark self.color_black = QColor( "#000000" ) self.color_white = QColor( "#ffffff" ) self.color_alpha = QColor( 0, 0, 0, 50 ) # Region def Circles( self, painter ): # Circle 0 ( Everything ) v0a = 0 v0b = 1 - ( 2*v0a ) circle_0 = QPainterPath() circle_0.addEllipse( self.px + self.side * v0a, self.py + self.side * v0a, self.side * v0b, self.side * v0b ) # Circle 1 ( Outter Most Region ) v1a = 0.025 v1b = 1 - ( 2*v1a ) circle_1 = QPainterPath() circle_1.addEllipse( self.px + self.side * v1a, self.py + self.side * v1a, self.side * v1b, self.side * v1b ) # Circle 2 ( Inner Most Region ) v2a = 0.068 v2b = 1 - ( 2*v2a ) circle_2 = QPainterPath() circle_2.addEllipse( self.px + self.side * v2a, self.py + self.side * v2a, self.side * v2b, self.side * v2b ) # Circle 3 ( Central Dot ) v3a = 0.13 v3b = 1 - ( 2*v3a ) circle_3 = QPainterPath() circle_3.addEllipse( self.px + self.side * v3a, self.py + self.side * v3a, self.side * v3b, self.side * v3b ) # Return return circle_0, circle_1, circle_2, circle_3 # Cursor def Cursor_Normal( self, painter, size ): # Variables w = 2 # Mask mask = QPainterPath() mask.addEllipse( int( self.event_x - size ), int( self.event_y - size ), int( size * 2 ), int( size * 2 ), ) mask.addEllipse( int( self.event_x - size + w * 2 ), int( self.event_y - size + w * 2 ), int( size * 2 - w * 4 ), int( size * 2 - w * 4 ), ) painter.setClipPath( mask ) # Black Circle painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_black ) ) painter.drawEllipse( int( self.event_x - size ), int( self.event_y - size ), int( size * 2 ), int( size * 2 ), ) # White Circle painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_white ) ) painter.drawEllipse( int( self.event_x - size + w ), int( self.event_y - size + w ), int( size * 2 - w * 2 ), int( size * 2 - w * 2 ), ) def Cursor_Zoom( self, painter, zoom_size, margin_size ): # Border painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_black ) ) painter.drawEllipse( int( self.event_x - zoom_size ), int( self.event_y - zoom_size ), int( zoom_size * 2 ), int( zoom_size * 2 ), ) # Hex Color painter.setBrush( QBrush( self.hex_color ) ) painter.drawEllipse( int( self.event_x - zoom_size + margin_size ), int( self.event_y - zoom_size + margin_size ), int( zoom_size * 2 - margin_size * 2 ), int( zoom_size * 2 - margin_size * 2 ), ) #endregion #region Header ##################################################################### class Color_Header( QWidget ): SIGNAL_SWAP = QtCore.pyqtSignal( int ) SIGNAL_SHIFT = QtCore.pyqtSignal( bool ) SIGNAL_RANDOM = QtCore.pyqtSignal( int ) SIGNAL_COMP = QtCore.pyqtSignal( int ) SIGNAL_ANALYSE = QtCore.pyqtSignal( int ) # Init def __init__( self, parent ): super( Color_Header, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, 100 ) def Init_Variables( self ): # Widget self.widget_width = 100 self.widget_height = 40 # Variables self.mode_ab = True self.progress_bar = 0 # Other self.other_show = False self.ui_k = 80 self.other_a = self.ui_k self.other_b = self.widget_width - self.ui_k # Colors HEX self.kac_1 = QColor( '#000000' ) self.kac_2 = QColor( '#323232' ) self.kbc_1 = QColor( '#ffffff' ) self.kbc_2 = QColor( '#cbcbcb' ) # Modules self.geometry = Geometry() # Relay def Set_Mode_AB( self, mode_ab ): self.mode_ab = mode_ab self.update() def Set_Size( self, widget_width, widget_height ): # widget self.widget_width = widget_width self.widget_height = widget_height # Limits if widget_width <= 200: self.other_a = widget_width * 0.4 self.other_b = widget_width * 0.7 else: self.other_a = self.ui_k self.other_b = widget_width - self.ui_k # Update self.update() def Set_Color_A1( self, kac_1 ): self.kac_1 = QColor( kac_1 ) self.update() def Set_Color_A2( self, kac_2 ): self.kac_2 = QColor( kac_2 ) self.update() def Set_Color_B1( self, kbc_1 ): self.kbc_1 = QColor( kbc_1 ) self.update() def Set_Color_B2( self, kbc_2 ): self.kbc_2 = QColor( kbc_2 ) self.update() def Set_Progress( self, progress_bar ): self.progress_bar = progress_bar self.update() # Mouse Interaction def mousePressEvent( self, event ): self.update() def mouseMoveEvent( self, event ): self.update() def mouseReleaseEvent( self, event ): self.update() # Context Menu Event def contextMenuEvent( self, event ): # Variables if self.mode_ab == True: side = "BG" else: side = "FG" # Menu if event.modifiers() == QtCore.Qt.NoModifier: cmenu = QMenu( self ) # Actions cmenu_swap = cmenu.addAction( "FG-BG Swap" ) cmenu_active = cmenu.addAction( side + " Active" ) cmenu_random = cmenu.addAction( "Random" ) cmenu_complementary = cmenu.addAction( "Complementary" ) cmenu_analyse = cmenu.addAction( "Analyse" ) # Map action = cmenu.exec_( self.mapToGlobal( event.pos() ) ) # Triggers if action == cmenu_swap: self.SIGNAL_SWAP.emit( 0 ) if action == cmenu_active: self.mode_ab = not self.mode_ab self.SIGNAL_SHIFT.emit( self.mode_ab ) if action == cmenu_random: self.SIGNAL_RANDOM.emit( 0 ) if action == cmenu_complementary: self.SIGNAL_COMP.emit( 0 ) if action == cmenu_analyse: self.SIGNAL_ANALYSE.emit( 0 ) # Interaction def enterEvent( self, event ): self.other_show = True self.update() def leaveEvent( self, event ): self.other_show = False self.update() # Paint Style def paintEvent( self, event ): # Theme krita_theme( self ) # Start Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) painter.setPen( QtCore.Qt.NoPen ) # Variable w1 = self.widget_width + 1 w2 = int( w1 * 0.5 ) # Progress Bar Mask mask = QPainterPath() mask.addRect( 0, 0, self.widget_width * self.progress_bar, self.widget_height ) painter.setClipPath( mask ) # Mode FG if self.mode_ab == True: # FG Active painter.setBrush( QBrush( self.kac_1 ) ) painter.drawRect( 0, 0, w2, self.widget_height ) # FG Previous painter.setBrush( QBrush( self.kac_2 ) ) painter.drawRect( w2, 0, w2, self.widget_height ) # BG over FG painter.setBrush( QBrush( self.kbc_1 ) ) if self.other_show == True: point = w1 - self.other_a painter.drawRect( point, 0, self.other_a, self.widget_height ) # Mode BG if self.mode_ab == False: # BG Active painter.setBrush( QBrush( self.kbc_1 ) ) painter.drawRect( 0, 0, w2, self.widget_height ) # BG Previous painter.setBrush( QBrush( self.kbc_2 ) ) painter.drawRect( w2, 0, w2, self.widget_height ) # FG over BG painter.setBrush( QBrush( self.kac_1 ) ) if self.other_show == True: painter.drawRect( 0, 0, self.other_a, self.widget_height ) class Harmony_Swatch( QWidget ): SIGNAL_RULE = QtCore.pyqtSignal( str ) SIGNAL_EDIT = QtCore.pyqtSignal( bool ) SIGNAL_INDEX = QtCore.pyqtSignal( int ) # Init def __init__( self, parent ): super( Harmony_Swatch, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, 100 ) def Init_Variables( self ): # Widget self.widget_width = 0 self.widget_height = 0 # Index self.harmony_rule = "Analogous" # "Monochromatic" "Complementary" "Analogous" "Triadic" "Tetradic" self.harmony_edit = False self.harmony_index = 0 self.harmony_parts = 5 self.harmony_color = [ "#000000", "#000000", "#000000", "#000000", "#000000" ] # Modules self.geometry = Geometry() # Relay def Set_Size( self, widget_width, widget_height ): self.widget_width = widget_width self.widget_height = widget_height self.update() def Set_Harmony_Rule( self, harmony_rule ): self.harmony_rule = harmony_rule self.update() def Set_Harmony_Parts( self, har_parts, har_color ): self.harmony_parts = har_parts self.harmony_color = har_color self.update() def Set_WheelSpace( self, wheel_space ): self.wheel_space = wheel_space self.update() # Update def Update_Harmony( self, harmony_rule, harmony_edit, harmony_index ): self.harmony_rule = harmony_rule self.harmony_edit = harmony_edit self.harmony_index = harmony_index self.update() def Update_Index( self, harmony_index ): self.harmony_index = harmony_index self.update() # Mouse Interaction def mousePressEvent( self, event ): self.Index_Signal( event ) self.update() def mouseMoveEvent( self, event ): self.Index_Signal( event ) self.update() def mouseDoubleClickEvent( self, event ): self.Index_Signal( event ) self.update() def mouseReleaseEvent( self, event ): self.Index_Signal( event ) self.update() # Signals def Index_Signal( self, event ): self.event_x = self.geometry.Limit_Range( event.x(), 0, self.widget_width ) percentage = self.event_x / self.widget_width self.harmony_index = self.geometry.Limit_Range( int( percentage * self.harmony_parts ), 0, self.harmony_parts - 1 ) + 1 self.SIGNAL_INDEX.emit( self.harmony_index ) self.update() # Context def contextMenuEvent( self, event ): # Menu cmenu = QMenu( self ) # Actions Harmony Rule cmenu_rule = cmenu.addMenu( "Harmony Rule" ) cmenu_rule_m = cmenu_rule.addAction( "Monochromatic" ) cmenu_rule_c = cmenu_rule.addAction( "Complementary" ) cmenu_rule_a = cmenu_rule.addAction( "Analogous" ) cmenu_rule_tri = cmenu_rule.addAction( "Triadic" ) cmenu_rule_tet = cmenu_rule.addAction( "Tetradic" ) cmenu_rule_m.setCheckable( True ) cmenu_rule_c.setCheckable( True ) cmenu_rule_a.setCheckable( True ) cmenu_rule_tri.setCheckable( True ) cmenu_rule_tet.setCheckable( True ) cmenu_rule_m.setChecked( self.harmony_rule == "Monochromatic" ) cmenu_rule_c.setChecked( self.harmony_rule == "Complementary" ) cmenu_rule_a.setChecked( self.harmony_rule == "Analogous" ) cmenu_rule_tri.setChecked( self.harmony_rule == "Triadic" ) cmenu_rule_tet.setChecked( self.harmony_rule == "Tetradic" ) # Actions Edit cmenu_edit = cmenu.addAction( "Edit" ) cmenu_edit.setCheckable( True ) cmenu_edit.setChecked( self.harmony_edit ) action = cmenu.exec_( self.mapToGlobal( event.pos() ) ) # Triggers if action == cmenu_rule_m: self.harmony_rule = "Monochromatic" self.SIGNAL_RULE.emit( self.harmony_rule ) if action == cmenu_rule_c: self.harmony_rule = "Complementary" self.SIGNAL_RULE.emit( self.harmony_rule ) if action == cmenu_rule_a: self.harmony_rule = "Analogous" self.SIGNAL_RULE.emit( self.harmony_rule ) if action == cmenu_rule_tri: self.harmony_rule = "Triadic" self.SIGNAL_RULE.emit( self.harmony_rule ) if action == cmenu_rule_tet: self.harmony_rule = "Tetradic" self.SIGNAL_RULE.emit( self.harmony_rule ) if action == cmenu_edit: self.harmony_edit = not self.harmony_edit self.SIGNAL_EDIT.emit( self.harmony_edit ) # Paint def paintEvent( self, event ): # Theme krita_theme( self ) # Start Qpainter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Correct Variable Range if self.harmony_index > self.harmony_parts: self.harmony_index = 0 # Swatch painter.setPen( QtCore.Qt.NoPen ) points = [] factor = 0.7 width = int( self.widget_width / self.harmony_parts + 1 ) height = int( self.widget_height*( 1-factor ) ) for i in range( 0, self.harmony_parts ): # Variables px = int( ( self.widget_width / self.harmony_parts ) * i ) py = int( self.widget_height * factor ) # Color painter.setBrush( QBrush( QColor( self.harmony_color[i] ) ) ) painter.drawRect( px, 0, width, self.widget_height ) # Stops points.append( px ) points.append( self.widget_width ) if self.harmony_index > len( points ): self.harmony_index = 1 # Index Cursor if self.harmony_index != 0: px = points[self.harmony_index-1] pw = points[self.harmony_index] width = pw - px painter.setBrush( QBrush( self.color_2 ) ) painter.drawRect( px, py, width, height+1 ) painter.setBrush( QBrush( self.color_1 ) ) painter.drawRect( px+1, py+1, width-2, height-1 ) class Harmony_Spread( QWidget ): SIGNAL_SPAN = QtCore.pyqtSignal( float ) SIGNAL_RELEASE = QtCore.pyqtSignal( int ) # Init def __init__( self, parent ): super( Harmony_Spread, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, 100 ) def Init_Variables( self ): # Widget self.widget_width = 0 self.widget_height = 0 self.w2 = 0 # Range self.harmony_span = 0.2 self.harmony_rule = "Analogous" # "Monochromatic" "Complementary" "Analogous" "Triadic" "Tetradic" self.no_span = ["Monochromatic", "Complementary"] # Have no span # Modules self.geometry = Geometry() # Relay def Set_Size( self, widget_width, widget_height ): self.widget_width = widget_width self.widget_height = widget_height self.w2 = int( widget_width * 0.5 ) self.update() def Set_Rule( self, rule ): self.harmony_rule = rule self.update() # Update def Update_Span( self, harmony_span ): # range 0-1 self.harmony_span = harmony_span self.update() # Mouse Interaction def mousePressEvent( self, event ): if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Range_Width( event ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ControlModifier ): self.Range_Pin( event ) def mouseMoveEvent( self, event ): if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Range_Width( event ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ControlModifier ): self.Range_Pin( event ) def mouseDoubleClickEvent( self, event ): if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Range_Width( event ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ControlModifier ): self.Range_Pin( event ) def mouseReleaseEvent( self, event ): if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Range_Width( event ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ControlModifier ): self.Range_Pin( event ) self.SIGNAL_RELEASE.emit( 0 ) # Signals def Range_Width( self, event ): # Read event_x = event.x() # Consider Widget Size value = self.geometry.Limit_Range( event_x, 0, self.widget_width ) # Sides if value >= self.w2: span_right = value span_left = self.widget_width - value else: span_left = value span_right = self.widget_width - value # Normalize Values delta = span_right - span_left if self.widget_width == 0: self.harmony_span = 0 else: self.harmony_span = delta / self.widget_width self.SIGNAL_SPAN.emit( self.harmony_span ) self.update() def Range_Pin( self, event ): # Read event_x = event.x() # Consider Widget Size value = self.geometry.Limit_Range( event_x, 0, self.widget_width ) # Pin stops = 72 unit = self.widget_width / stops distances = [] for i in range( 0, stops+1 ): dist = self.geometry.Trig_2D_Points_Distance( value, 0, ( unit * i ), 0 ) distances.append( dist ) value_min = min( distances ) index = distances.index( value_min ) value = unit * index percent = value / self.widget_width # Sides if value >= self.w2: span_right = value span_left = self.widget_width - value else: span_left = value span_right = self.widget_width - value # Normalize Values delta = span_right - span_left if self.widget_width == 0: self.harmony_span = 0 else: self.harmony_span = delta / self.widget_width self.SIGNAL_SPAN.emit( self.harmony_span ) self.update() # Paint def paintEvent( self, event ): # Theme krita_theme( self ) # Start Qpainter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Harmony Angle painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_1 ) ) if self.harmony_rule in self.no_span: px = int( self.w2 ) width = 1 else: px = int( self.w2 - ( self.harmony_span * self.widget_width * 0.5 ) ) width = int( self.widget_width * self.harmony_span ) if width <= 1: width = 1 painter.drawRect( px, 2, width, self.widget_height-4 ) #endregion #region Panels ##################################################################### class Panel_Fill( QWidget ): # Init def __init__( self, parent ): super( Panel_Fill, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, render_height ) def Init_Variables( self ): # Widget self.widget_width = 0 self.widget_height = 0 # Display self.hex_color = QColor( "#000000" ) # Set def Set_Size( self, widget_width, widget_height ): self.widget_width = widget_width self.widget_height = widget_height self.update() # Updates def Update_Panel( self, color ): self.hex_color = QColor( color["hex6_d"] ) self.update() # Paint def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Draw Pixmaps painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.hex_color ) ) painter.drawRect( 0, 0, self.widget_width, self.widget_height ) class Panel_Square( QWidget ): SIGNAL_VALUE = QtCore.pyqtSignal( dict ) SIGNAL_TAN = QtCore.pyqtSignal( float ) SIGNAL_RELEASE = QtCore.pyqtSignal( int ) SIGNAL_PIN_INDEX = QtCore.pyqtSignal( int ) SIGNAL_PIN_EDIT = QtCore.pyqtSignal( dict ) # Init def __init__( self, parent ): super( Panel_Square, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, render_height ) def Init_Variables( self ): # Widget self.widget_width = 0 self.widget_height = 0 self.origin_x = 0 self.origin_y = 0 self.origin_tan_axis = 0 self.event_x = 0 self.event_y = 0 self.press = False self.pressure = 0 self.input_pressure = 0.5 # Display self.zoom = False self.tan_axis = 0 # 0-360 because of background index self.tan_range = None # 360 255 self.qpixmap_list = [] # Format self.directory = None # Path self.d_cm = None # "A" "RGB" "CMYK" "YUV" "XYZ" "LAB" self.shape = None # "3" "4" "R" self.chan = None # "hsv" "hsl" "hsy" "ard" / "yuv" # Wheel self.wheel_space = None # "HSV" "HSL" "HSY" "ARD" # Colors self.hex_color = QColor( "#000000" ) self.color = None # Harmony Colors self.harmony_rule = None self.harmony_index = None self.harmony_list = None # Pinned Colors self.pin_index = None self.pin_list = None # Analyse Colors self.analyse = None # YUV primaries self.CR = [ 1, 0, 0 ] self.CY = [ 1, 1, 0 ] self.CG = [ 0, 1, 0 ] self.CC = [ 0, 1, 1 ] self.CB = [ 0, 0, 1 ] self.CM = [ 1, 0, 1 ] # Modules self.geometry = Geometry() self.convert = None def Init_Convert( self, convert ): self.convert = convert self.update() # Set def Set_ColorModel( self, d_cm ): self.d_cm = d_cm self.Set_ColorSpace_inDocument( self.directory, self.d_cm, self.wheel_space, self.shape ) self.update() def Set_WheelSpace( self, wheel_space ): self.wheel_space = wheel_space self.Set_ColorSpace_inDocument( self.directory, self.d_cm, self.wheel_space, self.shape ) self.update() def Set_Tangent_Range( self, tan_range ): self.tan_range = tan_range self.update() def Set_ColorSpace_inDocument( self, directory, d_cm, wheel_space, shape ): # Variables self.directory = directory self.d_cm = d_cm self.wheel_space = wheel_space self.shape = shape self.chan = self.wheel_space.lower() # Cursor if self.color != None: if self.shape == "3": cx = self.color["hsl_2"] cy = 1 - self.color["hsl_3"] inter = self.Triangle_Inter( cy, 1, 1 ) self.event_x = self.geometry.Limit_Range( cx * inter * self.widget_width, 0, self.widget_width ) self.event_y = self.geometry.Limit_Range( cy * self.widget_height, 0, self.widget_height ) if self.shape == "4": self.event_x = self.geometry.Limit_Range( self.color[f"{self.chan}_2"] * self.widget_width, 0, self.widget_width ) self.event_y = self.geometry.Limit_Range( self.color[f"{self.chan}_3"] * self.widget_height, 0, self.widget_height ) if self.shape == "R": cx = self.color["hsl_2"] cy = 1 - self.color["hsl_3"] mini, maxi, delta = self.Diamond_Inter( cy, 1, 1 ) value = mini + cx * delta self.event_x = self.geometry.Limit_Range( value * self.widget_width, mini * self.widget_width, maxi * self.widget_width ) self.event_y = self.geometry.Limit_Range( cy * self.widget_height, 0, self.widget_height ) # Primaries if self.wheel_space == "YUV": self.CR = self.convert.rgb_to_yuv( 1, 0, 0 ) self.CY = self.convert.rgb_to_yuv( 1, 1, 0 ) self.CG = self.convert.rgb_to_yuv( 0, 1, 0 ) self.CC = self.convert.rgb_to_yuv( 0, 1, 1 ) self.CB = self.convert.rgb_to_yuv( 0, 0, 1 ) self.CM = self.convert.rgb_to_yuv( 1, 0, 1 ) # Read Zip File location = os.path.join( self.directory, panel ) location = os.path.join( location, f"{ self.d_cm }_{ self.wheel_space }_{ self.shape }.zip" ) self.qpixmap_list = Read_Zip( self, location, self.tan_range, self.wheel_space, self.shape ) # Update self.update() def Set_Size( self, widget_width, widget_height ): # Variables self.widget_width = widget_width self.widget_height = widget_height # Mask ( slightly bigger than color display ) if self.shape == "3": polygon = QPolygon( [ QPoint( int( -1 ), int( -1 ) ), QPoint( int( self.widget_width + 1 ), int( self.widget_height * 0.5 ) ), QPoint( int( -1 ), int( self.widget_height + 1 ) ), ] ) triangle = QRegion( polygon, Qt.OddEvenFill ) self.setMask( triangle ) if self.shape == "4": polygon = QPolygon( [ QPoint( int( -1 ), int( -1 ) ), QPoint( int( self.widget_width + 1 ), int( -1 ) ), QPoint( int( self.widget_width + 1 ), int( self.widget_height + 1 ) ), QPoint( int( -1 ), int( self.widget_height + 1 ) ), ] ) square = QRegion( polygon, Qt.OddEvenFill ) self.setMask( square ) if self.shape == "R": polygon = QPolygon( [ QPoint( int( self.widget_width * 0.5 ), int( -1 ) ), QPoint( int( self.widget_width + 1 ), int( self.widget_height * 0.5 ) ), QPoint( int( self.widget_width * 0.5 ), int( self.widget_height + 1 ) ), QPoint( int( -1 ), int( self.widget_height * 0.5 ) ), ] ) diamond = QRegion( polygon, Qt.OddEvenFill ) self.setMask( diamond ) # Update self.update() def Set_Zoom( self, boolean ): self.press = boolean self.zoom = boolean self.update() # Updates def Update_Panel( self, color ): # Variables self.color = color # Display self.hex_color = QColor( color["hex6"] ) if self.shape == "3": # Variables cx = color["hsl_2"] cy = 1 - color["hsl_3"] inter = self.Triangle_Inter( cy, 1, 1 ) # Values self.tan_axis = color["hsl_1"] self.event_x = self.geometry.Limit_Range( cx * inter * self.widget_width, 0, self.widget_width ) self.event_y = self.geometry.Limit_Range( cy * self.widget_height, 0, self.widget_height ) if self.shape == "4": # Values self.tan_axis = color[f"{self.chan}_1"] self.event_x = color[f"{self.chan}_2"] * self.widget_width self.event_y = ( 1 - color[f"{self.chan}_3"] ) * self.widget_height if self.shape == "R": # Variables cx = color["hsl_2"] cy = 1 - color["hsl_3"] mini, maxi, delta = self.Diamond_Inter( cy, 1, 1 ) value = mini + cx * delta # Values self.tan_axis = color["hsl_1"] self.event_x = self.geometry.Limit_Range( value * self.widget_width, mini * self.widget_width, maxi * self.widget_width ) self.event_y = self.geometry.Limit_Range( cy * self.widget_height, 0, self.widget_height ) # Update self.update() def Update_Harmony( self, harmony_rule, harmony_index, harmony_list ): self.harmony_rule = harmony_rule self.harmony_index = harmony_index self.harmony_list = harmony_list self.update() def Update_Pin( self, pin_list ): self.pin_list = pin_list self.update() def Update_Analyse( self, analyse ): self.analyse = analyse self.update() # Panel Modifiers def Triangle_Inter ( self, y, px, py ): if y <= 0*py: inter = 0 elif ( y >= 0*py and y <= 0.5*py ): inter = self.geometry.Trig_2D_Points_Lines_Intersection( 0*px, y, 1*px, y, 0*px, 0*py, 1*px, 0.5*py )[0] elif y == 0.5*py: inter = 1 elif ( y >= 0.5*py and y <= 1*py ): inter = self.geometry.Trig_2D_Points_Lines_Intersection( 0*px, y, 1*px, y, 0*px, 1*py, 1*px, 0.5*py )[0] elif y >= 1*py: inter = 0 return inter def Diamond_Inter( self, y, px, py ): if y <= 0*py: mini = 0.5 * px maxi = 0.5 * py delta = 0 elif ( y >= 0*py and y <= 0.5*py ): mini = self.geometry.Trig_2D_Points_Lines_Intersection( 0*px, y, 1*px, y, 0.5*px, 0*py, 0*px, 0.5*py )[0] maxi = self.geometry.Trig_2D_Points_Lines_Intersection( 0*px, y, 1*px, y, 0.5*px, 0*py, 1*px, 0.5*py )[0] delta = abs( maxi - mini ) elif y == 0.5*py: mini = 0 maxi = py delta = py elif ( y >= 0.5*py and y <= 1*py ): mini = self.geometry.Trig_2D_Points_Lines_Intersection( 0*px, y, 1*px, y, 0.5*px, 1*py, 0*px, 0.5*py )[0] maxi = self.geometry.Trig_2D_Points_Lines_Intersection( 0*px, y, 1*px, y, 0.5*px, 1*py, 1*px, 0.5*py )[0] delta = abs( maxi - mini ) elif y >= 1*py: mini = 0.5 * px maxi = 0.5 * py delta = 0 return mini, maxi, delta # Mouse Interaction def mousePressEvent( self, event ): # Event ex = event.x() ey = event.y() # Variables self.press = True self.origin_x = ex self.origin_y = ey self.origin_tan_axis = self.tan_axis # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( ex, ey ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( ex, ey ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Snap( ex, ey ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): self.Cursor_Snap( ex, ey ) self.update() def mouseMoveEvent( self, event ): # Events ex = event.x() ey = event.y() # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( ex, ey ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( ex, ey ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Tangent( ex ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Snap( ex, ey ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): self.Cursor_Position( ex, ey ) self.update() def mouseDoubleClickEvent( self, event ): # Events ex = event.x() ey = event.y() # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( ex, ey ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( ex, ey ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Tangent( ex ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Snap( ex, ey ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): self.Cursor_Position( ex, ey ) self.update() def mouseReleaseEvent( self, event ): # Variables self.press = False self.zoom = False self.pressure = 0 self.origin_tan_axis = 0 self.pin_index = None # Updates self.SIGNAL_RELEASE.emit( 0 ) self.update() # Mouse Event def Cursor_Position( self, ex, ey ): # Variables ww = self.widget_width wh = self.widget_height # Input if self.shape == "3": # Cursor inter = self.Triangle_Inter( ey, ww, wh ) self.event_x = self.geometry.Limit_Range( ex, 0, inter ) self.event_y = self.geometry.Limit_Range( ey, 0, self.widget_height ) # Color if inter == 0:px = 0 else:px = self.event_x / inter py = ( wh - self.event_y ) / wh if self.shape == "4": # Cursor self.event_x = self.geometry.Limit_Range( ex, 0, ww ) self.event_y = self.geometry.Limit_Range( ey, 0, wh ) # Color px = self.event_x / ww py = ( wh - self.event_y ) / wh if self.shape == "R": # Cursor mini, maxi, delta = self.Diamond_Inter( ey, ww, wh ) self.event_x = self.geometry.Limit_Range( ex, mini, maxi ) self.event_y = self.geometry.Limit_Range( ey, 0, self.widget_height ) # Color if delta == 0:px = 0 else:px = ( self.event_x - mini ) / delta py = ( wh - self.event_y ) / wh # Variables if self.shape == "3": mode = "HSL" inter = self.Triangle_Inter( ey, self.widget_width, self.widget_height ) if inter == 0:c2 = 0 else:c2 = ex / inter if self.shape == "4": mode = self.wheel_space c2 = ex / self.widget_width if self.shape == "R": mode = "HSL" mini, maxi, delta = self.Diamond_Inter( ey, self.widget_width, self.widget_height ) if delta == 0:c2 = 0 else:c2 = ( ex - mini ) / delta c3 = ( self.widget_height - ey ) / self.widget_height # Limit c2 = self.geometry.Limit_Float( c2 ) c3 = self.geometry.Limit_Float( c3 ) # Signals dictionary = { "mode" : self.wheel_space, "c2" : c2, "c3" : c3, "pin_index" : self.pin_index } self.SIGNAL_VALUE.emit( dictionary ) if self.pin_index != None: self.SIGNAL_PIN_EDIT.emit( dictionary ) def Cursor_Tangent( self, ex ): # Hue delta_hue = ( ( ex - self.origin_x ) / self.widget_width ) angle = self.origin_tan_axis + delta_hue if self.wheel_space == "YUV": self.tan_axis = self.geometry.Limit_Float( angle ) else: self.tan_axis = self.geometry.Limit_Looper( angle, 1 ) # Update self.SIGNAL_TAN.emit( self.tan_axis ) def Cursor_Snap( self, ex, ey ): if self.pin_list != None: distance = [] for i in range( 0, len( self.pin_list ) ): if self.pin_list[i]["active"] == True: px = self.pin_list[i][f"{self.chan}_2"] * self.widget_width py = ( 1 - self.pin_list[i][f"{self.chan}_3"] ) * self.widget_height dist = self.geometry.Trig_2D_Points_Distance( ex, ey, px, py ) distance.append( ( dist, i ) ) if len( distance ) > 0: distance.sort() pin_index = distance[0][1] if pin_index < 20: self.pin_index = pin_index self.SIGNAL_PIN_INDEX.emit( self.pin_index ) # Tablet Interaction def tabletEvent( self, event ): self.pressure = event.pressure() self.update() # Paint def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Draw Gradient if len( self.qpixmap_list ) > 0: try: # Draw Masks if self.shape == "3": triangle = QPainterPath() triangle.moveTo( int( 0 ), int( 1 ) ) triangle.lineTo( int( self.widget_width ), int( self.widget_height * 0.5 ) ) triangle.lineTo( int( 0 ), int( self.widget_height ) ) painter.setClipPath( triangle ) if self.shape == "R": diamond = QPainterPath() diamond.moveTo( int( self.widget_width * 0.5 ), int( 0 ) ) diamond.lineTo( int( self.widget_width ), int( self.widget_height * 0.5 ) ) diamond.lineTo( int( self.widget_width * 0.5 ), int( self.widget_height ) ) diamond.lineTo( int( 0 ), int( self.widget_height * 0.5 ) ) painter.setClipPath( diamond ) # Draw Pixmaps painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QtCore.Qt.NoBrush ) index = int( self.tan_axis * self.tan_range ) qpixmap = self.qpixmap_list[index] if qpixmap.isNull() == False: render = qpixmap.scaled( self.widget_width, self.widget_height, Qt.IgnoreAspectRatio, Qt.FastTransformation ) else: render = qpixmap painter.drawPixmap( 0, 0, render ) except Exception as e: try:QtCore.qDebug( f"Pigment.O ERROR | { e }" ) except:pass # YUV Line if self.wheel_space == "YUV": # Variables line_size = 2 w1 = int( self.widget_width ) h1 = int( self.widget_height ) w2 = int( w1 * 0.5 ) h2 = int( h1 * 0.5 ) # Painter painter.setPen( QPen( self.color_1, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QtCore.Qt.NoBrush ) # Draw Cross painter.drawLine( w2, 0, w2, h1 ) painter.drawLine( 0, h2, w1, h2 ) # Draw Primaries painter.drawLine( int( self.CR[1] * w1 ), int( ( 1-self.CR[2] ) * h1 ), int( self.CM[1] * w1 ), int( ( 1-self.CM[2] ) * h1 ) ) painter.drawLine( int( self.CM[1] * w1 ), int( ( 1-self.CM[2] ) * h1 ), int( self.CB[1] * w1 ), int( ( 1-self.CB[2] ) * h1 ) ) painter.drawLine( int( self.CB[1] * w1 ), int( ( 1-self.CB[2] ) * h1 ), int( self.CC[1] * w1 ), int( ( 1-self.CC[2] ) * h1 ) ) painter.drawLine( int( self.CC[1] * w1 ), int( ( 1-self.CC[2] ) * h1 ), int( self.CG[1] * w1 ), int( ( 1-self.CG[2] ) * h1 ) ) painter.drawLine( int( self.CG[1] * w1 ), int( ( 1-self.CG[2] ) * h1 ), int( self.CY[1] * w1 ), int( ( 1-self.CY[2] ) * h1 ) ) painter.drawLine( int( self.CY[1] * w1 ), int( ( 1-self.CY[2] ) * h1 ), int( self.CR[1] * w1 ), int( ( 1-self.CR[2] ) * h1 ) ) # Analyse Colors if self.analyse != None: # Variables dot = 5 line_size = 2 length = len( self.analyse ) # Draw painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) for i in range( 0, length ): color = self.analyse[i] if int( self.tan_axis * 360 ) == int( color[f"{self.chan}_1"] * 360 ): px = color[f"{self.chan}_2"] * self.widget_width py = ( 1 - color[f"{self.chan}_3"] ) * self.widget_height painter.drawEllipse( int( px - dot ), int( py - dot ), int( dot * 2 ), int( dot * 2 ) ) # Pinned Colors if ( self.color != None and self.pin_list != None ): # Variables pin_size = 5 line_size = 2 length = len( self.pin_list ) # Draw painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) for i in range( 0, length ): if self.pin_list[i]["active"] == True: if self.shape == "3": cx = self.pin_list[i]["hsl_2"] cy = ( 1 - self.pin_list[i]["hsl_3"] ) inter = self.Triangle_Inter( cy, 1, 1 ) painter.drawEllipse( int( ( cx * inter * self.widget_width ) - pin_size ), int( ( cy * self.widget_height ) - pin_size ), int( pin_size * 2 ), int( pin_size * 2 ) ) if self.shape == "4": painter.drawEllipse( int( ( self.pin_list[i][f"{self.chan}_2"] * self.widget_width ) - pin_size ), int( ( ( 1-self.pin_list[i][f"{self.chan}_3"] ) * self.widget_height ) - pin_size ), int( pin_size * 2 ), int( pin_size * 2 ) ) if self.shape == "R": cx = self.pin_list[i]["hsl_2"] cy = ( 1 - self.pin_list[i]["hsl_3"] ) mini, maxi, delta = self.Diamond_Inter( cy, 1, 1 ) value = mini + cx * delta painter.drawEllipse( int( ( value * self.widget_width ) - pin_size ), int( ( cy * self.widget_height ) - pin_size ), int( pin_size * 2 ), int( pin_size * 2 ) ) # Harmony Colors if ( self.color != None and self.harmony_list != None ): # Variables line_size = 2 radius = 5 # Parsing points = [] for i in range( 0, len( self.harmony_list ) ): if self.shape == "3": cx = self.harmony_list[i]["hsl_2"] cy = ( 1 - self.harmony_list[i]["hsl_3"] ) inter = self.Triangle_Inter( cy, 1, 1 ) har_x = cx * inter * self.widget_width har_y = cy * self.widget_height if self.shape == "4": chan = self.wheel_space.lower() har_x = self.harmony_list[i][f"{chan}_2"] * self.widget_width har_y = ( 1 - self.harmony_list[i][f"{chan}_3"] ) * self.widget_height if self.shape == "R": cx = self.harmony_list[i]["hsl_2"] cy = ( 1 - self.harmony_list[i]["hsl_3"] ) mini, maxi, delta = self.Diamond_Inter( cy, 1, 1 ) value = mini + cx * delta har_x = value * self.widget_width har_y = cy * self.widget_height points.append( ( har_x, har_y ) ) length = len( points ) # Draw Line painter.setPen( QPen( self.color_1, line_size, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin ) ) painter.setBrush( QtCore.Qt.NoBrush ) for i in range( 1, length ): painter.drawLine( int( points[i-1][0] ), int( points[i-1][1] ), int( points[i][0] ), int( points[i][1] ) ) if self.harmony_rule in [ "Triadic", "Tetradic" ]: painter.drawLine( int( points[0][0] ), int( points[0][1] ), int( points[length-1][0] ), int( points[length-1][1] ) ) # Draw Points painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) for i in range( 0, length ): painter.drawEllipse( int( points[i][0] - radius ), int( points[i][1] - radius ), int( radius * 2 ), int( radius * 2 ) ) # Cursor size = 10 zoom_size = 100 margin_size = 10 if ( self.press == True and self.zoom == True ): size = zoom_size Cursor_Zoom( self, painter, size, margin_size ) elif( self.pressure > self.input_pressure ): size = zoom_size * self.pressure Cursor_Zoom( self, painter, size, margin_size ) else: Cursor_Normal( self, painter, size ) class Panel_HueCircle( QWidget ): SIGNAL_VALUE = QtCore.pyqtSignal( float ) SIGNAL_RELEASE = QtCore.pyqtSignal( int ) SIGNAL_SUBPANEL = QtCore.pyqtSignal( str ) # Init def __init__( self, parent ): super( Panel_HueCircle, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, render_height ) def Init_Variables( self ): # Widget self.press = False self.widget_width = 0 self.widget_height = 0 self.w2 = 0 self.h2 = 0 self.px = 0 self.py = 0 self.side = 0 # Variables self.wheel_mode = "DIGITAL" # "DIGITAL" "ANALOG" self.wheel_space = "HSV" # "HSV" "HSL" "HSY" "ARD" self.huecircle_shape = "None" # "None" "Triangle" "Square" "Diamond" # Color self.color = None self.hex_color = QColor( "#000000" ) self.theme_value = QColor( "#31363b" ) self.colors = [ [1, 0, 0], # Index = 0 > red [1, 0.5, 0], # Index = 1 > orange [1, 1, 0], # Index = 2 > yellow [0, 1, 0], # Index = 3 > green [0, 1, 1], # Index = 4 > cyan [0, 0, 1], # Index = 5 > blue [1, 0, 1], # Index = 6 > magenta ] # Hue self.digital = [ 0, 60, 120, 180, 240, 300, 360 ] self.analog = [ 0, 122, 165, 218, 275, 330, 360 ] # Harmony Colors self.harmony_rule = None # "Monochromatic" "Complementary" "Analogous" "Triadic" "Tetradic" self.harmony_index = None self.harmony_list = None self.harmony_span = 0 # Modules self.geometry = Geometry() self.convert = None def Init_Convert( self, convert ): self.convert = convert self.update() # Set def Set_WheelMode( self, wheel_mode ): self.wheel_mode = wheel_mode self.update() def Set_WheelSpace( self, wheel_space ): self.wheel_space = wheel_space self.update() def Set_Shape( self, huecircle_shape ): self.huecircle_shape = huecircle_shape self.update() def Set_Size( self, widget_width, widget_height, subpanel_shape ): # Widget self.widget_width = widget_width self.widget_height = widget_height self.w2 = widget_width * 0.5 self.h2 = widget_height * 0.5 # Frame if self.widget_width >= self.widget_height: self.side = self.widget_height self.px = self.w2 - ( self.side * 0.5 ) self.py = 0 else: self.side = self.widget_width self.px = 0 self.py = self.h2 - ( self.side * 0.5 ) # Variables m1 = 2 m2 = 2 * m1 m3 = 0.13 m4 = 1 - ( 2 * m3 ) # Regions circle_outter = QRegion( int( self.px - m1 ), int( self.py - m1 ), int( self.side + m2 ), int( self.side + m2 ), QRegion.Ellipse ) circle_inner = QRegion( int( self.px + self.side * m3 ), int( self.py + self.side * m3 ), int( self.side * m4 ), int( self.side * m4 ), QRegion.Ellipse ) if subpanel_shape == "None": region = circle_inner if subpanel_shape == "Triangle": x = 0.28 y = 0.13 k = 0.07 t = 1 - k - x polygon = QPolygon( [ QPoint( int( self.px + x * self.side ), int( self.py + y * self.side ) ), QPoint( int( self.px + self.side - k * self.side ), int( self.h2 ) ), QPoint( int( self.px + x * self.side ), int( self.py + self.side - y * self.side ) ), ] ) triangle = QRegion( polygon, Qt.OddEvenFill ) region = triangle.united( circle_inner ) if subpanel_shape == "Square": k = 0.2 square = QRegion( int( self.px + self.side * k ), int( self.py + self.side * k ), int( self.side - ( 2 * k * self.side ) ), int( self.side - ( 2 * k * self.side ) ), QRegion.Rectangle ) region = square.united( circle_inner ) if subpanel_shape == "Diamond": k = 0.07 kk = ( 1 - k * 2 ) / 2 polygon = QPolygon( [ QPoint( int( self.w2 ), int( self.h2 - self.side * kk ) ), QPoint( int( self.w2 + self.side * kk ), int( self.h2 ) ), QPoint( int( self.w2 ), int( self.h2 + self.side * kk ) ), QPoint( int( self.w2 - self.side * kk ), int( self.h2 ) ), ] ) diamond = QRegion( polygon, Qt.OddEvenFill ) region = diamond.united( circle_inner ) # Mask mask_region = circle_outter.subtracted( region ) self.setMask( mask_region ) # Update self.update() def Set_Theme( self, theme_value ): self.theme_value = QColor( theme_value ) self.update() # Update def Update_Panel( self, color ): self.color = color self.update() def Update_Colors( self, colors ): self.colors = colors self.update() def Update_Harmony( self, harmony_rule, harmony_index, harmony_list ): self.harmony_rule = harmony_rule self.harmony_index = harmony_index self.harmony_list = harmony_list self.update() def Update_Span( self, harmony_span ): self.harmony_span = harmony_span self.update() # Mouse Interaction def mousePressEvent( self, event ): # Variables self.press = True # LMB Neutral if event.buttons() == QtCore.Qt.LeftButton: self.Cursor_Angle( event ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): self.press = False self.Context_Menu( event ) # Update self.update() def mouseMoveEvent( self, event ): # LMB Neutral if event.buttons() == QtCore.Qt.LeftButton: self.Cursor_Angle( event ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # Update self.update() def mouseDoubleClickEvent( self, event ): # LMB Neutral if event.buttons() == QtCore.Qt.LeftButton: self.Cursor_Angle( event ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # Update self.update() def mouseReleaseEvent( self, event ): # Variables self.press = False # Update self.SIGNAL_RELEASE.emit( 0 ) self.update() def Cursor_Angle( self, event ): # Variables ex = event.x() ey = self.widget_height - event.y() # Angle Measure if self.wheel_mode == "DIGITAL": angle = self.geometry.Trig_2D_Points_Lines_Angle( ex, ey, self.w2, self.h2, 0, self.h2 ) if event.modifiers() == QtCore.Qt.ShiftModifier: angle = int( angle ) if event.modifiers() == QtCore.Qt.ControlModifier: angle = self.geometry.Limit_Angle( angle, 2.5 ) value = angle / 360 if self.wheel_mode == "ANALOG": px = self.w2 - ( self.side * 0.5 ) py = self.h2 - ( self.side * 0.314 ) # Inverted for the formula angle = self.geometry.Trig_2D_Points_Lines_Angle( ex, ey, self.w2, self.h2, px, py ) if event.modifiers() == QtCore.Qt.ShiftModifier: angle = int( angle ) if event.modifiers() == QtCore.Qt.ControlModifier: angle = self.geometry.Limit_Angle( angle, 2.5 ) value = self.convert.huea_to_hued( self.geometry.Limit_Looper( angle, 360 ) / 360 ) # Emit Values self.SIGNAL_VALUE.emit( value ) # Context def Context_Menu( self, event ): if self.press == False: # Menu cmenu = QMenu( self ) # Action cmenu_sn = cmenu.addAction( "None" ) cmenu_st = cmenu.addAction( "Triangle" ) cmenu_ss = cmenu.addAction( "Square" ) cmenu_sd = cmenu.addAction( "Diamond" ) cmenu_sn.setCheckable( True ) cmenu_st.setCheckable( True ) cmenu_ss.setCheckable( True ) cmenu_sd.setCheckable( True ) cmenu_sn.setChecked( self.huecircle_shape == "None" ) cmenu_st.setChecked( self.huecircle_shape == "Triangle" ) cmenu_ss.setChecked( self.huecircle_shape == "Square" ) cmenu_sd.setChecked( self.huecircle_shape == "Diamond" ) action = cmenu.exec_( self.mapToGlobal( event.pos() ) ) # Triggers if action == cmenu_sn: self.huecircle_shape = "None" self.SIGNAL_SUBPANEL.emit( self.huecircle_shape ) if action == cmenu_st: self.huecircle_shape = "Triangle" self.SIGNAL_SUBPANEL.emit( self.huecircle_shape ) if action == cmenu_ss: self.huecircle_shape = "Square" self.SIGNAL_SUBPANEL.emit( self.huecircle_shape ) if action == cmenu_sd: self.huecircle_shape = "Diamond" self.SIGNAL_SUBPANEL.emit( self.huecircle_shape ) # Paint def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Variables line_width = 4 circle_0, circle_1, circle_2, circle_3 = Circles( self, painter ) # Hue if self.wheel_mode == "DIGITAL": index = "hue_d" if self.wheel_mode == "ANALOG": index = "hue_a" # Circle Points radius = 0.5 circle_points = [] if self.harmony_rule != None: for i in range( 0, len( self.harmony_list ) ): if self.wheel_mode == "DIGITAL": px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, self.harmony_list[i][index] * 360 ) if self.wheel_mode == "ANALOG": px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, ( self.harmony_list[i][index] * 360 ) - hue_a ) circle_points.append( [ px, py ] ) else: if self.wheel_mode == "DIGITAL": px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, self.color[index] * 360 ) if self.wheel_mode == "ANALOG": px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, ( self.color[index] * 360 ) - hue_a ) circle_points.append( [ px, py ] ) length = len( circle_points ) # Divisions div = [] margin = 0.01 if self.wheel_mode == "DIGITAL": for i in range( 0, len( self.digital ) ): px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius - margin, self.digital[i] ) div.append( [ px, py ] ) if self.wheel_mode == "ANALOG": for i in range( 0, len( self.analog ) ): px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius - margin, self.analog[i] - hue_a ) div.append( [ px, py ] ) # Dark Border painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.theme_value ) ) circle_02 = circle_0.subtracted( circle_2 ) painter.drawPath( circle_02 ) # Dark Border painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.theme_value ) ) circle_02 = circle_0.subtracted( circle_2 ) painter.drawPath( circle_02 ) # Dark Lines painter.setPen( QPen( self.theme_value, line_width, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QtCore.Qt.NoBrush ) s1 = self.side s2 = self.side * 0.5 if self.wheel_mode == "DIGITAL": for i in range( 0, len( self.digital ) ): painter.drawLine( int( div[i][0] ), int( div[i][1] ), int( self.w2 ), int( self.h2 ) ) if self.wheel_mode == "ANALOG": for i in range( 0, len( self.analog ) ): painter.drawLine( int( div[i][0] ), int( div[i][1] ), int( self.w2 ), int( self.h2 ) ) # Line Gray painter.setPen( QPen( self.color_1, line_width, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QtCore.Qt.NoBrush ) circle_13 = circle_1.subtracted( circle_3 ) if length > 0: line_gray = QPainterPath() for i in range( 0, length ): # Variables px = circle_points[i][0] py = circle_points[i][1] # Draw line_gray.moveTo( int( self.w2 ), int( self.h2 ) ) line_gray.lineTo( int( px ), int( py ) ) painter.setClipPath( circle_13 ) painter.drawPath( line_gray ) # Hue Gradient painter.setPen( QtCore.Qt.NoPen ) d = 255 if self.wheel_mode == "DIGITAL": hue = QConicalGradient( QPoint( int( self.w2 ), int( self.h2 ) ), 180 ) hue.setColorAt( 0.000, QColor( int( self.colors[0][0] * d ), int( self.colors[0][1] * d ), int( self.colors[0][2] * d ) ) ) # RED hue.setColorAt( 0.166, QColor( int( self.colors[6][0] * d ), int( self.colors[6][1] * d ), int( self.colors[6][2] * d ) ) ) # MAGENTA hue.setColorAt( 0.333, QColor( int( self.colors[5][0] * d ), int( self.colors[5][1] * d ), int( self.colors[5][2] * d ) ) ) # BLUE hue.setColorAt( 0.500, QColor( int( self.colors[4][0] * d ), int( self.colors[4][1] * d ), int( self.colors[4][2] * d ) ) ) # CYAN hue.setColorAt( 0.666, QColor( int( self.colors[3][0] * d ), int( self.colors[3][1] * d ), int( self.colors[3][2] * d ) ) ) # GREEN hue.setColorAt( 0.833, QColor( int( self.colors[2][0] * d ), int( self.colors[2][1] * d ), int( self.colors[2][2] * d ) ) ) # YELLOW hue.setColorAt( 1.000, QColor( int( self.colors[0][0] * d ), int( self.colors[0][1] * d ), int( self.colors[0][2] * d ) ) ) # RED if self.wheel_mode == "ANALOG": hue = QConicalGradient( QPoint( int( self.w2 ), int( self.h2 ) ), 210 ) hue.setColorAt( 0.000, QColor( int( self.colors[0][0] * d ), int( self.colors[0][1] * d ), int( self.colors[0][2] * d ) ) ) # RED hue.setColorAt( 0.083, QColor( int( self.colors[6][0] * d ), int( self.colors[6][1] * d ), int( self.colors[6][2] * d ) ) ) # MAGENTA hue.setColorAt( 0.236, QColor( int( self.colors[5][0] * d ), int( self.colors[5][1] * d ), int( self.colors[5][2] * d ) ) ) # BLUE hue.setColorAt( 0.394, QColor( int( self.colors[4][0] * d ), int( self.colors[4][1] * d ), int( self.colors[4][2] * d ) ) ) # CYAN hue.setColorAt( 0.541, QColor( int( self.colors[3][0] * d ), int( self.colors[3][1] * d ), int( self.colors[3][2] * d ) ) ) # GREEN hue.setColorAt( 0.661, QColor( int( self.colors[2][0] * d ), int( self.colors[2][1] * d ), int( self.colors[2][2] * d ) ) ) # YELLOW hue.setColorAt( 0.833, QColor( int( self.colors[1][0] * d ), int( self.colors[1][1] * d ), int( self.colors[1][2] * d ) ) ) # ORANGE hue.setColorAt( 1.000, QColor( int( self.colors[0][0] * d ), int( self.colors[0][1] * d ), int( self.colors[0][2] * d ) ) ) # RED painter.setBrush( QBrush( hue ) ) circle_01 = circle_0.subtracted( circle_1 ) painter.setClipPath( circle_01 ) painter.drawRect( int( self.px ), int( self.py ), int( self.side ), int( self.side ) ) # Line Dark over Hue painter.setPen( QPen( self.theme_value, line_width, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QtCore.Qt.NoBrush ) circle_01 = circle_0.subtracted( circle_1 ) painter.setClipPath( circle_01 ) for i in range( 0, length ): painter.drawLine( int( circle_points[i][0] ), int( circle_points[i][1] ), int( self.w2 ), int( self.h2 ) ) class Panel_Gamut( QWidget ): SIGNAL_VALUE = QtCore.pyqtSignal( dict ) SIGNAL_TAN = QtCore.pyqtSignal( float ) SIGNAL_RELEASE = QtCore.pyqtSignal( int ) SIGNAL_MASK = QtCore.pyqtSignal( str ) SIGNAL_PROFILE = QtCore.pyqtSignal( list ) SIGNAL_PIN_INDEX = QtCore.pyqtSignal( int ) SIGNAL_PIN_EDIT = QtCore.pyqtSignal( dict ) # Init def __init__( self, parent ): super( Panel_Gamut, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, render_height ) def Init_Variables( self ): # Widget self.press = False self.widget_width = 0 self.widget_height = 0 self.w2 = 0 self.h2 = 0 self.event_x = 0 self.event_y = 0 self.origin_x = 0 self.origin_y = 0 self.origin_tan_axis = 0 self.origin_angle = 0 self.origin_dist = 0 self.previous_dist = 0 self.px = 0 self.py = 0 self.side = 0 self.pressure = 0 self.input_pressure = 0.5 self.region = None # "RING" "DISK" # Disk self.disk_var = 0.068 self.disk_radius = 0.5 * ( 1 - ( 2 * self.disk_var ) ) # 0.432 self.disk_x = 0 self.disk_y = 0 self.disk_side = 0 # Display self.zoom = False self.tan_axis = 0 # 0-255 because of background index self.tan_range = 255 # Format self.directory = None # Path self.shape = None # "CD" "CA" self.d_cm = None # "A" "RGB" "CMYK" "YUV" "XYZ" "LAB" self.chan = None # "hsv" "hsl" "hsy" "ard" # Wheel self.wheel_mode = "DIGITAL" # "DIGITAL" "ANALOG" self.wheel_space = "HSV" # "HSV" "HSL" "HSY" "ARD" # Color self.color = None self.hex_color = QColor( "#000000" ) self.theme_value = QColor( "#31363b" ) # Harmony Colors self.harmony_index = None self.harmony_list = None # Pinned Colors self.pin_index = None self.pin_list = None # Analyse Colors self.analyse = None # Hue self.digital = [ 0, 60, 120, 180, 240, 300, 360 ] self.analog = [ 0, 122, 165, 218, 275, 330, 360 ] # Harmony Colors self.harmony_rule = None # "Monochromatic" "Complementary" "Analogous" "Triadic" "Tetradic" self.harmony_index = None self.harmony_list = None self.harmony_span = 0 # Gamut self.gamut_mask = "None" # "None" "Triangle" "Square" "Circle" "2 Circle" "3 Pie" "Reset" self.gamut_index = None self.gamut_rotation = None # Gamut Neutral self.neutral_1tri = [ ( 0.5, 0.5 ), ( 0.5, 0.1 ), ( 0.84641, 0.7 ), ( 0.15359, 0.7 ) ] # Centroid + Polygon self.neutral_1squ = [ ( 0.5, 0.5 ), ( 0.5, 0.1 ), ( 0.9, 0.5 ), ( 0.5, 0.9 ), ( 0.1, 0.5 ) ] # Centroid + Polygon self.neutral_1cir = [ ( 0.5, 0.5 ), ( 0.5, 0.1 ), ( 0.9, 0.5 ), ( 0.5, 0.9 ), ( 0.1, 0.5 ) ] # Centroid + Polygon self.neutral_2cir = [ ( 0.5, 0.275 ), ( 0.5, 0.1 ), ( 0.675, 0.275 ), ( 0.5, 0.45 ), ( 0.325, 0.275 ), # Circle 1 ( Centroid + Polygon ) ( 0.5, 0.725 ), ( 0.5, 0.55 ), ( 0.675, 0.725 ), ( 0.5, 0.9 ), ( 0.325, 0.725 ) ] # Circle 2 ( Centroid + Polygon ) self.neutral_3pie = [ ( 0.5, 0.5 ), ( 0.5, 0.15359 ), ( 0.8, 0.32679 ), ( 0.8, 0.67321 ), ( 0.5, 0.84641 ), ( 0.2, 0.67321 ), ( 0.2, 0.32679 ) ] # Centroid + Polygon # Gamut Lists self.gamut_1tri = self.neutral_1tri.copy() self.gamut_1squ = self.neutral_1squ.copy() self.gamut_1cir = self.neutral_1cir.copy() self.gamut_2cir = self.neutral_2cir.copy() self.gamut_3pie = self.neutral_3pie.copy() # Modules self.geometry = Geometry() self.convert = None def Init_Convert( self, convert ): self.convert = convert self.update() # Set def Set_ColorModel( self, d_cm ): self.d_cm = d_cm self.Set_ColorSpace_inDocument( self.directory, self.d_cm, self.wheel_space, self.shape ) self.update() def Set_WheelMode( self, wheel_mode ): self.wheel_mode = wheel_mode if self.wheel_mode == "DIGITAL": self.shape = "D" if self.wheel_mode == "ANALOG": self.shape = "A" self.Set_ColorSpace_inDocument( self.directory, self.d_cm, self.wheel_space, self.shape ) self.update() def Set_WheelSpace( self, wheel_space ): self.wheel_space = wheel_space self.Set_ColorSpace_inDocument( self.directory, self.d_cm, self.wheel_space, self.shape ) self.update() def Set_ColorSpace_inDocument( self, directory, d_cm, wheel_space, shape ): # Variables self.directory = directory self.d_cm = d_cm self.wheel_space = wheel_space self.shape = shape self.chan = self.wheel_space.lower() # Cursor if self.color != None: self.event_x, self.event_y = self.Update_Cursor( self.color ) # Read Zip File location = os.path.join( self.directory, panel ) location = os.path.join( location, f"{ self.d_cm }_{ self.wheel_space }_{ self.shape }.zip" ) self.qpixmap_list = Read_Zip( self, location, self.tan_range, self.wheel_space, self.shape ) # Update self.update() def Set_Size( self, widget_width, widget_height ): # Widget self.widget_width = widget_width self.widget_height = widget_height self.w2 = widget_width * 0.5 self.h2 = widget_height * 0.5 # Frame if self.widget_width >= self.widget_height: self.side = self.widget_height self.px = self.w2 - ( self.side * 0.5 ) self.py = 0 else: self.side = self.widget_width self.px = 0 self.py = self.h2 - ( self.side * 0.5 ) # Disk Coordinates self.disk_x = self.px + self.disk_var * self.side self.disk_y = self.py + self.disk_var * self.side self.disk_side = ( 1 - 2 * self.disk_var ) * self.side # Update self.update() def Set_Theme( self, theme_value ): self.theme_value = QColor( theme_value ) self.update() # Update def Update_Panel( self, color ): # Variables self.color = color self.hex_color = QColor( color["hex6"] ) # Location of Cursor self.event_x, self.event_y = self.Update_Cursor( self.color ) self.previous_dist = self.geometry.Trig_2D_Points_Distance( self.event_x, self.event_y, self.w2, self.h2 ) # Update self.update() def Update_Cursor( self, color ): # Angle if self.wheel_mode == "DIGITAL": angle = color["hue_d"] * 360 if self.wheel_mode == "ANALOG": angle = color["hue_a"] * 360 - hue_a # Radius radius = color[f"{ self.chan }_2"] self.tan_axis = color[f"{ self.chan }_3"] # Location ex, ey = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.disk_side * 0.5, radius, angle ) return ex, ey def Update_Harmony( self, harmony_rule, harmony_index, harmony_list ): self.harmony_rule = harmony_rule self.harmony_index = harmony_index self.harmony_list = harmony_list self.update() def Update_Pin( self, pin_list ): self.pin_list = pin_list self.update() def Update_Analyse( self, analyse ): self.analyse = analyse self.update() def Update_Mask( self, gamut_mask ): self.gamut_mask = gamut_mask self.update() def Update_Profile( self, gamut_profile ): self.gamut_1tri = gamut_profile[0] self.gamut_1squ = gamut_profile[1] self.gamut_1cir = gamut_profile[2] self.gamut_2cir = gamut_profile[3] self.gamut_3pie = gamut_profile[4] self.update() # Emit def Signal_Profile( self ): profile = [ self.gamut_1tri, self.gamut_1squ, self.gamut_1cir, self.gamut_2cir, self.gamut_3pie ] self.SIGNAL_PROFILE.emit( profile ) # Mouse Interaction def mousePressEvent( self, event ): # Event ex = event.x() ey = event.y() # Variables self.press = True self.origin_x = ex self.origin_y = ey self.origin_tan_axis = self.tan_axis self.origin_angle = self.geometry.Trig_2D_Points_Lines_Angle( 0, self.h2, self.w2, self.h2, ex, ey ) self.origin_dist = self.geometry.Trig_2D_Points_Distance( ex, ey, self.w2, self.h2 ) self.region = self.geometry.Trig_2D_Points_Distance( ex, ey, self.w2, self.h2 ) <= ( self.side * self.disk_radius ) # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( ex, ey, False ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( ex, ey, False ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Tangent( ex, ey ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Snap( ex, ey ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): self.press = False self.Context_Menu( event ) # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): self.Cursor_Snap( ex, ey ) # Rotate if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == ( QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier ) ): self.gamut_rotation = self.Gamut_Rotation() # Update self.update() def mouseMoveEvent( self, event ): # Event ex = event.x() ey = event.y() # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( ex, ey, False ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( ex, ey, False ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Tangent( ex, ey ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Snap( ex, ey ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): # self.Cursor_Move( ex, ey ) self.Cursor_Position( ex, ey, False ) # Rotate if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == ( QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier ) ): self.Cursor_Rotation( ex, ey ) # Update self.update() def mouseDoubleClickEvent( self, event ): # Event ex = event.x() ey = event.y() # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( ex, ey, False ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( ex, ey, False ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Tangent( ex, ey ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Snap( ex, ey ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): # self.Cursor_Move( ex, ey ) self.Cursor_Position( ex, ey, False ) # Rotate if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == ( QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier ) ): self.Cursor_Rotation( ex, ey ) # Update self.update() def mouseReleaseEvent( self, event ): # Previous self.previous_dist = self.geometry.Trig_2D_Points_Distance( self.event_x, self.event_y, self.w2, self.h2 ) # Variables self.press = False self.zoom = False self.pressure = 0 self.region = None self.origin_x = 0 self.origin_y = 0 self.origin_angle = 0 self.origin_dist = 0 # Gamut self.gamut_index = None self.gamut_rotation = None # Pin self.pin_index = None # Updates self.SIGNAL_RELEASE.emit( 0 ) self.update() def Cursor_Position( self, ex, ey, clip ): # Hue angle = self.geometry.Trig_2D_Points_Lines_Angle( 0, self.h2, self.w2, self.h2, ex, ey ) if clip == True: angle = self.geometry.Limit_Angle( angle, 2.5 ) if self.wheel_mode == "DIGITAL": hue = angle / 360 if self.wheel_mode == "ANALOG": angle = self.geometry.Limit_Looper( angle + hue_a, 360 ) hue = self.convert.huea_to_hued( angle / 360 ) # Saturation distance = 0 radius = self.side * self.disk_radius if self.region == False: # Ring distance = self.previous_dist self.event_x, self.event_y = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.disk_side, distance, angle ) if self.region == True: # Disk distance = self.geometry.Trig_2D_Points_Distance( ex, ey, self.w2, self.h2 ) if distance >= radius: distance = radius self.event_x, self.event_y = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.disk_side, 0.5, angle ) else: self.event_x = ex self.event_y = ey # Variables c1 = hue c2 = distance / radius # Edit Dot if self.gamut_index != None: # Move mx = ( self.event_x - self.disk_x ) / self.disk_side my = ( self.event_y - self.disk_y ) / self.disk_side # Apply lista = self.Gamut_List( self.gamut_mask ) if ( self.gamut_mask != "2 Circle" and self.gamut_index != 0 ): lista[self.gamut_index] = ( mx, my ) if ( self.gamut_mask == "2 Circle" and self.gamut_index != 0 and self.gamut_index != 5 ): lista[self.gamut_index] = ( mx, my ) elif self.pin_index != None: pin = { "pin_index" : self.pin_index, "c1" : c1, "c2" : c2 } self.SIGNAL_PIN_EDIT.emit( pin ) # Emition value = { "c1" : c1, "c2" : c2 } self.SIGNAL_VALUE.emit( value ) # Update Centroids self.Gamut_Centroid( self.gamut_mask ) def Cursor_Tangent( self, ex, ey ): if self.region == False: # Ring self.Cursor_Position( ex, ey, True ) if self.region == True: # Disk # Hue delta_hue = ( ( ex - self.origin_x ) / self.widget_width ) self.tan_axis = self.geometry.Limit_Float( self.origin_tan_axis + delta_hue ) # Update self.SIGNAL_TAN.emit( self.tan_axis ) def Cursor_Snap( self, ex, ey ): # Gamut Profile lista = self.Gamut_List( self.gamut_mask ) points = self.Gamut_Points( ex, ey, lista ) # Pins points = self.Pin_Points( ex, ey, points ) # Position if len( points ) > 0: points.sort( key = lambda row: row[0] ) check_gamut = points[0][3] # gamut_index check_pin = points[0][4] # pin_index if check_gamut != None: self.gamut_index = check_gamut self.Cursor_Position( points[0][1], points[0][2], False ) elif check_pin != None: self.pin_index = check_pin self.SIGNAL_PIN_INDEX.emit( check_pin ) def Cursor_Rotation( self, ex, ey ): lista = self.Gamut_List( self.gamut_mask ) ang_new = self.geometry.Trig_2D_Points_Lines_Angle( 0, self.h2, self.w2, self.h2, ex, ey ) delta = self.geometry.Limit_Looper( ang_new - self.origin_angle, 360 ) for i in range( 0, len( lista ) ): # Read dis = self.gamut_rotation[i][0] angle = self.gamut_rotation[i][1] if dis != 0: radius = dis / self.disk_side angle = self.geometry.Limit_Looper( angle + delta, 360 ) dx, dy = self.geometry.Trig_2D_Angle_Circle( 0.5, 0.5, 1, radius, angle ) lista[i] = ( dx, dy ) # Update Centroids self.Gamut_Centroid( self.gamut_mask ) # Gamut def Gamut_List( self, mode ): if mode == "None": lista = [] if mode == "Triangle": lista = self.gamut_1tri if mode == "Square": lista = self.gamut_1squ if mode == "Circle": lista = self.gamut_1cir if mode == "2 Circle": lista = self.gamut_2cir if mode == "3 Pie": lista = self.gamut_3pie return lista def Gamut_Rotation( self ): lista = self.Gamut_List( self.gamut_mask ) rotation = [] for i in range( 0, len( lista ) ): px = self.disk_x + lista[i][0] * self.disk_side py = self.disk_y + lista[i][1] * self.disk_side dist = self.geometry.Trig_2D_Points_Distance( px, py, self.w2, self.h2 ) angle = self.geometry.Trig_2D_Points_Lines_Angle( 0, self.h2, self.w2, self.h2, px, py ) rotation.append( ( dist, angle ) ) return rotation def Gamut_Centroid( self, mode ): # Update Centroid if mode == "Triangle": p = self.gamut_1tri c = self.geometry.Trig_2D_Centroid_Triangle( p[1][0], p[1][1], p[2][0], p[2][1], p[3][0], p[3][1] ) self.gamut_1tri[0] = ( c[0], c[1] ) if mode == "Square": p = self.gamut_1squ c = self.geometry.Trig_2D_Centroid_Square( p[1][0], p[1][1], p[2][0], p[2][1], p[3][0], p[3][1], p[4][0], p[4][1] ) self.gamut_1squ[0] = ( c[0], c[1] ) if mode == "Circle": p = self.gamut_1cir c = self.geometry.Trig_2D_Centroid_Square( p[1][0], p[1][1], p[2][0], p[2][1], p[3][0], p[3][1], p[4][0], p[4][1] ) self.gamut_1cir[0] = ( c[0], c[1] ) if mode == "2 Circle": # Circle 1 p = self.gamut_2cir[0:5] c = self.geometry.Trig_2D_Centroid_Square( p[1][0], p[1][1], p[2][0], p[2][1], p[3][0], p[3][1], p[4][0], p[4][1] ) self.gamut_2cir[0] = ( c[0], c[1] ) # Circle 2 p = self.gamut_2cir[5:10] c = self.geometry.Trig_2D_Centroid_Square( p[1][0], p[1][1], p[2][0], p[2][1], p[3][0], p[3][1], p[4][0], p[4][1] ) self.gamut_2cir[5] = ( c[0], c[1] ) # Save Profile self.Signal_Profile() # Points def Gamut_Points( self, ex, ey, lista ): points = [] for i in range( 0, len( lista ) ): px = self.disk_x + lista[i][0] * self.disk_side py = self.disk_y + lista[i][1] * self.disk_side distance = self.geometry.Trig_2D_Points_Distance( ex, ey, px, py ) points.append( ( distance, px, py, i, None ) ) # dist px py gamut_index, pin_index return points def Pin_Points( self, ex, ey, points ): if self.pin_list != None: for i in range( 0, len( self.pin_list ) - 1 ): if self.pin_list[i]["active"] == True: if self.wheel_mode == "DIGITAL": angle = self.pin_list[i]["hue_d"] * 360 if self.wheel_mode == "ANALOG": angle = self.pin_list[i]["hue_a"] * 360 - hue_a radius = self.pin_list[i][f"{self.chan}_2"] px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.disk_side * 0.5, radius, angle ) distance = self.geometry.Trig_2D_Points_Distance( ex, ey, px, py ) points.append( ( distance, px, py, None, i ) ) # dist px py gamut_index, pin_index return points # Context def Context_Menu( self, event ): if self.press == False: # Menu cmenu = QMenu( self ) # Action cmenu_gn = cmenu.addAction( "None" ) cmenu_gt = cmenu.addAction( "Triangle" ) cmenu_gs = cmenu.addAction( "Square" ) cmenu_g1c = cmenu.addAction( "Circle" ) cmenu_g2c = cmenu.addAction( "2 Circle" ) cmenu_g3p = cmenu.addAction( "3 Pie" ) cmenu_gr = cmenu.addAction( "Reset" ) cmenu_gn.setCheckable( True ) cmenu_gt.setCheckable( True ) cmenu_gs.setCheckable( True ) cmenu_g1c.setCheckable( True ) cmenu_g2c.setCheckable( True ) cmenu_g3p.setCheckable( True ) cmenu_gn.setChecked( self.gamut_mask == "None" ) cmenu_gt.setChecked( self.gamut_mask == "Triangle" ) cmenu_gs.setChecked( self.gamut_mask == "Square" ) cmenu_g1c.setChecked( self.gamut_mask == "Circle" ) cmenu_g2c.setChecked( self.gamut_mask == "2 Circle" ) cmenu_g3p.setChecked( self.gamut_mask == "3 Pie" ) # Mapping action = cmenu.exec_( self.mapToGlobal( event.pos() ) ) # Triggers if action == cmenu_gn: self.gamut_mask = "None" self.SIGNAL_MASK.emit( self.gamut_mask ) if action == cmenu_gt: self.gamut_mask = "Triangle" self.SIGNAL_MASK.emit( self.gamut_mask ) if action == cmenu_gs: self.gamut_mask = "Square" self.SIGNAL_MASK.emit( self.gamut_mask ) if action == cmenu_g1c: self.gamut_mask = "Circle" self.SIGNAL_MASK.emit( self.gamut_mask ) if action == cmenu_g2c: self.gamut_mask = "2 Circle" self.SIGNAL_MASK.emit( self.gamut_mask ) if action == cmenu_g3p: self.gamut_mask = "3 Pie" self.SIGNAL_MASK.emit( self.gamut_mask ) if action == cmenu_gr: if self.gamut_mask == "Triangle": self.gamut_1tri = self.neutral_1tri.copy() if self.gamut_mask == "Square": self.gamut_1squ = self.neutral_1squ.copy() if self.gamut_mask == "Circle": self.gamut_1cir = self.neutral_1cir.copy() if self.gamut_mask == "2 Circle": self.gamut_2cir = self.neutral_2cir.copy() if self.gamut_mask == "3 Pie": self.gamut_3pie = self.neutral_3pie.copy() self.update() self.Signal_Profile() # Tablet Interaction def tabletEvent( self, event ): self.pressure = event.pressure() self.update() # Paint def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Variables line_width = 5 circle_0, circle_1, circle_2, circle_3 = Circles( self, painter ) # Hue if self.wheel_mode == "DIGITAL": hue_index = "hue_d" if self.wheel_mode == "ANALOG": hue_index = "hue_a" # Circle Points radius = 0.5 circle_points = [] if self.harmony_rule != None: for i in range( 0, len( self.harmony_list ) ): if self.wheel_mode == "DIGITAL": px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, self.harmony_list[i][hue_index] * 360 ) if self.wheel_mode == "ANALOG": px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, ( self.harmony_list[i][hue_index] * 360 ) - hue_a ) circle_points.append( [ px, py ] ) else: if self.wheel_mode == "DIGITAL": px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, self.color[hue_index] * 360 ) if self.wheel_mode == "ANALOG": px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, ( self.color[hue_index] * 360 ) - hue_a ) circle_points.append( [ px, py ] ) length = len( circle_points ) # Divisions div = [] if self.wheel_mode == "DIGITAL": for i in range( 0, len( self.digital ) ): px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, self.digital[i] ) div.append( [ px, py ] ) if self.wheel_mode == "ANALOG": for i in range( 0, len( self.analog ) ): px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, self.analog[i] - hue_a ) div.append( [ px, py ] ) # Outter Mask outline = QPainterPath() outline.addEllipse( 0, 0, self.widget_width, self.widget_height ) painter.setClipPath( circle_0 ) # Dark Border painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.theme_value ) ) circle_02 = circle_0.subtracted( circle_2 ) painter.drawPath( circle_02 ) # Dark Lines painter.setPen( QPen( self.theme_value, line_width, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QtCore.Qt.NoBrush ) if self.wheel_mode == "DIGITAL": for i in range( 0, len( self.digital ) ): painter.drawLine( int( div[i][0] ), int( div[i][1] ), int( self.w2 ), int( self.h2 ) ) if self.wheel_mode == "ANALOG": for i in range( 0, len( self.analog ) ): painter.drawLine( int( div[i][0] ), int( div[i][1] ), int( self.w2 ), int( self.h2 ) ) # Line Gray painter.setPen( QPen( self.color_1, line_width, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QtCore.Qt.NoBrush ) circle_02 = circle_0.subtracted( circle_2 ) if length > 0: line_gray = QPainterPath() for i in range( 0, length ): # Variables px = circle_points[i][0] py = circle_points[i][1] # Draw line_gray.moveTo( self.w2, self.h2 ) line_gray.lineTo( px, py ) painter.setClipPath( circle_02 ) painter.drawPath( line_gray ) # Reset Mask square = QPainterPath() square.moveTo( int( 0 ), int( 0 ) ) square.lineTo( int( self.widget_width ), int( 0 ) ) square.lineTo( int( self.widget_width ), int( self.widget_height ) ) square.lineTo( int( 0 ), int( self.widget_height ) ) painter.setClipPath( square ) # Draw Gradient if len( self.qpixmap_list ) > 0: try: # Pixmaps painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QtCore.Qt.NoBrush ) tan_index = int( self.tan_axis * self.tan_range ) qpixmap = self.qpixmap_list[tan_index] # Brush qbrush = QBrush( qpixmap ) if qpixmap.isNull() == False: qtransform = QTransform() qtransform.translate( int( self.disk_x ), int( self.disk_y ) ) qtransform.scale( int( self.disk_side ) / qpixmap.width(), int( self.disk_side ) / qpixmap.height() ) qbrush.setTransform( qtransform ) # Painter painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( qbrush ) except Exception as e: try:QtCore.qDebug( f"Pigment.O ERROR | { e }" ) except:pass # Polygon dot = 5 line_size = 2 key = self.gamut_mask gdx = self.disk_x gdy = self.disk_y gds = self.disk_side if self.gamut_mask == "None": painter.setPen( QtCore.Qt.NoPen ) painter.drawEllipse( int( gdx ), int( gdy ), int( gds ), int( gds ) ) if self.gamut_mask == "Triangle": # Polygon painter.setPen( QtCore.Qt.NoPen ) poly = QPolygon( [ QPoint( int( gdx + gds * self.gamut_1tri[1][0] ), int( gdy + gds * self.gamut_1tri[1][1] ) ), QPoint( int( gdx + gds * self.gamut_1tri[2][0] ), int( gdy + gds * self.gamut_1tri[2][1] ) ), QPoint( int( gdx + gds * self.gamut_1tri[3][0] ), int( gdy + gds * self.gamut_1tri[3][1] ) ), ] ) painter.drawPolygon( poly ) # Display Subjective Primaries painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_1tri[0][0] - dot ), int( gdy + gds * self.gamut_1tri[0][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_1tri[1][0] - dot ), int( gdy + gds * self.gamut_1tri[1][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_1tri[2][0] - dot ), int( gdy + gds * self.gamut_1tri[2][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_1tri[3][0] - dot ), int( gdy + gds * self.gamut_1tri[3][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) if self.gamut_mask == "Square": # Polygon painter.setPen( QtCore.Qt.NoPen ) poly = QPolygon( [ QPoint( int( gdx + gds * self.gamut_1squ[1][0] ), int( gdy + gds * self.gamut_1squ[1][1] ) ), QPoint( int( gdx + gds * self.gamut_1squ[2][0] ), int( gdy + gds * self.gamut_1squ[2][1] ) ), QPoint( int( gdx + gds * self.gamut_1squ[3][0] ), int( gdy + gds * self.gamut_1squ[3][1] ) ), QPoint( int( gdx + gds * self.gamut_1squ[4][0] ), int( gdy + gds * self.gamut_1squ[4][1] ) ), ] ) painter.drawPolygon( poly ) # Display Subjective Primaries painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_1squ[0][0] - dot ), int( gdy + gds * self.gamut_1squ[0][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_1squ[1][0] - dot ), int( gdy + gds * self.gamut_1squ[1][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_1squ[2][0] - dot ), int( gdy + gds * self.gamut_1squ[2][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_1squ[3][0] - dot ), int( gdy + gds * self.gamut_1squ[3][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_1squ[4][0] - dot ), int( gdy + gds * self.gamut_1squ[4][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) if self.gamut_mask == "Circle": # Profile Points path, P0, P1, P2, P3, P4 = self.Render_Circle( gdx, gdy, gds, self.gamut_1cir ) # Polygon painter.setPen( QtCore.Qt.NoPen ) painter.drawPath( path ) # Display Subjective Primaries painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) painter.drawEllipse( int( P0[0] - dot ), int( P0[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P1[0] - dot ), int( P1[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P2[0] - dot ), int( P2[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P3[0] - dot ), int( P3[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P4[0] - dot ), int( P4[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) if self.gamut_mask == "2 Circle": # Profile Points path_0, P0_0, P1_0, P2_0, P3_0, P4_0 = self.Render_Circle( gdx, gdy, gds, self.gamut_2cir[0:5] ) path_1, P0_1, P1_1, P2_1, P3_1, P4_1 = self.Render_Circle( gdx, gdy, gds, self.gamut_2cir[5:10] ) # Polygon painter.setPen( QtCore.Qt.NoPen ) painter.drawPath( path_0 ) painter.drawPath( path_1 ) # Display Subjective Primaries painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) # Circle 0 painter.drawEllipse( int( P0_0[0] - dot ), int( P0_0[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P1_0[0] - dot ), int( P1_0[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P2_0[0] - dot ), int( P2_0[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P3_0[0] - dot ), int( P3_0[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P4_0[0] - dot ), int( P4_0[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) # Circle 1 painter.drawEllipse( int( P0_1[0] - dot ), int( P0_1[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P1_1[0] - dot ), int( P1_1[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P2_1[0] - dot ), int( P2_1[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P3_1[0] - dot ), int( P3_1[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( P4_1[0] - dot ), int( P4_1[1] - dot ), int( dot * 2 ), int( dot * 2 ) ) if self.gamut_mask == "3 Pie": rect = QRect( int( gdx ), int( gdy ), int( gds ), int( gds ) ) ang_a1 = 16 * self.geometry.Trig_2D_Points_Lines_Angle( gdx + self.gamut_3pie[1][0] * gds, gdy + self.gamut_3pie[1][1] * gds, self.w2, self.h2, self.widget_width, self.h2, ) ang_a2 = -16 * self.geometry.Trig_2D_Points_Lines_Angle( gdx + self.gamut_3pie[1][0] * gds, gdy + self.gamut_3pie[1][1] * gds, self.w2, self.h2, gdx + self.gamut_3pie[2][0] * gds, gdy + self.gamut_3pie[2][1] * gds, ) ang_b1 = 16 * self.geometry.Trig_2D_Points_Lines_Angle( gdx + self.gamut_3pie[3][0] * gds, gdy + self.gamut_3pie[3][1] * gds, self.w2, self.h2, self.widget_width, self.h2, ) ang_b2 = -16 * self.geometry.Trig_2D_Points_Lines_Angle( gdx + self.gamut_3pie[3][0] * gds, gdy + self.gamut_3pie[3][1] * gds, self.w2, self.h2, gdx + self.gamut_3pie[4][0] * gds, gdy + self.gamut_3pie[4][1] * gds, ) ang_c1 = 16 * self.geometry.Trig_2D_Points_Lines_Angle( gdx + self.gamut_3pie[5][0] * gds, gdy + self.gamut_3pie[5][1] * gds, self.w2, self.h2, self.widget_width, self.h2, ) ang_c2 = -16 * self.geometry.Trig_2D_Points_Lines_Angle( gdx + self.gamut_3pie[5][0] * gds, gdy + self.gamut_3pie[5][1] * gds, self.w2, self.h2, gdx + self.gamut_3pie[6][0] * gds, gdy + self.gamut_3pie[6][1] * gds, ) # Polygon painter.setPen( QtCore.Qt.NoPen ) painter.drawPie( rect, int( ang_a1 ), int( ang_a2 ) ) painter.drawPie( rect, int( ang_b1 ), int( ang_b2 ) ) painter.drawPie( rect, int( ang_c1 ), int( ang_c2 ) ) # Display Subjective Primaries painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_3pie[0][0] - dot ), int( gdy + gds * self.gamut_3pie[0][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_3pie[1][0] - dot ), int( gdy + gds * self.gamut_3pie[1][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_3pie[2][0] - dot ), int( gdy + gds * self.gamut_3pie[2][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_3pie[3][0] - dot ), int( gdy + gds * self.gamut_3pie[3][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_3pie[4][0] - dot ), int( gdy + gds * self.gamut_3pie[4][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_3pie[5][0] - dot ), int( gdy + gds * self.gamut_3pie[5][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) painter.drawEllipse( int( gdx + gds * self.gamut_3pie[6][0] - dot ), int( gdy + gds * self.gamut_3pie[6][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) # Analyse Colors if self.analyse != None: # Variables dot = 5 line_size = 2 length = len( self.analyse ) # Draw painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) for i in range( 0, length ): color = self.analyse[i] if int( self.tan_axis * 255 ) == int( color[f"{self.chan}_3"] * 255 ): if self.wheel_mode == "DIGITAL": angle = color[hue_index] * 360 if self.wheel_mode == "ANALOG": angle = color[hue_index] * 360 - hue_a radius = color[f"{self.chan}_2"] px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.disk_side * 0.5, radius, angle ) painter.drawEllipse( int( px - dot ), int( py - dot ), int( dot * 2 ), int( dot * 2 ) ) # Pinned Colors if ( self.color != None and self.pin_list != None ): # Variables dot = 5 line_size = 2 length = len( self.pin_list ) # Draw painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) for i in range( 0, length ): if self.pin_list[i]["active"] == True: if self.wheel_mode == "DIGITAL": angle = self.pin_list[i][hue_index] * 360 if self.wheel_mode == "ANALOG": angle = self.pin_list[i][hue_index] * 360 - hue_a radius = self.pin_list[i][f"{self.chan}_2"] px, py = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.disk_side * 0.5, radius, angle ) painter.drawEllipse( int( px - dot ), int( py - dot ), int( dot * 2 ), int( dot * 2 ) ) # Harmony Colors if ( self.color != None and self.harmony_list != None ): # Variables line_size = 2 dot = 5 # Parsing points = [] for i in range( 0, len( self.harmony_list ) ): if self.wheel_mode == "DIGITAL": angle = self.harmony_list[i][hue_index] * 360 if self.wheel_mode == "ANALOG": angle = self.harmony_list[i][hue_index] * 360 - hue_a radius = self.harmony_list[i][f"{self.chan}_2"] har_x, har_y = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.disk_side * 0.5, radius, angle ) points.append( ( har_x, har_y ) ) length = len( points ) # Draw Line painter.setPen( QPen( self.color_1, line_size, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin ) ) painter.setBrush( QtCore.Qt.NoBrush ) for i in range( 1, length ): painter.drawLine( int( points[i-1][0] ), int( points[i-1][1] ), int( points[i][0] ), int( points[i][1] ) ) if self.harmony_rule in [ "Triadic", "Tetradic" ]: painter.drawLine( int( points[0][0] ), int( points[0][1] ), int( points[length-1][0] ), int( points[length-1][1] ) ) # Draw Points painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) for i in range( 0, length ): painter.drawEllipse( int( points[i][0] - dot ), int( points[i][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) # Cursor size = 10 zoom_size = 100 margin_size = 10 if ( self.press == True and self.zoom == True ): size = zoom_size Cursor_Zoom( self, painter, size, margin_size ) elif( self.pressure > self.input_pressure ): size = zoom_size * self.pressure Cursor_Zoom( self, painter, size, margin_size ) else: Cursor_Normal( self, painter, size ) def Render_Circle( self, px, py, side, points ): # Points from User P0 = [ px + points[0][0] * side, py + points[0][1] * side ] P1 = [ px + points[1][0] * side, py + points[1][1] * side ] P2 = [ px + points[2][0] * side, py + points[2][1] * side ] P3 = [ px + points[3][0] * side, py + points[3][1] * side ] P4 = [ px + points[4][0] * side, py + points[4][1] * side ] # Angles from the Points O1 = self.geometry.Trig_2D_Points_Lines_Angle( px, P0[1], P0[0],P0[1], P1[0],P1[1] ) O2 = self.geometry.Trig_2D_Points_Lines_Angle( px, P0[1], P0[0],P0[1], P2[0],P2[1] ) O3 = self.geometry.Trig_2D_Points_Lines_Angle( px, P0[1], P0[0],P0[1], P3[0],P3[1] ) O4 = self.geometry.Trig_2D_Points_Lines_Angle( px, P0[1], P0[0],P0[1], P4[0],P4[1] ) # Order Angles in Sequence order = [ ( O1, P1 ), ( O2, P2 ), ( O3, P3 ), ( O4, P4 ) ] order.sort() A1 = order[0][1] A2 = order[1][1] A3 = order[2][1] A4 = order[3][1] # Bridge Points B12 = [ self.geometry.Lerp_1D( 0.5, A1[0], A2[0] ), self.geometry.Lerp_1D( 0.5, A1[1], A2[1] ) ] B23 = [ self.geometry.Lerp_1D( 0.5, A2[0], A3[0] ), self.geometry.Lerp_1D( 0.5, A2[1], A3[1] ) ] B34 = [ self.geometry.Lerp_1D( 0.5, A3[0], A4[0] ), self.geometry.Lerp_1D( 0.5, A3[1], A4[1] ) ] B41 = [ self.geometry.Lerp_1D( 0.5, A4[0], A1[0] ), self.geometry.Lerp_1D( 0.5, A4[1], A1[1] ) ] # Bridge Components dist_B12 = self.geometry.Trig_2D_Ortogonal_Components( P0[0], P0[1], B12[0], B12[1] ) dist_B23 = self.geometry.Trig_2D_Ortogonal_Components( P0[0], P0[1], B23[0], B23[1] ) dist_B34 = self.geometry.Trig_2D_Ortogonal_Components( P0[0], P0[1], B34[0], B34[1] ) dist_B41 = self.geometry.Trig_2D_Ortogonal_Components( P0[0], P0[1], B41[0], B41[1] ) # Intermediate Points scalar = 2 P12 = [ P0[0] + scalar * dist_B12[0], P0[1] + scalar * dist_B12[1] ] P23 = [ P0[0] + scalar * dist_B23[0], P0[1] + scalar * dist_B23[1] ] P34 = [ P0[0] + scalar * dist_B34[0], P0[1] + scalar * dist_B34[1] ] P41 = [ P0[0] + scalar * dist_B41[0], P0[1] + scalar * dist_B41[1] ] # Painter Path Object path = QPainterPath() a = 0.551915024494 b = 1 - 0.551915024494 path.moveTo( A1[0], A1[1] ) path.cubicTo( QPoint( self.geometry.Lerp_1D( a, A1[0], P12[0] ), self.geometry.Lerp_1D( a, A1[1], P12[1] ) ), QPoint( self.geometry.Lerp_1D( b, P12[0], A2[0] ), self.geometry.Lerp_1D( b, P12[1], A2[1] ) ), QPoint( A2[0], A2[1] ) ) path.cubicTo( QPoint( self.geometry.Lerp_1D( a, A2[0], P23[0] ), self.geometry.Lerp_1D( a, A2[1], P23[1] ) ), QPoint( self.geometry.Lerp_1D( b, P23[0], A3[0] ), self.geometry.Lerp_1D( b, P23[1], A3[1] ) ), QPoint( A3[0], A3[1] ) ) path.cubicTo( QPoint( self.geometry.Lerp_1D( a, A3[0], P34[0] ), self.geometry.Lerp_1D( a, A3[1], P34[1] ) ), QPoint( self.geometry.Lerp_1D( b, P34[0], A4[0] ), self.geometry.Lerp_1D( b, P34[1], A4[1] ) ), QPoint( A4[0], A4[1] ) ) path.cubicTo( QPoint( self.geometry.Lerp_1D( a, A4[0], P41[0] ), self.geometry.Lerp_1D( a, A4[1], P41[1] ) ), QPoint( self.geometry.Lerp_1D( b, P41[0], A1[0] ), self.geometry.Lerp_1D( b, P41[1], A1[1] ) ), QPoint( A1[0], A1[1] ) ) # Return return path, P0, P1, P2, P3, P4 class Panel_Hexagon( QWidget ): SIGNAL_VALUE = QtCore.pyqtSignal( dict ) SIGNAL_TAN = QtCore.pyqtSignal( float ) SIGNAL_RELEASE = QtCore.pyqtSignal( int ) SIGNAL_PIN_INDEX = QtCore.pyqtSignal( int ) SIGNAL_PIN_EDIT = QtCore.pyqtSignal( dict ) # Init def __init__( self, parent ): super( Panel_Hexagon, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, render_height ) def Init_Variables( self ): # Widget self.widget_width = 0 self.widget_height = 0 self.w2 = 0 self.h2 = 0 self.origin_x = 0 self.origin_y = 0 self.origin_tan_axis = 0 self.event_x = 0 self.event_y = 0 self.press = False self.pressure = 0 self.input_pressure = 0.5 self.px = 0 self.py = 0 self.side = 0 # Display self.zoom = False self.tan_axis = 0 # 0-360 because of background index self.tan_range = 255 self.qpixmap_list = [] # Format self.directory = None # Path self.d_cm = None # "A" "RGB" "CMYK" "YUV" "XYZ" "LAB" # Wheel self.wheel_mode = "DIGITAL" # "DIGITAL" "ANALOG" self.wheel_space = "HSV" # "HSV" "HSL" "HSY" "ARD" # Geometry self.O1 = None self.O2 = None self.O3 = None self.O4 = None self.O5 = None self.O6 = None self.C61 = None # Colors self.hex_color = QColor( "#000000" ) self.color = None # Harmony Colors self.harmony_rule = None self.harmony_index = None self.harmony_list = None # Pinned Colors self.pin_index = None self.pin_list = None # Analyse Colors self.analyse = None # Modules self.geometry = Geometry() self.convert = None def Init_Convert( self, convert ): self.convert = convert self.update() # Set def Set_ColorModel( self, d_cm ): self.d_cm = d_cm self.Set_ColorSpace_inDocument( self.directory, self.d_cm ) self.update() def Set_Wheel( self, wheel_mode, wheel_space ): self.wheel_mode = wheel_mode self.wheel_space = wheel_space self.update() def Set_ColorSpace_inDocument( self, directory, d_cm ): # Variables self.directory = directory self.d_cm = d_cm # Cursor if self.color != None: self.event_x = self.geometry.Limit_Range( self.color[f"uvd_1"] * self.widget_width, 0, self.widget_width ) self.event_y = self.geometry.Limit_Range( self.color[f"uvd_2"] * self.widget_height, 0, self.widget_height ) # Read Zip File location = os.path.join( self.directory, panel ) location = os.path.join( location, f"{ self.d_cm }_UVD_4.zip" ) self.qpixmap_list = Read_Zip( self, location, self.tan_range, "UVD", "4" ) # Update self.update() def Set_Size( self, widget_width, widget_height ): # Variables self.widget_width = widget_width self.widget_height = widget_height self.w2 = widget_width * 0.5 self.h2 = widget_height * 0.5 # Frame if self.widget_width >= self.widget_height: self.side = self.widget_height self.px = self.w2 - ( self.side * 0.5 ) self.py = 0 else: self.side = self.widget_width self.px = 0 self.py = self.h2 - ( self.side * 0.5 ) # Origin Points self.O1, self.O2, self.O3, self.O4, self.O5, self.O6, C12, C23, C34, C45, C56, self.C61 = self.convert.uvd_hexagon( self.tan_axis, 0.5, 0.5, -1 ) # Update self.update() def Set_Zoom( self, boolean ): self.press = boolean self.zoom = boolean self.update() # Updates def Update_Panel( self, color ): # Variables self.color = color # Display self.hex_color = QColor( color["hex6"] ) # Values self.event_x = self.px + ( 0.5 + color["uvd_1"] * 0.5 ) * self.side self.event_y = self.py + ( 0.5 - color["uvd_2"] * 0.5 ) * self.side self.tan_axis = color["uvd_3"] # Origin Points self.O1, self.O2, self.O3, self.O4, self.O5, self.O6, C12, C23, C34, C45, C56, self.C61 = self.convert.uvd_hexagon( self.tan_axis, 0.5, 0.5, -1 ) # Update self.update() def Update_Harmony( self, harmony_rule, harmony_index, harmony_list ): self.harmony_rule = harmony_rule self.harmony_index = harmony_index self.harmony_list = harmony_list self.update() def Update_Pin( self, pin_list ): self.pin_list = pin_list self.update() def Update_Analyse( self, analyse ): self.analyse = analyse self.update() # Mouse Interaction def mousePressEvent( self, event ): # Event ex = event.x() ey = event.y() # Variables self.origin_x = ex self.origin_y = ey self.origin_tan_axis = self.tan_axis self.press = True # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( ex, ey, None ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( ex, ey, None ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Tangent( ex ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Snap( ex, ey ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): self.Cursor_Snap( ex, ey ) self.update() def mouseMoveEvent( self, event ): # Events ex = event.x() ey = event.y() # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( ex, ey, None ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( ex, ey, None ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Tangent( ex ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Snap( ex, ey ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): self.Cursor_Position( ex, ey, self.pin_index ) self.update() def mouseDoubleClickEvent( self, event ): # Events ex = event.x() ey = event.y() # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( ex, ey, None ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( ex, ey, None ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Tangent( ex ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Snap( ex, ey ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): self.Cursor_Position( ex, ey, self.pin_index ) self.update() def mouseReleaseEvent( self, event ): # Variables self.press = False self.zoom = False self.pressure = 0 self.pin_index = None # Updates self.SIGNAL_RELEASE.emit( 0 ) self.update() # Mouse Event def Cursor_Position( self, ex, ey, pin_index ): # Calculation if ( self.tan_axis <= 0 or self.tan_axis >= 1 ): self.event_x = self.w2 self.event_y = self.h2 else: self.event_x, self.event_y = self.Hexagon_Inter( ex, ey, self.tan_axis) u = 2 * ( ( self.event_x - self.px ) / self.side ) - 1 v = -2 * ( ( self.event_y - self.py ) / self.side ) + 1 # Signal values = { "c1" : u, "c2" : v, "pin_index" : pin_index } self.SIGNAL_VALUE.emit( values ) if pin_index != None: self.SIGNAL_PIN_EDIT.emit( values ) def Cursor_Tangent( self, ex ): # Hue delta_hue = ( ( ex - self.origin_x ) / self.widget_width ) self.tan_axis = self.geometry.Limit_Float( self.origin_tan_axis + delta_hue ) # Update self.SIGNAL_TAN.emit( self.tan_axis ) def Cursor_Snap( self, ex, ey ): if self.pin_list != None: distance = [] for i in range( 0, len( self.pin_list ) ): if self.pin_list[i]["active"] == True: px = self.px + ( 0.5 + self.pin_list[i]["uvd_1"] * 0.5 ) * self.side py = self.py + ( 0.5 - self.pin_list[i]["uvd_2"] * 0.5 ) * self.side dist = self.geometry.Trig_2D_Points_Distance( ex, ey, px, py ) distance.append( ( dist, i ) ) if len( distance ) > 0: distance.sort() pin_index = distance[0][1] if pin_index < 20: self.pin_index = pin_index self.SIGNAL_PIN_INDEX.emit( self.pin_index ) # Panel Modifiers def Hexagon_Inter( self, ex, ey, d ): # Variables red_x = self.px + self.C61[0] * self.side red_y = self.py + self.C61[1] * self.side o1_x = self.px + self.O1[0] * self.side o1_y = self.py + self.O1[1] * self.side o2_x = self.px + self.O2[0] * self.side o2_y = self.py + self.O2[1] * self.side o3_x = self.px + self.O3[0] * self.side o3_y = self.py + self.O3[1] * self.side o4_x = self.px + self.O4[0] * self.side o4_y = self.py + self.O4[1] * self.side o5_x = self.px + self.O5[0] * self.side o5_y = self.py + self.O5[1] * self.side o6_x = self.px + self.O6[0] * self.side o6_y = self.py + self.O6[1] * self.side # Single Points di = round( d * 3, 14 ) if ( di <= 0 or di >= 3 ): ex = self.w2 ey = self.h2 else: # Angles angle = self.geometry.Trig_2D_Points_Lines_Angle( red_x, red_y, self.w2, self.h2, ex, ey ) a1 = self.geometry.Trig_2D_Points_Lines_Angle( red_x, red_y, self.w2, self.h2, o1_x, o1_y ) a2 = self.geometry.Trig_2D_Points_Lines_Angle( red_x, red_y, self.w2, self.h2, o2_x, o2_y ) a3 = self.geometry.Trig_2D_Points_Lines_Angle( red_x, red_y, self.w2, self.h2, o3_x, o3_y ) a4 = self.geometry.Trig_2D_Points_Lines_Angle( red_x, red_y, self.w2, self.h2, o4_x, o4_y ) a5 = self.geometry.Trig_2D_Points_Lines_Angle( red_x, red_y, self.w2, self.h2, o5_x, o5_y ) a6 = self.geometry.Trig_2D_Points_Lines_Angle( red_x, red_y, self.w2, self.h2, o6_x, o6_y ) if a6 == 0: a6 = 360 # Limit if angle <= a1: lx, ly = self.geometry.Trig_2D_Points_Lines_Intersection( red_x, red_y, o1_x, o1_y, ex, ey, self.w2, self.h2 ) elif ( angle >= a1 and angle <= a2 ): lx, ly = self.geometry.Trig_2D_Points_Lines_Intersection( o1_x, o1_y, o2_x, o2_y, ex, ey, self.w2, self.h2 ) elif ( angle >= a2 and angle <= a3 ): lx, ly = self.geometry.Trig_2D_Points_Lines_Intersection( o2_x, o2_y, o3_x, o3_y, ex, ey, self.w2, self.h2 ) elif ( angle >= a3 and angle <= a4 ): lx, ly = self.geometry.Trig_2D_Points_Lines_Intersection( o3_x, o3_y, o4_x, o4_y, ex, ey, self.w2, self.h2 ) elif ( angle >= a4 and angle <= a5 ): lx, ly = self.geometry.Trig_2D_Points_Lines_Intersection( o4_x, o4_y, o5_x, o5_y, ex, ey, self.w2, self.h2 ) elif ( angle >= a5 and angle <= a6 ): lx, ly = self.geometry.Trig_2D_Points_Lines_Intersection( o5_x, o5_y, o6_x, o6_y, ex, ey, self.w2, self.h2 ) elif angle >= a6: lx, ly = self.geometry.Trig_2D_Points_Lines_Intersection( o6_x, o6_y, red_x, red_y, ex, ey, self.w2, self.h2 ) else: radius = self.geometry.Trig_2D_Points_Distance( o1_x, o1_y, self.w2, self.h2 ) lx, ly = self.geometry.Trig_2D_Angle_Circle( self.w2, self.h2, self.side, radius, angle ) # Distance event_dist = self.geometry.Trig_2D_Points_Distance( ex, ey, self.w2, self.h2 ) limit_dist = self.geometry.Trig_2D_Points_Distance( lx, ly, self.w2, self.h2 ) if event_dist >= limit_dist: ex = lx ey = ly # Return return ex, ey # Tablet Interaction def tabletEvent( self, event ): self.pressure = event.pressure() self.update() # Paint def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Draw Gradient if len( self.qpixmap_list ) > 0: try: # Draw Masks hexagon = QPainterPath() hexagon.moveTo( int( self.px + self.O1[0] * self.side ), int( self.py + self.O1[1] * self.side ) ) hexagon.lineTo( int( self.px + self.O2[0] * self.side ), int( self.py + self.O2[1] * self.side ) ) hexagon.lineTo( int( self.px + self.O3[0] * self.side ), int( self.py + self.O3[1] * self.side ) ) hexagon.lineTo( int( self.px + self.O4[0] * self.side ), int( self.py + self.O4[1] * self.side ) ) hexagon.lineTo( int( self.px + self.O5[0] * self.side ), int( self.py + self.O5[1] * self.side ) ) hexagon.lineTo( int( self.px + self.O6[0] * self.side ), int( self.py + self.O6[1] * self.side ) ) painter.setClipPath( hexagon ) # Draw Pixmaps painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QtCore.Qt.NoBrush ) index = int( self.tan_axis * self.tan_range ) qpixmap = self.qpixmap_list[index] if qpixmap.isNull() == False: render = qpixmap.scaled( self.side, self.side, Qt.IgnoreAspectRatio, Qt.FastTransformation ) painter.drawPixmap( int( self.px ), int( self.py ), render ) # Reset Mask square = QPainterPath() square.moveTo( int( 0 ), int( 0 ) ) square.lineTo( int( self.widget_width ), int( 0 ) ) square.lineTo( int( self.widget_width ), int( self.widget_height ) ) square.lineTo( int( 0 ), int( self.widget_height ) ) painter.setClipPath( square ) except Exception as e: try:QtCore.qDebug( f"Pigment.O ERROR | { e }" ) except:pass # Analyse Colors if self.analyse != None: # Variables dot = 5 line_size = 2 length = len( self.analyse ) # Draw painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) for i in range( 0, length ): color = self.analyse[i] if int( self.tan_axis * 255 ) == int( color[f"uvd_3"] * 255 ): px = self.px + ( 0.5 + color["uvd_1"] * 0.5 ) * self.side py = self.py + ( 0.5 - color["uvd_2"] * 0.5 ) * self.side painter.drawEllipse( int( px - dot ), int( py - dot ), int( dot * 2 ), int( dot * 2 ) ) # Pinned Colors if ( self.color != None and self.pin_list != None ): # Variables pin_size = 5 line_size = 2 length = len( self.pin_list ) # Draw painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) for i in range( 0, length ): if self.pin_list[i]["active"] == True: cx = self.px + ( 0.5 + self.pin_list[i]["uvd_1"] * 0.5 ) * self.side cy = self.py + ( 0.5 - self.pin_list[i]["uvd_2"] * 0.5 ) * self.side painter.drawEllipse( int( cx - pin_size ), int( cy - pin_size ), int( pin_size * 2 ), int( pin_size * 2 ) ) # Harmony Colors if ( self.color != None and self.harmony_list != None ): # Variables line_size = 2 dot = 5 # Parsing points = [] for i in range( 0, len( self.harmony_list ) ): har_x = self.px + ( 0.5 + self.harmony_list[i]["uvd_1"] * 0.5 ) * self.side har_y = self.py + ( 0.5 - self.harmony_list[i]["uvd_2"] * 0.5 ) * self.side points.append( ( har_x, har_y ) ) length = len( points ) # Draw Line painter.setPen( QPen( self.color_1, line_size, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin ) ) painter.setBrush( QtCore.Qt.NoBrush ) for i in range( 1, length ): painter.drawLine( int( points[i-1][0] ), int( points[i-1][1] ), int( points[i][0] ), int( points[i][1] ) ) if self.harmony_rule in [ "Triadic", "Tetradic" ]: painter.drawLine( int( points[0][0] ), int( points[0][1] ), int( points[length-1][0] ), int( points[length-1][1] ) ) # Draw Points painter.setPen( QPen( self.color_2, line_size, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin ) ) painter.setBrush( QBrush( self.color_1 ) ) for i in range( 0, length ): painter.drawEllipse( int( points[i][0] - dot ), int( points[i][1] - dot ), int( dot * 2 ), int( dot * 2 ) ) # Cursor size = 10 zoom_size = 100 margin_size = 10 if ( self.press == True and self.zoom == True ): size = zoom_size Cursor_Zoom( self, painter, size, margin_size ) elif( self.pressure > self.input_pressure ): size = zoom_size * self.pressure Cursor_Zoom( self, painter, size, margin_size ) else: Cursor_Normal( self, painter, size ) class Panel_Dot( QWidget ): SIGNAL_VALUE = QtCore.pyqtSignal( str ) SIGNAL_RELEASE = QtCore.pyqtSignal( int ) SIGNAL_INTERPOLATION = QtCore.pyqtSignal( str ) SIGNAL_DIMENSION = QtCore.pyqtSignal( int ) SIGNAL_EDIT = QtCore.pyqtSignal( bool ) SIGNAL_ZORN = QtCore.pyqtSignal( int ) # Init def __init__( self, parent ): super( Panel_Dot, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, render_height ) def Init_Variables( self ): # Widget self.press = False self.widget_width = 0 self.widget_height = 0 self.w2 = 0 self.h2 = 0 self.press = False self.zoom = False self.pressure = 0 self.input_pressure = 0.5 # Events self.event_x = 0 self.event_y = 0 self.dot_x = 0 self.dot_y = 0 # Settings self.dot_interpolation = "RGB" self.dot_dimension = 11 self.dot_edit = True self.dot_matrix = None self.unit = 24 self.margin = 5 self.shape = "CIRCLE" # SQUARE CIRCLE self.Update_Side() self.Cursor_Move( self.dot_x, self.dot_y ) # Context Menu Checks self.dot_interpolation = "RGB" self.dot_dimension = 11 # Color self.hex_color = QColor( "#000000" ) # Modules self.geometry = Geometry() self.convert = None def Init_Convert( self, convert ): self.convert = convert self.update() # Relay def Set_Size( self, widget_width, widget_height ): # Widget Size self.widget_width = widget_width self.widget_height = widget_height self.w2 = int( widget_width * 0.5 ) self.h2 = int( widget_height * 0.5 ) # Cursor Move self.Cursor_Move( self.dot_x, self.dot_y ) self.update() def Set_Interpolation( self, string ): self.dot_interpolation = string self.update() def Set_Dimension( self, number ): self.dot_dimension = number self.Update_Side() self.update() def Set_Edit( self, boolean ): self.dot_edit = boolean self.update() # Update def Update_Color( self, dot_matrix ): self.dot_matrix = dot_matrix self.update() def Update_Side( self ): self.side = ( self.unit * self.dot_dimension ) + ( self.margin * ( self.dot_dimension - 1 ) ) # Mouse Interaction def mousePressEvent( self, event ): # Input self.origin_x = event.x() self.origin_y = event.y() self.press = True # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( event ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( event ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( event ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( event ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): self.press = False self.Context_Menu( event ) # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): pass self.update() def mouseMoveEvent( self, event ): # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( event ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( event ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( event ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( event ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): pass self.update() def mouseDoubleClickEvent( self, event ): # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( event ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Position( event ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( event ) if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Position( event ) # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): pass self.update() def mouseReleaseEvent( self, event ): # Variables self.press = False self.zoom = False self.pressure = 0 # Updates self.SIGNAL_RELEASE.emit( 0 ) self.update() # Mouse Event def Cursor_Move( self, dot_x, dot_y ): if self.dot_matrix != None: self.event_x = int( self.w2 - ( self.side * 0.5 ) + ( self.unit*dot_x + self.margin*( dot_x-1 ) ) + ( self.unit * 0.5 ) ) self.event_y = int( self.h2 - ( self.side * 0.5 ) + ( self.unit*dot_y + self.margin*( dot_y-1 ) ) + ( self.unit * 0.5 ) ) def Cursor_Position( self, event ): # Read ex = event.x() ey = event.y() if self.dot_matrix != None: # Points points = [] for y in range( 0, self.dot_dimension ): for x in range( 0, self.dot_dimension ): px = int( self.w2 - ( self.side * 0.5 ) + ( self.unit*x + self.margin*( x-1 ) ) + ( self.unit * 0.5 ) ) py = int( self.h2 - ( self.side * 0.5 ) + ( self.unit*y + self.margin*( y-1 ) ) + ( self.unit * 0.5 ) ) dist = self.geometry.Trig_2D_Points_Distance( px, py, ex, ey ) hex_code = self.dot_matrix[y][x] points.append( [dist, px, py, hex_code, x, y] ) points.sort() # Input self.event_x = points[0][1] self.event_y = points[0][2] # Lock self.dot_x = points[0][4] self.dot_y = points[0][5] # Signal hex_code = points[0][3] self.Hex_Color( hex_code ) def Hex_Color( self, hex_code ): self.hex_color = QColor( hex_code ) self.SIGNAL_VALUE.emit( hex_code ) self.update() # Context def Context_Menu( self, event ): if self.press == False: # Menu cmenu = QMenu( self ) # Interpolation cmenu_interpolation = cmenu.addMenu( "Interpolation" ) cmenu_int_rgb = cmenu_interpolation.addAction( "RGB" ) cmenu_int_cmyk = cmenu_interpolation.addAction( "CMYK" ) cmenu_int_ryb = cmenu_interpolation.addAction( "RYB" ) cmenu_int_yuv = cmenu_interpolation.addAction( "YUV" ) cmenu_int_hsv = cmenu_interpolation.addAction( "HSV" ) cmenu_int_hsl = cmenu_interpolation.addAction( "HSL" ) cmenu_int_hsy = cmenu_interpolation.addAction( "HSY" ) cmenu_int_ard = cmenu_interpolation.addAction( "ARD" ) cmenu_int_rgb.setCheckable( True ) cmenu_int_cmyk.setCheckable( True ) cmenu_int_ryb.setCheckable( True ) cmenu_int_yuv.setCheckable( True ) cmenu_int_hsv.setCheckable( True ) cmenu_int_hsl.setCheckable( True ) cmenu_int_hsy.setCheckable( True ) cmenu_int_ard.setCheckable( True ) cmenu_int_rgb.setChecked( self.dot_interpolation == "RGB" ) cmenu_int_cmyk.setChecked( self.dot_interpolation == "CMYK" ) cmenu_int_ryb.setChecked( self.dot_interpolation == "RYB" ) cmenu_int_yuv.setChecked( self.dot_interpolation == "YUV" ) cmenu_int_hsv.setChecked( self.dot_interpolation == "HSV" ) cmenu_int_hsl.setChecked( self.dot_interpolation == "HSL" ) cmenu_int_hsy.setChecked( self.dot_interpolation == "HSY" ) cmenu_int_ard.setChecked( self.dot_interpolation == "ARD" ) # Dimension cmenu_dimension = cmenu.addMenu( "Dimension" ) cmenu_dim_3 = cmenu_dimension.addAction( "3 x 3" ) cmenu_dim_5 = cmenu_dimension.addAction( "5 x 5" ) cmenu_dim_7 = cmenu_dimension.addAction( "7 x 7" ) cmenu_dim_9 = cmenu_dimension.addAction( "9 x 9" ) cmenu_dim_11 = cmenu_dimension.addAction( "11 x 11" ) cmenu_dim_13 = cmenu_dimension.addAction( "13 x 13" ) cmenu_dim_15 = cmenu_dimension.addAction( "15 x 15" ) cmenu_dim_17 = cmenu_dimension.addAction( "17 x 17" ) cmenu_dim_19 = cmenu_dimension.addAction( "19 x 19" ) cmenu_dim_21 = cmenu_dimension.addAction( "21 x 21" ) cmenu_dim_3.setCheckable( True ) cmenu_dim_5.setCheckable( True ) cmenu_dim_7.setCheckable( True ) cmenu_dim_9.setCheckable( True ) cmenu_dim_11.setCheckable( True ) cmenu_dim_13.setCheckable( True ) cmenu_dim_15.setCheckable( True ) cmenu_dim_17.setCheckable( True ) cmenu_dim_19.setCheckable( True ) cmenu_dim_21.setCheckable( True ) cmenu_dim_3.setChecked( self.dot_dimension == "3 x 3" ) cmenu_dim_5.setChecked( self.dot_dimension == "5 x 5" ) cmenu_dim_7.setChecked( self.dot_dimension == "7 x 7" ) cmenu_dim_9.setChecked( self.dot_dimension == "9 x 9" ) cmenu_dim_11.setChecked( self.dot_dimension == "11 x 11" ) cmenu_dim_13.setChecked( self.dot_dimension == "13 x 13" ) cmenu_dim_15.setChecked( self.dot_dimension == "15 x 15" ) cmenu_dim_17.setChecked( self.dot_dimension == "17 x 17" ) cmenu_dim_19.setChecked( self.dot_dimension == "19 x 19" ) cmenu_dim_21.setChecked( self.dot_dimension == "21 x 21" ) # Edit cmenu_edit = cmenu.addAction( "Edit" ) cmenu_edit.setCheckable( True ) cmenu_edit.setChecked( self.dot_edit ) # Reset cmenu_zorn = cmenu.addAction( "Zorn Palette" ) # Actions action = cmenu.exec_( self.mapToGlobal( event.pos() ) ) # Triggers if action == cmenu_int_rgb: self.dot_interpolation = "RGB" self.SIGNAL_INTERPOLATION.emit( self.dot_interpolation ) if action == cmenu_int_cmyk: self.dot_interpolation = "CMYK" self.SIGNAL_INTERPOLATION.emit( self.dot_interpolation ) if action == cmenu_int_ryb: self.dot_interpolation = "RYB" self.SIGNAL_INTERPOLATION.emit( self.dot_interpolation ) if action == cmenu_int_yuv: self.dot_interpolation = "YUV" self.SIGNAL_INTERPOLATION.emit( self.dot_interpolation ) if action == cmenu_int_hsv: self.dot_interpolation = "HSV" self.SIGNAL_INTERPOLATION.emit( self.dot_interpolation ) if action == cmenu_int_hsl: self.dot_interpolation = "HSL" self.SIGNAL_INTERPOLATION.emit( self.dot_interpolation ) if action == cmenu_int_hsy: self.dot_interpolation = "HSY" self.SIGNAL_INTERPOLATION.emit( self.dot_interpolation ) if action == cmenu_int_ard: self.dot_interpolation = "ARD" self.SIGNAL_INTERPOLATION.emit( self.dot_interpolation ) if action == cmenu_dim_3: self.dot_dimension = 3 self.SIGNAL_DIMENSION.emit( self.dot_dimension ) if action == cmenu_dim_5: self.dot_dimension = 5 self.SIGNAL_DIMENSION.emit( self.dot_dimension ) if action == cmenu_dim_7: self.dot_dimension = 7 self.SIGNAL_DIMENSION.emit( self.dot_dimension ) if action == cmenu_dim_9: self.dot_dimension = 9 self.SIGNAL_DIMENSION.emit( self.dot_dimension ) if action == cmenu_dim_11: self.dot_dimension = 11 self.SIGNAL_DIMENSION.emit( self.dot_dimension ) if action == cmenu_dim_13: self.dot_dimension = 13 self.SIGNAL_DIMENSION.emit( self.dot_dimension ) if action == cmenu_dim_15: self.dot_dimension = 15 self.SIGNAL_DIMENSION.emit( self.dot_dimension ) if action == cmenu_dim_17: self.dot_dimension = 17 self.SIGNAL_DIMENSION.emit( self.dot_dimension ) if action == cmenu_dim_19: self.dot_dimension = 19 self.SIGNAL_DIMENSION.emit( self.dot_dimension ) if action == cmenu_dim_21: self.dot_dimension = 21 self.SIGNAL_DIMENSION.emit( self.dot_dimension ) if action == cmenu_edit: self.SIGNAL_EDIT.emit( not self.dot_edit ) if action == cmenu_zorn: self.SIGNAL_ZORN.emit( 0 ) # Tablet Interaction def tabletEvent( self, event ): self.pressure = event.pressure() self.update() # Paint def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Dots if self.dot_matrix != None: painter.setPen( QtCore.Qt.NoPen ) for y in range( 0, self.dot_dimension ): for x in range( 0, self.dot_dimension ): try: hex6 = self.dot_matrix[y][x] except: hex6 = "#000000" painter.setBrush( QBrush( QColor( hex6 ) ) ) point_x = int( self.w2 - ( self.side * 0.5 ) + ( self.unit * x + self.margin * x ) ) point_y = int( self.h2 - ( self.side * 0.5 ) + ( self.unit * y + self.margin * y ) ) if self.shape == "CIRCLE": painter.drawEllipse( point_x, point_y, self.unit, self.unit ) elif self.shape == "SQUARE": painter.drawRect( point_x, point_y, self.unit, self.unit ) # Cursor zoom_size = 100 margin_size = 10 size = 10 s2 = int( size * 0.5 ) if ( self.press == True and self.zoom == True ): size = zoom_size self.Cursor_Zoom( painter, size, margin_size, s2 ) elif( self.pressure > self.input_pressure ): size = zoom_size * self.pressure self.Cursor_Zoom( painter, size, margin_size, s2 ) else: self.Cursor_Normal( painter, size, s2 ) # Cursor def Cursor_Normal( self, painter, size, s2 ): # Variables w = 2 # Mask mask = QPainterPath() mask.addEllipse( int( self.event_x - s2 ), int( self.event_y - s2 ), int( size * 2 ), int( size * 2 ), ) mask.addEllipse( int( self.event_x - s2 + w * 2 ), int( self.event_y - s2 + w * 2 ), int( size * 2 - w * 4 ), int( size * 2 - w * 4 ), ) painter.setClipPath( mask ) # Black Circle painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_black ) ) painter.drawEllipse( int( self.event_x - s2 ), int( self.event_y - s2 ), int( size * 2 ), int( size * 2 ), ) # White Circle painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_white ) ) painter.drawEllipse( int( self.event_x - s2 + w ), int( self.event_y - s2 + w ), int( size * 2 - w * 2 ), int( size * 2 - w * 2 ), ) def Cursor_Zoom( self, painter, zoom_size, margin_size, s2 ): # Border painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_black ) ) painter.drawEllipse( int( self.event_x - zoom_size + s2 ), int( self.event_y - zoom_size + s2 ), int( zoom_size * 2 ), int( zoom_size * 2 ), ) # Hex Color painter.setBrush( QBrush( self.hex_color ) ) painter.drawEllipse( int( self.event_x - zoom_size + margin_size + s2 ), int( self.event_y - zoom_size + margin_size + s2 ), int( zoom_size * 2 - margin_size * 2 ), int( zoom_size * 2 - margin_size * 2 ), ) class Panel_Mask( QWidget ): SIGNAL_VALUE = QtCore.pyqtSignal( str ) SIGNAL_RELEASE = QtCore.pyqtSignal( int ) SIGNAL_MASKSET = QtCore.pyqtSignal( str ) SIGNAL_EDIT = QtCore.pyqtSignal( bool ) SIGNAL_RESET = QtCore.pyqtSignal( bool ) # Init def __init__( self, parent ): super( Panel_Mask, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, render_height ) def Init_Variables( self ): # Widget self.press = False self.widget_width = 0 self.widget_height = 0 self.w2 = 0 self.h2 = 0 self.press = False self.zoom = False self.pressure = 0 self.input_pressure = 0.5 # Events self.origin_x = -10 self.origin_y = -10 self.event_x = -10 self.event_y = -10 # Mask self.mask_edit = True # Path self.directory = None self.file_format = ".png" self.files = [ "b1", "b2", "b3", "d1", "d2", "d3", "d4", "d5", "d6", "f1", "f2", "f3", ] # Masks self.mask_path = [] self.mask_color = { "b1" : "#7f7f7f", "b2" : "#000000", "b3" : "#000000", "d1" : "#231402", "d2" : "#543713", "d3" : "#fe9f0e", "d4" : "#ffca32", "d5" : "#000000", "d6" : "#000000", "f1" : "#000000", "f2" : "#ffff96", "f3" : "#ffffff", } self.mask_alpha = { "b1" : 0.0, "b2" : 1.0, "b3" : 1.0, "d1" : 1.0, "d2" : 1.0, "d3" : 1.0, "d4" : 1.0, "d5" : 0.0, "d6" : 0.0, "f1" : 1.0, "f2" : 1.0, "f3" : 1.0, } self.mask_qpixmaps = None # Color self.hex_color = QColor( "#000000" ) # Modules self.geometry = Geometry() # Image self.qimage = QImage() # Set def Set_Size( self, widget_width, widget_height ): self.widget_width = widget_width self.widget_height = widget_height self.w2 = widget_width * 0.5 self.h2 = widget_height * 0.5 self.event_x = -10 self.event_y = -10 self.update() def Set_Directory( self, directory_plugin ): # Variables self.directory = os.path.normpath( directory_plugin ) # Update self.Update_Path( os.path.join( self.directory, "SPHERE" ) ) self.update() def Set_Edit( self, boolean ): self.mask_edit = boolean self.update() # Update def Update_Path( self, mask_set ): self.mask_path = [] for i in range( 0, len( self.files ) ): path = os.path.normpath( str( mask_set ) + "\\" + self.files[i] + self.file_format ) qpixmap = QPixmap( path ) if qpixmap.isNull() == False: self.mask_path.append( path ) else: self.mask_path.append( None ) if None not in self.mask_path: self.mask_qpixmaps = self.Pixmap_Composite( self.mask_path, self.mask_color, self.mask_alpha ) else: self.mask_qpixmaps = None self.update() def Update_Color( self, mask_color, mask_alpha ): self.mask_color = mask_color self.mask_alpha = mask_alpha self.mask_qpixmaps = self.Pixmap_Composite( self.mask_path, self.mask_color, self.mask_alpha ) self.update() # Calculations def Pixmap_Composite( self, mask_path, mask_color, mask_alpha ): # Pre Compose pixmaps to display qpixmaps = [] for i in range( 0, len( mask_path ) ): pixmap_1 = QPixmap.fromImage( QImage( mask_path[i] ) ) image = QImage( pixmap_1.width(), pixmap_1.height(), QImage.Format_ARGB32_Premultiplied ) color = QColor( self.mask_color[self.files[i]] ) color.setAlphaF( self.mask_alpha[self.files[i]] ) image.fill( color ) pixmap_2 = QPixmap.fromImage( image ) painter = QPainter() painter.begin( pixmap_1 ) painter.setCompositionMode( QPainter.CompositionMode_SourceIn ) painter.drawImage( 0, 0, pixmap_2.toImage() ) painter.end() qpixmaps.append( pixmap_1 ) # Return return qpixmaps # Mouse Interaction def mousePressEvent( self, event ): # Input ex = event.x() ey = event.y() self.origin_x = ex self.origin_y = ey self.press = True self.qimage = self.grab().toImage() # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Color( ex, ey, self.qimage ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Color( ex, ey, self.qimage ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): pass # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): self.press = False self.Context_Menu( event ) # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): pass self.update() def mouseMoveEvent( self, event ): # Input ex = event.x() ey = event.y() self.event_x = ex self.event_y = ey self.press = True # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Color( ex, ey, self.qimage ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Color( ex, ey, self.qimage ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): pass # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): pass self.update() def mouseDoubleClickEvent( self, event ): # Input ex = event.x() ey = event.y() self.event_x = ex self.event_y = ey self.press = True # LMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton ): self.Cursor_Color( ex, ey, self.qimage ) # LMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.LeftButton ): self.zoom = True self.Cursor_Color( ex, ey, self.qimage ) if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.LeftButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.LeftButton ): pass # RMB Neutral if ( event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.RightButton ): pass # RMB Modifiers if ( event.modifiers() == QtCore.Qt.ShiftModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.ControlModifier and event.buttons() == QtCore.Qt.RightButton ): pass if ( event.modifiers() == QtCore.Qt.AltModifier and event.buttons() == QtCore.Qt.RightButton ): pass self.update() def mouseReleaseEvent( self, event ): # Input self.event_x = event.x() self.event_y = event.y() # Variables self.press = False self.zoom = False self.pressure = 0 # Updates self.SIGNAL_RELEASE.emit( 0 ) self.update() def Cursor_Color( self, ex, ey, qimage ): # Pixel Color pixel = qimage.pixelColor( ex, ey ) code = pixel.name() self.hex_color = QColor( code ) # Emit self.SIGNAL_VALUE.emit( code ) # Context def Context_Menu( self, event ): # Variables sub_folder = [name for name in os.listdir( self.directory ) if os.path.isdir( os.path.join( self.directory, name ) )] # Menu if self.press == False: # Menu cmenu = QMenu( self ) # Mask Sets cmenu_maps = cmenu.addMenu( "Maps" ) actions = {} for i in range( 0, len( sub_folder ) ): actions[i] = cmenu_maps.addAction( sub_folder[i] ) # Edit cmenu_edit = cmenu.addAction( "Edit" ) cmenu_edit.setCheckable( True ) cmenu_edit.setChecked( self.mask_edit ) # Reset cmenu_reset = cmenu.addAction( "Reset" ) # Actions action = cmenu.exec_( self.mapToGlobal( event.pos() ) ) # Triggers for i in range( 0, len( sub_folder ) ): if action == actions[i]: path = os.path.join( self.directory, sub_folder[i] ) self.Update_Path( path ) self.SIGNAL_MASKSET.emit( path ) break if action == cmenu_edit: self.SIGNAL_EDIT.emit( not self.mask_edit ) if action == cmenu_reset: self.SIGNAL_RESET.emit( True ) # Tablet Interaction def tabletEvent( self, event ): self.pressure = event.pressure() self.update() # Paint def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Background painter.setPen( QtCore.Qt.NoPen ) bw = QLinearGradient( 0, 0, 0, self.widget_height ) bw.setColorAt( 0.000, QColor( 0, 0, 0, 0 ) ) # White bw.setColorAt( 1.000, QColor( 0, 0, 0, 100 ) ) # Black painter.setBrush( QBrush( bw ) ) painter.drawRect( 0,0, self.widget_width,self.widget_height ) # Draw Pixmaps if self.mask_qpixmaps != None: for i in range( 0, len( self.mask_qpixmaps ) ): qpixmap = self.mask_qpixmaps[i] if qpixmap.isNull() == False: render = qpixmap.scaled( self.widget_width, self.widget_height, Qt.KeepAspectRatio, Qt.FastTransformation ) else: render = QPixmap( 1,1 ).scaled( self.widget_width, self.widget_height, Qt.KeepAspectRatio, Qt.FastTransformation ) w = render.width() h = render.height() px = int( self.w2 - ( w * 0.5 ) ) py = int( self.h2 - ( h * 0.5 ) ) painter.drawPixmap( px, py, render ) # Cursor size = 10 zoom_size = 100 margin_size = 10 if ( self.press == True and self.zoom == True ): size = zoom_size Cursor_Zoom( self, painter, size, margin_size ) elif( self.pressure > self.input_pressure ): size = zoom_size * self.pressure Cursor_Zoom( self, painter, size, margin_size ) else: Cursor_Normal( self, painter, size ) #endregion #region Channels ################################################################### class Channel_Slider( QWidget ): SIGNAL_VALUE = QtCore.pyqtSignal( dict ) SIGNAL_RELEASE = QtCore.pyqtSignal( bool ) SIGNAL_STOPS = QtCore.pyqtSignal( int ) SIGNAL_TEXT = QtCore.pyqtSignal( str ) # Init def __init__( self, parent ): super( Channel_Slider, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, 100 ) def Init_Variables( self ): # Modules self.geometry = Geometry() # Widget self.widget_width = 1 self.widget_height = 1 # Display self.origin_x = 0 self.origin_stops = 0 # Variables self.mode = "LINEAR" # "LINEAR" "CIRCULAR" "MIXER" self.minimum = 0 self.half = 0.5 self.maximum = 1 self.stops = 1 self.value = 0 self.colors = None self.alpha = 1 self.index = None # Relay def Set_Mode( self, mode ): self.mode = mode self.update() def Set_Size( self, widget_width, widget_height ): self.widget_width = widget_width self.widget_height = widget_height self.update() def Set_Limits( self, minimum, half, maximum ): self.minimum = minimum self.half = half self.maximum = maximum self.update() def Set_Colors( self, colors, alpha ): self.colors = colors self.alpha = alpha self.update() def Set_Stops( self, stops ): self.stops = stops self.update() def Set_Value( self, value ): self.value = value * self.widget_width self.update() def Set_Index( self, index ): self.index = index # Interaction def mousePressEvent( self, event ): # Start Event self.origin_x = event.x() self.origin_stops = self.stops # LMB Neutral if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Emit_Value_Linear( event ) # LMB Modifier if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ShiftModifier ): pass if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ControlModifier ): self.Snap_Stop( event ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.AltModifier ): self.Stops_Shift( event ) self.update() def mouseMoveEvent( self, event ): # LMB Neutral if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Emit_Value_Linear( event ) # LMB Modifier if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ShiftModifier ): pass if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ControlModifier ): self.Snap_Stop( event ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.AltModifier ): self.Stops_Shift( event ) self.update() def mouseDoubleClickEvent( self, event ): # LMB Neutral if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Emit_Value_Linear( event ) # LMB Modifier if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ShiftModifier ): pass if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ControlModifier ): self.Snap_Half( event ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.AltModifier ): self.Stops_Reset() self.update() def mouseReleaseEvent( self, event ): # Update self.SIGNAL_TEXT.emit( "" ) self.SIGNAL_RELEASE.emit( True ) self.update() # Functions def Mouse_Position( self, event ): # Event read pos_x = event.pos().x() # Calculations if ( self.mode == "LINEAR" or self.mode == "MIXER" ): value = self.geometry.Limit_Range( pos_x, 0, self.widget_width ) if self.mode == "CIRCULAR": value = self.Loop_Hue( pos_x, self.widget_width ) return value def Loop_Hue( self, value, limit ): if value < 0: delta = value - limit units = abs( int( delta / limit ) ) value = value + ( limit*units ) if value > limit: units = abs( int( value / limit ) ) value = value - ( limit*units ) return value # Value def Emit_Value_Linear( self, event ): # Mouse self.value = self.Mouse_Position( event ) # Calculations percent = self.value / self.widget_width text = str( round( percent*100,2 ) ) + " %" # Emission self.SIGNAL_VALUE.emit( { "index":self.index, "value":percent } ) self.SIGNAL_TEXT.emit( text ) # Snap def Snap_Half( self, event ): self.value = self.half * self.widget_width self.SIGNAL_VALUE.emit( { "index":self.index, "value":self.half } ) self.SIGNAL_TEXT.emit( "50 %" ) def Snap_Stop( self, event ): # Mouse value = self.Mouse_Position( event ) # Calculations unit = self.widget_width / self.stops distances = [] for i in range( 0, self.stops+1 ): dist = self.geometry.Trig_2D_Points_Distance( value, 0, ( unit * i ), 0 ) distances.append( dist ) value_min = min( distances ) index = distances.index( value_min ) self.value = unit * index percent = self.value / self.widget_width text = str( round( percent*100,2 ) ) + " %" # Emission self.SIGNAL_VALUE.emit( { "index":self.index, "value":percent } ) self.SIGNAL_TEXT.emit( text ) # Stops def Stops_Shift( self, event ): # Variables minimum = 2 divisions = 100 unit = 50 # Calculations delta = event.x() - self.origin_x value = int( delta / unit ) self.stops = self.geometry.Limit_Range( self.origin_stops + value, minimum, divisions ) # Emission self.SIGNAL_STOPS.emit( self.stops ) self.SIGNAL_TEXT.emit( "snap " + str( self.stops ) ) def Stops_Reset( self ): # Set Number if self.mode == "LINEAR": self.stops = 4 if self.mode == "CIRCULAR": self.stops = 6 if self.mode == "MIXER": self.stops = 2 # Emission self.SIGNAL_STOPS.emit( self.stops ) # Wheel Events def wheelEvent( self, event ): delta_y = event.angleDelta().y() if delta_y > 20: num = 1 if delta_y < -20: num = -1 # Calculate if ( self.mode == "LINEAR" or self.mode == "MIXER" ): self.value = self.geometry.Limit_Range( self.value + num, 0, self.widget_width ) if self.mode == "CIRCULAR": self.value = self.Loop_Hue( self.value + num, self.widget_width ) percent = self.value / self.widget_width text = str( round( percent*100,2 ) ) + " %" # Emit self.SIGNAL_VALUE.emit( { "index":self.index, "value":percent } ) self.SIGNAL_TEXT.emit( text ) self.update() # Paint Style def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Variables ww = self.widget_width hh = self.widget_height w1 = ww - 1 w2 = ww - 2 h1 = hh - 1 h4 = hh - 4 # Background Style painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_alpha ) ) painter.drawRect( 0, 0, self.widget_width, self.widget_height ) # Stops painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_1 ) ) if self.stops <= 0 : painter.drawRect( 0, 0, self.widget_width, 1 ) else: for i in range( 0, self.stops ): percent = int( self.widget_width * ( i / self.stops ) ) painter.drawRect( percent, 0, 1, 1 ) painter.drawRect( self.widget_width * 1 - 1, 0, 1, 1 ) # Draw Colors Gradient if self.colors != None: painter.setPen( QtCore.Qt.NoPen ) grad = QLinearGradient( 0, 0, self.widget_width, 0 ) number = len( self.colors ) for i in range( 0, number ): grad.setColorAt( round( i / number, 3 ), QColor( int( self.colors[i][0] * 255 ), int( self.colors[i][1] * 255 ), int( self.colors[i][2] * 255 ), int( self.alpha * 255 ) ) ) painter.setBrush( QBrush( grad ) ) square = QPolygon( [ QPoint( 1, 1 ), QPoint( w1, 1 ), QPoint( w1, h1 ), QPoint( 1, h1 ), ] ) painter.drawPolygon( square ) # Cursor value = int( self.value ) bl = value - 3 br = value + 3 wl = value - 1 wr = value + 1 top1 = 0 bot1 = self.widget_height top2 = 1 bot2 = self.widget_height - 1 # Black Square painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_black ) ) black = QPolygon( [ QPoint( bl, top1 ), QPoint( bl, bot1 ), QPoint( br, bot1 ), QPoint( br, top1 ), ] ) painter.drawPolygon( black ) # White Square painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_white ) ) white = QPolygon( [ QPoint( wl, top2 ), QPoint( wl, bot2 ), QPoint( wr, bot2 ), QPoint( wr, top2 ), ] ) painter.drawPolygon( white ) class Channel_Selection( QWidget ): SIGNAL_VALUE = QtCore.pyqtSignal( dict ) SIGNAL_RELEASE = QtCore.pyqtSignal( bool ) SIGNAL_RESET = QtCore.pyqtSignal( int ) # Init def __init__( self, parent ): super( Channel_Selection, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, 100 ) def Init_Variables( self ): # Modules self.geometry = Geometry() # Widget self.widget_width = 1 self.widget_height = 1 # Variables self.mode = "LINEAR" # "LINEAR" "CIRCULAR" self.value = 0 self.colors = None self.alpha = 1 self.sele = None self.sele_origin = None self.marker = None # Relay def Set_Mode( self, mode ): self.mode = mode self.update() def Set_Size( self, widget_width, widget_height ): self.widget_width = widget_width self.widget_height = widget_height self.update() # Update def Update_Value( self, value ): self.value = value self.update() def Update_Colors( self, colors, alpha ): self.colors = colors self.alpha = alpha self.update() def Update_Selection( self, sele ): self.sele = sele self.update() # Interaction def mousePressEvent( self, event ): # Event ex = event.x() # Variables self.sele_origin = self.sele QtCore.qDebug( "self.sele_origin = " + str( self.sele_origin ) ) # LMB Neutral if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.marker = self.Marker_Index( ex, "ALL" ) QtCore.qDebug( "self.marker = " + str( self.marker ) ) self.Cursor_Position( ex ) # LMB Modifier if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ShiftModifier ): self.marker = self.Marker_Index( ex, "1" ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ControlModifier ): self.marker = self.Marker_Index( ex, "0" ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.AltModifier ): pass # Reset if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == ( QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier ) ): self.Cursor_Reset() self.update() def mouseMoveEvent( self, event ): # Event ex = event.x() # LMB Neutral if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Cursor_Position( ex ) # LMB Modifier if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ShiftModifier ): self.Cursor_Position( ex ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ControlModifier ): self.Cursor_Position( ex ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.AltModifier ): pass # Reset if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == ( QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier ) ): self.Cursor_Reset() self.update() def mouseDoubleClickEvent( self, event ): # Event ex = event.x() # LMB Neutral if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Cursor_Position( ex ) # LMB Modifier if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ShiftModifier ): self.Cursor_Position( ex ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.ControlModifier ): self.Cursor_Position( ex ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.AltModifier ): pass # Reset if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == ( QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier ) ): self.Cursor_Reset() self.update() def mouseReleaseEvent( self, event ): # Variables self.marker = None # Update self.SIGNAL_RELEASE.emit( True ) self.update() # Cursor def Cursor_Position( self, ex ): if ( self.mode != None ): # Interaction limit = 20 if self.marker[0] <= limit: # Variables index = self.marker[1] ww = self.widget_width vv = self.value * ww # Markers Normal l0 = vv - self.sele_origin["l0"] * ww l1 = vv - self.sele_origin["l1"] * ww r1 = vv + self.sele_origin["r1"] * ww r0 = vv + self.sele_origin["r0"] * ww # Markers Negative n_l0 = vv - self.sele_origin["l0"] * ww + ww n_l1 = vv - self.sele_origin["l1"] * ww + ww n_r1 = vv + self.sele_origin["r1"] * ww - ww n_r0 = vv + self.sele_origin["r0"] * ww - ww # Calculations if index == "l0": delta = ex - l0 value = l0 + delta if self.mode == "CIRCULAR": value = self.geometry.Limit_Range( value, n_r0, l1 ) else: value = self.geometry.Limit_Range( value, 0, l1 ) percentage = ( vv - value ) / self.widget_width self.sele["l0"] = percentage if index == "l1": delta = ex - l1 value = l1 + delta if self.mode == "CIRCULAR": value = self.geometry.Limit_Range( value, l0, vv ) else: value = self.geometry.Limit_Range( value, l0, vv ) percentage = ( vv - value ) / self.widget_width self.sele["l1"] = percentage if index == "r1": delta = ex - r1 value = r1 + delta if self.mode == "CIRCULAR": value = self.geometry.Limit_Range( value, vv, r0 ) else: value = self.geometry.Limit_Range( value, vv, r0 ) percentage = ( value - vv ) / self.widget_width self.sele["r1"] = percentage if index == "r0": delta = ex - r0 value = r0 + delta if self.mode == "CIRCULAR": value = self.geometry.Limit_Range( value, r1, n_l0 ) else: value = self.geometry.Limit_Range( value, r1, ww ) percentage = ( value - vv ) / self.widget_width self.sele["r0"] = percentage if index == "n_l0": delta = ex - l0 value = l0 + delta if self.mode == "CIRCULAR": value = self.geometry.Limit_Range( value, n_r0, l1 ) - ww else: value = self.geometry.Limit_Range( value, 0, l1 ) - ww percentage = ( vv - value - ww ) / self.widget_width self.sele["l0"] = percentage if index == "n_l1": delta = ex - l1 value = l1 + delta if self.mode == "CIRCULAR": value = self.geometry.Limit_Range( value, l0, vv ) - ww else: value = self.geometry.Limit_Range( value, l0, vv ) - ww percentage = ( vv - value -ww ) / self.widget_width self.sele["l1"] = percentage if index == "n_r1": delta = ex - r1 value = r1 + delta if self.mode == "CIRCULAR": value = self.geometry.Limit_Range( value, vv, r0 ) + ww else: value = self.geometry.Limit_Range( value, vv, r0 ) + ww percentage = ( value - vv - ww ) / self.widget_width self.sele["r1"] = percentage if index == "n_r0": delta = ex - r0 value = r0 + delta if self.mode == "CIRCULAR": value = self.geometry.Limit_Range( value, r1, n_l0 ) + ww else: value = self.geometry.Limit_Range( value, r1, ww ) + ww percentage = ( value - vv - ww ) / self.widget_width self.sele["r0"] = percentage self.SIGNAL_VALUE.emit( self.sele ) self.update() def Cursor_Reset( self ): if self.mode != None: self.SIGNAL_RESET.emit( 0 ) # Marker def Marker_Index( self, ex, mode ): # Markers Normal ww = self.widget_width vv = self.value * ww l0 = vv - self.sele["l0"] * ww l1 = vv - self.sele["l1"] * ww r1 = vv + self.sele["r1"] * ww r0 = vv + self.sele["r0"] * ww # Markers Negative n_l0 = vv - self.sele["l0"] * ww + ww n_l1 = vv - self.sele["l1"] * ww + ww n_r1 = vv + self.sele["r1"] * ww - ww n_r0 = vv + self.sele["r0"] * ww - ww # Distance d_ml0 = abs( ex - l0 ) d_ml1 = abs( ex - l1 ) d_mr1 = abs( ex - r1 ) d_mr0 = abs( ex - r0 ) d_n_ml0 = abs( ex - n_l0 ) d_n_ml1 = abs( ex - n_l1 ) d_n_mr1 = abs( ex - n_r1 ) d_n_mr0 = abs( ex - n_r0 ) # Index if mode == "ALL": distance = [ ( d_n_ml0, "n_l0" ), ( d_n_ml1, "n_l1" ), ( d_ml0, "l0" ), ( d_ml1, "l1" ), ( d_mr1, "r1" ), ( d_mr0, "r0" ), ( d_n_mr1, "n_r1" ), ( d_n_mr0, "n_r0" ) ] elif mode == "1": distance = [ ( d_n_ml1, "n_l1" ), ( d_ml1, "l1" ), ( d_mr1, "r1" ), ( d_n_mr1, "n_r1" ) ] elif mode == "0": distance = [ ( d_n_ml0, "n_l0" ), ( d_ml0, "l0" ), ( d_mr0, "r0" ), ( d_n_mr0, "n_r0" ) ] distance.sort() marker = distance[0] # Return return marker # Paint Style def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Variables ww = self.widget_width hh = self.widget_height w1 = ww - 1 w2 = ww - 2 h1 = hh - 1 h4 = hh - 4 h5 = hh - 5 h6 = hh - 6 # Slider if ( self.value != None and self.sele != None ): # Variables 1 vv = int( self.value * self.widget_width ) l0 = int( vv - self.sele["l0"] * self.widget_width ) l1 = int( vv - self.sele["l1"] * self.widget_width ) r1 = int( vv + self.sele["r1"] * self.widget_width ) r0 = int( vv + self.sele["r0"] * self.widget_width ) # Variables 2 l0i = int( l0 + 1 ) l1i = int( l1 + 1 ) r1i = int( r1 - 1 ) r0i = int( r0 - 1 ) # Background Style painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_alpha ) ) painter.drawRect( 0, h5, self.widget_width, 5 ) # Markers Range left_full = QPolygon( [ QPoint( vv, 1 ), QPoint( vv, 2 ), QPoint( l1i, 2 ), QPoint( l1i, h6 ), QPoint( vv, h6 ), QPoint( vv, h5 ), QPoint( l1, h5 ), QPoint( l1, 1 ), ] ) right_full = QPolygon( [ QPoint( vv, 1 ), QPoint( vv, 2 ), QPoint( r1i, 2 ), QPoint( r1i, h6 ), QPoint( vv, h6 ), QPoint( vv, h5 ), QPoint( r1, h5 ), QPoint( r1, 1 ), ] ) left_tri = QPolygon( [ QPoint( l1, 1 ), QPoint( l1, h5 ), QPoint( l0, h5 ), QPoint( l0, 1 ), ] ) right_tri = QPolygon( [ QPoint( r1, 1 ), QPoint( r1, h5 ), QPoint( r0, h5 ), QPoint( r0, 1 ), ] ) left_cap = QPolygon( [ QPoint( l0, 1 ), QPoint( l0, h5 ), QPoint( l0+1, h5 ), QPoint( l0+1, 1 ), ] ) right_cap = QPolygon( [ QPoint( r0, 1 ), QPoint( r0, h5 ), QPoint( r0-1, h5 ), QPoint( r0-1, 1 ), ] ) painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( QColor( self.color_1 ) ) ) painter.drawPolygon( left_full ) painter.drawPolygon( right_full ) painter.setBrush( QBrush( QColor( self.color_1 ), Qt.BDiagPattern ) ) painter.drawPolygon( left_tri ) painter.setBrush( QBrush( QColor( self.color_1 ), Qt.FDiagPattern ) ) painter.drawPolygon( right_tri ) painter.setBrush( QBrush( QColor( self.color_1 ) ) ) painter.drawPolygon( left_cap ) painter.setBrush( QBrush( QColor( self.color_1 ) ) ) painter.drawPolygon( right_cap ) if self.mode == "CIRCULAR": # Polygons neg_left_full = QPolygon( [ QPoint( vv - ww, 1 ), QPoint( vv - ww, 2 ), QPoint( r1i - ww, 2 ), QPoint( r1i - ww, h6 ), QPoint( vv - ww, h6 ), QPoint( vv - ww, h5 ), QPoint( r1 - ww, h5 ), QPoint( r1 - ww, 1 ), ] ) neg_right_full = QPolygon( [ QPoint( vv + ww, 1 ), QPoint( vv + ww, 2 ), QPoint( l1i + ww, 2 ), QPoint( l1i + ww, h6 ), QPoint( vv + ww, h6 ), QPoint( vv + ww, h5 ), QPoint( l1 + ww, h5 ), QPoint( l1 + ww, 1 ), ] ) neg_left_tri = QPolygon( [ QPoint( r1 - ww, 1 ), QPoint( r1 - ww, h5 ), QPoint( r0 - ww, h5 ), QPoint( r0 - ww, 1 ), ] ) neg_right_tri = QPolygon( [ QPoint( l1 + ww, 1 ), QPoint( l1 + ww, h5 ), QPoint( l0 + ww, h5 ), QPoint( l0 + ww, 1 ), ] ) neg_left_cap = QPolygon( [ QPoint( r0 - ww, 1 ), QPoint( r0 - ww, h5 ), QPoint( r0-1 - ww, h5 ), QPoint( r0-1 - ww, 1 ), ] ) neg_right_cap = QPolygon( [ QPoint( l0 + ww, 1 ), QPoint( l0 + ww, h5 ), QPoint( l0+1 + ww, h5 ), QPoint( l0+1 + ww, 1 ), ] ) # Draw painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( QColor( self.color_1 ) ) ) painter.drawPolygon( neg_left_full ) painter.drawPolygon( neg_right_full ) painter.setBrush( QBrush( QColor( self.color_1 ), Qt.FDiagPattern ) ) painter.drawPolygon( neg_left_tri ) painter.setBrush( QBrush( QColor( self.color_1 ), Qt.BDiagPattern ) ) painter.drawPolygon( neg_right_tri ) painter.setBrush( QBrush( QColor( self.color_1 ) ) ) painter.drawPolygon( neg_left_cap ) painter.setBrush( QBrush( QColor( self.color_1 ) ) ) painter.drawPolygon( neg_right_cap ) # Draw Colors Gradient if self.colors != None: painter.setPen( QtCore.Qt.NoPen ) grad = QLinearGradient( 0, 0, self.widget_width, 0 ) number = len( self.colors ) for i in range( 0, number ): grad.setColorAt( round( i / number, 3 ), QColor( int( self.colors[i][0] * 255 ), int( self.colors[i][1] * 255 ), int( self.colors[i][2] * 255 ), int( self.alpha * 255 ) ) ) painter.setBrush( QBrush( grad ) ) square = QPolygon( [ QPoint( 1, h4 ), QPoint( w1, h4 ), QPoint( w1, h1 ), QPoint( 1, h1 ), ] ) painter.drawPolygon( square ) # Cursor if self.value != None: vv = int( self.value * self.widget_width ) bl = vv - 3 br = vv + 3 wl = vv - 1 wr = vv + 1 top1 = 0 bot1 = self.widget_height top2 = 1 bot2 = self.widget_height - 1 # Black Square painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_black ) ) black = QPolygon( [ QPoint( bl, top1 ), QPoint( bl, bot1 ), QPoint( br, bot1 ), QPoint( br, top1 ), ] ) painter.drawPolygon( black ) # White Square painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_white ) ) white = QPolygon( [ QPoint( wl, top2 ), QPoint( wl, bot2 ), QPoint( wr, bot2 ), QPoint( wr, top2 ), ] ) painter.drawPolygon( white ) #endregion #region Pin ######################################################################## class Pin_Color( QWidget ): SIGNAL_APPLY = QtCore.pyqtSignal( int ) SIGNAL_SAVE = QtCore.pyqtSignal( int ) SIGNAL_CLEAN = QtCore.pyqtSignal( int ) SIGNAL_ALPHA = QtCore.pyqtSignal( float ) SIGNAL_TEXT = QtCore.pyqtSignal( str ) # Init def __init__( self, parent ): super( Pin_Color, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( 500, 500 ) def Init_Variables( self ): # Widget self.widget_width = 1 self.widget_height = 1 # Events self.event_x = 0 self.event_y = 0 # States self.press = False self.active = False self.index = None # Color self.color = None # None == no color self.alpha = None # None == no alpha # Modifier Keys self.mod_1 = [ QtCore.Qt.ShiftModifier, QtCore.Qt.ControlModifier, QtCore.Qt.AltModifier ] self.mod_3 = ( QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier ) # Modules self.geometry = Geometry() # Relay def Set_Size( self, widget_width, widget_height ): self.widget_width = widget_width self.widget_height = widget_height self.update() def Set_Index( self, index ): self.index = index def Set_Active( self, active ): self.active = active self.update() def Set_Color( self, color ): self.color = QColor( color ) self.update() def Set_Clean( self ): self.color = None self.update() def Set_Alpha( self, alpha ): self.alpha = alpha self.update() # Interaction def mousePressEvent( self, event ): # Variables self.press = True # LMB Neutral if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.press = "operation" self.Swipe_String( event ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() in self.mod_1 ): self.press = "alpha" self.Swipe_Alpha( event ) # RMB Neutral if ( event.buttons() == QtCore.Qt.RightButton and event.modifiers() == QtCore.Qt.NoModifier ): self.press = False self.Context_Menu( event ) # Clean if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == self.mod_3 ): self.press = True self.SIGNAL_CLEAN.emit( self.index ) self.update() def mouseMoveEvent( self, event ): # LMB Neutral if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Swipe_String( event ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() in self.mod_1 ): self.Swipe_Alpha( event ) # RMB Neutral if ( event.buttons() == QtCore.Qt.RightButton and event.modifiers() == QtCore.Qt.NoModifier ): pass # Clean if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == self.mod_3 ): pass self.update() def mouseDoubleClickEvent( self, event ): # LMB Neutral if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == QtCore.Qt.NoModifier ): self.Swipe_String( event ) if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() in self.mod_1 ): self.Swipe_Alpha( event ) # RMB Neutral if ( event.buttons() == QtCore.Qt.RightButton and event.modifiers() == QtCore.Qt.NoModifier ): pass # Clean if ( event.buttons() == QtCore.Qt.LeftButton and event.modifiers() == self.mod_3 ): pass self.update() def mouseReleaseEvent( self, event ): # Input self.Swipe_Operation( event ) self.press = False # Update self.update() # Swipe Operations def Swipe_String( self, event ): if self.press == "operation": # Input ex = event.x() ey = event.y() # Signals if ( ( ex >= 0 and ex <= self.widget_width ) and ey <= 0 ): self.SIGNAL_TEXT.emit( "APPLY" ) elif ( ( ex >= 0 and ex <= self.widget_width ) and ey >= self.widget_height ): self.SIGNAL_TEXT.emit( "SAVE" ) else: self.SIGNAL_TEXT.emit( "" ) def Swipe_Operation( self, event ): if self.press == "operation": # Input ex = event.x() ey = event.y() # Signals if ( ( ex >= 0 and ex <= self.widget_width ) and ey <= 0 ): self.SIGNAL_APPLY.emit( self.index ) elif ( ( ex >= 0 and ex <= self.widget_width ) and ey >= self.widget_height ): self.SIGNAL_SAVE.emit( self.index ) self.SIGNAL_TEXT.emit( "" ) def Swipe_Alpha( self, event ): if ( self.press == "alpha" and self.alpha != None ): # Input ex = event.x() ex = self.geometry.Limit_Range( ex, 0, self.widget_width ) # Signals if self.widget_width != 0: self.alpha = ex / self.widget_width self.SIGNAL_ALPHA.emit( self.alpha ) self.update() # Context def Context_Menu( self, event ): if self.press == False: # Menu cmenu = QMenu( self ) # Actions cmenu_apply = cmenu.addAction( "APPLY" ) cmenu_save = cmenu.addAction( "SAVE" ) cmenu_clean = cmenu.addAction( "CLEAN" ) action = cmenu.exec_( self.mapToGlobal( QPoint( 10,5 ) ) ) # Triggers if action == cmenu_apply: self.SIGNAL_APPLY.emit( self.index ) if action == cmenu_save: self.SIGNAL_SAVE.emit( self.index ) if action == cmenu_clean: self.SIGNAL_CLEAN.emit( self.index ) # Paint Style def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Background painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.color_alpha ) ) painter.drawRect( 0, 0, self.widget_width, self.widget_height ) # Active if self.active == True: # Dot w1 = 1 w3 = self.widget_width-2 # Color h2 = 2 h4 = self.widget_height-3 else: # Dot w1 = 0 w3 = 1 # Color h2 = 1 h4 = self.widget_height-2 if self.alpha == None: a1 = 0 else: a1 = 2 # Dot painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( QColor( self.color_1 ) ) ) painter.drawRect( w1, 0, w3, 1 ) # Color painter.setPen( QtCore.Qt.NoPen ) if self.color != None: qcolor = self.color if self.alpha != None: qcolor.setAlphaF( self.alpha ) painter.setBrush( QBrush( qcolor ) ) painter.drawRect( QRectF( 1, h2, self.widget_width - 2, h4 - a1 ) ) # Alpha if self.alpha != None: painter.setPen( QtCore.Qt.NoPen ) # Background painter.setBrush( QBrush( self.color_2 ) ) painter.drawRect( QRectF( 1, self.widget_height-a1, self.widget_width - 2, a1 ) ) # Slider painter.setBrush( QBrush( self.color_1 ) ) painter.drawRect( QRectF( 1, self.widget_height-a1, ( self.widget_width*self.alpha ) - 2, a1 ) ) #endregion #region Ink ####################################################################### class Sample_Map( QWidget ): SIGNAL_INDEX = QtCore.pyqtSignal( int ) # Init def __init__( self, parent ): super( Sample_Map, self ).__init__( parent ) self.Init_Variables() def sizeHint( self ): return QtCore.QSize( render_width, render_height ) def Init_Variables( self ): # Widget self.widget_width = 1 self.widget_height = 1 # Render self.index = None self.qpixmap = None self.qcolor = QColor( 0, 0, 0, 50 ) # Relay def Set_Size( self, widget_width, widget_height ): self.widget_width = widget_width self.widget_height = widget_height self.update() def Set_Sample( self, index, qpixmap, qcolor ): self.index = index self.qpixmap = qpixmap if qcolor == True: self.qcolor = QColor( 255, 0, 0, 50 ) else: self.qcolor = QColor( 0, 0, 0, 50 ) self.update() # Context Menu def contextMenuEvent( self, event ): # Variables check = self.index != None and self.qpixmap != None # Menu cmenu = QMenu( self ) # Actions if check == True: cmenu_insert = cmenu.addAction( "Insert Map" ) # Map action = cmenu.exec_( self.mapToGlobal( event.pos() ) ) # Triggers if check == True: if action == cmenu_insert: self.SIGNAL_INDEX.emit( self.index ) # Paint Style def paintEvent( self, event ): # Theme krita_theme( self ) # Painter painter = QPainter( self ) painter.setRenderHint( QtGui.QPainter.Antialiasing, True ) # Background painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QBrush( self.qcolor ) ) painter.drawRect( 0, 0, self.widget_width, self.widget_height ) # Render try: # Scale render_pix = self.qpixmap.scaled( self.widget_width, self.widget_height, Qt.KeepAspectRatio, Qt.FastTransformation ) # Variables rw = render_pix.width() rh = render_pix.height() px = int( ( self.widget_width * 0.5 ) - ( rw * 0.5 ) ) py = int( ( self.widget_height * 0.5 ) - ( rh * 0.5 ) ) # Painter settings painter.setPen( QtCore.Qt.NoPen ) painter.setBrush( QtCore.Qt.NoBrush ) # Image painter.drawPixmap( px, py, render_pix ) except: self.Render_None( painter, event ) def Render_None( self, painter, event ): painter.setPen( self.color_1 ) painter.setFont( QFont( 'Liberation Mono', 10 ) ) painter.drawText( event.rect(), Qt.AlignCenter, "None" ) #endregion