listpick 0.1.14.3__tar.gz → 0.1.14.5__tar.gz

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 (58) hide show
  1. {listpick-0.1.14.3 → listpick-0.1.14.5}/CHANGELOG.md +1 -0
  2. {listpick-0.1.14.3/src/listpick.egg-info → listpick-0.1.14.5}/PKG-INFO +1 -1
  3. {listpick-0.1.14.3 → listpick-0.1.14.5}/setup.py +1 -1
  4. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/listpick_app.py +117 -28
  5. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/input_field.py +47 -6
  6. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/keys.py +4 -4
  7. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/table_to_list_of_lists.py +111 -36
  8. {listpick-0.1.14.3 → listpick-0.1.14.5/src/listpick.egg-info}/PKG-INFO +1 -1
  9. {listpick-0.1.14.3 → listpick-0.1.14.5}/.gitignore +0 -0
  10. {listpick-0.1.14.3 → listpick-0.1.14.5}/LICENSE.txt +0 -0
  11. {listpick-0.1.14.3 → listpick-0.1.14.5}/README.md +0 -0
  12. {listpick-0.1.14.3 → listpick-0.1.14.5}/TODO.md +0 -0
  13. {listpick-0.1.14.3 → listpick-0.1.14.5}/assets/aria2tui_screenshot.png +0 -0
  14. {listpick-0.1.14.3 → listpick-0.1.14.5}/assets/file_compare.png +0 -0
  15. {listpick-0.1.14.3 → listpick-0.1.14.5}/assets/lpfman.png +0 -0
  16. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/data_generation/list_files.toml +0 -0
  17. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/data_generation/list_files_empty.toml +0 -0
  18. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/data_generation/video_duplicates.toml +0 -0
  19. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/data_generation/video_mediainfo.toml +0 -0
  20. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/input_files/polynomials.tsv +0 -0
  21. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/input_guides/gnuplot_graph.md +0 -0
  22. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/auxiallary_files/2024-25_Premier_League.pkl +0 -0
  23. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/footer_string_example.py +0 -0
  24. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/picker_example.py +0 -0
  25. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/template.py +0 -0
  26. {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/wikipedia_table.py +0 -0
  27. {listpick-0.1.14.3 → listpick-0.1.14.5}/listpick.py +0 -0
  28. {listpick-0.1.14.3 → listpick-0.1.14.5}/requirements.txt +0 -0
  29. {listpick-0.1.14.3 → listpick-0.1.14.5}/setup.cfg +0 -0
  30. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/__init__.py +0 -0
  31. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/__main__.py +0 -0
  32. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/__init__.py +0 -0
  33. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/build_help.py +0 -0
  34. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/footer.py +0 -0
  35. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/git-bugreport-2025-08-16-1438.txt +0 -0
  36. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/help_screen.py +0 -0
  37. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/pane_stuff.py +0 -0
  38. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/picker_colours.py +0 -0
  39. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/__init__.py +0 -0
  40. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/clipboard_operations.py +0 -0
  41. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/config.py +0 -0
  42. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/dump.py +0 -0
  43. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/filtering.py +0 -0
  44. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/generate_data.py +0 -0
  45. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/options_selectors.py +0 -0
  46. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/paste_operations.py +0 -0
  47. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/picker_log.py +0 -0
  48. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/search_and_filter_utils.py +0 -0
  49. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/searching.py +0 -0
  50. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/sorting.py +0 -0
  51. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/utils.py +0 -0
  52. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick.egg-info/SOURCES.txt +0 -0
  53. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick.egg-info/dependency_links.txt +0 -0
  54. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick.egg-info/entry_points.txt +0 -0
  55. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick.egg-info/requires.txt +0 -0
  56. {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick.egg-info/top_level.txt +0 -0
  57. {listpick-0.1.14.3 → listpick-0.1.14.5}/tests/kitty_control.sh +0 -0
  58. {listpick-0.1.14.3 → listpick-0.1.14.5}/tests/sorting_dates.csv +0 -0
@@ -7,6 +7,7 @@ Note that the changes between 0.1.11.0 and 1.1.12.0 are listed under 0.1.11
7
7
  - Added __sizeof__() function for the Picker class.
8
8
  - Fixed rows resizing twice when opening/switching between some files.
9
9
  - Added to settings: goto row, goto column
10
+ - NaN replaced with empty string when loading empty cells from xlsx or ods files.
10
11
 
11
12
  ## [0.1.14] 2025-08-20
12
13
  - Fixed bug when cells are centred vertically.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listpick
3
- Version: 0.1.14.3
3
+ Version: 0.1.14.5
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
@@ -16,7 +16,7 @@ with open("README.md", "r", encoding = "utf-8") as fh:
16
16
 
17
17
  setuptools.setup(
18
18
  name = "listpick",
19
- version = "0.1.14.3",
19
+ version = "0.1.14.5",
20
20
  author = "Grim",
21
21
  author_email = "grimandgreedy@protonmail.com",
22
22
  description = "Listpick is a powerful TUI data tool for creating TUI apps or viewing/comparing tabulated data.",
@@ -20,6 +20,8 @@ import json
20
20
  import threading
21
21
  import string
22
22
  import logging
23
+ import tty
24
+ import select
23
25
 
24
26
  from listpick.ui.picker_colours import get_colours, get_help_colours, get_notification_colours, get_theme_count, get_fallback_colours
25
27
  from listpick.utils.options_selectors import default_option_input, output_file_option_selector, default_option_selector
@@ -349,6 +351,65 @@ class Picker:
349
351
  if not attr_name.startswith('__') and not callable(getattr(self, attr_name)):
350
352
  size += sys.getsizeof(getattr(self, attr_name))
351
353
  return size
354
+
355
+ def set_config(self, path: str ="~/.config/listpick/config.toml"):
356
+ """ Set config from toml file. """
357
+ config = self.get_config(path)
358
+ self.logger.info(f"function: set_config()")
359
+ if "general" in config:
360
+ for key, val in config["general"].items():
361
+ self.logger.info(f"set_config: key={key}, val={val}.")
362
+ try:
363
+ setattr(self, key, val)
364
+ except Exception as e:
365
+ self.logger.error(f"set_config: key={key}, val={val}. {e}")
366
+
367
+
368
+ def get_config(self, path: str ="~/.config/listpick/config.toml") -> dict:
369
+ """ Get config from file. """
370
+ self.logger.info(f"function: get_config()")
371
+ import toml
372
+ if os.path.exists(os.path.expanduser(path)):
373
+ with open(os.path.expanduser(path), "r") as f:
374
+ config = toml.load(f)
375
+ return config
376
+ # full_config = self.get_default_config()
377
+ # default_path = "~/.config/listpick/config.toml"
378
+ #
379
+ # CONFIGPATH = default_path
380
+
381
+
382
+
383
+ # if "general" in config:
384
+ # for key in config["general"]:
385
+ # full_config["general"][key] = config["general"][key]
386
+ # if "appearance" in config:
387
+ # for key in config["appearance"]:
388
+ # full_config["appearance"][key] = config["appearance"][key]
389
+ #
390
+ # return full_config
391
+
392
+ def get_default_config(self) -> dict:
393
+ default_config = {
394
+ "general" : {
395
+ "url": "http://localhost",
396
+ "port": "6800",
397
+ "token": "",
398
+ "startupcmds": ["aria2c"],
399
+ "restartcmds": ["pkill aria2c && sleep 1 && aria2c"],
400
+ "ariaconfigpath": "~/.config/aria2/aria2.conf",
401
+ "paginate": False,
402
+ "refresh_timer": 2,
403
+ "global_stats_timer": 1,
404
+ "terminal_file_manager": "yazi",
405
+ "gui_file_manager": "kitty yazi",
406
+ "launch_command": "xdg-open",
407
+ },
408
+ "appearance":{
409
+ "theme": 0
410
+ }
411
+ }
412
+ return default_config
352
413
 
353
414
  def calculate_section_sizes(self):
354
415
  """
@@ -573,10 +634,6 @@ class Picker:
573
634
  # If a sort is passed
574
635
  if len(self.indexed_items) > 0:
575
636
  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
576
- # if len(self.items[0]) == 1:
577
- # self.number_columns = False
578
-
579
-
580
637
 
581
638
  h, w = self.stdscr.getmaxyx()
582
639
 
@@ -677,7 +734,14 @@ class Picker:
677
734
  # Update current column index
678
735
  self.selected_column = new_index
679
736
 
680
- def test_screen_size(self):
737
+ def test_screen_size(self) -> bool:
738
+ """
739
+ Determine if the terminal is large enough to display the picker.
740
+ If the terminal is too small then display a message saying so.
741
+
742
+ Returns: True if terminal is large enough to display the Picker.
743
+
744
+ """
681
745
  self.logger.debug("function: test_screen_size()")
682
746
  h, w = self.stdscr.getmaxyx()
683
747
  ## Terminal too small to display Picker
@@ -689,9 +753,10 @@ class Picker:
689
753
  return False
690
754
  return True
691
755
 
692
- def splash_screen(self, message=""):
693
- self.logger.info(f"function: splash_screen({message})")
756
+ def splash_screen(self, message="") -> None:
694
757
  """ Display a splash screen with a message. Useful when loading a large data set. """
758
+
759
+ self.logger.info(f"function: splash_screen({message})")
695
760
  h, w =self.stdscr.getmaxyx()
696
761
  self.stdscr.bkgd(' ', curses.color_pair(2))
697
762
  try:
@@ -854,14 +919,6 @@ class Picker:
854
919
  except:
855
920
  pass
856
921
 
857
- # Draw:
858
- # 1. standard row
859
- # 2. highlights l0
860
- # 3. selected
861
- # 4. above-selected highlights l1
862
- # 5. cursor
863
- # 6. top-level highlights l2
864
- ## Display rows and highlights
865
922
 
866
923
  def sort_highlights(highlights):
867
924
  """
@@ -921,6 +978,15 @@ class Picker:
921
978
  except:
922
979
  pass
923
980
 
981
+ # Draw:
982
+ # 1. standard row
983
+ # 2. highlights l0
984
+ # 3. selected
985
+ # 4. above-selected highlights l1
986
+ # 5. cursor
987
+ # 6. top-level highlights l2
988
+ ## Display rows and highlights
989
+
924
990
  l0_highlights, l1_highlights, l2_highlights = sort_highlights(self.highlights)
925
991
 
926
992
 
@@ -2174,10 +2240,14 @@ class Picker:
2174
2240
  function_data = self.get_function_data()
2175
2241
  return [], "", function_data
2176
2242
 
2177
- # Main loop
2243
+ # Open tty to accept input
2244
+ tty_fd = open_tty()
2178
2245
 
2246
+ # Main loop
2179
2247
  while True:
2180
- key = self.stdscr.getch()
2248
+ # key = self.stdscr.getch()
2249
+
2250
+ key = get_char(tty_fd, timeout=0.2)
2181
2251
  if key != -1:
2182
2252
  self.logger.info(f"key={key}")
2183
2253
  h, w = self.stdscr.getmaxyx()
@@ -2416,7 +2486,7 @@ class Picker:
2416
2486
  formula_auto_complete=False,
2417
2487
  function_auto_complete=False,
2418
2488
  word_auto_complete=True,
2419
- auto_complete_words=["ft", "ct", "cv"]
2489
+ auto_complete_words=["ft", "ct", "cv"],
2420
2490
  )
2421
2491
  if return_val:
2422
2492
  self.user_settings = usrtxt
@@ -3500,6 +3570,24 @@ def unrestrict_curses(stdscr: curses.window) -> None:
3500
3570
  curses.raw() # Disable control keys (ctrl-c, ctrl-s, ctrl-q, etc.)
3501
3571
  curses.curs_set(False)
3502
3572
 
3573
+
3574
+ def open_tty():
3575
+ """ Return a file descriptor for the tty that we are opening"""
3576
+ tty_fd = os.open('/dev/tty', os.O_RDONLY)
3577
+ tty.setraw(tty_fd)
3578
+ return tty_fd
3579
+
3580
+ def get_char(tty_fd, timeout: float = 0.2) -> int:
3581
+ """ Get character from a tty_fd with a timeout. """
3582
+ rlist, _, _ = select.select([tty_fd], [], [], timeout)
3583
+ if rlist:
3584
+ # key = ord(tty_fd.read(1))
3585
+ key = ord(os.read(tty_fd, 1))
3586
+ # os.system(f"notify-send { key }")
3587
+ else:
3588
+ key = -1
3589
+ return key
3590
+
3503
3591
  def main() -> None:
3504
3592
  """ Main function when listpick is executed. Deals with command line arguments and starts a Picker. """
3505
3593
  args, function_data = parse_arguments()
@@ -3535,16 +3623,16 @@ def main() -> None:
3535
3623
  "color": 8,
3536
3624
  }
3537
3625
  ]
3538
- function_data["cell_cursor"] = True
3539
- function_data["display_modes"] = True
3540
- function_data["centre_in_cols"] = True
3541
- function_data["show_row_header"] = True
3542
- function_data["keys_dict"] = picker_keys
3543
- function_data["id_column"] = -1
3544
- function_data["track_entries_upon_refresh"] = True
3545
- function_data["centre_in_terminal_vertical"] = True
3546
- function_data["highlight_full_row"] = True
3547
- function_data["pin_cursor"] = True
3626
+ # function_data["cell_cursor"] = True
3627
+ # function_data["display_modes"] = True
3628
+ # function_data["centre_in_cols"] = True
3629
+ # function_data["show_row_header"] = True
3630
+ # function_data["keys_dict"] = picker_keys
3631
+ # function_data["id_column"] = -1
3632
+ # function_data["track_entries_upon_refresh"] = True
3633
+ # function_data["centre_in_terminal_vertical"] = True
3634
+ # function_data["highlight_full_row"] = True
3635
+ # function_data["pin_cursor"] = True
3548
3636
  # function_data["display_infobox"] = True
3549
3637
  # function_data["infobox_items"] = [["1"], ["2"], ["3"]]
3550
3638
  # function_data["infobox_title"] = "Title"
@@ -3567,6 +3655,7 @@ def main() -> None:
3567
3655
 
3568
3656
  # app = Picker(stdscr, **function_data)
3569
3657
  app = Picker(stdscr)
3658
+ app.set_config("~/.config/listpick/config.toml")
3570
3659
  app.set_function_data(function_data)
3571
3660
  app.splash_screen("Listpick is loading your data...")
3572
3661
  app.load_input_history("~/.config/listpick/cmdhist.json")
@@ -16,7 +16,26 @@ from datetime import datetime
16
16
  import logging
17
17
 
18
18
  logger = logging.getLogger('picker_log')
19
-
19
+ import select
20
+ import tty
21
+
22
+ def open_tty():
23
+ """ Return a file descriptor for the tty that we are opening"""
24
+ tty_fd = os.open('/dev/tty', os.O_RDONLY)
25
+ tty.setraw(tty_fd)
26
+ return tty_fd
27
+
28
+ def get_char(tty_fd, timeout: float = 0.2) -> int:
29
+ """ Get character from a tty_fd with a timeout. """
30
+ rlist, _, _ = select.select([tty_fd], [], [], timeout)
31
+ if rlist:
32
+ # key = ord(tty_fd.read(1))
33
+ key = ord(os.read(tty_fd, 1))
34
+ # os.system(f"notify-send { key }")
35
+ else:
36
+ key = -1
37
+ return key
38
+
20
39
  def input_field(
21
40
  stdscr: curses.window,
22
41
  usrtxt:str="",
@@ -106,6 +125,10 @@ def input_field(
106
125
  offscreen_x, offscreen_y = False, False
107
126
  orig_x, orig_y = x, y
108
127
 
128
+ # tty_fd = open('/dev/tty')
129
+ # tty_fd = os.open('/dev/tty', os.O_RDONLY)
130
+ # tty.setraw(tty_fd)
131
+ tty_fd = open_tty()
109
132
  # Input field loop
110
133
  while True:
111
134
 
@@ -131,7 +154,7 @@ def input_field(
131
154
 
132
155
  # Clear background to end of the input field
133
156
  stdscr.addstr(field_y, x(), " "*(max_field_length), curses.color_pair(colours_start+colour_pair_bg))
134
- stdscr.refresh()
157
+ # stdscr.refresh()
135
158
 
136
159
  if literal:
137
160
  field_string_length = len(repr(usrtxt)) + len(field_prefix)
@@ -206,12 +229,30 @@ def input_field(
206
229
  pass
207
230
 
208
231
 
209
- key = stdscr.getch()
232
+ stdscr.refresh()
233
+ # timeout = 0.05
234
+ # rlist, _, _ = select.select([tty_fd], [], [], timeout)
235
+ # if rlist:
236
+ # # key = ord(tty_fd.read(1))
237
+ # key = ord(os.read(tty_fd, 1))
238
+ # os.system(f"notify-send { key }")
239
+ # else:
240
+ # key = -1
241
+ key = get_char(tty_fd, timeout=0.5)
242
+ # key = ord(tty.read(1))
243
+ # key = stdscr.getch()
210
244
 
211
245
  if key in [27, 7]: # ESC/ALT key or Ctrl+g
212
246
  # For Alt-key combinations: set nodelay and get the second key
213
- stdscr.nodelay(True)
214
- key2 = stdscr.getch()
247
+ # stdscr.nodelay(True)
248
+ # key2 = stdscr.getch()
249
+ # rlist, _, _ = select.select([tty_fd], [], [], timeout)
250
+ # if rlist:
251
+ # key2 = ord(os.read(tty_fd, 1))
252
+ # os.system(f"notify-send 'metakey { key }'")
253
+ # else:
254
+ # key2 = -1
255
+ key2 = get_char(tty_fd, timeout=0.05)
215
256
 
216
257
  if key2 == -1: # ESCAPE key (no key-combination)
217
258
  stdscr.nodelay(False)
@@ -311,7 +352,7 @@ def input_field(
311
352
  curses.endwin()
312
353
  exit()
313
354
 
314
- elif key == 10: # Enter/return key
355
+ elif key in [10, 13]: # Enter/return key
315
356
  # Return
316
357
  return usrtxt, True
317
358
 
@@ -32,7 +32,7 @@ picker_keys = {
32
32
  "select_none": [ord('M'), 18], # Ctrl-r
33
33
  "visual_selection_toggle": [ord('v')],
34
34
  "visual_deselection_toggle": [ord('V')],
35
- "enter": [ord('\n'), curses.KEY_ENTER],
35
+ "enter": [ord('\n'), curses.KEY_ENTER, 13],
36
36
  "redraw_screen": [12], # Ctrl-l
37
37
  "cycle_sort_method": [ord('s')],
38
38
  "cycle_sort_method_reverse": [ord('S')],
@@ -152,7 +152,7 @@ menu_keys = {
152
152
  "cursor_top": [ord('g'), curses.KEY_HOME],
153
153
  "five_up": [ord('K')],
154
154
  "five_down": [ord('J')],
155
- "enter": [ord('\n'), curses.KEY_ENTER, ord('l')],
155
+ "enter": [ord('\n'), curses.KEY_ENTER, ord('l'), 13],
156
156
  "redraw_screen": [12], # Ctrl-l
157
157
  "filter_input": [ord('f')],
158
158
  "search_input": [ord('/')],
@@ -183,7 +183,7 @@ options_keys = {
183
183
  "select_none": [ord('M'), 18], # Ctrl-r
184
184
  "visual_selection_toggle": [ord('v')],
185
185
  "visual_deselection_toggle": [ord('V')],
186
- "enter": [ord('\n'), curses.KEY_ENTER, ord('l')],
186
+ "enter": [ord('\n'), curses.KEY_ENTER, ord('l'), 13],
187
187
  "redraw_screen": [12], # Ctrl-l
188
188
  "cycle_sort_method": [ord('s')],
189
189
  "cycle_sort_method_reverse": [ord('S')],
@@ -213,7 +213,7 @@ edit_menu_keys = {
213
213
  "cursor_top": [ord('g'), curses.KEY_HOME],
214
214
  "five_up": [ord('K')],
215
215
  "five_down": [ord('J')],
216
- "enter": [ord('\n'), curses.KEY_ENTER],
216
+ "enter": [ord('\n'), curses.KEY_ENTER, 13],
217
217
  "redraw_screen": [12], # Ctrl-l
218
218
  "cycle_sort_method": [ord('s')],
219
219
  "cycle_sort_method_reverse": [ord('S')],
@@ -36,6 +36,102 @@ def strip_whitespace(item: Iterable) -> Iterable:
36
36
  return item
37
37
 
38
38
 
39
+ def xlsx_to_list(file_name: str, sheet_number:int = 0, extract_formulae: bool = False):
40
+ import pandas as pd
41
+ from openpyxl import load_workbook
42
+ # wb = load_workbook(filename=input_arg, read_only=True)
43
+ # values or formulae
44
+ if not os.path.exists(file_name):
45
+ return [], [], []
46
+ wb = load_workbook(filename=file_name, read_only=True, data_only=not extract_formulae)
47
+
48
+ if not isinstance(sheet_number, int): sheet_number = 0
49
+ sheet_number = max(0, min(sheet_number, len(wb.sheetnames)-1))
50
+ ws = wb.worksheets[sheet_number]
51
+
52
+ # Read data and formulas from the sheet
53
+ table_data = []
54
+ # table_data = [[cell for cell in row] for row in ws.iter_rows(min_row=1, values_only=False)]
55
+ # table_data = [[cell.value for cell in row] for row in ws.iter_rows(min_row=1, values_only=False)]
56
+ table_data = [[cell if cell != None else "" for cell in row] for row in ws.iter_rows(min_row=1, values_only=True)]
57
+ header = []
58
+ # header = [cell for cell in list(ws.iter_rows(values_only=True))[0]] # Assuming the first row is the header
59
+ header = table_data[0]
60
+ table_data = table_data[1:]
61
+ #
62
+ # for row in ws.iter_rows(min_row=2, values_only=True): # Skip the header row
63
+ # row_data = []
64
+ # for cell in row:
65
+ # if isinstance(cell, str) and '=' in cell: # Check if it's a formula
66
+ # row_data.append(cell)
67
+ # else:
68
+ # row_data.append(str(cell))
69
+ # table_data.append(row_data)
70
+
71
+ return table_data, header, wb.sheetnames
72
+
73
+ def ods_to_list(filename: str, sheet_number: int = 0, extract_formulas: bool = False, first_row_is_header: bool = True):
74
+ from odf.opendocument import load
75
+ from odf import table, text
76
+
77
+ from odf.namespaces import TABLENS
78
+ # Load the ODS file
79
+ doc = load(filename)
80
+
81
+ sheets = doc.spreadsheet.getElementsByType(table.Table)
82
+ sheet_names = [s.attributes.get((TABLENS, 'name')) for s in sheets]
83
+
84
+
85
+ # Get the sheet by index
86
+ sheet = doc.spreadsheet.getElementsByType(table.Table)[sheet_number]
87
+
88
+ data = []
89
+ for row in sheet.getElementsByType(table.TableRow):
90
+ row_data = []
91
+ for cell in row.getElementsByType(table.TableCell):
92
+ if extract_formulas:
93
+ formula = cell.attributes.get((TABLENS, 'formula'))
94
+ if formula is not None:
95
+ row_data.append(formula)
96
+ continue # Skip extracting value if formula found
97
+
98
+ # Extract value (as text) from <text:p> elements
99
+ cell_text = ""
100
+ for p in cell.getElementsByType(text.P):
101
+ cell_text += str(p.firstChild) if p.firstChild is not None else ""
102
+ row_data.append(cell_text)
103
+ data.append(row_data)
104
+ if first_row_is_header and len(data) > 0:
105
+ header = data[0]
106
+ data = data[1:]
107
+ else:
108
+ header = []
109
+
110
+ return data, header, sheet_names
111
+
112
+ def ods_to_list_old(file_name: str, sheet_number:int = 0, extract_formulae: bool = False):
113
+ try:
114
+ import pandas as pd
115
+ ef = pd.ExcelFile(file_name)
116
+ sheets = ef.sheet_names
117
+ sheet_number = max(0, min(sheet_number, len(sheets)-1))
118
+ df = pd.read_excel(file_name, engine='odf', sheet_name=sheet_number)
119
+ # if sheet_number < len(sheets):
120
+ # df = pd.read_excel(input_arg, engine='odf', sheet_name=sheet_number)
121
+ # else:
122
+ # df = pd.read_excel(input_arg, engine='odf')
123
+ table_data = df.values.tolist()
124
+ table_data = [[x if not pd.isna(x) else "" for x in row] for row in table_data]
125
+
126
+ try:
127
+ header = list(df.columns)
128
+ except:
129
+ header = []
130
+ return table_data, header, sheets
131
+ except Exception as e:
132
+ print(f"Error loading ODS file: {e}")
133
+ return [], [], []
134
+
39
135
 
40
136
  def table_to_list(
41
137
 
@@ -80,7 +176,17 @@ def table_to_list(
80
176
  reader = csv.reader(f, skipinitialspace=True)
81
177
  return [row for row in reader]
82
178
 
83
- if file_type == 'csv' or delimiter in [',']:
179
+ if input_arg == '--stdin':
180
+ os.system(f"notify-send stdin")
181
+ input_data = sys.stdin.read()
182
+ elif input_arg == '--stdin2':
183
+ os.system(f"notify-send stdin2")
184
+ input_count = int(sys.stdin.readline())
185
+ input_data = "\n".join([sys.stdin.readline() for i in range(input_count)])
186
+ sys.stdin.flush()
187
+ # sys.stdin.close()
188
+ # sys.stdin = open('/dev/tty', 'r')
189
+ elif file_type == 'csv' or delimiter in [',']:
84
190
  try:
85
191
  if input_arg == '--stdin':
86
192
  input_data = sys.stdin.read()
@@ -141,38 +247,12 @@ def table_to_list(
141
247
  return [], [], []
142
248
 
143
249
  elif file_type == 'xlsx':
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()
152
- try:
153
- header = list(df.columns)
154
- except:
155
- header = []
156
- return table_data, header, sheets
250
+ extract_formulae = False
251
+ return xlsx_to_list(input_arg, sheet_number, extract_formulae)
157
252
 
158
253
  elif file_type == 'ods':
159
- try:
160
- import pandas as pd
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')
167
- table_data = df.values.tolist()
168
- try:
169
- header = list(df.columns)
170
- except:
171
- header = []
172
- return table_data, header, sheets
173
- except Exception as e:
174
- print(f"Error loading ODS file: {e}")
175
- return [], [], []
254
+ extract_formulae = False
255
+ return ods_to_list(input_arg, sheet_number, extract_formulae)
176
256
  elif file_type == 'pkl':
177
257
  with open(os.path.expandvars(os.path.expanduser(input_arg)), 'rb') as f:
178
258
  loaded_data = pickle.load(f)
@@ -180,11 +260,6 @@ def table_to_list(
180
260
  header = loaded_data["header"] if "header" in loaded_data else []
181
261
  return items, header, []
182
262
 
183
- if input_arg == '--stdin':
184
- input_data = sys.stdin.read()
185
- elif input_arg == '--stdin2':
186
- input_count = int(sys.stdin.readline())
187
- input_data = "\n".join([sys.stdin.readline() for i in range(input_count)])
188
263
  else:
189
264
  input_data = read_file_content(input_arg)
190
265
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listpick
3
- Version: 0.1.14.3
3
+ Version: 0.1.14.5
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes