listpick 0.1.13.60__tar.gz → 0.1.13.62__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 (56) hide show
  1. {listpick-0.1.13.60 → listpick-0.1.13.62}/CHANGELOG.md +2 -0
  2. {listpick-0.1.13.60/src/listpick.egg-info → listpick-0.1.13.62}/PKG-INFO +5 -5
  3. {listpick-0.1.13.60 → listpick-0.1.13.62}/README.md +4 -4
  4. {listpick-0.1.13.60 → listpick-0.1.13.62}/setup.py +1 -1
  5. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/listpick_app.py +37 -12
  6. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/generate_data.py +4 -1
  7. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/utils.py +19 -0
  8. {listpick-0.1.13.60 → listpick-0.1.13.62/src/listpick.egg-info}/PKG-INFO +5 -5
  9. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick.egg-info/SOURCES.txt +0 -10
  10. listpick-0.1.13.60/examples/footer_string_example.py +0 -21
  11. listpick-0.1.13.60/examples/input.toml +0 -25
  12. listpick-0.1.13.60/examples/input.txt +0 -20
  13. listpick-0.1.13.60/examples/list_files.toml +0 -15
  14. listpick-0.1.13.60/examples/list_files_empty.toml +0 -15
  15. listpick-0.1.13.60/examples/picker_example.py +0 -45
  16. listpick-0.1.13.60/examples/setup.cfg +0 -26
  17. listpick-0.1.13.60/examples/template.py +0 -19
  18. listpick-0.1.13.60/examples/video_duplicates.toml +0 -27
  19. listpick-0.1.13.60/src/listpick/ui/footer_1.py +0 -202
  20. {listpick-0.1.13.60 → listpick-0.1.13.62}/.gitignore +0 -0
  21. {listpick-0.1.13.60 → listpick-0.1.13.62}/LICENSE.txt +0 -0
  22. {listpick-0.1.13.60 → listpick-0.1.13.62}/TODO.md +0 -0
  23. {listpick-0.1.13.60 → listpick-0.1.13.62}/assets/aria2tui_screenshot.png +0 -0
  24. {listpick-0.1.13.60 → listpick-0.1.13.62}/assets/file_compare.png +0 -0
  25. {listpick-0.1.13.60 → listpick-0.1.13.62}/assets/lpfman.png +0 -0
  26. {listpick-0.1.13.60 → listpick-0.1.13.62}/listpick.py +0 -0
  27. {listpick-0.1.13.60 → listpick-0.1.13.62}/requirements.txt +0 -0
  28. {listpick-0.1.13.60 → listpick-0.1.13.62}/setup.cfg +0 -0
  29. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/__init__.py +0 -0
  30. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/__main__.py +0 -0
  31. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/ui/__init__.py +0 -0
  32. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/ui/build_help.py +0 -0
  33. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/ui/footer.py +0 -0
  34. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/ui/help_screen.py +0 -0
  35. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/ui/input_field.py +0 -0
  36. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/ui/keys.py +0 -0
  37. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/ui/pane_stuff.py +0 -0
  38. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/ui/picker_colours.py +0 -0
  39. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/__init__.py +0 -0
  40. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/clipboard_operations.py +0 -0
  41. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/config.py +0 -0
  42. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/dump.py +0 -0
  43. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/filtering.py +0 -0
  44. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/options_selectors.py +0 -0
  45. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/paste_operations.py +0 -0
  46. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/picker_log.py +0 -0
  47. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/search_and_filter_utils.py +0 -0
  48. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/searching.py +0 -0
  49. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/sorting.py +0 -0
  50. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick/utils/table_to_list_of_lists.py +0 -0
  51. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick.egg-info/dependency_links.txt +0 -0
  52. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick.egg-info/entry_points.txt +0 -0
  53. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick.egg-info/requires.txt +0 -0
  54. {listpick-0.1.13.60 → listpick-0.1.13.62}/src/listpick.egg-info/top_level.txt +0 -0
  55. {listpick-0.1.13.60 → listpick-0.1.13.62}/tests/kitty_control.sh +0 -0
  56. {listpick-0.1.13.60 → listpick-0.1.13.62}/tests/sorting_dates.csv +0 -0
@@ -29,6 +29,8 @@
29
29
  - Speed improvements:
30
30
  - Create and track self.selected_cells_by_row when selections change rather than derive it from the self.cell_selections
31
31
  - Much faster with very large data sets as we need to determine selected_cells_by_row every time we run self.draw_screen()
32
+ - We can now pipe data from cells in multiple columns to a command.
33
+ - e.g., pipe two cols to gnuplot
32
34
 
33
35
  ## [0.1.13] 2025-07-28
34
36
  - Cell-based picker is now supported.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listpick
3
- Version: 0.1.13.60
3
+ Version: 0.1.13.62
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
@@ -80,7 +80,7 @@ close_curses(stdscr)
80
80
  Use the listpick binary to generate and display rows based on a list of commands:
81
81
 
82
82
  ```
83
- wget https://raw.githubusercontent.com/grimandgreedy/listpick/refs/heads/master/examples/list_files.toml
83
+ wget https://raw.githubusercontent.com/grimandgreedy/listpick/refs/heads/master/examples/data_generation/list_files.toml
84
84
  listpick -g list_files.py
85
85
  ```
86
86
 
@@ -100,9 +100,9 @@ The application allows you to:
100
100
  ## Examples
101
101
 
102
102
 
103
- ### Identify video duplicates (./examples/video_duplicates.toml):
103
+ ### Identify video duplicates (./examples/data_generation//video_duplicates.toml):
104
104
  ```python
105
- listpick -g ./examples/video_duplicates.toml
105
+ listpick -g ./examples/data_generation/video_duplicates.toml
106
106
  ```
107
107
  - From the list of commands in the toml file we generate the properties we will use to identify the duplicates.
108
108
 
@@ -142,7 +142,7 @@ listpick -i ~/dn.pkl -t pkl
142
142
 
143
143
  2. **Generate data based on an toml file with relevant commands to generate the rows.**
144
144
  ```python
145
- listpick -g ./examples/video_duplicates.toml
145
+ listpick -g ./examples/data_generation/video_duplicates.toml
146
146
  ```
147
147
 
148
148
  - See ./examples/
@@ -39,7 +39,7 @@ close_curses(stdscr)
39
39
  Use the listpick binary to generate and display rows based on a list of commands:
40
40
 
41
41
  ```
42
- wget https://raw.githubusercontent.com/grimandgreedy/listpick/refs/heads/master/examples/list_files.toml
42
+ wget https://raw.githubusercontent.com/grimandgreedy/listpick/refs/heads/master/examples/data_generation/list_files.toml
43
43
  listpick -g list_files.py
44
44
  ```
45
45
 
@@ -59,9 +59,9 @@ The application allows you to:
59
59
  ## Examples
60
60
 
61
61
 
62
- ### Identify video duplicates (./examples/video_duplicates.toml):
62
+ ### Identify video duplicates (./examples/data_generation//video_duplicates.toml):
63
63
  ```python
64
- listpick -g ./examples/video_duplicates.toml
64
+ listpick -g ./examples/data_generation/video_duplicates.toml
65
65
  ```
66
66
  - From the list of commands in the toml file we generate the properties we will use to identify the duplicates.
67
67
 
@@ -101,7 +101,7 @@ listpick -i ~/dn.pkl -t pkl
101
101
 
102
102
  2. **Generate data based on an toml file with relevant commands to generate the rows.**
103
103
  ```python
104
- listpick -g ./examples/video_duplicates.toml
104
+ listpick -g ./examples/data_generation/video_duplicates.toml
105
105
  ```
106
106
 
107
107
  - See ./examples/
@@ -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.13.60",
19
+ version = "0.1.13.62",
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.",
@@ -449,8 +449,6 @@ class Picker:
449
449
  # self.stdscr.refresh()
450
450
  # self.draw_screen(self.indexed_items, self.highlights)
451
451
 
452
-
453
-
454
452
  def initialise_variables(self, get_data: bool = False) -> None:
455
453
  """ Initialise the variables that keep track of the data. """
456
454
 
@@ -473,6 +471,7 @@ class Picker:
473
471
 
474
472
  self.items, self.header = self.refresh_function()
475
473
 
474
+ self.items = pad_lists_to_same_length(self.items)
476
475
 
477
476
  if self.items == []: self.items = [[]]
478
477
  ## Ensure that items is a List[List[Str]] object
@@ -1261,7 +1260,12 @@ class Picker:
1261
1260
  message_width = notification_width-5
1262
1261
 
1263
1262
  if not message: message = "!!"
1264
- submenu_items = [" "+message[i*message_width:(i+1)*message_width] for i in range(len(message)//message_width+1)]
1263
+ if type(message) == type(""):
1264
+ mw = message_width
1265
+ submenu_items = [[message[i*mw:(i+1)*mw]] for i in range(len(message)//mw+1)]
1266
+ elif type(message) != type([]):
1267
+ submenu_items = [[" !!"]]
1268
+
1265
1269
 
1266
1270
  notification_remap_keys = {
1267
1271
  curses.KEY_RESIZE: curses.KEY_F5,
@@ -1959,7 +1963,8 @@ class Picker:
1959
1963
 
1960
1964
  while True:
1961
1965
  key = self.stdscr.getch()
1962
- self.logger.info(f"key={key}")
1966
+ if key:
1967
+ self.logger.info(f"key={key}")
1963
1968
  h, w = self.stdscr.getmaxyx()
1964
1969
  if key in self.disabled_keys: continue
1965
1970
  clear_screen=True
@@ -2760,26 +2765,46 @@ class Picker:
2760
2765
  if not selected_indices:
2761
2766
  selected_indices = [self.indexed_items[self.cursor_pos][0]]
2762
2767
 
2763
- full_values = [format_row_full(self.items[i], self.hidden_columns) for i in selected_indices] # Use format_row_full for full data
2764
- full_values = [self.items[i][self.selected_column] for i in selected_indices]
2768
+ # full_values = [format_row_full(self.items[i], self.hidden_columns) for i in selected_indices] # Use format_row_full for full data
2769
+ if self.cell_cursor:
2770
+
2771
+ full_values = []
2772
+ for row in self.selected_cells_by_row.keys():
2773
+ selected_cell_row_str = ""
2774
+ for cell in self.selected_cells_by_row[row]:
2775
+ if " " in self.items[row][cell]:
2776
+ selected_cell_row_str += repr(self.items[row][cell])
2777
+ else:
2778
+ selected_cell_row_str += self.items[row][cell]
2779
+ selected_cell_row_str += "\t"
2780
+ full_values.append(selected_cell_row_str.strip())
2781
+
2782
+
2783
+ # full_values = ["\t".join([repr(self.items[key][cell]) for cell in self.selected_cells_by_row[key]]) for key in self.selected_cells_by_row.keys()]
2784
+ # full_values = ["\t".join([self.items[key][cell] for cell in self.selected_cells_by_row[key]]) for key in self.selected_cells_by_row.keys()]
2785
+ else:
2786
+ full_values = [self.items[i][self.selected_column] for i in selected_indices]
2765
2787
  if full_values:
2766
- command = usrtxt.split()
2788
+ # command = usrtxt.split()
2789
+ command = usrtxt
2767
2790
  # command = ['xargs', '-d' , '"\n"' '-I', '{}', 'mpv', '{}']
2768
2791
  # command = ['xargs', '-d' , '"\n"' '-I', '{}', 'mpv', '{}']
2769
2792
  # command = "xargs -d '\n' -I{} mpv {}"
2770
2793
 
2771
2794
  try:
2772
- process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
2795
+ process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True)
2773
2796
 
2774
2797
  if process.stdin != None:
2775
2798
  for value in full_values:
2776
- process.stdin.write((repr(value) + '\n').encode())
2799
+ process.stdin.write((value + '\n').encode())
2800
+ # process.stdin.write((value + '\n').encode())
2777
2801
 
2778
2802
  process.stdin.close()
2779
2803
 
2780
2804
  self.notification(self.stdscr, message=f"{len(full_values)} strings piped to {repr(usrtxt)}")
2781
2805
  except Exception as e:
2782
2806
  self.notification(self.stdscr, message=f"{e}")
2807
+ # self.notification(self.stdscr, message=f"Error: {str(e)}")
2783
2808
 
2784
2809
 
2785
2810
  elif self.check_key("open", key, self.keys_dict):
@@ -3158,9 +3183,9 @@ def main() -> None:
3158
3183
  function_data["centre_in_terminal_vertical"] = True
3159
3184
  function_data["highlight_full_row"] = True
3160
3185
  function_data["pin_cursor"] = True
3161
- function_data["display_infobox"] = True
3162
- function_data["infobox_items"] = [["1"], ["2"], ["3"]]
3163
- function_data["infobox_title"] = "Title"
3186
+ # function_data["display_infobox"] = True
3187
+ # function_data["infobox_items"] = [["1"], ["2"], ["3"]]
3188
+ # function_data["infobox_title"] = "Title"
3164
3189
  # function_data["footer_string"] = "Title"
3165
3190
  function_data["highlights"] = highlights
3166
3191
  function_data["show_footer"] = False
@@ -25,7 +25,10 @@ def generate_columns(funcs: list, files: list) -> list[list[str]]:
25
25
  for file in files:
26
26
  item = []
27
27
  for func in funcs:
28
- item.append(func(file))
28
+ try:
29
+ item.append(func(file))
30
+ except:
31
+ item.append("")
29
32
  items.append(item)
30
33
 
31
34
  return items
@@ -344,3 +344,22 @@ def guess_file_type(filename: str) -> str:
344
344
  """ Guess filetype. Currently just uses the extension of the file. """
345
345
  logger.info("function: guess_file_type (utils.py)")
346
346
  return filename.split(".")[-1]
347
+
348
+
349
+ def pad_lists_to_same_length(list_of_lists: list[list[str]]) -> list[list[str]]:
350
+ """ Ensure that all lists in a list of lists are of the same length. Pad any shorter sublists with empty strings. """
351
+ if not list_of_lists or list_of_lists in [[], [[]]]:
352
+ return []
353
+ if type(list_of_lists) == type([]) and len(list_of_lists) and type(list_of_lists[0]) == type(""):
354
+ list_of_lists = [[x] for x in list_of_lists]
355
+
356
+ # Find the maximum length of the sublists
357
+ lengths = [len(sublist) for sublist in list_of_lists]
358
+ max_length = max(lengths)
359
+ min_length = min(lengths)
360
+ if min_length == max_length: return list_of_lists
361
+
362
+ # Pad each sublist with empty strings to match the maximum length
363
+ padded_list = [sublist + [''] * (max_length - len(sublist)) for sublist in list_of_lists]
364
+
365
+ return padded_list
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listpick
3
- Version: 0.1.13.60
3
+ Version: 0.1.13.62
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
@@ -80,7 +80,7 @@ close_curses(stdscr)
80
80
  Use the listpick binary to generate and display rows based on a list of commands:
81
81
 
82
82
  ```
83
- wget https://raw.githubusercontent.com/grimandgreedy/listpick/refs/heads/master/examples/list_files.toml
83
+ wget https://raw.githubusercontent.com/grimandgreedy/listpick/refs/heads/master/examples/data_generation/list_files.toml
84
84
  listpick -g list_files.py
85
85
  ```
86
86
 
@@ -100,9 +100,9 @@ The application allows you to:
100
100
  ## Examples
101
101
 
102
102
 
103
- ### Identify video duplicates (./examples/video_duplicates.toml):
103
+ ### Identify video duplicates (./examples/data_generation//video_duplicates.toml):
104
104
  ```python
105
- listpick -g ./examples/video_duplicates.toml
105
+ listpick -g ./examples/data_generation/video_duplicates.toml
106
106
  ```
107
107
  - From the list of commands in the toml file we generate the properties we will use to identify the duplicates.
108
108
 
@@ -142,7 +142,7 @@ listpick -i ~/dn.pkl -t pkl
142
142
 
143
143
  2. **Generate data based on an toml file with relevant commands to generate the rows.**
144
144
  ```python
145
- listpick -g ./examples/video_duplicates.toml
145
+ listpick -g ./examples/data_generation/video_duplicates.toml
146
146
  ```
147
147
 
148
148
  - See ./examples/
@@ -9,15 +9,6 @@ setup.py
9
9
  assets/aria2tui_screenshot.png
10
10
  assets/file_compare.png
11
11
  assets/lpfman.png
12
- examples/footer_string_example.py
13
- examples/input.toml
14
- examples/input.txt
15
- examples/list_files.toml
16
- examples/list_files_empty.toml
17
- examples/picker_example.py
18
- examples/setup.cfg
19
- examples/template.py
20
- examples/video_duplicates.toml
21
12
  src/listpick/__init__.py
22
13
  src/listpick/__main__.py
23
14
  src/listpick/listpick_app.py
@@ -30,7 +21,6 @@ src/listpick.egg-info/top_level.txt
30
21
  src/listpick/ui/__init__.py
31
22
  src/listpick/ui/build_help.py
32
23
  src/listpick/ui/footer.py
33
- src/listpick/ui/footer_1.py
34
24
  src/listpick/ui/help_screen.py
35
25
  src/listpick/ui/input_field.py
36
26
  src/listpick/ui/keys.py
@@ -1,21 +0,0 @@
1
- from listpick.listpick_app import Picker, close_curses, start_curses
2
- from datetime import datetime
3
-
4
-
5
- l = [["Time"], ["in"], ["footer"], ["string"]]
6
-
7
- stdscr = start_curses()
8
- x = Picker(
9
- stdscr = stdscr,
10
- items = l,
11
- title="Footer string example",
12
- footer_string_auto_refresh=True,
13
- footer_timer=0.1,
14
- footer_string_refresh_function=lambda:str(datetime.now()).split('.')[0],
15
- )
16
- selected_indices, opts, picker_data = x.run()
17
-
18
- close_curses(stdscr)
19
-
20
- print(f"Selected: {selected_indices}")
21
- print(f"Opts: {opts}")
@@ -1,25 +0,0 @@
1
- [environment]
2
- cwd="~/Videos"
3
-
4
- [data]
5
- files_command = "eza -1 --no-quotes | grep mp4"
6
-
7
- commands = [
8
- """eza -1 --no-quotes | grep mp4""",
9
- """mediainfo {} | grep -i format | head -n 1 | awk -F ':' '{{print $2}}'""",
10
- """mediainfo {} | grep -i height | head -n 1 | awk -F ':' '{{print $2}}'""",
11
- """mediainfo {} | grep -i width | head -n 1 | awk -F ':' '{{print $2}}'""",
12
- """mediainfo {} | grep -i duration | head -n 1 | awk -F ':' '{{print $2}}'""",
13
- """mediainfo {} | grep -i 'bit rate' | head -n 1 | awk -F ':' '{{print $2}}'""",
14
- """mediainfo {} | grep -i 'frame rate' | head -n 1 | awk -F ':' '{{print $2}}'""",
15
- ]
16
-
17
- header = [
18
- "file",
19
- "format",
20
- "height",
21
- "width",
22
- "duration",
23
- "bitrate",
24
- "framerate",
25
- ]
@@ -1,20 +0,0 @@
1
- --- Environment
2
- cwd=~/Videos
3
-
4
- --- Commands
5
- eza -1 --no-quotes | grep mp4
6
- mediainfo {} | grep -i format | head -n 1 | awk -F ':' '{{print $2}}'
7
- mediainfo {} | grep -i height | head -n 1 | awk -F ':' '{{print $2}}'
8
- mediainfo {} | grep -i width | head -n 1 | awk -F ':' '{{print $2}}'
9
- mediainfo {} | grep -i duration | head -n 1 | awk -F ':' '{{print $2}}'
10
- mediainfo {} | grep -i "bit rate" | head -n 1 | awk -F ':' '{{print $2}}'
11
- mediainfo {} | grep -i "frame rate" | head -n 1 | awk -F ':' '{{print $2}}'
12
-
13
- --- Header
14
- file
15
- formatkajsdflkjasfdjkasfdj
16
- height
17
- width
18
- duration
19
- bitrate
20
- framerate0123456789
@@ -1,15 +0,0 @@
1
- [environment]
2
- cwd="~/"
3
-
4
- [data]
5
- files_command = "eza -1 --no-quotes -a"
6
-
7
- commands = [
8
- """eza -1 --no-quotes -a""",
9
- """eza -1 /nosuchdirectory""",
10
- ]
11
-
12
- header = [
13
- "file",
14
- "empty column"
15
- ]
@@ -1,15 +0,0 @@
1
- [environment]
2
- cwd="~/Videos"
3
-
4
- [data]
5
- files_command = "eza -1 --no-quotes | grep zzz"
6
-
7
- commands = [
8
- """eza -1 --no-quotes | grep zzz""",
9
- """eza -1 /nosuchdirectory""",
10
- ]
11
-
12
- header = [
13
- "file",
14
- "empty column"
15
- ]
@@ -1,45 +0,0 @@
1
- import sys, os
2
- from listpick.listpick_app import Picker, close_curses, start_curses
3
-
4
- l = [["The", "many"], ["words", "of"]]
5
- l += [["the", "Athenians"], ["I", "do"]]
6
- l += [["not", "understand."], ["They", "said"]]
7
- l += [["a great", "deal"], ["in praise", "of themselves"]]
8
- l += [["but", "nowhere"], ["denied", "that they"]]
9
- l += [["are", "injuring"], ["our", "allies"]]
10
-
11
- header=["Pericles", "Is Dead"]
12
-
13
- highlights = [
14
- {
15
- "match": "praise",
16
- "field": 0,
17
- "color": 8,
18
- },
19
- {
20
- "match": "theni..",
21
- "field": 1,
22
- "color": 9,
23
- },
24
- {
25
- "match": ".*",
26
- "row": 5,
27
- "field": 1,
28
- "color": 11,
29
- },
30
- ]
31
-
32
- stdscr = start_curses()
33
- x = Picker(
34
- stdscr = stdscr,
35
- items = l,
36
- title="Test Picker",
37
- header=header,
38
- highlights=highlights,
39
- )
40
- selected_indices, opts, picker_data = x.run()
41
-
42
- close_curses(stdscr)
43
-
44
- print(f"Selected: {selected_indices}")
45
- print(f"Opts: {opts}")
@@ -1,26 +0,0 @@
1
- [metadata]
2
- name = listpick
3
- version = 0.1.4
4
- author = Grim
5
- author_email = grimandgreedy@protonmail.com
6
- description = Listpick is a powerful TUI data tool for creating TUI apps or viewing/comparing tabulated data.
7
- ; long_description = file: README.md, LICENSE.txt
8
- long_description = file: README.md
9
- long_description_content_type = text/markdown
10
- url = https://github.com/grimandgreedy/listpick
11
- project_urls =
12
- Bug Tracker = https://github.com/grimandgreedy/listpick/issues
13
- repository = https://github.com/grimandgreedy/listpick
14
- classifiers =
15
- Programming Language :: Python :: 3
16
- ; License :: OSI Approved :: MIT License
17
- Operating System :: Unix
18
-
19
- [options]
20
- package_dir =
21
- = src
22
- packages = find:
23
- python_requires = >=3.1
24
-
25
- [options.packages.find]
26
- where = src
@@ -1,19 +0,0 @@
1
- from listpick.listpick_app import Picker, close_curses, start_curses
2
-
3
- l = [["1", "2"], ["3", "4"]]
4
- header=["Pericles", "Is Dead"]
5
-
6
- stdscr = start_curses()
7
- x = Picker(
8
- stdscr = stdscr,
9
- items = l,
10
- title="Test Picker",
11
- header=header,
12
- highlights=highlights,
13
- )
14
- selected_indices, opts, picker_data = x.run()
15
-
16
- close_curses(stdscr)
17
-
18
- print(f"Selected: {selected_indices}")
19
- print(f"Opts: {opts}")
@@ -1,27 +0,0 @@
1
- [environment]
2
- cwd="~/Downloads/new/"
3
-
4
- [data]
5
- files_command = "eza -1 --no-quotes | grep mp4"
6
-
7
- commands = [
8
- """eza -1 --no-quotes | grep mp4""",
9
- # """sha1sum {} | awk '{{print $1}}'""",
10
- """ffprobe -show_entries format=duration -v quiet -of csv="p=0" -i {}""",
11
- """ffprobe -v error -select_streams v:0 -show_entries stream=width -of csv=s=x:p=0 {}""",
12
- """ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=s=x:p=0 {}"""
13
- """du -hs {} | awk '{{ print $1 }}'"""
14
- ]
15
-
16
- header = [
17
- "file",
18
- # "sha1",
19
- "duration",
20
- "width",
21
- "height",
22
- "size",
23
- ]
24
-
25
- run = [
26
- "mpv",
27
- ]
@@ -1,202 +0,0 @@
1
- """
2
- footer.py
3
- Lines to be displayed on the help screen.
4
-
5
- Author: GrimAndGreedy
6
- License: MIT
7
- """
8
-
9
- import curses
10
- import logging
11
-
12
- logger = logging.getLogger('picker_log')
13
-
14
- class Footer:
15
- def __init__(self, stdscr, colours_start, get_state_function):
16
- """
17
- stdscr: curses screen object
18
- colours_start: base colour pair index
19
- get_state_callback: function that returns a dict with all required data for rendering
20
- """
21
- self.stdscr = stdscr
22
- self.colours_start = colours_start
23
- self.get_state = get_state_function
24
- self.height = 0
25
-
26
- def draw(self, h, w):
27
- """
28
- Draw the footer. Must be implemented by subclasses.
29
- """
30
- raise NotImplementedError
31
-
32
- class StandardFooter(Footer):
33
- def __init__(self, stdscr, colours_start, get_state_function):
34
- """
35
- stdscr: curses screen object
36
- colours_start: base colour pair index
37
- get_state_callback: function that returns a dict with all required data for rendering
38
- """
39
- self.stdscr = stdscr
40
- self.colours_start = colours_start
41
- self.get_state = get_state_function
42
- self.height = 3
43
- def draw(self, h, w):
44
- state = self.get_state()
45
-
46
- # Fill background
47
- for i in range(3):
48
- self.stdscr.addstr(h-3+i, 0, ' '*(w-1), curses.color_pair(self.colours_start+20))
49
-
50
- if state["filter_query"]:
51
- self.stdscr.addstr(h - 2, 2, f" Filter: {state['filter_query']} "[:w-40], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
52
- if state["search_query"]:
53
- self.stdscr.addstr(h - 3, 2, f" Search: {state['search_query']} [{state['search_index']}/{state['search_count']}] "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
54
- if state["user_opts"]:
55
- self.stdscr.addstr(h - 1, 2, f" Opts: {state['user_opts']} "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
56
-
57
- # Sort info
58
- sort_column_info = f"{state['sort_column'] if state['sort_column'] is not None else 'None'}"
59
- sort_method_info = f"{state['SORT_METHODS'][state['columns_sort_method'][state['sort_column']]]}" if state['sort_column'] is not None else "NA"
60
- sort_order_info = "Desc." if state["sort_reverse"] else "Asc."
61
- sort_order_info = "▼" if state["sort_reverse"][state['sort_column']] else "▲"
62
- sort_disp_str = f" Sort: ({sort_column_info}, {sort_method_info}, {sort_order_info}) "
63
- self.stdscr.addstr(h - 2, w-35, f"{sort_disp_str:>34}", curses.color_pair(self.colours_start+20))
64
-
65
- if state["footer_string"]:
66
- # footer_string_width = min(w-1, max(len(state["footer_string"]), 50))
67
- # disp_string = f"{state['footer_string'][:footer_string_width]:>{footer_string_width-1}} "
68
- # self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
69
- # self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
70
-
71
- # disp_string = f"{footer_string:>{footer_string_width-1}} "
72
- # self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
73
- # self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
74
-
75
- footer_string_width = min(w-1, len(state["footer_string"])+2)
76
-
77
- disp_string = f"{state["footer_string"][:footer_string_width]}"
78
- disp_string = f" {disp_string:>{footer_string_width-2}} "
79
- self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
80
- self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
81
-
82
-
83
- else:
84
- select_mode = "Cursor"
85
- if state["is_selecting"]: select_mode = "Visual Selection"
86
- elif state["is_deselecting"]: select_mode = "Visual deselection"
87
- self.stdscr.addstr(h - 1, w-35, f"{select_mode:>33} ", curses.color_pair(self.colours_start+20))
88
-
89
- # Cursor & selection info
90
- selected_count = sum(state["selections"].values())
91
- if state["paginate"]:
92
- cursor_disp_str = f" {state['cursor_pos']+1}/{len(state['indexed_items'])} Page {state['cursor_pos']//state['items_per_page']}/{len(state['indexed_items'])} Selected {selected_count}"
93
- else:
94
- cursor_disp_str = f" {state['cursor_pos']+1}/{len(state['indexed_items'])} | Selected {selected_count}"
95
- self.stdscr.addstr(h - 3, w-35, f"{cursor_disp_str:>33} ", curses.color_pair(self.colours_start+20))
96
-
97
- self.stdscr.refresh()
98
-
99
-
100
-
101
- class CompactFooter(Footer):
102
- def __init__(self, stdscr, colours_start, get_state_function):
103
- """
104
- stdscr: curses screen object
105
- colours_start: base colour pair index
106
- get_state_callback: function that returns a dict with all required data for rendering
107
- """
108
- self.stdscr = stdscr
109
- self.colours_start = colours_start
110
- self.get_state = get_state_function
111
- self.height = 1
112
-
113
- def draw(self, h, w):
114
- state = self.get_state()
115
-
116
- # Fill background
117
- if state["search_query"]: self.height = 3
118
- elif state["filter_query"]: self.height = 2
119
- elif state["user_opts"]: self.height = 1
120
- elif state["footer_string"]: self.height = 2
121
- else: self.height = 1
122
- for i in range(self.height):
123
- self.stdscr.addstr(h-(i+1), 0, ' '*(w-1), curses.color_pair(self.colours_start+20))
124
-
125
- if state["user_opts"]:
126
- self.stdscr.addstr(h - 1, 2, f" Opts: {state['user_opts']} "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
127
- if state["filter_query"]:
128
- self.stdscr.addstr(h - 2, 2, f" Filter: {state['filter_query']} "[:w-40], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
129
- if state["search_query"]:
130
- self.stdscr.addstr(h - 3, 2, f" Search: {state['search_query']} [{state['search_index']}/{state['search_count']}] "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
131
-
132
- right_width = 40
133
- # Sort info
134
- sort_column_info = f"{state['sort_column'] if state['sort_column'] is not None else 'None'}"
135
- sort_method_info = f"{state['SORT_METHODS'][state['columns_sort_method'][state['sort_column']]]}" if state['sort_column'] is not None else "NA"
136
- sort_order_info = "Desc." if state["sort_reverse"][state['sort_column']] else "Asc."
137
- sort_order_info = "▼" if state["sort_reverse"][state['sort_column']] else "▲"
138
- sort_disp_str = f" ({sort_column_info}, {sort_method_info}, {sort_order_info}) "
139
- # self.stdscr.addstr(h - 2, w-right_width, f"{sort_disp_str:>{right_width-1}}", curses.color_pair(self.colours_start+20))
140
-
141
- if state["footer_string"]:
142
- footer_string_width = min(w-1, len(state["footer_string"])+2)
143
-
144
- disp_string = f"{state["footer_string"][:footer_string_width]}"
145
- disp_string = f" {disp_string:>{footer_string_width-2}} "
146
- self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
147
- self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
148
- selected_count = sum(state["selections"].values())
149
- if state["paginate"]:
150
- cursor_disp_str = f" {state['cursor_pos']+1}/{len(state['indexed_items'])} Page {state['cursor_pos']//state['items_per_page']}/{len(state['indexed_items'])} Selected {selected_count}"
151
- else:
152
- cursor_disp_str = f"{sort_disp_str} [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])}"
153
- self.stdscr.addstr(h-2, w-right_width, f"{cursor_disp_str:>{right_width-2}}"[:right_width-1], curses.color_pair(self.colours_start+20))
154
- else:
155
- # Cursor & selection info
156
- selected_count = sum(state["selections"].values())
157
- if state["paginate"]:
158
- cursor_disp_str = f" {state['cursor_pos']+1}/{len(state['indexed_items'])} Page {state['cursor_pos']//state['items_per_page']}/{len(state['indexed_items'])} Selected {selected_count}"
159
- else:
160
- cursor_disp_str = f"{sort_disp_str} [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])}"
161
- self.stdscr.addstr(h - 1, w-right_width, f"{cursor_disp_str:>{right_width-2}}"[:right_width-1], curses.color_pair(self.colours_start+20))
162
-
163
- self.stdscr.refresh()
164
-
165
- class NoFooter(Footer):
166
- def __init__(self, stdscr, colours_start, get_state_function):
167
- """
168
- stdscr: curses screen object
169
- colours_start: base colour pair index
170
- get_state_callback: function that returns a dict with all required data for rendering
171
- """
172
- self.stdscr = stdscr
173
- self.colours_start = colours_start
174
- self.get_state = get_state_function
175
- self.height = 0
176
- def draw(self, h, w):
177
- state = self.get_state()
178
-
179
- if state["search_query"]: self.height = 3
180
- elif state["filter_query"]: self.height = 2
181
- elif state["user_opts"]: self.height = 1
182
- elif state["footer_string"]: self.height = 1
183
- else: self.height = 0
184
-
185
- for i in range(self.height):
186
- self.stdscr.addstr(h-(i+1), 0, ' '*(w-1), curses.color_pair(self.colours_start+20))
187
-
188
- if state["user_opts"]:
189
- self.stdscr.addstr(h - 1, 2, f" Opts: {state['user_opts']} "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
190
- if state["filter_query"]:
191
- self.stdscr.addstr(h - 2, 2, f" Filter: {state['filter_query']} "[:w-40], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
192
- if state["search_query"]:
193
- self.stdscr.addstr(h - 3, 2, f" Search: {state['search_query']} [{state['search_index']}/{state['search_count']}] "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
194
- self.height = 3
195
-
196
-
197
- if state["footer_string"]:
198
- footer_string_width = min(w-1, len(state["footer_string"])+2)
199
- disp_string = f"{state["footer_string"][:footer_string_width]}"
200
- disp_string = f" {disp_string:>{footer_string_width-2}} "
201
- self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
202
- self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
File without changes
File without changes
File without changes
File without changes
File without changes