├── .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 | 
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
--------------------------------------------------------------------------------