tomwidgets 0.2.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.
Files changed (139) hide show
  1. tomwidgets/Template/Template.py +231 -0
  2. tomwidgets/Template/TemplateGroup.py +141 -0
  3. tomwidgets/Template/Template_back.py +616 -0
  4. tomwidgets/Template/__init__.py +10 -0
  5. tomwidgets/__init__.py +88 -0
  6. tomwidgets/examples/__init__.py +44 -0
  7. tomwidgets/examples/basetool_example.py +310 -0
  8. tomwidgets/examples/btnbar_example.py +250 -0
  9. tomwidgets/examples/cmdeditor_example.py +62 -0
  10. tomwidgets/examples/cmdhistory_example.py +236 -0
  11. tomwidgets/examples/cmdmgr_example.py +305 -0
  12. tomwidgets/examples/cmdwin_example.py +78 -0
  13. tomwidgets/examples/codetool_example.py +30 -0
  14. tomwidgets/examples/codewin_example.py +269 -0
  15. tomwidgets/examples/combobar_example.py +232 -0
  16. tomwidgets/examples/configeditor_example.py +128 -0
  17. tomwidgets/examples/configwin_example.py +77 -0
  18. tomwidgets/examples/crawler_example.py +25 -0
  19. tomwidgets/examples/dictview_example.py +40 -0
  20. tomwidgets/examples/editbar_example.py +194 -0
  21. tomwidgets/examples/emoji_example.py +53 -0
  22. tomwidgets/examples/eventbus_example.py +222 -0
  23. tomwidgets/examples/folderbar_example.py +177 -0
  24. tomwidgets/examples/frame_example.py +564 -0
  25. tomwidgets/examples/icontool_example.py +23 -0
  26. tomwidgets/examples/infobox_example.py +274 -0
  27. tomwidgets/examples/inputbar_example.py +214 -0
  28. tomwidgets/examples/inputbox_example.py +335 -0
  29. tomwidgets/examples/inputdialog_example.py +263 -0
  30. tomwidgets/examples/inputlistbar_example.py +88 -0
  31. tomwidgets/examples/jsoneditor_example.py +139 -0
  32. tomwidgets/examples/jsonfile_example.py +101 -0
  33. tomwidgets/examples/menubtn_example.py +219 -0
  34. tomwidgets/examples/optionbar_example.py +92 -0
  35. tomwidgets/examples/optionmenu_example.py +236 -0
  36. tomwidgets/examples/popmenu_example.py +160 -0
  37. tomwidgets/examples/pyinstall_example.py +22 -0
  38. tomwidgets/examples/retool_example.py +27 -0
  39. tomwidgets/examples/segoe_example.py +51 -0
  40. tomwidgets/examples/singletonbase_example.py +255 -0
  41. tomwidgets/examples/stapling_example.py +214 -0
  42. tomwidgets/examples/tabview_example.py +276 -0
  43. tomwidgets/examples/template_example.py +172 -0
  44. tomwidgets/examples/templategroup_example.py +411 -0
  45. tomwidgets/examples/templatewin_example.py +65 -0
  46. tomwidgets/examples/templatex_example.py +196 -0
  47. tomwidgets/examples/text_example.py +234 -0
  48. tomwidgets/examples/textbar_example.py +471 -0
  49. tomwidgets/examples/textbox_example.py +417 -0
  50. tomwidgets/examples/texttwo_example.py +78 -0
  51. tomwidgets/examples/titlebar_example.py +338 -0
  52. tomwidgets/examples/toolwin_example.py +9 -0
  53. tomwidgets/examples/urltool_example.py +22 -0
  54. tomwidgets/examples/visiblebtn_example.py +502 -0
  55. tomwidgets/examples/wrapbox_example.py +234 -0
  56. tomwidgets/examples/wrapbtnbar_example.py +250 -0
  57. tomwidgets/model/Cmd.py +12 -0
  58. tomwidgets/model/CmdHistory.py +233 -0
  59. tomwidgets/model/Emoji.py +1990 -0
  60. tomwidgets/model/Segoe.py +95 -0
  61. tomwidgets/model/__init__.py +4 -0
  62. tomwidgets/tools/CodeTool.py +261 -0
  63. tomwidgets/tools/Crawler.py +252 -0
  64. tomwidgets/tools/IconTool.py +45 -0
  65. tomwidgets/tools/PyInstall.py +127 -0
  66. tomwidgets/tools/RETool.py +509 -0
  67. tomwidgets/tools/TextTwo.py +498 -0
  68. tomwidgets/tools/UrlTool.py +226 -0
  69. tomwidgets/tools/Urls.py +148 -0
  70. tomwidgets/tools/__init__.py +13 -0
  71. tomwidgets/util/ClassUtil.py +90 -0
  72. tomwidgets/util/EventBus.py +48 -0
  73. tomwidgets/util/JsonFile.py +88 -0
  74. tomwidgets/util/ModuleUtil.py +24 -0
  75. tomwidgets/util/__init__.py +10 -0
  76. tomwidgets/widget/BaseWin.py +278 -0
  77. tomwidgets/widget/BtnBar.py +346 -0
  78. tomwidgets/widget/CmdEditor.py +237 -0
  79. tomwidgets/widget/CmdMgr.py +95 -0
  80. tomwidgets/widget/CmdWin.py +311 -0
  81. tomwidgets/widget/CodeWin.py +602 -0
  82. tomwidgets/widget/ComboBar.py +214 -0
  83. tomwidgets/widget/Config.py +151 -0
  84. tomwidgets/widget/ConfigEditor.py +371 -0
  85. tomwidgets/widget/ConfigWin.py +229 -0
  86. tomwidgets/widget/DictView.py +198 -0
  87. tomwidgets/widget/EditBar.py +264 -0
  88. tomwidgets/widget/FolderBar.py +311 -0
  89. tomwidgets/widget/InfoBox.py +180 -0
  90. tomwidgets/widget/InputBar.py +60 -0
  91. tomwidgets/widget/InputBox.py +170 -0
  92. tomwidgets/widget/InputListBar.py +244 -0
  93. tomwidgets/widget/JsonEditor.py +325 -0
  94. tomwidgets/widget/MenuBtn.py +120 -0
  95. tomwidgets/widget/OptionBar.py +248 -0
  96. tomwidgets/widget/PopMenu.py +31 -0
  97. tomwidgets/widget/Settings.py +135 -0
  98. tomwidgets/widget/Stapling.py +104 -0
  99. tomwidgets/widget/TemplateWin.py +414 -0
  100. tomwidgets/widget/TextBar.py +706 -0
  101. tomwidgets/widget/Theme.py +298 -0
  102. tomwidgets/widget/TitleBar.py +337 -0
  103. tomwidgets/widget/ToolWin.py +334 -0
  104. tomwidgets/widget/VisibleBtn.py +230 -0
  105. tomwidgets/widget/WinMgr.py +65 -0
  106. tomwidgets/widget/WrapBox.py +176 -0
  107. tomwidgets/widget/WrapBtnBar.py +317 -0
  108. tomwidgets/widget/__init__.py +65 -0
  109. tomwidgets/widget/basic/BaseWidget.py +73 -0
  110. tomwidgets/widget/basic/Button.py +13 -0
  111. tomwidgets/widget/basic/CheckBox.py +8 -0
  112. tomwidgets/widget/basic/ComboBox.py +10 -0
  113. tomwidgets/widget/basic/Dragging.py +62 -0
  114. tomwidgets/widget/basic/Entry.py +7 -0
  115. tomwidgets/widget/basic/EventBus.py +48 -0
  116. tomwidgets/widget/basic/EventHandler.py +36 -0
  117. tomwidgets/widget/basic/Font.py +6 -0
  118. tomwidgets/widget/basic/Frame.py +17 -0
  119. tomwidgets/widget/basic/Image.py +6 -0
  120. tomwidgets/widget/basic/InputDialog.py +10 -0
  121. tomwidgets/widget/basic/Label.py +7 -0
  122. tomwidgets/widget/basic/OptionMenu.py +13 -0
  123. tomwidgets/widget/basic/ProgressBar.py +7 -0
  124. tomwidgets/widget/basic/RadioButton.py +9 -0
  125. tomwidgets/widget/basic/ScrollableFrame.py +21 -0
  126. tomwidgets/widget/basic/Scrollbar.py +7 -0
  127. tomwidgets/widget/basic/SegmentedButton.py +8 -0
  128. tomwidgets/widget/basic/Slider.py +8 -0
  129. tomwidgets/widget/basic/Switch.py +7 -0
  130. tomwidgets/widget/basic/Tabview.py +7 -0
  131. tomwidgets/widget/basic/Text.py +254 -0
  132. tomwidgets/widget/basic/Textbox.py +215 -0
  133. tomwidgets/widget/basic/Tk.py +7 -0
  134. tomwidgets/widget/basic/Toplevel.py +11 -0
  135. tomwidgets/widget/basic/__init__.py +25 -0
  136. tomwidgets/widget/theme/theme_gold.json +369 -0
  137. tomwidgets-0.2.0.dist-info/METADATA +9 -0
  138. tomwidgets-0.2.0.dist-info/RECORD +139 -0
  139. tomwidgets-0.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,231 @@
1
+ """
2
+ TemplateX class that extends Jinja2.Template functionality
3
+ """
4
+ from __future__ import annotations
5
+ import jinja2
6
+ import os
7
+ import re
8
+ from enum import Enum
9
+ from jinja2 import Environment, meta
10
+
11
+
12
+ # Create a Jinja2 environment
13
+ Env = Environment(loader=jinja2.FileSystemLoader(searchpath="./"))
14
+
15
+
16
+ class ConfigKeys(Enum):
17
+ TEMPLATE_TYPE = "template-type"
18
+ TARGET_PATH = "target-path"
19
+ ROOT_PATH = "root-path"
20
+
21
+
22
+ class TemplateType(Enum):
23
+ NEW = "template-new"
24
+ UPDATE = "template-update"
25
+
26
+ def getType(value):
27
+ for item in TemplateType:
28
+ if item.value == value:
29
+ return item
30
+ return None
31
+
32
+
33
+ DefaultTemplateConfig = {
34
+ ConfigKeys.TEMPLATE_TYPE.value: TemplateType.NEW.value,
35
+ ConfigKeys.ROOT_PATH.value: "",
36
+ ConfigKeys.TARGET_PATH.value: "templates/output",
37
+ }
38
+
39
+
40
+ class Template:
41
+ """
42
+ Extended Template class with additional functionality
43
+ """
44
+
45
+ def __init__(self, source=None, filePath=None):
46
+ """
47
+ Create template from source or filePath
48
+ :param source: Template source string
49
+ :param filePath: Path to template file
50
+ """
51
+ self.filePath = filePath
52
+ self.source = source
53
+ self._template = None
54
+ self._config = DefaultTemplateConfig.copy()
55
+
56
+ if source is not None:
57
+ # Create template from source
58
+ self.source = source
59
+ self._template = Env.from_string(source)
60
+ elif filePath is not None and os.path.exists(filePath):
61
+ # Create template from filePath
62
+ with open(filePath, 'r', encoding='utf-8') as f:
63
+ source = f.read()
64
+ self.source = source
65
+ # Load template from file using the environment
66
+ self._template = Env.from_string(source)
67
+ self.filePath = filePath
68
+ else:
69
+ raise ValueError(
70
+ "Either source or filePath must be provided and valid")
71
+
72
+ self.config()
73
+
74
+ def variables(self):
75
+ """
76
+ Return all variables in template content
77
+ """
78
+ return list(GetVariables(self.source))
79
+
80
+ def updateTemplate(self, source=None):
81
+ """
82
+ Update template file with new source
83
+ :param source: New template source
84
+ """
85
+ if source is None:
86
+ return False
87
+
88
+ if self.filePath is None:
89
+ raise ValueError(
90
+ "Template was created from source only, no file path available to update")
91
+
92
+ if not os.path.exists(self.filePath):
93
+ return False
94
+
95
+ self.source = source
96
+ with open(self.filePath, "w", encoding="utf-8") as f:
97
+ f.write(self.source)
98
+
99
+ self.reload()
100
+ return True
101
+
102
+ def reload(self):
103
+ path = self.filePath
104
+ if path is not None and os.path.exists(path):
105
+ # Create template from filePath
106
+ with open(path, 'r', encoding='utf-8') as f:
107
+ source = f.read()
108
+ self.source = source
109
+ # Load template from file using the environment
110
+ self._template = Env.from_string(source)
111
+
112
+ self.config()
113
+
114
+ def saveRenderResult(self, path=None, **kwargs) -> bool:
115
+ """
116
+ Save rendered template to file
117
+ :param path: Path to save the rendered result
118
+ :param kwargs: Variables to render the template with
119
+ """
120
+ try:
121
+ path = path or self.outputFilePath()
122
+
123
+ outputPath = os.path.dirname(path)
124
+ if not os.path.exists(outputPath):
125
+ os.makedirs(outputPath)
126
+
127
+ renderedContent = self._template.render(**kwargs).lstrip()
128
+ with open(path, "w", encoding="utf-8") as f:
129
+ f.write(renderedContent)
130
+ except Exception as e:
131
+ print(f"Error: Failed to save rendered template to {path}: {e}")
132
+ return False
133
+
134
+ return True
135
+
136
+ def render(self, **kwargs):
137
+ """
138
+ Render the template with given variables
139
+ """
140
+ return self._template.render(**kwargs)
141
+
142
+ def comments(self, keys: list[str] = None) -> list[str]:
143
+ """
144
+ Return all comments in template source if keys is None
145
+ If keys is not None, return comments which contain any key
146
+ :param keys: List of keys to search for in comments (None for all comments)
147
+ :return: List of comments
148
+ """
149
+ # Find all Jinja2 comments {# ... #}
150
+ comment_pattern = r'\{#\s*(.*?)\s*#\}'
151
+ all_comments = re.findall(comment_pattern, self.source, re.DOTALL)
152
+
153
+ if keys is None:
154
+ return all_comments
155
+
156
+ # Filter comments that contain any of the keys
157
+ filtered_comments = []
158
+ for comment in all_comments:
159
+ for key in keys:
160
+ if key.lower() in comment.lower():
161
+ filtered_comments.append(comment)
162
+ break # Add comment only once even if it contains multiple keys
163
+
164
+ return filtered_comments
165
+
166
+ @property
167
+ def sourceWithoutComments(self):
168
+ """
169
+ Return the template source without any comments
170
+ """
171
+ # Remove all Jinja2 comments {# ... #} from the source
172
+ comment_pattern = r'\{#\s*.*?\s*#\}'
173
+ return re.sub(comment_pattern, '', self.source, flags=re.DOTALL).lstrip()
174
+
175
+ def config(self, data: dict = None) -> dict:
176
+ """
177
+ if data == None:
178
+ get config in source
179
+ else:
180
+ set config to source
181
+ """
182
+ print(f"config: {data}")
183
+ if data == None:
184
+ self._getConfig()
185
+ else:
186
+ self._setConfig(data)
187
+
188
+ return self._config
189
+
190
+ def _getConfig(self):
191
+ self._config = DefaultTemplateConfig.copy()
192
+ match = self.comments()
193
+
194
+ for k, v in [item.split("=") for item in match if "=" in item]:
195
+ self._config[k.strip()] = v.strip()
196
+
197
+ print(f"getConfig: {self._config}")
198
+
199
+ def _setConfig(self, data: dict):
200
+ for key in data.keys():
201
+ self._config[key] = data[key]
202
+
203
+ print("self._config:", self._config)
204
+ self.source = "\n".join(
205
+ [f"{{# {key}={self._config[key]} #}}" for key in self._config.keys()]) + "\n\n" + self.sourceWithoutComments.lstrip()
206
+
207
+ return self._config
208
+
209
+ def outputPath(self, root: str = None) -> str:
210
+ """
211
+ Get output path from config or use default
212
+ :param root: Root path to use instead of config value (optional)
213
+ :return: Output path
214
+ """
215
+ conf = self.config()
216
+ root = root or conf.get(ConfigKeys.ROOT_PATH.value)
217
+ return os.path.join(root, conf.get(ConfigKeys.TARGET_PATH.value))
218
+
219
+ def outputFilePath(self, root: str = None) -> str:
220
+ return os.path.join(self.outputPath(root=root), os.path.basename(self.filePath))
221
+
222
+ def name(self):
223
+ if self.filePath is None:
224
+ return None
225
+
226
+ return os.path.basename(self.filePath)
227
+
228
+
229
+ def GetVariables(source: str):
230
+ """get variables from template source"""
231
+ return meta.find_undeclared_variables(Env.parse(source))
@@ -0,0 +1,141 @@
1
+ """
2
+ TemplateGroup class to manage a group of Template objects
3
+ """
4
+ import json
5
+ import os
6
+ from typing import Dict, List, Optional
7
+ from .Template import Template, Env
8
+
9
+ ConfigFileName = 'template.json'
10
+ ConfigFolder = "config"
11
+ DefaultConfig = {
12
+ "targetRoot": "templates/output",
13
+ }
14
+
15
+
16
+ class TemplateGroup:
17
+ """
18
+ Class to manage a group of Template objects from a folder
19
+ """
20
+
21
+ def __init__(self, folderPath: Optional[str] = None):
22
+ self.templates: Dict[str, Template] = {}
23
+ self.config = DefaultConfig.copy()
24
+ self.targetRoot = DefaultConfig["targetRoot"]
25
+
26
+ self.folderPath = folderPath
27
+
28
+ configPath = self.configPath = os.path.join(
29
+ folderPath, ConfigFolder, ConfigFileName) if folderPath else None
30
+
31
+ # If config path is provided, load configuration from the file
32
+ if configPath and os.path.exists(configPath):
33
+ self.loadConfig(configPath)
34
+ else:
35
+ # Create default config if config path is provided but file doesn't exist
36
+ if configPath:
37
+ self.createDefaultConfig(configPath)
38
+
39
+ # If folder path is provided, load templates from folder
40
+ if folderPath:
41
+ if folderPath and os.path.exists(folderPath):
42
+ self.loadFromFolder(folderPath)
43
+
44
+ def loadConfig(self, configPath: str = None, config: Dict = DefaultConfig):
45
+ """if configPath is None: load configuration from config"""
46
+ self.config = config.copy()
47
+
48
+ if configPath:
49
+ with open(configPath, 'r', encoding='utf-8') as f:
50
+ config = self.config = json.load(f)
51
+
52
+ self.targetRoot = config.get('targetRoot', None)
53
+
54
+ def createDefaultConfig(self, configPath: str):
55
+ # Ensure the directory exists
56
+ os.makedirs(os.path.dirname(configPath), exist_ok=True)
57
+
58
+ with open(configPath, 'w', encoding='utf-8') as f:
59
+ json.dump(DefaultConfig, f, indent=2)
60
+
61
+ # Load the newly created config
62
+ self.loadConfig()
63
+
64
+ def saveConfig(self, configPath: Optional[str] = None):
65
+ path_to_use = configPath or self.configPath
66
+
67
+ if not path_to_use:
68
+ raise ValueError(
69
+ "No configuration path provided or set during initialization")
70
+
71
+ config = {
72
+ "targetRoot": self.targetRoot,
73
+ }
74
+
75
+ # Ensure the directory exists
76
+ os.makedirs(os.path.dirname(path_to_use), exist_ok=True)
77
+
78
+ with open(path_to_use, 'w', encoding='utf-8') as f:
79
+ json.dump(config, f, indent=2)
80
+
81
+ def reload(self):
82
+ self.templates: Dict[str, Template] = {}
83
+ self.targetRoot: Optional[str] = None
84
+
85
+ self.loadConfig(self.configPath)
86
+ self.loadFromFolder(self.folderPath)
87
+
88
+ def loadFromFolder(self, folderPath: str):
89
+ if not os.path.exists(folderPath):
90
+ raise ValueError(f"Folder path does not exist: {folderPath}")
91
+
92
+ self.folderPath = folderPath
93
+ self.templates = {}
94
+
95
+ for filename in os.listdir(folderPath):
96
+ filePath = os.path.join(folderPath, filename)
97
+ if os.path.isfile(filePath):
98
+ # Create Template from file
99
+ template = Template(filePath=filePath)
100
+ self.templates[filename] = template
101
+
102
+ def get(self, name: str) -> Optional[Template]:
103
+ return self.templates.get(name)
104
+
105
+ def update(self, name: str, template: Template) -> bool:
106
+ self.templates[name] = template
107
+ return True
108
+
109
+ def delete(self, name: str) -> bool:
110
+ if name in self.templates:
111
+ del self.templates[name]
112
+ return True
113
+ return False
114
+
115
+ def getAllVariables(self) -> Dict[str, List[str]]:
116
+ allVariables = {}
117
+ for name, template in self.templates.items():
118
+ allVariables[name] = list(template.variables())
119
+ return allVariables
120
+
121
+ def renderAll(self, save: bool = False, **kwargs) -> Dict[str, str]:
122
+ renderedResults = {}
123
+
124
+ for name, template in self.templates.items():
125
+ params = {}
126
+ params.update(kwargs)
127
+
128
+ renderd = template.render(**params)
129
+ renderedResults[name] = renderd
130
+
131
+ if save:
132
+ path = template.outputFilePath(self.targetRoot)
133
+ template.saveRenderResult(path, params)
134
+
135
+ return renderedResults
136
+
137
+ def listTemplateKeys(self) -> List[str]:
138
+ return list(self.templates.keys())
139
+
140
+ def count(self) -> int:
141
+ return len(self.listTemplateKeys())