listpick 0.1.14.14__py3-none-any.whl → 0.1.15.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

listpick/listpick_app.py CHANGED
@@ -41,6 +41,8 @@ from listpick.ui.build_help import build_help_rows
41
41
  from listpick.ui.footer import StandardFooter, CompactFooter, NoFooter
42
42
  from listpick.utils.picker_log import setup_logger
43
43
  from listpick.utils.user_input import get_char, open_tty
44
+ from listpick.pane.pane_functions import right_split_file_attributes, right_split_graph, right_split_display_list
45
+ from listpick.pane.get_data import *
44
46
 
45
47
 
46
48
  try:
@@ -62,7 +64,7 @@ class Picker:
62
64
  items: list[list[str]] = [],
63
65
  cursor_pos: int = 0,
64
66
  colours: dict = get_colours(0),
65
- colour_theme_number: int = 0,
67
+ colour_theme_number: int = 3,
66
68
  max_selected: int = -1,
67
69
  top_gap: int =0,
68
70
  title: str ="Picker",
@@ -186,7 +188,17 @@ class Picker:
186
188
  sheets = ["Untitled"],
187
189
  sheet_name = "Untitled",
188
190
  sheet_index = 0,
189
- sheet_states = [{}],
191
+ sheet_states: list = [{}],
192
+
193
+ split_right: bool = False,
194
+ split_right_proportion: float = 1/2,
195
+ split_right_function: Callable = lambda stdscr, x, y, w, h, state, row, cell, data, test: False,
196
+ split_right_auto_refresh: bool = False,
197
+ split_right_refresh_data: Callable = lambda old_data, arg_dict: [],
198
+ split_right_refresh_data_timer: float = 1.0,
199
+ split_right_data: list = [],
200
+
201
+
190
202
 
191
203
 
192
204
  ):
@@ -337,6 +349,14 @@ class Picker:
337
349
  self.sheet_states = sheet_states
338
350
  self.sheets = sheets
339
351
 
352
+ self.split_right = split_right
353
+ self.split_right_proportion = split_right_proportion
354
+ self.split_right_function = split_right_function
355
+ self.split_right_auto_refresh = split_right_auto_refresh
356
+ self.split_right_refresh_data = split_right_refresh_data
357
+ self.split_right_refresh_data_timer = split_right_refresh_data_timer
358
+ self.split_right_data = split_right_data
359
+
340
360
  self.initialise_picker_state(reset_colours=self.reset_colours)
341
361
 
342
362
  # Note: We have to set the footer after initialising the picker state so that the footer can use the get_function_data method
@@ -405,20 +425,26 @@ class Picker:
405
425
 
406
426
  ## self.top_space
407
427
  h, w = self.stdscr.getmaxyx()
428
+ self.term_h, self.term_w = self.stdscr.getmaxyx()
429
+ if self.split_right and self.split_right_function(self.stdscr, 0,0,0,0,{},[],[],"",test=True):
430
+ self.rows_w, self.rows_h = int(self.term_w*self.split_right_proportion), self.term_h
431
+ else:
432
+ self.rows_w, self.rows_h = self.term_w, self.term_h
433
+
408
434
  self.top_space = self.top_gap
409
435
  if self.title: self.top_space+=1
410
436
  if self.modes and self.display_modes: self.top_space+=1
411
437
  if self.header and self.show_header: self.top_space += 1
412
438
 
413
439
  # self.items_per_page
414
- self.items_per_page = h - self.top_space - self.bottom_space
440
+ self.items_per_page = self.term_h - self.top_space - self.bottom_space
415
441
  if not self.show_footer and self.footer_string: self.items_per_page-=1
416
- self.items_per_page = min(h-self.top_space-1, self.items_per_page)
442
+ self.items_per_page = min(self.term_h-self.top_space-1, self.items_per_page)
417
443
 
418
444
 
419
445
  # Adjust top space if centring vertically and we have fewer rows than terminal lines
420
446
  if self.centre_in_terminal_vertical and len(self.indexed_items) < self.items_per_page:
421
- self.top_space += ((h-(self.top_space+self.bottom_space))-len(self.indexed_items))//2
447
+ self.top_space += ((self.term_h-(self.top_space+self.bottom_space))-len(self.indexed_items))//2
422
448
 
423
449
  # self.column_widths
424
450
  visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
@@ -427,8 +453,8 @@ class Picker:
427
453
  # self.startx
428
454
  self.startx = 1 if self.highlight_full_row else 2
429
455
  if self.show_row_header: self.startx += len(str(len(self.items))) + 2
430
- if visible_columns_total_width < w and self.centre_in_terminal:
431
- self.startx += (w - visible_columns_total_width) // 2
456
+ if visible_columns_total_width < self.rows_w and self.centre_in_terminal:
457
+ self.startx += (self.rows_w - visible_columns_total_width) // 2
432
458
 
433
459
  def get_visible_rows(self) -> list[list[str]]:
434
460
 
@@ -614,7 +640,6 @@ class Picker:
614
640
  if len(self.indexed_items) > 0:
615
641
  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
616
642
 
617
- h, w = self.stdscr.getmaxyx()
618
643
 
619
644
  # Adjust variables to ensure correctness if errors
620
645
  ## Move to a selectable row (if applicable)
@@ -761,8 +786,14 @@ class Picker:
761
786
  self.stdscr.erase()
762
787
 
763
788
  h, w = self.stdscr.getmaxyx()
789
+ self.term_h, self.term_w = self.stdscr.getmaxyx()
790
+ if self.split_right and self.split_right_function(self.stdscr, 0,0,0,0,{},[],[],"",test=True):
791
+ self.rows_w, self.rows_h = int(self.term_w*self.split_right_proportion), self.term_h
792
+ else:
793
+ self.rows_w, self.rows_h = self.term_w, self.term_h
794
+
764
795
  # The height of the footer may need to be adjusted if the file changes.
765
- self.footer.adjust_sizes(h,w)
796
+ self.footer.adjust_sizes(self.term_h,self.term_w)
766
797
  self.calculate_section_sizes()
767
798
 
768
799
  # Test if the terminal is of a sufficient size to display the picker
@@ -785,7 +816,7 @@ class Picker:
785
816
  # rows = [v[1] for v in self.indexed_items] if len(self.indexed_items) else self.items
786
817
  # Determine widths based only on the currently displayed indexed rows
787
818
  rows = [v[1] for v in self.indexed_items[start_index:end_index]] if len(self.indexed_items) else self.items
788
- self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w, unicode_char_width=self.unicode_char_width)
819
+ self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=self.rows_w, unicode_char_width=self.unicode_char_width)
789
820
  visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
790
821
  visible_columns_total_width = sum(visible_column_widths) + len(self.separator)*(len(visible_column_widths)-1)
791
822
 
@@ -797,23 +828,22 @@ class Picker:
797
828
  ## Display title (if applicable)
798
829
  if self.title:
799
830
  padded_title = f" {self.title.strip()} "
800
- self.stdscr.addstr(self.top_gap, 0, f"{' ':^{w}}", curses.color_pair(self.colours_start+16))
801
- title_x = (w-wcswidth(padded_title))//2
802
- # title = f"{title:^{w}}"
831
+ self.stdscr.addstr(self.top_gap, 0, f"{' ':^{self.term_w}}", curses.color_pair(self.colours_start+16))
832
+ title_x = (self.term_w-wcswidth(padded_title))//2
803
833
  self.stdscr.addstr(self.top_gap, title_x, padded_title, curses.color_pair(self.colours_start+16) | curses.A_BOLD)
804
834
  # top_space += 1
805
835
 
806
836
  ## Display modes
807
837
  if self.display_modes and self.modes not in [[{}], []]:
808
- self.stdscr.addstr(self.top_gap+1, 0, ' '*w, curses.A_REVERSE)
838
+ self.stdscr.addstr(self.top_gap+1, 0, ' '*self.term_w, curses.A_REVERSE)
809
839
  modes_list = [f"{mode['name']}" if 'name' in mode else f"{i}. " for i, mode in enumerate(self.modes)]
810
840
  # mode_colours = [mode["colour"] for mode ]
811
841
  mode_widths = get_mode_widths(modes_list)
812
- split_space = (w-sum(mode_widths))//len(self.modes)
842
+ split_space = (self.term_w-sum(mode_widths))//len(self.modes)
813
843
  xmode = 0
814
844
  for i, mode in enumerate(modes_list):
815
845
  if i == len(modes_list)-1:
816
- mode_str = f"{mode:^{mode_widths[i]+split_space+(w-sum(mode_widths))%len(self.modes)}}"
846
+ mode_str = f"{mode:^{mode_widths[i]+split_space+(self.term_w-sum(mode_widths))%len(self.modes)}}"
817
847
  else:
818
848
  mode_str = f"{mode:^{mode_widths[i]+split_space}}"
819
849
  # current mode
@@ -841,21 +871,19 @@ class Picker:
841
871
 
842
872
  header_str = header_str[self.leftmost_char:]
843
873
  header_ypos = self.top_gap + bool(self.title) + bool(self.display_modes and self.modes)
844
- self.stdscr.addstr(header_ypos, 0, ' '*w, curses.color_pair(self.colours_start+4) | curses.A_BOLD)
845
- self.stdscr.addstr(header_ypos, self.startx, header_str[:min(w-self.startx, visible_columns_total_width+1)], curses.color_pair(self.colours_start+4) | curses.A_BOLD)
874
+ self.stdscr.addstr(header_ypos, 0, ' '*self.rows_w, curses.color_pair(self.colours_start+4) | curses.A_BOLD)
875
+ self.stdscr.addstr(header_ypos, self.startx, header_str[:min(self.rows_w-self.startx, visible_columns_total_width+1)], curses.color_pair(self.colours_start+4) | curses.A_BOLD)
846
876
 
847
877
  # Highlight sort column
848
878
  try:
849
879
  if self.selected_column != None and self.selected_column not in self.hidden_columns:
850
- if len(self.header) > 1 and (len(up_to_selected_col)-self.leftmost_char) < w:
851
- # if len(up_to_selected_col) + 1 < w or True:
852
- # if self.startx + len(up_to_selected_col) - self.leftmost_char > 0 or True:
880
+ if len(self.header) > 1 and (len(up_to_selected_col)-self.leftmost_char) < self.rows_w:
853
881
  number = f"{self.selected_column}. " if self.number_columns else ""
854
882
  # number = f"{intStringToExponentString(self.selected_column)}. " if self.number_columns else ""
855
883
  # self.startx + len(up_to_selected_col) - self.leftmost_char
856
884
  highlighed_col_startx = max(self.startx, self.startx + len(up_to_selected_col) - self.leftmost_char)
857
885
  highlighted_col_str = (number+f"{self.header[self.selected_column]:^{self.column_widths[self.selected_column]-len(number)}}") + self.separator
858
- end_of_highlighted_col_str = w-(highlighed_col_startx+len(highlighted_col_str)) if (highlighed_col_startx+len(highlighted_col_str)) > w else len(highlighted_col_str)
886
+ end_of_highlighted_col_str = self.rows_w-(highlighed_col_startx+len(highlighted_col_str)) if (highlighed_col_startx+len(highlighted_col_str)) > self.rows_w else len(highlighted_col_str)
859
887
  start_of_highlighted_col_str = max(self.leftmost_char - len(up_to_selected_col), 0)
860
888
  self.stdscr.addstr(header_ypos, highlighed_col_startx , highlighted_col_str[start_of_highlighted_col_str:end_of_highlighted_col_str], curses.color_pair(self.colours_start+19) | curses.A_BOLD)
861
889
  except:
@@ -874,10 +902,10 @@ class Picker:
874
902
  cell_pos = sum(visible_column_widths[:col])+col*len(self.separator)-self.leftmost_char + self.startx
875
903
  # cell_width = self.column_widths[self.selected_column]
876
904
  cell_width = visible_column_widths[col] + len(self.separator)
877
- cell_max_width = w-cell_pos
905
+ cell_max_width = self.rows_w-cell_pos
878
906
  try:
879
907
  # Start of cell is on screen
880
- if self.startx <= cell_pos <= w:
908
+ if self.startx <= cell_pos <= self.rows_w:
881
909
  self.stdscr.addstr(y, cell_pos, (' '*cell_width)[:cell_max_width], curses.color_pair(self.colours_start+colour_pair_number))
882
910
  if self.centre_in_cols:
883
911
  cell_value = f"{self.indexed_items[row][1][col]:^{cell_width-len(self.separator)}}" + self.separator
@@ -888,10 +916,9 @@ class Picker:
888
916
  cell_value = cell_value + self.separator
889
917
  # cell_value = cell_value
890
918
  cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.centre_in_cols, self.unicode_char_width)
891
- # row_str = truncate_to_display_width(row_str_left_adj, min(w-self.startx, visible_columns_total_width))
892
919
  self.stdscr.addstr(y, cell_pos, cell_value, curses.color_pair(self.colours_start+colour_pair_number) | curses.A_BOLD)
893
920
  # Part of the cell is on screen
894
- elif self.startx <= cell_pos+cell_width <= w:
921
+ elif self.startx <= cell_pos+cell_width <= self.rows_w:
895
922
  cell_start = self.startx - cell_pos
896
923
  self.stdscr.addstr(y, self.startx, ' '*(cell_width-cell_start), curses.color_pair(self.colours_start+colour_pair_number))
897
924
  cell_value = self.indexed_items[row][1][col][cell_start:visible_column_widths[col]]
@@ -954,7 +981,7 @@ class Picker:
954
981
  continue
955
982
  highlight_start -= self.leftmost_char
956
983
  highlight_end -= self.leftmost_char
957
- self.stdscr.addstr(y, max(self.startx, self.startx+highlight_start), row_str[max(highlight_start,0):min(w-self.startx, highlight_end)], curses.color_pair(self.colours_start+highlight["color"]) | curses.A_BOLD)
984
+ self.stdscr.addstr(y, max(self.startx, self.startx+highlight_start), row_str[max(highlight_start,0):min(self.rows_w-self.startx, highlight_end)], curses.color_pair(self.colours_start+highlight["color"]) | curses.A_BOLD)
958
985
  except:
959
986
  pass
960
987
 
@@ -978,11 +1005,11 @@ class Picker:
978
1005
  # row_str = truncate_to_display_width(row_str, min(w-self.startx, visible_columns_total_width))
979
1006
  row_str_orig = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols, self.unicode_char_width)
980
1007
  row_str_left_adj = clip_left(row_str_orig, self.leftmost_char)
981
- row_str = truncate_to_display_width(row_str_left_adj, min(w-self.startx, visible_columns_total_width), self.unicode_char_width)
1008
+ row_str = truncate_to_display_width(row_str_left_adj, min(self.rows_w-self.startx, visible_columns_total_width), self.unicode_char_width)
982
1009
  # row_str = truncate_to_display_width(row_str, min(w-self.startx, visible_columns_total_width))[self.leftmost_char:]
983
1010
 
984
1011
  ## Display the standard row
985
- self.stdscr.addstr(y, self.startx, row_str[:min(w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+2))
1012
+ self.stdscr.addstr(y, self.startx, row_str[:min(self.rows_w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+2))
986
1013
 
987
1014
 
988
1015
  # Draw the level 0 highlights
@@ -1012,15 +1039,15 @@ class Picker:
1012
1039
  # Higlight cursor row and selected rows
1013
1040
  elif self.highlight_full_row:
1014
1041
  if self.selections[item[0]]:
1015
- self.stdscr.addstr(y, self.startx, row_str[:min(w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+25) | curses.A_BOLD)
1042
+ self.stdscr.addstr(y, self.startx, row_str[:min(self.rows_w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+25) | curses.A_BOLD)
1016
1043
  # Visually selected
1017
1044
  if self.is_selecting:
1018
1045
  if self.start_selection <= idx <= self.cursor_pos or self.start_selection >= idx >= self.cursor_pos:
1019
- self.stdscr.addstr(y, self.startx, row_str[:min(w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+25))
1046
+ self.stdscr.addstr(y, self.startx, row_str[:min(self.rows_w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+25))
1020
1047
  # Visually deslected
1021
1048
  elif self.is_deselecting:
1022
1049
  if self.start_selection >= idx >= self.cursor_pos or self.start_selection <= idx <= self.cursor_pos:
1023
- self.stdscr.addstr(y, self.startx, row_str[:min(w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+26))
1050
+ self.stdscr.addstr(y, self.startx, row_str[:min(self.rows_w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+26))
1024
1051
 
1025
1052
  # Highlight the cursor row and the first char of the selected rows.
1026
1053
  else:
@@ -1043,7 +1070,7 @@ class Picker:
1043
1070
  if self.cell_cursor:
1044
1071
  highlight_cell(idx, self.selected_column, visible_column_widths)
1045
1072
  else:
1046
- self.stdscr.addstr(y, self.startx, row_str[:min(w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+5) | curses.A_BOLD)
1073
+ self.stdscr.addstr(y, self.startx, row_str[:min(self.rows_w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+5) | curses.A_BOLD)
1047
1074
 
1048
1075
  if not self.highlights_hide:
1049
1076
  draw_highlights(l2_highlights, idx, y, item)
@@ -1054,36 +1081,49 @@ class Picker:
1054
1081
  if self.cursor_pos <= self.items_per_page//2:
1055
1082
  scroll_bar_start=self.top_space
1056
1083
  elif self.cursor_pos + self.items_per_page//2 >= len(self.indexed_items):
1057
- scroll_bar_start = h - int(bool(self.show_footer))*self.footer.height - scroll_bar_length
1084
+ scroll_bar_start = self.term_h - int(bool(self.show_footer))*self.footer.height - scroll_bar_length
1058
1085
  else:
1059
1086
  scroll_bar_start = int(((self.cursor_pos)/len(self.indexed_items))*self.items_per_page)+self.top_space - scroll_bar_length//2
1060
- scroll_bar_start = min(scroll_bar_start, h-self.top_space-1)
1061
- scroll_bar_length = min(scroll_bar_length, h - scroll_bar_start-1)
1087
+ scroll_bar_start = min(scroll_bar_start, self.term_h-self.top_space-1)
1088
+ scroll_bar_length = min(scroll_bar_length, self.term_h - scroll_bar_start-1)
1062
1089
  scroll_bar_length = max(1, scroll_bar_length)
1063
1090
  for i in range(scroll_bar_length):
1064
1091
  v = max(self.top_space+int(bool(self.header)), scroll_bar_start-scroll_bar_length//2)
1065
- self.stdscr.addstr(scroll_bar_start+i, w-1, ' ', curses.color_pair(self.colours_start+18))
1092
+ self.stdscr.addstr(scroll_bar_start+i, self.rows_w-1, ' ', curses.color_pair(self.colours_start+18))
1066
1093
 
1067
1094
  # Display refresh symbol
1068
1095
  if self.auto_refresh:
1069
1096
  if self.refreshing_data:
1070
- self.stdscr.addstr(0,w-3,"  ", curses.color_pair(self.colours_start+21) | curses.A_BOLD)
1097
+ self.stdscr.addstr(0,self.term_w-3,"  ", curses.color_pair(self.colours_start+21) | curses.A_BOLD)
1071
1098
  else:
1072
- self.stdscr.addstr(0,w-3,"  ", curses.color_pair(self.colours_start+23) | curses.A_BOLD)
1099
+ self.stdscr.addstr(0,self.term_w-3,"  ", curses.color_pair(self.colours_start+23) | curses.A_BOLD)
1073
1100
 
1074
1101
  ## Display footer
1075
1102
  if self.show_footer:
1076
1103
  # self.footer = NoFooter(self.stdscr, self.colours_start, self.get_function_data)
1077
1104
  h, w = self.stdscr.getmaxyx()
1078
1105
  try:
1079
- self.footer.draw(h, w)
1106
+ self.footer.draw(self.term_h, self.term_w)
1080
1107
  except:
1081
1108
  pass
1082
1109
  elif self.footer_string:
1083
- footer_string_width = min(w-1, len(self.footer_string)+2)
1110
+ footer_string_width = min(self.term_w-1, len(self.footer_string)+2)
1084
1111
  disp_string = f" {self.footer_string[:footer_string_width]:>{footer_string_width-2}} "
1085
- self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
1086
- self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
1112
+ self.stdscr.addstr(self.term_h - 1, self.term_w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
1113
+ self.stdscr.addstr(self.term_h - 1, self.term_w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
1114
+
1115
+ if self.split_right and self.split_right_function(self.stdscr, 0,0,0,0,{},[],[],"",test=True):
1116
+ self.right_pane_previous_data = self.split_right_function(
1117
+ self.stdscr,
1118
+ x = self.rows_w,
1119
+ y = self.top_space - int(bool(self.show_header and self.header)),
1120
+ w = self.term_w-self.rows_w,
1121
+ h = self.items_per_page + int(bool(self.show_header and self.header)),
1122
+ state = self.get_function_data(),
1123
+ row = self.indexed_items[self.cursor_pos] if self.indexed_items else [],
1124
+ cell = self.indexed_items[self.cursor_pos][1][self.selected_column] if self.indexed_items else "",
1125
+ data=self.split_right_data,
1126
+ )
1087
1127
 
1088
1128
  self.stdscr.refresh()
1089
1129
  ## Display infobox
@@ -1130,6 +1170,7 @@ class Picker:
1130
1170
  "title": title,
1131
1171
  "reset_colours": False,
1132
1172
  "cell_cursor": False,
1173
+ "split_right": False,
1133
1174
  }
1134
1175
 
1135
1176
  OptionPicker = Picker(submenu_win, **infobox_data)
@@ -1244,6 +1285,13 @@ class Picker:
1244
1285
  "sheets": self.sheets,
1245
1286
  "sheet_name": self.sheet_name,
1246
1287
  "sheet_states": self.sheet_states,
1288
+ "split_right": self.split_right,
1289
+ "split_right_proportion": self.split_right_proportion,
1290
+ "split_right_function": self.split_right_function,
1291
+ "split_right_auto_refresh": self.split_right_auto_refresh,
1292
+ "split_right_refresh_data_timer": self.split_right_refresh_data_timer,
1293
+ "split_right_refresh_data": self.split_right_refresh_data,
1294
+ "split_right_data": self.split_right_data,
1247
1295
  }
1248
1296
  return function_data
1249
1297
 
@@ -1278,6 +1326,9 @@ class Picker:
1278
1326
  "centre_in_terminal_vertical",
1279
1327
  "centre_in_cols",
1280
1328
  "centre_in_terminal",
1329
+ "split_right",
1330
+ "split_right_proportion",
1331
+ "split_right_function",
1281
1332
  ]
1282
1333
 
1283
1334
  for var in variables:
@@ -1370,6 +1421,7 @@ class Picker:
1370
1421
  "cancel_is_back": True,
1371
1422
  "number_columns": False,
1372
1423
  "reset_colours": False,
1424
+ "split_right": False,
1373
1425
  }
1374
1426
  while True:
1375
1427
  h, w = stdscr.getmaxyx()
@@ -1425,6 +1477,7 @@ class Picker:
1425
1477
  "top_gap": 0,
1426
1478
  "cancel_is_back": True,
1427
1479
  "reset_colours": False,
1480
+ "split_right": False,
1428
1481
 
1429
1482
  }
1430
1483
  OptionPicker = Picker(submenu_win, **notification_data)
@@ -1562,6 +1615,8 @@ class Picker:
1562
1615
  self.footer_style = (self.footer_style+1)%len(self.footer_options)
1563
1616
  self.footer = self.footer_options[self.footer_style]
1564
1617
  self.initialise_variables()
1618
+ elif setting == "pane":
1619
+ self.toggle_right_pane()
1565
1620
 
1566
1621
  elif setting.startswith("cwd="):
1567
1622
  os.chdir(os.path.expandvars(os.path.expanduser(setting[len("cwd="):])))
@@ -1951,7 +2006,6 @@ class Picker:
1951
2006
 
1952
2007
  self.stdscr = tmp
1953
2008
 
1954
- h, w = self.stdscr.getmaxyx()
1955
2009
  self.notification(self.stdscr, f"{repr(file_to_load)} has been loaded!")
1956
2010
 
1957
2011
  self.set_function_data({}, reset_absent_variables=True)
@@ -2177,6 +2231,9 @@ class Picker:
2177
2231
  self.set_function_data(function_data, reset_absent_variables=True)
2178
2232
  self.load_sheet(self.loaded_file, sheet_number=self.sheet_index)
2179
2233
 
2234
+ def toggle_right_pane(self):
2235
+ if self.split_right_function(self.stdscr, 0,0,0,0,{},[],[],"",test=True):
2236
+ self.split_right = not self.split_right
2180
2237
 
2181
2238
 
2182
2239
  def run(self) -> Tuple[list[int], str, dict]:
@@ -2192,6 +2249,7 @@ class Picker:
2192
2249
 
2193
2250
  initial_time = time.time()
2194
2251
  initial_time_footer = time.time()-self.footer_timer
2252
+ initial_split_time = time.time()-self.split_right_refresh_data_timer
2195
2253
 
2196
2254
  if self.startup_notification:
2197
2255
  self.notification(self.stdscr, message=self.startup_notification)
@@ -2226,6 +2284,11 @@ class Picker:
2226
2284
  tty_fd = open_tty()
2227
2285
 
2228
2286
  h, w = self.stdscr.getmaxyx()
2287
+ self.term_h, self.term_w = self.stdscr.getmaxyx()
2288
+ if self.split_right and self.split_right_function(self.stdscr, 0,0,0,0,{},[],[],"",test=True):
2289
+ self.rows_w, self.rows_h = int(self.term_w*self.split_right_proportion), self.term_h
2290
+ else:
2291
+ self.rows_w, self.rows_h = self.term_w, self.term_h
2229
2292
  def terminal_resized(old_w, old_h) -> bool:
2230
2293
  w, h = os.get_terminal_size()
2231
2294
  if old_h != h or old_w != w: return True
@@ -2246,6 +2309,11 @@ class Picker:
2246
2309
  key = curses.KEY_RESIZE
2247
2310
 
2248
2311
  h, w = self.stdscr.getmaxyx()
2312
+ self.term_h, self.term_w = self.stdscr.getmaxyx()
2313
+ if self.split_right and self.split_right_function(self.stdscr, 0,0,0,0,{},[],[],"",test=True):
2314
+ self.rows_w, self.rows_h = int(self.term_w*self.split_right_proportion), self.term_h
2315
+ else:
2316
+ self.rows_w, self.rows_h = self.term_w, self.term_h
2249
2317
 
2250
2318
  if key in self.disabled_keys: continue
2251
2319
  clear_screen=True
@@ -2267,7 +2335,7 @@ class Picker:
2267
2335
 
2268
2336
  elif self.check_key("refresh", key, self.keys_dict) or self.remapped_key(key, curses.KEY_F5, self.key_remappings) or (self.auto_refresh and (time.time() - initial_time) >= self.timer):
2269
2337
  self.logger.debug(f"Get new data (refresh).")
2270
- self.stdscr.addstr(0,w-3,"  ", curses.color_pair(self.colours_start+21) | curses.A_BOLD)
2338
+ self.stdscr.addstr(0,self.term_w-3,"  ", curses.color_pair(self.colours_start+21) | curses.A_BOLD)
2271
2339
  self.stdscr.refresh()
2272
2340
  if self.get_new_data and self.refresh_function:
2273
2341
  self.refreshing_data = True
@@ -2297,6 +2365,9 @@ class Picker:
2297
2365
  self.footer_string = self.footer_string_refresh_function()
2298
2366
  initial_time_footer = time.time()
2299
2367
  self.draw_screen(self.indexed_items, self.highlights)
2368
+ if self.split_right_auto_refresh and ((time.time() - initial_split_time) > self.split_right_refresh_data_timer):
2369
+ self.split_right_data = self.split_right_refresh_data(self.split_right_data, self.get_function_data())
2370
+ initial_split_time = time.time()
2300
2371
 
2301
2372
  if self.check_key("help", key, self.keys_dict):
2302
2373
  self.logger.info(f"key_function help")
@@ -2315,11 +2386,12 @@ class Picker:
2315
2386
  "highlight_full_row": True,
2316
2387
  "top_gap": 0,
2317
2388
  "paginate": self.paginate,
2318
- "centre_in_terminal": True,
2389
+ "centre_in_terminal": False,
2319
2390
  "centre_in_terminal_vertical": True,
2320
2391
  "hidden_columns": [],
2321
2392
  "reset_colours": False,
2322
2393
  "cell_cursor": False,
2394
+ "split_right": False,
2323
2395
 
2324
2396
  }
2325
2397
  OptionPicker = Picker(self.stdscr, **help_data)
@@ -2431,6 +2503,7 @@ class Picker:
2431
2503
  "hidden_columns": [],
2432
2504
  "reset_colours": False,
2433
2505
  "cell_cursor": False,
2506
+ "split_right": False,
2434
2507
 
2435
2508
  }
2436
2509
  OptionPicker = Picker(self.stdscr, **info_data)
@@ -2741,12 +2814,12 @@ class Picker:
2741
2814
 
2742
2815
  ## Scroll with column select
2743
2816
  rows = self.get_visible_rows()
2744
- self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w, unicode_char_width=self.unicode_char_width)
2817
+ self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=self.rows_w, unicode_char_width=self.unicode_char_width)
2745
2818
  visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
2746
2819
  column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
2747
2820
  start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
2748
2821
  end_of_cell = sum(visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
2749
- display_width = w-self.startx
2822
+ display_width = self.rows_w-self.startx
2750
2823
  # If the full column is within the current display then don't do anything
2751
2824
  if start_of_cell >= self.leftmost_char and end_of_cell <= self.leftmost_char + display_width:
2752
2825
  pass
@@ -2768,12 +2841,12 @@ class Picker:
2768
2841
 
2769
2842
  ## Scroll with column select
2770
2843
  rows = self.get_visible_rows()
2771
- self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w, unicode_char_width=self.unicode_char_width)
2844
+ self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=self.rows_w, unicode_char_width=self.unicode_char_width)
2772
2845
  visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
2773
2846
  column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
2774
2847
  start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
2775
2848
  end_of_cell = sum(visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
2776
- display_width = w-self.startx
2849
+ display_width = self.rows_w-self.startx
2777
2850
 
2778
2851
  # If the entire column is within the current display then don't do anything
2779
2852
  if start_of_cell >= self.leftmost_char and end_of_cell <= self.leftmost_char + display_width:
@@ -2788,7 +2861,7 @@ class Picker:
2788
2861
  self.logger.info(f"key_function scroll_right")
2789
2862
  if len(self.indexed_items):
2790
2863
  row_width = sum(self.column_widths) + len(self.separator)*(len(self.column_widths)-1)
2791
- if row_width-self.leftmost_char >= w-self.startx:
2864
+ if row_width-self.leftmost_char >= self.rows_w-self.startx:
2792
2865
  self.leftmost_char = self.leftmost_char+5
2793
2866
 
2794
2867
  elif self.check_key("scroll_left", key, self.keys_dict):
@@ -2813,7 +2886,7 @@ class Picker:
2813
2886
  # row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)
2814
2887
  # if len(row_str) > longest_row_str_len: longest_row_str_len=len(row_str)
2815
2888
  # self.notification(self.stdscr, f"{longest_row_str_len}")
2816
- self.leftmost_char = max(0, longest_row_str_len-w+2+self.startx)
2889
+ self.leftmost_char = max(0, longest_row_str_len-self.rows_w+2+self.startx)
2817
2890
  if len(self.items):
2818
2891
  self.selected_column = len(self.items[0])-1
2819
2892
 
@@ -2907,7 +2980,7 @@ class Picker:
2907
2980
  elif key == curses.KEY_RESIZE: # Terminal resize signal
2908
2981
 
2909
2982
  self.calculate_section_sizes()
2910
- self.column_widths = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w, unicode_char_width=self.unicode_char_width)
2983
+ self.column_widths = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=self.rows_w, unicode_char_width=self.unicode_char_width)
2911
2984
 
2912
2985
  self.draw_screen(self.indexed_items, self.highlights)
2913
2986
 
@@ -3149,6 +3222,9 @@ class Picker:
3149
3222
  elif self.check_key("file_prev", key, self.keys_dict):
3150
3223
  self.switch_file(increment=-1)
3151
3224
 
3225
+ elif self.check_key("toggle_right_pane", key, self.keys_dict):
3226
+ self.toggle_right_pane()
3227
+
3152
3228
  elif self.check_key("pipe_input", key, self.keys_dict):
3153
3229
  self.logger.info(f"key_function pipe_input")
3154
3230
  # usrtxt = "xargs -d '\n' -I{} "
@@ -3323,7 +3399,7 @@ class Picker:
3323
3399
  self.history_edits.append(usrtxt)
3324
3400
  elif self.check_key("edit_ipython", key, self.keys_dict):
3325
3401
  self.logger.info(f"key_function edit_picker")
3326
- import IPython
3402
+ import IPython, termios
3327
3403
  self.stdscr.clear()
3328
3404
  restrict_curses(self.stdscr)
3329
3405
  self.stdscr.clear()
@@ -3338,10 +3414,24 @@ class Picker:
3338
3414
  # ]
3339
3415
  msg = "The active Picker object has variable name self.\n"
3340
3416
  msg += "\te.g., self.items will display the items in Picker"
3417
+ tty_in = open("/dev/tty", "r")
3418
+ tty_out = open("/dev/tty", "w")
3419
+
3420
+ fd = tty_in.fileno()
3421
+ old_attrs = termios.tcgetattr(fd)
3422
+ new_attrs = termios.tcgetattr(fd)
3423
+ new_attrs[3] |= termios.ECHO # lflags
3424
+ termios.tcsetattr(fd, termios.TCSADRAIN, new_attrs)
3425
+
3426
+ sys.stdin = tty_in
3427
+ sys.stdout = tty_out
3428
+ sys.stderr = tty_out
3341
3429
  IPython.embed(header=msg, config=c)
3342
3430
 
3343
3431
  unrestrict_curses(self.stdscr)
3344
3432
 
3433
+ tty_in.close()
3434
+ tty_out.close()
3345
3435
  self.stdscr.clear()
3346
3436
  self.stdscr.refresh()
3347
3437
  self.initialise_variables()
@@ -3657,16 +3747,24 @@ def main() -> None:
3657
3747
  # function_data["paginate"] = True
3658
3748
  # function_data["debug"] = True
3659
3749
  # function_data["debug_level"] = 1
3750
+
3751
+ function_data["split_right"] = True
3752
+ function_data["split_right_proportion"] = 2/3
3753
+ function_data["split_right_refresh_data"] = data_refresh_randint_title
3754
+ function_data["split_right_function"] = right_split_display_list
3755
+ function_data["split_right_data"] = ["Files", [str(x) for x in range(100)]]
3756
+
3757
+
3758
+ # function_data["split_right_refresh_data"] = get_dl
3759
+
3760
+
3761
+ # function_data["split_right_function"] = right_split_file_attributes
3762
+ # function_data["split_right_auto_refresh"] = True
3763
+ # function_data["split_right_function"] = right_split_graph
3764
+
3660
3765
  stdscr = start_curses()
3661
3766
  try:
3662
3767
  # Run the Picker
3663
- # h, w = stdscr.getmaxyx()
3664
- # if (h>8 and w >20):
3665
- # curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
3666
- # stdscr.bkgd(' ', curses.color_pair(1)) # Apply background color
3667
- # s = "Listpick is loading your data..."
3668
- # stdscr.addstr(h//2, (w-len(s))//2, s)
3669
- # stdscr.refresh()
3670
3768
 
3671
3769
  # app = Picker(stdscr, **function_data)
3672
3770
  app = Picker(stdscr)
File without changes
@@ -0,0 +1,95 @@
1
+ #!/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ get_data.py
5
+ Functions to get data to be displayed in the a pane.
6
+
7
+ Author: GrimAndGreedy
8
+ License: MIT
9
+ """
10
+
11
+ def data_refresh_randint_by_row(data, state):
12
+ """
13
+ Add a random number to the data if row id is the same.
14
+
15
+
16
+ Can be used with right_split_graph()
17
+
18
+ data[0]: 0,1,2,...,n+1
19
+ data[1]: randint(), randint(), ...
20
+ data[2]: row id
21
+ """
22
+ from random import randint
23
+ if state["indexed_items"]:
24
+ id = state["indexed_items"][state["cursor_pos"]][1][state["id_column"]]
25
+ else:
26
+ return [[0], [0], -1]
27
+ if data in [[], {}, None] or data[2] != id:
28
+ return [[0], [randint(0, 1000)], id]
29
+ else:
30
+ data[0].append(data[0][-1]+1)
31
+ data[1].append(randint(0, 1000))
32
+ return data
33
+
34
+ def data_refresh_randint(data, state):
35
+ """
36
+ Add a random number to the data--regardless of whether the row id is the same.
37
+
38
+
39
+ Can be used with right_split_graph()
40
+
41
+ data[0]: 0,1,2,...,n+1
42
+ data[1]: randint(), randint(), ...
43
+ data[2]: row id
44
+ """
45
+ from random import randint
46
+
47
+ if data in [[], {}, None]:
48
+ return [[0], [randint(0, 1000)], -1]
49
+ else:
50
+ data[0].append(data[0][-1]+1)
51
+ data[1].append(randint(0, 1000))
52
+ return data
53
+
54
+
55
+ def data_refresh_randint_title(data, state):
56
+ """
57
+ Add a random number to data[1].
58
+
59
+ Can be used with right_split_display_list()
60
+
61
+ data[0]: title
62
+ data[1]: randint(), randint(), ...
63
+ """
64
+ from random import randint
65
+
66
+ if data in [[], {}, None]:
67
+ return ["I CHOOSE", [str(randint(0, 1000))]]
68
+ else:
69
+ data[1].append(str(randint(0, 1000)))
70
+ return data
71
+
72
+ def get_dl(data, state):
73
+ """
74
+ Get dl speed and add it to data[1]
75
+
76
+ data[0]: 0,1,2,...,n+1
77
+ data[1]: dl_speed_at_0, dl_speed_at_1, ...
78
+ data[2]: row id
79
+ """
80
+ from aria2tui.utils import aria2c_utils
81
+
82
+ gid = "3c60b0fc67230b66"
83
+ req = aria2c_utils.tellStatus(gid)
84
+ info = aria2c_utils.sendReq(req)
85
+ dl = info["result"]["downloadSpeed"]
86
+ ul = info["result"]["uploadSpeed"]
87
+
88
+ dl, ul = int(dl), int(ul)
89
+
90
+ if data in [[], {}, None]:
91
+ return [[0], [dl], gid]
92
+ else:
93
+ data[0].append(data[0][-1]+1)
94
+ data[1].append(dl)
95
+ return data
@@ -0,0 +1,112 @@
1
+ #!/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ pane_functions.py
5
+ Functions which are run by a listpick Picker to display data in a pane.
6
+
7
+ Author: GrimAndGreedy
8
+ License: MIT
9
+ """
10
+
11
+ import curses
12
+ import os
13
+ from listpick.pane.pane_utils import get_file_attributes, get_graph_string, escape_ansi
14
+
15
+ def right_split_file_attributes(stdscr, x, y, w, h, state, row, cell, past_data: list = [], data: list = [], test: bool = False):
16
+ """
17
+ Display file attributes in right pane.
18
+ """
19
+ if test: return True
20
+
21
+ os.chdir(os.path.expanduser("~/Downloads/new"))
22
+
23
+ # Title
24
+ title = "File attributes"
25
+ if len(title) < w: title = f"{title:^{w}}"
26
+ stdscr.addstr(y, x,title[:w], curses.color_pair(state["colours_start"]+4) | curses.A_BOLD)
27
+
28
+ # Separator
29
+ for j in range(h):
30
+ stdscr.addstr(j+y, x, ' ', curses.color_pair(state["colours_start"]+16))
31
+
32
+
33
+ # Filename/cursor cell value
34
+ stdscr.addstr(y+2, x+2, cell[:w-3])
35
+
36
+ attributes = get_file_attributes(cell)
37
+ for i, attr in enumerate(attributes):
38
+ stdscr.addstr(y+3+i, x+4, attr[:w-5])
39
+
40
+ return []
41
+
42
+ def right_split_graph(stdscr, x, y, w, h, state, row, cell, past_data: list = [], data: list = [], test: bool = False):
43
+ """
44
+ Display a graph of the data in right pane.
45
+
46
+ data[0] = x_vals
47
+ data[1] = y_vals
48
+ data[2] = id
49
+ """
50
+ if test: return True
51
+
52
+ # Title
53
+ title = "Graph"
54
+ if len(title) < w: title = f"{title:^{w}}"
55
+ stdscr.addstr(y, x,title[:w], curses.color_pair(state["colours_start"]+4) | curses.A_BOLD)
56
+
57
+ # Separator
58
+ for j in range(h):
59
+ stdscr.addstr(j+y, x, ' ', curses.color_pair(state["colours_start"]+16))
60
+
61
+ try:
62
+ import plotille as plt
63
+ except:
64
+ s = f"No module named 'plotille'"
65
+ stdscr.addstr(y+2, x+2, s[:w-2])
66
+ return None
67
+
68
+
69
+ # x_vals, y_vals = list(range(100)), [x**2 for x in range(100)]
70
+ if data in [[], {}, None]:
71
+ return None
72
+ x_vals, y_vals = data[0], data[1]
73
+ graph_str = get_graph_string(x_vals, y_vals, width=w-3-10, height=h-3)
74
+ for i, s in enumerate(graph_str.split("\n")):
75
+ s = escape_ansi(s)
76
+ stdscr.addstr(y+2+i, x+2, s[:w-2])
77
+
78
+ return []
79
+
80
+
81
+
82
+
83
+ def right_split_display_list(stdscr, x, y, w, h, state, row, cell, past_data: list = [], data: list = [], test: bool = False):
84
+ """
85
+ data[0]:str = title
86
+ data[1]:list[str] = list of strings to display
87
+ """
88
+ if test: return True
89
+
90
+ # Title
91
+ title = data[0]
92
+ if len(title) < w: title = f"{title:^{w}}"
93
+ stdscr.addstr(y, x,title[:w], curses.color_pair(state["colours_start"]+4) | curses.A_BOLD)
94
+
95
+ # Separator
96
+ for j in range(h):
97
+ stdscr.addstr(j+y, x, ' ', curses.color_pair(state["colours_start"]+16))
98
+
99
+ if data in [[], {}, None]:
100
+ return None
101
+
102
+ items = data[1]
103
+ number_to_display = min(len(items), h-3)
104
+ for i in range(number_to_display):
105
+ s = items[i]
106
+ stdscr.addstr(y+1+i, x+2, s[:w-2])
107
+
108
+ if number_to_display < len(items):
109
+ stdscr.addstr(y+1+number_to_display, x+2, f" ... {len(items)-number_to_display} more"[:w-2])
110
+
111
+
112
+ return []
@@ -0,0 +1,88 @@
1
+ #!/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ pane_utils.py
5
+ Utility functions for Picker panes.
6
+
7
+ Author: GrimAndGreedy
8
+ License: MIT
9
+ """
10
+
11
+ import re
12
+
13
+ def escape_ansi(line: str) -> str:
14
+ """ Remove ansi characters from string. """
15
+ ansi_escape =re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
16
+ return ansi_escape.sub('', line)
17
+
18
+
19
+ def get_graph_string(x_vals, y_vals, width=50, height=20, title=None, x_label=None, y_label=None):
20
+ """ Generate a graph of x_vals, y_vals using plotille"""
21
+
22
+ import plotille as plt
23
+ # Create a figure and axis object using plotille
24
+ fig = plt.Figure()
25
+
26
+ # Plot the data on the figure
27
+ fig.plot(x_vals, y_vals)
28
+
29
+ # Set the dimensions of the graph
30
+ fig.width = width-10
31
+ fig.height = height-4
32
+ fig.x_ticks_fkt = lambda x, _: f"{int(x)}s"
33
+ fig.y_ticks_fkt = lambda y, _: f"{int(y)}"
34
+
35
+ # Set the title and labels if provided
36
+ if title:
37
+ fig.title = title
38
+ if x_label:
39
+ fig.xlabel = x_label
40
+ if y_label:
41
+ fig.ylabel = y_label
42
+
43
+ # Generate the ASCII art of the graph
44
+ ascii_art = str(fig.show())
45
+
46
+ return ascii_art
47
+
48
+
49
+ def get_file_attributes(filename):
50
+ import mimetypes
51
+ import time
52
+ import os
53
+ try:
54
+ if not os.path.exists(filename) and len(filename) > 2 and filename[0] == "'" and filename[-1] == "'":
55
+ filename = filename[1:-1]
56
+
57
+ abs_path = os.path.abspath(filename)
58
+
59
+ if not os.path.exists(abs_path):
60
+ return ["File not found"]
61
+
62
+ # Get the size of the file in bytes and convert it to a human-readable format
63
+ size = os.path.getsize(abs_path)
64
+ size_str = f"{(size / (1024 * 1024 * 1024)):,.2f}GB" if size > 1024 ** 3 else \
65
+ f"{(size / (1024 * 1024)):,.2f}MB" if size > 1024 ** 2 else \
66
+ f"{(size / 1024):,.2f}KB" if size > 1024 else f"{size}B"
67
+
68
+ # Get the file type
69
+ mime_type, _ = mimetypes.guess_type(abs_path)
70
+ if mime_type is None:
71
+ mime_type = "application/octet-stream"
72
+
73
+ # Get the last modified time in a human-readable format
74
+ mod_time = os.path.getmtime(abs_path)
75
+ mod_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(mod_time))
76
+
77
+ attributes = [
78
+ f"Size: {size_str}",
79
+ f"Filetype: {mime_type}",
80
+ f"Last Modified: {mod_str}"
81
+ ]
82
+
83
+ return attributes
84
+
85
+ except Exception as e:
86
+ print(f"An error occurred: {e}")
87
+ return []
88
+
listpick/ui/build_help.py CHANGED
@@ -142,14 +142,15 @@ def build_help_rows(keys_dict: dict, debug: bool = False) -> list[list[str]]:
142
142
  "file_prev": "Go to the previous open file.",
143
143
  "sheet_next": "Go to the next sheet.",
144
144
  "sheet_prev": "Go to the previous sheet.",
145
+ "toggle_right_pane": "Toggle the right pane",
145
146
  }
146
147
  sections = {
147
148
  "Navigation:": [ "cursor_down", "cursor_up", "half_page_up", "half_page_down", "page_up", "page_down", "cursor_bottom", "cursor_top", "five_up", "five_down", "scroll_right", "scroll_left", "scroll_far_right", "scroll_far_left" ],
148
149
  "Selection:": [ "toggle_select", "select_all", "select_none", "visual_selection_toggle", "visual_deselection_toggle", "enter" ],
149
- "UI:": [ "toggle_footer", "redraw_screen", "decrease_lines_per_page", "increase_lines_per_page", "increase_column_width", "decrease_column_width", "notification_toggle" ],
150
+ "UI:": [ "toggle_footer", "redraw_screen", "decrease_lines_per_page", "increase_lines_per_page", "increase_column_width", "decrease_column_width", "notification_toggle", "toggle_right_pane"],
150
151
  "Sort:": [ "cycle_sort_method", "cycle_sort_method_reverse", "cycle_sort_order", ] ,
151
152
  "Data manipulation:": [ "delete", "delete_column", "edit", "edit_picker", "edit_ipython", "add_column_before", "add_column_after", "add_row_before", "add_row_after"],
152
- "Filter and sort:": [ "filter_input", "x", "search_input", "continue_search_forward", "continue_search_backward", ] ,
153
+ "Filter and sort:": [ "filter_input", "search_input", "continue_search_forward", "continue_search_backward", ] ,
153
154
  "Settings:": [ "settings_input", "settings_options" ],
154
155
  "Cancel:": [ "opts_input", "opts_select", "mode_next", "mode_prev", "pipe_input", "reset_opts", "col_select", "col_select_next", "col_select_prev", "col_hide" ],
155
156
  "Save, load, copy and paste:": [ "save", "load", "open", "copy", "paste" ],
@@ -197,15 +198,20 @@ def build_help_rows(keys_dict: dict, debug: bool = False) -> list[list[str]]:
197
198
  row = [f" {str(keys)[1:-1]}", description]
198
199
  section_rows.append(row)
199
200
  if section_rows:
200
- items.append([section_name, ""])
201
+ items.append([f" {section_name}", ""])
201
202
  items += section_rows
202
203
  items.append(["",""])
203
204
 
205
+ if debug:
206
+ for operation in keys_dict:
207
+ if operation not in help_descriptions:
208
+ print(f"Note that {operation} is not in the help_descriptions")
209
+
204
210
  return items
205
211
 
206
212
  if __name__ == "__main__":
207
213
  items = build_help_rows(picker_keys, debug=True)
208
- items = build_help_rows(notification_keys, debug=True)
214
+ # items = build_help_rows(notification_keys, debug=True)
209
215
 
210
216
  # from listpick.listpick_app import Picker, start_curses, close_curses
211
217
  # stdscr = start_curses()
listpick/ui/footer.py CHANGED
@@ -209,7 +209,7 @@ class StandardFooter(Footer):
209
209
  max_chars = min(len(sort_disp_str)+2, w)
210
210
  self.stdscr.addstr(self.sort_info_y, w-max_chars, f"{sort_disp_str:>{max_chars-1}}", curses.color_pair(self.colours_start+20))
211
211
 
212
- self.stdscr.refresh()
212
+ # self.stdscr.refresh()
213
213
 
214
214
 
215
215
 
listpick/ui/keys.py CHANGED
@@ -84,6 +84,9 @@ picker_keys = {
84
84
  "info": [ord('i')],
85
85
  "file_next": [ord('}')],
86
86
  "file_prev": [ord('{')],
87
+ # "sheet_next": [],
88
+ # "sheet_prev": [],
89
+ "toggle_right_pane": [ord("'")],
87
90
  }
88
91
 
89
92
 
@@ -0,0 +1,224 @@
1
+ #!/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ graphing.py
5
+
6
+ Author: GrimAndGreedy
7
+ License: MIT
8
+ """
9
+
10
+ import sys, os
11
+ sys.path.append(os.path.expanduser(".."))
12
+ os.chdir(os.path.dirname(os.path.realpath(__file__)))
13
+ os.chdir("../../..")
14
+ from aria2tui.lib.aria2c_wrapper import *
15
+ from aria2tui.utils.aria2c_utils import *
16
+ from listpick import *
17
+ from listpick.listpick_app import *
18
+ import time
19
+ import re
20
+ from typing import Callable
21
+
22
+ def escape_ansi(line: str) -> str:
23
+ """ Remove ansi characters from string. """
24
+ ansi_escape =re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
25
+ return ansi_escape.sub('', line)
26
+
27
+ def handle_plotille_not_found(stdscr: curses.window) -> None:
28
+ """ Display ModuleNotFoundError. """
29
+ h, w = stdscr.getmaxyx()
30
+ s = "ModuleNotFoundError: No module named 'plotille'"
31
+ stdscr.addstr(h//2, (w - len(s))//2, s)
32
+ stdscr.refresh()
33
+ stdscr.getch()
34
+
35
+
36
+ def graph_speeds(
37
+ stdscr: curses.window,
38
+ get_data_function: Callable,
39
+ timeout:int=1000,
40
+ title:str="",
41
+ refresh_time: int = 2,
42
+ xposf: Callable = lambda: 0,
43
+ yposf: Callable = lambda: 0,
44
+ graph_wh: Callable[None,Tuple[int, int]] = lambda: os.get_terminal_size(),
45
+
46
+ ) -> None:
47
+ """ Display a graph of the global stats in a curses window. """
48
+ try:
49
+ import plotille as plt
50
+ except:
51
+ handle_plotille_not_found(stdscr)
52
+ return None
53
+
54
+ initial_time = time.time()
55
+ x, y, y2 = [], [], []
56
+ while time.time()-initial_time < timeout:
57
+
58
+
59
+ resp = get_data_function()
60
+ x.append(time.time()-initial_time)
61
+ down = int(resp['result']['downloadSpeed'])
62
+ up = int(resp['result']['uploadSpeed'])
63
+
64
+ y.append(down)
65
+ y2.append(up)
66
+
67
+ fig = plt.Figure()
68
+ fig.plot(x, y)
69
+ fig.plot(x, y2)
70
+
71
+ fig.y_ticks_fkt = lambda y, _: bytes_to_human_readable(y)
72
+ fig.x_ticks_fkt = lambda x, _: f"{int(x)}s"
73
+ fig.set_y_limits(min_=0)
74
+ fig.set_x_limits(min_=0)
75
+ fig.text([x[-1]], [y[0]], ['Dn'])
76
+ fig.text([x[0]], [y2[0]], ['Up'])
77
+
78
+ width, height = graph_wh()
79
+ fig.width = width - 7
80
+ fig.height = height - 4
81
+ globh, globw = stdscr.getmaxyx()
82
+ xpos, ypos = xposf(), yposf()
83
+ maxw, maxh = globw-xpos-1, globh-ypos
84
+
85
+ stdscr.erase()
86
+
87
+ # Draw title
88
+ stdscr.addstr(ypos, xpos, f"{title:^{min(width,maxw)}}")
89
+
90
+ # Draw graph
91
+ lines = fig.show().split('\n')
92
+ for i, line in enumerate(lines):
93
+ if i > maxh-2: break
94
+ line = escape_ansi(line)
95
+
96
+ stdscr.addstr(i+1+ypos, xpos , line[:min(width,maxw)])
97
+
98
+ # Show the extreme points of the width and also the last printable char
99
+ show_control_chars = False
100
+ if show_control_chars:
101
+ stdscr.addstr(0,0, f"{maxw}, {maxh}")
102
+ stdscr.addstr(ypos, xpos, "*", curses.A_REVERSE)
103
+ stdscr.addstr(ypos+maxh-1, xpos, "*", curses.A_REVERSE)
104
+ stdscr.addstr(ypos, xpos+maxw-1, "*", curses.A_REVERSE)
105
+ stdscr.addstr(ypos+maxh-1, xpos+maxw-2, "*", curses.A_REVERSE)
106
+ stdscr.addstr(ypos+ min(height, maxh-1), xpos, "+", curses.A_REVERSE)
107
+ stdscr.addstr(ypos, xpos+min(maxw-1, width), "+", curses.A_REVERSE)
108
+ stdscr.addstr(ypos+min(height, maxh-1), xpos+min(width, maxw-2), "+", curses.A_REVERSE)
109
+
110
+ stdscr.refresh()
111
+ key = stdscr.getch()
112
+ if key in [3, ord('q')]:
113
+ return None
114
+
115
+ def graph_speeds_gid(
116
+ stdscr: curses.window,
117
+ get_data_function: Callable,
118
+ timeout:int=1000,
119
+ title:str="",
120
+ refresh_time: int = 2,
121
+ xposf: Callable = lambda: 0,
122
+ yposf: Callable = lambda: 0,
123
+ graph_wh: Callable[None,Tuple[int, int]] = lambda: os.get_terminal_size(),
124
+ gid:str = ""
125
+
126
+ ) -> None:
127
+ """ Display a graph in a curses window for a certain gid. """
128
+ try:
129
+ import plotille as plt
130
+ except:
131
+ handle_plotille_not_found(stdscr)
132
+ return None
133
+
134
+ initial_time = time.time()
135
+ x, y, y2 = [], [], []
136
+ while time.time()-initial_time < timeout:
137
+
138
+ resp = get_data_function(gid)
139
+ x.append(time.time()-initial_time)
140
+ down = int(resp['result']['downloadSpeed'])
141
+ up = int(resp['result']['uploadSpeed'])
142
+
143
+ y.append(down)
144
+ y2.append(up)
145
+
146
+ fig = plt.Figure()
147
+ fig.plot(x, y)
148
+ fig.plot(x, y2)
149
+
150
+ fig.y_ticks_fkt = lambda y, _: bytes_to_human_readable(y)
151
+ fig.x_ticks_fkt = lambda x, _: f"{int(x)}s"
152
+ fig.set_y_limits(min_=0)
153
+ fig.set_x_limits(min_=0)
154
+ fig.text([x[-1]], [y[0]], ['Dn'])
155
+ fig.text([x[0]], [y2[0]], ['Up'])
156
+
157
+ width, height = graph_wh()
158
+ fig.width = width - 7
159
+ fig.height = height - 4
160
+ globh, globw = stdscr.getmaxyx()
161
+ xpos, ypos = xposf(), yposf()
162
+ maxw, maxh = globw-xpos-1, globh-ypos
163
+
164
+ stdscr.erase()
165
+
166
+ # Draw title
167
+ stdscr.addstr(ypos, xpos, f"{title:^{min(width,maxw)}}")
168
+
169
+ # Draw graph
170
+ lines = fig.show().split('\n')
171
+ for i, line in enumerate(lines):
172
+ if i > maxh-2: break
173
+ line = escape_ansi(line)
174
+
175
+ stdscr.addstr(i+1+ypos, xpos , line[:min(width,maxw)])
176
+
177
+ # Show the extreme points of the width and also the last printable char
178
+ show_control_chars = False
179
+ if show_control_chars:
180
+ stdscr.addstr(0,0, f"{maxw}, {maxh}")
181
+ stdscr.addstr(ypos, xpos, "*", curses.A_REVERSE)
182
+ stdscr.addstr(ypos+maxh-1, xpos, "*", curses.A_REVERSE)
183
+ stdscr.addstr(ypos, xpos+maxw-1, "*", curses.A_REVERSE)
184
+ stdscr.addstr(ypos+maxh-1, xpos+maxw-2, "*", curses.A_REVERSE)
185
+ stdscr.addstr(ypos+ min(height, maxh-1), xpos, "+", curses.A_REVERSE)
186
+ stdscr.addstr(ypos, xpos+min(maxw-1, width), "+", curses.A_REVERSE)
187
+ stdscr.addstr(ypos+min(height, maxh-1), xpos+min(width, maxw-2), "+", curses.A_REVERSE)
188
+
189
+ stdscr.refresh()
190
+ key = stdscr.getch()
191
+ stdscr.refresh()
192
+ if key in [3, ord('q')]:
193
+ return None
194
+ resp = get_data_function(gid)
195
+
196
+
197
+
198
+ if __name__ == "__main__":
199
+ title = "Global Transfer Speeds"
200
+ end_time = 180
201
+ get_data_function = lambda: sendReq(getGlobalStat())
202
+ gid = "60e0c88ed77a24d6"
203
+ get_data_function = lambda: sendReq(tellStatus(gid))
204
+ wait_time = 2
205
+
206
+ stdscr = start_curses()
207
+ size_func = lambda: (
208
+ 3*os.get_terminal_size()[0]//4,
209
+ 3*os.get_terminal_size()[1]//4,
210
+ )
211
+ xposf = lambda: os.get_terminal_size()[0]//8
212
+ yposf = lambda: os.get_terminal_size()[1]//8
213
+ graph_speeds(
214
+ stdscr,
215
+ get_data_function=get_data_function,
216
+ timeout=end_time,
217
+ refresh_time=wait_time,
218
+ title=title,
219
+ graph_wh= size_func,
220
+ xposf=xposf,
221
+ yposf=yposf,
222
+
223
+ )
224
+ close_curses(stdscr)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listpick
3
- Version: 0.1.14.14
3
+ Version: 0.1.15.1
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
@@ -27,6 +27,7 @@ Requires-Dist: pyperclip; extra == "full"
27
27
  Requires-Dist: toml; extra == "full"
28
28
  Requires-Dist: traitlets; extra == "full"
29
29
  Requires-Dist: odfpy; extra == "full"
30
+ Requires-Dist: plotille; extra == "full"
30
31
  Dynamic: author
31
32
  Dynamic: author-email
32
33
  Dynamic: classifier
@@ -1,13 +1,16 @@
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=lvmg2iAxoL91NMv7ptQmdjZHCG0GvsSIqEKBJmtDhFw,187716
3
+ listpick/listpick_app.py,sha256=iWa6N4PVeOoLa9Yi9CMUojDHoXzSQviObnfv5vPgpiA,193216
4
+ listpick/pane/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ listpick/pane/get_data.py,sha256=21PTDXt9HP-4vZV4QUM8sidTqDEaKnKCDshl3-cSRCo,2255
6
+ listpick/pane/pane_functions.py,sha256=7xynpd3HjZnt6s9W0ZsJlrmYvGCMstAI5OJ7EuQSzdg,3092
7
+ listpick/pane/pane_utils.py,sha256=tMp8KlFng6ZQgNjd8iFrl1zWZLPXMH_Y1A_QeuQYAk0,2537
4
8
  listpick/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- listpick/ui/build_help.py,sha256=B_NuIqexvqui8sDZotyu36aN_Yypfg6EJYCynZICLOY,10176
6
- listpick/ui/footer.py,sha256=9oJm95pOCxMEoS7Y-77G3D3dPGK4NnsqPMEAtaOpJco,15087
9
+ listpick/ui/build_help.py,sha256=NY-bDVV5NYQ2mEJo3-tTUsFpG7R6hwROPZPgc8NGfA0,10451
10
+ listpick/ui/footer.py,sha256=OH27JLGvvq9TlUsI30ODG6cHvjm7NTGSbXvF6lN1qiY,15089
7
11
  listpick/ui/help_screen.py,sha256=zbfGIgb-IXtATpl4_Sx7nPbsnRXZ7eiMYlCKGS9EFmw,5608
8
12
  listpick/ui/input_field.py,sha256=ylf3fiLXdAD4pueHWfzIrlwaRb9f5zm8f1UGkEPBxgM,30539
9
- listpick/ui/keys.py,sha256=7ZhJfsSatpk-jwfXj_FvzgQsQdUoF7JkD5Mniu9XZ0o,13328
10
- listpick/ui/pane_stuff.py,sha256=7GXa4UnV_7YmBv-baRi5moN51wYcuS4p0odl5C3m0Tc,169
13
+ listpick/ui/keys.py,sha256=82uzU_cMo6NFHVi3P6cP0uE2ZN0h1EaVLYIVKuB26AU,13494
11
14
  listpick/ui/picker_colours.py,sha256=wftDxmUI6tGmOIPwBe4rUUsrPTtpFGgztptCifa_tqA,12287
12
15
  listpick/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
16
  listpick/utils/clipboard_operations.py,sha256=ORdNm2kgGbfs51xJSvgJPERgoSmBgT11axuMkvSoP9A,3133
@@ -15,6 +18,7 @@ listpick/utils/config.py,sha256=MEnAZg2Rhfl38XofEIN0uoVAOY7I0ftc79Evk3fOiVw,1654
15
18
  listpick/utils/dump.py,sha256=60YVIMNtBoYvWhmzfTJOsNGcetOvcCB3_T7yv-bYTPQ,3838
16
19
  listpick/utils/filtering.py,sha256=uS9sW0inmFvq0cIrwRC1BfuP8kjAD5IWWtls4jGB-70,1199
17
20
  listpick/utils/generate_data.py,sha256=7sv6JRhk0-Gcj4kOlkzx4qPNBJZ-GFWg9vM77GktzpI,3073
21
+ listpick/utils/graphing.py,sha256=ugjAH8js_iH7hulg4SySxb_W_f8B6GhTaceN5i7DID4,6954
18
22
  listpick/utils/keycodes.py,sha256=ZGkw1-4szxPnP81wj80r92L6_neIOlBBjQltEieCwnk,2696
19
23
  listpick/utils/options_selectors.py,sha256=Vbv4jRkUsSPs7g-EQAv9Q0nhYy6Z4sFsJqMjUIe1oeQ,2814
20
24
  listpick/utils/paste_operations.py,sha256=7wDXLPlxUgA3CA99gwsm47juWGO2YQ9EJghW06yo9vI,1242
@@ -25,9 +29,9 @@ listpick/utils/sorting.py,sha256=WZZiVlVA3Zkcpwji3U5SNFlQ14zVEk3cZJtQirBkecQ,532
25
29
  listpick/utils/table_to_list_of_lists.py,sha256=XBj7eGBDF15BRME-swnoXyOfZWxXCxrXp0pzsBfcJ5g,12224
26
30
  listpick/utils/user_input.py,sha256=oyJZPAwA7UGAclPhdPL44tKnPIVNHWhX-tZEnCdBKC0,4318
27
31
  listpick/utils/utils.py,sha256=McOl9uT3jh7l4TIWeSd8ZGjK_e7r0YZF0Gl20yI6fl0,13873
28
- listpick-0.1.14.14.dist-info/licenses/LICENSE.txt,sha256=2mP-MRHJptADDNE9VInMNg1tE-C6Qv93Z4CCQKrpg9w,1061
29
- listpick-0.1.14.14.dist-info/METADATA,sha256=Xm4gp-Ad6QT0I7nKM7k9c00IWS9xLNj5R68nRAVTzOc,8091
30
- listpick-0.1.14.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
- listpick-0.1.14.14.dist-info/entry_points.txt,sha256=-QCf_BKIkUz35Y9nkYpjZWs2Qg0KfRna2PAs5DnF6BE,43
32
- listpick-0.1.14.14.dist-info/top_level.txt,sha256=5mtsGEz86rz3qQDe0D463gGjAfSp6A3EWg4J4AGYr-Q,9
33
- listpick-0.1.14.14.dist-info/RECORD,,
32
+ listpick-0.1.15.1.dist-info/licenses/LICENSE.txt,sha256=2mP-MRHJptADDNE9VInMNg1tE-C6Qv93Z4CCQKrpg9w,1061
33
+ listpick-0.1.15.1.dist-info/METADATA,sha256=aQnHr2pHjI9T5RcXrHPqfOPoK5fR792K_79TOradL3E,8131
34
+ listpick-0.1.15.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
+ listpick-0.1.15.1.dist-info/entry_points.txt,sha256=-QCf_BKIkUz35Y9nkYpjZWs2Qg0KfRna2PAs5DnF6BE,43
36
+ listpick-0.1.15.1.dist-info/top_level.txt,sha256=5mtsGEz86rz3qQDe0D463gGjAfSp6A3EWg4J4AGYr-Q,9
37
+ listpick-0.1.15.1.dist-info/RECORD,,
listpick/ui/pane_stuff.py DELETED
@@ -1,8 +0,0 @@
1
- import curses
2
-
3
- features = {
4
- "position": "top",
5
- "height": 4,
6
- }
7
- def textbox(x:int, y:int, w:int, h:int, text:str):
8
- """ Draw a textbox in the region given. """