1 # SPDX-FileCopyrightText: 2017-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Authors: nfloyd, Francesco Siddi
8 module_logger
= logging
.getLogger(__name__
)
13 from bpy
.types
import (
19 If view name display is enabled,
20 it will check periodically if the view has been modified
22 get_preferences_timer() is the time in seconds between these checks.
23 It can be increased, if the view become sluggish
24 It is set in the add-on preferences
28 # Utility function get_preferences_timer for update of 3d view draw
29 def get_preferences_timer():
30 # replace the key if the add-on name changes
31 # TODO: expose refresh rate to ui???
32 addon
= bpy
.context
.preferences
.addons
[__package__
]
33 timer_update
= (addon
.preferences
.view_3d_update_rate
if addon
else False)
38 def init_draw(context
=None):
42 if "stored_views_osd" not in context
.window_manager
:
43 context
.window_manager
["stored_views_osd"] = False
45 if not context
.window_manager
["stored_views_osd"]:
46 context
.window_manager
["stored_views_osd"] = True
47 bpy
.ops
.stored_views
.draw()
50 def _draw_callback_px(self
, context
):
52 if area
and area
.type == 'VIEW_3D':
53 ui_scale
= context
.preferences
.system
.ui_scale
54 r_width
= text_location
= context
.region
.width
55 r_height
= context
.region
.height
56 font_id
= 0 # TODO: need to find out how best to get font_id
58 blf
.size(font_id
, 11 * ui_scale
)
59 text_size
= blf
.dimensions(0, self
.view_name
)
61 # compute the text location
63 overlap
= context
.preferences
.system
.use_region_overlap
65 for region
in area
.regions
:
66 if region
.type == "UI":
67 text_location
= r_width
- region
.width
69 text_x
= text_location
- text_size
[0] - 10
70 text_y
= r_height
- text_size
[1] - 8
71 blf
.position(font_id
, text_x
, text_y
, 0)
72 blf
.draw(font_id
, self
.view_name
)
75 class VIEW3D_stored_views_draw(Operator
):
76 bl_idname
= "stored_views.draw"
77 bl_label
= "Show current"
78 bl_description
= "Toggle the display current view name in the view 3D"
84 def handle_add(self
, context
):
85 VIEW3D_stored_views_draw
._handle
= bpy
.types
.SpaceView3D
.draw_handler_add(
86 _draw_callback_px
, (self
, context
), 'WINDOW', 'POST_PIXEL')
87 VIEW3D_stored_views_draw
._timer
= \
88 context
.window_manager
.event_timer_add(get_preferences_timer(), window
=context
.window
)
91 def handle_remove(context
):
92 if VIEW3D_stored_views_draw
._handle
is not None:
93 bpy
.types
.SpaceView3D
.draw_handler_remove(VIEW3D_stored_views_draw
._handle
, 'WINDOW')
94 if VIEW3D_stored_views_draw
._timer
is not None:
95 context
.window_manager
.event_timer_remove(VIEW3D_stored_views_draw
._timer
)
96 VIEW3D_stored_views_draw
._handle
= None
97 VIEW3D_stored_views_draw
._timer
= None
100 def poll(cls
, context
):
101 # return context.mode == 'OBJECT'
104 def modal(self
, context
, event
):
106 context
.area
.tag_redraw()
108 if not context
.area
or context
.area
.type != "VIEW_3D":
109 return {"PASS_THROUGH"}
111 data
= core
.DataStore()
112 stored_views
= context
.scene
.stored_views
114 if len(data
.list) > 0 and \
115 data
.current_index
>= 0 and \
116 not stored_views
.view_modified
:
118 if not stored_views
.view_modified
:
119 sv
= data
.list[data
.current_index
]
120 self
.view_name
= sv
.name
121 if event
.type == 'TIMER':
123 if data
.mode
== 'VIEW':
124 is_modified
= core
.View
.is_modified(context
, sv
)
125 elif data
.mode
== 'POV':
126 is_modified
= core
.POV
.is_modified(context
, sv
)
127 elif data
.mode
== 'LAYERS':
128 is_modified
= core
.Layers
.is_modified(context
, sv
)
129 elif data
.mode
== 'DISPLAY':
130 is_modified
= core
.Display
.is_modified(context
, sv
)
133 'view modified - index: %s name: %s' % (data
.current_index
, sv
.name
)
136 stored_views
.view_modified
= is_modified
138 return {"PASS_THROUGH"}
140 module_logger
.debug('exit')
141 context
.window_manager
["stored_views_osd"] = False
142 VIEW3D_stored_views_draw
.handle_remove(context
)
146 def execute(self
, context
):
147 if context
.area
.type == "VIEW_3D":
149 VIEW3D_stored_views_draw
.handle_add(self
, context
)
150 context
.window_manager
.modal_handler_add(self
)
152 return {"RUNNING_MODAL"}
154 self
.report({"WARNING"}, "View3D not found. Operation Cancelled")
159 class VIEW3D_PT_properties_stored_views(Panel
):
160 bl_label
= "Stored Views"
161 bl_space_type
= "VIEW_3D"
162 bl_region_type
= "UI"
164 bl_options
= {'DEFAULT_CLOSED'}
166 def draw(self
, context
):
167 self
.logger
= logging
.getLogger('%s Properties panel' % __name__
)
170 if bpy
.ops
.view3d
.stored_views_initialize
.poll():
171 layout
.operator("view3d.stored_views_initialize")
174 stored_views
= context
.scene
.stored_views
177 col
= layout
.column(align
=True)
178 col
.prop_enum(stored_views
, "mode", 'VIEW')
179 row
= layout
.row(align
=True)
180 row
.operator("view3d.camera_to_view", text
="Camera To view")
181 row
.operator("stored_views.newcamera")
183 row
= col
.row(align
=True)
184 row
.prop_enum(stored_views
, "mode", 'POV')
185 # row.prop_enum(stored_views, "mode", 'LAYERS')
186 # row.prop_enum(stored_views, "mode", 'DISPLAY')
190 row
.operator("stored_views.save").index
= -1
193 if core
.get_preferences():
194 row
= layout
.row(align
=True)
195 row
.operator("stored_views.import_from_scene", text
="Import from Scene")
196 row
.operator("stored_views.import_blsv", text
="", icon
="IMPORT")
197 row
.operator("stored_views.export_blsv", text
="", icon
="EXPORT")
199 data_store
= core
.DataStore()
200 list = data_store
.list
206 mode
= stored_views
.mode
207 for i
in range(len(list)):
209 icon_string
= "MESH_CUBE" # default icon
210 # TODO: icons for view
212 persp
= list[i
].perspective
214 icon_string
= "MESH_CUBE"
215 elif persp
== 'ORTHO':
216 icon_string
= "MESH_PLANE"
217 elif persp
== 'CAMERA':
218 if list[i
].camera_type
!= 'CAMERA':
219 icon_string
= 'OBJECT_DATAMODE'
221 icon_string
= "OUTLINER_DATA_CAMERA"
223 if list[i
].lock_camera_and_layers
is True:
224 icon_string
= 'SCENE_DATA'
226 icon_string
= 'RENDERLAYERS'
227 if mode
== 'DISPLAY':
228 shade
= list[i
].viewport_shade
229 if shade
== 'TEXTURED':
230 icon_string
= 'TEXTURE_SHADED'
231 if shade
== 'MATERIAL':
232 icon_string
= 'MATERIAL_DATA'
233 elif shade
== 'SOLID':
234 icon_string
= 'SOLID'
235 elif shade
== 'WIREFRAME':
237 elif shade
== 'BOUNDBOX':
239 elif shade
== 'RENDERED':
240 icon_string
= 'MATERIAL'
242 subrow
= box
.row(align
=True)
243 # current view indicator
244 if data_store
.current_index
== i
and context
.scene
.stored_views
.view_modified
is False:
245 subrow
.label(text
="", icon
='CHECKMARK')
246 subrow
.operator("stored_views.set",
247 text
="", icon
=icon_string
).index
= i
248 subrow
.prop(list[i
], "name", text
="")
249 subrow
.operator("stored_views.save",
250 text
="", icon
="REC").index
= i
251 subrow
.operator("stored_views.delete",
252 text
="", icon
="PANEL_CLOSE").index
= i
255 scene
= context
.scene
256 layout
.label(text
="Camera Selector")
257 cameras
= sorted([o
for o
in scene
.objects
if o
.type == 'CAMERA'],
258 key
=lambda o
: o
.name
)
261 for camera
in cameras
:
262 row
= layout
.row(align
=True)
263 row
.context_pointer_set("active_object", camera
)
264 row
.operator("cameraselector.set_scene_camera",
265 text
=camera
.name
, icon
='OUTLINER_DATA_CAMERA')
266 row
.operator("cameraselector.preview_scene_camera",
267 text
='', icon
='RESTRICT_VIEW_OFF')
268 row
.operator("cameraselector.add_camera_marker",
269 text
='', icon
='MARKER')
271 layout
.label(text
="No cameras in this scene")
274 VIEW3D_stored_views_draw
,
275 VIEW3D_PT_properties_stored_views
280 bpy
.utils
.register_class(cls
)
284 bpy
.utils
.unregister_class(cls
)