pywargame 0.3.1__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.
- pywargame/__init__.py +2 -0
- pywargame/common/__init__.py +3 -0
- pywargame/common/collector.py +87 -0
- pywargame/common/dicedraw.py +363 -0
- pywargame/common/drawdice.py +40 -0
- pywargame/common/singleton.py +22 -0
- pywargame/common/test.py +25 -0
- pywargame/common/verbose.py +59 -0
- pywargame/common/verboseguard.py +53 -0
- pywargame/cyberboard/__init__.py +18 -0
- pywargame/cyberboard/archive.py +283 -0
- pywargame/cyberboard/base.py +63 -0
- pywargame/cyberboard/board.py +462 -0
- pywargame/cyberboard/cell.py +200 -0
- pywargame/cyberboard/collect.py +49 -0
- pywargame/cyberboard/collectgbx0pwd.py +30 -0
- pywargame/cyberboard/collectgbxext.py +30 -0
- pywargame/cyberboard/collectgsnexp.py +32 -0
- pywargame/cyberboard/collectgsnext.py +30 -0
- pywargame/cyberboard/draw.py +396 -0
- pywargame/cyberboard/exporter.py +1132 -0
- pywargame/cyberboard/extractor.py +240 -0
- pywargame/cyberboard/features.py +17 -0
- pywargame/cyberboard/gamebox.py +81 -0
- pywargame/cyberboard/gbxexp.py +76 -0
- pywargame/cyberboard/gbxext.py +64 -0
- pywargame/cyberboard/gsnexp.py +147 -0
- pywargame/cyberboard/gsnext.py +59 -0
- pywargame/cyberboard/head.py +111 -0
- pywargame/cyberboard/image.py +76 -0
- pywargame/cyberboard/main.py +47 -0
- pywargame/cyberboard/mark.py +102 -0
- pywargame/cyberboard/palette.py +36 -0
- pywargame/cyberboard/piece.py +169 -0
- pywargame/cyberboard/player.py +36 -0
- pywargame/cyberboard/scenario.py +115 -0
- pywargame/cyberboard/testgrid.py +156 -0
- pywargame/cyberboard/tile.py +121 -0
- pywargame/cyberboard/tray.py +68 -0
- pywargame/cyberboard/windows.py +41 -0
- pywargame/cyberboard/zeropwd.py +45 -0
- pywargame/cyberboard.py +2728 -0
- pywargame/gbx0pwd.py +2776 -0
- pywargame/gbxextract.py +2795 -0
- pywargame/gsnexport.py +16499 -0
- pywargame/gsnextract.py +2790 -0
- pywargame/latex/__init__.py +2 -0
- pywargame/latex/collect.py +34 -0
- pywargame/latex/latexexporter.py +4010 -0
- pywargame/latex/main.py +184 -0
- pywargame/vassal/__init__.py +66 -0
- pywargame/vassal/base.py +139 -0
- pywargame/vassal/board.py +243 -0
- pywargame/vassal/buildfile.py +60 -0
- pywargame/vassal/chart.py +79 -0
- pywargame/vassal/chessclock.py +197 -0
- pywargame/vassal/collect.py +98 -0
- pywargame/vassal/collectpatch.py +28 -0
- pywargame/vassal/command.py +21 -0
- pywargame/vassal/documentation.py +322 -0
- pywargame/vassal/dumpcollect.py +28 -0
- pywargame/vassal/dumpvsav.py +28 -0
- pywargame/vassal/element.py +439 -0
- pywargame/vassal/exporter.py +89 -0
- pywargame/vassal/extension.py +101 -0
- pywargame/vassal/folder.py +103 -0
- pywargame/vassal/game.py +940 -0
- pywargame/vassal/gameelements.py +1091 -0
- pywargame/vassal/globalkey.py +127 -0
- pywargame/vassal/globalproperty.py +433 -0
- pywargame/vassal/grid.py +573 -0
- pywargame/vassal/map.py +1061 -0
- pywargame/vassal/mapelements.py +1020 -0
- pywargame/vassal/merge.py +57 -0
- pywargame/vassal/merger.py +460 -0
- pywargame/vassal/moduledata.py +275 -0
- pywargame/vassal/mrgcollect.py +31 -0
- pywargame/vassal/patch.py +44 -0
- pywargame/vassal/patchcollect.py +28 -0
- pywargame/vassal/player.py +83 -0
- pywargame/vassal/save.py +495 -0
- pywargame/vassal/skel.py +380 -0
- pywargame/vassal/trait.py +224 -0
- pywargame/vassal/traits/__init__.py +36 -0
- pywargame/vassal/traits/area.py +50 -0
- pywargame/vassal/traits/basic.py +35 -0
- pywargame/vassal/traits/calculatedproperty.py +22 -0
- pywargame/vassal/traits/cargo.py +29 -0
- pywargame/vassal/traits/click.py +41 -0
- pywargame/vassal/traits/clone.py +28 -0
- pywargame/vassal/traits/delete.py +24 -0
- pywargame/vassal/traits/deselect.py +32 -0
- pywargame/vassal/traits/dynamicproperty.py +112 -0
- pywargame/vassal/traits/globalcommand.py +55 -0
- pywargame/vassal/traits/globalhotkey.py +26 -0
- pywargame/vassal/traits/globalproperty.py +54 -0
- pywargame/vassal/traits/hide.py +67 -0
- pywargame/vassal/traits/label.py +76 -0
- pywargame/vassal/traits/layer.py +105 -0
- pywargame/vassal/traits/mark.py +20 -0
- pywargame/vassal/traits/mask.py +85 -0
- pywargame/vassal/traits/mat.py +26 -0
- pywargame/vassal/traits/moved.py +35 -0
- pywargame/vassal/traits/movefixed.py +51 -0
- pywargame/vassal/traits/nonrect.py +95 -0
- pywargame/vassal/traits/nostack.py +55 -0
- pywargame/vassal/traits/place.py +104 -0
- pywargame/vassal/traits/prototype.py +20 -0
- pywargame/vassal/traits/report.py +34 -0
- pywargame/vassal/traits/restrictaccess.py +28 -0
- pywargame/vassal/traits/restrictcommand.py +32 -0
- pywargame/vassal/traits/return.py +40 -0
- pywargame/vassal/traits/rotate.py +62 -0
- pywargame/vassal/traits/sendto.py +59 -0
- pywargame/vassal/traits/sheet.py +129 -0
- pywargame/vassal/traits/skel.py +9 -0
- pywargame/vassal/traits/stack.py +28 -0
- pywargame/vassal/traits/submenu.py +27 -0
- pywargame/vassal/traits/trail.py +61 -0
- pywargame/vassal/traits/trigger.py +72 -0
- pywargame/vassal/turn.py +272 -0
- pywargame/vassal/upgrade.py +191 -0
- pywargame/vassal/vmod.py +323 -0
- pywargame/vassal/vsav.py +100 -0
- pywargame/vassal/widget.py +358 -0
- pywargame/vassal/withtraits.py +634 -0
- pywargame/vassal/xml.py +4 -0
- pywargame/vassal/zone.py +399 -0
- pywargame/vassal.py +12500 -0
- pywargame/vmodpatch.py +12548 -0
- pywargame/vsavdump.py +12533 -0
- pywargame/vslmerge.py +13015 -0
- pywargame/wgexport.py +16689 -0
- pywargame/ztexport.py +14351 -0
- pywargame/zuntzu/__init__.py +5 -0
- pywargame/zuntzu/base.py +82 -0
- pywargame/zuntzu/collect.py +38 -0
- pywargame/zuntzu/countersheet.py +250 -0
- pywargame/zuntzu/dicehand.py +48 -0
- pywargame/zuntzu/exporter.py +936 -0
- pywargame/zuntzu/gamebox.py +154 -0
- pywargame/zuntzu/map.py +36 -0
- pywargame/zuntzu/piece.py +37 -0
- pywargame/zuntzu/scenario.py +208 -0
- pywargame/zuntzu/ztexp.py +115 -0
- pywargame-0.3.1.dist-info/METADATA +353 -0
- pywargame-0.3.1.dist-info/RECORD +150 -0
- pywargame-0.3.1.dist-info/WHEEL +5 -0
- pywargame-0.3.1.dist-info/licenses/LICENSE +5 -0
- pywargame-0.3.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,439 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from common import VerboseGuard
|
3
|
+
from . base import *
|
4
|
+
## END_IMPORT
|
5
|
+
|
6
|
+
# ====================================================================
|
7
|
+
class Element:
|
8
|
+
BUILD = 'VASSAL.build.'
|
9
|
+
MODULE = BUILD + 'module.'
|
10
|
+
WIDGET = BUILD + 'widget.'
|
11
|
+
FOLDER = MODULE + 'folder.'
|
12
|
+
MAP = MODULE + 'map.'
|
13
|
+
PICKER = MAP + 'boardPicker.'
|
14
|
+
BOARD = PICKER + 'board.'
|
15
|
+
known_tags = {}
|
16
|
+
unique_attrs = {}
|
17
|
+
|
18
|
+
def __init__(self,parent,tag,node=None,**kwargs):
|
19
|
+
'''Create a new element
|
20
|
+
|
21
|
+
Parameters
|
22
|
+
----------
|
23
|
+
parent : Element
|
24
|
+
Parent element to add this element to
|
25
|
+
tag : str
|
26
|
+
Element tag
|
27
|
+
node : xml.dom.Node
|
28
|
+
If not None, then read attributes from that. Otherwise
|
29
|
+
set elements according to kwargs
|
30
|
+
kwargs : dict
|
31
|
+
Attribute keys and values. Only used if node is None
|
32
|
+
'''
|
33
|
+
#from xml.dom.minidom import Document
|
34
|
+
|
35
|
+
if parent is not None:
|
36
|
+
self._tag = tag
|
37
|
+
self._root = (parent if isinstance(parent,xmlns.Document) else
|
38
|
+
parent._root)
|
39
|
+
self._node = (node if node is not None else
|
40
|
+
parent.addNode(tag,**kwargs))
|
41
|
+
else:
|
42
|
+
self._root = None
|
43
|
+
self._node = None
|
44
|
+
self._tag = None
|
45
|
+
|
46
|
+
# ----------------------------------------------------------------
|
47
|
+
@classmethod
|
48
|
+
def _make_unique(cls,tag,*values):
|
49
|
+
return tag + ('_'+'_'.join(values) if len(values)>0 else '')
|
50
|
+
|
51
|
+
def _unique(self):
|
52
|
+
uattr = Element.unique_attrs.get(self._tag,[])
|
53
|
+
return self._make_unique(self._tag,
|
54
|
+
*[self.getAttribute(a) for a in uattr])
|
55
|
+
|
56
|
+
def __hash__(self):
|
57
|
+
return hash(self._unique())
|
58
|
+
|
59
|
+
def __eq__(self,other):
|
60
|
+
'''Equality comparison - check if to elements are the same.
|
61
|
+
|
62
|
+
This is based on the tag of the elements first, then on the
|
63
|
+
attributes. However, not all attributes should be compared
|
64
|
+
for equality - only thise that are meant to be unique.
|
65
|
+
|
66
|
+
'''
|
67
|
+
#print(f'Compare {self} to {other} ({self._tag},{other._tag})')
|
68
|
+
if not isinstance(other,Element):
|
69
|
+
return False
|
70
|
+
return self.__hash__() == other.__hash__()
|
71
|
+
#if self._tag != other._tag:
|
72
|
+
# return False
|
73
|
+
|
74
|
+
# to be done
|
75
|
+
#
|
76
|
+
# uattr = Element.unique_attrs[self._tag]
|
77
|
+
# attr = self .getAttributes()
|
78
|
+
# oattr = other.getAttributes()
|
79
|
+
|
80
|
+
return True
|
81
|
+
|
82
|
+
|
83
|
+
# ----------------------------------------------------------------
|
84
|
+
# Attributes
|
85
|
+
def __contains__(self,key):
|
86
|
+
'''Check if element has attribute key'''
|
87
|
+
return self.hasAttribute(key)
|
88
|
+
|
89
|
+
def __getitem__(self,key):
|
90
|
+
'''Get attribute key value'''
|
91
|
+
return self.getAttribute(key)
|
92
|
+
|
93
|
+
def __setitem__(self,key,value):
|
94
|
+
'''Set attribute key value'''
|
95
|
+
self.setAttribute(key,value)
|
96
|
+
|
97
|
+
def hasAttribute(self,k):
|
98
|
+
'''Check if element has attribute '''
|
99
|
+
return self._node.hasAttribute(k)
|
100
|
+
|
101
|
+
def getAttribute(self,k):
|
102
|
+
'''Get attribute key value'''
|
103
|
+
return self._node.getAttribute(k)
|
104
|
+
|
105
|
+
def setAttribute(self,k,v):
|
106
|
+
'''Set attribute key value'''
|
107
|
+
self._node.setAttribute(k,str(v).lower()
|
108
|
+
if isinstance(v,bool) else str(v))
|
109
|
+
|
110
|
+
def setAttributes(self,**kwargs):
|
111
|
+
'''Set attributes to dictionary key and value'''
|
112
|
+
for k,v in kwargs.items():
|
113
|
+
self.setAttribute(k,v)
|
114
|
+
|
115
|
+
def getAttributes(self):
|
116
|
+
'''Get attributes as dict'''
|
117
|
+
return self._node.attributes
|
118
|
+
|
119
|
+
# ----------------------------------------------------------------
|
120
|
+
# Plain nodes
|
121
|
+
def getChildren(self):
|
122
|
+
'''Get child nodes (xml.dom.Node)'''
|
123
|
+
return self._node.childNodes
|
124
|
+
|
125
|
+
# ----------------------------------------------------------------
|
126
|
+
# Getters
|
127
|
+
#
|
128
|
+
# First generics
|
129
|
+
def getAsDict(self,tag='',key=None,enable=True):
|
130
|
+
'''Get elements with a specific tag as a dictionary
|
131
|
+
where the key is given by attribute key'''
|
132
|
+
cont = self._node.getElementsByTagName(tag)
|
133
|
+
if not enable or key is None:
|
134
|
+
return cont
|
135
|
+
|
136
|
+
return {e.getAttribute(key): e for e in cont}
|
137
|
+
|
138
|
+
def getAsOne(self,tag='',single=True):
|
139
|
+
'''Get elements with a specific tag, as a list.
|
140
|
+
If single is true, then assume we only have one such
|
141
|
+
child element, or fail.'''
|
142
|
+
cont = self._node.getElementsByTagName(tag)
|
143
|
+
if single and len(cont) != 1:
|
144
|
+
return None
|
145
|
+
return cont
|
146
|
+
|
147
|
+
def getElementsByKey(self,cls,key='',asdict=True):
|
148
|
+
'''Get elments of a specific class as a dictionary,
|
149
|
+
where the key is set by the key attribute.'''
|
150
|
+
cont = self.getAsDict(cls.TAG,key,asdict)
|
151
|
+
if cont is None: return None
|
152
|
+
|
153
|
+
if not asdict: return [cls(self,node=n) for n in cont]
|
154
|
+
|
155
|
+
return {k : cls(self,node=n) for k, n in cont.items()}
|
156
|
+
|
157
|
+
def getAllElements(self,cls,single=True):
|
158
|
+
'''Get elements with a specific tag, as a list. If single is
|
159
|
+
true, then assume we only have one such child element, or
|
160
|
+
fail.
|
161
|
+
|
162
|
+
If `cls` is None, then return _all_ child elements.
|
163
|
+
|
164
|
+
'''
|
165
|
+
#from xml.dom.minidom import Text, Element as XMLElement
|
166
|
+
if cls is None:
|
167
|
+
ret = []
|
168
|
+
for node in self.getChildren():
|
169
|
+
if isinstance(node,xmlns.Text):
|
170
|
+
continue
|
171
|
+
|
172
|
+
if not hasattr(node,'tagName'):
|
173
|
+
print(f'Do not know how to deal with {type(node)}')
|
174
|
+
continue
|
175
|
+
|
176
|
+
tag = node.tagName
|
177
|
+
cls = Element.getTagClass(tag)
|
178
|
+
if cls is None:
|
179
|
+
raise RuntimeError(f'No class reflection of tag {tag}')
|
180
|
+
|
181
|
+
ret.append(cls(self,node=node))
|
182
|
+
|
183
|
+
return ret
|
184
|
+
|
185
|
+
cont = self.getAsOne(cls.TAG,single=single)
|
186
|
+
if cont is None: return None
|
187
|
+
return [cls(self,node=n) for n in cont]
|
188
|
+
|
189
|
+
def getSpecificElements(self,cls,key,*names,asdict=True):
|
190
|
+
'''Get all elements of specific class and that has the
|
191
|
+
attribute key, and the attribute value is in names
|
192
|
+
|
193
|
+
'''
|
194
|
+
cont = self.getAsOne(cls.TAG,single=False)
|
195
|
+
cand = [cls(self,node=n) for n in cont
|
196
|
+
if n.getAttribute(key) in names]
|
197
|
+
if asdict:
|
198
|
+
return {c[key] : c for c in cand}
|
199
|
+
return cand
|
200
|
+
|
201
|
+
def getParent(self,cls=None,checkTag=True):
|
202
|
+
if self._node.parentNode is None:
|
203
|
+
return None
|
204
|
+
if cls is None:
|
205
|
+
cls = self.getTagClass(self._node.parentNode.tagName)
|
206
|
+
checkTag = False
|
207
|
+
if cls is None:
|
208
|
+
return None
|
209
|
+
if checkTag and self._node.parentNode.tagName != cls.TAG:
|
210
|
+
return None
|
211
|
+
return cls(self,node=self._node.parentNode)
|
212
|
+
|
213
|
+
def getParentOfClass(self,cls):
|
214
|
+
'''Searches back until we find the parent with the right
|
215
|
+
class, or none
|
216
|
+
'''
|
217
|
+
try:
|
218
|
+
iter(cls)
|
219
|
+
except:
|
220
|
+
cls = [cls]
|
221
|
+
t = {c.TAG: c for c in cls}
|
222
|
+
p = self._node.parentNode
|
223
|
+
while p is not None:
|
224
|
+
c = t.get(p.tagName,None)
|
225
|
+
if c is not None: return c(self,node=p)
|
226
|
+
p = p.parentNode
|
227
|
+
return None
|
228
|
+
|
229
|
+
@classmethod
|
230
|
+
def getTagClass(cls,tag):
|
231
|
+
'''Get class corresponding to the tag'''
|
232
|
+
# if tag not in cls.known_tags: return None;
|
233
|
+
# Older VASSAL may have funny tag-names
|
234
|
+
return cls.known_tags.get(tag,None)
|
235
|
+
|
236
|
+
# ----------------------------------------------------------------
|
237
|
+
# Adders
|
238
|
+
def addNode(self,tag,**attr):
|
239
|
+
'''Add a note to this element
|
240
|
+
|
241
|
+
Parameters
|
242
|
+
----------
|
243
|
+
tag : str
|
244
|
+
Node tag name
|
245
|
+
attr : dict
|
246
|
+
Attributes to set
|
247
|
+
'''
|
248
|
+
e = self._root.createElement(tag)
|
249
|
+
if self._node: self._node.appendChild(e)
|
250
|
+
|
251
|
+
for k, v in attr.items():
|
252
|
+
e.setAttribute(k,str(v).lower() if isinstance(v,bool) else str(v))
|
253
|
+
|
254
|
+
return e
|
255
|
+
|
256
|
+
def addText(self,text):
|
257
|
+
'''Add a text child node to an element'''
|
258
|
+
t = self._root.createTextNode(text)
|
259
|
+
self._node.appendChild(t)
|
260
|
+
return t
|
261
|
+
|
262
|
+
def hasText(self):
|
263
|
+
return self._node.firstChild is not None and \
|
264
|
+
self._node.firstChild.nodeType == self._node.firstChild.TEXT_NODE
|
265
|
+
|
266
|
+
def getText(self):
|
267
|
+
'''Get contained text node content'''
|
268
|
+
if self._node.firstChild is None or \
|
269
|
+
self._node.firstChild.nodeType != self._node.firstChild.TEXT_NODE:
|
270
|
+
return ''
|
271
|
+
return self._node.firstChild.nodeValue
|
272
|
+
|
273
|
+
def setText(self,txt):
|
274
|
+
'''Set contained text node content'''
|
275
|
+
if self._node.firstChild is None or \
|
276
|
+
self._node.firstChild.nodeType != self._node.firstChild.TEXT_NODE:
|
277
|
+
return
|
278
|
+
self._node.firstChild.nodeValue = txt
|
279
|
+
|
280
|
+
|
281
|
+
def add(self,cls,**kwargs):
|
282
|
+
'''Add an element and return wrapped in cls object'''
|
283
|
+
return cls(self,node=None,**kwargs)
|
284
|
+
|
285
|
+
def append(self,elem):
|
286
|
+
'''Append and element'''
|
287
|
+
if self._node.appendChild(elem._node):
|
288
|
+
return elem
|
289
|
+
return False
|
290
|
+
|
291
|
+
# ----------------------------------------------------------------
|
292
|
+
def remove(self,elem):
|
293
|
+
'''Remove an element'''
|
294
|
+
try:
|
295
|
+
self._node.removeChild(elem._node)
|
296
|
+
except:
|
297
|
+
return None
|
298
|
+
return elem
|
299
|
+
# ----------------------------------------------------------------
|
300
|
+
def insertBefore(self,toadd,ref):
|
301
|
+
'''Insert an element before another element'''
|
302
|
+
try:
|
303
|
+
self._node.insertBefore(toadd._node,ref._node)
|
304
|
+
except:
|
305
|
+
return None
|
306
|
+
return toadd
|
307
|
+
|
308
|
+
# ----------------------------------------------------------------
|
309
|
+
def print(self,file=None,recursive=False,indent=''):
|
310
|
+
'''Print this element, and possibly its child elements.
|
311
|
+
|
312
|
+
If `file` is None, then print to stdout. If `recursive` is
|
313
|
+
`True`, then also print child elements. If `recursive` is an
|
314
|
+
integer, then print this many deep levels of child elements.
|
315
|
+
|
316
|
+
'''
|
317
|
+
if file is None:
|
318
|
+
from sys import stdout
|
319
|
+
file = stdout
|
320
|
+
|
321
|
+
from io import StringIO
|
322
|
+
from textwrap import indent as i
|
323
|
+
|
324
|
+
stream = StringIO()
|
325
|
+
|
326
|
+
print(f'Element TAG={self.TAG} CLS={self.__class__.__name__}',
|
327
|
+
file=stream)
|
328
|
+
attrs = self.getAttributes()
|
329
|
+
#print(type(attrs))
|
330
|
+
ln = max([len(n) for n in attrs.keys()]+[0])
|
331
|
+
for name,value in attrs.items():
|
332
|
+
print(f' {name:{ln}s}: {value}',file=stream)
|
333
|
+
|
334
|
+
if isinstance(recursive,bool):
|
335
|
+
recursive = 1024 if recursive else 0# Large number
|
336
|
+
|
337
|
+
if recursive > 1:
|
338
|
+
for child in self.getAllElements(cls=None):
|
339
|
+
child.print(file=stream,
|
340
|
+
recursive=recursive-1,
|
341
|
+
indent=' ')
|
342
|
+
else:
|
343
|
+
n = len(self.getChildren())
|
344
|
+
if n > 0:
|
345
|
+
print(f' {n} child elements',file=stream)
|
346
|
+
|
347
|
+
print(i(stream.getvalue(),indent).rstrip(),file=file)
|
348
|
+
|
349
|
+
|
350
|
+
|
351
|
+
# --------------------------------------------------------------------
|
352
|
+
class DummyElement(Element):
|
353
|
+
def __init__(self,parent,node=None,**kwargs):
|
354
|
+
'''A dummy element we can use to select elements of different
|
355
|
+
classes
|
356
|
+
|
357
|
+
'''
|
358
|
+
super(DummyElement,self).__init__(parent,'Dummy',node=node)
|
359
|
+
|
360
|
+
# --------------------------------------------------------------------
|
361
|
+
class ToolbarElement(Element):
|
362
|
+
def __init__(self,
|
363
|
+
parent,
|
364
|
+
tag,
|
365
|
+
node = None,
|
366
|
+
name = '', # Toolbar element name
|
367
|
+
tooltip = '', # Tool tip
|
368
|
+
text = '', # Button text
|
369
|
+
icon = '', # Button icon,
|
370
|
+
hotkey = '', # Named key or key stroke
|
371
|
+
canDisable = False,
|
372
|
+
propertyGate = '',
|
373
|
+
disabledIcon = '',
|
374
|
+
**kwargs):
|
375
|
+
'''Base class for toolbar elements.
|
376
|
+
|
377
|
+
Parameters
|
378
|
+
----------
|
379
|
+
parent : Element
|
380
|
+
Parent element if any
|
381
|
+
tag : str
|
382
|
+
Element tag
|
383
|
+
node : XMLNode
|
384
|
+
Possible node - when reading back
|
385
|
+
name : str
|
386
|
+
Name of element (user reminder). If not set, and tooltip is set,
|
387
|
+
set to tooltip
|
388
|
+
toolttip : str
|
389
|
+
Tool tip when hovering. If not set, and name is set, then
|
390
|
+
use name as tooltip.
|
391
|
+
text : str
|
392
|
+
Text of button
|
393
|
+
icon : str
|
394
|
+
Image path for button image
|
395
|
+
hotkey : str
|
396
|
+
Named key or key-sequence
|
397
|
+
canDisable : bool
|
398
|
+
If true, then the element can be disabled
|
399
|
+
propertyGate : str
|
400
|
+
Name of a global property. When this property is `true`,
|
401
|
+
then this element is _disabled_. Note that this _must_ be
|
402
|
+
the name of a property - it cannot be a BeanShell
|
403
|
+
expression.
|
404
|
+
disabledIcon : str
|
405
|
+
Path to image to use when the element is disabled.
|
406
|
+
kwargs : dict
|
407
|
+
Other attributes to set on the element
|
408
|
+
'''
|
409
|
+
if name == '' and tooltip != '': name = tooltip
|
410
|
+
if name != '' and tooltip == '': tooltip = name
|
411
|
+
|
412
|
+
# Build arguments for super class
|
413
|
+
args = {
|
414
|
+
'node': node,
|
415
|
+
'name': name,
|
416
|
+
'icon': icon,
|
417
|
+
'tooltip': tooltip,
|
418
|
+
'hotkey': hotkey,
|
419
|
+
'canDisable': canDisable,
|
420
|
+
'propertyGate': propertyGate,
|
421
|
+
'disabledIcon': disabledIcon }
|
422
|
+
bt = kwargs.pop('buttonText',None)
|
423
|
+
# If the element expects buttonText attribute, then do not set
|
424
|
+
# the text attribute - some elements interpret that as a
|
425
|
+
# legacy name attribute,
|
426
|
+
if bt is not None:
|
427
|
+
args['buttonText'] = bt
|
428
|
+
else:
|
429
|
+
args['text'] = text
|
430
|
+
args.update(kwargs)
|
431
|
+
|
432
|
+
super(ToolbarElement,self).__init__(parent,
|
433
|
+
tag,
|
434
|
+
**args)
|
435
|
+
# print('Attributes\n','\n'.join([f'- {k}="{v}"' for k,v in self._node.attributes.items()]))
|
436
|
+
|
437
|
+
#
|
438
|
+
# EOF
|
439
|
+
#
|
@@ -0,0 +1,89 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from vassal.vmod import VMod
|
3
|
+
from common import VerboseGuard, Verbose
|
4
|
+
## END_IMPORT
|
5
|
+
|
6
|
+
class Exporter:
|
7
|
+
def __init__(self):
|
8
|
+
'''Base class for exporters'''
|
9
|
+
pass
|
10
|
+
|
11
|
+
|
12
|
+
def setup(self):
|
13
|
+
'''Should be defined to set-up for processing, for example
|
14
|
+
generating images and such. This is executed in a context
|
15
|
+
where the VMod file has been opened for writing via
|
16
|
+
`self._vmod`. Thus, files can be added to the module at this
|
17
|
+
stage.
|
18
|
+
'''
|
19
|
+
pass
|
20
|
+
|
21
|
+
def createBuildFile(self,ignores='(.*markers?|all|commons|[ ]+)'):
|
22
|
+
'''Should be defined to make the `buildFile.xml` document
|
23
|
+
|
24
|
+
Parameters
|
25
|
+
----------
|
26
|
+
ignores : str
|
27
|
+
Regular expression to match ignored categories for factions
|
28
|
+
determination. Python's re.fullmatch is applied to this
|
29
|
+
regular exression against chit categories. If the pattern
|
30
|
+
is matched, then the chit is not considered to belong to a
|
31
|
+
faction.
|
32
|
+
|
33
|
+
'''
|
34
|
+
pass
|
35
|
+
|
36
|
+
def createModuleData(self):
|
37
|
+
'''Should be defined to make the `moduledata` document'''
|
38
|
+
pass
|
39
|
+
|
40
|
+
def run(self,vmodname,patch=None):
|
41
|
+
'''Run the exporter to generate the module
|
42
|
+
'''
|
43
|
+
with VMod(vmodname,'w') as vmod:
|
44
|
+
self._vmod = vmod
|
45
|
+
self.setup()
|
46
|
+
self.createBuildFile()
|
47
|
+
self.createModuleData()
|
48
|
+
self.runPatch(patch)
|
49
|
+
self._vmod.addFiles(**{VMod.BUILD_FILE :
|
50
|
+
self._build.encode(),
|
51
|
+
VMod.MODULE_DATA :
|
52
|
+
self._moduleData.encode()})
|
53
|
+
Verbose().message('Created VMOD')
|
54
|
+
|
55
|
+
|
56
|
+
def runPatch(self,patch):
|
57
|
+
'''Run user specified patch script. The script should define
|
58
|
+
|
59
|
+
```
|
60
|
+
def patch(buildFile,moduleData,vmod,verbose):
|
61
|
+
```
|
62
|
+
|
63
|
+
where `buildFile` is the `buildFile.xml` and `moduleData` are
|
64
|
+
the XML documents as `xml.dom.Document` objects, `vmod` is a
|
65
|
+
`VMod` instance, and `verbose` is a `bool` selecting verbose
|
66
|
+
mode or not.
|
67
|
+
'''
|
68
|
+
if patch is None or patch == '':
|
69
|
+
return
|
70
|
+
|
71
|
+
from importlib.util import spec_from_file_location, module_from_spec
|
72
|
+
from pathlib import Path
|
73
|
+
from sys import modules
|
74
|
+
|
75
|
+
p = Path(patch)
|
76
|
+
with VerboseGuard(f'Will patch module with {p.stem}.patch function') \
|
77
|
+
as v:
|
78
|
+
|
79
|
+
spec = spec_from_file_location(p.stem, p.absolute())
|
80
|
+
module = module_from_spec(spec)
|
81
|
+
spec.loader.exec_module(module)
|
82
|
+
modules[p.stem] = module
|
83
|
+
|
84
|
+
# Patch must accept xml.dom.document,xml.dom.document,ZipFile
|
85
|
+
module.patch(self._build,
|
86
|
+
self._moduleData,
|
87
|
+
self._vmod,
|
88
|
+
Verbose().verbose)
|
89
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from common import VerboseGuard
|
3
|
+
from . base import *
|
4
|
+
from . element import Element
|
5
|
+
from . game import Game
|
6
|
+
## END_IMPORT
|
7
|
+
|
8
|
+
# ====================================================================
|
9
|
+
class Extension(Element):
|
10
|
+
TAG = Element.MODULE+'ModuleExtension'
|
11
|
+
def __init__(self,
|
12
|
+
parent = None,
|
13
|
+
node = None,
|
14
|
+
anyModule = False,
|
15
|
+
version = '',
|
16
|
+
description = '',
|
17
|
+
module = '',
|
18
|
+
moduleVersion = '',
|
19
|
+
vassalVersion = '',
|
20
|
+
nextPieceSlotId = 0,
|
21
|
+
extensionId = 0,
|
22
|
+
asDocument = False):
|
23
|
+
super().__init__(parent,self.TAG,node)
|
24
|
+
|
25
|
+
self._tag = 'extension'
|
26
|
+
if self._node is None:
|
27
|
+
#from xml.dom.minidom import Document
|
28
|
+
|
29
|
+
|
30
|
+
self._root = xmlns.Document()
|
31
|
+
self._node = self._root
|
32
|
+
self.setAttributes(
|
33
|
+
anyModule = anyModule,
|
34
|
+
version = version,
|
35
|
+
description = description,
|
36
|
+
module = module,
|
37
|
+
moduleVersion = moduleVersion,
|
38
|
+
vassalVersion = vassalVersion,
|
39
|
+
nextPieceSlotId = nextPieceSlotId,
|
40
|
+
extensionId = extensionId)
|
41
|
+
|
42
|
+
def addExtensionElement(self,**kwargs):
|
43
|
+
'''Add an extension element'''
|
44
|
+
|
45
|
+
return self.add(ExtensionElement,**kwargs)
|
46
|
+
|
47
|
+
# ----------------------------------------------------------------
|
48
|
+
def getExtensionElements(self,asdict=True):
|
49
|
+
'''Get all or a sole `GlobalPropertie` element(s) from this
|
50
|
+
|
51
|
+
Parameters
|
52
|
+
----------
|
53
|
+
asdict : bool
|
54
|
+
If `True`, return a dictonary that maps key to
|
55
|
+
`ExtensionElement` elements. If `False`, return a list of
|
56
|
+
all `ExtensionElement` children.
|
57
|
+
|
58
|
+
Returns
|
59
|
+
-------
|
60
|
+
children : dict or list
|
61
|
+
Dictionary or list of `Extension` children
|
62
|
+
|
63
|
+
'''
|
64
|
+
return self.getElementsByKey(ExtensionElement,'target',asdict)
|
65
|
+
|
66
|
+
registerElement(Extension)
|
67
|
+
|
68
|
+
# --------------------------------------------------------------------
|
69
|
+
class ExtensionElement(Element):
|
70
|
+
TAG = Element.MODULE + 'ExtensionElement'
|
71
|
+
UNIQUE = ['target']
|
72
|
+
|
73
|
+
def __init__(self,
|
74
|
+
extension,
|
75
|
+
node = None,
|
76
|
+
target = ''):
|
77
|
+
super().__init__(extension,
|
78
|
+
self.TAG,
|
79
|
+
node = node,
|
80
|
+
target = target)
|
81
|
+
|
82
|
+
|
83
|
+
def getTarget(self):
|
84
|
+
return self['target']
|
85
|
+
|
86
|
+
@property
|
87
|
+
def target(self):
|
88
|
+
return self.getTarget()
|
89
|
+
|
90
|
+
def getSelect(self):
|
91
|
+
parts = self.target.split('/')
|
92
|
+
specs = [p.split(':') for p in parts]
|
93
|
+
return specs
|
94
|
+
|
95
|
+
|
96
|
+
registerElement(ExtensionElement)
|
97
|
+
|
98
|
+
# --------------------------------------------------------------------
|
99
|
+
#
|
100
|
+
# EOF
|
101
|
+
#
|