Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
atticus-lv committed Jan 23, 2022
0 parents commit e0f6fed
Show file tree
Hide file tree
Showing 13 changed files with 1,202 additions and 0 deletions.
75 changes: 75 additions & 0 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
bl_info = {
"name": "Color Helper",
"author": "Atticus",
"blender": (3, 0, 0),
"version": (0, 1),
"category": "Interface",
"support": "COMMUNITY",
"doc_url": "",
"tracker_url": "",
"description": "",
'warning': "Only support Windows",
"location": "Node Editor N panel",
}

import importlib
import sys
import os
from itertools import groupby

# get folder name
__folder_name__ = __name__
__dict__ = {}

addon_dir = os.path.dirname(__file__)

# get all .py file dir
py_paths = [os.path.join(root, f) for root, dirs, files in os.walk(addon_dir) for f in files if
f.endswith('.py') and f != '__init__.py']

for path in py_paths:
name = os.path.basename(path)[:-3]
correct_path = path.replace('\\', '/')
# split dir with folder name
dir_list = [list(g) for k, g in groupby(correct_path.split('/'), lambda x: x == __folder_name__) if
not k]
# combine dir and make dict like this: 'name:folder.name'
if 'colorthief' not in dir_list[-1]:
r_name_raw = __folder_name__ + '.' + '.'.join(dir_list[-1])
__dict__[name] = r_name_raw[:-3]

# auto reload
for name in __dict__.values():
if name in sys.modules:
importlib.reload(sys.modules[name])
else:
globals()[name] = importlib.import_module(name)
setattr(globals()[name], 'modules', __dict__)

def prepare():
from addon_utils import enable
addons = [
'io_import_images_as_planes',
'io_import_dxf',
]
for addon in addons:
enable(addon)

def register():
for name in __dict__.values():
if name in sys.modules and hasattr(sys.modules[name], 'register'):
try:
sys.modules[name].register()
except ValueError: # open template file may cause this problem
pass

prepare()

def unregister():
for name in __dict__.values():
if name in sys.modules and hasattr(sys.modules[name], 'unregister'):
sys.modules[name].unregister()


if __name__ == '__main__':
register()
84 changes: 84 additions & 0 deletions ops/op_create_nodes_from_palette.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import bpy
import os
from bpy.props import EnumProperty, IntProperty, BoolProperty


class CH_OT_create_nodes_from_palette(bpy.types.Operator):
"""Create/Update shader node group from this palette\nShift to create/update and add group node to current material"""
bl_idname = 'ch.create_nodes_from_palette'
bl_label = 'Create Color From Palette'

palette_index: IntProperty()

@classmethod
def poll(cls, context):
return context.space_data.type == "NODE_EDITOR" and context.space_data.tree_type == 'ShaderNodeTree'

def _return(self, error_msg=None, info_msg=None):
if error_msg:
self.report({'ERROR'}, error_msg)
elif info_msg:
self.report({'INFO'}, info_msg)

return {'FINISHED'}

def invoke(self, context, event):
collection = context.scene.ch_palette_collection[context.scene.ch_palette_collection_index]
palette = collection.palettes[self.palette_index]
self.create_node_group(palette, create=event.shift)
return {'FINISHED'}

def create_node_group(self, palette, create):
if palette.node_group is not None:
nt = palette.node_group
for node in nt.nodes:
nt.nodes.remove(node)
else:
nt = bpy.data.node_groups.new(palette.name, 'ShaderNodeTree')
palette.node_group = nt

loc_x, loc_y = 0, 0

node_output = nt.nodes.new('NodeGroupOutput')

for i, color_item in enumerate(palette.colors):
color = color_item.color
node = nt.nodes.new('ShaderNodeRGB')
node.outputs[0].default_value = color
node.location = loc_x + i * 150, loc_y - i * 50
nt.links.new(node.outputs[0], node_output.inputs[i])

node_output.location = len(palette.colors) * 150 + 200, 0

loc_x, loc_y = bpy.context.space_data.cursor_location
bpy.ops.node.select_all(action='DESELECT')

# # Create Node Group
if bpy.context.space_data.edit_tree is None or not create: return

edit_tree = bpy.context.space_data.edit_tree

if edit_tree != nt and not (edit_tree.nodes.active and
hasattr(edit_tree.nodes.active, 'node_tree') and
getattr(edit_tree.nodes.active, 'node_tree') == nt):
group_node = edit_tree.nodes.new('ShaderNodeGroup')
group_node.node_tree = nt
group_node.location = loc_x, loc_y

bpy.ops.transform.translate('INVOKE_DEFAULT')


def draw_context(self, context):
layout = self.layout
layout.operator('ch.create_nodes_from_palette', text='Get Image Color')
layout.separator()


def register():
bpy.utils.register_class(CH_OT_create_nodes_from_palette)
# bpy.types.NODE_MT_context_menu.prepend(draw_context)


def unregister():
bpy.utils.unregister_class(CH_OT_create_nodes_from_palette)
# bpy.types.NODE_MT_context_menu.remove(draw_context)
104 changes: 104 additions & 0 deletions ops/op_create_palette.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import bpy
import os
from bpy.props import EnumProperty, StringProperty, CollectionProperty
from bpy_extras.io_utils import ExportHelper, ImportHelper


class CreatPaletteBase:

@classmethod
def poll(cls, context):
return context.space_data.type == "NODE_EDITOR" and context.space_data.tree_type == 'ShaderNodeTree'

def _return(self, error_msg=None, info_msg=None):
if error_msg:
self.report({'ERROR'}, error_msg)
elif info_msg:
self.report({'INFO'}, info_msg)

return {'FINISHED'}

def create_palette(self, palette):
if len(bpy.context.scene.ch_palette_collection) == 0:
collection = bpy.context.scene.ch_palette_collection.add()
collection.name = 'Collection'
bpy.context.scene.ch_palette_collection_index = 0

collection = bpy.context.scene.ch_palette_collection[bpy.context.scene.ch_palette_collection_index]

palette_item = collection.palettes.add()
palette_item.name = 'Palettes' + str(len(collection.palettes))
for i, color in enumerate(palette):
clr = palette_item.colors.add()
clr.color = color
return palette_item

class CH_OT_create_palette_from_palette(CreatPaletteBase, bpy.types.Operator, ImportHelper):
bl_idname = 'ch.create_palette_from_palette'
bl_label = 'Platte From Palette Files'
bl_options = {'UNDO_GROUPED'}

filename_ext = ".png"

files: CollectionProperty(type=bpy.types.PropertyGroup)

filter_glob: StringProperty(
default="*.png",
options={'HIDDEN'}
)

def execute(self, context):
from ..utils.process_image import extract_from_palette

dirname = os.path.dirname(self.filepath)
for f in self.files:
image = bpy.data.images.load(os.path.join(dirname, f.name), check_existing=False)
palette = extract_from_palette(image)

palette_item = self.create_palette(palette)
palette_item.name = f.name

bpy.data.images.remove(image)
return {'FINISHED'}


class CH_OT_create_palette_from_clipboard(CreatPaletteBase, bpy.types.Operator):
bl_idname = 'ch.create_palette_from_clipboard'
bl_label = 'Platte From Clipboard'
bl_options = {'UNDO_GROUPED'}

def execute(self, context):
from ..utils.process_image import extract_from_image
from ..utils.clipboard import Clipboard

clipboard = Clipboard()
filepath = clipboard.pull_image_from_clipboard()

if not os.path.exists(filepath) or not os.path.isfile(filepath):
return self._return(error_msg='Clipboard has no file')

image = bpy.data.images.load(filepath, check_existing=False)
channel_count = image.channels

if image.file_format not in ['JPEG', 'PNG']:
return self._return(error_msg=f'Currently, this only works for JPEG and PNG image files')
if channel_count not in [3, 4]:
return self._return(
error_msg=f"This image has {channel_count} channels, but this method can only handle 3 or 4 channels")
palette = extract_from_image(image)

self.create_palette(palette)

bpy.data.images.remove(image)

return {'FINISHED'}


def register():
bpy.utils.register_class(CH_OT_create_palette_from_palette)
bpy.utils.register_class(CH_OT_create_palette_from_clipboard)


def unregister():
bpy.utils.unregister_class(CH_OT_create_palette_from_palette)
bpy.utils.unregister_class(CH_OT_create_palette_from_clipboard)
Loading

0 comments on commit e0f6fed

Please sign in to comment.