listpick 0.1.14.4__py3-none-any.whl → 0.1.14.6__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.
- listpick/listpick_app.py +143 -36
- listpick/ui/footer.py +10 -2
- listpick/ui/input_field.py +29 -6
- listpick/ui/keycodes.py +70 -0
- listpick/ui/keys.py +5 -5
- listpick/utils/table_to_list_of_lists.py +163 -55
- {listpick-0.1.14.4.dist-info → listpick-0.1.14.6.dist-info}/METADATA +1 -1
- {listpick-0.1.14.4.dist-info → listpick-0.1.14.6.dist-info}/RECORD +12 -11
- {listpick-0.1.14.4.dist-info → listpick-0.1.14.6.dist-info}/WHEEL +0 -0
- {listpick-0.1.14.4.dist-info → listpick-0.1.14.6.dist-info}/entry_points.txt +0 -0
- {listpick-0.1.14.4.dist-info → listpick-0.1.14.6.dist-info}/licenses/LICENSE.txt +0 -0
- {listpick-0.1.14.4.dist-info → listpick-0.1.14.6.dist-info}/top_level.txt +0 -0
listpick/listpick_app.py
CHANGED
|
@@ -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
|
|
@@ -108,7 +110,7 @@ class Picker:
|
|
|
108
110
|
cell_selections: dict[tuple[int,int], bool] = {},
|
|
109
111
|
selected_cells_by_row: dict = {},
|
|
110
112
|
highlight_full_row: bool =False,
|
|
111
|
-
cell_cursor: bool =
|
|
113
|
+
cell_cursor: bool = True,
|
|
112
114
|
|
|
113
115
|
items_per_page : int = -1,
|
|
114
116
|
sort_method : int = 0,
|
|
@@ -349,6 +351,28 @@ 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
|
|
352
376
|
|
|
353
377
|
def calculate_section_sizes(self):
|
|
354
378
|
"""
|
|
@@ -1957,10 +1981,12 @@ class Picker:
|
|
|
1957
1981
|
self.items, self.header = tmp_items, tmp_header
|
|
1958
1982
|
self.data_ready = True
|
|
1959
1983
|
|
|
1960
|
-
def save_input_history(self, file_path: str) -> bool:
|
|
1984
|
+
def save_input_history(self, file_path: str, force_save: bool=True) -> bool:
|
|
1961
1985
|
""" Save input field history. Returns True if successful save. """
|
|
1962
1986
|
self.logger.info(f"function: save_input_history()")
|
|
1963
1987
|
file_path = os.path.expanduser(file_path)
|
|
1988
|
+
file_path = os.path.expandvars(file_path)
|
|
1989
|
+
directory = os.path.dirname(file_path)
|
|
1964
1990
|
history_dict = {
|
|
1965
1991
|
"history_filter_and_search" : self.history_filter_and_search,
|
|
1966
1992
|
"history_pipes" : self.history_pipes,
|
|
@@ -1968,8 +1994,10 @@ class Picker:
|
|
|
1968
1994
|
"history_edits" : self.history_edits,
|
|
1969
1995
|
"history_settings": self.history_settings,
|
|
1970
1996
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1997
|
+
if os.path.exists(directory) or force_save:
|
|
1998
|
+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
1999
|
+
with open(file_path, 'w') as f:
|
|
2000
|
+
json.dump(history_dict, f)
|
|
1973
2001
|
|
|
1974
2002
|
return True
|
|
1975
2003
|
|
|
@@ -2067,27 +2095,32 @@ class Picker:
|
|
|
2067
2095
|
self.notification(self.stdscr, message = f"File not found: {filename}")
|
|
2068
2096
|
return None
|
|
2069
2097
|
|
|
2070
|
-
|
|
2071
|
-
|
|
2098
|
+
try:
|
|
2099
|
+
filetype = guess_file_type(filename)
|
|
2100
|
+
items, header, sheets = table_to_list(filename, file_type=filetype)
|
|
2072
2101
|
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2102
|
+
if items != None:
|
|
2103
|
+
self.items = items
|
|
2104
|
+
self.header = header if header != None else []
|
|
2105
|
+
self.sheets = sheets
|
|
2077
2106
|
|
|
2078
2107
|
|
|
2079
|
-
|
|
2108
|
+
self.initialise_variables()
|
|
2109
|
+
except Exception as e:
|
|
2110
|
+
self.notification(self.stdscr, message=f"Error loading {filename}: {e}")
|
|
2080
2111
|
|
|
2081
2112
|
def load_sheet(self, filename: str, sheet_number: int = 0):
|
|
2082
2113
|
filetype = guess_file_type(filename)
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2114
|
+
try:
|
|
2115
|
+
items, header, sheets = table_to_list(filename, file_type=filetype, sheet_number=sheet_number)
|
|
2116
|
+
if items != None:
|
|
2117
|
+
self.items = items
|
|
2118
|
+
self.header = header if header != None else []
|
|
2119
|
+
self.sheets = sheets
|
|
2089
2120
|
|
|
2090
|
-
|
|
2121
|
+
self.initialise_variables()
|
|
2122
|
+
except Exception as e:
|
|
2123
|
+
self.notification(self.stdscr, message=f"Error loading {filename}, sheet {sheet_number}: {e}")
|
|
2091
2124
|
|
|
2092
2125
|
def switch_file(self, increment=1) -> None:
|
|
2093
2126
|
""" Go to the next file. """
|
|
@@ -2100,6 +2133,7 @@ class Picker:
|
|
|
2100
2133
|
self.loaded_file_index = (self.loaded_file_index + increment) % len(self.loaded_files)
|
|
2101
2134
|
self.loaded_file = self.loaded_files[self.loaded_file_index]
|
|
2102
2135
|
|
|
2136
|
+
idx, file = self.loaded_file_index, self.loaded_file
|
|
2103
2137
|
# If we already have a loaded state for this file
|
|
2104
2138
|
if self.loaded_file_states[self.loaded_file_index]:
|
|
2105
2139
|
self.set_function_data(self.loaded_file_states[self.loaded_file_index])
|
|
@@ -2107,6 +2141,7 @@ class Picker:
|
|
|
2107
2141
|
self.set_function_data({}, reset_absent_variables=True)
|
|
2108
2142
|
self.load_file(self.loaded_file)
|
|
2109
2143
|
|
|
2144
|
+
self.loaded_file_index, self.loaded_file = idx, file
|
|
2110
2145
|
|
|
2111
2146
|
def switch_sheet(self, increment=1) -> None:
|
|
2112
2147
|
if not os.path.exists(self.loaded_file):
|
|
@@ -2179,10 +2214,14 @@ class Picker:
|
|
|
2179
2214
|
function_data = self.get_function_data()
|
|
2180
2215
|
return [], "", function_data
|
|
2181
2216
|
|
|
2182
|
-
#
|
|
2217
|
+
# Open tty to accept input
|
|
2218
|
+
tty_fd = open_tty()
|
|
2183
2219
|
|
|
2220
|
+
# Main loop
|
|
2184
2221
|
while True:
|
|
2185
|
-
key = self.stdscr.getch()
|
|
2222
|
+
# key = self.stdscr.getch()
|
|
2223
|
+
|
|
2224
|
+
key = get_char(tty_fd, timeout=0.2)
|
|
2186
2225
|
if key != -1:
|
|
2187
2226
|
self.logger.info(f"key={key}")
|
|
2188
2227
|
h, w = self.stdscr.getmaxyx()
|
|
@@ -2386,6 +2425,7 @@ class Picker:
|
|
|
2386
2425
|
del self.loaded_file_states[self.loaded_file_index]
|
|
2387
2426
|
self.loaded_file_index = min(self.loaded_file_index, len(self.loaded_files)-1)
|
|
2388
2427
|
self.loaded_file = self.loaded_files[self.loaded_file_index]
|
|
2428
|
+
idx, file = self.loaded_file_index, self.loaded_file
|
|
2389
2429
|
|
|
2390
2430
|
|
|
2391
2431
|
# If we already have a loaded state for this file
|
|
@@ -2394,6 +2434,7 @@ class Picker:
|
|
|
2394
2434
|
else:
|
|
2395
2435
|
self.set_function_data({}, reset_absent_variables=True)
|
|
2396
2436
|
self.load_file(self.loaded_file)
|
|
2437
|
+
self.loaded_file_index, self.loaded_file = idx, file
|
|
2397
2438
|
self.draw_screen(self.indexed_items, self.highlights)
|
|
2398
2439
|
|
|
2399
2440
|
elif self.check_key("full_exit", key, self.keys_dict):
|
|
@@ -2421,7 +2462,7 @@ class Picker:
|
|
|
2421
2462
|
formula_auto_complete=False,
|
|
2422
2463
|
function_auto_complete=False,
|
|
2423
2464
|
word_auto_complete=True,
|
|
2424
|
-
auto_complete_words=["ft", "ct", "cv"]
|
|
2465
|
+
auto_complete_words=["ft", "ct", "cv"],
|
|
2425
2466
|
)
|
|
2426
2467
|
if return_val:
|
|
2427
2468
|
self.user_settings = usrtxt
|
|
@@ -3402,10 +3443,11 @@ def parse_arguments() -> Tuple[argparse.Namespace, dict]:
|
|
|
3402
3443
|
parser.add_argument('--stdin', dest='stdin', action='store_true', help='Table passed on stdin')
|
|
3403
3444
|
parser.add_argument('--stdin2', action='store_true', help='Table passed on stdin')
|
|
3404
3445
|
parser.add_argument('--generate', '-g', type=str, help='Pass file to generate data for listpick Picker.')
|
|
3405
|
-
parser.add_argument('-d', dest='delimiter', default='\t', help='Delimiter for rows in the table (default: tab)')
|
|
3446
|
+
parser.add_argument('--delimiter', '-d', dest='delimiter', default='\t', help='Delimiter for rows in the table (default: tab)')
|
|
3406
3447
|
parser.add_argument('-t', dest='file_type', choices=['tsv', 'csv', 'json', 'xlsx', 'ods', 'pkl'], help='Type of file (tsv, csv, json, xlsx, ods)')
|
|
3407
3448
|
parser.add_argument('--debug', action="store_true", help="Enable debug log.")
|
|
3408
3449
|
parser.add_argument('--debug-verbose', action="store_true", help="Enable debug verbose log.")
|
|
3450
|
+
parser.add_argument('--headerless', action="store_false", help="By default the first row is interpreted as a header row. If --headerless is passed then there is no header.")
|
|
3409
3451
|
args = parser.parse_args()
|
|
3410
3452
|
|
|
3411
3453
|
|
|
@@ -3458,13 +3500,31 @@ def parse_arguments() -> Tuple[argparse.Namespace, dict]:
|
|
|
3458
3500
|
filetype = args.file_type
|
|
3459
3501
|
|
|
3460
3502
|
|
|
3461
|
-
|
|
3503
|
+
while True:
|
|
3504
|
+
try:
|
|
3505
|
+
items, header, sheets = table_to_list(
|
|
3506
|
+
input_arg=input_arg,
|
|
3507
|
+
delimiter=args.delimiter,
|
|
3508
|
+
file_type = filetype,
|
|
3509
|
+
first_row_is_header=args.headerless,
|
|
3510
|
+
)
|
|
3511
|
+
if args.file:
|
|
3512
|
+
function_data["loaded_file"] = args.file[0]
|
|
3513
|
+
function_data["loaded_files"] = args.file
|
|
3514
|
+
break
|
|
3515
|
+
|
|
3516
|
+
except Exception as e:
|
|
3517
|
+
items, header, sheets = [], [], []
|
|
3518
|
+
function_data["startup_notification"] = f"Error loading {input_arg}. {e}"
|
|
3519
|
+
if args.file:
|
|
3520
|
+
args.file = args.file[1:]
|
|
3521
|
+
input_arg = args.file[0]
|
|
3522
|
+
else:
|
|
3523
|
+
break
|
|
3524
|
+
|
|
3462
3525
|
function_data["items"] = items
|
|
3463
3526
|
if header: function_data["header"] = header
|
|
3464
3527
|
function_data["sheets"] = sheets
|
|
3465
|
-
if args.file:
|
|
3466
|
-
function_data["loaded_file"] = args.file[0]
|
|
3467
|
-
function_data["loaded_files"] = args.file
|
|
3468
3528
|
|
|
3469
3529
|
return args, function_data
|
|
3470
3530
|
|
|
@@ -3505,6 +3565,52 @@ def unrestrict_curses(stdscr: curses.window) -> None:
|
|
|
3505
3565
|
curses.raw() # Disable control keys (ctrl-c, ctrl-s, ctrl-q, etc.)
|
|
3506
3566
|
curses.curs_set(False)
|
|
3507
3567
|
|
|
3568
|
+
|
|
3569
|
+
def open_tty():
|
|
3570
|
+
""" Return a file descriptor for the tty that we are opening"""
|
|
3571
|
+
tty_fd = os.open('/dev/tty', os.O_RDONLY)
|
|
3572
|
+
tty.setraw(tty_fd)
|
|
3573
|
+
return tty_fd
|
|
3574
|
+
|
|
3575
|
+
def get_char(tty_fd, timeout: float = 0.2, secondary: bool = False) -> int:
|
|
3576
|
+
""" Get character from a tty_fd with a timeout. """
|
|
3577
|
+
rlist, _, _ = select.select([tty_fd], [], [], timeout)
|
|
3578
|
+
if rlist:
|
|
3579
|
+
# key = ord(tty_fd.read(1))
|
|
3580
|
+
key = ord(os.read(tty_fd, 1))
|
|
3581
|
+
if not secondary:
|
|
3582
|
+
if key == 27:
|
|
3583
|
+
key2 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
3584
|
+
key3 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
3585
|
+
key4 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
3586
|
+
key5 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
3587
|
+
if key2 == ord('O') and key3 == ord('B'):
|
|
3588
|
+
key = curses.KEY_DOWN
|
|
3589
|
+
elif key2 == ord('O') and key3 == ord('A'):
|
|
3590
|
+
key = curses.KEY_UP
|
|
3591
|
+
elif key2 == ord('O') and key3 == ord('D'):
|
|
3592
|
+
key = curses.KEY_LEFT
|
|
3593
|
+
elif key2 == ord('O') and key3 == ord('C'):
|
|
3594
|
+
key = curses.KEY_RIGHT
|
|
3595
|
+
elif key2 == ord('[') and key3 == ord('Z'):
|
|
3596
|
+
key = 353
|
|
3597
|
+
elif key2 == ord('O') and key3 == ord('F'):
|
|
3598
|
+
key = curses.KEY_END
|
|
3599
|
+
elif key2 == ord('O') and key3 == ord('H'):
|
|
3600
|
+
key = curses.KEY_HOME
|
|
3601
|
+
elif key2 == ord('[') and key3 == ord('3') and key4 == ord('~'):
|
|
3602
|
+
key = curses.KEY_DC
|
|
3603
|
+
elif key2 == ord('[') and key3 == ord('3') and key4 == ord('~'):
|
|
3604
|
+
key = curses.KEY_DC
|
|
3605
|
+
elif key2 == ord('O') and key3 == ord('P'):
|
|
3606
|
+
key = curses.KEY_F1
|
|
3607
|
+
elif key2 == ord('[') and key3 == ord('1') and key4 == ord('5') and key5 == ord('~'):
|
|
3608
|
+
key = curses.KEY_F5
|
|
3609
|
+
|
|
3610
|
+
else:
|
|
3611
|
+
key = -1
|
|
3612
|
+
return key
|
|
3613
|
+
|
|
3508
3614
|
def main() -> None:
|
|
3509
3615
|
""" Main function when listpick is executed. Deals with command line arguments and starts a Picker. """
|
|
3510
3616
|
args, function_data = parse_arguments()
|
|
@@ -3540,16 +3646,16 @@ def main() -> None:
|
|
|
3540
3646
|
"color": 8,
|
|
3541
3647
|
}
|
|
3542
3648
|
]
|
|
3543
|
-
function_data["cell_cursor"] = True
|
|
3544
|
-
function_data["display_modes"] = True
|
|
3545
|
-
function_data["centre_in_cols"] = True
|
|
3546
|
-
function_data["show_row_header"] = True
|
|
3547
|
-
function_data["keys_dict"] = picker_keys
|
|
3548
|
-
function_data["id_column"] = -1
|
|
3549
|
-
function_data["track_entries_upon_refresh"] = True
|
|
3550
|
-
function_data["centre_in_terminal_vertical"] = True
|
|
3551
|
-
function_data["highlight_full_row"] = True
|
|
3552
|
-
function_data["pin_cursor"] = True
|
|
3649
|
+
# function_data["cell_cursor"] = True
|
|
3650
|
+
# function_data["display_modes"] = True
|
|
3651
|
+
# function_data["centre_in_cols"] = True
|
|
3652
|
+
# function_data["show_row_header"] = True
|
|
3653
|
+
# function_data["keys_dict"] = picker_keys
|
|
3654
|
+
# function_data["id_column"] = -1
|
|
3655
|
+
# function_data["track_entries_upon_refresh"] = True
|
|
3656
|
+
# function_data["centre_in_terminal_vertical"] = True
|
|
3657
|
+
# function_data["highlight_full_row"] = True
|
|
3658
|
+
# function_data["pin_cursor"] = True
|
|
3553
3659
|
# function_data["display_infobox"] = True
|
|
3554
3660
|
# function_data["infobox_items"] = [["1"], ["2"], ["3"]]
|
|
3555
3661
|
# function_data["infobox_title"] = "Title"
|
|
@@ -3572,6 +3678,7 @@ def main() -> None:
|
|
|
3572
3678
|
|
|
3573
3679
|
# app = Picker(stdscr, **function_data)
|
|
3574
3680
|
app = Picker(stdscr)
|
|
3681
|
+
app.set_config("~/.config/listpick/config.toml")
|
|
3575
3682
|
app.set_function_data(function_data)
|
|
3576
3683
|
app.splash_screen("Listpick is loading your data...")
|
|
3577
3684
|
app.load_input_history("~/.config/listpick/cmdhist.json")
|
listpick/ui/footer.py
CHANGED
|
@@ -94,9 +94,11 @@ class StandardFooter(Footer):
|
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
## Clear background of footer rows
|
|
97
98
|
for i in range(self.height):
|
|
98
99
|
self.stdscr.addstr(h-self.height+i, 0, ' '*(w-1), curses.color_pair(self.colours_start+20))
|
|
99
100
|
|
|
101
|
+
# Display loaded files
|
|
100
102
|
if len(state["loaded_files"]) > 1 and state["loaded_file"] in state["loaded_files"]:
|
|
101
103
|
|
|
102
104
|
sep = "◢ "
|
|
@@ -172,17 +174,23 @@ class StandardFooter(Footer):
|
|
|
172
174
|
|
|
173
175
|
|
|
174
176
|
|
|
177
|
+
|
|
178
|
+
## Cursor selection mode
|
|
175
179
|
select_mode = "C"
|
|
176
180
|
if state["is_selecting"]: select_mode = "VS"
|
|
177
181
|
elif state["is_deselecting"]: select_mode = "VDS"
|
|
178
182
|
if state["pin_cursor"]: select_mode = f"{select_mode} "
|
|
183
|
+
|
|
179
184
|
# Cursor & selection info
|
|
180
185
|
selected_count = sum(state["selections"].values())
|
|
181
186
|
if state["paginate"]:
|
|
182
|
-
# 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}"
|
|
183
187
|
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}"
|
|
184
188
|
else:
|
|
185
|
-
cursor_disp_str = f" [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])} | {select_mode}"
|
|
189
|
+
# cursor_disp_str = f" [{selected_count}] {state['cursor_pos']+1},{state['selected_column']}/{len(state['indexed_items'])},{len(state['column_widths'])} | {select_mode}"
|
|
190
|
+
if state["cell_cursor"]:
|
|
191
|
+
cursor_disp_str = f" [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])} | {state['selected_column']}/{len(state['column_widths'])} | {select_mode}"
|
|
192
|
+
else:
|
|
193
|
+
cursor_disp_str = f" [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])} | {select_mode}"
|
|
186
194
|
|
|
187
195
|
# Maximum chars that should be displayed
|
|
188
196
|
max_chars = min(len(cursor_disp_str)+2, w)
|
listpick/ui/input_field.py
CHANGED
|
@@ -16,7 +16,25 @@ 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
|
+
else:
|
|
35
|
+
key = -1
|
|
36
|
+
return key
|
|
37
|
+
|
|
20
38
|
def input_field(
|
|
21
39
|
stdscr: curses.window,
|
|
22
40
|
usrtxt:str="",
|
|
@@ -106,6 +124,8 @@ def input_field(
|
|
|
106
124
|
offscreen_x, offscreen_y = False, False
|
|
107
125
|
orig_x, orig_y = x, y
|
|
108
126
|
|
|
127
|
+
tty_fd = open_tty()
|
|
128
|
+
|
|
109
129
|
# Input field loop
|
|
110
130
|
while True:
|
|
111
131
|
|
|
@@ -131,7 +151,7 @@ def input_field(
|
|
|
131
151
|
|
|
132
152
|
# Clear background to end of the input field
|
|
133
153
|
stdscr.addstr(field_y, x(), " "*(max_field_length), curses.color_pair(colours_start+colour_pair_bg))
|
|
134
|
-
stdscr.refresh()
|
|
154
|
+
# stdscr.refresh()
|
|
135
155
|
|
|
136
156
|
if literal:
|
|
137
157
|
field_string_length = len(repr(usrtxt)) + len(field_prefix)
|
|
@@ -206,12 +226,15 @@ def input_field(
|
|
|
206
226
|
pass
|
|
207
227
|
|
|
208
228
|
|
|
209
|
-
|
|
229
|
+
stdscr.refresh()
|
|
230
|
+
key = get_char(tty_fd, timeout=0.5)
|
|
231
|
+
# key = stdscr.getch()
|
|
210
232
|
|
|
211
233
|
if key in [27, 7]: # ESC/ALT key or Ctrl+g
|
|
212
234
|
# For Alt-key combinations: set nodelay and get the second key
|
|
213
|
-
stdscr.nodelay(True)
|
|
214
|
-
key2 = stdscr.getch()
|
|
235
|
+
# stdscr.nodelay(True)
|
|
236
|
+
# key2 = stdscr.getch()
|
|
237
|
+
key2 = get_char(tty_fd, timeout=0.05)
|
|
215
238
|
|
|
216
239
|
if key2 == -1: # ESCAPE key (no key-combination)
|
|
217
240
|
stdscr.nodelay(False)
|
|
@@ -311,7 +334,7 @@ def input_field(
|
|
|
311
334
|
curses.endwin()
|
|
312
335
|
exit()
|
|
313
336
|
|
|
314
|
-
elif key
|
|
337
|
+
elif key in [10, 13]: # Enter/return key
|
|
315
338
|
# Return
|
|
316
339
|
return usrtxt, True
|
|
317
340
|
|
listpick/ui/keycodes.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Define constants for alt+a to alt+z
|
|
2
|
+
META_A = 1000
|
|
3
|
+
META_B = 1001
|
|
4
|
+
META_C = 1002
|
|
5
|
+
META_D = 1003
|
|
6
|
+
META_E = 1004
|
|
7
|
+
META_F = 1005
|
|
8
|
+
META_G = 1006
|
|
9
|
+
META_H = 1007
|
|
10
|
+
META_I = 1008
|
|
11
|
+
META_J = 1009
|
|
12
|
+
META_K = 1010
|
|
13
|
+
META_L = 1011
|
|
14
|
+
META_M = 1012
|
|
15
|
+
META_N = 1013
|
|
16
|
+
META_O = 1014
|
|
17
|
+
META_P = 1015
|
|
18
|
+
META_Q = 1016
|
|
19
|
+
META_R = 1017
|
|
20
|
+
META_S = 1018
|
|
21
|
+
META_T = 1019
|
|
22
|
+
META_U = 1020
|
|
23
|
+
META_V = 1021
|
|
24
|
+
META_W = 1022
|
|
25
|
+
META_X = 1023
|
|
26
|
+
META_Y = 1024
|
|
27
|
+
META_Z = 1025
|
|
28
|
+
|
|
29
|
+
# Define constants for alt+A to alt+Z (using uppercase)
|
|
30
|
+
META_a = 1050
|
|
31
|
+
META_b = 1051
|
|
32
|
+
META_c = 1052
|
|
33
|
+
META_d = 1053
|
|
34
|
+
META_e = 1054
|
|
35
|
+
META_f = 1055
|
|
36
|
+
META_g = 1056
|
|
37
|
+
META_h = 1057
|
|
38
|
+
META_i = 1058
|
|
39
|
+
META_j = 1059
|
|
40
|
+
META_k = 1060
|
|
41
|
+
META_l = 1061
|
|
42
|
+
META_m = 1062
|
|
43
|
+
META_n = 1063
|
|
44
|
+
META_o = 1064
|
|
45
|
+
META_p = 1065
|
|
46
|
+
META_q = 1066
|
|
47
|
+
META_r = 1067
|
|
48
|
+
META_s = 1068
|
|
49
|
+
META_t = 1069
|
|
50
|
+
META_u = 1070
|
|
51
|
+
META_v = 1071
|
|
52
|
+
META_w = 1072
|
|
53
|
+
META_x = 1073
|
|
54
|
+
META_y = 1074
|
|
55
|
+
META_z = 1075
|
|
56
|
+
|
|
57
|
+
# Dictionary to map character to its constant value
|
|
58
|
+
META_KEY_MAP = {
|
|
59
|
+
'a': META_a, 'b': META_b, 'c': META_c, 'd': META_d, 'e': META_e,
|
|
60
|
+
'f': META_f, 'g': META_g, 'h': META_h, 'i': META_i, 'j': META_j,
|
|
61
|
+
'k': META_k, 'l': META_l, 'm': META_m, 'n': META_n, 'o': META_o,
|
|
62
|
+
'p': META_p, 'q': META_q, 'r': META_r, 's': META_s, 't': META_t,
|
|
63
|
+
'u': META_u, 'v': META_v, 'w': META_w, 'x': META_x, 'y': META_y,
|
|
64
|
+
'z': META_z, 'A': META_A, 'B': META_B, 'C': META_C, 'D': META_D,
|
|
65
|
+
'E': META_E, 'F': META_F, 'G': META_G, 'H': META_H, 'I': META_I,
|
|
66
|
+
'J': META_J, 'K': META_K, 'L': META_L, 'M': META_M, 'N': META_N,
|
|
67
|
+
'O': META_O, 'P': META_P, 'Q': META_Q, 'R': META_R, 'S': META_S,
|
|
68
|
+
'T': META_T, 'U': META_U, 'V': META_V, 'W': META_W, 'X': META_X,
|
|
69
|
+
'Y': META_Y, 'Z': META_Z
|
|
70
|
+
}
|
listpick/ui/keys.py
CHANGED
|
@@ -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')],
|
|
@@ -122,7 +122,7 @@ help_keys = {
|
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
notification_keys = {
|
|
125
|
-
"exit": [ord('q'), ord('h'), curses.KEY_ENTER, ord('\n'), ord(' '), 27],
|
|
125
|
+
"exit": [ord('q'), ord('h'), curses.KEY_ENTER, ord('\n'), ord(' '), 27, 13],
|
|
126
126
|
"full_exit": [3], # Ctrl+c
|
|
127
127
|
"cursor_down": [ord('j'), curses.KEY_DOWN],
|
|
128
128
|
"cursor_up": [ord('k'), curses.KEY_UP],
|
|
@@ -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,13 +36,117 @@ 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, first_row_is_header: bool = True):
|
|
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
|
+
if first_row_is_header and len(table_data) > 1:
|
|
60
|
+
header = table_data[0]
|
|
61
|
+
table_data = table_data[1:]
|
|
62
|
+
else:
|
|
63
|
+
header = []
|
|
64
|
+
#
|
|
65
|
+
# for row in ws.iter_rows(min_row=2, values_only=True): # Skip the header row
|
|
66
|
+
# row_data = []
|
|
67
|
+
# for cell in row:
|
|
68
|
+
# if isinstance(cell, str) and '=' in cell: # Check if it's a formula
|
|
69
|
+
# row_data.append(cell)
|
|
70
|
+
# else:
|
|
71
|
+
# row_data.append(str(cell))
|
|
72
|
+
# table_data.append(row_data)
|
|
73
|
+
|
|
74
|
+
return table_data, header, wb.sheetnames
|
|
75
|
+
|
|
76
|
+
def ods_to_list(filename: str, sheet_number: int = 0, extract_formulas: bool = False, first_row_is_header: bool = True):
|
|
77
|
+
from odf.opendocument import load
|
|
78
|
+
from odf import table, text
|
|
79
|
+
|
|
80
|
+
from odf.namespaces import TABLENS
|
|
81
|
+
# Load the ODS file
|
|
82
|
+
doc = load(filename)
|
|
83
|
+
|
|
84
|
+
sheets = doc.spreadsheet.getElementsByType(table.Table)
|
|
85
|
+
sheet_names = [s.attributes.get((TABLENS, 'name')) for s in sheets]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# Get the sheet by index
|
|
89
|
+
sheet = doc.spreadsheet.getElementsByType(table.Table)[sheet_number]
|
|
90
|
+
|
|
91
|
+
data = []
|
|
92
|
+
for row in sheet.getElementsByType(table.TableRow):
|
|
93
|
+
row_data = []
|
|
94
|
+
for cell in row.getElementsByType(table.TableCell):
|
|
95
|
+
if extract_formulas:
|
|
96
|
+
formula = cell.attributes.get((TABLENS, 'formula'))
|
|
97
|
+
if formula is not None:
|
|
98
|
+
row_data.append(formula)
|
|
99
|
+
continue # Skip extracting value if formula found
|
|
100
|
+
|
|
101
|
+
# Extract value (as text) from <text:p> elements
|
|
102
|
+
cell_text = ""
|
|
103
|
+
for p in cell.getElementsByType(text.P):
|
|
104
|
+
cell_text += str(p.firstChild) if p.firstChild is not None else ""
|
|
105
|
+
row_data.append(cell_text)
|
|
106
|
+
data.append(row_data)
|
|
107
|
+
if first_row_is_header and len(data) > 1:
|
|
108
|
+
header = data[0]
|
|
109
|
+
data = data[1:]
|
|
110
|
+
else:
|
|
111
|
+
header = []
|
|
112
|
+
|
|
113
|
+
return data, header, sheet_names
|
|
114
|
+
|
|
115
|
+
def ods_to_list_old(file_name: str, sheet_number:int = 0, extract_formulae: bool = False, first_row_is_header: bool = True):
|
|
116
|
+
try:
|
|
117
|
+
import pandas as pd
|
|
118
|
+
ef = pd.ExcelFile(file_name)
|
|
119
|
+
sheets = ef.sheet_names
|
|
120
|
+
sheet_number = max(0, min(sheet_number, len(sheets)-1))
|
|
121
|
+
df = pd.read_excel(file_name, engine='odf', sheet_name=sheet_number)
|
|
122
|
+
# if sheet_number < len(sheets):
|
|
123
|
+
# df = pd.read_excel(input_arg, engine='odf', sheet_name=sheet_number)
|
|
124
|
+
# else:
|
|
125
|
+
# df = pd.read_excel(input_arg, engine='odf')
|
|
126
|
+
table_data = df.values.tolist()
|
|
127
|
+
table_data = [[x if not pd.isna(x) else "" for x in row] for row in table_data]
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
header = list(df.columns)
|
|
131
|
+
except:
|
|
132
|
+
header = []
|
|
133
|
+
|
|
134
|
+
if not first_row_is_header and header:
|
|
135
|
+
table_data = [header] + table_data
|
|
136
|
+
header = []
|
|
137
|
+
|
|
138
|
+
return table_data, header, sheets
|
|
139
|
+
except Exception as e:
|
|
140
|
+
print(f"Error loading ODS file: {e}")
|
|
141
|
+
return [], [], []
|
|
39
142
|
|
|
40
|
-
def table_to_list(
|
|
41
143
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
144
|
+
def table_to_list(
|
|
145
|
+
input_arg: str,
|
|
146
|
+
delimiter:str='\t',
|
|
147
|
+
file_type:Optional[str]=None,
|
|
148
|
+
sheet_number:int = 0,
|
|
149
|
+
first_row_is_header:bool = True,
|
|
46
150
|
|
|
47
151
|
) -> Tuple[list[list[str]], list[str], list[str]]:
|
|
48
152
|
"""
|
|
@@ -62,25 +166,60 @@ def table_to_list(
|
|
|
62
166
|
logger.info("function: table_to_list (table_to_list_of_lists.py)")
|
|
63
167
|
table_data = []
|
|
64
168
|
|
|
65
|
-
def
|
|
169
|
+
def parse_csv_like2(data:str, delimiter:str) -> list[list[str]]:
|
|
66
170
|
""" Convert value-separated data (e.g., CSV or TSV) to list of lists. """
|
|
67
171
|
logger.info("function: parse_csv_like (table_to_list_of_lists.py)")
|
|
68
172
|
|
|
69
173
|
try:
|
|
70
|
-
reader = csv.reader(StringIO(data), delimiter=delimiter)
|
|
174
|
+
# reader = csv.reader(StringIO(data), delimiter=delimiter)
|
|
175
|
+
reader = csv.reader(StringIO(data), dialect='unix')
|
|
71
176
|
return [row for row in reader]
|
|
72
177
|
except Exception as e:
|
|
73
178
|
print(f"Error reading CSV-like input: {e}")
|
|
74
179
|
return []
|
|
180
|
+
def parse_csv_like(data:str, delimiter: str=" "):
|
|
181
|
+
import re
|
|
182
|
+
def split_columns(line):
|
|
183
|
+
# Define the regex pattern to match quoted strings and split by whitespace
|
|
184
|
+
# pattern = r"(?:'[^']*'|[^'\s]+)"
|
|
185
|
+
pattern = r"(?:\"[^\"]*\"|'[^']*'|[^'\s]+)"
|
|
186
|
+
|
|
187
|
+
# Find all matches using the defined pattern
|
|
188
|
+
columns = re.findall(pattern, line)
|
|
189
|
+
|
|
190
|
+
return columns
|
|
191
|
+
|
|
192
|
+
lines = data.strip().split('\n')
|
|
193
|
+
result = []
|
|
194
|
+
|
|
195
|
+
for line in lines:
|
|
196
|
+
result.append(split_columns(line))
|
|
197
|
+
|
|
198
|
+
return result
|
|
75
199
|
|
|
76
|
-
def csv_string_to_list(csv_string:str) -> list[list[str]]:
|
|
200
|
+
def csv_string_to_list(csv_string:str, first_row_is_header: bool = True) -> list[list[str]]:
|
|
77
201
|
""" Convert csv string to list of lists using csv.reader. """
|
|
78
202
|
logger.info("function: csv_string_to_list (table_to_list_of_lists.py)")
|
|
79
203
|
f = StringIO(csv_string)
|
|
80
204
|
reader = csv.reader(f, skipinitialspace=True)
|
|
81
|
-
|
|
205
|
+
table_data = [row for row in reader]
|
|
206
|
+
if first_row_is_header and len(table_data) > 1:
|
|
207
|
+
header = table_data[0]
|
|
208
|
+
table_data = table_data[1:]
|
|
209
|
+
else:
|
|
210
|
+
header = []
|
|
211
|
+
return table_data, header
|
|
212
|
+
|
|
82
213
|
|
|
83
|
-
if
|
|
214
|
+
if input_arg == '--stdin':
|
|
215
|
+
input_data = sys.stdin.read()
|
|
216
|
+
elif input_arg == '--stdin2':
|
|
217
|
+
input_count = int(sys.stdin.readline())
|
|
218
|
+
input_data = "\n".join([sys.stdin.readline() for i in range(input_count)])
|
|
219
|
+
sys.stdin.flush()
|
|
220
|
+
# sys.stdin.close()
|
|
221
|
+
# sys.stdin = open('/dev/tty', 'r')
|
|
222
|
+
elif file_type == 'csv' or delimiter in [',']:
|
|
84
223
|
try:
|
|
85
224
|
if input_arg == '--stdin':
|
|
86
225
|
input_data = sys.stdin.read()
|
|
@@ -90,10 +229,11 @@ def table_to_list(
|
|
|
90
229
|
else:
|
|
91
230
|
input_data = read_file_content(input_arg)
|
|
92
231
|
|
|
93
|
-
table_data = csv_string_to_list(input_data)
|
|
232
|
+
table_data, header = csv_string_to_list(input_data, first_row_is_header)
|
|
94
233
|
table_data = strip_whitespace(table_data)
|
|
234
|
+
header = strip_whitespace([header])[0]
|
|
95
235
|
# table_data = parse_csv_like(input_data, ",")
|
|
96
|
-
return table_data,
|
|
236
|
+
return table_data, header, []
|
|
97
237
|
except Exception as e:
|
|
98
238
|
print(f"Error reading CSV/TSV input: {e}")
|
|
99
239
|
return [], [], []
|
|
@@ -141,45 +281,12 @@ def table_to_list(
|
|
|
141
281
|
return [], [], []
|
|
142
282
|
|
|
143
283
|
elif file_type == 'xlsx':
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
sheets = ef.sheet_names
|
|
147
|
-
sheet_number = min(0, max(sheet_number, len(sheets)-1))
|
|
148
|
-
df = pd.read_excel(input_arg, engine='odf', sheet_name=sheet_number)
|
|
149
|
-
# if sheet_number < len(sheets):
|
|
150
|
-
# df = pd.read_excel(input_arg, sheet_name=sheet_number)
|
|
151
|
-
# else:
|
|
152
|
-
# df = pd.read_excel(input_arg)
|
|
153
|
-
table_data = df.values.tolist()
|
|
154
|
-
table_data = [[x if not pd.isna(x) else "" for x in row] for row in table_data]
|
|
155
|
-
try:
|
|
156
|
-
header = list(df.columns)
|
|
157
|
-
except:
|
|
158
|
-
header = []
|
|
159
|
-
return table_data, header, sheets
|
|
284
|
+
extract_formulae = False
|
|
285
|
+
return xlsx_to_list(input_arg, sheet_number, extract_formulae, first_row_is_header)
|
|
160
286
|
|
|
161
287
|
elif file_type == 'ods':
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
ef = pd.ExcelFile(input_arg)
|
|
165
|
-
sheets = ef.sheet_names
|
|
166
|
-
sheet_number = min(0, max(sheet_number, len(sheets)-1))
|
|
167
|
-
df = pd.read_excel(input_arg, engine='odf', sheet_name=sheet_number)
|
|
168
|
-
# if sheet_number < len(sheets):
|
|
169
|
-
# df = pd.read_excel(input_arg, engine='odf', sheet_name=sheet_number)
|
|
170
|
-
# else:
|
|
171
|
-
# df = pd.read_excel(input_arg, engine='odf')
|
|
172
|
-
table_data = df.values.tolist()
|
|
173
|
-
table_data = [[x if not pd.isna(x) else "" for x in row] for row in table_data]
|
|
174
|
-
|
|
175
|
-
try:
|
|
176
|
-
header = list(df.columns)
|
|
177
|
-
except:
|
|
178
|
-
header = []
|
|
179
|
-
return table_data, header, sheets
|
|
180
|
-
except Exception as e:
|
|
181
|
-
print(f"Error loading ODS file: {e}")
|
|
182
|
-
return [], [], []
|
|
288
|
+
extract_formulae = False
|
|
289
|
+
return ods_to_list(input_arg, sheet_number, extract_formulae, first_row_is_header)
|
|
183
290
|
elif file_type == 'pkl':
|
|
184
291
|
with open(os.path.expandvars(os.path.expanduser(input_arg)), 'rb') as f:
|
|
185
292
|
loaded_data = pickle.load(f)
|
|
@@ -187,17 +294,18 @@ def table_to_list(
|
|
|
187
294
|
header = loaded_data["header"] if "header" in loaded_data else []
|
|
188
295
|
return items, header, []
|
|
189
296
|
|
|
190
|
-
if input_arg == '--stdin':
|
|
191
|
-
input_data = sys.stdin.read()
|
|
192
|
-
elif input_arg == '--stdin2':
|
|
193
|
-
input_count = int(sys.stdin.readline())
|
|
194
|
-
input_data = "\n".join([sys.stdin.readline() for i in range(input_count)])
|
|
195
297
|
else:
|
|
196
298
|
input_data = read_file_content(input_arg)
|
|
197
299
|
|
|
198
300
|
table_data = parse_csv_like(input_data, delimiter)
|
|
301
|
+
if first_row_is_header and len(table_data) > 1:
|
|
302
|
+
header = table_data[0]
|
|
303
|
+
table_data = table_data[1:]
|
|
304
|
+
else:
|
|
305
|
+
header = []
|
|
306
|
+
|
|
199
307
|
|
|
200
|
-
return table_data,
|
|
308
|
+
return table_data, header, []
|
|
201
309
|
|
|
202
310
|
if __name__ == '__main__':
|
|
203
311
|
parser = argparse.ArgumentParser(description='Convert table to list of lists.')
|
|
@@ -1,12 +1,13 @@
|
|
|
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=
|
|
3
|
+
listpick/listpick_app.py,sha256=sEe8IPPcICEZGw9Hz6mrdvkJMrIU1YxhQ-5bONvNcSc,188471
|
|
4
4
|
listpick/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
listpick/ui/build_help.py,sha256=8QtsRosIE2IMagRc_remzmwSWpCurFgLenLL7w1ly94,8949
|
|
6
|
-
listpick/ui/footer.py,sha256=
|
|
6
|
+
listpick/ui/footer.py,sha256=ZM5OWCxOZqy5RG6tFTe1ipvu9PRu6Gh3JCA8KXBR_Wc,15004
|
|
7
7
|
listpick/ui/help_screen.py,sha256=zbfGIgb-IXtATpl4_Sx7nPbsnRXZ7eiMYlCKGS9EFmw,5608
|
|
8
|
-
listpick/ui/input_field.py,sha256=
|
|
9
|
-
listpick/ui/
|
|
8
|
+
listpick/ui/input_field.py,sha256=PrYZICoT9M4e85W755d909TItscOae2LYvYfQsWtiXE,31068
|
|
9
|
+
listpick/ui/keycodes.py,sha256=1UilWAwtjhs801BrP-SndP-qIRS3AQJHl6KYKrWPgus,1614
|
|
10
|
+
listpick/ui/keys.py,sha256=C9wG_VPhaXq_c2bREBGKhd4Tb-AKqqOgub7y4W8xQmI,13182
|
|
10
11
|
listpick/ui/pane_stuff.py,sha256=7GXa4UnV_7YmBv-baRi5moN51wYcuS4p0odl5C3m0Tc,169
|
|
11
12
|
listpick/ui/picker_colours.py,sha256=FLOzvkq83orrN2bL0Mw-6RugWOZyuwUjQCrUFMUnKGY,11563
|
|
12
13
|
listpick/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -21,11 +22,11 @@ listpick/utils/picker_log.py,sha256=SW6GmjxpI7YrSf72fSr4O8Ux0fY_OzaSXUgTFdz6Xo4,
|
|
|
21
22
|
listpick/utils/search_and_filter_utils.py,sha256=XxGfkyDVXO9OAKcftPat8IReMTFIuTH-jorxI4o84tg,3239
|
|
22
23
|
listpick/utils/searching.py,sha256=Xk5UIqamNHL2L90z3ACB_Giqdpi9iRKoAJ6pKaqaD7Q,3093
|
|
23
24
|
listpick/utils/sorting.py,sha256=WZZiVlVA3Zkcpwji3U5SNFlQ14zVEk3cZJtQirBkecQ,5329
|
|
24
|
-
listpick/utils/table_to_list_of_lists.py,sha256=
|
|
25
|
+
listpick/utils/table_to_list_of_lists.py,sha256=XBj7eGBDF15BRME-swnoXyOfZWxXCxrXp0pzsBfcJ5g,12224
|
|
25
26
|
listpick/utils/utils.py,sha256=McOl9uT3jh7l4TIWeSd8ZGjK_e7r0YZF0Gl20yI6fl0,13873
|
|
26
|
-
listpick-0.1.14.
|
|
27
|
-
listpick-0.1.14.
|
|
28
|
-
listpick-0.1.14.
|
|
29
|
-
listpick-0.1.14.
|
|
30
|
-
listpick-0.1.14.
|
|
31
|
-
listpick-0.1.14.
|
|
27
|
+
listpick-0.1.14.6.dist-info/licenses/LICENSE.txt,sha256=2mP-MRHJptADDNE9VInMNg1tE-C6Qv93Z4CCQKrpg9w,1061
|
|
28
|
+
listpick-0.1.14.6.dist-info/METADATA,sha256=GJpvywbTsGBGaYZVeD2mIMqQvbCF4bvA_E1Rg_4fM8Q,8090
|
|
29
|
+
listpick-0.1.14.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
30
|
+
listpick-0.1.14.6.dist-info/entry_points.txt,sha256=-QCf_BKIkUz35Y9nkYpjZWs2Qg0KfRna2PAs5DnF6BE,43
|
|
31
|
+
listpick-0.1.14.6.dist-info/top_level.txt,sha256=5mtsGEz86rz3qQDe0D463gGjAfSp6A3EWg4J4AGYr-Q,9
|
|
32
|
+
listpick-0.1.14.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|