1 #+TITLE: org-show - simple presentations in org-mode
4 There are several options for "presenting" from org-mode. Here are the options I found.
6 https://github.com/rlister/org-present
7 https://github.com/eschulte/epresent
9 https://github.com/yjwen/org-reveal
10 https://github.com/takaxp/org-tree-slide/
11 https://github.com/tucasp/org-presie
13 http://orgmode.org/worg/exporters/beamer/tutorial.html
14 http://orgmode.org/worg/org-tutorials/non-beamer-presentations.html#sec-3
16 The one I like the best is discussed here: http://sachachua.com/blog/2013/04/how-to-present-using-org-mode-in-emacs/. I like it best because you can edit the slides as you go, execute arbitrary emacs code, and it is still pretty simple. I used Sacha's code as the basis for org-show. There is a lot of similarity.
20 elisp:org-show-start-slideshow
26 2. split to show slide and full image
29 ** Title slide :slide:
30 #+BEGIN_SRC emacs-lisp-slide
31 (org-show-animate '("Welcome to the org-show" "John Kitchin"))
34 ** Presentations in org-mode :slide:
37 1. Create your org-file. Tag headlines with :slide:
38 2. Enter org-show-mode, press f5. Use PageUp and PageDn to navigate slides
39 3. Go back and forth from the presentation to other files in Emacs or other software.
40 4. Edit yours slides as you go, e.g. to demonstrate something
41 5. Totally interactive, run code, etc...
43 org-show is based on this blog post: http://sachachua.com/blog/2013/04/how-to-present-using-org-mode-in-emacs/
46 ** Test out some themes :slide:
48 [[elisp:(load-theme 'my)]] [[elisp:(disable-theme 'my)]]
50 [[elisp:(load-theme 'adwaita)]] [[elisp:(disable-theme 'adwaita)]]
52 [[elisp:(load-theme 'deeper-blue)]] [[elisp:(disable-theme 'deeper-blue)]]
54 [[elisp:(load-theme 'light-blue)]] [[elisp:(disable-theme 'light-blue)]]
56 [[elisp:(load-theme 'manoj-dark)]] [[elisp:(disable-theme 'manoj-dark)]]
58 [[elisp:(load-theme 'misterioso)]] [[elisp:(disable-theme 'misterioso)]]
60 [[elisp:(load-theme 'tango)]] [[elisp:(disable-theme 'tango)]]
62 [[elisp:(load-theme 'tango-dark)]] [[elisp:(disable-theme 'tango-dark)]]
64 [[elisp:(load-theme 'tsdh-dark)]] [[elisp:(disable-theme 'tsdh-dark)]]
66 [[elisp:(load-theme 'tsdh-light)]] [[elisp:(disable-theme 'tsdh-light)]]
68 [[elisp:(load-theme 'wheatgrass)]] [[elisp:(disable-theme 'wheatgrass)]]
70 [[elisp:(load-theme 'whiteboard)]] [[elisp:(disable-theme 'whiteboard)]]
72 [[elisp:(load-theme 'wombat)]] [[elisp:(disable-theme 'wombat)]]
74 [[elisp:(load-theme 'solarized-light t)]] [[elisp:(disable-theme 'solarized-light)]]
76 [[elisp:(load-theme 'solarized-dark t)]] [[elisp:(disable-theme 'solarized-dark)]]
78 [[elisp:(load-theme 'zenburn t)]] [[elisp:(disable-theme 'zenburn)]]
80 [[elisp:(load-theme 'anti-zenburn t)]] [[elisp:(disable-theme 'anti-zenburn)]]
83 It should be easy to show equations like this $\int_0^x \frac{1}{2} \sin x dx = 6$.
85 It is. Maybe you prefer equation environments?
91 Want to see the equation source? [[elisp:(org-ctrl-c-ctrl-c)][click here]]
93 Back to equations: C-c C-x C-l
96 Figures should show up in two panes.
97 The left pane shows the slide. The right pane shows the figure, scaled to fit in the window.
99 Here is a little screen capture:
102 ** Need a more complicated layout? :slide:
103 Write some code to generate it, and put it in an emacs-lisp-slide block. org-show will run it and show you the result
105 #+BEGIN_SRC emacs-lisp
106 (delete-other-windows)
109 (find-file "taskbar.png")
112 (find-file "doi-utils.org")
116 : #<buffer doi-utils.org>
119 ,#+BEGIN_SRC emacs-lisp-slide
120 (delete-other-windows)
123 (find-file "taskbar.png")
126 (find-file "doi-utils.org")
130 ** A complicated layout :slide:
131 #+BEGIN_SRC emacs-lisp-slide
132 (delete-other-windows)
135 (find-file "taskbar.png")
138 (find-file "doi-utils.org")
140 ** Code blocks should be runnable and editable :slide:
148 ** We can use many languages :slide:
149 (of course, you must have them installed on your computer)
151 #+BEGIN_SRC emacs-lisp
162 #+BEGIN_SRC perl :results output
174 ** Interactivity is important
177 #+BEGIN_SRC emacs-lisp-slide
183 #+BEGIN_SRC emacs-lisp-slide
184 (when (and (boundp 'snake-buffer-name) (get-buffer snake-buffer-name))
185 (kill-buffer snake-buffer-name))
186 (delete-other-windows)
192 #+BEGIN_SRC emacs-lisp-slide
197 *** Become a graffiti artist :slide:
198 #+BEGIN_SRC emacs-lisp-slide
200 (switch-to-buffer (get-buffer-create "*artist*"))
205 (artist-select-op-spray-can))
209 *** Or draw lines :slide:
210 #+BEGIN_SRC emacs-lisp-slide
212 (switch-to-buffer (get-buffer-create "*artist*"))
213 (artist-select-op-line))
216 ** No seriously, we can do real work! :slide:
218 :CUSTOM_ID: sec:data-tab-code
221 Use this table as a data source.
230 #+BEGIN_SRC python :var data=tab-data
231 import matplotlib.pyplot as plt
234 plt.plot(d[:,0], d[:,1])
240 You can make links to a table like this: ref:tab-data.
241 ** Interactive links :slide:
244 You can have links that take you to places: [[beginning]], [[end]], to a [[#sec:data-tab-code][section]],
246 Or links that are functional: cite:mehta-2014-ident-poten.
248 Or that run code [[elisp:(message "Hello %s" user-full-name)]]
250 Or links to info: [[info:org#External%20links][info:org#External links]]
252 Or to open a [[http://kitchingroup.cheme.cmu.edu][website]].
255 ** Conclusions :slide:
258 #+BEGIN_SRC emacs-lisp-slide
259 (org-show-animate '("That's the end of the org-show." "Thank you for your attention!" "http://github.com/jkitchin/jmax"))
267 #+BEGIN_SRC emacs-lisp :tangle org-show.el
268 ;;; org-show.el --- Summary
269 ;; Copyright(C) 2014 John Kitchin
271 ;; Author: John Kitchin <jkitchin@andrew.cmu.edu>
272 ;; Contributions from Sacha Chua.
273 ;; This file is not currently part of GNU Emacs.
275 ;; This program is free software; you can redistribute it and/or
276 ;; modify it under the terms of the GNU General Public License as
277 ;; published by the Free Software Foundation; either version 2, or (at
278 ;; your option) any later version.
280 ;; This program is distributed in the hope that it will be useful, but
281 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
282 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
283 ;; General Public License for more details.
285 ;; You should have received a copy of the GNU General Public License
286 ;; along with this program ; see the file COPYING. If not, write to
287 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
288 ;; Boston, MA 02111-1307, USA.
291 ;; A simple mode for presenting org-files as slide-shows
295 #+BEGIN_SRC emacs-lisp :tangle org-show.el
296 (defvar org-show-presentation-file nil "File containing the presentation.")
297 (defvar org-show-slide-tag "slide" "Tag that marks slides.")
298 (defvar org-show-slide-tag-regexp (concat ":" (regexp-quote org-show-slide-tag) ":"))
299 (defvar org-show-latex-scale 4.0 "scale for latex preview")
301 (defvar org-show-original-latex-scale
302 (plist-get org-format-latex-options :scale)
303 "Original scale for latex preview, so we can reset it.")
305 (defvar org-show-text-scale 4 "scale for text in presentation")
306 (defvar org-show-current-slide-number 1 "holds current slide number")
308 (defvar org-show-mogrify-p (executable-find "mogrify"))
310 (defvar org-show-tags-column -60 "column position to move tags to in slide mode")
311 (defvar org-show-original-tags-column org-tags-column "Save value so we can change back to it")
313 (when org-show-mogrify-p (require 'eimp))
318 ** Make a minor mode and menu
320 #+BEGIN_SRC emacs-lisp :tangle org-show.el
323 (defvar org-show-mode-map
324 (let ((map (make-sparse-keymap)))
325 (define-key map [next] 'org-show-next-slide)
326 (define-key map [prior] 'org-show-previous-slide)
328 (define-key map [f5] 'org-show-start-slideshow)
329 (define-key map [f6] 'org-show-execute-slide)
330 (define-key map (kbd "C--") 'org-show-decrease-text-size)
331 (define-key map (kbd "C-=") 'org-show-increase-text-size)
332 (define-key map (kbd "\e\eg") 'org-show-goto-slide)
333 (define-key map (kbd "\e\et") 'org-show-toc)
334 (define-key map (kbd "\e\eq") 'org-show-stop-slideshow)
336 "Keymap for org-show-mode.")
338 (easy-menu-define my-menu org-show-mode-map "My own menu"
340 ["Start slide show" org-show-start-slideshow t]
341 ["Next slide" org-show-next-slide t]
342 ["Previous slide" org-show-previous-slide t]
343 ["Open this slide" org-show-open-slide t]
344 ["Goto slide" org-show-goto-slide t]
345 ["Table of contents" org-show-toc t]
346 ["Stop slide show" org-show-stop-slideshow t]
350 (define-minor-mode org-show-mode
351 "Minor mode for org-show
353 \\{org-show-mode-map}"
356 :keymap org-show-mode-map)
359 ** Prepare and show the slide
361 #+BEGIN_SRC emacs-lisp :tangle org-show.el
363 (defvar org-show-temp-images '() "list of temporary images")
365 (defun org-show-execute-slide ()
366 "Process slide at point.
367 If it contains an Emacs Lisp source block, evaluate it.
368 If it contains an image, view it in a split buffer
369 Else, focus on that buffer.
372 (setq org-show-presentation-file (expand-file-name (buffer-name)))
373 (delete-other-windows)
375 ;; make sure nothing is folded. This seems to be necessary to
376 ;; prevent an error on narrowing then trying to make latex fragments
380 (org-narrow-to-subtree)
382 (let ((heading-text (nth 4 (org-heading-components)))
383 (org-format-latex-options (plist-put org-format-latex-options :scale org-show-latex-scale)))
385 (set-frame-name (format "%-180s%15s%s" heading-text "slide " (cdr (assoc heading-text org-show-slide-titles))))
387 ;; preview equations in the current subtree
388 (org-preview-latex-fragment '(4))
389 (message "") ; clear minibuffer
392 ;; view images if there is one. WE only do this this for the first one.
393 ((and (goto-char (point-min))
394 (re-search-forward "\\[\\[\\(.*\\.\\(jpg\\|gif\\|png\\)\\)" nil t))
396 (unless (file-exists-p "org-show-images")
397 (make-directory "org-show-images"))
399 (let* ((png-file (match-string 1))
400 (temp-png (expand-file-name (concat "org-show-images/" (secure-hash 'sha1
402 (insert-file-contents png-file)
403 (buffer-string))) ".png"))))
405 (add-to-list 'org-show-temp-images temp-png)
406 (unless (file-exists-p temp-png)
407 (copy-file png-file temp-png t))
413 (when org-show-mogrify-p
414 (eimp-fit-image-width-to-window nil)))
416 (other-window 1) ; back to slide
417 (goto-char (point-min))
418 (text-scale-set org-show-text-scale)
419 (org-display-inline-images)
420 (org-cycle-hide-drawers t)
423 ;; find and execute source code blocks.
424 ;; you can either have images, or code. Not both.
425 ;; Only code blocks of type emacs-lisp-slide are used.
426 ((and (goto-char (point-min))
427 (re-search-forward "#\\+begin_src emacs-lisp-slide" nil t))
428 (let ((info (org-babel-get-src-block-info)))
430 (eval (read (concat "(progn " (nth 1 info) ")"))))))
434 (switch-to-buffer (current-buffer))
435 (text-scale-set org-show-text-scale)
437 (org-cycle-hide-drawers t)
438 (org-display-inline-images)
439 (delete-other-windows)))))
442 ** Next and previous slides
444 #+BEGIN_SRC emacs-lisp :tangle org-show.el
445 (defun org-show-next-slide ()
446 "Goto next slide in presentation"
448 (find-file org-show-presentation-file)
450 (if (<= (+ org-show-current-slide-number 1) (length org-show-slide-titles))
452 (setq org-show-current-slide-number (+ org-show-current-slide-number 1))
453 (org-show-goto-slide org-show-current-slide-number))
454 (org-show-goto-slide org-show-current-slide-number)
455 (message "This is the end. My only friend the end. Jim Morrison.")))
458 #+BEGIN_SRC emacs-lisp :tangle org-show.el
459 (defun org-show-previous-slide ()
460 "Goto previous slide in the list"
462 (find-file org-show-presentation-file)
464 (if (> (- org-show-current-slide-number 1) 0)
466 (setq org-show-current-slide-number (- org-show-current-slide-number 1))
467 (org-show-goto-slide org-show-current-slide-number))
468 (org-show-goto-slide org-show-current-slide-number)
469 (message "Once upon a time...")))
474 #+BEGIN_SRC emacs-lisp :tangle org-show.el
475 (defun org-show-open-slide ()
476 "Start show at this slide"
477 (setq org-show-presentation-file (expand-file-name (buffer-name)))
478 (org-show-initialize)
479 (let ((n (cdr (assoc (nth 4 (org-heading-components)) org-show-slide-titles))))
480 (setq org-show-current-slide-number n)
481 (org-show-goto-slide n)))
485 We need some functions for convenient starting and stopping.
487 On starting, we want to map the slides so we can get slide numbers for navigation and to display them on the frame. We also make the slide tags invisible. We set some temporary key bindings. These need to be global because sometimes we navigate out of the slideshow buffer, and we want page up and down to go to the next slides no matter where we are.
490 #+BEGIN_SRC emacs-lisp :tangle org-show.el
491 (defvar org-show-slide-list '() "List of slide numbers and markers to each slide")
492 (defvar org-show-slide-titles '() "List of titles and slide numbers for each slide")
494 (defun org-show-initialize ()
495 ;; make slide lists for future navigation. rerun this if you change slide order
496 (setq org-show-slide-titles '()
497 org-show-temp-images '()
498 org-show-slide-list '())
503 (when (string-match-p ":slide:" (or (nth 5 (org-heading-components)) ""))
506 (add-to-list 'org-show-slide-titles
507 (cons (nth 4 (org-heading-components)) n) t)
509 (add-to-list 'org-show-slide-list
510 (cons n (set-marker (make-marker) (point))) t))))))
512 (defun org-show-start-slideshow ()
513 "Start the slide show, at the beginning"
516 (setq org-show-presentation-file (expand-file-name (buffer-name)))
517 (beginning-of-buffer)
518 (setq org-tags-column org-show-tags-column)
519 (org-set-tags-command '(4) t)
521 (org-show-initialize)
524 (while (re-search-forward ":slide:" nil t)
526 (make-overlay (match-beginning 0)(match-end 0))
528 (add-to-invisibility-spec 'slide)
529 (beginning-of-buffer)
530 (delete-other-windows)
532 (setq org-show-current-slide-number 1)
533 (org-show-goto-slide 1))
538 #+BEGIN_SRC emacs-lisp :tangle org-show.el
539 (defun org-show-stop-slideshow ()
541 ;; make slide tag visible again
542 (remove-from-invisibility-spec 'slide)
545 (plist-put org-format-latex-options :scale org-show-original-latex-scale)
547 ;; clean up temp images
549 (let ((bname (file-name-nondirectory x)))
550 (when (get-buffer bname)
553 (kill-buffer bname)))
555 (when (file-exists-p x)
557 org-show-temp-images)
558 (setq org-show-temp-images '())
560 ;; ;; clean up miscellaneous buffers
561 (when (get-buffer "*Animation*") (kill-buffer "*Animation*"))
563 (when org-show-presentation-file (find-file org-show-presentation-file))
566 (delete-other-windows)
567 (setq org-show-presentation-file nil)
568 (setq org-show-current-slide-number 1)
569 (set-frame-name (if (buffer-file-name)
570 (abbreviate-file-name (buffer-file-name))))
571 (setq org-tags-column org-show-original-tags-column)
572 (org-set-tags-command '(4) t)
576 (defalias 'stop 'org-show-stop-slideshow)
580 #+BEGIN_SRC emacs-lisp :tangle org-show.el
581 (defun org-show-goto-slide (n)
583 (interactive "nSlide number: ")
584 (message "Going to slide %s" n)
585 (find-file org-show-presentation-file)
586 (setq org-show-current-slide-number n)
588 (goto-char (cdr (assoc n org-show-slide-list)))
589 (org-show-execute-slide))
593 #+BEGIN_SRC emacs-lisp :tangle org-show.el
594 (defun org-show-toc ()
596 (let ((links) (c-b (buffer-name)) (n))
605 (format " [[elisp:(progn (switch-to-buffer \"%s\")(goto-char %s)(org-show-execute-slide))][%2s %s]]\n\n"
606 (marker-buffer (cdr x))
607 (marker-position (cdr x))
609 (nth 4 (org-heading-components))) t))
610 org-show-slide-list))
612 (switch-to-buffer "*List of Slides*")
616 (insert (mapconcat 'identity links ""))
618 ;(setq buffer-read-only t)
619 (use-local-map (copy-keymap org-mode-map))
620 (local-set-key "q" #'(lambda () (interactive) (kill-buffer)))))
624 It seems like we might animate enough to have a function
625 #+BEGIN_SRC emacs-lisp :tangle org-show.el
628 (defun org-show-animate (strings)
629 "Animate STRINGS in an *Animation* buffer"
630 (switch-to-buffer (get-buffer-create
631 (or animation-buffer-name
635 (let* ((vpos (/ (- 20
636 1 ;; For the mode-line
637 (1- (length strings))
643 (setq hpos (/ (- width (length (car strings))) 2))
644 (when (> 0 hpos) (setq hpos 0))
645 (when (> 0 vpos) (setq vpos 0))
646 (animate-string (car strings) vpos hpos)
647 (setq vpos (1+ vpos))
648 (setq strings (cdr strings)))))
651 dynamic rescalling of text size
653 #+BEGIN_SRC emacs-lisp :tangle org-show.el
654 (defun org-show-increase-text-size (&optional arg)
655 "Increase text size. Bound to \\[org-show-increase-text-size].
657 With prefix ARG, set `org-show-text-scale' so subsquent slides are the same text size."
659 (text-scale-increase 1.5)
661 (setq org-show-text-scale (* org-show-text-scale 1.5))))
663 (defun org-show-decrease-text-size (&optional arg)
664 "Increase text size. Bound to \\[org-show-decrease-text-size].
666 With prefix ARG, set `org-show-text-scale' so subsquent slides are the same text size."
668 (text-scale-decrease 1.5)
670 (setq org-show-text-scale (/ org-show-text-scale 1.5)))
675 #+BEGIN_SRC emacs-lisp :tangle org-show.el
679 [[elisp:(org-babel-load-file "org-show.org")]]