├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── CHANGELOG.md ├── README.md ├── __init__.py └── icons └── image_empty_128x.png /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: schroef 7 | 8 | --- 9 | 10 | **OS:** 11 | - OS: [e.g. OSX, Windows, Linux] 12 | 13 | **ADDON:** 14 | - Version: [e.g.: Release date or Version] 15 | 16 | **Describe the bug** 17 | A clear and concise description of what the bug is. 18 | 19 | **To Reproduce** 20 | Steps to reproduce the behavior: 21 | 1. Go to '...' 22 | 2. Click on '....' 23 | 3. Scroll down to '....' 24 | 4. See error 25 | 26 | **Screenshots** 27 | If applicable, add screenshots to help explain your problem. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[REQUEST]" 5 | labels: enhancement 6 | assignees: schroef 7 | 8 | --- 9 | 10 | **Describe your request** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Additional context** 14 | Add any other context or screenshots about the feature request here. 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | 6 | ## [0.2.9] - 2024-11-08 7 | 8 | ### Removed 9 | 10 | - Simple Updater, think about Blenders Extension Platform, easier method perhaps 11 | 12 | ### Fixed 13 | 14 | - poll issue with nodes light object > use different approach to get active node vs material 15 | 16 | ## [0.2.8] - 2024-11-05 17 | 18 | ### Fixed 19 | 20 | - Poll issue texture preview > update nested image texture nodegroup 21 | - Update not working for nested image texture > nodegroup 22 | 23 | ### Changed 24 | 25 | -Shortcut double Right Click to preview texture UV Editor > Double Left click 26 | 27 | ## [0.2.7] - 2022-05-16 28 | 29 | ### Fixed 30 | 31 | - Poll issue texture preview > [#295](https://github.com/schroef/Extra-Image-List/issues/295) 32 | 33 | ## [0.2.6] - 2021-11-01 34 | 35 | ### Fixed 36 | 37 | - Poll issue texture preview 38 | 39 | ## [0.2.5] - 2021-07-29 40 | 41 | ### Fixed 42 | 43 | - Panel issue difference 2.83 and 290 44 | 45 | ## [0.2.4] - 2021-04-06 46 | 47 | ### Added 48 | 49 | - Node texture node update button 50 | - Simple updater check 51 | - Show/hide info > cleaner panel 52 | 53 | ### Updated 54 | 55 | - Panel design to 2.8 with sup-panels 56 | - Fixed list view updating with arrow buttons 57 | - Default rows & cols 58 | 59 | ## [0.2.3] - 2019-01-29 60 | 61 | ### Added 62 | 63 | - Added support for Blender 2.80 64 | 65 | ## [0.2.2] - 2018-09-26 66 | 67 | ### Added 68 | 69 | - Extra safety to clean image using a bool 70 | - Show texture name above dimensions for easier reading path and chosen texture 71 | 72 | ### Changed 73 | 74 | - Set basic rows to 4 and columns to 8 75 | - List view now is scaleable instead of locked height by number of images 76 | 77 | ## [0.2.1] - 2018-09-22 78 | 79 | ### Changed 80 | 81 | - Placed preview mode in a settings option (cleaner look) 82 | - Moved texture selector arrows down, no accidental hover over icon preview than 83 | 84 | ### Added 85 | 86 | - Initial repo with new panel look 87 | 88 | ## Notes 89 | 90 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 91 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 92 | 93 | [0.2.9]:https://github.com/schroef/Extra-Image-List/releases/tag/v0.2.9 94 | [0.2.8]:https://github.com/schroef/Extra-Image-List/releases/tag/v0.2.8 95 | [0.2.3]:https://github.com/schroef/Extra-Image-List/releases/tag/v.0.2.3 96 | [0.2.2_2.80]:https://github.com/schroef/Extra-Image-List/releases/tag/v.0.2.2_2.80 97 | [0.2.2]:https://github.com/schroef/Extra-Image-List/releases/tag/v.0.2.2 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extra Image List 2 | 3 | >Original addon & documentation can be found at: [MeshLogic / Extra Image List](https://meshlogic.github.io/posts/blender/addons/extra-image-list/) 4 | 5 | I added a cleaner look to the panel and added the panel settings to option bool. I also added source image so users can pick image sequence or sinle image. I Also added is showing the image texture name above its size, this is very convienent because the source path only shows a very small section. This makes it much easier to identify the used texture. 6 | 7 | !['Preview extra imge list'](https://raw.githubusercontent.com/wiki/schroef/extra-image-list/images/extra-image-list-v029.jpg?2025-11-12) 8 | 9 | 10 | ## Features 11 | 12 | - Two display options (preview and plain list) Set Rows & Columns 13 | - Button to clear all users for the selected image datablock 14 | - Double click on an image in the Node Editor opens the image in the UV/Image Editor 15 | - Located in UV/Image Editor - Tools panel (T) 16 | - Quickly update active image node from UV editor > selected image will be changed in Image Texture Node 17 | 18 | 19 | ### System Requirements 20 | 21 | | **OS** | **Blender** | 22 | | ------------- | ------------- | 23 | | OSX | Blender 2.80 | 24 | | Windows | Blender 2.80 | 25 | | Linux | Not Tested | 26 | 27 | ### Blender 2.78 - 2.79 28 | For older version try this release: [v.0.2.2](https://github.com/schroef/Extra-Image-List/releases/tag/v.0.2.2) 29 | 30 | ### Installation Process 31 | 32 | 1. Download the latest [release](https://github.com/schroef/extra-image-list/releases/) 33 | 2. If you downloaded the zip file. 34 | 3. Open Blender. 35 | 4. Go to File -> User Preferences -> Addons. 36 | 5. At the bottom of the window, choose *Install From File*. 37 | 6. Select the file `extra-image-list-master.zip` from your download location.. 38 | 7. Activate the checkbox for the plugin that you will now find in the list. 39 | 40 | 41 | 42 | ### Changelog 43 | [Full Changelog](CHANGELOG.md) 44 | 45 | 46 | 47 | 48 | 49 | 54 | 55 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Extra Image List - Addon for Blender 3 | # 4 | # - Two display options (preview and plain list) 5 | # - Button to clear all users for the selected image datablock 6 | # - Double click on image in Node Editor opens the image in UV/Image Editor 7 | # 8 | # Version: 0.2 9 | # Revised: 30.05.2017 10 | # Author: Miki (meshlogic) 11 | #------------------------------------------------------------------------------- 12 | 13 | ####################################################### 14 | 15 | ''' 16 | # Extra Image List ###### 17 | 18 | # Changelog 19 | 20 | ## [0.2.9] - 2024-11-12 21 | 22 | ### Removed 23 | 24 | - Updater, thing about Blenders Extension Platform, easier method perhaps 25 | 26 | ### Fixed 27 | 28 | - poll issue with nodes light object > use different approach to get active node vs material 29 | 30 | ## [0.2.8] - 2024-11-05 31 | 32 | ### Fixed 33 | 34 | - Poll isseu texture preview > update nested image texture nodegroup 35 | - Update not working for neste image texture > nodegroup 36 | 37 | ### Changed 38 | 39 | -Shortcut double Right Click to preview texture UV Editor > Double Left click 40 | 41 | ## 0.2.7] - 2022-05-16 42 | 43 | ### Fixed 44 | 45 | - Poll isseu texture preview > #295 46 | 47 | ## [0.2.6] - 2021-11-01 48 | 49 | ### Fixed 50 | 51 | - Poll isseu texture preview 52 | 53 | ## [0.2.5] - 2021-07-29 54 | 55 | ### Fixed 56 | 57 | - Panel issue difference 2.83 and 290 58 | 59 | ## [0.2.4] - 2021-04-06 60 | 61 | ### Added 62 | 63 | - Node textore node update btn 64 | - Simple updater check 65 | - Show/hide info > cleaner panel 66 | 67 | ### Updated 68 | 69 | - Panel design to 2.8 with sup-panels 70 | - Default rows & cols 71 | 72 | ### Fixed 73 | 74 | - List view updating with arrow buttons 75 | 76 | ## [0.2.3] - 2019-11-01 77 | 78 | ### Fixed 79 | 80 | - Got 2.80 working 81 | 82 | # 83 | # Todo 84 | # 85 | # V Fix left right not adjust list view IDX 86 | ####################################################### 87 | ''' 88 | 89 | 90 | bl_info = { 91 | "name": "Extra Image List", 92 | "author": "Miki (meshlogic) - Rombout Versluijs (updated panel)", 93 | "category": "UV", 94 | "description": "An alternative image list for UV/Image Editor.", 95 | "location": "UV/Image Editor > Tools > Image List", 96 | "version": (0, 2, 9), 97 | "blender": (2, 80, 0), 98 | "wiki_url": "https://github.com/schroef/Extra-Image-List", 99 | "tracker_url": "https://github.com/schroef/Extra-Image-List/issues", 100 | } 101 | 102 | import bpy 103 | import os 104 | from bpy.props import * 105 | 106 | from bpy.types import ( 107 | Panel, 108 | AddonPreferences, 109 | Menu, 110 | Operator, 111 | Scene, 112 | UIList, 113 | PropertyGroup 114 | ) 115 | from bpy.props import ( 116 | EnumProperty, 117 | StringProperty, 118 | BoolProperty, 119 | IntProperty, 120 | PointerProperty 121 | ) 122 | from bpy.app.handlers import persistent 123 | #------------------------------------------------------------------------------- 124 | # UI PANEL - Extra Image List 125 | #------------------------------------------------------------------------------- 126 | class EIL_PT_ImageListPanel(Panel): 127 | bl_space_type = 'IMAGE_EDITOR' 128 | bl_region_type = 'UI' 129 | bl_category = "Image List" 130 | bl_label = "Extra Image List" 131 | 132 | def draw(self, context): 133 | layout = self.layout 134 | cs = context.scene 135 | props = cs.extra_image_list 136 | 137 | #--- Get the current image in the UV editor and list of all images 138 | img = context.space_data.image 139 | img_list = bpy.data.images 140 | 141 | layout = self.layout 142 | #----------------------------------------------------------------------- 143 | # PREVIEW List Style 144 | #----------------------------------------------------------------------- 145 | if props.style == 'PREVIEW': 146 | #--- Image preview list 147 | layout.template_ID_preview( 148 | context.space_data, "image", 149 | new = "image.new", 150 | open = "image.open", 151 | rows = props.rows, cols = props.cols 152 | ) 153 | 154 | #----------------------------------------------------------------------- 155 | # LIST Style 156 | #----------------------------------------------------------------------- 157 | elif props.style == 'LIST': 158 | layout.row() 159 | layout.template_list( 160 | "EIL_UL_ImageList", "", 161 | bpy.data, "images", 162 | props, "image_id", 163 | #rows = len(bpy.data.images) 164 | ) 165 | 166 | #----------------------------------------------------------------------- 167 | # Image Source 168 | #----------------------------------------------------------------------- 169 | if img != None: 170 | if props.info: 171 | #--- Image source 172 | row = layout.row() 173 | row.prop(img, "source") 174 | #row.label(text="Image Source:", icon='DISK_DRIVE') 175 | row = layout.row(align=True) 176 | 177 | if img.source == 'FILE': 178 | if img.packed_file: 179 | row.operator("image.unpack", text="", icon='PACKAGE') 180 | else: 181 | row.operator("image.pack", text="", icon='UGLYPACKAGE') 182 | 183 | row.prop(img, "filepath", text="") 184 | row.operator("image.reload", text="", icon='FILE_REFRESH') 185 | else: 186 | row.label(text=img.source + " : " + img.type) 187 | 188 | #--- Image size 189 | col = layout.column(align=True) 190 | row = layout.row(align=True) 191 | row.alignment = 'LEFT' 192 | 193 | if img.has_data: 194 | filename = os.path.basename(img.filepath) 195 | #--- Image name 196 | col.label(text=filename, icon='FILE_IMAGE') 197 | #--- Image size 198 | row.label(text="Size:", icon='TEXTURE') 199 | row.label(text="%d x %d x %db" % (img.size[0], img.size[1], img.depth)) 200 | else: 201 | row.label(text="Can't load image file!", icon='ERROR') 202 | 203 | row = layout.row() 204 | split = row.split(factor=0.5) 205 | #--- Navigation button PREV 206 | sub = split.column(align=True) 207 | sub.scale_y = 1.5 208 | sub.operator("extra_image_list.nav", text="", icon='BACK').direction = 'PREV' 209 | 210 | # Disable button for the first image or for no images 211 | sub.enabled = (img!=img_list[0] if (img!=None and len(img_list)>0) else False) 212 | 213 | #--- Navigation button NEXT 214 | sub = split.column(align=True) 215 | sub.scale_y = 1.5 216 | sub.operator("extra_image_list.nav", text="", icon='FORWARD').direction = 'NEXT' 217 | 218 | row = layout.row() 219 | row.operator("extra_image_list.update_node", icon='NODE_TEXTURE') 220 | 221 | # Disable button for the last image or for no images 222 | sub.enabled = (img!=img_list[-1] if (img!=None and len(img_list)>0) else False) 223 | 224 | #------------------------------------------------------------------------------- 225 | # OPTIONS SUB-PANEL 226 | #------------------------------------------------------------------------------- 227 | # Expand Background Sub-panel 228 | class EIL_PT_ImageListPanel_Options(Panel): 229 | bl_space_type = 'IMAGE_EDITOR' 230 | bl_region_type = 'UI' 231 | bl_category = "Image List" 232 | bl_label = "Options" 233 | bl_parent_id = "EIL_PT_ImageListPanel" 234 | bl_options = {'DEFAULT_CLOSED'} 235 | 236 | def draw(self, context): 237 | layout = self.layout 238 | cs = context.scene 239 | props = cs.extra_image_list 240 | 241 | layout.use_property_split = True 242 | layout.use_property_decorate = False 243 | 244 | #----------------------------------------------------------------------- 245 | # SETTINGS 246 | #----------------------------------------------------------------------- 247 | layout.prop(props, "info") 248 | #--- List style buttons 249 | #--- Num. of rows & cols for image preview list 250 | if (bpy.app.version[1] < 90): 251 | column = layout.column(align=True) 252 | column.label(text="Preview Style") 253 | else: 254 | column = layout.column(heading="Preview Style:", align=True) 255 | column.prop(props, "style") #,expand=True 256 | if props.style =='PREVIEW': 257 | column.prop(props, "rows") 258 | column.prop(props, "cols") 259 | 260 | layout.use_property_split = True 261 | if (bpy.app.version[1] < 90): 262 | op = layout.column(align=True) 263 | op.label(text="Clean") 264 | else: 265 | op = layout.column(heading="Clean", align=True) 266 | op.prop(props,"clean_enabled", text="Blend File") 267 | 268 | flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=False) 269 | flow.active = props.clean_enabled 270 | flow.enabled = props.clean_enabled 271 | flow.prop(props, "clear_mode", text="") 272 | flow.operator("extra_image_list.clear", text="Clear", icon='ERROR') 273 | 274 | 275 | #------------------------------------------------------------------------------- 276 | # CUSTOM TEMPLATE_LIST FOR IMAGES 277 | #------------------------------------------------------------------------------- 278 | class EIL_UL_ImageList(UIList): 279 | bl_idname = "EIL_UL_ImageList" 280 | 281 | def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): 282 | ##NEW BL280 283 | # 'DEFAULT' and 'COMPACT' layout types should usually use the same draw code. 284 | if self.layout_type in {'DEFAULT', 'COMPACT'}: 285 | pass 286 | # 'GRID' layout type should be as compact as possible (typically a single icon!). 287 | elif self.layout_type in {'GRID'}: 288 | pass 289 | 290 | # Image name and icon 291 | row = layout.row(align=True) 292 | if data == None: 293 | row.prop(item, "name", text="", emboss=False, icon_value=custom_icons["image_empty"].icon_id) 294 | row.prop(item, "name", text="", emboss=False, icon_value=icon) 295 | 296 | # Image status (fake user, zero users, packed file) 297 | row = row.row(align=True) 298 | row.alignment = 'RIGHT' 299 | 300 | # if item.use_fake_user: 301 | # row.label(text="F") 302 | # else: 303 | # Allows user to edit it from listview 304 | row.prop(item, "use_fake_user", text="", emboss=False) 305 | if item.users == 0: 306 | row.label(text="0") 307 | 308 | if item.packed_file: 309 | #row.label(icon='PACKAGE') 310 | row.operator("image.unpack", text="", icon='PACKAGE', emboss=False) 311 | 312 | 313 | #--- Update the active image when you select another item in the template_list 314 | def update_active_image(self, context): 315 | try: 316 | id = bpy.context.scene.extra_image_list.image_id 317 | 318 | if id < len(bpy.data.images): 319 | img = bpy.data.images[id] 320 | bpy.context.space_data.image = img 321 | except: 322 | pass 323 | 324 | #------------------------------------------------------------------------------- 325 | # UPDATE NODE IMAGE 326 | #------------------------------------------------------------------------------- 327 | class EIL_OT_UpdateNode(Operator): 328 | bl_idname = "extra_image_list.update_node" 329 | bl_label = "Update Texture Node" 330 | bl_description = "Updates the active Texture Node" 331 | 332 | @classmethod 333 | def poll(cls, context): 334 | aNode = None 335 | # print(bpy.context.active_object.active_material) 336 | # print(bpy.context.active_object.active_material.node_tree.nodes.active) 337 | # return context.active_node and context.active_node.type == 'TEX_IMAGE' 338 | # source: https://blender.stackexchange.com/a/68351/7631 339 | # return context.scene.node_tree.nodes.active and context.scene.node_tree.nodes.active.type == 'TEX_IMAGE' 340 | # return context.active_object.active_material.node_tree.nodes.active is not None 341 | # return context.active_node is not None 342 | # actN = context.active_node 343 | if bpy.context.active_object.type == 'LIGHT': 344 | ntree = bpy.context.active_object.data.id_data.node_tree 345 | aNode = ntree.nodes.active 346 | else: 347 | if bpy.context.active_object.active_material: 348 | aNode = bpy.context.active_object.active_material.node_tree.nodes.active 349 | if aNode!=None: 350 | if aNode.type=='GROUP': 351 | aNode = aNode.node_tree.nodes.active 352 | else: 353 | return False 354 | return aNode.type == 'TEX_IMAGE' and aNode != None #and bpy.context.active_object.active_material.node_tree.nodes 355 | 356 | def execute(self, context): 357 | aNode = None 358 | # Get active node 359 | if bpy.context.active_object.type == 'LIGHT': 360 | ntree = bpy.context.active_object.data.id_data.node_tree 361 | aNode = ntree.nodes.active 362 | else: 363 | if bpy.context.active_object.active_material: 364 | aNode = bpy.context.active_object.active_material.node_tree.nodes.active 365 | 366 | # print(aNode.type) 367 | # aNode = context.active_object.active_material.node_tree.nodes.active 368 | if aNode!=None: 369 | if aNode.type=='GROUP': 370 | aNode = aNode.node_tree.nodes.active 371 | 372 | # Get list of all images 373 | img_list = list(bpy.data.images) 374 | 375 | # Get index of the current image in UV editor, return if there is none image 376 | img = context.space_data.image 377 | if img in img_list: 378 | id = img_list.index(img) 379 | else: 380 | return{'FINISHED'} 381 | 382 | # if id: 383 | if aNode: 384 | if aNode.type == 'TEX_IMAGE': 385 | aNode.image = img_list[id] 386 | # aNode.image = bpy.data.images[id] 387 | 388 | return{'FINISHED'} 389 | 390 | #------------------------------------------------------------------------------- 391 | # IMAGE NAVIGATION OPERATOR 392 | #------------------------------------------------------------------------------- 393 | class EIL_OT_Nav(Operator): 394 | bl_idname = "extra_image_list.nav" 395 | bl_label = "Nav" 396 | bl_description = "Navigation button" 397 | 398 | direction : EnumProperty( 399 | items = [ 400 | ('NEXT', "PREV", "PREV"), 401 | ('PREV', "PREV", "PREV") 402 | ], 403 | name = "direction", 404 | default = 'NEXT') 405 | 406 | def execute(self, context): 407 | # Get list of all images 408 | img_list = list(bpy.data.images) 409 | 410 | # Get index of the current image in UV editor, return if there is none image 411 | img = context.space_data.image 412 | if img in img_list: 413 | id = img_list.index(img) 414 | else: 415 | return{'FINISHED'} 416 | 417 | # Navigate 418 | if self.direction == 'NEXT': 419 | if id+1 < len(img_list): 420 | context.space_data.image = img_list[id+1] 421 | 422 | if self.direction == 'PREV': 423 | if id > 0: 424 | context.space_data.image = img_list[id-1] 425 | 426 | return{'FINISHED'} 427 | 428 | 429 | #------------------------------------------------------------------------------- 430 | # CLEAR IMAGE USERS OPERATOR 431 | #------------------------------------------------------------------------------- 432 | BAKE_TYPES = ('COMBINED', 'AO', 'SHADOW', 'NORMAL', 'UV', 'EMIT', 'ENVIRONMENT', 433 | 'DIFFUSE', 'GLOSSY', 'TRANSMISSION', 'SUBSURFACE', 'GRID', 434 | 'FULL', 'NORMALS', 'TEXTURE', 'DISPLACEMENT', 'DERIVATIVE', 'VERTEX_COLORS', 'EMIT', 435 | 'ALPHA', 'MIRROR_INTENSITY', 'MIRROR_COLOR', 'SPEC_INTENSITY', 'SPEC_COLOR') 436 | 437 | class EIL_OT_Clear(Operator): 438 | bl_idname = "extra_image_list.clear" 439 | bl_label = "Clear Users" 440 | bl_description = """Use with caution !!\nClear all users for selected image datablocks.\nSo the image datablock can disappear after save and reload of the blend file.""" 441 | 442 | def execute(self, context): 443 | cs = context.scene 444 | props = cs.extra_image_list 445 | 446 | #--- SELECTED IMAGE ---------------------------------------------------- 447 | if props.clear_mode == 'SELECTED': 448 | 449 | # Get image in the editor 450 | img = context.space_data.image 451 | if img != None: 452 | img.user_clear() 453 | 454 | #--- NO USERS ---------------------------------------------------- 455 | if props.clear_mode == 'NO USERS': 456 | 457 | for img in bpy.data.images: 458 | if img.users == 0: 459 | bpy.data.images.remove(img) 460 | 461 | #--- INVALID IMAGES ---------------------------------------------------- 462 | elif props.clear_mode == 'INVALID': 463 | 464 | for img in bpy.data.images: 465 | 466 | # Load image in the editor 467 | context.space_data.image = img 468 | try: 469 | img.update() 470 | except: 471 | pass 472 | 473 | # Clear if loaded image has no data 474 | if img.has_data == False: 475 | img.user_clear() 476 | 477 | #--- GENERATED IMAGES -------------------------------------------------- 478 | elif props.clear_mode == 'GENERATED': 479 | 480 | for img in bpy.data.images: 481 | if img.source == 'GENERATED': 482 | img.user_clear() 483 | 484 | #--- BAKED IMAGES ------------------------------------------------------ 485 | elif props.clear_mode == 'BAKED': 486 | 487 | # Seek for images ending with "_baketype" 488 | bake_types = tuple(["_"+s for s in BAKE_TYPES]) 489 | 490 | for img in bpy.data.images: 491 | 492 | # Get image name without extension 493 | name = img.name.upper() 494 | if len(name.split(".")[-1]) <= 3: 495 | name = name.rsplit(".", 1)[0] 496 | 497 | # Clear if name ends with a bake type 498 | if name.endswith(bake_types): 499 | img.user_clear() 500 | 501 | #--- ALL IMAGES -------------------------------------------------------- 502 | elif props.clear_mode == 'ALL': 503 | for img in bpy.data.images: 504 | if img != None: 505 | img.user_clear() 506 | 507 | return{'FINISHED'} 508 | 509 | 510 | #------------------------------------------------------------------------------- 511 | # SHOW NODE IMAGE OPERATOR 512 | # - Show node image in the IMAGE_EDITOR after double click on the node 513 | #------------------------------------------------------------------------------- 514 | IMG_NODES = ("ShaderNodeTexImage", "ShaderNodeTexEnvironment") 515 | 516 | class EIL_OT_ShowNodeImage(Operator): 517 | bl_idname = "node.show_image" 518 | bl_label = "Show node image in the UV/Image Editor" 519 | 520 | def execute(self, context): 521 | node = context.active_node 522 | 523 | #--- Test if the active node is image type 524 | if node and node.bl_idname in IMG_NODES: 525 | 526 | # Find IMAGE_EDITOR 527 | for area in bpy.context.screen.areas: 528 | if area.type == 'IMAGE_EDITOR': 529 | space = area.spaces.active 530 | 531 | # Show image in IMAGE_EDITOR 532 | if node.image: 533 | space.image = node.image 534 | 535 | return {"FINISHED"} 536 | 537 | 538 | #------------------------------------------------------------------------------- 539 | # CUSTOM HANDLER (scene_update_post) #NEW name bl 2.80 depsgraph_update_post 540 | # - This handler is invoked after the scene updates 541 | # - Keeps template_list synced with the active image 542 | #------------------------------------------------------------------------------- 543 | @persistent 544 | def update_image_list(context): 545 | # try: 546 | props = bpy.context.scene.extra_image_list 547 | 548 | # Try to find the active image in the IMAGE_EDITOR 549 | img = None 550 | for area in bpy.context.screen.areas: 551 | if area.type == 'IMAGE_EDITOR': 552 | img = area.spaces.active.image 553 | break 554 | 555 | # Update selected item in the template_list 556 | if img != None: 557 | id = bpy.data.images.find(img.name) 558 | # if id != -1 and id != props.image_id: 559 | props.image_id = id 560 | # except: 561 | # pass 562 | 563 | 564 | #------------------------------------------------------------------------------- 565 | # CUSTOM SCENE PROPS 566 | #------------------------------------------------------------------------------- 567 | class ExtraImageList_Props(PropertyGroup): 568 | 569 | style : EnumProperty( 570 | items = [ 571 | ('PREVIEW', "Preview", "", 0), 572 | ('LIST', "List", "", 1), 573 | ], 574 | default = 'PREVIEW', 575 | name = "Style", 576 | description = "Image list style") 577 | 578 | clean_enabled : BoolProperty( 579 | default=False, 580 | name="Clean:", 581 | description="Enables option to clear scene of image textures. Be careful!") 582 | 583 | clear_mode : EnumProperty( 584 | items = [ 585 | ('NO USERS', "No Users", "Clears all images with no users", 0), 586 | ('SELECTED', "Selected Image", "Clear the image selected in the editor", 1), 587 | ('INVALID', "Invalid Images", "Clear invalid images (has_data == False)", 2), 588 | ('GENERATED', "Generated Images", "Clear generated images (source == 'GENERATED')", 3), 589 | ('BAKED', "Baked Images", "Clear images ending with a bake type e.g. '_COMBINED'", 4), 590 | ('ALL', "All Images", "Clear all images", 4), 591 | ], 592 | default = 'NO USERS', 593 | name = "Image Selection", 594 | description = "Select images to be cleared") 595 | 596 | rows : IntProperty( 597 | name = "Rows", 598 | description = "Num. of rows in the preview list", 599 | default = 4, min = 1, max = 15) 600 | 601 | cols : IntProperty( 602 | name = "Cols", 603 | description = "Num. of columns in the preview list", 604 | default = 6, min = 1, max = 30) 605 | 606 | # Index of the active image in the template_list 607 | image_id : IntProperty( 608 | name = "Image ID", 609 | default = 0, 610 | update = update_active_image) 611 | 612 | options : BoolProperty( 613 | name="Options", 614 | default=False) 615 | 616 | info : BoolProperty( 617 | name="Show Info", 618 | default=False) 619 | 620 | settings : BoolProperty( 621 | name="Settings", 622 | default=False) 623 | 624 | def icon_Load(): 625 | # importing icons 626 | import bpy.utils.previews 627 | global custom_icons 628 | custom_icons = bpy.utils.previews.new() 629 | 630 | # path to the folder where the icon is 631 | # the path is calculated relative to this py file inside the addon folder 632 | icons_dir = os.path.join(os.path.dirname(__file__), "icons") 633 | 634 | # load a preview thumbnail of a file and store in the previews collection 635 | custom_icons.load("empty", os.path.join(icons_dir, "empty_image_128x.png"), 'IMAGE') 636 | 637 | # global variable to store icons in 638 | custom_icons = None 639 | 640 | #------------------------------------------------------------------------------- 641 | # REGISTER/UNREGISTER ADDON CLASSES 642 | #------------------------------------------------------------------------------- 643 | keymaps = [] 644 | 645 | #Classes for register and unregister 646 | classes = ( 647 | EIL_PT_ImageListPanel, 648 | EIL_PT_ImageListPanel_Options, 649 | EIL_UL_ImageList, 650 | EIL_OT_ShowNodeImage, 651 | ##NW bl280 PropertyGroup Needs to be added now? 652 | ExtraImageList_Props, 653 | EIL_OT_Clear, 654 | EIL_OT_UpdateNode, 655 | EIL_OT_Nav, 656 | ) 657 | 658 | def register(): 659 | for cls in classes: 660 | bpy.utils.register_class(cls) 661 | 662 | bpy.types.Scene.extra_image_list = PointerProperty(type=ExtraImageList_Props) 663 | bpy.app.handlers.depsgraph_update_post.append(update_image_list) 664 | 665 | 666 | # Add custom shortcut (image node double click) 667 | kc = bpy.context.window_manager.keyconfigs.addon 668 | km = kc.keymaps.new(name="Node Editor", space_type='NODE_EDITOR') 669 | kmi = km.keymap_items.new("node.show_image", 'LEFTMOUSE', 'DOUBLE_CLICK') 670 | keymaps.append((km, kmi)) 671 | icon_Load() 672 | 673 | def unregister(): 674 | global custom_icons 675 | bpy.utils.previews.remove(custom_icons) 676 | 677 | del bpy.types.Scene.extra_image_list 678 | bpy.app.handlers.depsgraph_update_post.remove(update_image_list) 679 | 680 | # Remove custom shortcuts 681 | for km, kmi in keymaps: 682 | km.keymap_items.remove(kmi) 683 | keymaps.clear() 684 | 685 | for cls in reversed(classes): 686 | bpy.utils.unregister_class(cls) 687 | 688 | 689 | if __name__ == "__main__": 690 | register() 691 | 692 | -------------------------------------------------------------------------------- /icons/image_empty_128x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schroef/Extra-Image-List/34439b63571de5ea298361d7c39016387020cf26/icons/image_empty_128x.png --------------------------------------------------------------------------------