Color Panel for Nuke

Color Panel for Nuke in PySide & QtPy

Download a panel in PySide & QtPy to customize the color of nodes, backdrops, notes in Nuke

Download a panel done in PySide & QtPy

In this quick explanation, I will show you how to create the same panel in PySide, PySide2 and QtPy to change colors of nodes and other elements for Nuke (special thanks to Alexey Kuchinski).

You can download the panel for Nuke here:
Download Color Panel







At this link you can find an example with QtPy.



Useful Links



QT

Why QtPy?

Qt is a cross-platform application framework and widget toolkit for creating classic and embedded graphical user interfaces, and applications that run on various software and hardware platforms with little or no change in the underlying codebase, while still being a native application with native capabilities and speed. Qt is currently being developed both by The Qt Company, a publicly listed company, and the Qt Project under open-source governance, involving individual developers and firms working to advance Qt. Qt is available with both proprietary[4] and open source GPL 2.0, GPL 3.0, and LGPL 3.0 licenses.

PyQt provides two different APIs, the first of which provides QStrings, QVariants, etc as is in Python. The new API 2 provides automatic conversion between the Qt classes and respective native Python datatypes and is much more Pythonic in nature. PyQt on Python 2.x defaults to API 1, while PyQt on Python 3 defaults to API 2. PySide only supports PyQt's API 2 for details. Therefore Qt classes such as QStrings, QStringLists, and QVariants are not available on PySide. Instead, you should simply use native Python datatypes. If you're porting code from PyQt, you might want to first modify the PyQt code to use API 2 (using a sip.setapi(class,ver) call before importing PyQt4), and only after getting that change working, change the imports to use PySide instead.



How to Install Qt.py

Click here to read how to install QtPy

from Qt import QtWidgets
from Qt import QtCompat
from Qt import QtGui
from Qt import QtCore




PySide


Why PySide and PySide2?

PySide2 is a Python binding of the cross-platform GUI toolkit Qt, currently developed by The Qt Company under the Qt for Python project. It is one of the alternatives to the standard library package Tkinter. Like Qt, PySide2 is free software. The project started out using Boost.Python from the Boost C++ Libraries for the bindings and later switched to the binding generator Shiboken to reduce the size of the binaries and the memory footprint.

PySide was released under the LGPL in August 2009 by Nokia, the former owners of the Qt toolkit, after Nokia failed to reach an agreement with PyQt developers Riverbank Computing[7] to change its licensing terms to include LGPL as an alternative license.

Work is currently underway to officially launch PySide2 as a Qt product, after all the effort on porting PySide to work with Qt 5.

PySide2 supports Linux/X11, Mac OS X, Windows and Maemo. Support for Android is currently being added by the PySide community.




PySide2 and Nuke11?

When launching a Python script with Nuke 11, if your Nuke script requires Python's PySide module then Nuke will throw an exception which will prevent it from opening.
With the release of Nuke 11, there have been significant library updates done with the aim of making Nuke VFX Reference Platform 2017 complaint.

In the case of the PySide, this was updated from PySide 1.2.2 to PySide 2.0 within the Nuke core libraries, so it can now be imported as Pyside2 rather than PySide. For more information on the library versions shipped with Nuke 11. As a result of this update from PySide to PySide2, starting with Nuke 11 PySide modules are no longer callable.

So, that's why in the new Python script it's always better to check which version of Nuke you are using.

#EXAMPLE 01
if nuke.NUKE_VERSION_MAJOR < 11:
	#Nuke < 11
	from PySide import QtGui, QtCore
else:
	#Nuke >= 11
	from PySide2 import QtGui, QtCore


#---------------------------------------------------------
#EXAMPLE 02
if nuke.NUKE_VERSION_MAJOR < 11:
	from PySide.QtGui import *
	from PySide.QtCore import *
	from PySide.QtUiTools import QUiLoader
	from PySide import QtCore, QtGui, QtUiTools, QtGui as QtWidgets
else:
	from PySide2.QtGui import *
	from PySide2.QtCore import *
	from PySide2.QtUiTools import QUiLoader
	from PySide2 import QtCore, QtGui, QtUiTools, QtGui, QtWidgets



Example with Nodes

This is the final Color Panel created in this tutorial.


With this panel, selecting the nodes you can modify the colors. This is just an example:


Select all the nodes, click on the green button and see the result.


Now let's select a color in the first Grid of the Color Panel. We will get all the nodes of the same color of the Merge nodes.


You can also select a custom color






Example with Backdrops and StickyNotes

Selecting Backdrops and StickyNotes, you can also change the color of them
















Do you want to change colors?

You can change colors of the ColorPanel if you don't like them. Just open the file ColorPanel.ui with QtDesigner. Click on the buttons and change the attribute background-color.







Tricks

1. Load file .ui from the same folder of the file .py:

thisFileDir = os.path.dirname(os.path.realpath(__file__))
file_interface = os.path.join(thisFileDir, "ColorPanel.ui")

2. If you wanna have the Panel always on top:

self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)

3. Change color of a Node (color has to be hex):

nuke.selectedNode().knob('tile_color').setValue(312364891)

4. Convert from RGB to Hex. RGB is a number in the range 0-1

weird = int('%02x%02x%02x%02x' % (red*255,green*255,blue*255,1),16)

5. Get color of the Node Class

nuke.defaultNodeColor(n.Class())
#if result is 0, then it's the default colors

6. Get color of the Node

nuke.selectedColor()['tile_color'].getValue()
#if result is 0, then it's the default colors

7. Set color of the Node

for n in nuke.selectedNodes(): 
	n['tile_color'].setValue('insert INT color in hex')

8. Open a website in Python

import webbrowser
webbrowser.open('http://www.andreageremia.it/tutorial_color_panel.html')

9. Read selected Node from Group

#nuke.selectedNode().selectedNodes()

		try:
		nodeSelected = nuke.selectedNode()
		if (nodeSelected.Class() == "Group" and nodeSelected.selectedNodes()):
			#if node selected is a group, then check if anything is selected inside
			nuke.message("Changing color of Nodes inside Group: " + nodeSelected.name())
			nodeSelected = nodeSelected.selectedNodes()
		else:
			nodeSelected = nuke.selectedNodes()
	except:
		nuke.message("SELECT A NODE.\nIf you want to change color of Nodes inside a Group, please select also the Group in the Node Graph.")







Code in QtPy

Differences between the version QtPy and PySide have the highlight:
#****************************************************************
#************************ THIS IS DIFFERENT**************************
#****************************************************************

import os
import nuke


#****************************************************************
#*********************** THIS IS DIFFERENT***********************
#****************************************************************
from Qt import QtWidgets
from Qt import QtCompat
from Qt import QtGui
from Qt import QtCore


import webbrowser


#read the file UI from the same folder
thisFileDir = os.path.dirname(os.path.realpath(__file__))
file_interface = os.path.join(thisFileDir, "ColorPanel.ui")

global color_copied
color_copied = None

#---------------------------------------------------------
#****************************************************************
#*********************** THIS IS DIFFERENT***********************
#****************************************************************
class MyWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MyWindow, self).__init__(parent)
        self.main_widget = QtCompat.loadUi(file_interface)
        self.setCentralWidget(self.main_widget)
        self.setWindowTitle("ColorPanel")
        #windows always on top
        self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        
        #set Fixed Sizes
        self.setFixedWidth(370)
        self.setFixedHeight(678)

        self.load_ui()


    def load_ui(self):
            self.button1 = self.main_widget.findChild(QtWidgets.QPushButton, 'button1')
            self.button1.clicked.connect(lambda: self.changeColorNode(self.button1.styleSheet()))

            self.button2 = self.main_widget.findChild(QtWidgets.QPushButton, 'button2')
            self.button2.clicked.connect(lambda: self.changeColorNode(self.button2.styleSheet()))

            self.button3 = self.main_widget.findChild(QtWidgets.QPushButton, 'button3')
            self.button3.clicked.connect(lambda: self.changeColorNode(self.button3.styleSheet()))

            self.button4 = self.main_widget.findChild(QtWidgets.QPushButton, 'button4')
            self.button4.clicked.connect(lambda: self.changeColorNode(self.button4.styleSheet()))

            self.button5 = self.main_widget.findChild(QtWidgets.QPushButton, 'button5')
            self.button5.clicked.connect(lambda: self.changeColorNode(self.button5.styleSheet()))

            self.button6 = self.main_widget.findChild(QtWidgets.QPushButton, 'button6')
            self.button6.clicked.connect(lambda: self.changeColorNode(self.button6.styleSheet()))  

            #.
            #.
            #.

            self.button56 = self.main_widget.findChild(QtWidgets.QPushButton, 'button56')
            self.button56.clicked.connect(lambda: self.changeColorNode(self.button56.styleSheet()))

            self.button57 = self.main_widget.findChild(QtWidgets.QPushButton, 'button57')
            self.button57.clicked.connect(lambda: self.changeColorNode(self.button57.styleSheet()))

            self.button58 = self.main_widget.findChild(QtWidgets.QPushButton, 'button58')
            self.button58.clicked.connect(lambda: self.changeColorNode(self.button58.styleSheet()))    

            self.button59 = self.main_widget.findChild(QtWidgets.QPushButton, 'button59')
            self.button59.clicked.connect(lambda: self.changeColorNode(self.button59.styleSheet()))

            self.button60 = self.main_widget.findChild(QtWidgets.QPushButton, 'button60')
            self.button60.clicked.connect(lambda: self.changeColorNode(self.button60.styleSheet()))


            self.customColor = self.main_widget.findChild(QtWidgets.QPushButton, 'customColor')
            self.customColor.clicked.connect(self.changeColorNodeCustom)
            
            self.restoreColor = self.main_widget.findChild(QtWidgets.QPushButton, 'restoreColor')
            self.restoreColor.clicked.connect(self.restoreOriginalColor)
            
            self.copyButton = self.main_widget.findChild(QtWidgets.QPushButton, 'copyButton')
            self.copyButton.clicked.connect(self.copy_color)

            self.pasteButton = self.main_widget.findChild(QtWidgets.QPushButton, 'pasteButton')
            self.pasteButton.clicked.connect(self.paste_color)
			
            self.infoButton = self.main_widget.findChild(QtWidgets.QPushButton, 'infoButton')
            self.infoButton.clicked.connect(self.website)
			
            self.checkboxClose = self.main_widget.findChild(QtWidgets.QCheckBox, 'checkboxClose')


    #if clicked button
    def changeColorNode(self, color):
        color = color.replace('background-color: rgb','').replace(';','').replace('(','').replace(')','').replace(' ','')
        r,g,b = color.split(',')
        #convert from RGB to Hex
        red = float(float(r)/255)
        green = float(float(g)/255)
        blue = float(float(b)/255)
        hexColour = '%02x' % (red*255) + '%02x' % (green*255) + '%02x' % (blue*255)
        weird = int('%02x%02x%02x%02x' % (red*255,green*255,blue*255,1),16)
        #weird = int(hexColour,16)
        print "COLOR: " + str(weird)
        #print self.button.palette().color(QtGui.QPalette.Background).getRgb()

        for node in nuke.selectedNodes():
            node.knob('tile_color').setValue(weird)
        
        self.closeWindow()

#---------------------------------------------------------
    #if clicked custom color button
    def changeColorNodeCustom(self):
        col = nuke.getColor()

        if col:
            for n in nuke.selectedNodes():
                n['tile_color'].setValue(col)
                n['gl_color'].setValue(col)
	print "COLOR: " + str(col)
        
        self.closeWindow()

#---------------------------------------------------------
    #COPY
    def copy_color(self):
	global color_copied
        if len(nuke.selectedNodes()) >= 1:
	    n = nuke.selectedNode()
	    if n['tile_color'].getValue()==0:
		color_copied = nuke.defaultNodeColor(n.Class())
	    else:
		color_copied = int(n['tile_color'].getValue())
        else:
	    nuke.message("select a node")
	    if color_copied is None:
	    	color_copied = 0
	print "COPIED COLOR: " + str(color_copied)


    #PASTE
    def paste_color(self):
	if color_copied is not None: 
		for n in nuke.selectedNodes(): 
		    n['tile_color'].setValue(color_copied)
#---------------------------------------------------------         
	#www
    def website(self):
        webbrowser.open('http://www.andreageremia.it/tutorial_color_panel.html')  # Go to the website
#---------------------------------------------------------
    #close window
    def closeWindow(self):
        if self.checkboxClose.isChecked():
            self.close()
    
#GOOOOOOOOOOOOOOOOOO!
def colorPanel():
    my_window = MyWindow()
    my_window.show()


#colorPanel()







Code in PySide and PySide2

import os
import nuke
import webbrowser

#****************************************************************
#*********************** THIS IS DIFFERENT***********************
#****************************************************************
if nuke.NUKE_VERSION_MAJOR < 11:
	from PySide.QtGui import *
	from PySide.QtCore import *
	from PySide.QtUiTools import QUiLoader
	from PySide import QtCore, QtGui, QtUiTools, QtGui as QtWidgets
else:
	from PySide2.QtGui import *
	from PySide2.QtCore import *
	from PySide2.QtUiTools import QUiLoader
	from PySide2 import QtCore, QtGui, QtUiTools, QtGui, QtWidgets



thisFileDir = os.path.dirname(os.path.realpath(__file__))
file_interface = os.path.join(thisFileDir, "ColorPanel.ui")


global color_copied
color_copied = None

#****************************************************************
#*********************** THIS IS DIFFERENT***********************
#****************************************************************
class MyWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MyWindow, self).__init__(parent)
        self.main_widget = self.load_ui(file_interface)
        self.setCentralWidget(self.main_widget)
        self.setWindowTitle("ColorPanel")
        #windows always on top
        self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        
        #set Fixed Sizes
        self.setFixedWidth(361)
        self.setFixedHeight(628)

        self.load_ui_elements()


#****************************************************************
#*********************** THIS IS DIFFERENT***********************
#****************************************************************
    def load_ui(self, ui_file):
        loader = QUiLoader()
        file = QFile(ui_file)
        file.open(QFile.ReadOnly)
        myWidget = loader.load(file, None)
        file.close()
        return myWidget


    def load_ui(self):
            self.button1 = self.main_widget.findChild(QtWidgets.QPushButton, 'button1')
            self.button1.clicked.connect(lambda: self.changeColorNode(self.button1.styleSheet()))

            self.button2 = self.main_widget.findChild(QtWidgets.QPushButton, 'button2')
            self.button2.clicked.connect(lambda: self.changeColorNode(self.button2.styleSheet()))

            self.button3 = self.main_widget.findChild(QtWidgets.QPushButton, 'button3')
            self.button3.clicked.connect(lambda: self.changeColorNode(self.button3.styleSheet()))

            self.button4 = self.main_widget.findChild(QtWidgets.QPushButton, 'button4')
            self.button4.clicked.connect(lambda: self.changeColorNode(self.button4.styleSheet()))

            self.button5 = self.main_widget.findChild(QtWidgets.QPushButton, 'button5')
            self.button5.clicked.connect(lambda: self.changeColorNode(self.button5.styleSheet()))

            self.button6 = self.main_widget.findChild(QtWidgets.QPushButton, 'button6')
            self.button6.clicked.connect(lambda: self.changeColorNode(self.button6.styleSheet()))  

            #.
            #.
            #.

            self.button56 = self.main_widget.findChild(QtWidgets.QPushButton, 'button56')
            self.button56.clicked.connect(lambda: self.changeColorNode(self.button56.styleSheet()))

            self.button57 = self.main_widget.findChild(QtWidgets.QPushButton, 'button57')
            self.button57.clicked.connect(lambda: self.changeColorNode(self.button57.styleSheet()))

            self.button58 = self.main_widget.findChild(QtWidgets.QPushButton, 'button58')
            self.button58.clicked.connect(lambda: self.changeColorNode(self.button58.styleSheet()))    

            self.button59 = self.main_widget.findChild(QtWidgets.QPushButton, 'button59')
            self.button59.clicked.connect(lambda: self.changeColorNode(self.button59.styleSheet()))

            self.button60 = self.main_widget.findChild(QtWidgets.QPushButton, 'button60')
            self.button60.clicked.connect(lambda: self.changeColorNode(self.button60.styleSheet()))


            self.customColor = self.main_widget.findChild(QtWidgets.QPushButton, 'customColor')
            self.customColor.clicked.connect(self.changeColorNodeCustom)
            
            self.restoreColor = self.main_widget.findChild(QtWidgets.QPushButton, 'restoreColor')
            self.restoreColor.clicked.connect(self.restoreOriginalColor)
            
            self.copyButton = self.main_widget.findChild(QtWidgets.QPushButton, 'copyButton')
            self.copyButton.clicked.connect(self.copy_color)

            self.pasteButton = self.main_widget.findChild(QtWidgets.QPushButton, 'pasteButton')
            self.pasteButton.clicked.connect(self.paste_color)
			
            self.infoButton = self.main_widget.findChild(QtWidgets.QPushButton, 'infoButton')
            self.infoButton.clicked.connect(self.website)
			
            self.checkboxClose = self.main_widget.findChild(QtWidgets.QCheckBox, 'checkboxClose')


    #if clicked button
    def changeColorNode(self, color):
        color = color.replace('background-color: rgb','').replace(';','').replace('(','').replace(')','').replace(' ','')
        r,g,b = color.split(',')
        #convert from RGB to Hex
        red = float(float(r)/255)
        green = float(float(g)/255)
        blue = float(float(b)/255)
        hexColour = '%02x' % (red*255) + '%02x' % (green*255) + '%02x' % (blue*255)
        weird = int('%02x%02x%02x%02x' % (red*255,green*255,blue*255,1),16)
        #weird = int(hexColour,16)
        print weird
        #print self.button.palette().color(QtGui.QPalette.Background).getRgb()

        for node in nuke.selectedNodes():
            node.knob('tile_color').setValue(weird)
        
        self.closeWindow()

#---------------------------------------------------------
    #if clicked custom color button
    def changeColorNodeCustom(self):
        col = nuke.getColor()

        if col:
            for n in nuke.selectedNodes():
                n['tile_color'].setValue(col)
                n['gl_color'].setValue(col)
	print "COLOR: " + str(col)
        
        self.closeWindow()
#---------------------------------------------------------

    #COPY
    def copy_color(self):
	global color_copied
        if len(nuke.selectedNodes()) >= 1:
	    n = nuke.selectedNode()
	    if n['tile_color'].getValue()==0:
		color_copied = nuke.defaultNodeColor(n.Class())
	    else:
		color_copied = int(n['tile_color'].getValue())
        else:
	    nuke.message("select a node")
	    if color_copied is None:
	    	color_copied = 0
	print "COPIED COLOR: " + str(color_copied)


    #PASTE
    def paste_color(self):
	if color_copied is not None: 
		for n in nuke.selectedNodes(): 
		    n['tile_color'].setValue(color_copied)
#---------------------------------------------------------         
	#www
    def website(self):
        webbrowser.open('http://www.andreageremia.it/tutorial_color_panel.html')  # Go to the website
#---------------------------------------------------------
    #close window
    def closeWindow(self):
        if self.checkboxClose.isChecked():
            self.close()
    
#GOOOOOOOOOOOOOOOOOO!
def colorPanel():
    my_window = MyWindow()
    my_window.show()


#colorPanel()

This code creates the Color Panel, but you have to have in the same folder also the file ui

Leave a comment