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/expr.py
CHANGED
@@ -38,10 +38,8 @@ def setValuesFromExpr(self, rows, expr):
|
|
38
38
|
for row in Progress(rows, 'setting'):
|
39
39
|
# Note: expressions that are only calculated once, do not need to pass column identity
|
40
40
|
# they can reference their "previous selves" once without causing a recursive problem
|
41
|
-
|
42
|
-
|
43
|
-
except Exception as e:
|
44
|
-
vd.exceptionCaught(e)
|
41
|
+
v = vd.callNoExceptions(self.sheet.evalExpr, compiledExpr, row)
|
42
|
+
vd.callNoExceptions(self.setValue, row, v)
|
45
43
|
self.recalc()
|
46
44
|
vd.status('set %d values = %s' % (len(rows), expr))
|
47
45
|
|
@@ -59,3 +57,9 @@ Sheet.addCommand('gz=', 'setcol-iter', 'cursorCol.setValues(someSelectedRows, *l
|
|
59
57
|
Sheet.addCommand(None, 'show-expr', 'status(evalExpr(inputExpr("show expr="), cursorRow))', 'evaluate Python expression on current row and show result on status line')
|
60
58
|
|
61
59
|
vd.addGlobals({'CompleteExpr': CompleteExpr})
|
60
|
+
|
61
|
+
vd.addMenuItems('''
|
62
|
+
Edit > Modify > current cell > Python expression > setcell-expr
|
63
|
+
Edit > Modify > selected cells > Python sequence > setcol-expr
|
64
|
+
Column > Add column > Python expr > addcol-expr
|
65
|
+
''')
|
visidata/extensible.py
CHANGED
@@ -8,7 +8,7 @@ class Extensible:
|
|
8
8
|
|
9
9
|
@classmethod
|
10
10
|
def init(cls, membername, initfunc=lambda: None, copy=False):
|
11
|
-
'
|
11
|
+
'Prepend equivalent of ``self.<membername> = initfunc()`` to ``<cls>.__init__``. If *copy* is True, <membername> will be copied when object is copied.'
|
12
12
|
|
13
13
|
def thisclass_hasattr(cls, k):
|
14
14
|
return getattr(cls, k, None) is not getattr(cls.__bases__[0], k, None)
|
@@ -16,12 +16,12 @@ class Extensible:
|
|
16
16
|
# must check hasattr first or else this might be parent's __init__
|
17
17
|
oldinit = thisclass_hasattr(cls, '__init__') and getattr(cls, '__init__')
|
18
18
|
def newinit(self, *args, **kwargs):
|
19
|
+
if not hasattr(self, membername): # can be overridden by a subclass
|
20
|
+
setattr(self, membername, initfunc())
|
19
21
|
if oldinit:
|
20
22
|
oldinit(self, *args, **kwargs)
|
21
23
|
else:
|
22
24
|
super(cls, self).__init__(*args, **kwargs)
|
23
|
-
if not hasattr(self, membername): # can be overridden by a subclass
|
24
|
-
setattr(self, membername, initfunc())
|
25
25
|
cls.__init__ = wraps(oldinit)(newinit) if oldinit else newinit
|
26
26
|
|
27
27
|
oldcopy = thisclass_hasattr(cls, '__copy__') and getattr(cls, '__copy__')
|
@@ -30,15 +30,32 @@ class Extensible:
|
|
30
30
|
ret = oldcopy(self, *args, **kwargs)
|
31
31
|
else:
|
32
32
|
ret = super(cls, self).__copy__(*args, **kwargs)
|
33
|
-
|
33
|
+
|
34
|
+
if not hasattr(ret, membername):
|
35
|
+
if copy and hasattr(self, membername):
|
36
|
+
v = getattr(self, membername)
|
37
|
+
else:
|
38
|
+
v = initfunc()
|
39
|
+
setattr(ret, membername, v)
|
40
|
+
|
34
41
|
return ret
|
35
42
|
cls.__copy__ = wraps(oldcopy)(newcopy) if oldcopy else newcopy
|
36
43
|
|
44
|
+
@classmethod
|
45
|
+
def superclasses(cls):
|
46
|
+
yield cls
|
47
|
+
yield from cls.__bases__
|
48
|
+
for b in cls.__bases__:
|
49
|
+
if hasattr(b, 'superclasses'):
|
50
|
+
yield from b.superclasses()
|
51
|
+
|
37
52
|
@classmethod
|
38
53
|
def api(cls, func):
|
39
54
|
oldfunc = getattr(cls, func.__name__, None)
|
40
55
|
if oldfunc:
|
41
56
|
func = wraps(oldfunc)(func)
|
57
|
+
from visidata import vd
|
58
|
+
func.importingModule = vd.importingModule
|
42
59
|
setattr(cls, func.__name__, func)
|
43
60
|
return func
|
44
61
|
|
@@ -75,6 +92,14 @@ class Extensible:
|
|
75
92
|
|
76
93
|
@classmethod
|
77
94
|
def class_api(cls, func):
|
95
|
+
'''`@Class.class_api` works much like `@Class.api`, but for class methods. This method is used internally but may not be all that useful for plugin and module authors. Note that `@classmethod` must still be provided, and **the order of multiple decorators is crucial**, in that `@<class>.class_api` must come before `@classmethod`:
|
96
|
+
|
97
|
+
::
|
98
|
+
@Sheet.class_api
|
99
|
+
@classmethod
|
100
|
+
def addCommand(cls, ...):
|
101
|
+
'''
|
102
|
+
|
78
103
|
name = func.__get__(None, dict).__func__.__name__
|
79
104
|
oldfunc = getattr(cls, name, None)
|
80
105
|
if oldfunc:
|
@@ -94,11 +119,12 @@ class Extensible:
|
|
94
119
|
@classmethod
|
95
120
|
def lazy_property(cls, func):
|
96
121
|
'Return ``func()`` on first access and cache result; return cached result thereafter.'
|
122
|
+
name = '_' + func.__name__
|
123
|
+
cls.init(name, lambda: None, copy=False)
|
97
124
|
@property
|
98
125
|
@wraps(func)
|
99
126
|
def get_if_not(self):
|
100
|
-
name
|
101
|
-
if not hasattr(self, name):
|
127
|
+
if getattr(self, name, None) is None:
|
102
128
|
setattr(self, name, func(self))
|
103
129
|
return getattr(self, name)
|
104
130
|
setattr(cls, func.__name__, get_if_not)
|
File without changes
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# requirements: mutagen
|
2
|
+
|
3
|
+
import functools
|
4
|
+
|
5
|
+
from visidata import vd, Column, AttrColumn, DirSheet
|
6
|
+
|
7
|
+
|
8
|
+
@functools.lru_cache(None)
|
9
|
+
def get_mutagen_info(path):
|
10
|
+
mutagen = vd.importExternal('mutagen')
|
11
|
+
m = mutagen.File(path)
|
12
|
+
return m.info
|
13
|
+
|
14
|
+
|
15
|
+
class MutagenColumn(AttrColumn):
|
16
|
+
def calcValue(self, r):
|
17
|
+
md = get_mutagen_info(r)
|
18
|
+
return getattr(md, self.expr, None)
|
19
|
+
|
20
|
+
|
21
|
+
@DirSheet.api
|
22
|
+
def audiometadata_columns(sheet):
|
23
|
+
return [
|
24
|
+
Column('audio_info', width=0, getter=lambda c,r: get_mutagen_info(r)),
|
25
|
+
MutagenColumn('bitrate'),
|
26
|
+
MutagenColumn('channels'),
|
27
|
+
MutagenColumn('encoder_info'),
|
28
|
+
MutagenColumn('encoder_settings'),
|
29
|
+
MutagenColumn('frame_offset'),
|
30
|
+
MutagenColumn('length'),
|
31
|
+
MutagenColumn('mode'),
|
32
|
+
MutagenColumn('padding'),
|
33
|
+
MutagenColumn('protected'),
|
34
|
+
MutagenColumn('sample_rate'),
|
35
|
+
MutagenColumn('track_gain'),
|
36
|
+
]
|
37
|
+
|
38
|
+
|
39
|
+
DirSheet.addCommand('', 'addcol-audiometadata', 'addColumn(*audiometadata_columns())', 'add metadata columns for audio files (MP3, FLAC, Ogg, etc)')
|
40
|
+
|
41
|
+
|
42
|
+
vd.addMenuItems('Column > Add column > audio metadata > addcol-audiometadata')
|
@@ -0,0 +1,34 @@
|
|
1
|
+
from visidata import vd, Column, Sheet, asyncthread, Progress
|
2
|
+
|
3
|
+
|
4
|
+
class HistogramColumn(Column):
|
5
|
+
def calcValue(col, row):
|
6
|
+
histogram = col.sheet.options.disp_histogram
|
7
|
+
histolen = (col.width-2)*col.sourceCol.getTypedValue(row)//(col.sourceCol.largest-col.sourceCol.smallest)
|
8
|
+
return histogram*histolen
|
9
|
+
|
10
|
+
|
11
|
+
@Sheet.api
|
12
|
+
def addcol_histogram(sheet, col): #2052
|
13
|
+
newcol = HistogramColumn(col.name+'_histogram', sourceCol=col)
|
14
|
+
col.smallest = None
|
15
|
+
col.largest = None
|
16
|
+
sheet.calc_histogram_bounds(col)
|
17
|
+
return newcol
|
18
|
+
|
19
|
+
|
20
|
+
@Sheet.api
|
21
|
+
@asyncthread
|
22
|
+
def calc_histogram_bounds(sheet, col):
|
23
|
+
for row in Progress(sheet.rows):
|
24
|
+
v = col.getTypedValue(row)
|
25
|
+
if col.smallest is None or v < col.smallest:
|
26
|
+
col.smallest = v
|
27
|
+
if col.largest is None or v > col.largest:
|
28
|
+
col.largest = v
|
29
|
+
|
30
|
+
|
31
|
+
Sheet.addCommand('', 'addcol-histogram', 'addColumnAtCursor(addcol_histogram(cursorCol))', 'add column with histogram of current column')
|
32
|
+
|
33
|
+
|
34
|
+
vd.addMenuItems('Column > Add column > histogram > addcol-histogram')
|
@@ -0,0 +1,69 @@
|
|
1
|
+
'''
|
2
|
+
Add svg saver to Canvas.
|
3
|
+
|
4
|
+
Requires matplotlib.pyplot
|
5
|
+
'''
|
6
|
+
|
7
|
+
|
8
|
+
import collections
|
9
|
+
|
10
|
+
from visidata import VisiData, Canvas, vd, Progress
|
11
|
+
|
12
|
+
vd.option('plt_marker', '.', 'matplotlib.markers')
|
13
|
+
|
14
|
+
|
15
|
+
@Canvas.api
|
16
|
+
def plot_sheet(self, ax):
|
17
|
+
plt = vd.importExternal('matplotlib.pyplot', 'matplotlib')
|
18
|
+
nerrors = 0
|
19
|
+
nplotted = 0
|
20
|
+
|
21
|
+
self.reset()
|
22
|
+
|
23
|
+
vd.status('loading data points')
|
24
|
+
catcols = [c for c in self.xcols if not vd.isNumeric(c)]
|
25
|
+
numcols = [c for c in self.xcols if vd.isNumeric(c)]
|
26
|
+
for ycol in self.ycols:
|
27
|
+
xpts = collections.defaultdict(list)
|
28
|
+
ypts = collections.defaultdict(list)
|
29
|
+
for rownum, row in enumerate(Progress(self.sourceRows, 'plotting')): # rows being plotted from source
|
30
|
+
try:
|
31
|
+
k = tuple(c.getValue(row) for c in catcols) if catcols else (ycol.name,)
|
32
|
+
|
33
|
+
# convert deliberately to float (to e.g. linearize date)
|
34
|
+
graph_x = numcols[0].type(numcols[0].getValue(row)) if numcols else rownum
|
35
|
+
graph_y = ycol.type(ycol.getValue(row))
|
36
|
+
|
37
|
+
xpts[k].append(graph_x)
|
38
|
+
ypts[k].append(graph_y)
|
39
|
+
|
40
|
+
nplotted += 1
|
41
|
+
except Exception:
|
42
|
+
nerrors += 1
|
43
|
+
if vd.options.debug:
|
44
|
+
raise
|
45
|
+
lines = []
|
46
|
+
for k in xpts:
|
47
|
+
line = ax.scatter(xpts[k], ypts[k], label=' '.join(str(x) for x in k), **vd.options.getall('plt_'))
|
48
|
+
lines.append(line)
|
49
|
+
|
50
|
+
ax.legend(handles=lines)
|
51
|
+
ax.set_xlabel(','.join(xcol.name for xcol in self.xcols if vd.isNumeric(xcol)) or 'row#')
|
52
|
+
ax.xaxis.set_major_locator(plt.MaxNLocator(4))
|
53
|
+
ax.yaxis.set_major_locator(plt.MaxNLocator(4))
|
54
|
+
|
55
|
+
|
56
|
+
@VisiData.api
|
57
|
+
def save_svg(vd, p, *sheets):
|
58
|
+
plt = vd.importExternal('matplotlib.pyplot', 'matplotlib')
|
59
|
+
fig_, ax = plt.subplots()
|
60
|
+
for vs in sheets:
|
61
|
+
if not isinstance(vs, Canvas):
|
62
|
+
vd.warning(f'{vs.name} not a Canvas')
|
63
|
+
continue
|
64
|
+
vs.plot_sheet(ax)
|
65
|
+
|
66
|
+
ax.grid()
|
67
|
+
ax.set_title('\n'.join(vs.name for vs in sheets))
|
68
|
+
plt.xticks()
|
69
|
+
plt.savefig(p, format='svg')
|
@@ -0,0 +1,46 @@
|
|
1
|
+
__author__ = 'Andy Craig, andycraig (https://github.com/andycraig)'
|
2
|
+
|
3
|
+
|
4
|
+
import re
|
5
|
+
|
6
|
+
from visidata import vd, Sheet, Column, floatsi, currency, date
|
7
|
+
|
8
|
+
date_fmtstrs = [
|
9
|
+
'%Y',
|
10
|
+
'%Y-%m',
|
11
|
+
# '%Y-W%U',
|
12
|
+
'%Y-%m-%d',
|
13
|
+
'%Y-%m-%d %H',
|
14
|
+
'%Y-%m-%d %H:%M',
|
15
|
+
'%Y-%m-%d %H:%M:%S',
|
16
|
+
'%Y-%m-%d %H:%M:%S.%f',
|
17
|
+
]
|
18
|
+
|
19
|
+
@Column.api
|
20
|
+
def setcol_precision(col, amount:int):
|
21
|
+
if col.type is date:
|
22
|
+
try:
|
23
|
+
i = date_fmtstrs.index(col.fmtstr)
|
24
|
+
except ValueError:
|
25
|
+
i = 2
|
26
|
+
col.fmtstr = date_fmtstrs[(i+amount)%len(date_fmtstrs)]
|
27
|
+
elif col.type in (float, floatsi, currency):
|
28
|
+
if col.fmtstr == '':
|
29
|
+
col.fmtstr = f'%.{2 + amount}f'
|
30
|
+
else:
|
31
|
+
precision_str = re.match(r'%.([0-9]+)f', col.fmtstr)
|
32
|
+
if not precision_str is None:
|
33
|
+
col.fmtstr = f'%.{max(0, int(precision_str[1]) + amount)}f'
|
34
|
+
else:
|
35
|
+
col.type = float
|
36
|
+
if col.fmtstr == '':
|
37
|
+
col.fmtstr = '%.2f'
|
38
|
+
|
39
|
+
|
40
|
+
vd.addMenuItems('''
|
41
|
+
Column > Set precision > more > setcol-precision-more
|
42
|
+
Column > Set precision > less > setcol-precision-less
|
43
|
+
''')
|
44
|
+
|
45
|
+
Sheet.addCommand('Alt+-', 'setcol-precision-less', 'cursorCol.setcol_precision(-1)', 'show less precision in current column')
|
46
|
+
Sheet.addCommand('Alt++', 'setcol-precision-more', 'cursorCol.setcol_precision(1)', 'show more precision in current column')
|
@@ -0,0 +1,163 @@
|
|
1
|
+
import collections
|
2
|
+
from functools import partial
|
3
|
+
from visidata import DrawablePane, BaseSheet, vd, VisiData, CompleteKey, clipdraw, HelpSheet, colors, AcceptInput, AttrDict, drawcache_property
|
4
|
+
|
5
|
+
|
6
|
+
vd.theme_option('color_cmdpalette', 'black on 72', 'base color of command palette')
|
7
|
+
vd.theme_option('disp_cmdpal_max', 10, 'max number of suggestions for command palette')
|
8
|
+
|
9
|
+
def add_to_input(v, i, value=''):
|
10
|
+
items = list(v.split())
|
11
|
+
if not v or v.endswith(' '):
|
12
|
+
items.append(value)
|
13
|
+
else:
|
14
|
+
items[-1] = value
|
15
|
+
v = ' '.join(items) + ' '
|
16
|
+
return v, len(v)
|
17
|
+
|
18
|
+
|
19
|
+
def accept_input(v, i, value=None):
|
20
|
+
raise AcceptInput(v if value is None else value)
|
21
|
+
|
22
|
+
|
23
|
+
@VisiData.lazy_property
|
24
|
+
def usedInputs(vd):
|
25
|
+
return collections.defaultdict(int)
|
26
|
+
|
27
|
+
@DrawablePane.after
|
28
|
+
def execCommand2(sheet, cmd, *args, **kwargs):
|
29
|
+
vd.usedInputs[cmd.longname] += 1
|
30
|
+
|
31
|
+
@BaseSheet.api
|
32
|
+
def inputPalette(sheet, prompt, items,
|
33
|
+
value_key='key',
|
34
|
+
formatter=lambda m, item, trigger_key: f'{trigger_key} {item}',
|
35
|
+
multiple=False,
|
36
|
+
**kwargs):
|
37
|
+
bindings = dict()
|
38
|
+
|
39
|
+
tabitem = -1
|
40
|
+
|
41
|
+
def tab(n, nitems):
|
42
|
+
nonlocal tabitem
|
43
|
+
tabitem = (tabitem + n) % nitems
|
44
|
+
|
45
|
+
def _draw_palette(value):
|
46
|
+
words = value.lower().split()
|
47
|
+
|
48
|
+
if multiple and words:
|
49
|
+
if value.endswith(' '):
|
50
|
+
finished_words = words
|
51
|
+
unfinished_words = []
|
52
|
+
else:
|
53
|
+
finished_words = words[:-1]
|
54
|
+
unfinished_words = [words[-1]]
|
55
|
+
else:
|
56
|
+
unfinished_words = words
|
57
|
+
finished_words = []
|
58
|
+
|
59
|
+
unuseditems = [item for item in items if item[value_key] not in finished_words]
|
60
|
+
|
61
|
+
matches = vd.fuzzymatch(unuseditems, unfinished_words)
|
62
|
+
|
63
|
+
h = sheet.windowHeight
|
64
|
+
w = min(100, sheet.windowWidth)
|
65
|
+
nitems = min(h-1, sheet.options.disp_cmdpal_max)
|
66
|
+
|
67
|
+
useditems = []
|
68
|
+
palrows = []
|
69
|
+
|
70
|
+
for m in matches[:nitems]:
|
71
|
+
useditems.append(m.match)
|
72
|
+
palrows.append((m, m.match))
|
73
|
+
|
74
|
+
favitems = sorted([item for item in unuseditems if item not in useditems],
|
75
|
+
key=lambda item: -vd.usedInputs.get(item[value_key], 0))
|
76
|
+
|
77
|
+
for item in favitems[:nitems-len(palrows)]:
|
78
|
+
palrows.append((None, item))
|
79
|
+
|
80
|
+
navailitems = min(len(palrows), nitems)
|
81
|
+
|
82
|
+
bindings['^I'] = lambda *args: tab(1, navailitems) or args
|
83
|
+
bindings['KEY_BTAB'] = lambda *args: tab(-1, navailitems) or args
|
84
|
+
|
85
|
+
for i in range(nitems-len(palrows)):
|
86
|
+
palrows.append((None, None))
|
87
|
+
|
88
|
+
for i, (m, item) in enumerate(palrows):
|
89
|
+
trigger_key = ' '
|
90
|
+
if tabitem >= 0 and item:
|
91
|
+
trigger_key = f'{i+1}'[-1]
|
92
|
+
bindings[trigger_key] = partial(add_to_input if multiple else accept_input, value=item[value_key])
|
93
|
+
|
94
|
+
attr = colors.color_cmdpalette
|
95
|
+
|
96
|
+
if tabitem < 0 and palrows:
|
97
|
+
_ , topitem = palrows[0]
|
98
|
+
bindings['^J'] = partial(accept_input, value=None)
|
99
|
+
if multiple:
|
100
|
+
bindings[' '] = partial(add_to_input, value=topitem[value_key])
|
101
|
+
elif item and i == tabitem:
|
102
|
+
bindings['^J'] = partial(accept_input, value=None)
|
103
|
+
if multiple:
|
104
|
+
bindings[' '] = partial(add_to_input, value=item[value_key])
|
105
|
+
attr = colors.color_menu_spec
|
106
|
+
|
107
|
+
match_summary = formatter(m, item, trigger_key) if item else ' '
|
108
|
+
|
109
|
+
clipdraw(sheet._scr, h-nitems-1+i, 0, match_summary, attr, w=w)
|
110
|
+
|
111
|
+
return None
|
112
|
+
|
113
|
+
completer = CompleteKey(sorted(item[value_key] for item in items))
|
114
|
+
return vd.input(prompt,
|
115
|
+
completer=completer,
|
116
|
+
updater=_draw_palette,
|
117
|
+
bindings=bindings,
|
118
|
+
**kwargs)
|
119
|
+
|
120
|
+
|
121
|
+
def cmdlist(sheet):
|
122
|
+
return [
|
123
|
+
AttrDict(longname=row.longname,
|
124
|
+
description=sheet.cmddict[(row.sheet, row.longname)].helpstr)
|
125
|
+
for row in sheet.rows
|
126
|
+
]
|
127
|
+
HelpSheet.cmdlist = drawcache_property(cmdlist)
|
128
|
+
|
129
|
+
|
130
|
+
@BaseSheet.api
|
131
|
+
def inputLongname(sheet):
|
132
|
+
prompt = 'command name: '
|
133
|
+
# get set of commands possible in the sheet
|
134
|
+
this_sheets_help = HelpSheet('', source=sheet)
|
135
|
+
this_sheets_help.ensureLoaded()
|
136
|
+
|
137
|
+
def _fmt_cmdpal_summary(match, row, trigger_key):
|
138
|
+
keystrokes = this_sheets_help.revbinds.get(row.longname, [None])[0] or ' '
|
139
|
+
formatted_longname = match.formatted.get('longname', row.longname) if match else row.longname
|
140
|
+
formatted_name = f'[:onclick {row.longname}]{formatted_longname}[/]'
|
141
|
+
if vd.options.debug and match:
|
142
|
+
keystrokes = f'[{match.score}]'
|
143
|
+
r = f' [:keystrokes]{keystrokes.rjust(len(prompt)-5)}[/] '
|
144
|
+
r += f'[:keystrokes]{trigger_key}[/] {formatted_name}'
|
145
|
+
if row.description:
|
146
|
+
formatted_desc = match.formatted.get('description', row.description) if match else row.description
|
147
|
+
r += f' - {formatted_desc}'
|
148
|
+
return r
|
149
|
+
|
150
|
+
return sheet.inputPalette(prompt, this_sheets_help.cmdlist,
|
151
|
+
value_key='longname',
|
152
|
+
formatter=_fmt_cmdpal_summary,
|
153
|
+
type='longname')
|
154
|
+
|
155
|
+
|
156
|
+
@BaseSheet.api
|
157
|
+
def exec_longname(sheet, longname):
|
158
|
+
if not sheet.getCommand(longname):
|
159
|
+
vd.fail(f'no command {longname}')
|
160
|
+
sheet.execCommand(longname)
|
161
|
+
|
162
|
+
|
163
|
+
vd.addCommand('Space', 'exec-longname', 'exec_longname(inputLongname())', 'execute command by its longname')
|