space_dopesheet.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  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. # <pep8 compliant>
  19. import bpy
  20. from bpy.types import (
  21. Header,
  22. Menu,
  23. Panel,
  24. )
  25. #######################################
  26. # DopeSheet Filtering - Header Buttons
  27. # used for DopeSheet, NLA, and Graph Editors
  28. def dopesheet_filter(layout, context):
  29. dopesheet = context.space_data.dopesheet
  30. is_nla = context.area.type == 'NLA_EDITOR'
  31. row = layout.row(align=True)
  32. row.prop(dopesheet, "show_only_selected", text="")
  33. row.prop(dopesheet, "show_hidden", text="")
  34. if is_nla:
  35. row.prop(dopesheet, "show_missing_nla", text="")
  36. else: # graph and dopesheet editors - F-Curves and drivers only
  37. row.prop(dopesheet, "show_only_errors", text="")
  38. #######################################
  39. # Dopesheet Filtering Popovers
  40. # Generic Layout - Used as base for filtering popovers used in all animation editors
  41. # Used for DopeSheet, NLA, and Graph Editors
  42. class DopesheetFilterPopoverBase:
  43. bl_region_type = 'HEADER'
  44. bl_label = "Filters"
  45. # Generic = Affects all datatypes
  46. # XXX: Perhaps we want these to stay in the header instead, for easy/fast access
  47. @classmethod
  48. def draw_generic_filters(cls, context, layout):
  49. dopesheet = context.space_data.dopesheet
  50. is_nla = context.area.type == 'NLA_EDITOR'
  51. col = layout.column(align=True)
  52. col.prop(dopesheet, "show_only_selected", icon='NONE')
  53. col.prop(dopesheet, "show_hidden", icon='NONE')
  54. if is_nla:
  55. col.prop(dopesheet, "show_missing_nla", icon='NONE')
  56. else: # graph and dopesheet editors - F-Curves and drivers only
  57. col.prop(dopesheet, "show_only_errors", icon='NONE')
  58. # Name/Membership Filters
  59. # XXX: Perhaps these should just stay in the headers (exclusively)?
  60. @classmethod
  61. def draw_search_filters(cls, context, layout, generic_filters_only=False):
  62. dopesheet = context.space_data.dopesheet
  63. is_nla = context.area.type == 'NLA_EDITOR'
  64. col = layout.column(align=True)
  65. if not is_nla:
  66. row = col.row(align=True)
  67. row.prop(dopesheet, "filter_fcurve_name", text="")
  68. else:
  69. row = col.row(align=True)
  70. row.prop(dopesheet, "filter_text", text="")
  71. if (not generic_filters_only) and (bpy.data.collections):
  72. col = layout.column(align=True)
  73. col.prop(dopesheet, "filter_collection", text="")
  74. # Standard = Present in all panels
  75. @classmethod
  76. def draw_standard_filters(cls, context, layout):
  77. dopesheet = context.space_data.dopesheet
  78. # datablock filters
  79. layout.label(text="Filter by Type:")
  80. flow = layout.grid_flow(row_major=True, columns=2, even_rows=False, align=False)
  81. flow.prop(dopesheet, "show_scenes", text="Scenes")
  82. flow.prop(dopesheet, "show_nodes", text="Node Trees")
  83. # object types
  84. if bpy.data.armatures:
  85. flow.prop(dopesheet, "show_armatures", text="Armatures")
  86. if bpy.data.cameras:
  87. flow.prop(dopesheet, "show_cameras", text="Cameras")
  88. if bpy.data.grease_pencils:
  89. flow.prop(dopesheet, "show_gpencil", text="Grease Pencil Objects")
  90. if bpy.data.lights:
  91. flow.prop(dopesheet, "show_lights", text="Lights")
  92. if bpy.data.meshes:
  93. flow.prop(dopesheet, "show_meshes", text="Meshes")
  94. if bpy.data.curves:
  95. flow.prop(dopesheet, "show_curves", text="Curves")
  96. if bpy.data.lattices:
  97. flow.prop(dopesheet, "show_lattices", text="Lattices")
  98. if bpy.data.metaballs:
  99. flow.prop(dopesheet, "show_metaballs", text="Metaballs")
  100. # data types
  101. flow.prop(dopesheet, "show_worlds", text="Worlds")
  102. if bpy.data.particles:
  103. flow.prop(dopesheet, "show_particles", text="Particles")
  104. if bpy.data.linestyles:
  105. flow.prop(dopesheet, "show_linestyles", text="Line Styles")
  106. if bpy.data.speakers:
  107. flow.prop(dopesheet, "show_speakers", text="Speakers")
  108. if bpy.data.materials:
  109. flow.prop(dopesheet, "show_materials", text="Materials")
  110. if bpy.data.textures:
  111. flow.prop(dopesheet, "show_textures", text="Textures")
  112. if bpy.data.shape_keys:
  113. flow.prop(dopesheet, "show_shapekeys", text="Shape Keys")
  114. if bpy.data.cache_files:
  115. flow.prop(dopesheet, "show_cache_files", text="Cache Files")
  116. if bpy.data.movieclips:
  117. flow.prop(dopesheet, "show_movieclips", text="Movie Clips")
  118. layout.separator()
  119. # Object Data Filters
  120. # TODO: Add per-channel/axis convenience toggles?
  121. split = layout.split()
  122. col = split.column()
  123. col.prop(dopesheet, "show_transforms", text="Transforms")
  124. col = split.column()
  125. col.prop(dopesheet, "show_modifiers", text="Modifiers")
  126. layout.separator()
  127. # performance-related options (users will mostly have these enabled)
  128. col = layout.column(align=True)
  129. col.label(text="Options:")
  130. col.prop(dopesheet, "use_datablock_sort", icon='NONE')
  131. # Popover for Dopesheet Editor(s) - Dopesheet, Action, Shapekey, GPencil, Mask, etc.
  132. class DOPESHEET_PT_filters(DopesheetFilterPopoverBase, Panel):
  133. bl_space_type = 'DOPESHEET_EDITOR'
  134. bl_region_type = 'HEADER'
  135. bl_label = "Filters"
  136. def draw(self, context):
  137. layout = self.layout
  138. dopesheet = context.space_data.dopesheet
  139. ds_mode = context.space_data.mode
  140. layout.prop(dopesheet, "show_summary", text="Summary")
  141. DopesheetFilterPopoverBase.draw_generic_filters(context, layout)
  142. if ds_mode in {'DOPESHEET', 'ACTION', 'GPENCIL'}:
  143. layout.separator()
  144. generic_filters_only = ds_mode != 'DOPESHEET'
  145. DopesheetFilterPopoverBase.draw_search_filters(context, layout,
  146. generic_filters_only=generic_filters_only)
  147. if ds_mode == 'DOPESHEET':
  148. layout.separator()
  149. DopesheetFilterPopoverBase.draw_standard_filters(context, layout)
  150. #######################################
  151. # DopeSheet Editor - General/Standard UI
  152. class DOPESHEET_HT_header(Header):
  153. bl_space_type = 'DOPESHEET_EDITOR'
  154. def draw(self, context):
  155. layout = self.layout
  156. st = context.space_data
  157. layout.template_header()
  158. if st.mode == 'TIMELINE':
  159. from bl_ui.space_time import (
  160. TIME_MT_editor_menus,
  161. TIME_HT_editor_buttons,
  162. )
  163. TIME_MT_editor_menus.draw_collapsible(context, layout)
  164. TIME_HT_editor_buttons.draw_header(context, layout)
  165. else:
  166. layout.prop(st, "ui_mode", text="")
  167. DOPESHEET_MT_editor_menus.draw_collapsible(context, layout)
  168. DOPESHEET_HT_editor_buttons.draw_header(context, layout)
  169. # Header for "normal" dopesheet editor modes (e.g. Dope Sheet, Action, Shape Keys, etc.)
  170. class DOPESHEET_HT_editor_buttons(Header):
  171. bl_idname = "DOPESHEET_HT_editor_buttons"
  172. bl_space_type = 'DOPESHEET_EDITOR'
  173. bl_label = ""
  174. def draw(self, context):
  175. pass
  176. @staticmethod
  177. def draw_header(context, layout):
  178. st = context.space_data
  179. tool_settings = context.tool_settings
  180. if st.mode in {'ACTION', 'SHAPEKEY'}:
  181. # TODO: These buttons need some tidying up -
  182. # Probably by using a popover, and bypassing the template_id() here
  183. row = layout.row(align=True)
  184. row.operator("action.layer_prev", text="", icon='TRIA_DOWN')
  185. row.operator("action.layer_next", text="", icon='TRIA_UP')
  186. row = layout.row(align=True)
  187. row.operator("action.push_down", text="Push Down", icon='NLA_PUSHDOWN')
  188. row.operator("action.stash", text="Stash", icon='FREEZE')
  189. layout.separator_spacer()
  190. layout.template_ID(st, "action", new="action.new", unlink="action.unlink")
  191. layout.separator_spacer()
  192. if st.mode == 'DOPESHEET':
  193. dopesheet_filter(layout, context)
  194. elif st.mode == 'ACTION':
  195. dopesheet_filter(layout, context)
  196. elif st.mode == 'GPENCIL':
  197. row = layout.row(align=True)
  198. row.prop(st.dopesheet, "show_gpencil_3d_only", text="Active Only")
  199. if st.dopesheet.show_gpencil_3d_only:
  200. row = layout.row(align=True)
  201. row.prop(st.dopesheet, "show_only_selected", text="")
  202. row.prop(st.dopesheet, "show_hidden", text="")
  203. row = layout.row(align=True)
  204. row.prop(st.dopesheet, "filter_text", text="")
  205. layout.popover(
  206. panel="DOPESHEET_PT_filters",
  207. text="",
  208. icon='FILTER',
  209. )
  210. # Grease Pencil mode doesn't need snapping, as it's frame-aligned only
  211. if st.mode != 'GPENCIL':
  212. layout.prop(st, "auto_snap", text="")
  213. row = layout.row(align=True)
  214. row.prop(tool_settings, "use_proportional_action", text="", icon_only=True)
  215. sub = row.row(align=True)
  216. sub.active = tool_settings.use_proportional_action
  217. sub.prop(tool_settings, "proportional_edit_falloff", text="", icon_only=True)
  218. class DOPESHEET_MT_editor_menus(Menu):
  219. bl_idname = "DOPESHEET_MT_editor_menus"
  220. bl_label = ""
  221. def draw(self, context):
  222. layout = self.layout
  223. st = context.space_data
  224. layout.menu("DOPESHEET_MT_view")
  225. layout.menu("DOPESHEET_MT_select")
  226. layout.menu("DOPESHEET_MT_marker")
  227. if st.mode == 'DOPESHEET' or (st.mode == 'ACTION' and st.action is not None):
  228. layout.menu("DOPESHEET_MT_channel")
  229. elif st.mode == 'GPENCIL':
  230. layout.menu("DOPESHEET_MT_gpencil_channel")
  231. if st.mode != 'GPENCIL':
  232. layout.menu("DOPESHEET_MT_key")
  233. else:
  234. layout.menu("DOPESHEET_MT_gpencil_frame")
  235. class DOPESHEET_MT_view(Menu):
  236. bl_label = "View"
  237. def draw(self, context):
  238. layout = self.layout
  239. st = context.space_data
  240. layout.prop(st, "show_region_ui")
  241. layout.separator()
  242. layout.prop(st.dopesheet, "use_multi_word_filter", text="Multi-word Match Search")
  243. layout.separator()
  244. layout.prop(st, "use_realtime_update")
  245. layout.prop(st, "show_frame_indicator")
  246. layout.prop(st, "show_sliders")
  247. layout.prop(st, "show_group_colors")
  248. layout.prop(st, "show_interpolation")
  249. layout.prop(st, "show_extremes")
  250. layout.prop(st, "show_marker_lines")
  251. layout.prop(st, "use_auto_merge_keyframes")
  252. layout.prop(st, "show_seconds")
  253. layout.prop(st, "show_locked_time")
  254. layout.separator()
  255. layout.operator("anim.previewrange_set")
  256. layout.operator("anim.previewrange_clear")
  257. layout.operator("action.previewrange_set")
  258. layout.separator()
  259. layout.operator("action.view_all")
  260. layout.operator("action.view_selected")
  261. layout.operator("action.view_frame")
  262. # Add this to show key-binding (reverse action in dope-sheet).
  263. layout.separator()
  264. props = layout.operator("wm.context_set_enum", text="Toggle Graph Editor", icon='GRAPH')
  265. props.data_path = "area.type"
  266. props.value = 'GRAPH_EDITOR'
  267. layout.separator()
  268. layout.menu("INFO_MT_area")
  269. class DOPESHEET_MT_select(Menu):
  270. bl_label = "Select"
  271. def draw(self, context):
  272. layout = self.layout
  273. layout.operator("action.select_all", text="All").action = 'SELECT'
  274. layout.operator("action.select_all", text="None").action = 'DESELECT'
  275. layout.operator("action.select_all", text="Invert").action = 'INVERT'
  276. layout.separator()
  277. layout.operator("action.select_box").axis_range = False
  278. layout.operator("action.select_box", text="Border Axis Range").axis_range = True
  279. layout.operator("action.select_circle")
  280. layout.separator()
  281. layout.operator("action.select_column", text="Columns on Selected Keys").mode = 'KEYS'
  282. layout.operator("action.select_column", text="Column on Current Frame").mode = 'CFRA'
  283. layout.operator("action.select_column", text="Columns on Selected Markers").mode = 'MARKERS_COLUMN'
  284. layout.operator("action.select_column", text="Between Selected Markers").mode = 'MARKERS_BETWEEN'
  285. layout.separator()
  286. props = layout.operator("action.select_leftright", text="Before Current Frame")
  287. props.extend = False
  288. props.mode = 'LEFT'
  289. props = layout.operator("action.select_leftright", text="After Current Frame")
  290. props.extend = False
  291. props.mode = 'RIGHT'
  292. # FIXME: grease pencil mode isn't supported for these yet, so skip for that mode only
  293. if context.space_data.mode != 'GPENCIL':
  294. layout.separator()
  295. layout.operator("action.select_more")
  296. layout.operator("action.select_less")
  297. layout.separator()
  298. layout.operator("action.select_linked")
  299. class DOPESHEET_MT_marker(Menu):
  300. bl_label = "Marker"
  301. def draw(self, context):
  302. layout = self.layout
  303. from bl_ui.space_time import marker_menu_generic
  304. marker_menu_generic(layout, context)
  305. st = context.space_data
  306. if st.mode in {'ACTION', 'SHAPEKEY'} and st.action:
  307. layout.separator()
  308. layout.prop(st, "show_pose_markers")
  309. if st.show_pose_markers is False:
  310. layout.operator("action.markers_make_local")
  311. layout.prop(st, "use_marker_sync")
  312. #######################################
  313. # Keyframe Editing
  314. class DOPESHEET_MT_channel(Menu):
  315. bl_label = "Channel"
  316. def draw(self, _context):
  317. layout = self.layout
  318. layout.operator_context = 'INVOKE_REGION_CHANNELS'
  319. layout.operator("anim.channels_delete")
  320. layout.separator()
  321. layout.operator("anim.channels_group")
  322. layout.operator("anim.channels_ungroup")
  323. layout.separator()
  324. layout.operator_menu_enum("anim.channels_setting_toggle", "type")
  325. layout.operator_menu_enum("anim.channels_setting_enable", "type")
  326. layout.operator_menu_enum("anim.channels_setting_disable", "type")
  327. layout.separator()
  328. layout.operator("anim.channels_editable_toggle")
  329. layout.operator_menu_enum("action.extrapolation_type", "type", text="Extrapolation Mode")
  330. layout.separator()
  331. layout.operator("anim.channels_expand")
  332. layout.operator("anim.channels_collapse")
  333. layout.separator()
  334. layout.operator_menu_enum("anim.channels_move", "direction", text="Move...")
  335. layout.separator()
  336. layout.operator("anim.channels_fcurves_enable")
  337. class DOPESHEET_MT_key(Menu):
  338. bl_label = "Key"
  339. def draw(self, _context):
  340. layout = self.layout
  341. layout.menu("DOPESHEET_MT_key_transform", text="Transform")
  342. layout.operator_menu_enum("action.snap", "type", text="Snap")
  343. layout.operator_menu_enum("action.mirror", "type", text="Mirror")
  344. layout.separator()
  345. layout.operator("action.keyframe_insert")
  346. layout.separator()
  347. layout.operator("action.frame_jump")
  348. layout.separator()
  349. layout.operator("action.copy")
  350. layout.operator("action.paste")
  351. layout.operator("action.paste", text="Paste Flipped").flipped = True
  352. layout.operator("action.duplicate_move")
  353. layout.operator("action.delete")
  354. layout.separator()
  355. layout.operator_menu_enum("action.keyframe_type", "type", text="Keyframe Type")
  356. layout.operator_menu_enum("action.handle_type", "type", text="Handle Type")
  357. layout.operator_menu_enum("action.interpolation_type", "type", text="Interpolation Mode")
  358. layout.separator()
  359. layout.operator("action.clean").channels = False
  360. layout.operator("action.clean", text="Clean Channels").channels = True
  361. layout.operator("action.sample")
  362. class DOPESHEET_MT_key_transform(Menu):
  363. bl_label = "Transform"
  364. def draw(self, _context):
  365. layout = self.layout
  366. layout.operator("transform.transform", text="Move").mode = 'TIME_TRANSLATE'
  367. layout.operator("transform.transform", text="Extend").mode = 'TIME_EXTEND'
  368. layout.operator("transform.transform", text="Slide").mode = 'TIME_SLIDE'
  369. layout.operator("transform.transform", text="Scale").mode = 'TIME_SCALE'
  370. #######################################
  371. # Grease Pencil Editing
  372. class DOPESHEET_MT_gpencil_channel(Menu):
  373. bl_label = "Channel"
  374. def draw(self, _context):
  375. layout = self.layout
  376. layout.operator_context = 'INVOKE_REGION_CHANNELS'
  377. layout.operator("anim.channels_delete")
  378. layout.separator()
  379. layout.operator("anim.channels_setting_toggle")
  380. layout.operator("anim.channels_setting_enable")
  381. layout.operator("anim.channels_setting_disable")
  382. layout.separator()
  383. layout.operator("anim.channels_editable_toggle")
  384. # XXX: to be enabled when these are ready for use!
  385. # layout.separator()
  386. # layout.operator("anim.channels_expand")
  387. # layout.operator("anim.channels_collapse")
  388. # layout.separator()
  389. #layout.operator_menu_enum("anim.channels_move", "direction", text="Move...")
  390. class DOPESHEET_MT_gpencil_frame(Menu):
  391. bl_label = "Frame"
  392. def draw(self, _context):
  393. layout = self.layout
  394. layout.menu("DOPESHEET_MT_key_transform", text="Transform")
  395. layout.operator_menu_enum("action.snap", "type", text="Snap")
  396. layout.operator_menu_enum("action.mirror", "type", text="Mirror")
  397. layout.separator()
  398. layout.operator("action.duplicate")
  399. layout.operator("action.delete")
  400. layout.separator()
  401. layout.operator("action.keyframe_type")
  402. # layout.separator()
  403. # layout.operator("action.copy")
  404. # layout.operator("action.paste")
  405. class DOPESHEET_MT_delete(Menu):
  406. bl_label = "Delete"
  407. def draw(self, _context):
  408. layout = self.layout
  409. layout.operator("action.delete")
  410. layout.separator()
  411. layout.operator("action.clean").channels = False
  412. layout.operator("action.clean", text="Clean Channels").channels = True
  413. class DOPESHEET_MT_context_menu(Menu):
  414. bl_label = "Dope Sheet Context Menu"
  415. def draw(self, _context):
  416. layout = self.layout
  417. layout.operator_context = 'INVOKE_DEFAULT'
  418. layout.operator("action.copy", text="Copy", icon='COPYDOWN')
  419. layout.operator("action.paste", text="Paste", icon='PASTEDOWN')
  420. layout.operator("action.paste", text="Paste Flipped", icon='PASTEFLIPDOWN').flipped = True
  421. layout.separator()
  422. layout.operator_menu_enum("action.keyframe_type", "type", text="Keyframe Type")
  423. layout.operator_menu_enum("action.handle_type", "type", text="Handle Type")
  424. layout.operator_menu_enum("action.interpolation_type", "type", text="Interpolation Mode")
  425. layout.separator()
  426. layout.operator("action.keyframe_insert").type = 'SEL'
  427. layout.operator("action.duplicate_move")
  428. layout.operator_context = 'EXEC_REGION_WIN'
  429. layout.operator("action.delete")
  430. layout.separator()
  431. layout.operator_menu_enum("action.mirror", "type", text="Mirror")
  432. layout.operator_menu_enum("action.snap", "type", text="Snap")
  433. class DOPESHEET_MT_channel_context_menu(Menu):
  434. bl_label = "Dope Sheet Channel Context Menu"
  435. def draw(self, _context):
  436. layout = self.layout
  437. layout.operator("anim.channels_setting_enable", text="Mute Channels").type = 'MUTE'
  438. layout.operator("anim.channels_setting_disable", text="Unmute Channels").type = 'MUTE'
  439. layout.separator()
  440. layout.operator("anim.channels_setting_enable", text="Protect Channels").type = 'PROTECT'
  441. layout.operator("anim.channels_setting_disable", text="Unprotect Channels").type = 'PROTECT'
  442. layout.separator()
  443. layout.operator("anim.channels_group")
  444. layout.operator("anim.channels_ungroup")
  445. layout.separator()
  446. layout.operator("anim.channels_editable_toggle")
  447. layout.operator_menu_enum("action.extrapolation_type", "type", text="Extrapolation Mode")
  448. layout.separator()
  449. layout.operator("anim.channels_expand")
  450. layout.operator("anim.channels_collapse")
  451. layout.separator()
  452. layout.operator_menu_enum("anim.channels_move", "direction", text="Move...")
  453. layout.separator()
  454. layout.operator("anim.channels_delete")
  455. class DOPESHEET_MT_snap_pie(Menu):
  456. bl_label = "Snap"
  457. def draw(self, _context):
  458. layout = self.layout
  459. pie = layout.menu_pie()
  460. pie.operator("action.snap", text="Current Frame").type = 'CFRA'
  461. pie.operator("action.snap", text="Nearest Frame").type = 'NEAREST_FRAME'
  462. pie.operator("action.snap", text="Nearest Second").type = 'NEAREST_SECOND'
  463. pie.operator("action.snap", text="Nearest Marker").type = 'NEAREST_MARKER'
  464. classes = (
  465. DOPESHEET_HT_header,
  466. DOPESHEET_HT_editor_buttons,
  467. DOPESHEET_MT_editor_menus,
  468. DOPESHEET_MT_view,
  469. DOPESHEET_MT_select,
  470. DOPESHEET_MT_marker,
  471. DOPESHEET_MT_channel,
  472. DOPESHEET_MT_key,
  473. DOPESHEET_MT_key_transform,
  474. DOPESHEET_MT_gpencil_channel,
  475. DOPESHEET_MT_gpencil_frame,
  476. DOPESHEET_MT_delete,
  477. DOPESHEET_MT_context_menu,
  478. DOPESHEET_MT_channel_context_menu,
  479. DOPESHEET_MT_snap_pie,
  480. DOPESHEET_PT_filters,
  481. )
  482. if __name__ == "__main__": # only for live edit.
  483. from bpy.utils import register_class
  484. for cls in classes:
  485. register_class(cls)