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.")
298 (defvar org-show-slide-tag "slide" "Tag that marks slides.")
300 (defvar org-show-slide-tag-regexp (concat ":" (regexp-quote org-show-slide-tag) ":"))
302 (defvar org-show-latex-scale 4.0 "scale for latex preview")
304 (defvar org-show-original-latex-scale
305 (plist-get org-format-latex-options :scale)
306 "Original scale for latex preview, so we can reset it.")
308 (defvar org-show-text-scale 4 "scale for text in presentation")
310 (defvar org-show-current-slide-number 1 "holds current slide number")
312 (defvar org-show-mogrify-p (executable-find "mogrify"))
314 (defvar org-show-tags-column -60 "column position to move tags to in slide mode")
316 (defvar org-show-original-tags-column org-tags-column "Save value so we can change back to it")
318 (defvar *org-show-flyspell-mode* (when (boundp flyspell-mode)
320 "whether flyspell mode is enabled at beginning of show")
322 (defvar *org-show-running* nil
323 "Flag for if the show is running")
325 (when org-show-mogrify-p (ignore-errors (require 'eimp)))
328 ** Make a minor mode and menu
330 #+BEGIN_SRC emacs-lisp :tangle org-show.el
333 (defvar org-show-mode-map
334 (let ((map (make-sparse-keymap)))
335 (define-key map [next] 'org-show-next-slide)
336 (define-key map [prior] 'org-show-previous-slide)
338 (define-key map [f5] 'org-show-start-slideshow)
339 (define-key map [f6] 'org-show-execute-slide)
340 (define-key map (kbd "C--") 'org-show-decrease-text-size)
341 (define-key map (kbd "C-=") 'org-show-increase-text-size)
342 (define-key map (kbd "\e\eg") 'org-show-goto-slide)
343 (define-key map (kbd "\e\et") 'org-show-toc)
344 (define-key map (kbd "\e\eq") 'org-show-stop-slideshow)
346 "Keymap for org-show-mode.")
348 (easy-menu-define my-menu org-show-mode-map "My own menu"
350 ["Start slide show" org-show-start-slideshow t]
351 ["Next slide" org-show-next-slide t]
352 ["Previous slide" org-show-previous-slide t]
353 ["Open this slide" org-show-open-slide t]
354 ["Goto slide" org-show-goto-slide t]
355 ["Table of contents" org-show-toc t]
356 ["Stop slide show" org-show-stop-slideshow t]
360 (define-minor-mode org-show-mode
361 "Minor mode for org-show
363 \\{org-show-mode-map}"
366 :keymap org-show-mode-map
369 ;; we are in org-show mode
371 ;; turn off flyspell mode
374 (setq *org-show-flyspell-mode* t)
376 (setq *org-show-flyspell-mode* nil)))
377 ;; we are leaving flyspell mode
378 (when *org-show-flyspell-mode*
382 (when *org-show-running*
383 (org-show-stop-slideshow))))
386 ** Prepare and show the slide
388 #+BEGIN_SRC emacs-lisp :tangle org-show.el
390 (defvar org-show-temp-images '() "list of temporary images")
392 (defun org-show-execute-slide ()
393 "Process slide at point.
394 If it contains an Emacs Lisp source block, evaluate it.
395 If it contains an image, view it in a split buffer
396 Else, focus on that buffer.
399 (setq org-show-presentation-file (expand-file-name (buffer-name)))
400 (delete-other-windows)
402 ;; make sure nothing is folded. This seems to be necessary to
403 ;; prevent an error on narrowing then trying to make latex fragments
407 (org-narrow-to-subtree)
409 (let ((heading-text (nth 4 (org-heading-components)))
410 (org-format-latex-options (plist-put org-format-latex-options :scale org-show-latex-scale)))
412 (set-frame-name (format "%-180s%15s%s" heading-text "slide " (cdr (assoc heading-text org-show-slide-titles))))
414 ;; preview equations in the current subtree
415 (org-preview-latex-fragment '(4))
416 (message "") ; clear minibuffer
419 ;; view images if there is one. WE only do this this for the first one.
420 ((and (goto-char (point-min))
421 (re-search-forward "\\[\\[\\(.*\\.\\(jpg\\|gif\\|png\\)\\)" nil t))
423 (unless (file-exists-p "org-show-images")
424 (make-directory "org-show-images"))
426 (let* ((png-file (match-string 1))
427 (temp-png (expand-file-name (concat "org-show-images/" (secure-hash 'sha1
429 (insert-file-contents png-file)
430 (buffer-string))) ".png"))))
432 (add-to-list 'org-show-temp-images temp-png)
433 (unless (file-exists-p temp-png)
434 (copy-file png-file temp-png t))
440 (when org-show-mogrify-p
441 (eimp-fit-image-width-to-window nil)))
443 (other-window 1) ; back to slide
444 (goto-char (point-min))
445 (text-scale-set org-show-text-scale)
446 (org-remove-inline-images)
447 (org-cycle-hide-drawers t)
450 ;; find and execute source code blocks.
451 ;; you can either have images, or code. Not both.
452 ;; Only code blocks of type emacs-lisp-slide are used.
453 ((and (goto-char (point-min))
454 (re-search-forward "#\\+begin_src emacs-lisp-slide" nil t))
455 (let ((info (org-babel-get-src-block-info)))
457 (eval (read (concat "(progn " (nth 1 info) ")"))))))
461 (switch-to-buffer (current-buffer))
462 (text-scale-set org-show-text-scale)
464 (org-cycle-hide-drawers t)
465 (org-display-inline-images)
466 (delete-other-windows)))))
469 ** Next and previous slides
471 #+BEGIN_SRC emacs-lisp :tangle org-show.el
472 (defun org-show-next-slide ()
473 "Goto next slide in presentation"
475 (find-file org-show-presentation-file)
477 (if (<= (+ org-show-current-slide-number 1) (length org-show-slide-titles))
479 (setq org-show-current-slide-number (+ org-show-current-slide-number 1))
480 (org-show-goto-slide org-show-current-slide-number))
481 (org-show-goto-slide org-show-current-slide-number)
482 (message "This is the end. My only friend the end. Jim Morrison.")))
485 #+BEGIN_SRC emacs-lisp :tangle org-show.el
486 (defun org-show-previous-slide ()
487 "Goto previous slide in the list"
489 (find-file org-show-presentation-file)
491 (if (> (- org-show-current-slide-number 1) 0)
493 (setq org-show-current-slide-number (- org-show-current-slide-number 1))
494 (org-show-goto-slide org-show-current-slide-number))
495 (org-show-goto-slide org-show-current-slide-number)
496 (message "Once upon a time...")))
501 #+BEGIN_SRC emacs-lisp :tangle org-show.el
502 (defun org-show-open-slide ()
503 "Start show at this slide"
504 (setq org-show-presentation-file (expand-file-name (buffer-name)))
505 (org-show-initialize)
506 (let ((n (cdr (assoc (nth 4 (org-heading-components)) org-show-slide-titles))))
507 (setq org-show-current-slide-number n)
508 (org-show-goto-slide n)))
512 We need some functions for convenient starting and stopping.
514 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.
517 #+BEGIN_SRC emacs-lisp :tangle org-show.el
518 (defvar org-show-slide-list '() "List of slide numbers and markers to each slide")
519 (defvar org-show-slide-titles '() "List of titles and slide numbers for each slide")
521 (defun org-show-initialize ()
522 ;; make slide lists for future navigation. rerun this if you change slide order
523 (setq org-show-slide-titles '()
524 org-show-temp-images '()
525 org-show-slide-list '())
530 (when (string-match-p ":slide:" (or (nth 5 (org-heading-components)) ""))
533 (add-to-list 'org-show-slide-titles
534 (cons (nth 4 (org-heading-components)) n) t)
536 (add-to-list 'org-show-slide-list
537 (cons n (set-marker (make-marker) (point))) t))))))
539 (defun org-show-start-slideshow ()
540 "Start the slide show, at the beginning"
543 (setq *org-show-running* t)
544 (setq org-show-presentation-file (expand-file-name (buffer-name)))
545 (beginning-of-buffer)
546 (setq org-tags-column org-show-tags-column)
547 (org-set-tags-command '(4) t)
549 (org-show-initialize)
552 (while (re-search-forward ":slide:" nil t)
554 (make-overlay (match-beginning 0)(match-end 0))
556 (add-to-invisibility-spec 'slide)
557 (beginning-of-buffer)
558 (delete-other-windows)
560 (setq org-show-current-slide-number 1)
561 (org-show-goto-slide 1))
566 #+BEGIN_SRC emacs-lisp :tangle org-show.el
567 (defun org-show-stop-slideshow ()
569 ;; make slide tag visible again
570 (remove-from-invisibility-spec 'slide)
572 ;; Redisplay inline images
573 (org-display-inline-images)
576 (plist-put org-format-latex-options :scale org-show-original-latex-scale)
578 ;; clean up temp images
580 (let ((bname (file-name-nondirectory x)))
581 (when (get-buffer bname)
584 (kill-buffer bname)))
586 (when (file-exists-p x)
588 org-show-temp-images)
589 (setq org-show-temp-images '())
591 ;; ;; clean up miscellaneous buffers
592 (when (get-buffer "*Animation*") (kill-buffer "*Animation*"))
594 (when org-show-presentation-file (find-file org-show-presentation-file))
597 (delete-other-windows)
598 (setq org-show-presentation-file nil)
599 (setq org-show-current-slide-number 1)
600 (set-frame-name (if (buffer-file-name)
601 (abbreviate-file-name (buffer-file-name))))
602 (setq org-tags-column org-show-original-tags-column)
603 (org-set-tags-command '(4) t)
604 (setq *org-show-running* nil)
607 (defalias 'stop 'org-show-stop-slideshow)
611 #+BEGIN_SRC emacs-lisp :tangle org-show.el
612 (defun org-show-goto-slide (n)
614 (interactive "nSlide number: ")
615 (message "Going to slide %s" n)
616 (find-file org-show-presentation-file)
617 (setq org-show-current-slide-number n)
619 (goto-char (cdr (assoc n org-show-slide-list)))
620 (org-show-execute-slide))
624 #+BEGIN_SRC emacs-lisp :tangle org-show.el
625 (defun org-show-toc ()
627 (let ((links) (c-b (buffer-name)) (n))
636 (format " [[elisp:(progn (switch-to-buffer \"%s\")(goto-char %s)(org-show-execute-slide))][%2s %s]]\n\n"
637 (marker-buffer (cdr x))
638 (marker-position (cdr x))
640 (nth 4 (org-heading-components))) t))
641 org-show-slide-list))
643 (switch-to-buffer "*List of Slides*")
647 (insert (mapconcat 'identity links ""))
649 ;(setq buffer-read-only t)
650 (use-local-map (copy-keymap org-mode-map))
651 (local-set-key "q" #'(lambda () (interactive) (kill-buffer)))))
655 It seems like we might animate enough to have a function
656 #+BEGIN_SRC emacs-lisp :tangle org-show.el
659 (defun org-show-animate (strings)
660 "Animate STRINGS in an *Animation* buffer"
661 (switch-to-buffer (get-buffer-create
662 (or animation-buffer-name
666 (let* ((vpos (/ (- 20
667 1 ;; For the mode-line
668 (1- (length strings))
674 (setq hpos (/ (- width (length (car strings))) 2))
675 (when (> 0 hpos) (setq hpos 0))
676 (when (> 0 vpos) (setq vpos 0))
677 (animate-string (car strings) vpos hpos)
678 (setq vpos (1+ vpos))
679 (setq strings (cdr strings)))))
682 dynamic rescalling of text size
684 #+BEGIN_SRC emacs-lisp :tangle org-show.el
685 (defun org-show-increase-text-size (&optional arg)
686 "Increase text size. Bound to \\[org-show-increase-text-size].
688 With prefix ARG, set `org-show-text-scale' so subsquent slides are the same text size."
690 (text-scale-increase 1.5)
692 (setq org-show-text-scale (* org-show-text-scale 1.5))))
694 (defun org-show-decrease-text-size (&optional arg)
695 "Increase text size. Bound to \\[org-show-decrease-text-size].
697 With prefix ARG, set `org-show-text-scale' so subsquent slides are the same text size."
699 (text-scale-decrease 1.5)
701 (setq org-show-text-scale (/ org-show-text-scale 1.5)))
706 #+BEGIN_SRC emacs-lisp :tangle org-show.el
710 [[elisp:(org-babel-load-file "org-show.org")]]