visidata 2.11.dev0__py3-none-any.whl → 3.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.
- visidata/__init__.py +72 -91
- visidata/_input.py +263 -44
- visidata/_open.py +84 -29
- visidata/_types.py +22 -4
- visidata/_urlcache.py +17 -4
- visidata/aggregators.py +65 -25
- visidata/apps/__init__.py +0 -0
- visidata/apps/vdsql/__about__.py +8 -0
- visidata/apps/vdsql/__init__.py +5 -0
- visidata/apps/vdsql/__main__.py +27 -0
- visidata/apps/vdsql/_ibis.py +748 -0
- visidata/apps/vdsql/bigquery.py +61 -0
- visidata/apps/vdsql/clickhouse.py +53 -0
- visidata/apps/vdsql/setup.py +40 -0
- visidata/apps/vdsql/snowflake.py +67 -0
- visidata/apps/vgit/__init__.py +13 -0
- visidata/apps/vgit/__main__.py +3 -0
- visidata/apps/vgit/abort.py +23 -0
- visidata/apps/vgit/blame.py +76 -0
- visidata/apps/vgit/branch.py +153 -0
- visidata/apps/vgit/config.py +95 -0
- visidata/apps/vgit/diff.py +169 -0
- visidata/apps/vgit/gitsheet.py +161 -0
- visidata/apps/vgit/grep.py +37 -0
- visidata/apps/vgit/log.py +81 -0
- visidata/apps/vgit/main.py +55 -0
- visidata/apps/vgit/remote.py +57 -0
- visidata/apps/vgit/repos.py +71 -0
- visidata/apps/vgit/setup.py +37 -0
- visidata/apps/vgit/stash.py +69 -0
- visidata/apps/vgit/status.py +204 -0
- visidata/apps/vgit/statusbar.py +34 -0
- visidata/basesheet.py +59 -50
- visidata/canvas.py +251 -99
- visidata/choose.py +15 -11
- visidata/clean_names.py +29 -0
- visidata/clipboard.py +84 -18
- visidata/cliptext.py +220 -46
- visidata/cmdlog.py +89 -114
- visidata/color.py +142 -56
- visidata/column.py +134 -131
- visidata/ddw/input.ddw +74 -79
- visidata/ddw/regex.ddw +57 -0
- visidata/ddwplay.py +33 -14
- visidata/deprecated.py +77 -3
- visidata/desktop/visidata.desktop +7 -0
- visidata/editor.py +12 -6
- visidata/errors.py +5 -1
- visidata/experimental/__init__.py +0 -0
- visidata/experimental/diff_sheet.py +29 -0
- visidata/experimental/digit_autoedit.py +6 -0
- visidata/experimental/gdrive.py +89 -0
- visidata/experimental/google.py +37 -0
- visidata/experimental/gsheets.py +79 -0
- visidata/experimental/live_search.py +37 -0
- visidata/experimental/liveupdate.py +45 -0
- visidata/experimental/mark.py +133 -0
- visidata/experimental/noahs_tapestry/__init__.py +1 -0
- visidata/experimental/noahs_tapestry/tapestry.py +147 -0
- visidata/experimental/rownum.py +73 -0
- visidata/experimental/slide_cells.py +26 -0
- visidata/expr.py +8 -4
- visidata/extensible.py +32 -6
- visidata/features/__init__.py +0 -0
- visidata/features/addcol_audiometadata.py +42 -0
- visidata/features/addcol_histogram.py +34 -0
- visidata/features/canvas_save_svg.py +69 -0
- visidata/features/change_precision.py +46 -0
- visidata/features/cmdpalette.py +163 -0
- visidata/features/colorbrewer.py +363 -0
- visidata/{colorsheet.py → features/colorsheet.py} +17 -16
- visidata/features/command_server.py +105 -0
- visidata/features/currency_to_usd.py +70 -0
- visidata/{customdate.py → features/customdate.py} +2 -0
- visidata/features/dedupe.py +132 -0
- visidata/{describe.py → features/describe.py} +17 -15
- visidata/features/errors_guide.py +26 -0
- visidata/features/expand_cols.py +202 -0
- visidata/{fill.py → features/fill.py} +4 -2
- visidata/{freeze.py → features/freeze.py} +11 -6
- visidata/features/graph_seaborn.py +79 -0
- visidata/features/helloworld.py +10 -0
- visidata/features/hint_types.py +17 -0
- visidata/{incr.py → features/incr.py} +5 -0
- visidata/{join.py → features/join.py} +107 -53
- visidata/features/known_cols.py +21 -0
- visidata/features/layout.py +62 -0
- visidata/{melt.py → features/melt.py} +33 -21
- visidata/features/normcol.py +118 -0
- visidata/features/open_config.py +7 -0
- visidata/features/open_syspaste.py +18 -0
- visidata/features/ping.py +157 -0
- visidata/features/procmgr.py +208 -0
- visidata/features/random_sample.py +6 -0
- visidata/{regex.py → features/regex.py} +47 -31
- visidata/features/reload_every.py +55 -0
- visidata/features/rename_col_cascade.py +30 -0
- visidata/features/scroll_context.py +60 -0
- visidata/features/select_equal_selected.py +11 -0
- visidata/features/setcol_fake.py +65 -0
- visidata/{slide.py → features/slide.py} +75 -21
- visidata/features/sparkline.py +48 -0
- visidata/features/status_source.py +20 -0
- visidata/{sysedit.py → features/sysedit.py} +2 -1
- visidata/features/sysopen_mailcap.py +46 -0
- visidata/features/term_extras.py +13 -0
- visidata/{transpose.py → features/transpose.py} +5 -4
- visidata/features/type_ipaddr.py +73 -0
- visidata/features/type_url.py +11 -0
- visidata/{unfurl.py → features/unfurl.py} +9 -9
- visidata/{window.py → features/window.py} +2 -2
- visidata/form.py +50 -21
- visidata/freqtbl.py +81 -33
- visidata/fuzzymatch.py +414 -0
- visidata/graph.py +105 -33
- visidata/guide.py +180 -0
- visidata/help.py +75 -44
- visidata/hint.py +39 -0
- visidata/indexsheet.py +109 -0
- visidata/input_history.py +55 -0
- visidata/interface.py +58 -0
- visidata/keys.py +17 -16
- visidata/loaders/__init__.py +9 -0
- visidata/loaders/_pandas.py +61 -21
- visidata/loaders/api_airtable.py +70 -0
- visidata/loaders/api_bitio.py +102 -0
- visidata/loaders/api_matrix.py +148 -0
- visidata/loaders/api_reddit.py +306 -0
- visidata/loaders/api_zulip.py +249 -0
- visidata/loaders/archive.py +41 -7
- visidata/loaders/arrow.py +7 -7
- visidata/loaders/conll.py +49 -0
- visidata/loaders/csv.py +25 -7
- visidata/loaders/eml.py +3 -4
- visidata/loaders/f5log.py +1204 -0
- visidata/loaders/fec.py +325 -0
- visidata/loaders/fixed_width.py +3 -5
- visidata/loaders/frictionless.py +3 -3
- visidata/loaders/geojson.py +8 -5
- visidata/loaders/google.py +48 -0
- visidata/loaders/graphviz.py +4 -4
- visidata/loaders/hdf5.py +4 -4
- visidata/loaders/html.py +48 -10
- visidata/loaders/http.py +84 -30
- visidata/loaders/imap.py +20 -10
- visidata/loaders/jrnl.py +52 -0
- visidata/loaders/json.py +83 -29
- visidata/loaders/jsonla.py +74 -0
- visidata/loaders/lsv.py +15 -11
- visidata/loaders/mailbox.py +40 -0
- visidata/loaders/markdown.py +1 -3
- visidata/loaders/mbtiles.py +4 -5
- visidata/loaders/mysql.py +11 -13
- visidata/loaders/npy.py +7 -7
- visidata/loaders/odf.py +4 -1
- visidata/loaders/orgmode.py +428 -0
- visidata/loaders/pandas_freqtbl.py +14 -20
- visidata/loaders/parquet.py +62 -6
- visidata/loaders/pcap.py +3 -3
- visidata/loaders/pdf.py +4 -3
- visidata/loaders/png.py +19 -13
- visidata/loaders/postgres.py +9 -8
- visidata/loaders/rec.py +7 -3
- visidata/loaders/s3.py +342 -0
- visidata/loaders/sas.py +5 -5
- visidata/loaders/scrape.py +186 -0
- visidata/loaders/shp.py +6 -5
- visidata/loaders/spss.py +5 -6
- visidata/loaders/sqlite.py +68 -28
- visidata/loaders/texttables.py +1 -1
- visidata/loaders/toml.py +60 -0
- visidata/loaders/tsv.py +61 -19
- visidata/loaders/ttf.py +19 -7
- visidata/loaders/unzip_http.py +6 -5
- visidata/loaders/usv.py +1 -1
- visidata/loaders/vcf.py +16 -16
- visidata/loaders/vds.py +10 -7
- visidata/loaders/vdx.py +30 -5
- visidata/loaders/xlsb.py +8 -1
- visidata/loaders/xlsx.py +145 -25
- visidata/loaders/xml.py +6 -3
- visidata/loaders/xword.py +4 -4
- visidata/loaders/yaml.py +15 -5
- visidata/macos.py +1 -1
- visidata/macros.py +130 -41
- visidata/main.py +119 -94
- visidata/mainloop.py +101 -154
- visidata/man/parse_options.py +2 -2
- visidata/man/vd.1 +302 -147
- visidata/man/vd.txt +291 -151
- visidata/memory.py +3 -3
- visidata/menu.py +104 -423
- visidata/metasheets.py +59 -141
- visidata/modify.py +79 -23
- visidata/motd.py +3 -3
- visidata/mouse.py +137 -0
- visidata/movement.py +43 -35
- visidata/optionssheet.py +99 -0
- visidata/path.py +131 -43
- visidata/pivot.py +74 -47
- visidata/plugins.py +65 -192
- visidata/pyobj.py +50 -201
- visidata/rename_col.py +20 -0
- visidata/save.py +42 -20
- visidata/search.py +54 -10
- visidata/selection.py +84 -5
- visidata/settings.py +162 -24
- visidata/sheets.py +229 -257
- visidata/shell.py +51 -21
- visidata/sidebar.py +162 -0
- visidata/sort.py +11 -4
- visidata/statusbar.py +113 -104
- visidata/stored_list.py +43 -0
- visidata/stored_prop.py +38 -0
- visidata/tests/conftest.py +3 -3
- visidata/tests/test_cliptext.py +39 -0
- visidata/tests/test_commands.py +62 -7
- visidata/tests/test_edittext.py +2 -2
- visidata/tests/test_features.py +17 -0
- visidata/tests/test_menu.py +14 -0
- visidata/tests/test_path.py +13 -4
- visidata/text_source.py +53 -0
- visidata/textsheet.py +10 -3
- visidata/theme.py +44 -0
- visidata/themes/__init__.py +0 -0
- visidata/themes/ascii8.py +84 -0
- visidata/themes/asciimono.py +84 -0
- visidata/themes/light.py +17 -0
- visidata/threads.py +87 -39
- visidata/tuiwin.py +22 -0
- visidata/type_currency.py +22 -3
- visidata/type_date.py +31 -9
- visidata/type_floatsi.py +5 -1
- visidata/undo.py +18 -6
- visidata/utils.py +106 -23
- visidata/vdobj.py +28 -17
- visidata/windows.py +10 -0
- visidata/wrappers.py +9 -3
- visidata-3.0.data/data/share/applications/visidata.desktop +7 -0
- {visidata-2.11.dev0.data → visidata-3.0.data}/data/share/man/man1/vd.1 +302 -147
- {visidata-2.11.dev0.data → visidata-3.0.data}/data/share/man/man1/visidata.1 +302 -147
- visidata-3.0.data/scripts/vd2to3.vdx +9 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/METADATA +13 -11
- visidata-3.0.dist-info/RECORD +257 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/WHEEL +1 -1
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/entry_points.txt +0 -1
- visidata/layout.py +0 -44
- visidata/misc.py +0 -5
- visidata-2.11.dev0.dist-info/RECORD +0 -142
- /visidata/{repeat.py → features/repeat.py} +0 -0
- {visidata-2.11.dev0.data → visidata-3.0.data}/scripts/vd +0 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/top_level.txt +0 -0
visidata/column.py
CHANGED
@@ -7,10 +7,11 @@ import re
|
|
7
7
|
import time
|
8
8
|
import json
|
9
9
|
|
10
|
-
from visidata import options, anytype, stacktrace, vd
|
10
|
+
from visidata import options, anytype, stacktrace, vd, drawcache
|
11
11
|
from visidata import asyncthread, dispwidth, clipstr, iterchars
|
12
12
|
from visidata import wrapply, TypedWrapper, TypedExceptionWrapper
|
13
|
-
from visidata import Extensible, AttrDict, undoAttrFunc
|
13
|
+
from visidata import Extensible, AttrDict, undoAttrFunc, ExplodingMock, MissingAttrFormatter
|
14
|
+
from visidata import getitem, setitem, getitemdef, getitemdeep, setitemdeep, getattrdeep, setattrdeep, iterchunks
|
14
15
|
|
15
16
|
class InProgress(Exception):
|
16
17
|
@property
|
@@ -19,31 +20,16 @@ class InProgress(Exception):
|
|
19
20
|
|
20
21
|
INPROGRESS = TypedExceptionWrapper(None, exception=InProgress()) # sentinel
|
21
22
|
|
22
|
-
vd.option('col_cache_size', 0, 'max number of cache entries in each cached column')
|
23
|
-
vd.option('
|
24
|
-
|
25
|
-
__all__ = [
|
26
|
-
'clean_to_id',
|
27
|
-
'Column',
|
28
|
-
'setitem',
|
29
|
-
'getattrdeep',
|
30
|
-
'setattrdeep',
|
31
|
-
'getitemdef',
|
32
|
-
'ColumnAttr', 'AttrColumn',
|
33
|
-
'ColumnItem', 'ItemColumn',
|
34
|
-
'SettableColumn',
|
35
|
-
'SubColumnFunc',
|
36
|
-
'SubColumnItem',
|
37
|
-
'SubColumnAttr',
|
38
|
-
'ColumnExpr', 'ExprColumn',
|
39
|
-
'DisplayWrapper',
|
40
|
-
]
|
23
|
+
vd.option('col_cache_size', 0, 'max number of cache entries in each cached column', max_help=-1)
|
24
|
+
vd.option('disp_formatter', 'generic', 'formatter to create the text in each cell (also used by text savers)', replay=True, max_help=0)
|
25
|
+
vd.option('disp_displayer', 'generic', 'displayer to render the text in each cell', replay=False, max_help=0)
|
41
26
|
|
42
27
|
|
43
28
|
class DisplayWrapper:
|
44
|
-
def __init__(self, value=None, *,
|
29
|
+
def __init__(self, value=None, *, typedval=None, text=None, note=None, notecolor=None, error=None):
|
45
30
|
self.value = value # actual value (any type)
|
46
|
-
self.
|
31
|
+
self.typedval = typedval # consistently typed value (or None)
|
32
|
+
self.text = text # displayed string
|
47
33
|
self.note = note # single unicode character displayed in cell far right
|
48
34
|
self.notecolor = notecolor # configurable color name (like 'color_warning')
|
49
35
|
self.error = error # list of strings for stacktrace
|
@@ -54,11 +40,6 @@ class DisplayWrapper:
|
|
54
40
|
def __eq__(self, other):
|
55
41
|
return self.value == other
|
56
42
|
|
57
|
-
|
58
|
-
def clean_to_id(s): # [Nas Banov] https://stackoverflow.com/a/3305731
|
59
|
-
return re.sub(r'\W|^(?=\d)', '_', str(s)).strip('_')
|
60
|
-
|
61
|
-
|
62
43
|
def _default_colnames():
|
63
44
|
'A B C .. Z AA AB .. ZZ AAA .. to infinity'
|
64
45
|
i=0
|
@@ -88,14 +69,14 @@ class Column(Extensible):
|
|
88
69
|
- *kwargs*: other attributes to be set on this column.
|
89
70
|
'''
|
90
71
|
def __init__(self, name=None, *, type=anytype, cache=False, **kwargs):
|
91
|
-
self.sheet =
|
72
|
+
self.sheet = ExplodingMock('use addColumn() on all columns') # owning Sheet, set in .recalc() via Sheet.addColumn
|
92
73
|
if name is None:
|
93
74
|
name = next(default_colnames)
|
94
75
|
self.name = str(name) # display visible name
|
95
76
|
self.fmtstr = '' # by default, use str()
|
96
77
|
self._type = type # anytype/str/int/float/date/func
|
97
78
|
self.getter = lambda col, row: row
|
98
|
-
self.setter =
|
79
|
+
self.setter = None
|
99
80
|
self._width = None # == 0 if hidden, None if auto-compute next time
|
100
81
|
self.hoffset = 0 # starting horizontal (char) offset of displayed column value
|
101
82
|
self.voffset = 0 # starting vertical (line) offset of displayed column value
|
@@ -103,7 +84,9 @@ class Column(Extensible):
|
|
103
84
|
self.keycol = 0 # keycol index (or 0 if not key column)
|
104
85
|
self.expr = None # Column-type-dependent parameter
|
105
86
|
self.formatter = ''
|
87
|
+
self.displayer = ''
|
106
88
|
self.defer = False
|
89
|
+
self.max_help = 10 # auto-hide above this disp_help level
|
107
90
|
|
108
91
|
self.setCache(cache)
|
109
92
|
for k, v in kwargs.items():
|
@@ -118,13 +101,17 @@ class Column(Extensible):
|
|
118
101
|
ret._cachedValues = collections.OrderedDict() # an unrelated cache for copied columns
|
119
102
|
return ret
|
120
103
|
|
104
|
+
def __str__(self):
|
105
|
+
return f'{type(self).__name__}:{self.name}'
|
106
|
+
|
107
|
+
def __repr__(self):
|
108
|
+
return f'<{type(self).__name__}: {self.name}>'
|
109
|
+
|
121
110
|
def __deepcopy__(self, memo):
|
122
111
|
return self.__copy__() # no separate deepcopy
|
123
112
|
|
124
113
|
def __getstate__(self):
|
125
|
-
|
126
|
-
d['type'] = self.type.__name__
|
127
|
-
return d
|
114
|
+
return {k:getattr(self, k) for k in 'name typestr width height expr keycol formatter fmtstr voffset hoffset aggstr'.split() if hasattr(self, k)}
|
128
115
|
|
129
116
|
def __setstate__(self, d):
|
130
117
|
for attr, v in d.items():
|
@@ -145,6 +132,9 @@ class Column(Extensible):
|
|
145
132
|
|
146
133
|
@name.setter
|
147
134
|
def name(self, name):
|
135
|
+
self.setName(name)
|
136
|
+
|
137
|
+
def setName(self, name):
|
148
138
|
if name is None:
|
149
139
|
name = ''
|
150
140
|
if isinstance(name, str):
|
@@ -194,6 +184,20 @@ class Column(Extensible):
|
|
194
184
|
vd.addUndo(setattr, self, '_width', self.width)
|
195
185
|
self._width = w
|
196
186
|
|
187
|
+
@property
|
188
|
+
def formatted_help(self):
|
189
|
+
return MissingAttrFormatter().format(self.help, sheet=self.sheet, col=self, vd=vd)
|
190
|
+
|
191
|
+
@property
|
192
|
+
def help_formatters(self):
|
193
|
+
formatters = [k[10:] for k in dir(self) if k.startswith('formatter_')]
|
194
|
+
return ' '.join(formatters)
|
195
|
+
|
196
|
+
@property
|
197
|
+
def help_displayers(self):
|
198
|
+
displayers = [k[10:] for k in dir(self) if k.startswith('displayer_')]
|
199
|
+
return ' '.join(displayers)
|
200
|
+
|
197
201
|
@property
|
198
202
|
def _formatdict(col):
|
199
203
|
if '=' in col.fmtstr:
|
@@ -229,6 +233,7 @@ class Column(Extensible):
|
|
229
233
|
def formatter_python(self, fmtstr):
|
230
234
|
return lambda v,*args,**kwargs: str(v)
|
231
235
|
|
236
|
+
@drawcache
|
232
237
|
def make_formatter(self):
|
233
238
|
'Return function for format(v) from the current formatter and fmtstr'
|
234
239
|
_formatMaker = getattr(self, 'formatter_'+(self.formatter or self.sheet.options.disp_formatter))
|
@@ -250,7 +255,34 @@ class Column(Extensible):
|
|
250
255
|
if isinstance(typedval, bytes):
|
251
256
|
typedval = typedval.decode(options.encoding, options.encoding_errors)
|
252
257
|
|
253
|
-
|
258
|
+
gt = vd.getType(self._type)
|
259
|
+
return gt.formatter(self._fmtstr or gt.fmtstr, typedval)
|
260
|
+
|
261
|
+
def displayer_generic(self, dw:DisplayWrapper, width=None):
|
262
|
+
'''Fit *dw.text* into *width* charcells.
|
263
|
+
Generate list of (attr:str, text:str) suitable for clipdraw_chunks.
|
264
|
+
|
265
|
+
The 'generic' displayer does not do any formatting.
|
266
|
+
'''
|
267
|
+
if width is not None and width > 1 and vd.isNumeric(self):
|
268
|
+
yield ('', dw.text.rjust(width-2))
|
269
|
+
else:
|
270
|
+
yield ('', dw.text)
|
271
|
+
|
272
|
+
def displayer_full(self, dw:DisplayWrapper, width=None):
|
273
|
+
'''Fit *dw.text* into *width* charcells.
|
274
|
+
Generate list of (attr:str, text:str) suitable for clipdraw_chunks.
|
275
|
+
|
276
|
+
The 'full' displayer allows formatting like [:color].
|
277
|
+
'''
|
278
|
+
if width is not None and width > 1 and vd.isNumeric(self):
|
279
|
+
yield from iterchunks(text.rjust(width-2))
|
280
|
+
else:
|
281
|
+
yield from iterchunks(dw.text)
|
282
|
+
|
283
|
+
def display(self, *args, **kwargs):
|
284
|
+
f = getattr(self, 'displayer_'+(self.displayer or self.sheet.options.disp_displayer), self.displayer_generic)
|
285
|
+
return f(*args, **kwargs)
|
254
286
|
|
255
287
|
def hide(self, hide=True):
|
256
288
|
if hide:
|
@@ -284,7 +316,7 @@ class Column(Extensible):
|
|
284
316
|
|
285
317
|
@asyncthread
|
286
318
|
def _calcIntoCacheAsync(self, row):
|
287
|
-
# causes
|
319
|
+
# causes issues when moved into _calcIntoCache gen case
|
288
320
|
self._cachedValues[self.sheet.rowid(row)] = INPROGRESS
|
289
321
|
self._calcIntoCache(row)
|
290
322
|
|
@@ -335,34 +367,35 @@ class Column(Extensible):
|
|
335
367
|
else:
|
336
368
|
dispval = options.disp_error_val
|
337
369
|
return DisplayWrapper(cellval.val, error=exc.stacktrace,
|
338
|
-
|
370
|
+
text=dispval,
|
339
371
|
note=options.note_getter_exc,
|
340
372
|
notecolor='color_error')
|
341
373
|
elif typedval.val is None: # early out for strict None
|
342
|
-
return DisplayWrapper(None,
|
374
|
+
return DisplayWrapper(None, text='', # force empty display for None
|
343
375
|
note=options.disp_note_none,
|
344
376
|
notecolor='color_note_type')
|
345
377
|
elif isinstance(typedval, TypedExceptionWrapper): # calc succeeded, type failed
|
346
|
-
return DisplayWrapper(typedval.val,
|
378
|
+
return DisplayWrapper(typedval.val, text=str(cellval),
|
347
379
|
error=typedval.stacktrace,
|
348
380
|
note=options.note_type_exc,
|
349
381
|
notecolor='color_warning')
|
350
382
|
else:
|
351
|
-
return DisplayWrapper(typedval.val,
|
383
|
+
return DisplayWrapper(typedval.val, text=str(typedval.val),
|
352
384
|
error='unknown',
|
353
385
|
note=options.note_type_exc,
|
354
386
|
notecolor='color_warning')
|
355
387
|
|
356
388
|
elif isinstance(typedval, threading.Thread):
|
357
389
|
return DisplayWrapper(None,
|
358
|
-
|
390
|
+
text=options.disp_pending,
|
359
391
|
note=options.note_pending,
|
360
392
|
notecolor='color_note_pending')
|
361
393
|
|
362
394
|
dw = DisplayWrapper(cellval)
|
395
|
+
dw.typedval = typedval
|
363
396
|
|
364
397
|
try:
|
365
|
-
dw.
|
398
|
+
dw.text = self.format(typedval, width=(self.width or 0)*2) or ''
|
366
399
|
|
367
400
|
# annotate cells with raw value type in anytype columns, except for strings
|
368
401
|
if self.type is anytype and type(cellval) is not str:
|
@@ -375,43 +408,41 @@ class Column(Extensible):
|
|
375
408
|
e.stacktrace = stacktrace()
|
376
409
|
dw.error = e.stacktrace
|
377
410
|
try:
|
378
|
-
dw.
|
411
|
+
dw.text = str(cellval)
|
379
412
|
except Exception as e:
|
380
|
-
dw.
|
413
|
+
dw.text = str(e)
|
381
414
|
dw.note = options.note_format_exc
|
382
415
|
dw.notecolor = 'color_warning'
|
383
416
|
|
417
|
+
# dw.display = self.display(dw) # set during draw() when colwidth is known
|
384
418
|
return dw
|
385
419
|
|
386
420
|
def getDisplayValue(self, row):
|
387
421
|
'Return string displayed in this column for given *row*.'
|
388
|
-
return self.getCell(row).
|
422
|
+
return self.getCell(row).text
|
389
423
|
|
390
424
|
def putValue(self, row, val):
|
391
425
|
'Change value for *row* in this column to *val* immediately. Does not check the type. Overridable; by default calls ``.setter(row, val)``.'
|
392
|
-
|
426
|
+
if self.setter:
|
427
|
+
return self.setter(self, row, val)
|
393
428
|
|
394
|
-
def setValue(self, row, val):
|
429
|
+
def setValue(self, row, val, setModified=True):
|
395
430
|
'Change value for *row* in this column to *val*. Call ``putValue`` immediately if not a deferred column (added to deferred parent at load-time); otherwise cache until later ``putChanges``. Caller must add undo function.'
|
396
431
|
if self.defer:
|
397
432
|
self.cellChanged(row, val)
|
398
433
|
else:
|
399
434
|
self.putValue(row, val)
|
400
|
-
|
401
|
-
|
402
|
-
def setValueSafe(self, row, value):
|
403
|
-
'setValue and ignore exceptions.'
|
404
|
-
try:
|
405
|
-
return self.setValue(row, value)
|
406
|
-
except Exception as e:
|
407
|
-
vd.exceptionCaught(e)
|
435
|
+
if setModified: #1800
|
436
|
+
self.sheet.setModified()
|
408
437
|
|
409
438
|
@asyncthread
|
410
439
|
def setValues(self, rows, *values):
|
411
440
|
'Set values in this column for *rows* to *values*, recycling values as needed to fill *rows*.'
|
412
441
|
vd.addUndoSetValues([self], rows)
|
442
|
+
|
413
443
|
for r, v in zip(rows, itertools.cycle(values)):
|
414
|
-
self.
|
444
|
+
vd.callNoExceptions(self.setValue, r, v)
|
445
|
+
|
415
446
|
self.recalc()
|
416
447
|
return vd.status('set %d cells to %d values' % (len(rows), len(values)))
|
417
448
|
|
@@ -419,7 +450,7 @@ class Column(Extensible):
|
|
419
450
|
'Set values on this column for *rows* to *values*, coerced to column type, recycling values as needed to fill *rows*. Abort on type exception.'
|
420
451
|
vd.addUndoSetValues([self], rows)
|
421
452
|
for r, v in zip(rows, itertools.cycle(self.type(val) for val in values)):
|
422
|
-
self.
|
453
|
+
vd.callNoExceptions(self.setValue, r, v)
|
423
454
|
|
424
455
|
self.recalc()
|
425
456
|
|
@@ -430,92 +461,47 @@ class Column(Extensible):
|
|
430
461
|
w = 0
|
431
462
|
nlen = dispwidth(self.name)
|
432
463
|
if len(rows) > 0:
|
433
|
-
|
434
|
-
|
464
|
+
w_max = 0
|
465
|
+
for r in rows:
|
466
|
+
row_w = dispwidth(self.getDisplayValue(r), maxwidth=self.sheet.windowWidth)
|
467
|
+
if w_max < row_w:
|
468
|
+
w_max = row_w
|
469
|
+
if w_max >= self.sheet.windowWidth:
|
470
|
+
break #1747 early out to speed up wide columns
|
471
|
+
w = w_max
|
472
|
+
w = max(w, nlen)+2
|
473
|
+
w = min(w, self.sheet.windowWidth)
|
474
|
+
return w
|
435
475
|
|
436
476
|
|
437
|
-
# ---- Column makers
|
438
477
|
|
439
|
-
|
440
|
-
r[i] = v
|
441
|
-
return True
|
478
|
+
# ---- basic Columns
|
442
479
|
|
443
|
-
|
444
|
-
def getattrdeep(obj, attr, *default, getter=getattr):
|
445
|
-
try:
|
446
|
-
'Return dotted attr (like "a.b.c") from obj, or default if any of the components are missing.'
|
447
|
-
if not isinstance(attr, str):
|
448
|
-
return getter(obj, attr, *default)
|
449
|
-
|
450
|
-
try: # if attribute exists, return toplevel value, even if dotted
|
451
|
-
if attr in obj:
|
452
|
-
return getter(obj, attr)
|
453
|
-
except Exception as e:
|
454
|
-
pass
|
455
|
-
|
456
|
-
attrs = attr.split('.')
|
457
|
-
for a in attrs[:-1]:
|
458
|
-
obj = getter(obj, a)
|
459
|
-
|
460
|
-
return getter(obj, attrs[-1])
|
461
|
-
except Exception as e:
|
462
|
-
if not default: raise
|
463
|
-
return default[0]
|
464
|
-
|
465
|
-
|
466
|
-
def setattrdeep(obj, attr, val, getter=getattr, setter=setattr):
|
467
|
-
'Set dotted attr (like "a.b.c") on obj to val.'
|
468
|
-
if not isinstance(attr, str):
|
469
|
-
return setter(obj, attr, val)
|
470
|
-
|
471
|
-
try: # if attribute exists, overwrite toplevel value, even if dotted
|
472
|
-
getter(obj, attr)
|
473
|
-
return setter(obj, attr, val)
|
474
|
-
except Exception as e:
|
475
|
-
pass
|
476
|
-
|
477
|
-
attrs = attr.split('.')
|
478
|
-
for a in attrs[:-1]:
|
479
|
-
try:
|
480
|
-
obj = getter(obj, a)
|
481
|
-
except Exception as e:
|
482
|
-
obj = obj[a] = type(obj)() # assume homogeneous nesting
|
483
|
-
|
484
|
-
setter(obj, attrs[-1], val)
|
485
|
-
|
486
|
-
|
487
|
-
def getitemdeep(obj, k, *default):
|
488
|
-
return getattrdeep(obj, k, *default, getter=getitem)
|
489
|
-
|
490
|
-
def setitemdeep(obj, k, val):
|
491
|
-
return setattrdeep(obj, k, val, getter=getitemdef, setter=setitem)
|
492
|
-
|
493
|
-
def AttrColumn(name='', attr=None, **kwargs):
|
480
|
+
class AttrColumn(Column):
|
494
481
|
'Column using getattr/setattr with *attr*.'
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
482
|
+
def __init__(self, name=None, expr=None, **kwargs):
|
483
|
+
super().__init__(name,
|
484
|
+
expr=expr if expr is not None else name,
|
485
|
+
getter=lambda col,row: getattrdeep(row, col.expr, None),
|
486
|
+
**kwargs)
|
500
487
|
|
501
|
-
def
|
502
|
-
|
488
|
+
def putValue(self, row, val):
|
489
|
+
super().putValue(row, val)
|
490
|
+
setattrdeep(row, self.expr, val)
|
503
491
|
|
504
|
-
def getitemdef(o, k, default=None):
|
505
|
-
try:
|
506
|
-
return default if o is None else o[k]
|
507
|
-
except Exception:
|
508
|
-
return default
|
509
492
|
|
510
493
|
class ItemColumn(Column):
|
511
|
-
'Column using getitem/setitem with *
|
494
|
+
'Column using getitem/setitem with *expr*.'
|
512
495
|
def __init__(self, name=None, expr=None, **kwargs):
|
513
496
|
super().__init__(name,
|
514
497
|
expr=expr if expr is not None else name,
|
515
498
|
getter=lambda col,row: getitemdeep(row, col.expr, None),
|
516
|
-
setter=lambda col,row,val: setitemdeep(row, col.expr, val),
|
517
499
|
**kwargs)
|
518
500
|
|
501
|
+
def putValue(self, row, val):
|
502
|
+
super().putValue(row, val)
|
503
|
+
setitemdeep(row, self.expr, val)
|
504
|
+
|
519
505
|
|
520
506
|
class SubColumnFunc(Column):
|
521
507
|
'Column compositor; preprocess row with *subfunc*(row, *expr*) before passing to *origcol*.getValue and *origcol*.setValue.'
|
@@ -573,7 +559,7 @@ class ExprColumn(Column):
|
|
573
559
|
a = self.getDisplayValue(row)
|
574
560
|
b = self.format(self.type(val))
|
575
561
|
if a != b:
|
576
|
-
vd.warning(
|
562
|
+
vd.warning("Cannot change value of calculated column. Use `'` to freeze column.")
|
577
563
|
|
578
564
|
@property
|
579
565
|
def expr(self):
|
@@ -597,7 +583,24 @@ class SettableColumn(Column):
|
|
597
583
|
SettableColumn.init('_store', dict, copy=True)
|
598
584
|
|
599
585
|
|
586
|
+
vd.addGlobals(
|
587
|
+
INPROGRESS=INPROGRESS,
|
588
|
+
Column=Column,
|
589
|
+
setitem=setitem,
|
590
|
+
getattrdeep=getattrdeep,
|
591
|
+
setattrdeep=setattrdeep,
|
592
|
+
getitemdef=getitemdef,
|
593
|
+
AttrColumn=AttrColumn,
|
594
|
+
ItemColumn=ItemColumn,
|
595
|
+
ExprColumn=ExprColumn,
|
596
|
+
SettableColumn=SettableColumn,
|
597
|
+
SubColumnFunc=SubColumnFunc,
|
598
|
+
SubColumnItem=SubColumnItem,
|
599
|
+
SubColumnAttr=SubColumnAttr,
|
600
|
+
|
600
601
|
# synonyms
|
601
|
-
ColumnItem
|
602
|
-
ColumnAttr
|
603
|
-
ColumnExpr
|
602
|
+
ColumnItem=ItemColumn,
|
603
|
+
ColumnAttr=AttrColumn,
|
604
|
+
ColumnExpr=ExprColumn,
|
605
|
+
DisplayWrapper=DisplayWrapper
|
606
|
+
)
|