PrEditor 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of PrEditor might be problematic. Click here for more details.

Files changed (158) hide show
  1. preditor/__init__.py +322 -0
  2. preditor/__main__.py +13 -0
  3. preditor/about_module.py +161 -0
  4. preditor/cli.py +192 -0
  5. preditor/config.py +302 -0
  6. preditor/contexts.py +119 -0
  7. preditor/cores/__init__.py +0 -0
  8. preditor/cores/core.py +20 -0
  9. preditor/dccs/maya/PrEditor_maya.mod +2 -0
  10. preditor/dccs/maya/plug-ins/PrEditor_maya.py +110 -0
  11. preditor/debug.py +144 -0
  12. preditor/delayable_engine/__init__.py +302 -0
  13. preditor/delayable_engine/delayables.py +85 -0
  14. preditor/enum.py +728 -0
  15. preditor/excepthooks.py +131 -0
  16. preditor/gui/__init__.py +93 -0
  17. preditor/gui/app.py +160 -0
  18. preditor/gui/codehighlighter.py +209 -0
  19. preditor/gui/completer.py +226 -0
  20. preditor/gui/console.py +867 -0
  21. preditor/gui/dialog.py +178 -0
  22. preditor/gui/drag_tab_bar.py +190 -0
  23. preditor/gui/editor_chooser.py +57 -0
  24. preditor/gui/errordialog.py +68 -0
  25. preditor/gui/find_files.py +125 -0
  26. preditor/gui/fuzzy_search/__init__.py +0 -0
  27. preditor/gui/fuzzy_search/fuzzy_search.py +93 -0
  28. preditor/gui/group_tab_widget/__init__.py +325 -0
  29. preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
  30. preditor/gui/group_tab_widget/grouped_tab_models.py +108 -0
  31. preditor/gui/group_tab_widget/grouped_tab_widget.py +78 -0
  32. preditor/gui/group_tab_widget/one_tab_widget.py +54 -0
  33. preditor/gui/level_buttons.py +343 -0
  34. preditor/gui/logger_window_handler.py +48 -0
  35. preditor/gui/logger_window_plugin.py +32 -0
  36. preditor/gui/loggerwindow.py +1385 -0
  37. preditor/gui/newtabwidget.py +69 -0
  38. preditor/gui/set_text_editor_path_dialog.py +59 -0
  39. preditor/gui/status_label.py +99 -0
  40. preditor/gui/suggest_path_quotes_dialog.py +50 -0
  41. preditor/gui/ui/editor_chooser.ui +93 -0
  42. preditor/gui/ui/errordialog.ui +74 -0
  43. preditor/gui/ui/find_files.ui +140 -0
  44. preditor/gui/ui/loggerwindow.ui +1105 -0
  45. preditor/gui/ui/set_text_editor_path_dialog.ui +189 -0
  46. preditor/gui/ui/suggest_path_quotes_dialog.ui +225 -0
  47. preditor/gui/window.py +161 -0
  48. preditor/gui/workbox_mixin.py +389 -0
  49. preditor/gui/workbox_text_edit.py +137 -0
  50. preditor/gui/workboxwidget.py +298 -0
  51. preditor/logging_config.py +52 -0
  52. preditor/osystem.py +401 -0
  53. preditor/plugins.py +118 -0
  54. preditor/prefs.py +74 -0
  55. preditor/resource/environment_variables.html +26 -0
  56. preditor/resource/error_mail.html +85 -0
  57. preditor/resource/error_mail_inline.html +41 -0
  58. preditor/resource/img/README.md +17 -0
  59. preditor/resource/img/arrow_forward.png +0 -0
  60. preditor/resource/img/check-bold.png +0 -0
  61. preditor/resource/img/chevron-down.png +0 -0
  62. preditor/resource/img/chevron-up.png +0 -0
  63. preditor/resource/img/close-thick.png +0 -0
  64. preditor/resource/img/comment-edit.png +0 -0
  65. preditor/resource/img/content-copy.png +0 -0
  66. preditor/resource/img/content-cut.png +0 -0
  67. preditor/resource/img/content-duplicate.png +0 -0
  68. preditor/resource/img/content-paste.png +0 -0
  69. preditor/resource/img/content-save.png +0 -0
  70. preditor/resource/img/debug_disabled.png +0 -0
  71. preditor/resource/img/eye-check.png +0 -0
  72. preditor/resource/img/file-plus.png +0 -0
  73. preditor/resource/img/file-remove.png +0 -0
  74. preditor/resource/img/format-align-left.png +0 -0
  75. preditor/resource/img/format-letter-case-lower.png +0 -0
  76. preditor/resource/img/format-letter-case-upper.png +0 -0
  77. preditor/resource/img/format-letter-case.svg +1 -0
  78. preditor/resource/img/information.png +0 -0
  79. preditor/resource/img/logging_critical.png +0 -0
  80. preditor/resource/img/logging_custom.png +0 -0
  81. preditor/resource/img/logging_debug.png +0 -0
  82. preditor/resource/img/logging_error.png +0 -0
  83. preditor/resource/img/logging_info.png +0 -0
  84. preditor/resource/img/logging_not_set.png +0 -0
  85. preditor/resource/img/logging_warning.png +0 -0
  86. preditor/resource/img/marker.png +0 -0
  87. preditor/resource/img/play.png +0 -0
  88. preditor/resource/img/playlist-play.png +0 -0
  89. preditor/resource/img/plus-minus-variant.png +0 -0
  90. preditor/resource/img/preditor.ico +0 -0
  91. preditor/resource/img/preditor.png +0 -0
  92. preditor/resource/img/preditor.psd +0 -0
  93. preditor/resource/img/preditor.svg +44 -0
  94. preditor/resource/img/regex.svg +1 -0
  95. preditor/resource/img/restart.svg +1 -0
  96. preditor/resource/img/skip-forward-outline.png +0 -0
  97. preditor/resource/img/skip-next-outline.png +0 -0
  98. preditor/resource/img/skip-next.png +0 -0
  99. preditor/resource/img/skip-previous.png +0 -0
  100. preditor/resource/img/subdirectory-arrow-right.png +0 -0
  101. preditor/resource/img/text-search-variant.png +0 -0
  102. preditor/resource/img/warning-big.png +0 -0
  103. preditor/resource/lang/python.json +30 -0
  104. preditor/resource/settings.ini +25 -0
  105. preditor/resource/stylesheet/Bright.css +65 -0
  106. preditor/resource/stylesheet/Dark.css +199 -0
  107. preditor/scintilla/__init__.py +22 -0
  108. preditor/scintilla/delayables/__init__.py +11 -0
  109. preditor/scintilla/delayables/smart_highlight.py +94 -0
  110. preditor/scintilla/delayables/spell_check.py +173 -0
  111. preditor/scintilla/documenteditor.py +2038 -0
  112. preditor/scintilla/finddialog.py +68 -0
  113. preditor/scintilla/lang/__init__.py +80 -0
  114. preditor/scintilla/lang/config/bash.ini +15 -0
  115. preditor/scintilla/lang/config/batch.ini +14 -0
  116. preditor/scintilla/lang/config/cpp.ini +19 -0
  117. preditor/scintilla/lang/config/css.ini +19 -0
  118. preditor/scintilla/lang/config/eyeonscript.ini +17 -0
  119. preditor/scintilla/lang/config/html.ini +21 -0
  120. preditor/scintilla/lang/config/javascript.ini +24 -0
  121. preditor/scintilla/lang/config/lua.ini +16 -0
  122. preditor/scintilla/lang/config/maxscript.ini +20 -0
  123. preditor/scintilla/lang/config/mel.ini +18 -0
  124. preditor/scintilla/lang/config/mu.ini +22 -0
  125. preditor/scintilla/lang/config/nsi.ini +19 -0
  126. preditor/scintilla/lang/config/perl.ini +19 -0
  127. preditor/scintilla/lang/config/puppet.ini +19 -0
  128. preditor/scintilla/lang/config/python.ini +28 -0
  129. preditor/scintilla/lang/config/ruby.ini +19 -0
  130. preditor/scintilla/lang/config/sql.ini +7 -0
  131. preditor/scintilla/lang/config/xml.ini +21 -0
  132. preditor/scintilla/lang/config/yaml.ini +18 -0
  133. preditor/scintilla/lang/language.py +240 -0
  134. preditor/scintilla/lexers/__init__.py +0 -0
  135. preditor/scintilla/lexers/cpplexer.py +21 -0
  136. preditor/scintilla/lexers/javascriptlexer.py +25 -0
  137. preditor/scintilla/lexers/maxscriptlexer.py +234 -0
  138. preditor/scintilla/lexers/mellexer.py +368 -0
  139. preditor/scintilla/lexers/mulexer.py +32 -0
  140. preditor/scintilla/lexers/pythonlexer.py +41 -0
  141. preditor/scintilla/ui/finddialog.ui +160 -0
  142. preditor/settings.py +71 -0
  143. preditor/stream/__init__.py +80 -0
  144. preditor/stream/director.py +73 -0
  145. preditor/stream/manager.py +74 -0
  146. preditor/streamhandler_helper.py +46 -0
  147. preditor/utils/__init__.py +0 -0
  148. preditor/utils/cute.py +30 -0
  149. preditor/utils/stylesheets.py +54 -0
  150. preditor/utils/text_search.py +342 -0
  151. preditor/version.py +21 -0
  152. preditor/weakref.py +363 -0
  153. preditor-1.0.0.dist-info/METADATA +224 -0
  154. preditor-1.0.0.dist-info/RECORD +158 -0
  155. preditor-1.0.0.dist-info/WHEEL +5 -0
  156. preditor-1.0.0.dist-info/entry_points.txt +18 -0
  157. preditor-1.0.0.dist-info/licenses/LICENSE +165 -0
  158. preditor-1.0.0.dist-info/top_level.txt +1 -0
preditor/settings.py ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env python
2
+ from __future__ import absolute_import, print_function
3
+
4
+ import os
5
+ import sys
6
+
7
+ try:
8
+ import configparser
9
+ except Exception:
10
+ import ConfigParser as configparser # noqa: N813
11
+
12
+ # define the default environment variables
13
+ OS_TYPE = ''
14
+ if os.name == 'posix':
15
+ OS_TYPE = 'Linux'
16
+ elif os.name == 'nt':
17
+ OS_TYPE = 'Windows'
18
+ elif os.name == 'osx':
19
+ OS_TYPE = 'MacOS'
20
+
21
+ # The sections to add from settings.ini
22
+ # The order matters. Add from most specific to least specific.
23
+ # Example: Add Windows Offline, then Windows, then Default.
24
+ # Environment variables that exist in os.environ will not be added.
25
+ _SECTIONS_TO_ADD = []
26
+ if os.getenv('BDEV_OFFLINE') == '1':
27
+ _SECTIONS_TO_ADD.append('{} Offline'.format(OS_TYPE))
28
+ _SECTIONS_TO_ADD += [OS_TYPE, 'Default']
29
+
30
+ _currentEnv = ''
31
+ defaults = {}
32
+
33
+
34
+ def environStr(value):
35
+ if sys.version_info[0] > 2:
36
+ # Python 3 requires a unicode value. aka str(), which these values already are
37
+ return value
38
+ # Python 2 requires str object, not unicode
39
+ return value.encode('utf8')
40
+
41
+
42
+ def addConfigSection(config_parser, section):
43
+ """
44
+ Add a config section to os.environ for a section.
45
+
46
+ Does not add options that already exist in os.environ.
47
+
48
+ Args:
49
+ config_parser (configparser.RawConfigParser): The parser to read from.
50
+ Must already be read.
51
+ section (str): The section name to add.
52
+ """
53
+
54
+ for option in config_parser.options(section):
55
+ if option.upper() not in os.environ:
56
+ value = config_parser.get(section, option)
57
+ if value == 'None':
58
+ value = ''
59
+ # In python2.7 on windows you can't pass unicode values to
60
+ # subprocess.Popen's env argument. This is the reason we are calling str()
61
+ os.environ[environStr(option.upper())] = environStr(value)
62
+
63
+
64
+ # load the default environment from the settings INI
65
+ config = configparser.RawConfigParser()
66
+ config.read(os.path.join(os.path.dirname(__file__), 'resource', 'settings.ini'))
67
+ for section in _SECTIONS_TO_ADD:
68
+ addConfigSection(config, section)
69
+
70
+ # store the blurdev path in the environment
71
+ os.environ['BDEV_PATH'] = environStr(os.path.dirname(__file__))
@@ -0,0 +1,80 @@
1
+ """ A system for capturing stream output and later inserting the captured output into a
2
+ gui, and allowing that GUI to directly capture any new output written to the streams.
3
+
4
+ A use case for this is to attach a Manager's as early as possible to ``sys.stdout``
5
+ and ``sys.stderr`` process, before any GUI's are created. Then later you initialize a
6
+ GUI python console that should show all python output that has already been written.
7
+ Any future writes are delivered directly to the gui using a callback mechanism.
8
+
9
+ Example::
10
+
11
+ # Startup script or plugin for DCC runs this as early as possible.
12
+ from preditor.stream import install_to_std
13
+
14
+ manager = install_to_std()
15
+ # Startup script exits and DCC continues to load eventually creating the main gui.
16
+
17
+ # From a menu a user chooses to show a custom console(A gui that replicates a
18
+ # python interactive console inside the DCC).
19
+ console = PythonConsole()
20
+
21
+ # We will want to see all the previous writes made by python, so replay them
22
+ # in the console so it can properly handle stdout and stderr writes.
23
+ for msg, state in manager:
24
+ console.write(msg, state)
25
+
26
+ # Make it so any future writes are automatically added to the console.
27
+ manager.add_callback(console.write)
28
+
29
+ # Optionally, disable storing data in the buffer. buffer.write calls will now
30
+ # directly write to console so there is no reason to duplicate the data to the
31
+ # buffer.
32
+ manager.append_writes = False
33
+ """
34
+ from __future__ import absolute_import, print_function
35
+
36
+ import sys
37
+
38
+ STDERR = 1
39
+ STDIN = 2
40
+ STDOUT = 3
41
+
42
+ from .director import Director # noqa: E402
43
+ from .manager import Manager # noqa: E402
44
+
45
+ """Set when :py:attr:``install_to_std`` is called. This stores the installed Manager
46
+ so it can be accessed to install callbacks.
47
+ """
48
+ active = None
49
+
50
+ __all__ = [
51
+ "active",
52
+ "Director",
53
+ "install_to_std",
54
+ "Manager",
55
+ "STDERR",
56
+ "STDIN",
57
+ "STDOUT",
58
+ ]
59
+
60
+
61
+ def install_to_std(out=True, err=True):
62
+ """Replaces ``sys.stdout`` and ``sys.stderr`` with :py:class:`Director`'s
63
+ using the returned :py:class:`Manager`. This manager is stored as the ``active``
64
+ variable and can be accessed later. This can be called more than once, and it will
65
+ simply return the already installed Manager.
66
+
67
+ Args:
68
+ out (bool, optional): Enables replacement of ``sys.stdout`` on first call.
69
+ err (bool, optional): Enables replacement of ``sys.stderr`` on first call.
70
+ """
71
+ global active
72
+
73
+ if active is None:
74
+ active = Manager()
75
+ if out:
76
+ sys.stdout = Director(active, STDOUT)
77
+ if err:
78
+ sys.stderr = Director(active, STDERR)
79
+
80
+ return active
@@ -0,0 +1,73 @@
1
+ from __future__ import absolute_import, print_function
2
+
3
+ import io
4
+ import sys
5
+
6
+ from . import STDERR, STDOUT
7
+
8
+
9
+ class Director(io.TextIOBase):
10
+ """A file like object that stores the text written to it in a manager.
11
+ This manager can be shared between multiple Directors to build a single
12
+ continuous history of all writes.
13
+
14
+ Args:
15
+ manager (Manager): The manager that writes are stored in.
16
+ state: The state passed to the manager. This is often ``preditor.stream.STDOUT``
17
+ or ``preditor.stream.STDERR``.
18
+ old_stream: A second stream that will be written to every time this stream
19
+ is written to. This allows this object to replace sys.stdout and still
20
+ send that output to the original stdout, which is useful for not breaking
21
+ DCC's script editors. Pass False to disable this feature. If you pass None
22
+ and state is set to ``preditor.stream.STDOUT`` or ``preditor.stream.STDERR``
23
+ this will automatically be set to the current sys.stdout or sys.stderr.
24
+ """
25
+
26
+ def __init__(self, manager, state, old_stream=None, *args, **kwargs):
27
+ super(Director, self).__init__(*args, **kwargs)
28
+ self.manager = manager
29
+ self.state = state
30
+
31
+ # Keep track of whether we wrapped a std stream
32
+ # that way we don't .close() any streams that we don't control
33
+ self.std_stream_wrapped = False
34
+
35
+ if old_stream is False:
36
+ old_stream = None
37
+ elif old_stream is None:
38
+ if state == STDOUT:
39
+ # On Windows if we're in pythonw.exe, then sys.stdout is named "nul"
40
+ # And it uses cp1252 encoding (which breaks with unicode)
41
+ # So if we find this nul TextIOWrapper, it's safe to just skip it
42
+ if getattr(sys.stdout, 'name', '') != 'nul':
43
+ self.std_stream_wrapped = True
44
+ old_stream = sys.stdout
45
+ elif state == STDERR:
46
+ if getattr(sys.stderr, 'name', '') != 'nul':
47
+ self.std_stream_wrapped = True
48
+ old_stream = sys.stderr
49
+
50
+ self.old_stream = old_stream
51
+
52
+ def close(self):
53
+ if (
54
+ self.old_stream
55
+ and not self.std_stream_wrapped
56
+ and self.old_stream is not sys.__stdout__
57
+ and self.old_stream is not sys.__stderr__
58
+ ):
59
+ self.old_stream.close()
60
+
61
+ super(Director, self).close()
62
+
63
+ def flush(self):
64
+ if self.old_stream:
65
+ self.old_stream.flush()
66
+
67
+ super(Director, self).flush()
68
+
69
+ def write(self, msg):
70
+ self.manager.write(msg, self.state)
71
+
72
+ if self.old_stream:
73
+ self.old_stream.write(msg)
@@ -0,0 +1,74 @@
1
+ from __future__ import absolute_import, print_function
2
+
3
+ import collections
4
+
5
+ from ..weakref import WeakList
6
+
7
+
8
+ class Manager(collections.deque):
9
+ """Stores all of the data from the stdout/stderr writes. You can iterate over this
10
+ object to see all of the (msg, state) calls that have been written to it up to the
11
+ maxlen specified when constructing it.
12
+
13
+ Args:
14
+ maxlen (int, optional): The maximum number of raw writes to store. If this is
15
+ exceeded, the oldest writes are discarded.
16
+
17
+ Properties:
18
+ store_writes (bool): Set this to False if you no longer want write calls to
19
+ store on the manager.
20
+ """
21
+
22
+ def __init__(self, maxlen=10000):
23
+ super(Manager, self).__init__(maxlen=maxlen)
24
+ self.callbacks = WeakList()
25
+ self.store_writes = True
26
+
27
+ def add_callback(self, callback, replay=False, disable_writes=False, clear=False):
28
+ """Add a callable that will be called every time write is called.
29
+
30
+ Args:
31
+ callback (callable): A callable object that takes two arguments. It must
32
+ take two arguments (msg, state). See write for more details.
33
+ replay (bool, optional): If True, then iterate over all the stored writes
34
+ and pass them to callback. This is useful for when you are initializing
35
+ a gui and want to include all previous prints.
36
+ disable_writes (bool, optional): Set store_writes to False if this is True.
37
+ clear (bool, optional): Clear the stored history on this object.
38
+ """
39
+ self.callbacks.append(callback)
40
+
41
+ if replay:
42
+ # Replay the existing prints into the console.
43
+ for msg, state in self:
44
+ callback(msg, state)
45
+
46
+ if disable_writes:
47
+ # Disable storing data in the buffer. buffer.write calls will now
48
+ # directly write to console so there is no reason to duplicate the
49
+ # data to the buffer.
50
+ self.store_writes = False
51
+
52
+ if clear:
53
+ self.clear()
54
+
55
+ def remove_callback(self, callback):
56
+ self.callbacks.remove(callback)
57
+
58
+ def get_value(self, fmt="[{state}:{msg}]"):
59
+ return ''.join([fmt.format(msg=d[0], state=d[1]) for d in self])
60
+
61
+ def write(self, msg, state):
62
+ """Adds the written text to the manager and passes it to any attached callbacks.
63
+
64
+ Args:
65
+ msg (str): The text to be written.
66
+ state: A identifier for how the text is to be written. For example if this
67
+ write is coming from sys.stderr this will likely be set to
68
+ ``preditor.stream.STDERR``.
69
+ """
70
+ if self.store_writes:
71
+ self.append((msg, state))
72
+
73
+ for callback in self.callbacks:
74
+ callback(msg, state)
@@ -0,0 +1,46 @@
1
+ from __future__ import absolute_import
2
+
3
+ import logging
4
+ import sys
5
+
6
+
7
+ class StreamHandlerHelper(object):
8
+ """A collection of functions for manipulating ``logging.StreamHandler`` objects."""
9
+
10
+ @classmethod
11
+ def set_stream(cls, handler, stream):
12
+ """For the given StreamHandler, set its stream. This works around
13
+ python 2's lack of StreamHandler.setStream by replicating python 3.
14
+ """
15
+ # TODO: once python 2 is no longer supported, replace any uses of this
16
+ # function with `handler.setStream(stream)`
17
+ if sys.version_info[0] > 2:
18
+ handler.setStream(stream)
19
+ else:
20
+ # Copied from python 3's logging's setStream to work in python 2
21
+ handler.acquire()
22
+ try:
23
+ handler.flush()
24
+ handler.stream = stream
25
+ finally:
26
+ handler.release()
27
+
28
+ @classmethod
29
+ def replace_stream(cls, old, new, logger=None):
30
+ """Replaces the stream of StreamHandlers by checking all
31
+ `logging.StreamHandler`'s attached to the provided logger. If any of them are
32
+ using old for their stream, update that stream to new.
33
+
34
+ Args:
35
+ old (stream): Only StreamHandlers using this stream will be updated to new.
36
+ new (stream): A file stream object like `sys.stderr` that will replace old.
37
+ logger (logging.Logger, optional): The logger to update streams for. If
38
+ None, the root logger(`logging.getLogger()`) will be used.
39
+ """
40
+ if logger is None:
41
+ logger = logging.getLogger()
42
+
43
+ for handler in logger.handlers:
44
+ if isinstance(handler, logging.StreamHandler):
45
+ if handler.stream == old:
46
+ cls.set_stream(handler, new)
File without changes
preditor/utils/cute.py ADDED
@@ -0,0 +1,30 @@
1
+ from __future__ import absolute_import
2
+
3
+ __all__ = ["ensureWindowIsVisible"]
4
+ from Qt.QtWidgets import QApplication
5
+
6
+
7
+ def ensureWindowIsVisible(widget):
8
+ """
9
+ Checks the widget's geometry against all of the system's screens. If it does
10
+ not intersect it will reposition it to the top left corner of the highest
11
+ numbered desktop. Returns a boolean indicating if it had to move the
12
+ widget.
13
+ """
14
+ desktop = QApplication.desktop()
15
+ geo = widget.geometry()
16
+ for screen in range(desktop.screenCount()):
17
+ monGeo = desktop.screenGeometry(screen)
18
+ if monGeo.intersects(geo):
19
+ break
20
+ else:
21
+ geo.moveTo(monGeo.x() + 7, monGeo.y() + 30)
22
+ # setting the geometry may trigger a second check if setGeometry is overridden
23
+ disable = hasattr(widget, 'checkScreenGeo') and widget.checkScreenGeo
24
+ if disable:
25
+ widget.checkScreenGeo = False
26
+ widget.setGeometry(geo)
27
+ if disable:
28
+ widget.checkScreenGeo = True
29
+ return True
30
+ return False
@@ -0,0 +1,54 @@
1
+ from __future__ import absolute_import
2
+
3
+ import glob
4
+ import os
5
+
6
+
7
+ def read_stylesheet(stylesheet='', path=None):
8
+ """Returns the contents of the requested stylesheet.
9
+
10
+ Args:
11
+
12
+ stylesheet (str): the name of the stylesheet. Attempt to load stylesheet.css
13
+ shipped with preditor. Ignored if path is provided.
14
+
15
+ path (str): Return the contents of this file path.
16
+
17
+ Returns:
18
+ str: The contents of stylesheet or blank if stylesheet was not found.
19
+ valid: A stylesheet was found and loaded.
20
+ """
21
+ if path is None:
22
+ path = os.path.join(
23
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
24
+ 'resource',
25
+ 'stylesheet',
26
+ '{}.css'.format(stylesheet),
27
+ )
28
+ if os.path.isfile(path):
29
+ with open(path) as f:
30
+ return f.read(), True
31
+ return '', False
32
+
33
+
34
+ def stylesheets(subFolder=None):
35
+ """Returns a list of installed stylesheet names.
36
+
37
+ Args:
38
+ subFolder (str or None, optional): Use this to access sub-folders of
39
+ the stylesheet resource directory.
40
+
41
+ Returns:
42
+ list: A list .css file paths in the target directory.
43
+ """
44
+ components = [
45
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
46
+ 'resource',
47
+ 'stylesheet',
48
+ ]
49
+ if subFolder is not None:
50
+ components.append(subFolder)
51
+ cssdir = os.path.join(*components)
52
+ cssfiles = sorted(glob.glob(os.path.join(cssdir, '*.css')))
53
+ # Only return the filename without the .css extension
54
+ return [os.path.splitext(os.path.basename(fp))[0] for fp in cssfiles]