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.
- {listpick-0.1.14.3 → listpick-0.1.14.5}/CHANGELOG.md +1 -0
- {listpick-0.1.14.3/src/listpick.egg-info → listpick-0.1.14.5}/PKG-INFO +1 -1
- {listpick-0.1.14.3 → listpick-0.1.14.5}/setup.py +1 -1
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/listpick_app.py +117 -28
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/input_field.py +47 -6
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/keys.py +4 -4
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/table_to_list_of_lists.py +111 -36
- {listpick-0.1.14.3 → listpick-0.1.14.5/src/listpick.egg-info}/PKG-INFO +1 -1
- {listpick-0.1.14.3 → listpick-0.1.14.5}/.gitignore +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/LICENSE.txt +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/README.md +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/TODO.md +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/assets/aria2tui_screenshot.png +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/assets/file_compare.png +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/assets/lpfman.png +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/data_generation/list_files.toml +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/data_generation/list_files_empty.toml +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/data_generation/video_duplicates.toml +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/data_generation/video_mediainfo.toml +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/input_files/polynomials.tsv +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/input_guides/gnuplot_graph.md +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/auxiallary_files/2024-25_Premier_League.pkl +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/footer_string_example.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/picker_example.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/template.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/wikipedia_table.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/listpick.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/requirements.txt +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/setup.cfg +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/__init__.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/__main__.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/__init__.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/build_help.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/footer.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/git-bugreport-2025-08-16-1438.txt +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/help_screen.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/pane_stuff.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/ui/picker_colours.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/__init__.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/clipboard_operations.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/config.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/dump.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/filtering.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/generate_data.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/options_selectors.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/paste_operations.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/picker_log.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/search_and_filter_utils.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/searching.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/sorting.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick/utils/utils.py +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick.egg-info/SOURCES.txt +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick.egg-info/dependency_links.txt +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick.egg-info/entry_points.txt +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick.egg-info/requires.txt +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/src/listpick.egg-info/top_level.txt +0 -0
- {listpick-0.1.14.3 → listpick-0.1.14.5}/tests/kitty_control.sh +0 -0
- {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.
|
|
@@ -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.
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
145
|
-
|
|
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
|
-
|
|
160
|
-
|
|
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
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{listpick-0.1.14.3 → listpick-0.1.14.5}/examples/picker/auxiallary_files/2024-25_Premier_League.pkl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|