Skip to content

Commit c1d796d

Browse files
authored
Merge pull request #77 from hdmf-dev/add/rstfigure
Add helper class for creating RSTFigure
2 parents 96d381e + 65b08a3 commit c1d796d

2 files changed

Lines changed: 230 additions & 54 deletions

File tree

hdmf_docutils/doctools/rst.py

Lines changed: 228 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,28 @@ def __init__(self):
1919
self.newline = "\n"
2020
self.default_indent = ' '
2121

22+
def __str__(self):
23+
return self.document
24+
25+
def __iadd__(self, other):
26+
"""Add other to this document"""
27+
if isinstance(other, str):
28+
self.document += other
29+
return self
30+
elif isinstance(other, RSTDocument):
31+
self.document += other.document
32+
return self
33+
else:
34+
raise ValueError("+= for RSTDocument only supported with string and RSTDocument objects")
35+
36+
def __add__(self, other):
37+
"""Create a new RSTDocument containing the text of this and the other document"""
38+
new_rst_doc = RSTDocument()
39+
new_rst_doc.document = self.document + other.document
40+
new_rst_doc.newline = self.newline
41+
new_rst_doc.default_indent = self.default_indent
42+
return new_rst_doc
43+
2244
@staticmethod
2345
def __get_headingline(title, heading_char):
2446
"""Create a heading line for a given title"""
@@ -210,59 +232,14 @@ def add_include(self, filename, indent=None):
210232
self.document += self.newline
211233

212234
def add_figure(self,
213-
img,
214-
caption=None,
215-
legend=None,
216-
alt=None,
217-
height=None,
218-
width=None,
219-
scale=None,
220-
align=None,
221-
target=None):
222-
"""
223-
224-
:param img: Path to the image to be shown as part of the figure.
225-
:param caption: Optional caption for the figure
226-
:type caption: String or RSTDocument
227-
:param legend: Figure legend
228-
:type legend: String or RST Document
229-
:param alt: Alternate text. A short description of the image used when the image cannot be displayed.
230-
:param height: Height of the figure in pixel
231-
:type height: Int
232-
:param width: Width of the figure in pixel
233-
:type width: Int
234-
:param scale: Uniform scaling of the figure in %. Default is 100.
235-
:type scale: Int
236-
:param align: Alignment of the figure. One of RTDDocument.ALIGN
237-
:param target: Hyperlink to be placed on the image.
235+
figure):
238236
"""
239-
self.document += self.newline
240-
self.document += ".. figure:: %s" % img
241-
self.document += self.newline
242-
if scale is not None:
243-
self.document += (self.indent_text(':scale: %i' % scale) + ' %' + self.newline)
244-
if alt is not None:
245-
self.document += (self.indent_text(':alt: %s' % alt) + self.newline)
246-
if height is not None:
247-
self.document += (self.indent_text(':height: %i px' % height) + self.newline)
248-
if width is not None:
249-
self.document += (self.indent_text(':width: %i px' % width) + self.newline)
250-
if align is not None:
251-
if align not in self.ALIGN:
252-
raise ValueError('align not valid. Found %s expected one of %s' % (str(align), str(self.ALIGN)))
253-
self.document += (self.indent_text(':align: %s' % align) + self.newline)
254-
if target is not None:
255-
self.document += (self.indent_text(':target: %s' % target) + self.newline)
256-
self.document += self.newline
257-
if caption is not None:
258-
curr_caption = caption if not isinstance(caption, RSTDocument) else caption.document
259-
self.document += (self.indent_text(curr_caption) + self.newline)
260-
if legend is not None:
261-
if caption is None:
262-
self.document += (self.indent_text('.. ') + self.newline + self.default_indent + self.newline)
263-
curr_legend = legend if not isinstance(legend, RSTDocument) else legend.document
264-
self.document += (self.indent_text(curr_legend) + self.newline)
265-
self.document += self.newline
237+
Add a Figure to the document
238+
239+
:param figure: RSTFigure to add to the document. If set to None then do nothing
240+
"""
241+
if figure is not None:
242+
figure.render(rst_doc=self)
266243

267244
def add_sidebar(self, text, title, subtitle=None):
268245
"""
@@ -346,6 +323,16 @@ def add_table(self, rst_table, **kwargs):
346323
"""
347324
rst_table.render(self, **kwargs)
348325

326+
def add_toc(self, rst_toc):
327+
"""
328+
Render an RSTToc in this document
329+
330+
:param rst_toc: RST table of contents object to render
331+
:type rst_toc: RSTToc
332+
:return:
333+
"""
334+
rst_toc.render(self)
335+
349336
def write(self, filename, mode='w'):
350337
"""
351338
Write the document to file
@@ -438,6 +425,10 @@ def set_cell(self, row, col, text):
438425
def __len__(self):
439426
return self.num_rows()
440427

428+
def __str__(self):
429+
"""Render figure as string"""
430+
return self.render(rst_doc=None).document
431+
441432
def num_rows(self):
442433
"""
443434
:return: Number of rows in the table
@@ -564,6 +555,8 @@ def render(self,
564555
:param table_ref: Name of the reference to be used for the table
565556
:param latex_tablecolumns: Latex columns description to be rendered as part of the Sphinx tabularcolumns::
566557
argument. E.g. '|p{3.5cm}|p{1cm}|p{10.5cm}|'
558+
559+
:returns: RSTDocument with the rendered table.
567560
"""
568561
if len(self.__table) == 0 and ignore_empty:
569562
return rst_doc if rst_doc is not None else RSTDocument()
@@ -604,3 +597,186 @@ def render(self,
604597

605598
# Return the table
606599
return rst_doc
600+
601+
602+
class RSTToc:
603+
"""
604+
Helper class for defining a table of contents
605+
"""
606+
def __init__(
607+
self,
608+
entries: list = None,
609+
caption: str = None,
610+
name: str = None,
611+
maxdepth: int = None,
612+
titlesonly: bool = False,
613+
hidden: bool = False,
614+
numbered: bool = False,
615+
glob: bool = False,
616+
includehidden: bool = False,
617+
reversed: bool = False
618+
):
619+
self.entries = entries if entries is not None else []
620+
self.caption = caption
621+
self.name = name
622+
self.maxdepth = maxdepth
623+
self.titlesonly = titlesonly
624+
self.hidden = hidden
625+
self.numbered = numbered
626+
self.glob = glob
627+
self.includehidden = includehidden
628+
self.reversed = reversed
629+
630+
def __iadd__(self, other):
631+
"""
632+
Add and entry to the table of contents
633+
:param other: String for a single entry or list, tuple, or set of strings with multiple entries, or
634+
another RSTToc object with the entries to add
635+
:return:
636+
"""
637+
if isinstance(other, str):
638+
self.entries.append(other)
639+
elif isinstance(other, (list, tuple, set)):
640+
self.entries += list(other)
641+
elif isinstance(other, RSTToc):
642+
self.entries.append(other.entries)
643+
else:
644+
raise ValueError("Adding to an RSTDoc with += only supported for str, list, tuple, set, and RSTToc")
645+
return self
646+
647+
def render(self,
648+
rst_doc: RSTDocument = None):
649+
"""
650+
Render the toc to an RSTDocument
651+
652+
:param rst_doc: RSTDocument where the table should be rendered in or None if a new document should be created
653+
654+
:return: RSTDocument with the rendered toc
655+
"""
656+
rst_doc = rst_doc if rst_doc is not None else RSTDocument()
657+
rst_doc += rst_doc.newline
658+
rst_doc += ".. toctree::"
659+
rst_doc += rst_doc.newline
660+
if self.caption is not None:
661+
rst_doc += (rst_doc.indent_text(":caption: %s" % self.caption) + rst_doc.newline)
662+
if self.name is not None:
663+
rst_doc += (rst_doc.indent_text(":name: %s" % self.name) + rst_doc.newline)
664+
if self.maxdepth is not None:
665+
rst_doc += (rst_doc.indent_text(":maxdepth: %i" % self.maxdepth) + rst_doc.newline)
666+
667+
# Add all the bool options
668+
bool_options = {
669+
'titlesonly': self.titlesonly,
670+
'hidden': self.hidden,
671+
'numbered': self.numbered,
672+
'glob': self.glob,
673+
'includehidden': self.includehidden,
674+
'reversed': self.reversed
675+
}
676+
for option_name, option_value in bool_options.items():
677+
if option_value:
678+
rst_doc += (rst_doc.indent_text(":%s:" % option_name) + rst_doc.newline)
679+
rst_doc += rst_doc.newline
680+
681+
# Add all the entries
682+
for entry in self.entries:
683+
rst_doc += (rst_doc.indent_text(entry) + rst_doc.newline)
684+
rst_doc += rst_doc.newline
685+
686+
# Return the document
687+
return rst_doc
688+
689+
690+
class RSTFigure:
691+
"""
692+
Helper class to describe an RST Figure
693+
694+
:ivar image_path: Path to the image to be shown as part of the figure.
695+
:ivar caption: Optional caption for the figure
696+
:ivar legend: Figure legend
697+
:ivar alt: Alternate text. A short description of the image used when the image cannot be displayed.
698+
:ivar height: Integer height of the figure in pixel or string which may include the metric (e.g., 50%)
699+
:ivar width: Integer width of the figure in pixel or string which may include the metric (e.g., 50%)
700+
:ivar scale: Uniform scaling of the figure in %. Default is 100.
701+
:ivar align: Alignment of the figure. One of RTDDocument.ALIGN
702+
:ivar target: Hyperlink to be placed on the image.
703+
"""
704+
def __init__(
705+
self,
706+
image_path,
707+
caption=None,
708+
legend=None,
709+
alt=None,
710+
height=None,
711+
width=None,
712+
scale=None,
713+
align=None,
714+
target=None):
715+
"""
716+
717+
:param image_path: Path to the image to be shown as part of the figure.
718+
:param caption: Optional caption for the figure
719+
:type caption: String or RSTDocument
720+
:param legend: Figure legend
721+
:type legend: String or RST Document
722+
:param alt: Alternate text. A short description of the image used when the image cannot be displayed.
723+
:param height: Integer height of the figure in pixel or string which may include the metric (e.g., 50%)
724+
:param width: Integer width of the figure in pixel or string which may include the metric (e.g., 50%)
725+
:param scale: Uniform scaling of the figure in %. Default is 100.
726+
:param align: Alignment of the figure. One of RTDDocument.ALIGN
727+
:param target: Hyperlink to be placed on the image.
728+
"""
729+
self.image_path = image_path
730+
self.caption = caption
731+
self.legend = legend
732+
self.alt = alt
733+
self.height = height
734+
self.width = width
735+
self.scale = scale
736+
self.align = align
737+
self.target = target
738+
739+
def render(self,
740+
rst_doc: RSTDocument = None):
741+
"""
742+
Render the figure to an RSTDocument
743+
744+
:param rst_doc: RSTDocument where the table should be rendered in or None if a new document should be created
745+
746+
:return: RSTDocument with the rendered figure.
747+
"""
748+
rst_doc = rst_doc if rst_doc is not None else RSTDocument()
749+
rst_doc += rst_doc.newline
750+
rst_doc += ".. figure:: %s" % self.image_path
751+
rst_doc += rst_doc.newline
752+
if self.scale is not None:
753+
rst_doc += (rst_doc.indent_text(':scale: %i' % self.scale) + ' %' + rst_doc.newline)
754+
if self.alt is not None:
755+
rst_doc += (rst_doc.indent_text(':alt: %s' % self.alt) + rst_doc.newline)
756+
if self.height is not None:
757+
height_str = self.height if isinstance(self.height, str) else ("%i px" % self.height)
758+
rst_doc += (rst_doc.indent_text(':height: %s' % height_str) + rst_doc.newline)
759+
if self.width is not None:
760+
width_str = self.width if isinstance(self.width, str) else ("%i px" % self.width)
761+
rst_doc += (rst_doc.indent_text(':width: %s' % width_str) + rst_doc.newline)
762+
if self.align is not None:
763+
if self.align not in self.ALIGN:
764+
raise ValueError('align not valid. Found %s expected one of %s' % (str(self.align), str(self.ALIGN)))
765+
rst_doc += (rst_doc.indent_text(':align: %s' % self.align) + rst_doc.newline)
766+
if self.target is not None:
767+
rst_doc += (rst_doc.indent_text(':target: %s' % self.target) + rst_doc.newline)
768+
rst_doc += rst_doc.newline
769+
if self.caption is not None:
770+
curr_caption = self.caption if not isinstance(self.caption, RSTDocument) else self.caption.document
771+
rst_doc += (rst_doc.indent_text(curr_caption) + rst_doc.newline)
772+
if self.legend is not None:
773+
if self.caption is None:
774+
rst_doc += (rst_doc.indent_text('.. ') + rst_doc.newline + self.default_indent + rst_doc.newline)
775+
curr_legend = self.legend if not isinstance(self.legend, RSTDocument) else self.legend.document
776+
rst_doc += (rst_doc.indent_text(curr_legend) + rst_doc.newline)
777+
rst_doc += rst_doc.newline
778+
return rst_doc
779+
780+
def __str__(self):
781+
"""Render figure as string"""
782+
return self.render(rst_doc=None).document

hdmf_docutils/generate_format_docs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import os
1212

1313
from .doctools.rst import RSTSectionLabelHelper as LabelHelper
14-
from .doctools.rst import RSTDocument
14+
from .doctools.rst import RSTDocument, RSTFigure
1515
from .doctools.renderrst import SpecToRST, DataTypeSection
1616
from .doctools.output import PrintHelper, GitHashHelper
1717

@@ -280,7 +280,7 @@ def render_data_type_section(section,
280280
bbox_inches='tight',
281281
pad_inches=0)
282282
plt.close()
283-
type_desc_doc.add_figure(img='./_format_auto_docs/'+rt+".*", alt=rt)
283+
type_desc_doc.add_figure(RSTFigure(image_path='./_format_auto_docs/'+rt+".*", alt=rt))
284284
if print_status:
285285
PrintHelper.print(" " + rt + '-- RENDER OK.',
286286
PrintHelper.OKGREEN)

0 commit comments

Comments
 (0)