visidata 2.11.1__py3-none-any.whl → 3.0.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.
Files changed (256) hide show
  1. visidata/__init__.py +72 -91
  2. visidata/_input.py +259 -42
  3. visidata/_open.py +84 -29
  4. visidata/_types.py +21 -3
  5. visidata/_urlcache.py +17 -4
  6. visidata/aggregators.py +78 -25
  7. visidata/apps/__init__.py +0 -0
  8. visidata/apps/vdsql/__about__.py +8 -0
  9. visidata/apps/vdsql/__init__.py +5 -0
  10. visidata/apps/vdsql/__main__.py +27 -0
  11. visidata/apps/vdsql/_ibis.py +748 -0
  12. visidata/apps/vdsql/bigquery.py +61 -0
  13. visidata/apps/vdsql/clickhouse.py +53 -0
  14. visidata/apps/vdsql/setup.py +40 -0
  15. visidata/apps/vdsql/snowflake.py +67 -0
  16. visidata/apps/vgit/__init__.py +13 -0
  17. {vgit → visidata/apps/vgit}/blame.py +5 -2
  18. {vgit → visidata/apps/vgit}/branch.py +31 -16
  19. {vgit → visidata/apps/vgit}/config.py +3 -3
  20. visidata/apps/vgit/diff.py +169 -0
  21. visidata/apps/vgit/gitsheet.py +161 -0
  22. {vgit → visidata/apps/vgit}/grep.py +6 -5
  23. visidata/apps/vgit/log.py +81 -0
  24. {vgit → visidata/apps/vgit}/main.py +18 -5
  25. {vgit → visidata/apps/vgit}/remote.py +8 -4
  26. visidata/apps/vgit/repos.py +71 -0
  27. {vgit → visidata/apps/vgit}/setup.py +6 -4
  28. visidata/apps/vgit/stash.py +69 -0
  29. visidata/apps/vgit/status.py +204 -0
  30. {vgit → visidata/apps/vgit}/statusbar.py +2 -0
  31. visidata/basesheet.py +63 -51
  32. visidata/canvas.py +208 -93
  33. visidata/choose.py +6 -6
  34. visidata/clean_names.py +29 -0
  35. visidata/clipboard.py +73 -17
  36. visidata/cliptext.py +220 -46
  37. visidata/cmdlog.py +88 -114
  38. visidata/color.py +142 -56
  39. visidata/column.py +121 -129
  40. visidata/ddw/input.ddw +74 -79
  41. visidata/ddw/regex.ddw +57 -0
  42. visidata/ddwplay.py +33 -14
  43. visidata/deprecated.py +77 -3
  44. visidata/desktop/visidata.desktop +7 -0
  45. visidata/editor.py +12 -6
  46. visidata/errors.py +6 -2
  47. visidata/experimental/__init__.py +0 -0
  48. visidata/experimental/diff_sheet.py +29 -0
  49. visidata/experimental/digit_autoedit.py +6 -0
  50. visidata/experimental/gdrive.py +89 -0
  51. visidata/experimental/google.py +37 -0
  52. visidata/experimental/gsheets.py +79 -0
  53. visidata/experimental/live_search.py +37 -0
  54. visidata/experimental/liveupdate.py +45 -0
  55. visidata/experimental/mark.py +133 -0
  56. visidata/experimental/noahs_tapestry/__init__.py +1 -0
  57. visidata/experimental/noahs_tapestry/tapestry.py +147 -0
  58. visidata/experimental/rownum.py +73 -0
  59. visidata/experimental/slide_cells.py +26 -0
  60. visidata/expr.py +8 -4
  61. visidata/extensible.py +22 -4
  62. visidata/features/__init__.py +0 -0
  63. visidata/features/addcol_audiometadata.py +42 -0
  64. visidata/features/addcol_histogram.py +34 -0
  65. visidata/features/canvas_save_svg.py +69 -0
  66. visidata/features/change_precision.py +46 -0
  67. visidata/features/cmdpalette.py +197 -0
  68. visidata/features/colorbrewer.py +363 -0
  69. visidata/{colorsheet.py → features/colorsheet.py} +17 -16
  70. visidata/features/command_server.py +105 -0
  71. visidata/features/currency_to_usd.py +70 -0
  72. visidata/{customdate.py → features/customdate.py} +2 -0
  73. visidata/features/dedupe.py +132 -0
  74. visidata/{describe.py → features/describe.py} +17 -15
  75. visidata/features/errors_guide.py +26 -0
  76. visidata/features/expand_cols.py +202 -0
  77. visidata/{fill.py → features/fill.py} +3 -1
  78. visidata/{freeze.py → features/freeze.py} +11 -6
  79. visidata/features/graph_seaborn.py +79 -0
  80. visidata/features/helloworld.py +10 -0
  81. visidata/features/hint_types.py +17 -0
  82. visidata/{incr.py → features/incr.py} +5 -0
  83. visidata/{join.py → features/join.py} +107 -53
  84. visidata/features/known_cols.py +21 -0
  85. visidata/features/layout.py +62 -0
  86. visidata/{melt.py → features/melt.py} +32 -21
  87. visidata/features/normcol.py +118 -0
  88. visidata/features/open_config.py +7 -0
  89. visidata/features/open_syspaste.py +18 -0
  90. visidata/features/ping.py +157 -0
  91. visidata/features/procmgr.py +208 -0
  92. visidata/features/random_sample.py +6 -0
  93. visidata/{regex.py → features/regex.py} +47 -31
  94. visidata/features/reload_every.py +55 -0
  95. visidata/features/rename_col_cascade.py +30 -0
  96. visidata/features/scroll_context.py +60 -0
  97. visidata/features/select_equal_selected.py +11 -0
  98. visidata/features/setcol_fake.py +65 -0
  99. visidata/{slide.py → features/slide.py} +77 -21
  100. visidata/features/sparkline.py +48 -0
  101. visidata/features/status_source.py +20 -0
  102. visidata/{sysedit.py → features/sysedit.py} +2 -1
  103. visidata/features/sysopen_mailcap.py +46 -0
  104. visidata/features/term_extras.py +13 -0
  105. visidata/{transpose.py → features/transpose.py} +5 -4
  106. visidata/features/type_ipaddr.py +73 -0
  107. visidata/features/type_url.py +11 -0
  108. visidata/{unfurl.py → features/unfurl.py} +9 -9
  109. visidata/{window.py → features/window.py} +2 -2
  110. visidata/form.py +50 -21
  111. visidata/freqtbl.py +81 -33
  112. visidata/fuzzymatch.py +414 -0
  113. visidata/graph.py +105 -33
  114. visidata/guide.py +200 -0
  115. visidata/help.py +75 -44
  116. visidata/hint.py +39 -0
  117. visidata/indexsheet.py +109 -0
  118. visidata/input_history.py +55 -0
  119. visidata/interface.py +58 -0
  120. visidata/keys.py +20 -16
  121. visidata/loaders/__init__.py +9 -0
  122. visidata/loaders/_pandas.py +61 -21
  123. visidata/loaders/api_airtable.py +70 -0
  124. visidata/loaders/api_bitio.py +102 -0
  125. visidata/loaders/api_matrix.py +148 -0
  126. visidata/loaders/api_reddit.py +306 -0
  127. visidata/loaders/api_zulip.py +249 -0
  128. visidata/loaders/archive.py +41 -7
  129. visidata/loaders/arrow.py +7 -7
  130. visidata/loaders/conll.py +49 -0
  131. visidata/loaders/csv.py +25 -7
  132. visidata/loaders/eml.py +3 -4
  133. visidata/loaders/f5log.py +1204 -0
  134. visidata/loaders/fec.py +325 -0
  135. visidata/loaders/fixed_width.py +2 -4
  136. visidata/loaders/frictionless.py +3 -3
  137. visidata/loaders/geojson.py +8 -5
  138. visidata/loaders/google.py +48 -0
  139. visidata/loaders/graphviz.py +4 -4
  140. visidata/loaders/hdf5.py +4 -4
  141. visidata/loaders/html.py +54 -12
  142. visidata/loaders/http.py +84 -30
  143. visidata/loaders/imap.py +20 -10
  144. visidata/loaders/jrnl.py +52 -0
  145. visidata/loaders/json.py +83 -29
  146. visidata/loaders/jsonla.py +74 -0
  147. visidata/loaders/lsv.py +15 -11
  148. visidata/loaders/mailbox.py +40 -0
  149. visidata/loaders/markdown.py +1 -3
  150. visidata/loaders/mbtiles.py +4 -5
  151. visidata/loaders/mysql.py +11 -13
  152. visidata/loaders/npy.py +7 -7
  153. visidata/loaders/odf.py +4 -1
  154. visidata/loaders/orgmode.py +428 -0
  155. visidata/loaders/pandas_freqtbl.py +14 -20
  156. visidata/loaders/parquet.py +62 -6
  157. visidata/loaders/pcap.py +3 -3
  158. visidata/loaders/pdf.py +4 -3
  159. visidata/loaders/png.py +19 -13
  160. visidata/loaders/postgres.py +9 -8
  161. visidata/loaders/rec.py +7 -3
  162. visidata/loaders/s3.py +342 -0
  163. visidata/loaders/sas.py +5 -5
  164. visidata/loaders/scrape.py +186 -0
  165. visidata/loaders/shp.py +6 -5
  166. visidata/loaders/spss.py +5 -6
  167. visidata/loaders/sqlite.py +68 -28
  168. visidata/loaders/texttables.py +1 -1
  169. visidata/loaders/toml.py +60 -0
  170. visidata/loaders/tsv.py +61 -19
  171. visidata/loaders/ttf.py +19 -7
  172. visidata/loaders/unzip_http.py +6 -5
  173. visidata/loaders/usv.py +1 -1
  174. visidata/loaders/vcf.py +16 -16
  175. visidata/loaders/vds.py +10 -7
  176. visidata/loaders/vdx.py +30 -5
  177. visidata/loaders/xlsb.py +8 -1
  178. visidata/loaders/xlsx.py +145 -25
  179. visidata/loaders/xml.py +6 -3
  180. visidata/loaders/xword.py +4 -4
  181. visidata/loaders/yaml.py +15 -5
  182. visidata/macros.py +129 -42
  183. visidata/main.py +119 -94
  184. visidata/mainloop.py +101 -155
  185. visidata/man/parse_options.py +2 -2
  186. visidata/man/vd.1 +302 -149
  187. visidata/man/vd.txt +291 -154
  188. visidata/memory.py +3 -3
  189. visidata/menu.py +104 -423
  190. visidata/metasheets.py +59 -141
  191. visidata/modify.py +78 -23
  192. visidata/motd.py +3 -3
  193. visidata/mouse.py +137 -0
  194. visidata/movement.py +43 -35
  195. visidata/optionssheet.py +99 -0
  196. visidata/path.py +113 -32
  197. visidata/pivot.py +73 -47
  198. visidata/plugins.py +65 -192
  199. visidata/pyobj.py +55 -205
  200. visidata/rename_col.py +20 -0
  201. visidata/save.py +37 -20
  202. visidata/search.py +54 -10
  203. visidata/selection.py +84 -5
  204. visidata/settings.py +162 -25
  205. visidata/sheets.py +239 -260
  206. visidata/shell.py +51 -21
  207. visidata/sidebar.py +162 -0
  208. visidata/sort.py +11 -4
  209. visidata/statusbar.py +114 -104
  210. visidata/stored_list.py +43 -0
  211. visidata/stored_prop.py +38 -0
  212. visidata/tests/benchmark.csv +52 -0
  213. visidata/tests/conftest.py +3 -3
  214. visidata/tests/test_cliptext.py +39 -0
  215. visidata/tests/test_commands.py +65 -7
  216. visidata/tests/test_edittext.py +2 -2
  217. visidata/tests/test_features.py +28 -0
  218. visidata/tests/test_menu.py +14 -0
  219. visidata/tests/test_path.py +13 -4
  220. visidata/text_source.py +53 -0
  221. visidata/textsheet.py +10 -3
  222. visidata/theme.py +44 -0
  223. visidata/themes/__init__.py +0 -0
  224. visidata/themes/ascii8.py +84 -0
  225. visidata/themes/asciimono.py +84 -0
  226. visidata/themes/light.py +17 -0
  227. visidata/threads.py +89 -40
  228. visidata/tuiwin.py +22 -0
  229. visidata/type_currency.py +22 -3
  230. visidata/type_date.py +31 -9
  231. visidata/type_floatsi.py +5 -1
  232. visidata/undo.py +17 -5
  233. visidata/utils.py +106 -23
  234. visidata/vdobj.py +28 -17
  235. visidata/windows.py +10 -0
  236. visidata/wrappers.py +9 -3
  237. visidata-3.0.1.data/data/share/applications/visidata.desktop +7 -0
  238. {visidata-2.11.1.data → visidata-3.0.1.data}/data/share/man/man1/vd.1 +302 -149
  239. {visidata-2.11.1.data → visidata-3.0.1.data}/data/share/man/man1/visidata.1 +302 -149
  240. visidata-3.0.1.data/scripts/vd2to3.vdx +9 -0
  241. {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/METADATA +12 -8
  242. visidata-3.0.1.dist-info/RECORD +258 -0
  243. {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/WHEEL +1 -1
  244. vgit/__init__.py +0 -1
  245. vgit/gitsheet.py +0 -164
  246. visidata/layout.py +0 -44
  247. visidata/misc.py +0 -5
  248. visidata-2.11.1.data/scripts/vgit +0 -9
  249. visidata-2.11.1.dist-info/RECORD +0 -155
  250. {vgit → visidata/apps/vgit}/__main__.py +0 -0
  251. {vgit → visidata/apps/vgit}/abort.py +0 -0
  252. /visidata/{repeat.py → features/repeat.py} +0 -0
  253. {visidata-2.11.1.data → visidata-3.0.1.data}/scripts/vd +0 -0
  254. {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/LICENSE.gpl3 +0 -0
  255. {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/entry_points.txt +0 -0
  256. {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/top_level.txt +0 -0
@@ -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,197 @@
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
+ vd.help_longname = '''# Choose Command
10
+ Start typing a command longname or keyword in its helpstring.
11
+
12
+ - `Enter` to execute top command.
13
+ - `Tab` to highlight top command.
14
+
15
+ ## When Command Highlighted
16
+
17
+ - `Tab`/`Shift+Tab` to cycle highlighted command.
18
+ - `Enter` to execute highlighted command.
19
+ - `0-9` to execute numbered command.
20
+ '''
21
+
22
+ def add_to_input(v, i, value=''):
23
+ items = list(v.split())
24
+ if not v or v.endswith(' '):
25
+ items.append(value)
26
+ else:
27
+ items[-1] = value
28
+ v = ' '.join(items) + ' '
29
+ return v, len(v)
30
+
31
+
32
+ def accept_input(v, i, value=None):
33
+ raise AcceptInput(v if value is None else value)
34
+
35
+ def accept_input_if_subset(v, i, value=''):
36
+ # if no input, accept value under cmd palette cursor
37
+ if not v:
38
+ raise AcceptInput(value)
39
+
40
+ # if the last item is a partial match, replace it with the full value
41
+ parts = v.split()
42
+ if value and value.startswith(parts[-1]):
43
+ v = ' '.join(parts[:-1] + [value])
44
+
45
+ raise AcceptInput(v)
46
+
47
+ @VisiData.lazy_property
48
+ def usedInputs(vd):
49
+ return collections.defaultdict(int)
50
+
51
+ @DrawablePane.after
52
+ def execCommand2(sheet, cmd, *args, **kwargs):
53
+ vd.usedInputs[cmd.longname] += 1
54
+
55
+ @BaseSheet.api
56
+ def inputPalette(sheet, prompt, items,
57
+ value_key='key',
58
+ formatter=lambda m, item, trigger_key: f'{trigger_key} {item}',
59
+ multiple=False,
60
+ **kwargs):
61
+ bindings = dict()
62
+
63
+ tabitem = -1
64
+
65
+ def tab(n, nitems):
66
+ nonlocal tabitem
67
+ tabitem = (tabitem + n) % nitems
68
+
69
+ def _draw_palette(value):
70
+ words = value.lower().split()
71
+
72
+ if multiple and words:
73
+ if value.endswith(' '):
74
+ finished_words = words
75
+ unfinished_words = []
76
+ else:
77
+ finished_words = words[:-1]
78
+ unfinished_words = [words[-1]]
79
+ else:
80
+ unfinished_words = words
81
+ finished_words = []
82
+
83
+ unuseditems = [item for item in items if item[value_key] not in finished_words]
84
+
85
+ matches = vd.fuzzymatch(unuseditems, unfinished_words)
86
+
87
+ h = sheet.windowHeight
88
+ w = min(100, sheet.windowWidth)
89
+ nitems = min(h-1, sheet.options.disp_cmdpal_max)
90
+
91
+ useditems = []
92
+ palrows = []
93
+
94
+ for m in matches[:nitems]:
95
+ useditems.append(m.match)
96
+ palrows.append((m, m.match))
97
+
98
+ favitems = sorted([item for item in unuseditems if item not in useditems],
99
+ key=lambda item: -vd.usedInputs.get(item[value_key], 0))
100
+
101
+ for item in favitems[:nitems-len(palrows)]:
102
+ palrows.append((None, item))
103
+
104
+ navailitems = min(len(palrows), nitems)
105
+
106
+ bindings['^I'] = lambda *args: tab(1, navailitems) or args
107
+ bindings['KEY_BTAB'] = lambda *args: tab(-1, navailitems) or args
108
+
109
+ for i in range(nitems-len(palrows)):
110
+ palrows.append((None, None))
111
+
112
+ for i, (m, item) in enumerate(palrows):
113
+ trigger_key = ''
114
+ if tabitem >= 0 and item:
115
+ trigger_key = f'{i+1}'[-1]
116
+ bindings[trigger_key] = partial(add_to_input if multiple else accept_input, value=item[value_key])
117
+
118
+ attr = colors.color_cmdpalette
119
+
120
+ if tabitem < 0 and palrows:
121
+ _ , topitem = palrows[0]
122
+ if multiple:
123
+ bindings[' '] = partial(add_to_input, value=topitem[value_key])
124
+ bindings['^J'] = partial(accept_input_if_subset, value=topitem[value_key])
125
+ else:
126
+ bindings['^J'] = partial(accept_input, value=topitem[value_key])
127
+ elif item and i == tabitem:
128
+ if multiple:
129
+ bindings['^J'] = partial(accept_input_if_subset, value=item[value_key])
130
+ bindings[' '] = partial(add_to_input, value=item[value_key])
131
+ else:
132
+ bindings['^J'] = partial(accept_input, value=item[value_key])
133
+ attr = colors.color_menu_spec
134
+
135
+ match_summary = formatter(m, item, trigger_key) if item else ' '
136
+
137
+ clipdraw(sheet._scr, h-nitems-1+i, 0, match_summary, attr, w=w)
138
+
139
+ return None
140
+
141
+ completer = CompleteKey(sorted(item[value_key] for item in items))
142
+ return vd.input(prompt,
143
+ completer=completer,
144
+ updater=_draw_palette,
145
+ bindings=bindings,
146
+ **kwargs)
147
+
148
+
149
+ def cmdlist(sheet):
150
+ return [
151
+ AttrDict(longname=row.longname,
152
+ description=sheet.cmddict[(row.sheet, row.longname)].helpstr)
153
+ for row in sheet.rows
154
+ ]
155
+ HelpSheet.cmdlist = drawcache_property(cmdlist)
156
+
157
+
158
+ @BaseSheet.api
159
+ def inputLongname(sheet):
160
+ prompt = 'command name: '
161
+ # get set of commands possible in the sheet
162
+ this_sheets_help = HelpSheet('', source=sheet)
163
+ this_sheets_help.ensureLoaded()
164
+
165
+ def _fmt_cmdpal_summary(match, row, trigger_key):
166
+ keystrokes = this_sheets_help.revbinds.get(row.longname, [None])[0] or ' '
167
+ formatted_longname = match.formatted.get('longname', row.longname) if match else row.longname
168
+ formatted_name = f'[:longname][:onclick {row.longname}]{formatted_longname}[/][/]'
169
+ if vd.options.debug and match:
170
+ keystrokes = f'[{match.score}]'
171
+ r = f' [:keystrokes]{keystrokes.rjust(len(prompt)-5)}[/] '
172
+ if trigger_key:
173
+ r += f'[:keystrokes]{trigger_key}[/]'
174
+ else:
175
+ r += ' '
176
+
177
+ r += f' {formatted_name}'
178
+ if row.description:
179
+ formatted_desc = match.formatted.get('description', row.description) if match else row.description
180
+ r += f' - {formatted_desc}'
181
+ return r
182
+
183
+ return sheet.inputPalette(prompt, this_sheets_help.cmdlist,
184
+ value_key='longname',
185
+ formatter=_fmt_cmdpal_summary,
186
+ help=vd.help_longname,
187
+ type='longname')
188
+
189
+
190
+ @BaseSheet.api
191
+ def exec_longname(sheet, longname):
192
+ if not sheet.getCommand(longname):
193
+ vd.fail(f'no command {longname}')
194
+ sheet.execCommand(longname)
195
+
196
+
197
+ vd.addCommand('Space', 'exec-longname', 'exec_longname(inputLongname())', 'execute command by its longname')
@@ -0,0 +1,363 @@
1
+ '''Colorbrewer scales for plotting in Visidata.
2
+
3
+ Thanks to:
4
+ - Prof. Cynthia Brewer et al. @ colorbrewer.org
5
+ - @dsc https://github.com/dsc/colorbrewer-python
6
+ - @MicahElliott https://gist.github.com/MicahElliott/719710
7
+ - @er1kb https://github.com/er1kb/visidata-plugins
8
+
9
+ This feature adds these commands which can be used while viewing a plot (GraphSheet):
10
+ - color-cycle: change plot colors to another palette
11
+ - color-reset:
12
+ '''
13
+ from visidata import vd, VisiData, GraphSheet, BaseSheet, Sheet, ItemColumn, CellColorizer, ENTER
14
+
15
+ # https://raw.githubusercontent.com/er1kb/colorbrewer-python/master/colorbrewer.py
16
+ # https://gist.githubusercontent.com/er1kb/02f1fee3453431d5c0ccad5e62326a99/raw/73d047f0a3ffc35f0655488547e7f24fa3f04ea6/colortrans.py
17
+
18
+ vd.option('plot_palette', 'Set3', 'colorbrewer palette to use')
19
+
20
+
21
+ colorbrewer_palettes = dict(
22
+ Accent='114 146 216 228 61 198 130 59',
23
+ Dark2='36 166 97 162 70 178 136 59',
24
+ Paired='152 31 150 70 210 160 215 208 182 60 228 130',
25
+ Pastel1='217 152 194 188 223 230 187 225 231',
26
+ Pastel2='152 223 188 224 194 229 224 188',
27
+ Set1='160 67 71 97 208 227 130 211 102',
28
+ Set2='73 209 110 176 149 220 186 145',
29
+ Set3='116 229 146 209 110 215 149 224 188 139 194 227',
30
+
31
+ YellowGreen = {
32
+ 3: '229 150 71',
33
+ 4: '230 150 114 29',
34
+ 5: '230 150 114 71 23',
35
+ 6: '230 193 150 114 71 23',
36
+ 7: '230 193 150 114 71 29 23',
37
+ 8: '230 229 193 150 114 71 29 23',
38
+ 9: '230 229 193 150 114 71 29 23 22',
39
+ },
40
+ YellowGreenBlue = {
41
+ 3: '229 115 31',
42
+ 4: '230 151 74 25',
43
+ 5: '230 151 74 31 24',
44
+ 6: '230 187 115 74 31 24',
45
+ 7: '230 187 115 74 31 25 18',
46
+ 8: '230 229 187 115 74 31 25 18',
47
+ 9: '230 229 187 115 74 31 25 24 17',
48
+ },
49
+ GreenBlue = {
50
+ 3: '194 151 74',
51
+ 4: '230 151 116 31',
52
+ 5: '230 151 116 74 25',
53
+ 6: '230 194 151 116 74 25',
54
+ 7: '230 194 151 116 74 31 25',
55
+ 8: '231 194 194 151 116 74 31 25',
56
+ 9: '231 194 194 151 116 74 31 25 24',
57
+ },
58
+ BlueGreen = {
59
+ 3: '195 116 35',
60
+ 4: '231 152 73 29',
61
+ 5: '231 152 73 35 22',
62
+ 6: '231 194 116 73 35 22',
63
+ 7: '231 194 116 73 72 29 22',
64
+ 8: '231 195 194 116 73 72 29 22',
65
+ 9: '231 195 194 116 73 72 29 22 22',
66
+ },
67
+ PurpleBlueGreen = {
68
+ 3: '225 146 30',
69
+ 4: '231 152 74 30',
70
+ 5: '231 152 74 30 23',
71
+ 6: '231 188 146 74 30 23',
72
+ 7: '231 188 146 74 67 30 23',
73
+ 8: '231 225 188 146 74 67 30 23',
74
+ 9: '231 225 188 146 74 67 30 23 23',
75
+ },
76
+ PurpleBlue = {
77
+ 3: '225 146 31',
78
+ 4: '231 152 110 25',
79
+ 5: '231 152 110 31 24',
80
+ 6: '231 188 146 110 31 24',
81
+ 7: '231 188 146 110 67 25 24',
82
+ 8: '231 225 188 146 110 67 25 24',
83
+ 9: '231 225 188 146 110 67 25 24 23',
84
+ },
85
+ BluePurple = {
86
+ 3: '195 146 97',
87
+ 4: '231 152 104 97',
88
+ 5: '231 152 104 97 90',
89
+ 6: '231 152 146 104 97 90',
90
+ 7: '231 152 146 104 97 97 53',
91
+ 8: '231 195 152 146 104 97 97 53',
92
+ 9: '231 195 152 146 104 97 97 90 53',
93
+ },
94
+ RedPurple = {
95
+ 3: '224 217 162',
96
+ 4: '230 217 205 126',
97
+ 5: '230 217 205 162 90',
98
+ 6: '230 223 217 205 162 90',
99
+ 7: '230 223 217 205 168 126 90',
100
+ 8: '231 224 223 217 205 168 126 90',
101
+ 9: '231 224 223 217 205 168 126 90 53',
102
+ },
103
+ PurpleRed = {
104
+ 3: '189 176 162',
105
+ 4: '231 182 169 161',
106
+ 5: '231 182 169 162 89',
107
+ 6: '231 182 176 169 162 89',
108
+ 7: '231 182 176 169 162 161 89',
109
+ 8: '231 189 182 176 169 162 161 89',
110
+ 9: '231 189 182 176 169 162 161 89 52',
111
+ },
112
+ OrangeRed = {
113
+ 3: '224 216 167',
114
+ 4: '230 222 209 166',
115
+ 5: '230 222 209 167 124',
116
+ 6: '230 223 216 209 167 124',
117
+ 7: '230 223 216 209 203 166 88',
118
+ 8: '231 224 223 216 209 203 166 88',
119
+ 9: '231 224 223 216 209 203 166 124 88',
120
+ },
121
+ YellowOrangeRed = {
122
+ 3: '229 215 202',
123
+ 4: '229 221 209 160',
124
+ 5: '229 221 209 202 124',
125
+ 6: '229 222 215 209 202 124',
126
+ 7: '229 222 215 209 202 160 124',
127
+ 8: '230 229 222 215 209 202 160 124',
128
+ 9: '230 229 222 215 209 202 160 124 88',
129
+ },
130
+ YellowOrangeBrown= {
131
+ 3: '229 221 166',
132
+ 4: '230 222 208 166',
133
+ 5: '230 222 208 166 94',
134
+ 6: '230 222 221 208 166 94',
135
+ 7: '230 222 221 208 202 166 88',
136
+ 8: '230 229 222 221 208 202 166 88',
137
+ 9: '230 229 222 221 208 202 166 94 52',
138
+ },
139
+ Purples = {
140
+ 3: '231 146 97',
141
+ 4: '231 188 140 61',
142
+ 5: '231 188 140 97 54',
143
+ 6: '231 189 146 140 97 54',
144
+ 7: '231 189 146 140 103 61 54',
145
+ 8: '231 231 189 146 140 103 61 54',
146
+ 9: '231 231 189 146 140 103 61 54 54',
147
+ },
148
+ Blues = {
149
+ 3: '195 152 67',
150
+ 4: '231 152 74 25',
151
+ 5: '231 152 74 67 25',
152
+ 6: '231 189 152 74 67 25',
153
+ 7: '231 189 152 74 68 25 24',
154
+ 8: '231 195 189 152 74 68 25 24',
155
+ 9: '231 195 189 152 74 68 25 25 23',
156
+ },
157
+ Greens = {
158
+ 3: '194 151 71',
159
+ 4: '230 151 114 29',
160
+ 5: '230 151 114 71 22',
161
+ 6: '230 187 151 114 71 22',
162
+ 7: '230 187 151 114 71 29 23',
163
+ 8: '231 194 187 151 114 71 29 23',
164
+ 9: '231 194 187 151 114 71 29 22 22',
165
+ },
166
+ Oranges = {
167
+ 3: '224 215 166',
168
+ 4: '230 216 209 166',
169
+ 5: '230 216 209 166 130',
170
+ 6: '230 223 215 209 166 130',
171
+ 7: '230 223 215 209 202 166 88',
172
+ 8: '231 224 223 215 209 202 166 88',
173
+ 9: '231 224 223 215 209 202 166 130 88',
174
+ },
175
+ Reds = {
176
+ 3: '224 209 160',
177
+ 4: '224 216 203 160',
178
+ 5: '224 216 203 160 124',
179
+ 6: '224 217 209 203 160 124',
180
+ 7: '224 217 209 203 202 160 88',
181
+ 8: '231 224 217 209 203 202 160 88',
182
+ 9: '231 224 217 209 203 202 160 124 52',
183
+ },
184
+ Greys = {
185
+ 3: '231 145 59',
186
+ 4: '231 188 102 59',
187
+ 5: '231 188 102 59 16',
188
+ 6: '231 188 145 102 59 16',
189
+ 7: '231 188 145 102 102 59 16',
190
+ 8: '231 231 188 145 102 102 59 16',
191
+ 9: '231 231 188 145 102 102 59 16 16',
192
+ },
193
+ PurpleOrange = {
194
+ 3: '215 231 104',
195
+ 4: '166 215 146 60',
196
+ 5: '166 215 231 146 60',
197
+ 6: '130 215 223 189 104 54',
198
+ 7: '130 215 223 231 189 104 54',
199
+ 8: '130 172 215 223 189 146 103 54',
200
+ 9: '130 172 215 223 231 189 146 103 54',
201
+ 10: '94 130 172 215 223 189 146 103 54 17',
202
+ 11: '94 130 172 215 223 231 189 146 103 54 17',
203
+ },
204
+ BrownGreen = {
205
+ 3: '179 231 73',
206
+ 4: '130 180 115 29',
207
+ 5: '130 180 231 115 29',
208
+ 6: '94 179 224 188 73 23',
209
+ 7: '94 179 224 231 188 73 23',
210
+ 8: '94 136 180 224 188 115 66 23',
211
+ 9: '94 136 180 224 231 188 115 66 23',
212
+ 10: '58 94 136 180 224 188 115 66 23 23',
213
+ 11: '58 94 136 180 224 231 188 115 66 23 23',
214
+ },
215
+ PurpleGreen = {
216
+ 3: '140 231 108',
217
+ 4: '96 146 151 29',
218
+ 5: '96 146 231 151 29',
219
+ 6: '90 140 188 194 108 29',
220
+ 7: '90 140 188 231 194 108 29',
221
+ 8: '90 97 146 188 194 151 71 29',
222
+ 9: '90 97 146 188 231 194 151 71 29',
223
+ 10: '53 90 97 146 188 194 151 71 29 22',
224
+ 11: '53 90 97 146 188 231 194 151 71 29 22',
225
+ },
226
+ PinkYellowGreen = {
227
+ 3: '182 231 149',
228
+ 4: '162 218 150 70',
229
+ 5: '162 218 231 150 70',
230
+ 6: '162 182 225 194 149 64',
231
+ 7: '162 182 225 231 194 149 64',
232
+ 8: '162 175 218 225 194 150 107 64',
233
+ 9: '162 175 218 225 231 194 150 107 64',
234
+ 10: '89 162 175 218 225 194 150 107 64 22',
235
+ 11: '89 162 175 218 225 231 194 150 107 64 22',
236
+ },
237
+ RedBlue = {
238
+ 3: '209 231 74',
239
+ 4: '160 216 116 25',
240
+ 5: '160 216 231 116 25',
241
+ 6: '124 209 224 189 74 25',
242
+ 7: '124 209 224 231 189 74 25',
243
+ 8: '124 167 216 224 189 116 68 25',
244
+ 9: '124 167 216 224 231 189 116 68 25',
245
+ 10: '52 124 167 216 224 189 116 68 25 23',
246
+ 11: '52 124 167 216 224 231 189 116 68 25 23',
247
+ },
248
+ RedGrey = {
249
+ 3: '209 231 102',
250
+ 4: '160 216 145 59',
251
+ 5: '160 216 231 145 59',
252
+ 6: '124 209 224 188 102 59',
253
+ 7: '124 209 224 231 188 102 59',
254
+ 8: '124 167 216 224 188 145 102 59',
255
+ 9: '124 167 216 224 231 188 145 102 59',
256
+ 10: '52 124 167 216 224 188 145 102 59 16',
257
+ 11: '52 124 167 216 224 231 188 145 102 59 16',
258
+ },
259
+ RedYellowBlue = {
260
+ 3: '209 229 110',
261
+ 4: '160 215 152 31',
262
+ 5: '160 215 229 152 31',
263
+ 6: '166 209 222 195 110 67',
264
+ 7: '166 209 222 229 195 110 67',
265
+ 8: '166 203 215 222 195 152 110 67',
266
+ 9: '166 203 215 222 229 195 152 110 67',
267
+ 10: '124 166 203 215 222 195 152 110 67 60',
268
+ 11: '124 166 203 215 222 229 195 152 110 67 60',
269
+ },
270
+ Spectral = {
271
+ 3: '209 229 114',
272
+ 4: '160 215 151 31',
273
+ 5: '160 215 229 151 31',
274
+ 6: '167 209 222 192 114 67',
275
+ 7: '167 209 222 229 192 114 67',
276
+ 8: '167 203 215 222 192 151 73 67',
277
+ 9: '167 203 215 222 229 192 151 73 67',
278
+ 10: '125 167 203 215 222 192 151 73 67 61',
279
+ 11: '125 167 203 215 222 229 192 151 73 67 61',
280
+ },
281
+ RedYellowGreen = {
282
+ 3: '209 229 113',
283
+ 4: '160 215 149 29',
284
+ 5: '160 215 229 149 29',
285
+ 6: '166 209 222 192 113 29',
286
+ 7: '166 209 222 229 192 113 29',
287
+ 8: '166 203 215 222 192 149 71 29',
288
+ 9: '166 203 215 222 229 192 149 71 29',
289
+ 10: '124 166 203 215 222 192 149 71 29 23',
290
+ 11: '124 166 203 215 222 229 192 149 71 29 23',
291
+ },
292
+ )
293
+
294
+ vd.colorbrewer_choices = [dict(key=k, colors=v) for k, v in colorbrewer_palettes.items()]
295
+
296
+
297
+ class PalettesSheet(Sheet):
298
+ colorizers = [
299
+ CellColorizer(2, None, lambda s,c,r,v: f'black on {v.value}' if c and r and c.name.startswith('c') else None)
300
+ ]
301
+ columns = [
302
+ ItemColumn('name', 0),
303
+ ItemColumn('n', 1, type=int),
304
+ ] + [
305
+ ItemColumn(f'c{i}', i+1)
306
+ for i in range(1, 13)
307
+ ]
308
+
309
+ def iterload(self):
310
+ for palname, pals in colorbrewer_palettes.items():
311
+ if isinstance(pals, str):
312
+ yield [palname, len(pals.split())] + pals.split()
313
+ else:
314
+ for n, pal in pals.items():
315
+ yield [palname, n] + pal.split()
316
+
317
+
318
+ @GraphSheet.api
319
+ def cycle_palette(obj):
320
+ pals = list(colorbrewer_palettes.keys())
321
+ i = pals.index(obj.options.plot_palette)
322
+ i = (i+1)%len(pals)
323
+ n = len(obj.legends) if getattr(obj, 'legends', None) else 8
324
+ obj.set_palette(pals[i], n)
325
+
326
+
327
+ @GraphSheet.api
328
+ def set_palette(obj, palname, n):
329
+ r = colorbrewer_palettes[palname]
330
+ if not isinstance(r, str):
331
+ n = max(n, min(r.keys()))
332
+ n = min(n, max(r.keys()))
333
+ r = r[n]
334
+
335
+ vd.status(f'Using {palname} {n}-color palette')
336
+ obj.options.plot_palette = palname
337
+ obj.options.plot_colors = r
338
+
339
+ if isinstance(obj, GraphSheet):
340
+ obj.reload()
341
+
342
+
343
+ VisiData.set_palette = GraphSheet.set_palette
344
+ VisiData.cycle_palette = GraphSheet.cycle_palette
345
+
346
+
347
+ BaseSheet.addCommand(None, 'open-palettes', 'vd.push(PalettesSheet("palettes", source=vd))', 'open color palettes sheet for graphs')
348
+ BaseSheet.addCommand(None, 'cycle-palette', 'vd.cycle_palette()', 'cycle to next color palette for graphs')
349
+ BaseSheet.addCommand(None, 'unset-palette', 'vd.options.unset("plot_colors")', 'reset to default color palette for graphs')
350
+
351
+ GraphSheet.addCommand('C', 'open-palettes-sheet', 'vd.push(PalettesSheet("palettes", source=sheet))', 'open color palettes sheet for this graph')
352
+ GraphSheet.addCommand('zc', 'cycle-palette-sheet', 'sheet.cycle_palette()', 'cycle to next color palette for this graph')
353
+ GraphSheet.addCommand(None, 'unset-palette-sheet', 'sheet.options.unset("plot_colors"); reload()', 'reset to default color palette for this graph')
354
+
355
+ PalettesSheet.addCommand(ENTER, 'choose-palette', 'source.set_palette(cursorRow[0], cursorRow[1])', 'set current palette for source graph')
356
+
357
+ vd.addMenuItems('''
358
+ Plot > Palette > cycle > cycle-palette-sheet
359
+ Plot > Palette > unset > unset-palette-sheet
360
+ Plot > Palette > choose > open-palettes
361
+ ''')
362
+
363
+ vd.addGlobals(PalettesSheet=PalettesSheet)
@@ -1,42 +1,43 @@
1
1
  import curses
2
- from visidata import VisiData, colors, Sheet, Column, RowColorizer, wrapply, BaseSheet
2
+ from visidata import VisiData, colors, Sheet, Column, ItemColumn, RowColorizer, wrapply, BaseSheet
3
3
 
4
4
 
5
5
  class ColorSheet(Sheet):
6
- rowtype = 'colors' # rowdef: [(fg,bg), (color_attr, colornamestr)]
6
+ rowtype = 'colors' # rowdef: [fg, bg, color_attr, colornamestr]
7
7
  columns = [
8
- Column('color', getter=lambda c,r: r[1][1]),
9
- Column('fg', type=int, getter=lambda c,r: r[0][0]),
10
- Column('bg', type=int, getter=lambda c,r: r[0][1]),
11
- Column('attr', width=0, type=int, getter=lambda c,r: r[1][0]),
12
- ]
8
+ ItemColumn('fg', 0, type=int),
9
+ ItemColumn('bg', 1, type=int),
10
+ ItemColumn('pairnum', 2),
11
+ ItemColumn('name', 3),
12
+ ]
13
13
  colorizers = [
14
- RowColorizer(7, None, lambda s,c,r,v: r and r[1][1])
14
+ RowColorizer(7, None, lambda s,c,r,v: r and r[3])
15
15
  ]
16
16
 
17
17
  def iterload(self):
18
18
  self.rows = []
19
19
  for k, v in colors.color_pairs.items():
20
- yield [k, v]
20
+ fg, bg = k
21
+ pairnum, colorname = v
22
+ yield [fg, bg, pairnum, colorname]
21
23
 
22
24
  for i in range(0, 256):
23
- yield [(i, 0), (None, f'{i}')]
25
+ yield [i, 0, None, f'{i}']
24
26
 
25
27
  def draw(self, scr):
26
28
  super().draw(scr)
27
29
  rightcol = max(self._visibleColLayout.values())
28
30
  xstart = rightcol[0] + rightcol[1] + 4
29
31
  for i, r in enumerate(self.rows[(self.topRowIndex//6)*6:(self.bottomRowIndex//6+1)*6]):
32
+ fg, bg, _, colorstr = r
33
+ s = f'█▌{fg:3}▐█'
30
34
  y=i//6+1
31
- x=(i%6)*3+xstart
32
- c = r[1][1]
33
- s = '██'
35
+ x=(i%6)*(len(s)+2)+xstart
34
36
  if y > self.windowHeight-1:
35
37
  break
36
38
  if r is self.cursorRow:
37
- x -= 1
38
- s = '[██]'
39
- scr.addstr(y, x, s, colors[c])
39
+ s = f'█[{fg:3}]█'
40
+ scr.addstr(y, x, s, colors[colorstr].attr)
40
41
 
41
42
 
42
43
  BaseSheet.addCommand(None, 'open-colors', 'vd.push(vd.colorsSheet)', 'open Color Sheet with available terminal colors')