listpick 0.1.13.61__py3-none-any.whl → 0.1.14.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.

Potentially problematic release.


This version of listpick might be problematic. Click here for more details.

listpick/listpick_app.py CHANGED
@@ -106,6 +106,7 @@ class Picker:
106
106
 
107
107
  selections: dict = {},
108
108
  cell_selections: dict[tuple[int,int], bool] = {},
109
+ selected_cells_by_row: dict = {},
109
110
  highlight_full_row: bool =False,
110
111
  cell_cursor: bool = False,
111
112
 
@@ -173,6 +174,19 @@ class Picker:
173
174
  debug: bool = False,
174
175
  debug_level: int = 1,
175
176
 
177
+ command_stack: list = [],
178
+
179
+ loaded_file: str = "Untitled",
180
+ loaded_files: list[str] = ["Untitled"],
181
+ loaded_file_index: int = 0,
182
+ loaded_file_states: list[dict] = [{}],
183
+
184
+
185
+ sheets = ["Untitled"],
186
+ sheet_name = "Untitled",
187
+ sheet_index = 0,
188
+ sheet_states = [{}],
189
+
176
190
  ):
177
191
  self.stdscr = stdscr
178
192
  self.items = items
@@ -225,6 +239,7 @@ class Picker:
225
239
 
226
240
  self.selections = selections
227
241
  self.cell_selections = cell_selections
242
+ self.selected_cells_by_row = selected_cells_by_row
228
243
  self.highlight_full_row = highlight_full_row
229
244
  self.cell_cursor = cell_cursor
230
245
 
@@ -283,7 +298,7 @@ class Picker:
283
298
  self.registers = {}
284
299
 
285
300
  self.SORT_METHODS = SORT_METHODS
286
- self.command_stack = []
301
+ self.command_stack = command_stack
287
302
  self.leftmost_column = leftmost_column
288
303
  self.leftmost_char = leftmost_char
289
304
 
@@ -297,7 +312,6 @@ class Picker:
297
312
  self.cursor_pos_prev = 0
298
313
  self.ids = []
299
314
  self.ids_tuples = []
300
- self.selected_cells_by_row = {}
301
315
 
302
316
  # History variables
303
317
  self.history_filter_and_search = history_filter_and_search
@@ -312,6 +326,17 @@ class Picker:
312
326
  self.debug = debug
313
327
  self.debug_level = debug_level
314
328
 
329
+ # Multiple file support
330
+ self.loaded_files = loaded_files
331
+ self.loaded_file = loaded_file
332
+ self.loaded_file_index = loaded_file_index
333
+ self.loaded_file_states = loaded_file_states
334
+
335
+ # Multiple sheet support
336
+ self.sheet_index = sheet_index
337
+ self.sheet_name = sheet_name
338
+ self.sheet_states = sheet_states
339
+ self.sheets = sheets
315
340
 
316
341
  self.initialise_picker_state(reset_colours=self.reset_colours)
317
342
 
@@ -320,6 +345,8 @@ class Picker:
320
345
  self.footer = self.footer_options[self.footer_style]
321
346
 
322
347
 
348
+
349
+
323
350
  def calculate_section_sizes(self):
324
351
  """
325
352
  Calculte the following for the Picker:
@@ -495,8 +522,12 @@ class Picker:
495
522
 
496
523
  if len(self.items) and len(self.cell_selections) != len(self.items)*len(self.items[0]):
497
524
  self.cell_selections = {(i, j) : False if (i, j) not in self.cell_selections else self.cell_selections[(i, j)] for i in range(len(self.items)) for j in range(len(self.items[0]))}
525
+ self.selected_cells_by_row = get_selected_cells_by_row(self.cell_selections)
498
526
  elif len(self.items) == 0:
499
527
  self.cell_selections = {}
528
+ self.selected_cells_by_row = {}
529
+
530
+
500
531
 
501
532
  if len(self.require_option) < len(self.items):
502
533
  self.require_option += [self.require_option_default for i in range(len(self.items)-len(self.require_option))]
@@ -556,6 +587,20 @@ class Picker:
556
587
  assert new_pos < len(self.items)
557
588
  self.cursor_pos = new_pos
558
589
 
590
+ # Sheets and files
591
+ if len(self.sheet_states) < len(self.sheets):
592
+ self.sheet_states += [{} for _ in range(len(self.sheets) - len(self.sheet_states))]
593
+ if len(self.sheets):
594
+ if self.sheet_index >= len(self.sheets):
595
+ self.sheet_index = 0
596
+ self.sheet_name = self.sheets[self.sheet_index]
597
+
598
+ if len(self.loaded_file_states) < len(self.loaded_files):
599
+ self.loaded_file_states += [{} for _ in range(len(self.loaded_files) - len(self.loaded_file_states))]
600
+ if len(self.loaded_files):
601
+ if self.loaded_file_index >= len(self.loaded_files):
602
+ self.loaded_file_index = 0
603
+ self.loaded_file = self.loaded_files[self.loaded_file_index]
559
604
 
560
605
  # if tracking and len(self.items) > 1:
561
606
  # Ensure that selected indices are tracked upon data refresh
@@ -591,6 +636,7 @@ class Picker:
591
636
  self.cursor_pos = [i[0] for i in self.indexed_items].index(cursor_pos_x)
592
637
  else:
593
638
  self.cursor_pos = 0
639
+
594
640
 
595
641
 
596
642
 
@@ -1045,6 +1091,7 @@ class Picker:
1045
1091
  function_data = {
1046
1092
  "selections": self.selections,
1047
1093
  "cell_selections": self.cell_selections,
1094
+ "selected_cells_by_row": self.selected_cells_by_row,
1048
1095
  "items_per_page": self.items_per_page,
1049
1096
  "current_row": self.current_row,
1050
1097
  "current_page": self.current_page,
@@ -1136,17 +1183,55 @@ class Picker:
1136
1183
  "debug_level": self.debug_level,
1137
1184
  "reset_colours": self.reset_colours,
1138
1185
  "unicode_char_width": self.unicode_char_width,
1186
+ "command_stack": self.command_stack,
1187
+ "loaded_file": self.loaded_file,
1188
+ "loaded_files": self.loaded_files,
1189
+ "loaded_file_index": self.loaded_file_index,
1190
+ "loaded_file_states": self.loaded_file_states,
1191
+ "sheet_index": self.sheet_index,
1192
+ "sheets": self.sheets,
1193
+ "sheet_name": self.sheet_name,
1194
+ "sheet_states": self.sheet_states,
1139
1195
  }
1140
1196
  return function_data
1141
1197
 
1142
- def set_function_data(self, function_data: dict) -> None:
1198
+ def set_function_data(self, function_data: dict, reset_absent_variables: bool = False, do_not_set: list=[]) -> None:
1143
1199
  """ Set variables from state dict containing core variables."""
1144
1200
  self.logger.info(f"function: set_function_data()")
1145
1201
  variables = self.get_function_data().keys()
1146
1202
 
1203
+ x = Picker(self.stdscr, reset_colours=False)
1204
+
1205
+
1206
+ common_picker_vars = [
1207
+ "loaded_file_index",
1208
+ "loaded_file_states",
1209
+ "loaded_files",
1210
+ "loaded_file",
1211
+ "command_stack",
1212
+ "colour_theme_number",
1213
+ "reset_colours",
1214
+ "show_footer",
1215
+ "show_header",
1216
+ "history_filter_and_search",
1217
+ "history_settings",
1218
+ "history_opts",
1219
+ "history_edits",
1220
+ "history_pipes",
1221
+ "reset_colours",
1222
+ "cell_cursor",
1223
+ "top_gap",
1224
+ "unicode_char_width",
1225
+ "show_row_header",
1226
+ ]
1227
+
1147
1228
  for var in variables:
1148
1229
  if var in function_data:
1149
1230
  setattr(self, var, function_data[var])
1231
+ elif reset_absent_variables and var not in common_picker_vars and var not in do_not_set:
1232
+ # Set value to the default for an empty picker
1233
+ setattr(self, var, getattr(x, var))
1234
+
1150
1235
 
1151
1236
  reset_colours = bool("colour_theme_number" in function_data)
1152
1237
  self.initialise_picker_state(reset_colours=reset_colours)
@@ -1260,7 +1345,12 @@ class Picker:
1260
1345
  message_width = notification_width-5
1261
1346
 
1262
1347
  if not message: message = "!!"
1263
- submenu_items = [" "+message[i*message_width:(i+1)*message_width] for i in range(len(message)//message_width+1)]
1348
+ if type(message) == type(""):
1349
+ mw = message_width
1350
+ submenu_items = [[message[i*mw:(i+1)*mw]] for i in range(len(message)//mw+1)]
1351
+ elif type(message) != type([]):
1352
+ submenu_items = [[" !!"]]
1353
+
1264
1354
 
1265
1355
  notification_remap_keys = {
1266
1356
  curses.KEY_RESIZE: curses.KEY_F5,
@@ -1270,6 +1360,7 @@ class Picker:
1270
1360
  h, w = stdscr.getmaxyx()
1271
1361
 
1272
1362
  submenu_win = curses.newwin(notification_height, notification_width, 3, w - (notification_width+4))
1363
+ # submenu_win = self.stdscr.subwin(notification_height, notification_width, 3, w - (notification_width+4))
1273
1364
  notification_data = {
1274
1365
  "items": submenu_items,
1275
1366
  "title": title,
@@ -1286,6 +1377,10 @@ class Picker:
1286
1377
  "cancel_is_back": True,
1287
1378
  "reset_colours": False,
1288
1379
 
1380
+ "loaded_files": [],
1381
+ "loaded_file_states": [],
1382
+ "loaded_file": "",
1383
+ "loaded_file_index": 0,
1289
1384
  }
1290
1385
  OptionPicker = Picker(submenu_win, **notification_data)
1291
1386
  s, o, f = OptionPicker.run()
@@ -1338,15 +1433,15 @@ class Picker:
1338
1433
  # highlights = [highlight for highlight in highlights if "type" not in highlight or highlight["type"] != "search" ]
1339
1434
 
1340
1435
  self.highlights_hide = not self.highlights_hide
1341
- elif setting[0] == "s":
1342
- if 0 <= int(setting[1:]) < len(self.items[0]):
1343
- self.sort_column = int(setting[1:])
1344
- if len(self.indexed_items):
1345
- current_pos = self.indexed_items[self.cursor_pos][0]
1346
- sort_items(self.indexed_items, sort_method=self.columns_sort_method[self.sort_column], sort_column=self.sort_column, sort_reverse=self.sort_reverse[self.sort_column]) # Re-sort items based on new column
1347
- if len(self.indexed_items):
1348
- new_pos = [row[0] for row in self.indexed_items].index(current_pos)
1349
- self.cursor_pos = new_pos
1436
+ # elif setting[0] == "s":
1437
+ # if 0 <= int(setting[1:]) < len(self.items[0]):
1438
+ # self.sort_column = int(setting[1:])
1439
+ # if len(self.indexed_items):
1440
+ # current_pos = self.indexed_items[self.cursor_pos][0]
1441
+ # sort_items(self.indexed_items, sort_method=self.columns_sort_method[self.sort_column], sort_column=self.sort_column, sort_reverse=self.sort_reverse[self.sort_column]) # Re-sort items based on new column
1442
+ # if len(self.indexed_items):
1443
+ # new_pos = [row[0] for row in self.indexed_items].index(current_pos)
1444
+ # self.cursor_pos = new_pos
1350
1445
  elif setting == "ct":
1351
1446
  self.centre_in_terminal = not self.centre_in_terminal
1352
1447
  elif setting == "cc":
@@ -1391,6 +1486,47 @@ class Picker:
1391
1486
  self.pin_cursor = not self.pin_cursor
1392
1487
  elif setting == "unicode":
1393
1488
  self.unicode_char_width = not self.unicode_char_width
1489
+ elif setting == "file_next":
1490
+ if len(self.loaded_files) > 1:
1491
+ self.command_stack.append(Command("setting", self.user_settings))
1492
+ # Cache file state
1493
+ self.loaded_file_states[self.loaded_file_index] = self.get_function_data()
1494
+
1495
+ self.loaded_file_index = (self.loaded_file_index + 1) % len(self.loaded_files)
1496
+ self.loaded_file = self.loaded_files[self.loaded_file_index]
1497
+
1498
+ # If we already have a loaded state for this file
1499
+ if self.loaded_file_states[self.loaded_file_index]:
1500
+ self.set_function_data(self.loaded_file_states[self.loaded_file_index])
1501
+ else:
1502
+ self.set_function_data({}, reset_absent_variables=True)
1503
+ self.load_file(self.loaded_file)
1504
+
1505
+ elif setting == "sheet_next":
1506
+ if not os.path.exists(self.loaded_file):
1507
+ self.notification(self.stdscr, message=f"File {repr(self.loaded_file)} not found.")
1508
+ return None
1509
+ if len(self.sheets) > 1:
1510
+ self.command_stack.append(Command("setting", self.user_settings))
1511
+
1512
+ # Cache sheet state
1513
+ self.sheet_states[self.sheet_index] = self.get_function_data()
1514
+ self.sheet_index = (self.sheet_index + 1) % len(self.sheets)
1515
+ self.sheet_name = self.sheets[self.sheet_index]
1516
+
1517
+ # If we already have a loaded state for this file
1518
+ if self.sheet_states[self.sheet_index]:
1519
+ self.set_function_data(self.sheet_states[self.sheet_index])
1520
+ else:
1521
+ function_data = {
1522
+ "sheet_index": self.sheet_index,
1523
+ "sheet_name": self.sheet_name,
1524
+ "sheet_states":self.sheet_states,
1525
+ "sheets": self.sheets,
1526
+ }
1527
+ self.set_function_data(function_data, reset_absent_variables=True)
1528
+ self.load_sheet(self.loaded_file, sheet_number=self.sheet_index)
1529
+
1394
1530
 
1395
1531
  elif setting.startswith("ft"):
1396
1532
  if len(setting) > 2 and setting[2:].isnumeric():
@@ -1458,9 +1594,9 @@ class Picker:
1458
1594
  self.user_settings = ""
1459
1595
  return None
1460
1596
 
1461
-
1462
- self.command_stack.append(Command("setting", self.user_settings))
1463
- self.user_settings = ""
1597
+ if self.user_settings:
1598
+ self.command_stack.append(Command("setting", self.user_settings))
1599
+ self.user_settings = ""
1464
1600
 
1465
1601
  def apply_command(self, command: Command):
1466
1602
  self.logger.info(f"function: apply_command()")
@@ -1768,17 +1904,41 @@ class Picker:
1768
1904
  ]
1769
1905
 
1770
1906
  if s:
1771
- file_to_load = file_picker()
1772
- if file_to_load:
1907
+ restrict_curses(self.stdscr)
1908
+ files_to_load = file_picker()
1909
+ unrestrict_curses(self.stdscr)
1910
+ if files_to_load:
1773
1911
  index = list(s.keys())[0]
1912
+ file_to_load = files_to_load[0]
1774
1913
  return_val = funcs[index](file_to_load)
1775
- self.set_function_data(return_val)
1776
1914
 
1915
+ self.loaded_file_states[self.loaded_file_index] = self.get_function_data()
1916
+
1917
+ self.stdscr.clear()
1918
+ self.draw_screen(self.indexed_items, self.highlights)
1919
+
1920
+ tmp = self.stdscr
1921
+
1922
+ self.loaded_files += files_to_load
1923
+ self.loaded_file_states += [{} for _ in files_to_load]
1924
+ self.loaded_file = file_to_load
1925
+ self.loaded_file_index = len(self.loaded_files)-len(files_to_load)
1926
+
1927
+
1928
+ self.stdscr = tmp
1929
+
1930
+ h, w = self.stdscr.getmaxyx()
1931
+ self.notification(self.stdscr, f"{repr(file_to_load)} has been loaded!")
1932
+
1933
+ self.set_function_data({}, reset_absent_variables=True)
1934
+ self.load_file(self.loaded_file)
1777
1935
  # items = return_val["items"]
1778
1936
  # header = return_val["header"]
1779
- self.initialise_variables()
1937
+ self.stdscr.clear()
1938
+ # self.initialise_variables()
1780
1939
  self.draw_screen(self.indexed_items, self.highlights)
1781
- self.notification(self.stdscr, f"{repr(file_to_load)} has been loaded!")
1940
+ # self.stdscr.refresh()
1941
+
1782
1942
  # if return_val:
1783
1943
  # notification(stdscr, message=return_val, title="Error")
1784
1944
 
@@ -1910,6 +2070,36 @@ class Picker:
1910
2070
  self.cursor_pos = current_cursor_pos
1911
2071
 
1912
2072
 
2073
+ def load_file(self, filename: str) -> None:
2074
+ if not os.path.exists(filename):
2075
+ self.notification(self.stdscr, message = f"File not found: {filename}")
2076
+ return None
2077
+
2078
+ filetype = guess_file_type(filename)
2079
+ items, header, sheets = table_to_list(filename, file_type=filetype)
2080
+
2081
+ if items != None:
2082
+ self.items = items
2083
+ self.header = header if header != None else []
2084
+ self.sheets = sheets
2085
+
2086
+
2087
+ self.initialise_variables()
2088
+
2089
+ def load_sheet(self, filename: str, sheet_number: int = 0):
2090
+ filetype = guess_file_type(filename)
2091
+ items, header, sheets = table_to_list(filename, file_type=filetype, sheet_number=sheet_number)
2092
+
2093
+ if items != None:
2094
+ self.items = items
2095
+ self.header = header if header != None else []
2096
+ self.sheets = sheets
2097
+
2098
+ self.initialise_variables()
2099
+
2100
+
2101
+
2102
+
1913
2103
 
1914
2104
  def run(self) -> Tuple[list[int], str, dict]:
1915
2105
  """ Run the picker. """
@@ -1958,7 +2148,7 @@ class Picker:
1958
2148
 
1959
2149
  while True:
1960
2150
  key = self.stdscr.getch()
1961
- if key:
2151
+ if key != -1:
1962
2152
  self.logger.info(f"key={key}")
1963
2153
  h, w = self.stdscr.getmaxyx()
1964
2154
  if key in self.disabled_keys: continue
@@ -2040,9 +2230,24 @@ class Picker:
2040
2230
 
2041
2231
  elif self.check_key("exit", key, self.keys_dict):
2042
2232
  self.stdscr.clear()
2043
- function_data = self.get_function_data()
2044
- function_data["last_key"] = key
2045
- return [], "", function_data
2233
+ if len(self.loaded_files) <= 1:
2234
+ function_data = self.get_function_data()
2235
+ function_data["last_key"] = key
2236
+ return [], "", function_data
2237
+ else:
2238
+ del self.loaded_files[self.loaded_file_index]
2239
+ del self.loaded_file_states[self.loaded_file_index]
2240
+ self.loaded_file_index = min(self.loaded_file_index, len(self.loaded_files)-1)
2241
+ self.loaded_file = self.loaded_files[self.loaded_file_index]
2242
+
2243
+
2244
+ # If we already have a loaded state for this file
2245
+ if self.loaded_file_states[self.loaded_file_index]:
2246
+ self.set_function_data(self.loaded_file_states[self.loaded_file_index])
2247
+ else:
2248
+ self.set_function_data({}, reset_absent_variables=True)
2249
+ self.load_file(self.loaded_file)
2250
+
2046
2251
  elif self.check_key("full_exit", key, self.keys_dict):
2047
2252
  close_curses(self.stdscr)
2048
2253
  exit()
@@ -2607,6 +2812,7 @@ class Picker:
2607
2812
  # 4. if self.cancel_is_back (e.g., notification) then we exit
2608
2813
  # 4. selecting
2609
2814
 
2815
+ pass
2610
2816
  # Cancel visual de/selection
2611
2817
  if self.is_selecting or self.is_deselecting:
2612
2818
  self.start_selection = -1
@@ -2684,41 +2890,51 @@ class Picker:
2684
2890
 
2685
2891
  elif self.check_key("mode_next", key, self.keys_dict): # tab key
2686
2892
  self.logger.info(f"key_function mode_next")
2687
- # apply setting
2688
- prev_mode_index = self.mode_index
2689
- self.mode_index = (self.mode_index+1)%len(self.modes)
2690
- mode = self.modes[self.mode_index]
2691
- for key, val in mode.items():
2692
- if key == 'filter':
2693
- if 'filter' in self.modes[prev_mode_index]:
2694
- self.filter_query = self.filter_query.replace(self.modes[prev_mode_index]['filter'], '')
2695
- self.filter_query = f"{self.filter_query.strip()} {val.strip()}".strip()
2696
- prev_index = self.indexed_items[self.cursor_pos][0] if len(self.indexed_items)>0 else 0
2697
-
2698
- self.indexed_items = filter_items(self.items, self.indexed_items, self.filter_query)
2699
- if prev_index in [x[0] for x in self.indexed_items]: new_index = [x[0] for x in self.indexed_items].index(prev_index)
2700
- else: new_index = 0
2701
- self.cursor_pos = new_index
2702
- # Re-sort self.items after applying filter
2703
- sort_items(self.indexed_items, sort_method=self.columns_sort_method[self.sort_column], sort_column=self.sort_column, sort_reverse=self.sort_reverse[self.sort_column]) # Re-sort self.items based on new column
2893
+ if len(self.modes):
2894
+ prev_mode_index = self.mode_index
2895
+ self.mode_index = (self.mode_index+1)%len(self.modes)
2896
+ mode = self.modes[self.mode_index]
2897
+ for key, val in mode.items():
2898
+ if key == 'filter':
2899
+ if 'filter' in self.modes[prev_mode_index]:
2900
+ self.filter_query = self.filter_query.replace(self.modes[prev_mode_index]['filter'], '')
2901
+ self.filter_query = f"{self.filter_query.strip()} {val.strip()}".strip()
2902
+ prev_index = self.indexed_items[self.cursor_pos][0] if len(self.indexed_items)>0 else 0
2903
+
2904
+ self.indexed_items = filter_items(self.items, self.indexed_items, self.filter_query)
2905
+ if prev_index in [x[0] for x in self.indexed_items]: new_index = [x[0] for x in self.indexed_items].index(prev_index)
2906
+ else: new_index = 0
2907
+ self.cursor_pos = new_index
2908
+ # Re-sort self.items after applying filter
2909
+ sort_items(self.indexed_items, sort_method=self.columns_sort_method[self.sort_column], sort_column=self.sort_column, sort_reverse=self.sort_reverse[self.sort_column]) # Re-sort self.items based on new column
2704
2910
  elif self.check_key("mode_prev", key, self.keys_dict): # shift+tab key
2705
2911
  self.logger.info(f"key_function mode_prev")
2706
- # apply setting
2707
- prev_mode_index = self.mode_index
2708
- self.mode_index = (self.mode_index-1)%len(self.modes)
2709
- mode = self.modes[self.mode_index]
2710
- for key, val in mode.items():
2711
- if key == 'filter':
2712
- if 'filter' in self.modes[prev_mode_index]:
2713
- self.filter_query = self.filter_query.replace(self.modes[prev_mode_index]['filter'], '')
2714
- self.filter_query = f"{self.filter_query.strip()} {val.strip()}".strip()
2715
- prev_index = self.indexed_items[self.cursor_pos][0] if len(self.indexed_items)>0 else 0
2716
- self.indexed_items = filter_items(self.items, self.indexed_items, self.filter_query)
2717
- if prev_index in [x[0] for x in self.indexed_items]: new_index = [x[0] for x in self.indexed_items].index(prev_index)
2718
- else: new_index = 0
2719
- self.cursor_pos = new_index
2720
- # Re-sort self.items after applying filter
2721
- sort_items(self.indexed_items, sort_method=self.columns_sort_method[self.sort_column], sort_column=self.sort_column, sort_reverse=self.sort_reverse[self.sort_column]) # Re-sort self.items based on new column
2912
+ if len(self.modes):
2913
+ prev_mode_index = self.mode_index
2914
+ self.mode_index = (self.mode_index-1)%len(self.modes)
2915
+ mode = self.modes[self.mode_index]
2916
+ for key, val in mode.items():
2917
+ if key == 'filter':
2918
+ if 'filter' in self.modes[prev_mode_index]:
2919
+ self.filter_query = self.filter_query.replace(self.modes[prev_mode_index]['filter'], '')
2920
+ self.filter_query = f"{self.filter_query.strip()} {val.strip()}".strip()
2921
+ prev_index = self.indexed_items[self.cursor_pos][0] if len(self.indexed_items)>0 else 0
2922
+ self.indexed_items = filter_items(self.items, self.indexed_items, self.filter_query)
2923
+ if prev_index in [x[0] for x in self.indexed_items]: new_index = [x[0] for x in self.indexed_items].index(prev_index)
2924
+ else: new_index = 0
2925
+ self.cursor_pos = new_index
2926
+ # Re-sort self.items after applying filter
2927
+ sort_items(self.indexed_items, sort_method=self.columns_sort_method[self.sort_column], sort_column=self.sort_column, sort_reverse=self.sort_reverse[self.sort_column]) # Re-sort self.items based on new column
2928
+ elif self.check_key("file_next", key, self.keys_dict):
2929
+ if len(self.loaded_files):
2930
+ self.loaded_file_index = (self.loaded_file_index + 1) % len(self.loaded_files)
2931
+ self.loaded_file = self.loaded_files[self.loaded_file_index]
2932
+
2933
+ elif self.check_key("file_prev", key, self.keys_dict):
2934
+ if len(self.loaded_files):
2935
+ self.loaded_file_index = (self.loaded_file_index - 1) % len(self.loaded_files)
2936
+ self.loaded_file = self.loaded_files[self.loaded_file_index]
2937
+
2722
2938
  elif self.check_key("pipe_input", key, self.keys_dict):
2723
2939
  self.logger.info(f"key_function pipe_input")
2724
2940
  # usrtxt = "xargs -d '\n' -I{} "
@@ -2760,26 +2976,46 @@ class Picker:
2760
2976
  if not selected_indices:
2761
2977
  selected_indices = [self.indexed_items[self.cursor_pos][0]]
2762
2978
 
2763
- full_values = [format_row_full(self.items[i], self.hidden_columns) for i in selected_indices] # Use format_row_full for full data
2764
- full_values = [self.items[i][self.selected_column] for i in selected_indices]
2979
+ # full_values = [format_row_full(self.items[i], self.hidden_columns) for i in selected_indices] # Use format_row_full for full data
2980
+ if self.cell_cursor:
2981
+
2982
+ full_values = []
2983
+ for row in self.selected_cells_by_row.keys():
2984
+ selected_cell_row_str = ""
2985
+ for cell in self.selected_cells_by_row[row]:
2986
+ if " " in self.items[row][cell]:
2987
+ selected_cell_row_str += repr(self.items[row][cell])
2988
+ else:
2989
+ selected_cell_row_str += self.items[row][cell]
2990
+ selected_cell_row_str += "\t"
2991
+ full_values.append(selected_cell_row_str.strip())
2992
+
2993
+
2994
+ # full_values = ["\t".join([repr(self.items[key][cell]) for cell in self.selected_cells_by_row[key]]) for key in self.selected_cells_by_row.keys()]
2995
+ # full_values = ["\t".join([self.items[key][cell] for cell in self.selected_cells_by_row[key]]) for key in self.selected_cells_by_row.keys()]
2996
+ else:
2997
+ full_values = [self.items[i][self.selected_column] for i in selected_indices]
2765
2998
  if full_values:
2766
- command = usrtxt.split()
2999
+ # command = usrtxt.split()
3000
+ command = usrtxt
2767
3001
  # command = ['xargs', '-d' , '"\n"' '-I', '{}', 'mpv', '{}']
2768
3002
  # command = ['xargs', '-d' , '"\n"' '-I', '{}', 'mpv', '{}']
2769
3003
  # command = "xargs -d '\n' -I{} mpv {}"
2770
3004
 
2771
3005
  try:
2772
- process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
3006
+ process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True)
2773
3007
 
2774
3008
  if process.stdin != None:
2775
3009
  for value in full_values:
2776
- process.stdin.write((repr(value) + '\n').encode())
3010
+ process.stdin.write((value + '\n').encode())
3011
+ # process.stdin.write((value + '\n').encode())
2777
3012
 
2778
3013
  process.stdin.close()
2779
3014
 
2780
3015
  self.notification(self.stdscr, message=f"{len(full_values)} strings piped to {repr(usrtxt)}")
2781
3016
  except Exception as e:
2782
3017
  self.notification(self.stdscr, message=f"{e}")
3018
+ # self.notification(self.stdscr, message=f"Error: {str(e)}")
2783
3019
 
2784
3020
 
2785
3021
  elif self.check_key("open", key, self.keys_dict):
@@ -3011,7 +3247,8 @@ def parse_arguments() -> Tuple[argparse.Namespace, dict]:
3011
3247
  """ Parse command line arguments. """
3012
3248
  parser = argparse.ArgumentParser(description='Convert table to list of lists.')
3013
3249
  # parser.add_argument('filename', type=str, help='The file to process')
3014
- parser.add_argument('-i', dest='file', help='File containing the table to be converted.')
3250
+ # parser.add_argument('-i', dest='file', help='File containing the table to be converted.')
3251
+ parser.add_argument('-i', dest='file', nargs='+', help='File containing the table to be converted.')
3015
3252
  parser.add_argument('--load', '-l', dest='load', type=str, help='Load file from Picker dump.')
3016
3253
  parser.add_argument('--stdin', dest='stdin', action='store_true', help='Table passed on stdin')
3017
3254
  parser.add_argument('--stdin2', action='store_true', help='Table passed on stdin')
@@ -3033,7 +3270,8 @@ def parse_arguments() -> Tuple[argparse.Namespace, dict]:
3033
3270
  }
3034
3271
 
3035
3272
  if args.file:
3036
- input_arg = args.file
3273
+ input_arg = args.file[0]
3274
+
3037
3275
  elif args.stdin:
3038
3276
  input_arg = '--stdin'
3039
3277
  elif args.stdin2:
@@ -3071,9 +3309,14 @@ def parse_arguments() -> Tuple[argparse.Namespace, dict]:
3071
3309
  filetype = args.file_type
3072
3310
 
3073
3311
 
3074
- items, header = table_to_list(input_arg, args.delimiter, filetype)
3312
+ items, header, sheets = table_to_list(input_arg, args.delimiter, filetype)
3075
3313
  function_data["items"] = items
3076
3314
  if header: function_data["header"] = header
3315
+ function_data["sheets"] = sheets
3316
+ if args.file:
3317
+ function_data["loaded_file"] = args.file[0]
3318
+ function_data["loaded_files"] = args.file
3319
+
3077
3320
  return args, function_data
3078
3321
 
3079
3322
  def start_curses() -> curses.window:
@@ -3158,12 +3401,13 @@ def main() -> None:
3158
3401
  function_data["centre_in_terminal_vertical"] = True
3159
3402
  function_data["highlight_full_row"] = True
3160
3403
  function_data["pin_cursor"] = True
3161
- function_data["display_infobox"] = True
3162
- function_data["infobox_items"] = [["1"], ["2"], ["3"]]
3163
- function_data["infobox_title"] = "Title"
3404
+ # function_data["display_infobox"] = True
3405
+ # function_data["infobox_items"] = [["1"], ["2"], ["3"]]
3406
+ # function_data["infobox_title"] = "Title"
3164
3407
  # function_data["footer_string"] = "Title"
3165
3408
  function_data["highlights"] = highlights
3166
- function_data["show_footer"] = False
3409
+ # function_data["show_footer"] = False
3410
+ # function_data["paginate"] = True
3167
3411
  # function_data["debug"] = True
3168
3412
  # function_data["debug_level"] = 1
3169
3413
  stdscr = start_curses()
listpick/ui/footer.py CHANGED
@@ -50,32 +50,109 @@ class StandardFooter(Footer):
50
50
  def draw(self, h, w):
51
51
  state = self.get_state()
52
52
  # Fill background
53
- for i in range(self.height):
54
- self.stdscr.addstr(h-self.height+i, 0, ' '*(w-1), curses.color_pair(self.colours_start+20))
55
53
 
54
+
55
+ sheets_y=-1
56
56
  if state["footer_string"]:
57
- footer_string_width = min(w-1, len(state["footer_string"])+2)
58
57
 
59
- disp_string = f"{state["footer_string"][:footer_string_width]}"
60
- disp_string = f" {disp_string:>{footer_string_width-2}} "
61
- self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
62
- self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
63
58
 
59
+ footer_string_y = h-1
64
60
  picker_info_y = h-3
65
61
  sort_info_y = h-2
62
+
66
63
  self.height = 3
67
64
 
68
65
  else:
69
66
  picker_info_y = h-2
70
67
  sort_info_y = h-1
71
- ""
72
- select_mode = "C"
73
- if state["is_selecting"]: select_mode = "VS"
74
- elif state["is_deselecting"]: select_mode = "VDS"
75
- if state["pin_cursor"]: select_mode = f"{select_mode} "
76
- self.stdscr.addstr(h - 1, w-35, f"{select_mode:>33} ", curses.color_pair(self.colours_start+20))
68
+ footer_string_y = -1
77
69
  self.height = 2
78
70
 
71
+ if len(state["sheets"]) > 1:
72
+ self.height += 1
73
+ picker_info_y -= 1
74
+ sort_info_y -= 1
75
+ footer_string_y -= 1
76
+ sheets_y = h-1
77
+
78
+ if len(state["loaded_files"]) > 1 and state["loaded_file"] in state["loaded_files"]:
79
+ self.height += 1
80
+ picker_info_y -= 1
81
+ sort_info_y -= 1
82
+ footer_string_y -= 1
83
+ sheets_y -= 1
84
+
85
+ files_y = h-1
86
+
87
+
88
+ for i in range(self.height):
89
+ self.stdscr.addstr(h-self.height+i, 0, ' '*(w-1), curses.color_pair(self.colours_start+20))
90
+
91
+ if len(state["loaded_files"]) > 1 and state["loaded_file"] in state["loaded_files"]:
92
+
93
+ sep = "◢ "
94
+ files = [x.split("/")[-1] for x in state["loaded_files"]]
95
+ filename = state["loaded_file"].split("/")[:-1]
96
+
97
+ files_str = sep.join(files)
98
+ files_str = files_str[:w-2]
99
+
100
+ idx = state["loaded_file_index"]
101
+ current_file_x = sum((len(x) for x in files[:idx])) + idx*len(sep)
102
+ current_file_str = state["loaded_file"].split("/")[-1]
103
+ current_file_x_end = current_file_x + len(current_file_str) + 2
104
+ self.stdscr.addstr(files_y, 0, ' '*(w-1), curses.color_pair(self.colours_start+4))
105
+ if current_file_x_end < w:
106
+
107
+ self.stdscr.addstr(files_y, 0, f" {files_str}", curses.color_pair(self.colours_start+4))
108
+
109
+ self.stdscr.addstr(files_y, current_file_x, f" {current_file_str}{sep[0]}", curses.color_pair(self.colours_start+4) | curses.A_REVERSE)
110
+ else:
111
+ files_str = sep.join(files)
112
+ files_str = files_str[current_file_x_end-w:current_file_x_end][:w-2]
113
+ self.stdscr.addstr(files_y, 0, f" {files_str}", curses.color_pair(self.colours_start+4))
114
+
115
+ self.stdscr.addstr(files_y, w - (len(current_file_str)+3), f" {current_file_str}{sep[0]}", curses.color_pair(self.colours_start+4) | curses.A_REVERSE)
116
+
117
+ if len(state["sheets"]) > 1:
118
+
119
+ sep = "◢ "
120
+ sheets = [x.split("/")[-1] for x in state["sheets"]]
121
+ filename = state["sheet_name"].split("/")[:-1]
122
+
123
+ sheets_str = sep.join(sheets)
124
+ sheets_str = sheets_str[:w-2]
125
+
126
+ idx = state["sheet_index"]
127
+ current_sheet_x = sum((len(x) for x in sheets[:idx])) + idx*len(sep)
128
+ current_sheet_str = state["sheet_name"].split("/")[-1]
129
+ current_sheet_x_end = current_sheet_x + len(current_sheet_str) + 2
130
+ self.stdscr.addstr(sheets_y, 0, ' '*(w-1), curses.color_pair(self.colours_start+4))
131
+ if current_sheet_x_end < w:
132
+
133
+ self.stdscr.addstr(sheets_y, 0, f" {sheets_str}", curses.color_pair(self.colours_start+4))
134
+
135
+ self.stdscr.addstr(sheets_y, current_sheet_x, f" {current_sheet_str}{sep[0]}", curses.color_pair(self.colours_start+4) | curses.A_REVERSE)
136
+ else:
137
+ sheets_str = sep.join(sheets)
138
+ sheets_str = sheets_str[current_sheet_x_end-w:current_sheet_x_end][:w-2]
139
+ self.stdscr.addstr(sheets_y, 0, f" {sheets_str}", curses.color_pair(self.colours_start+4))
140
+
141
+ self.stdscr.addstr(sheets_y, w - (len(current_sheet_str)+3), f" {current_sheet_str}{sep[0]}", curses.color_pair(self.colours_start+4) | curses.A_REVERSE)
142
+
143
+
144
+
145
+
146
+ if state["footer_string"]:
147
+ footer_string_width = min(w-1, len(state["footer_string"])+2)
148
+
149
+ disp_string = f"{state["footer_string"][:footer_string_width]}"
150
+ disp_string = f" {disp_string:>{footer_string_width-2}} "
151
+ self.stdscr.addstr(footer_string_y, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
152
+ self.stdscr.addstr(footer_string_y, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
153
+
154
+
155
+
79
156
 
80
157
  if state["filter_query"]:
81
158
  self.stdscr.addstr(h - 2, 2, f" Filter: {state['filter_query']} "[:w-40], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
@@ -93,10 +170,14 @@ class StandardFooter(Footer):
93
170
  # Cursor & selection info
94
171
  selected_count = sum(state["selections"].values())
95
172
  if state["paginate"]:
96
- cursor_disp_str = f" [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])} Page {state['cursor_pos']//state['items_per_page']}/{len(state['indexed_items'])}"
173
+ # cursor_disp_str = f" [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])} | Page {state['cursor_pos']//state['items_per_page']}/{len(state['indexed_items'])//state['items_per_page']} | {select_mode}"
174
+ cursor_disp_str = f" [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])} | Page {state['cursor_pos']//state['items_per_page']}/{len(state['indexed_items'])//state['items_per_page']} | {select_mode}"
97
175
  else:
98
176
  cursor_disp_str = f" [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])} | {select_mode}"
99
- self.stdscr.addstr(picker_info_y, w-35, f"{cursor_disp_str:>33} ", curses.color_pair(self.colours_start+20))
177
+
178
+ # Maximum chars that should be displayed
179
+ max_chars = min(len(cursor_disp_str)+2, w)
180
+ self.stdscr.addstr(picker_info_y, w-max_chars, f"{cursor_disp_str:>{max_chars-2}} ", curses.color_pair(self.colours_start+20))
100
181
 
101
182
 
102
183
  # Sort info
@@ -105,7 +186,8 @@ class StandardFooter(Footer):
105
186
  sort_order_info = "Desc." if state["sort_reverse"] else "Asc."
106
187
  sort_order_info = "▼" if state["sort_reverse"][state['sort_column']] else "▲"
107
188
  sort_disp_str = f" Sort: ({sort_column_info}, {sort_method_info}, {sort_order_info}) "
108
- self.stdscr.addstr(sort_info_y, w-35, f"{sort_disp_str:>34}", curses.color_pair(self.colours_start+20))
189
+ max_chars = min(len(sort_disp_str)+2, w)
190
+ self.stdscr.addstr(sort_info_y, w-max_chars, f"{sort_disp_str:>{max_chars-1}}", curses.color_pair(self.colours_start+20))
109
191
 
110
192
  self.stdscr.refresh()
111
193
 
@@ -25,7 +25,10 @@ def generate_columns(funcs: list, files: list) -> list[list[str]]:
25
25
  for file in files:
26
26
  item = []
27
27
  for func in funcs:
28
- item.append(func(file))
28
+ try:
29
+ item.append(func(file))
30
+ except:
31
+ item.append("")
29
32
  items.append(item)
30
33
 
31
34
  return items
@@ -37,10 +37,27 @@ def strip_whitespace(item: Iterable) -> Iterable:
37
37
 
38
38
 
39
39
 
40
- def table_to_list(input_arg: str, delimiter:str='\t', file_type:Optional[str]=None) -> Tuple[list[list[str]], list[str]]:
40
+ def table_to_list(
41
+
42
+ input_arg: str,
43
+ delimiter:str='\t',
44
+ file_type:Optional[str]=None,
45
+ sheet_number:int = 0,
46
+
47
+ ) -> Tuple[list[list[str]], list[str], list[str]]:
41
48
  """
42
49
  Convert data string to list. The input_arg
43
50
  Currently accepts: csv, tsv, json, xlsx, ods
51
+
52
+
53
+ input_arg: filename
54
+
55
+
56
+ returns:
57
+ items: list[list[str]]
58
+ header: list[str]
59
+ sheets: list[str]
60
+
44
61
  """
45
62
  logger.info("function: table_to_list (table_to_list_of_lists.py)")
46
63
  table_data = []
@@ -76,10 +93,10 @@ def table_to_list(input_arg: str, delimiter:str='\t', file_type:Optional[str]=No
76
93
  table_data = csv_string_to_list(input_data)
77
94
  table_data = strip_whitespace(table_data)
78
95
  # table_data = parse_csv_like(input_data, ",")
79
- return table_data, []
96
+ return table_data, [], []
80
97
  except Exception as e:
81
98
  print(f"Error reading CSV/TSV input: {e}")
82
- return [], []
99
+ return [], [], []
83
100
 
84
101
  elif file_type == 'tsv':
85
102
  try:
@@ -99,10 +116,10 @@ def table_to_list(input_arg: str, delimiter:str='\t', file_type:Optional[str]=No
99
116
 
100
117
  table_data = parse_csv_like(input_data, delimiter)
101
118
  table_data = strip_whitespace(table_data)
102
- return table_data, []
119
+ return table_data, [], []
103
120
  except Exception as e:
104
121
  print(f"Error reading CSV/TSV input: {e}")
105
- return [], []
122
+ return [], [], []
106
123
 
107
124
  elif file_type == 'json':
108
125
  try:
@@ -115,55 +132,53 @@ def table_to_list(input_arg: str, delimiter:str='\t', file_type:Optional[str]=No
115
132
  input_data = read_file_content(input_arg)
116
133
 
117
134
  table_data = json.loads(input_data)
118
- return table_data, []
135
+ return table_data, [], []
119
136
  except json.JSONDecodeError as e:
120
137
  print(f"Error decoding JSON input: {e}")
121
- return [], []
138
+ return [], [], []
122
139
  except FileNotFoundError as e:
123
140
  print(f"File not found: {e}")
124
- return [], []
141
+ return [], [], []
125
142
 
126
143
  elif file_type == 'xlsx':
127
- from openpyxl import load_workbook
144
+ import pandas as pd
145
+ ef = pd.ExcelFile(input_arg)
146
+ sheets = ef.sheet_names
147
+ if sheet_number < len(sheets):
148
+ df = pd.read_excel(input_arg, sheet_name=sheet_number)
149
+ else:
150
+ df = pd.read_excel(input_arg)
151
+ table_data = df.values.tolist()
128
152
  try:
129
- if input_arg == '--stdin':
130
- input_data = sys.stdin.read()
131
- with open('temp.xlsx', 'wb') as f:
132
- f.write(input_data.encode())
133
- elif input_arg == '--stdin2':
134
- input_count = int(sys.stdin.readline())
135
- input_data = "\n".join([sys.stdin.readline() for i in range(input_count)])
136
- with open('temp.xlsx', 'wb') as f:
137
- f.write(input_data.encode())
138
- else:
139
- input_data = read_file_content(input_arg)
140
- with open('temp.xlsx', 'wb') as f:
141
- f.write(input_data.encode())
142
-
143
- wb = load_workbook(filename='temp.xlsx')
144
- sheet = wb.active
145
- for row in sheet.iter_rows(values_only=True):
146
- table_data.append(list(row))
147
- return table_data, []
148
- except Exception as e:
149
- print(f"Error loading Excel file: {e}")
150
- return [], []
153
+ header = list(df.columns)
154
+ except:
155
+ header = []
156
+ return table_data, header, sheets
151
157
 
152
158
  elif file_type == 'ods':
153
159
  try:
154
160
  import pandas as pd
155
- df = pd.read_excel(input_arg, engine='odf')
161
+ ef = pd.ExcelFile(input_arg)
162
+ sheets = ef.sheet_names
163
+ if sheet_number < len(sheets):
164
+ df = pd.read_excel(input_arg, engine='odf', sheet_name=sheet_number)
165
+ else:
166
+ df = pd.read_excel(input_arg, engine='odf')
156
167
  table_data = df.values.tolist()
157
- return table_data, []
168
+ try:
169
+ header = list(df.columns)
170
+ except:
171
+ header = []
172
+ return table_data, header, sheets
158
173
  except Exception as e:
159
174
  print(f"Error loading ODS file: {e}")
160
- return [], []
175
+ return [], [], []
161
176
  elif file_type == 'pkl':
162
177
  with open(os.path.expandvars(os.path.expanduser(input_arg)), 'rb') as f:
163
178
  loaded_data = pickle.load(f)
164
179
  items = loaded_data["items"] if "items" in loaded_data else []
165
180
  header = loaded_data["header"] if "header" in loaded_data else []
166
- return items, header
181
+ return items, header, []
167
182
 
168
183
  if input_arg == '--stdin':
169
184
  input_data = sys.stdin.read()
@@ -175,7 +190,7 @@ def table_to_list(input_arg: str, delimiter:str='\t', file_type:Optional[str]=No
175
190
 
176
191
  table_data = parse_csv_like(input_data, delimiter)
177
192
 
178
- return table_data, []
193
+ return table_data, [], []
179
194
 
180
195
  if __name__ == '__main__':
181
196
  parser = argparse.ArgumentParser(description='Convert table to list of lists.')
listpick/utils/utils.py CHANGED
@@ -311,7 +311,7 @@ def openFiles(files: list[str]) -> str:
311
311
  # return result.stderr.read().decode("utf-8").strip()
312
312
  return ""
313
313
 
314
- def file_picker() -> str:
314
+ def file_picker() -> list[str]:
315
315
  """ Run file picker (yazi by default) and return the path of the file picked. If no file is picked an empty string is returned. """
316
316
 
317
317
  logger.info("function: file_picker (utils.py)")
@@ -320,10 +320,11 @@ def file_picker() -> str:
320
320
 
321
321
  lines = tmpfile.readlines()
322
322
  if lines:
323
- filename = lines[0].decode("utf-8").strip()
324
- return filename
323
+ filenames = [line.decode("utf-8").strip() for line in lines]
324
+ # filename = lines[0].decode("utf-8").strip()
325
+ return filenames
325
326
  else:
326
- return ""
327
+ return []
327
328
 
328
329
 
329
330
  def dir_picker() -> str:
@@ -350,6 +351,8 @@ def pad_lists_to_same_length(list_of_lists: list[list[str]]) -> list[list[str]]:
350
351
  """ Ensure that all lists in a list of lists are of the same length. Pad any shorter sublists with empty strings. """
351
352
  if not list_of_lists or list_of_lists in [[], [[]]]:
352
353
  return []
354
+ if type(list_of_lists) == type([]) and len(list_of_lists) and type(list_of_lists[0]) == type(""):
355
+ list_of_lists = [[x] for x in list_of_lists]
353
356
 
354
357
  # Find the maximum length of the sublists
355
358
  lengths = [len(sublist) for sublist in list_of_lists]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listpick
3
- Version: 0.1.13.61
3
+ Version: 0.1.14.0
4
4
  Summary: Listpick is a powerful TUI data tool for creating TUI apps or viewing/comparing tabulated data.
5
5
  Home-page: https://github.com/grimandgreedy/listpick
6
6
  Author: Grim
@@ -26,6 +26,7 @@ Requires-Dist: pandas; extra == "full"
26
26
  Requires-Dist: pyperclip; extra == "full"
27
27
  Requires-Dist: toml; extra == "full"
28
28
  Requires-Dist: traitlets; extra == "full"
29
+ Requires-Dist: odfpy; extra == "full"
29
30
  Dynamic: author
30
31
  Dynamic: author-email
31
32
  Dynamic: classifier
@@ -80,7 +81,7 @@ close_curses(stdscr)
80
81
  Use the listpick binary to generate and display rows based on a list of commands:
81
82
 
82
83
  ```
83
- wget https://raw.githubusercontent.com/grimandgreedy/listpick/refs/heads/master/examples/list_files.toml
84
+ wget https://raw.githubusercontent.com/grimandgreedy/listpick/refs/heads/master/examples/data_generation/list_files.toml
84
85
  listpick -g list_files.py
85
86
  ```
86
87
 
@@ -100,9 +101,9 @@ The application allows you to:
100
101
  ## Examples
101
102
 
102
103
 
103
- ### Identify video duplicates (./examples/video_duplicates.toml):
104
+ ### Identify video duplicates (./examples/data_generation//video_duplicates.toml):
104
105
  ```python
105
- listpick -g ./examples/video_duplicates.toml
106
+ listpick -g ./examples/data_generation/video_duplicates.toml
106
107
  ```
107
108
  - From the list of commands in the toml file we generate the properties we will use to identify the duplicates.
108
109
 
@@ -142,7 +143,7 @@ listpick -i ~/dn.pkl -t pkl
142
143
 
143
144
  2. **Generate data based on an toml file with relevant commands to generate the rows.**
144
145
  ```python
145
- listpick -g ./examples/video_duplicates.toml
146
+ listpick -g ./examples/data_generation/video_duplicates.toml
146
147
  ```
147
148
 
148
149
  - See ./examples/
@@ -1,9 +1,9 @@
1
1
  listpick/__init__.py,sha256=ExXc97-bibodH--wlwpQivl0zCNR5D1hvpvrf7OBofU,154
2
2
  listpick/__main__.py,sha256=wkCjDdqw093W27yWwnlC3nG_sMRKaIad7hHHWy0RBgY,193
3
- listpick/listpick_app.py,sha256=xCtAMJTeWwos2XTCN7PwZgEgo3lAKmsp74M0cNgyvUI,165274
3
+ listpick/listpick_app.py,sha256=3nGXMVky_jftqWbzSLTkW6GAm0_lCKaJGe_WBTAo5xU,176388
4
4
  listpick/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  listpick/ui/build_help.py,sha256=_rVKKrX3HfFJtw-pyeNb2lQWbml4-AAw8sZIUYGn97Y,8731
6
- listpick/ui/footer.py,sha256=s1L68MNmhWwbWRy0mn0ChmnE_dMQBAzNlTv917pyHE0,10673
6
+ listpick/ui/footer.py,sha256=85sVZ6zvzZXSc0TwgvCHGuzGElp6KwUVzwzF5VQwLyY,14219
7
7
  listpick/ui/help_screen.py,sha256=zbfGIgb-IXtATpl4_Sx7nPbsnRXZ7eiMYlCKGS9EFmw,5608
8
8
  listpick/ui/input_field.py,sha256=eyoWHoApdZybjfXcp7Eth7xwb-C-856ZVnq5j_Q3Ojs,30412
9
9
  listpick/ui/keys.py,sha256=TzaadgBP_rC7jbp--RFJZDOkHd0EB4K1wToDTiVs6CI,13029
@@ -14,18 +14,18 @@ listpick/utils/clipboard_operations.py,sha256=ORdNm2kgGbfs51xJSvgJPERgoSmBgT11ax
14
14
  listpick/utils/config.py,sha256=MEnAZg2Rhfl38XofEIN0uoVAOY7I0ftc79Evk3fOiVw,1654
15
15
  listpick/utils/dump.py,sha256=60YVIMNtBoYvWhmzfTJOsNGcetOvcCB3_T7yv-bYTPQ,3838
16
16
  listpick/utils/filtering.py,sha256=uS9sW0inmFvq0cIrwRC1BfuP8kjAD5IWWtls4jGB-70,1199
17
- listpick/utils/generate_data.py,sha256=ByfEgPywAWp-Nzso5QgFr_Obf-TshZn8Evvb9EwQY3E,3000
17
+ listpick/utils/generate_data.py,sha256=7sv6JRhk0-Gcj4kOlkzx4qPNBJZ-GFWg9vM77GktzpI,3073
18
18
  listpick/utils/options_selectors.py,sha256=Vbv4jRkUsSPs7g-EQAv9Q0nhYy6Z4sFsJqMjUIe1oeQ,2814
19
19
  listpick/utils/paste_operations.py,sha256=7wDXLPlxUgA3CA99gwsm47juWGO2YQ9EJghW06yo9vI,1242
20
20
  listpick/utils/picker_log.py,sha256=SW6GmjxpI7YrSf72fSr4O8Ux0fY_OzaSXUgTFdz6Xo4,805
21
21
  listpick/utils/search_and_filter_utils.py,sha256=XxGfkyDVXO9OAKcftPat8IReMTFIuTH-jorxI4o84tg,3239
22
22
  listpick/utils/searching.py,sha256=Xk5UIqamNHL2L90z3ACB_Giqdpi9iRKoAJ6pKaqaD7Q,3093
23
23
  listpick/utils/sorting.py,sha256=WZZiVlVA3Zkcpwji3U5SNFlQ14zVEk3cZJtQirBkecQ,5329
24
- listpick/utils/table_to_list_of_lists.py,sha256=T-i-nV1p6g8UagdgUPKrhIGpKY_YXZDxf4xZzcPepNA,7635
25
- listpick/utils/utils.py,sha256=m4uzL2ZPVpFuboIola0_ZIfLzlbGqirdz0_oo61tIlU,13636
26
- listpick-0.1.13.61.dist-info/licenses/LICENSE.txt,sha256=2mP-MRHJptADDNE9VInMNg1tE-C6Qv93Z4CCQKrpg9w,1061
27
- listpick-0.1.13.61.dist-info/METADATA,sha256=VPjp9FR1XNwseaF4k0z4jyEHp3J7qXqXe3vFVlMXZrQ,7988
28
- listpick-0.1.13.61.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
- listpick-0.1.13.61.dist-info/entry_points.txt,sha256=-QCf_BKIkUz35Y9nkYpjZWs2Qg0KfRna2PAs5DnF6BE,43
30
- listpick-0.1.13.61.dist-info/top_level.txt,sha256=5mtsGEz86rz3qQDe0D463gGjAfSp6A3EWg4J4AGYr-Q,9
31
- listpick-0.1.13.61.dist-info/RECORD,,
24
+ listpick/utils/table_to_list_of_lists.py,sha256=Ox_4OWtZcFp5XWcItlMqE6_Q27YiJz7M9w23Y9vfzYQ,7604
25
+ listpick/utils/utils.py,sha256=McOl9uT3jh7l4TIWeSd8ZGjK_e7r0YZF0Gl20yI6fl0,13873
26
+ listpick-0.1.14.0.dist-info/licenses/LICENSE.txt,sha256=2mP-MRHJptADDNE9VInMNg1tE-C6Qv93Z4CCQKrpg9w,1061
27
+ listpick-0.1.14.0.dist-info/METADATA,sha256=NDlbmPlgFWGV9V3UmkIyhSYXY71kjhp3GKvUkxzOPRE,8090
28
+ listpick-0.1.14.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
+ listpick-0.1.14.0.dist-info/entry_points.txt,sha256=-QCf_BKIkUz35Y9nkYpjZWs2Qg0KfRna2PAs5DnF6BE,43
30
+ listpick-0.1.14.0.dist-info/top_level.txt,sha256=5mtsGEz86rz3qQDe0D463gGjAfSp6A3EWg4J4AGYr-Q,9
31
+ listpick-0.1.14.0.dist-info/RECORD,,