Update for API change: scene.cursor_location -> scene.cursor.location
[blender-addons.git] / paint_palette.py
blobb9c57365b7f0b87d27e3fd345a7439836ad77afc
1 # paint_palette.py (c) 2011 Dany Lebel (Axon_D)
3 # ##### BEGIN GPL LICENSE BLOCK #####
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # ##### END GPL LICENSE BLOCK #####
22 bl_info = {
23 "name": "Paint Palettes",
24 "author": "Dany Lebel (Axon D)",
25 "version": (0, 9, 4),
26 "blender": (2, 63, 0),
27 "location": "Image Editor and 3D View > Any Paint mode > Color Palette or Weight Palette panel",
28 "description": "Palettes for color and weight paint modes",
29 "warning": "",
30 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
31 "Scripts/Paint/Palettes",
32 "category": "Paint",
35 """
36 This add-on brings palettes to the paint modes.
38 * Color Palette for Image Painting, Texture Paint and Vertex Paint modes.
39 * Weight Palette for the Weight Paint mode.
41 Set a number of colors (or weights according to the mode) and then associate it
42 with the brush by using the button under the color.
43 """
45 import bpy
46 from bpy.types import (
47 Operator,
48 Menu,
49 Panel,
50 PropertyGroup,
52 from bpy.props import (
53 BoolProperty,
54 FloatProperty,
55 FloatVectorProperty,
56 IntProperty,
57 StringProperty,
58 PointerProperty,
59 CollectionProperty,
63 def update_panels():
64 pp = bpy.context.scene.palette_props
65 current_color = pp.colors[pp.current_color_index].color
66 pp.color_name = pp.colors[pp.current_color_index].name
67 brush = current_brush()
68 brush.color = current_color
69 pp.index = pp.current_color_index
72 def sample():
73 pp = bpy.context.scene.palette_props
74 current_color = pp.colors[pp.current_color_index]
75 brush = current_brush()
76 current_color.color = brush.color
77 return None
80 def current_brush():
81 context = bpy.context
82 if context.area.type == 'VIEW_3D' and context.vertex_paint_object:
83 brush = context.tool_settings.vertex_paint.brush
84 elif context.area.type == 'VIEW_3D' and context.image_paint_object:
85 brush = context.tool_settings.image_paint.brush
86 elif context.area.type == 'IMAGE_EDITOR' and context.space_data.mode == 'PAINT':
87 brush = context.tool_settings.image_paint.brush
88 else:
89 brush = None
90 return brush
93 def update_weight_value():
94 pp = bpy.context.scene.palette_props
95 tt = bpy.context.tool_settings
96 tt.unified_paint_settings.weight = pp.weight_value
97 return None
100 def check_path_return():
101 from os.path import normpath
102 preset_path = bpy.path.abspath(bpy.context.scene.palette_props.presets_folder)
103 paths = normpath(preset_path)
105 return paths if paths else ""
108 class PALETTE_MT_menu(Menu):
109 bl_label = "Presets"
110 preset_subdir = ""
111 preset_operator = "palette.load_gimp_palette"
113 def path_menu(self, searchpaths, operator, props_default={}):
114 layout = self.layout
115 # hard coded to set the operators 'filepath' to the filename.
116 import os
117 import bpy.utils
119 layout = self.layout
121 if bpy.data.filepath == "":
122 layout.label(text="*Please save the .blend file first*")
123 return
125 if not searchpaths[0]:
126 layout.label(text="* Missing Paths *")
127 return
129 # collect paths
130 files = []
131 for directory in searchpaths:
132 files.extend([(f, os.path.join(directory, f)) for f in os.listdir(directory)])
134 files.sort()
136 for f, filepath in files:
138 if f.startswith("."):
139 continue
140 # do not load everything from the given folder, only .gpl files
141 if f[-4:] != ".gpl":
142 continue
144 preset_name = bpy.path.display_name(f)
145 props = layout.operator(operator, text=preset_name)
147 for attr, value in props_default.items():
148 setattr(props, attr, value)
150 props.filepath = filepath
151 if operator == "palette.load_gimp_palette":
152 props.menu_idname = self.bl_idname
154 def draw_preset(self, context):
155 paths = check_path_return()
156 self.path_menu([paths], self.preset_operator)
158 draw = draw_preset
161 class PALETTE_OT_load_gimp_palette(Operator):
162 """Execute a preset"""
163 bl_idname = "palette.load_gimp_palette"
164 bl_label = "Load a Gimp palette"
166 filepath: StringProperty(
167 name="Path",
168 description="Path of the .gpl file to load",
169 default=""
171 menu_idname: StringProperty(
172 name="Menu ID Name",
173 description="ID name of the menu this was called from",
174 default=""
177 def execute(self, context):
178 from os.path import basename
179 import re
180 filepath = self.filepath
182 palette_props = bpy.context.scene.palette_props
183 palette_props.current_color_index = 0
185 # change the menu title to the most recently chosen option
186 preset_class = getattr(bpy.types, self.menu_idname)
187 preset_class.bl_label = bpy.path.display_name(basename(filepath))
189 palette_props.columns = 0
190 error_palette = False # errors found
191 error_import = [] # collect exception messages
192 start_color_index = 0 # store the starting line for color definitions
194 if filepath[-4:] != ".gpl":
195 error_palette = True
196 else:
197 gpl = open(filepath, "r")
198 lines = gpl.readlines()
199 palette_props.notes = ''
200 has_color = False
201 for index_0, line in enumerate(lines):
202 if not line or (line[:12] == "GIMP Palette"):
203 pass
204 elif line[:5] == "Name:":
205 palette_props.palette_name = line[5:]
206 elif line[:8] == "Columns:":
207 palette_props.columns = int(line[8:])
208 elif line[0] == "#":
209 palette_props.notes += line
210 elif line[0] == "\n":
211 pass
212 else:
213 has_color = True
214 start_color_index = index_0
215 break
216 i = -1
217 if has_color:
218 for i, ln in enumerate(lines[start_color_index:]):
219 try:
220 palette_props.colors[i]
221 except IndexError:
222 palette_props.colors.add()
223 try:
224 # get line - find keywords with re.split, remove the empty ones with filter
225 get_line = list(filter(None, re.split(r'\t+|\s+', ln.rstrip('\n'))))
226 extract_colors = get_line[:3]
227 get_color_name = [str(name) for name in get_line[3:]]
228 color = [float(rgb) / 255 for rgb in extract_colors]
229 palette_props.colors[i].color = color
230 palette_props.colors[i].name = " ".join(get_color_name) or "Color " + str(i)
231 except Exception as e:
232 error_palette = True
233 error_import.append(".gpl file line: {}, error: {}".format(i + 1 + start_color_index, e))
234 pass
236 exceeding = i + 1
237 while palette_props.colors.__len__() > exceeding:
238 palette_props.colors.remove(exceeding)
240 if has_color:
241 update_panels()
242 gpl.close()
243 pass
245 message = "Loaded palette from file: {}".format(filepath)
247 if error_palette:
248 message = "Not supported palette format for file: {}".format(filepath)
249 if error_import:
250 message = "Some of the .gpl palette data can not be parsed. See Console for more info"
251 print("\n[Paint Palette]\nOperator: palette.load_gimp_palette\nErrors: %s\n" %
252 ('\n'.join(error_import)))
254 self.report({'INFO'}, message)
256 return {'FINISHED'}
259 class WriteGimpPalette():
260 """Base preset class, only for subclassing
261 subclasses must define
262 - preset_values
263 - preset_subdir """
264 bl_options = {'REGISTER'} # only because invoke_props_popup requires
266 name: StringProperty(
267 name="Name",
268 description="Name of the preset, used to make the path name",
269 maxlen=64,
270 options={'SKIP_SAVE'},
271 default=""
273 remove_active: BoolProperty(
274 default=False,
275 options={'HIDDEN'}
278 @staticmethod
279 def as_filename(name): # could reuse for other presets
280 for char in " !@#$%^&*(){}:\";'[]<>,.\\/?":
281 name = name.replace(char, '_')
282 return name.lower().strip()
284 def execute(self, context):
285 import os
286 pp = bpy.context.scene.palette_props
288 if hasattr(self, "pre_cb"):
289 self.pre_cb(context)
291 preset_menu_class = getattr(bpy.types, self.preset_menu)
292 target_path = check_path_return()
294 if not target_path:
295 self.report({'WARNING'}, "Failed to create presets path")
296 return {'CANCELLED'}
298 if not os.path.exists(target_path):
299 self.report({'WARNING'},
300 "Failure to open the saved Palettes Folder. Check if the path exists")
301 return {'CANCELLED'}
303 if not self.remove_active:
304 if not self.name:
305 self.report({'INFO'},
306 "No name is given for the preset entry. Operation Cancelled")
307 return {'FINISHED'}
309 filename = self.as_filename(self.name)
310 filepath = os.path.join(target_path, filename) + ".gpl"
311 file_preset = open(filepath, 'wb')
312 gpl = "GIMP Palette\n"
313 gpl += "Name: %s\n" % filename
314 gpl += "Columns: %d\n" % pp.columns
315 gpl += pp.notes
316 if pp.colors.items():
317 for i, color in enumerate(pp.colors):
318 gpl += "%3d%4d%4d %s" % (color.color.r * 255, color.color.g * 255,
319 color.color.b * 255, color.name + '\n')
320 file_preset.write(bytes(gpl, 'UTF-8'))
322 file_preset.close()
324 pp.palette_name = filename
325 preset_menu_class.bl_label = bpy.path.display_name(filename)
327 self.report({'INFO'}, "Created Palette: {}".format(filepath))
329 else:
330 preset_active = preset_menu_class.bl_label
331 filename = self.as_filename(preset_active)
333 filepath = os.path.join(target_path, filename) + ".gpl"
335 if not filepath or not os.path.exists(filepath):
336 self.report({'WARNING'}, "Preset could not be found. Operation Cancelled")
337 self.reset_preset_name(preset_menu_class, pp)
338 return {'CANCELLED'}
340 if hasattr(self, "remove"):
341 self.remove(context, filepath)
342 else:
343 try:
344 os.remove(filepath)
345 self.report({'INFO'}, "Deleted palette: {}".format(filepath))
346 except:
347 import traceback
348 traceback.print_exc()
350 self.reset_preset_name(preset_menu_class, pp)
352 if hasattr(self, "post_cb"):
353 self.post_cb(context)
355 return {'FINISHED'}
357 @staticmethod
358 def reset_preset_name(presets, props):
359 # XXX, still stupid!
360 presets.bl_label = "Presets"
361 props.palette_name = ""
363 def check(self, context):
364 self.name = self.as_filename(self.name)
366 def invoke(self, context, event):
367 if not self.remove_active:
368 wm = context.window_manager
369 return wm.invoke_props_dialog(self)
371 return self.execute(context)
374 class PALETTE_OT_preset_add(WriteGimpPalette, Operator):
375 bl_idname = "palette.preset_add"
376 bl_label = "Add Palette Preset"
377 preset_menu = "PALETTE_MT_menu"
378 bl_description = "Add a Palette Preset"
380 preset_defines = []
381 preset_values = []
382 preset_subdir = "palette"
385 class PALETTE_OT_add_color(Operator):
386 bl_idname = "palette_props.add_color"
387 bl_label = ""
388 bl_description = "Add a Color to the Palette"
390 def execute(self, context):
391 pp = bpy.context.scene.palette_props
392 new_index = 0
393 if pp.colors.items():
394 new_index = pp.current_color_index + 1
395 pp.colors.add()
397 last = pp.colors.__len__() - 1
399 pp.colors.move(last, new_index)
400 pp.current_color_index = new_index
401 sample()
402 update_panels()
404 return {'FINISHED'}
407 class PALETTE_OT_remove_color(Operator):
408 bl_idname = "palette_props.remove_color"
409 bl_label = ""
410 bl_description = "Remove Selected Color"
412 @classmethod
413 def poll(cls, context):
414 pp = bpy.context.scene.palette_props
415 return bool(pp.colors.items())
417 def execute(self, context):
418 pp = context.scene.palette_props
419 i = pp.current_color_index
420 pp.colors.remove(i)
422 if pp.current_color_index >= pp.colors.__len__():
423 pp.index = pp.current_color_index = pp.colors.__len__() - 1
425 return {'FINISHED'}
428 class PALETTE_OT_sample_tool_color(Operator):
429 bl_idname = "palette_props.sample_tool_color"
430 bl_label = ""
431 bl_description = "Sample Tool Color"
433 def execute(self, context):
434 pp = context.scene.palette_props
435 brush = current_brush()
436 pp.colors[pp.current_color_index].color = brush.color
438 return {'FINISHED'}
441 class IMAGE_OT_select_color(Operator):
442 bl_idname = "paint.select_color"
443 bl_label = ""
444 bl_description = "Select this color"
445 bl_options = {'UNDO'}
447 color_index: IntProperty()
449 def invoke(self, context, event):
450 palette_props = context.scene.palette_props
451 palette_props.current_color_index = self.color_index
453 update_panels()
455 return {'FINISHED'}
458 def color_palette_draw(self, context):
459 palette_props = context.scene.palette_props
461 layout = self.layout
463 row = layout.row(align=True)
464 row.menu("PALETTE_MT_menu", text=PALETTE_MT_menu.bl_label)
465 row.operator("palette.preset_add", text="", icon='ADD').remove_active = False
466 row.operator("palette.preset_add", text="", icon='REMOVE').remove_active = True
468 col = layout.column(align=True)
469 row = col.row(align=True)
470 row.operator("palette_props.add_color", icon='ADD')
471 row.prop(palette_props, "index")
472 row.operator("palette_props.remove_color", icon="PANEL_CLOSE")
474 row = col.row(align=True)
475 row.prop(palette_props, "columns")
476 if palette_props.colors.items():
477 layout = col.box()
478 row = layout.row(align=True)
479 row.prop(palette_props, "color_name")
480 row.operator("palette_props.sample_tool_color", icon="COLOR")
482 laycol = layout.column(align=False)
484 if palette_props.columns:
485 columns = palette_props.columns
486 else:
487 columns = 16
489 for i, color in enumerate(palette_props.colors):
490 if not i % columns:
491 row1 = laycol.row(align=True)
492 row1.scale_y = 0.8
493 row2 = laycol.row(align=True)
494 row2.scale_y = 0.8
496 active = True if i == palette_props.current_color_index else False
497 icons = "LAYER_ACTIVE" if active else "LAYER_USED"
498 row1.prop(palette_props.colors[i], "color", event=True, toggle=True)
499 row2.operator("paint.select_color", text=" ",
500 emboss=active, icon=icons).color_index = i
502 layout = self.layout
503 row = layout.row()
504 row.prop(palette_props, "presets_folder", text="")
507 class BrushButtonsPanel():
508 bl_space_type = 'IMAGE_EDITOR'
509 bl_region_type = 'UI'
511 @classmethod
512 def poll(cls, context):
513 sima = context.space_data
514 toolsettings = context.tool_settings.image_paint
515 return sima.show_paint and toolsettings.brush
518 class PaintPanel():
519 bl_space_type = 'VIEW_3D'
520 bl_region_type = 'TOOLS'
521 bl_category = 'Tools'
523 @staticmethod
524 def paint_settings(context):
525 ts = context.tool_settings
527 if context.vertex_paint_object:
528 return ts.vertex_paint
529 elif context.weight_paint_object:
530 return ts.weight_paint
531 elif context.texture_paint_object:
532 return ts.image_paint
533 return None
536 class IMAGE_PT_color_palette(BrushButtonsPanel, Panel):
537 bl_label = "Color Palette"
538 bl_options = {'DEFAULT_CLOSED'}
540 def draw(self, context):
541 color_palette_draw(self, context)
544 class VIEW3D_PT_color_palette(PaintPanel, Panel):
545 bl_label = "Color Palette"
546 bl_options = {'DEFAULT_CLOSED'}
548 @classmethod
549 def poll(cls, context):
550 return (context.image_paint_object or context.vertex_paint_object)
552 def draw(self, context):
553 color_palette_draw(self, context)
556 class VIEW3D_OT_select_weight(Operator):
557 bl_idname = "paint.select_weight"
558 bl_label = ""
559 bl_description = "Select this weight value slot"
560 bl_options = {'UNDO'}
562 weight_index: IntProperty()
564 def current_weight(self):
565 pp = bpy.context.scene.palette_props
566 if self.weight_index == 0:
567 weight = pp.weight_0
568 elif self.weight_index == 1:
569 weight = pp.weight_1
570 elif self.weight_index == 2:
571 weight = pp.weight_2
572 elif self.weight_index == 3:
573 weight = pp.weight_3
574 elif self.weight_index == 4:
575 weight = pp.weight_4
576 elif self.weight_index == 5:
577 weight = pp.weight_5
578 elif self.weight_index == 6:
579 weight = pp.weight_6
580 elif self.weight_index == 7:
581 weight = pp.weight_7
582 elif self.weight_index == 8:
583 weight = pp.weight_8
584 elif self.weight_index == 9:
585 weight = pp.weight_9
586 elif self.weight_index == 10:
587 weight = pp.weight_10
588 return weight
590 def invoke(self, context, event):
591 palette_props = context.scene.palette_props
592 palette_props.current_weight_index = self.weight_index
594 if self.weight_index == 0:
595 weight = palette_props.weight_0
596 elif self.weight_index == 1:
597 weight = palette_props.weight_1
598 elif self.weight_index == 2:
599 weight = palette_props.weight_2
600 elif self.weight_index == 3:
601 weight = palette_props.weight_3
602 elif self.weight_index == 4:
603 weight = palette_props.weight_4
604 elif self.weight_index == 5:
605 weight = palette_props.weight_5
606 elif self.weight_index == 6:
607 weight = palette_props.weight_6
608 elif self.weight_index == 7:
609 weight = palette_props.weight_7
610 elif self.weight_index == 8:
611 weight = palette_props.weight_8
612 elif self.weight_index == 9:
613 weight = palette_props.weight_9
614 elif self.weight_index == 10:
615 weight = palette_props.weight_10
616 palette_props.weight = weight
618 return {'FINISHED'}
621 class VIEW3D_OT_reset_weight_palette(Operator):
622 bl_idname = "paint.reset_weight_palette"
623 bl_label = ""
624 bl_description = "Reset the active Weight slot to it's default value"
626 def execute(self, context):
627 try:
628 palette_props = context.scene.palette_props
629 dict_defs = {
630 0: 0.0, 1: 0.1, 2: 0.25,
631 3: 0.333, 4: 0.4, 5: 0.5,
632 6: 0.6, 7: 0.6666, 8: 0.75,
633 9: 0.9, 10: 1.0
635 current_idx = palette_props.current_weight_index
636 palette_props.weight = dict_defs[current_idx]
638 var_name = "weight_" + str(current_idx)
639 var_to_change = getattr(palette_props, var_name, None)
640 if var_to_change:
641 var_to_change = dict_defs[current_idx]
643 return {'FINISHED'}
645 except Exception as e:
646 self.report({'WARNING'},
647 "Reset Weight palette could not be completed (See Console for more info)")
648 print("\n[Paint Palette]\nOperator: paint.reset_weight_palette\nError: %s\n" % e)
650 return {'CANCELLED'}
653 class VIEW3D_PT_weight_palette(PaintPanel, Panel):
654 bl_label = "Weight Palette"
655 bl_options = {'DEFAULT_CLOSED'}
657 @classmethod
658 def poll(cls, context):
659 return context.weight_paint_object
661 def draw(self, context):
662 palette_props = context.scene.palette_props
664 layout = self.layout
665 row = layout.row()
666 row.prop(palette_props, "weight", slider=True)
667 box = layout.box()
669 selected_weight = palette_props.current_weight_index
670 for props in range(0, 11):
671 embossed = False if props == selected_weight else True
672 prop_name = "weight_" + str(props)
673 prop_value = getattr(palette_props, prop_name, "")
674 if props in (0, 10):
675 row = box.row(align=True)
676 elif (props + 2) % 3 == 0:
677 col = box.column(align=True)
678 row = col.row(align=True)
679 else:
680 if props == 1:
681 row = box.row(align=True)
682 row = row.row(align=True)
684 row.operator("paint.select_weight", text="%.2f" % prop_value,
685 emboss=embossed).weight_index = props
687 row = layout.row()
688 row.operator("paint.reset_weight_palette", text="Reset")
691 class PALETTE_Colors(PropertyGroup):
692 """Class for colors CollectionProperty"""
693 color: FloatVectorProperty(
694 name="",
695 description="",
696 default=(0.8, 0.8, 0.8),
697 min=0, max=1,
698 step=1, precision=3,
699 subtype='COLOR_GAMMA',
700 size=3
704 class PALETTE_Props(PropertyGroup):
706 def update_color_name(self, context):
707 pp = bpy.context.scene.palette_props
708 pp.colors[pp.current_color_index].name = pp.color_name
709 return None
711 def move_color(self, context):
712 pp = bpy.context.scene.palette_props
713 if pp.colors.items() and pp.current_color_index != pp.index:
714 if pp.index >= pp.colors.__len__():
715 pp.index = pp.colors.__len__() - 1
717 pp.colors.move(pp.current_color_index, pp.index)
718 pp.current_color_index = pp.index
719 return None
721 def update_weight(self, context):
722 pp = context.scene.palette_props
723 weight = pp.weight
724 if pp.current_weight_index == 0:
725 pp.weight_0 = weight
726 elif pp.current_weight_index == 1:
727 pp.weight_1 = weight
728 elif pp.current_weight_index == 2:
729 pp.weight_2 = weight
730 elif pp.current_weight_index == 3:
731 pp.weight_3 = weight
732 elif pp.current_weight_index == 4:
733 pp.weight_4 = weight
734 elif pp.current_weight_index == 5:
735 pp.weight_5 = weight
736 elif pp.current_weight_index == 6:
737 pp.weight_6 = weight
738 elif pp.current_weight_index == 7:
739 pp.weight_7 = weight
740 elif pp.current_weight_index == 8:
741 pp.weight_8 = weight
742 elif pp.current_weight_index == 9:
743 pp.weight_9 = weight
744 elif pp.current_weight_index == 10:
745 pp.weight_10 = weight
746 bpy.context.tool_settings.unified_paint_settings.weight = weight
747 return None
749 palette_name: StringProperty(
750 name="Palette Name",
751 default="Preset",
752 subtype='FILE_NAME'
754 color_name: StringProperty(
755 name="",
756 description="Color Name",
757 default="Untitled",
758 update=update_color_name
760 columns: IntProperty(
761 name="Columns",
762 description="Number of Columns",
763 min=0, max=16,
764 default=0
766 index: IntProperty(
767 name="Index",
768 description="Move Selected Color",
769 min=0,
770 update=move_color
772 notes: StringProperty(
773 name="Palette Notes",
774 default="#\n"
776 current_color_index: IntProperty(
777 name="Current Color Index",
778 description="",
779 default=0,
780 min=0
782 current_weight_index: IntProperty(
783 name="Current Color Index",
784 description="",
785 default=10,
786 min=-1
788 presets_folder: StringProperty(name="",
789 description="Palettes Folder",
790 subtype="DIR_PATH",
791 default="//"
793 colors: CollectionProperty(
794 type=PALETTE_Colors
796 weight: FloatProperty(
797 name="Weight",
798 description="Modify the active Weight preset slot value",
799 default=0.0,
800 min=0.0, max=1.0,
801 precision=3,
802 update=update_weight
804 weight_0: FloatProperty(
805 default=0.0,
806 min=0.0, max=1.0,
807 precision=3
809 weight_1: FloatProperty(
810 default=0.1,
811 min=0.0, max=1.0,
812 precision=3
814 weight_2: FloatProperty(
815 default=0.25,
816 min=0.0, max=1.0,
817 precision=3
819 weight_3: FloatProperty(
820 default=0.333,
821 min=0.0, max=1.0,
822 precision=3
824 weight_4: FloatProperty(
825 default=0.4,
826 min=0.0, max=1.0,
827 precision=3
829 weight_5: FloatProperty(
830 default=0.5,
831 min=0.0, max=1.0,
832 precision=3
834 weight_6: FloatProperty(
835 default=0.6,
836 min=0.0, max=1.0,
837 precision=3
839 weight_7: FloatProperty(
840 default=0.6666,
841 min=0.0, max=1.0,
842 precision=3
844 weight_8: FloatProperty(
845 default=0.75,
846 min=0.0, max=1.0,
847 precision=3
849 weight_9: FloatProperty(
850 default=0.9,
851 min=0.0, max=1.0,
852 precision=3
854 weight_10: FloatProperty(
855 default=1.0,
856 min=0.0, max=1.0,
857 precision=3
861 classes = (
862 PALETTE_MT_menu,
863 PALETTE_OT_load_gimp_palette,
864 PALETTE_OT_preset_add,
865 PALETTE_OT_add_color,
866 PALETTE_OT_remove_color,
867 PALETTE_OT_sample_tool_color,
868 IMAGE_OT_select_color,
869 IMAGE_PT_color_palette,
870 VIEW3D_PT_color_palette,
871 VIEW3D_OT_select_weight,
872 VIEW3D_OT_reset_weight_palette,
873 VIEW3D_PT_weight_palette,
874 PALETTE_Colors,
875 PALETTE_Props,
879 def register():
880 for cls in classes:
881 bpy.utils.register_class(cls)
883 bpy.types.Scene.palette_props = PointerProperty(
884 type=PALETTE_Props,
885 name="Palette Props",
886 description=""
890 def unregister():
891 for cls in reversed(classes):
892 bpy.utils.unregister_class(cls)
894 del bpy.types.Scene.palette_props
897 if __name__ == "__main__":
898 register()