Sun Position: Fix crash when Blender was started in background
[blender-addons.git] / space_view3d_stored_views / ui.py
blob6d9e8d8ad3389b785d6ecd7e9f16b6b557739beb
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # Authors: nfloyd, Francesco Siddi
5 import logging
6 module_logger = logging.getLogger(__name__)
8 import bpy
9 import blf
10 from . import core
11 from bpy.types import (
12 Operator,
13 Panel,
16 """
17 If view name display is enabled,
18 it will check periodically if the view has been modified
19 since last set.
20 get_preferences_timer() is the time in seconds between these checks.
21 It can be increased, if the view become sluggish
22 It is set in the add-on preferences
23 """
26 # Utility function get_preferences_timer for update of 3d view draw
27 def get_preferences_timer():
28 # replace the key if the add-on name changes
29 # TODO: expose refresh rate to ui???
30 addon = bpy.context.preferences.addons[__package__]
31 timer_update = (addon.preferences.view_3d_update_rate if addon else False)
33 return timer_update
36 def init_draw(context=None):
37 if context is None:
38 context = bpy.context
40 if "stored_views_osd" not in context.window_manager:
41 context.window_manager["stored_views_osd"] = False
43 if not context.window_manager["stored_views_osd"]:
44 context.window_manager["stored_views_osd"] = True
45 bpy.ops.stored_views.draw()
48 def _draw_callback_px(self, context):
49 if context.area and context.area.type == 'VIEW_3D':
50 r_width = text_location = context.region.width
51 r_height = context.region.height
52 font_id = 0 # TODO: need to find out how best to get font_id
54 blf.size(font_id, 11, context.preferences.system.dpi)
55 text_size = blf.dimensions(0, self.view_name)
57 # compute the text location
58 text_location = 0
59 overlap = context.preferences.system.use_region_overlap
60 if overlap:
61 for region in context.area.regions:
62 if region.type == "UI":
63 text_location = r_width - region.width
65 text_x = text_location - text_size[0] - 10
66 text_y = r_height - text_size[1] - 8
67 blf.position(font_id, text_x, text_y, 0)
68 blf.draw(font_id, self.view_name)
71 class VIEW3D_stored_views_draw(Operator):
72 bl_idname = "stored_views.draw"
73 bl_label = "Show current"
74 bl_description = "Toggle the display current view name in the view 3D"
76 _handle = None
77 _timer = None
79 @staticmethod
80 def handle_add(self, context):
81 VIEW3D_stored_views_draw._handle = bpy.types.SpaceView3D.draw_handler_add(
82 _draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
83 VIEW3D_stored_views_draw._timer = \
84 context.window_manager.event_timer_add(get_preferences_timer(), window=context.window)
86 @staticmethod
87 def handle_remove(context):
88 if VIEW3D_stored_views_draw._handle is not None:
89 bpy.types.SpaceView3D.draw_handler_remove(VIEW3D_stored_views_draw._handle, 'WINDOW')
90 if VIEW3D_stored_views_draw._timer is not None:
91 context.window_manager.event_timer_remove(VIEW3D_stored_views_draw._timer)
92 VIEW3D_stored_views_draw._handle = None
93 VIEW3D_stored_views_draw._timer = None
95 @classmethod
96 def poll(cls, context):
97 # return context.mode == 'OBJECT'
98 return True
100 def modal(self, context, event):
101 if context.area:
102 context.area.tag_redraw()
104 if not context.area or context.area.type != "VIEW_3D":
105 return {"PASS_THROUGH"}
107 data = core.DataStore()
108 stored_views = context.scene.stored_views
110 if len(data.list) > 0 and \
111 data.current_index >= 0 and \
112 not stored_views.view_modified:
114 if not stored_views.view_modified:
115 sv = data.list[data.current_index]
116 self.view_name = sv.name
117 if event.type == 'TIMER':
118 is_modified = False
119 if data.mode == 'VIEW':
120 is_modified = core.View.is_modified(context, sv)
121 elif data.mode == 'POV':
122 is_modified = core.POV.is_modified(context, sv)
123 elif data.mode == 'LAYERS':
124 is_modified = core.Layers.is_modified(context, sv)
125 elif data.mode == 'DISPLAY':
126 is_modified = core.Display.is_modified(context, sv)
127 if is_modified:
128 module_logger.debug(
129 'view modified - index: %s name: %s' % (data.current_index, sv.name)
131 self.view_name = ""
132 stored_views.view_modified = is_modified
134 return {"PASS_THROUGH"}
135 else:
136 module_logger.debug('exit')
137 context.window_manager["stored_views_osd"] = False
138 VIEW3D_stored_views_draw.handle_remove(context)
140 return {'FINISHED'}
142 def execute(self, context):
143 if context.area.type == "VIEW_3D":
144 self.view_name = ""
145 VIEW3D_stored_views_draw.handle_add(self, context)
146 context.window_manager.modal_handler_add(self)
148 return {"RUNNING_MODAL"}
149 else:
150 self.report({"WARNING"}, "View3D not found. Operation Cancelled")
152 return {"CANCELLED"}
155 class VIEW3D_PT_properties_stored_views(Panel):
156 bl_label = "Stored Views"
157 bl_space_type = "VIEW_3D"
158 bl_region_type = "UI"
159 bl_category = "View"
160 bl_options = {'DEFAULT_CLOSED'}
162 def draw(self, context):
163 self.logger = logging.getLogger('%s Properties panel' % __name__)
164 layout = self.layout
166 if bpy.ops.view3d.stored_views_initialize.poll():
167 layout.operator("view3d.stored_views_initialize")
168 return
170 stored_views = context.scene.stored_views
172 # UI : mode
173 col = layout.column(align=True)
174 col.prop_enum(stored_views, "mode", 'VIEW')
175 row = layout.row(align=True)
176 row.operator("view3d.camera_to_view", text="Camera To view")
177 row.operator("stored_views.newcamera")
179 row = col.row(align=True)
180 row.prop_enum(stored_views, "mode", 'POV')
181 # row.prop_enum(stored_views, "mode", 'LAYERS')
182 # row.prop_enum(stored_views, "mode", 'DISPLAY')
184 # UI : operators
185 row = layout.row()
186 row.operator("stored_views.save").index = -1
188 # IO Operators
189 if core.get_preferences():
190 row = layout.row(align=True)
191 row.operator("stored_views.import_from_scene", text="Import from Scene")
192 row.operator("stored_views.import_blsv", text="", icon="IMPORT")
193 row.operator("stored_views.export_blsv", text="", icon="EXPORT")
195 data_store = core.DataStore()
196 list = data_store.list
197 # UI : items list
198 if len(list) > 0:
199 row = layout.row()
200 box = row.box()
201 # items list
202 mode = stored_views.mode
203 for i in range(len(list)):
204 # associated icon
205 icon_string = "MESH_CUBE" # default icon
206 # TODO: icons for view
207 if mode == 'POV':
208 persp = list[i].perspective
209 if persp == 'PERSP':
210 icon_string = "MESH_CUBE"
211 elif persp == 'ORTHO':
212 icon_string = "MESH_PLANE"
213 elif persp == 'CAMERA':
214 if list[i].camera_type != 'CAMERA':
215 icon_string = 'OBJECT_DATAMODE'
216 else:
217 icon_string = "OUTLINER_DATA_CAMERA"
218 if mode == 'LAYERS':
219 if list[i].lock_camera_and_layers is True:
220 icon_string = 'SCENE_DATA'
221 else:
222 icon_string = 'RENDERLAYERS'
223 if mode == 'DISPLAY':
224 shade = list[i].viewport_shade
225 if shade == 'TEXTURED':
226 icon_string = 'TEXTURE_SHADED'
227 if shade == 'MATERIAL':
228 icon_string = 'MATERIAL_DATA'
229 elif shade == 'SOLID':
230 icon_string = 'SOLID'
231 elif shade == 'WIREFRAME':
232 icon_string = "WIRE"
233 elif shade == 'BOUNDBOX':
234 icon_string = 'BBOX'
235 elif shade == 'RENDERED':
236 icon_string = 'MATERIAL'
237 # stored view row
238 subrow = box.row(align=True)
239 # current view indicator
240 if data_store.current_index == i and context.scene.stored_views.view_modified is False:
241 subrow.label(text="", icon='CHECKMARK')
242 subrow.operator("stored_views.set",
243 text="", icon=icon_string).index = i
244 subrow.prop(list[i], "name", text="")
245 subrow.operator("stored_views.save",
246 text="", icon="REC").index = i
247 subrow.operator("stored_views.delete",
248 text="", icon="PANEL_CLOSE").index = i
250 layout = self.layout
251 scene = context.scene
252 layout.label(text="Camera Selector")
253 cameras = sorted([o for o in scene.objects if o.type == 'CAMERA'],
254 key=lambda o: o.name)
256 if len(cameras) > 0:
257 for camera in cameras:
258 row = layout.row(align=True)
259 row.context_pointer_set("active_object", camera)
260 row.operator("cameraselector.set_scene_camera",
261 text=camera.name, icon='OUTLINER_DATA_CAMERA')
262 row.operator("cameraselector.preview_scene_camera",
263 text='', icon='RESTRICT_VIEW_OFF')
264 row.operator("cameraselector.add_camera_marker",
265 text='', icon='MARKER')
266 else:
267 layout.label(text="No cameras in this scene")
269 classes = (
270 VIEW3D_stored_views_draw,
271 VIEW3D_PT_properties_stored_views
274 def register():
275 for cls in classes:
276 bpy.utils.register_class(cls)
278 def unregister():
279 for cls in classes:
280 bpy.utils.unregister_class(cls)