├── README.md └── generic_note.py /README.md: -------------------------------------------------------------------------------- 1 | GenericNote 2 | =========== 3 | 4 | Out of date. 5 | 6 | 7 | A generic note addon for blender 8 | For documentation see here: http://wiki.blender.org/index.php?title=Extensions:2.6/Py/Scripts/Nodes/Generic_Note 9 | 10 | Download from here: https://github.com/ly29/GenericNote/raw/master/generic_note.py 11 | 12 | Note: The whole repoistory won't work as a Blender add on 13 | -------------------------------------------------------------------------------- /generic_note.py: -------------------------------------------------------------------------------- 1 | # ##### BEGIN GPL LICENSE BLOCK ##### 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # as published by the Free Software Foundation; either version 2 6 | # of the License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software Foundation, 15 | # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 | # 17 | # ##### END GPL LICENSE BLOCK ##### 18 | # Author Linus Yng 19 | # 20 | # pep8 21 | 22 | 23 | bl_info = { 24 | "name": "Generic Note Node", 25 | "description": "A generic note node", 26 | "author": "Linus Yng", 27 | "version": (0, 2, 0), 28 | "blender": (2, 7, 7), 29 | "location": "Node Editor, N-Panel or menu Layout", 30 | "category": "Node", 31 | "warning": "The note will not work for people without this addon", 32 | "wiki-url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Nodes/Generic_Note", 33 | } 34 | 35 | import textwrap 36 | import importlib 37 | 38 | import bpy 39 | import nodeitems_builtins 40 | from nodeitems_utils import NodeItem 41 | from nodeitems_builtins import (CompositorNodeCategory, 42 | ShaderNewNodeCategory, 43 | ShaderOldNodeCategory, 44 | TextureNodeCategory) 45 | from bpy.props import StringProperty, FloatVectorProperty, BoolProperty 46 | 47 | 48 | TEXT_WIDTH = 6 49 | 50 | # every textwrap.wrap call creates an instance of of TectWrap 51 | # we wan't to avoid overhead since draw is called very often 52 | TW = textwrap.TextWrapper() 53 | 54 | 55 | def get_lines(text_file): 56 | for line in text_file.lines: 57 | yield line.body 58 | 59 | 60 | class GenericNoteNode(bpy.types.Node): 61 | ''' Note ''' 62 | bl_idname = 'GenericNoteNode' 63 | bl_label = 'Note' 64 | bl_icon = 'OUTLINER_OB_EMPTY' 65 | 66 | @classmethod 67 | def poll(cls, ntree): 68 | return True 69 | 70 | text = StringProperty(name='text', 71 | default='', 72 | description="Text to show, if set will overide file") 73 | 74 | text_file = StringProperty(description="Textfile to show") 75 | 76 | def format_text(self): 77 | global TW 78 | out = [] 79 | if self.text: 80 | lines = self.text.splitlines() 81 | elif self.text_file: 82 | text_file = bpy.data.texts.get(self.text_file) 83 | if text_file: 84 | lines = get_lines(text_file) 85 | else: 86 | return [] 87 | else: 88 | return [] 89 | width = self.width 90 | TW.width = int(width) // TEXT_WIDTH 91 | for t in lines: 92 | out.extend(TW.wrap(t)) 93 | out.append("") 94 | return out 95 | 96 | def init(self, context): 97 | self.width = 400 98 | pref = bpy.context.user_preferences.addons[__name__].preferences 99 | self.color = pref.note_node_color[:] 100 | self.use_custom_color = True 101 | 102 | def draw_buttons(self, context, layout): 103 | has_text = self.text or self.text_file 104 | if has_text: 105 | col = layout.column(align=True) 106 | text_lines = self.format_text() 107 | for l in text_lines: 108 | if l: 109 | col.label(text=l) 110 | else: 111 | col = layout.column(align=True) 112 | col.operator("node.generic_note_from_clipboard", text="From clipboard") 113 | col.prop(self, "text", text="Text") 114 | col.prop_search(self, 'text_file', bpy.data, 'texts', text='Text file', icon='TEXT') 115 | 116 | def draw_buttons_ext(self, context, layout): 117 | layout.prop(self, "text", text="Text") 118 | layout.prop_search(self, 'text_file', bpy.data, 'texts', text='Text file', icon='TEXT') 119 | layout.operator("node.generic_note_from_clipboard", text="From clipboard") 120 | layout.operator("node.generic_note_to_text", text="To text editor") 121 | layout.operator("node.generic_note_clear") 122 | 123 | def clear(self): 124 | self.text = "" 125 | self.text_file = "" 126 | 127 | def to_text(self): 128 | text_name = "Generic Note Text" 129 | text = bpy.data.texts.get(text_name) 130 | if not text: 131 | text = bpy.data.texts.new(text_name) 132 | text.clear() 133 | text.write(self.text) 134 | 135 | 136 | class GenericNoteTextFromClipboard(bpy.types.Operator): 137 | """ 138 | Update note text from clipboard 139 | """ 140 | bl_idname = "node.generic_note_from_clipboard" 141 | bl_label = "" 142 | bl_options = {'REGISTER', 'UNDO'} 143 | 144 | def execute(self, context): 145 | text = bpy.context.window_manager.clipboard 146 | if not text: 147 | self.report({"INFO"}, "No text selected") 148 | return {'CANCELLED'} 149 | node = context.node 150 | node.text = text 151 | return {'FINISHED'} 152 | 153 | 154 | class GenericNoteClear(bpy.types.Operator): 155 | """ 156 | Clear Note Node 157 | """ 158 | bl_idname = "node.generic_note_clear" 159 | bl_label = "Clear" 160 | bl_options = {'REGISTER', 'UNDO'} 161 | 162 | def execute(self, context): 163 | node = context.node 164 | node.clear() 165 | return {'FINISHED'} 166 | 167 | 168 | class GenericNoteNodeToText(bpy.types.Operator): 169 | """ 170 | Put note into a text buffer 171 | """ 172 | bl_idname = "node.generic_note_to_text" 173 | bl_label = "To text" 174 | bl_options = {'REGISTER', 'UNDO'} 175 | 176 | def execute(self, context): 177 | node = context.node 178 | text = node.text 179 | if not text: 180 | self.report({"INFO"}, "No text in node") 181 | return {'CANCELLED'} 182 | node.to_text() 183 | self.report({"INFO"}, "See text editor: Generic Note Text") 184 | return {'FINISHED'} 185 | 186 | 187 | class GenericNoteNodePanel(bpy.types.Panel): 188 | bl_idname = "generic_note_node_panel" 189 | bl_label = "Note" 190 | bl_space_type = 'NODE_EDITOR' 191 | bl_region_type = 'UI' 192 | bl_category = 'Generic' 193 | bl_options = {'DEFAULT_CLOSED'} 194 | use_pin = True 195 | 196 | @classmethod 197 | def poll(cls, context): 198 | return bool(context.space_data.node_tree) 199 | 200 | def draw(self, context): 201 | layout = self.layout 202 | op = layout.operator("node.add_node", text="New Note") 203 | op.type = "GenericNoteNode" 204 | op.use_transform = True 205 | 206 | op = layout.operator("node.add_node", 207 | text="New note from clipboard") 208 | op.type = "GenericNoteNode" 209 | op.use_transform = False 210 | item = op.settings.add() 211 | item.name = "text" 212 | # item.value get through eval later 213 | item.value = repr(bpy.context.window_manager.clipboard) 214 | 215 | pref = context.user_preferences.addons[__name__].preferences 216 | layout.prop(pref, "note_node_color") 217 | 218 | 219 | class GenericNotePreferences(bpy.types.AddonPreferences): 220 | bl_idname = __name__ 221 | 222 | def menu_switch(self, context): 223 | if self.register_menus: 224 | register_menus() 225 | else: 226 | unregister_menus() 227 | 228 | note_node_color = FloatVectorProperty(name="Note Color", 229 | description='Default color for note node', 230 | size=3, min=0.0, max=1.0, 231 | default=(.5, 0.5, .5), subtype='COLOR') 232 | 233 | register_menus = BoolProperty(name="Register Menus", 234 | description="Register the note node in the layout category", 235 | default=False, 236 | update=menu_switch) 237 | 238 | def draw(self, context): 239 | layout = self.layout 240 | row = layout.row() 241 | row.prop(self, "register_menus") 242 | row = layout.row() 243 | row.prop(self, "note_node_color") 244 | 245 | 246 | # code for registering menus. would like a proper interface for this 247 | # replacement layout categories 248 | 249 | menu_categories = { 250 | "CMP_LAYOUT": (CompositorNodeCategory, "CMP_LAYOUT", "Layout", [ 251 | NodeItem("NodeFrame"), 252 | NodeItem("NodeReroute"), 253 | NodeItem("GenericNoteNode"), 254 | NodeItem("CompositorNodeSwitch"), 255 | ]), 256 | "TEX_LAYOUT": (TextureNodeCategory, "TEX_LAYOUT", "Layout", [ 257 | NodeItem("NodeFrame"), 258 | NodeItem("NodeReroute"), 259 | NodeItem("GenericNoteNode"), 260 | ]), 261 | "SH_NEW_LAYOUT": (ShaderNewNodeCategory, "SH_NEW_LAYOUT", "Layout", [ 262 | NodeItem("NodeFrame"), 263 | NodeItem("NodeReroute"), 264 | NodeItem("GenericNoteNode"), 265 | ]), 266 | "SH_LAYOUT": (ShaderOldNodeCategory, "SH_LAYOUT", "Layout", [ 267 | NodeItem("NodeFrame"), 268 | NodeItem("NodeReroute"), 269 | NodeItem("GenericNoteNode"), 270 | ]) 271 | } 272 | 273 | """ 274 | Really prefer there would be an interface for this instead of 275 | patching this in as done here. Also happy for suggestions for 276 | better methods. 277 | """ 278 | 279 | 280 | def register_menus(): 281 | # remove, replace, add back the menus 282 | nodeitems_builtins.unregister() 283 | 284 | menus = [ 285 | nodeitems_builtins.shader_node_categories, 286 | nodeitems_builtins.compositor_node_categories, 287 | nodeitems_builtins.texture_node_categories, 288 | ] 289 | for menu in menus: 290 | for index, node_cat in enumerate(menu): 291 | if node_cat.identifier in menu_categories: 292 | cls, c_type, text, items = menu_categories[node_cat.identifier] 293 | menu[index] = cls(c_type, text, items=items) 294 | 295 | nodeitems_builtins.register() 296 | 297 | 298 | def unregister_menus(): 299 | # if this fails menus aren't loaded. this ensure that they can load 300 | try: 301 | nodeitems_builtins.unregister() 302 | except: 303 | pass 304 | # reload the code dumps all changes 305 | importlib.reload(nodeitems_builtins) 306 | nodeitems_builtins.register() 307 | 308 | 309 | def register(): 310 | bpy.utils.register_module(__name__) 311 | pref = bpy.context.user_preferences.addons[__name__].preferences 312 | 313 | if pref.register_menus: 314 | register_menus() 315 | 316 | 317 | def unregister(): 318 | bpy.utils.unregister_module(__name__) 319 | unregister_menus() 320 | 321 | if __name__ == "__main__": 322 | register() 323 | --------------------------------------------------------------------------------