vappman 0.7__tar.gz → 0.9__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vappman
3
- Version: 0.7
3
+ Version: 0.9
4
4
  Summary: A visual wrapper for appman
5
5
  Author-email: Joe Defen <joedef@google.com>
6
6
  License: MIT
@@ -41,7 +41,7 @@ But it does NOT cover:
41
41
  * (-v) version of appman
42
42
  * --force-latest to get the most recent stable release AND
43
43
  all other options and unmentioned commands.
44
-
44
+
45
45
  ## Usage
46
46
  * Run `vappman` from the command line.
47
47
  * It presents some keys available on the top line.
@@ -67,13 +67,15 @@ But it does NOT cover:
67
67
  * `ESC` clears the filter and jumps to the top of the listing.
68
68
  * Each time the filter is changed, the position jumps to the top of the listing.
69
69
  * Use `i` to install apps, and `r` to remove apps. When you install or remove an app, `appman` drops out of `curses` mode, runs the `appman` command so you can see the result, and then prompts your to hit ENTER to return to `vappman.
70
+ * Use `t` to "test" an installed app. This launches a terminal emulator and then the app so you can see issues. This is not for daily use obviously, but for after install or when having unknown issues and you wish to start the investigation.
70
71
 
71
- ## Example Screenshot
72
+ ## Example Screenshot (of v0.7 ... current release will vary slightly)
72
73
  ![vappman-with-filter](https://github.com/joedefen/vappman/blob/main/images/vappman-with-filter.png?raw=true).
73
74
 
74
75
  ---
75
76
 
76
77
  NOTES: in this example:
78
+ * keys to the left of `❚` apply to the highlighted app; keys to the right apply globally.
77
79
  * the filter is `card` so it shows app lines with words starting with `card`.
78
80
  * the reverse video, current position is on `glabels`;
79
81
  thus if `i` (or ENTER) is typed, `appman install glabels` is run.
@@ -81,6 +83,8 @@ NOTES: in this example:
81
83
  all the filtered apps; otherwise, the decoration suggests where you are in the
82
84
  partial view of the filtered apps.
83
85
  * the matching installed app has the '✔✔✔' prefix.
84
- * the fixed top line shows some of the available action keys (e.g., `q` quits the app)
86
+ * the fixed top line shows mos of the available action keys (e.g., `q` quits the app)
85
87
  * use `?` to open the help screen describing all keys (including navigation)
86
88
 
89
+ ## Screen Recording (Intro to vappman based on v0.7)
90
+ [![Screen Recording](https://i9.ytimg.com/vi_webp/NUHYN9_DZtA/mq3.webp?sqp=CMTu4LMG-oaymwEmCMACELQB8quKqQMa8AEB-AHqBYAC4AOKAgwIABABGEogZShRMA8=&rs=AOn4CLBaBrOpAhJkRIQQNNdCzYaqpOYl-Q)](https://youtu.be/NUHYN9_DZtA)
@@ -23,7 +23,7 @@ But it does NOT cover:
23
23
  * (-v) version of appman
24
24
  * --force-latest to get the most recent stable release AND
25
25
  all other options and unmentioned commands.
26
-
26
+
27
27
  ## Usage
28
28
  * Run `vappman` from the command line.
29
29
  * It presents some keys available on the top line.
@@ -49,13 +49,15 @@ But it does NOT cover:
49
49
  * `ESC` clears the filter and jumps to the top of the listing.
50
50
  * Each time the filter is changed, the position jumps to the top of the listing.
51
51
  * Use `i` to install apps, and `r` to remove apps. When you install or remove an app, `appman` drops out of `curses` mode, runs the `appman` command so you can see the result, and then prompts your to hit ENTER to return to `vappman.
52
+ * Use `t` to "test" an installed app. This launches a terminal emulator and then the app so you can see issues. This is not for daily use obviously, but for after install or when having unknown issues and you wish to start the investigation.
52
53
 
53
- ## Example Screenshot
54
+ ## Example Screenshot (of v0.7 ... current release will vary slightly)
54
55
  ![vappman-with-filter](https://github.com/joedefen/vappman/blob/main/images/vappman-with-filter.png?raw=true).
55
56
 
56
57
  ---
57
58
 
58
59
  NOTES: in this example:
60
+ * keys to the left of `❚` apply to the highlighted app; keys to the right apply globally.
59
61
  * the filter is `card` so it shows app lines with words starting with `card`.
60
62
  * the reverse video, current position is on `glabels`;
61
63
  thus if `i` (or ENTER) is typed, `appman install glabels` is run.
@@ -63,6 +65,8 @@ NOTES: in this example:
63
65
  all the filtered apps; otherwise, the decoration suggests where you are in the
64
66
  partial view of the filtered apps.
65
67
  * the matching installed app has the '✔✔✔' prefix.
66
- * the fixed top line shows some of the available action keys (e.g., `q` quits the app)
68
+ * the fixed top line shows mos of the available action keys (e.g., `q` quits the app)
67
69
  * use `?` to open the help screen describing all keys (including navigation)
68
70
 
71
+ ## Screen Recording (Intro to vappman based on v0.7)
72
+ [![Screen Recording](https://i9.ytimg.com/vi_webp/NUHYN9_DZtA/mq3.webp?sqp=CMTu4LMG-oaymwEmCMACELQB8quKqQMa8AEB-AHqBYAC4AOKAgwIABABGEogZShRMA8=&rs=AOn4CLBaBrOpAhJkRIQQNNdCzYaqpOYl-Q)](https://youtu.be/NUHYN9_DZtA)
@@ -55,7 +55,7 @@ build-backend = "setuptools.build_meta"
55
55
 
56
56
  [project]
57
57
  name = "vappman"
58
- version = "0.7"
58
+ version = "0.9"
59
59
  description = "A visual wrapper for appman"
60
60
  authors = [
61
61
  { name = "Joe Defen", email = "joedef@google.com" }
@@ -588,7 +588,7 @@ class Window:
588
588
  def alert(self, title='ALERT', message='', height=1, width=80):
589
589
  """Alert box"""
590
590
  def mod_key(key):
591
- return 7 if key == 10 else key
591
+ return 7 if key in (10, curses.KEY_ENTER) else key
592
592
 
593
593
  # need 3 extra cols for rectangle (so we don't draw in southeast corner)
594
594
  # and 3 rows (top/prompt/bottom)
@@ -6,7 +6,7 @@ Interactive, visual thin layer atop appman
6
6
  # pylint: disable=broad-exception-caught,consider-using-with
7
7
  # pylint: disable=too-many-instance-attributes,too-many-branches
8
8
  # pylint: disable=too-many-return-statements,too-many-statements
9
- # pylint: disable=consider-using-in
9
+ # pylint: disable=consider-using-in,too-many-nested-blocks
10
10
  # pylint: disable=wrong-import-position,disable=wrong-import-order
11
11
  # import VirtEnv
12
12
  # VirtEnv.ensure_venv(__name__)
@@ -14,6 +14,7 @@ Interactive, visual thin layer atop appman
14
14
  import os
15
15
  import sys
16
16
  import re
17
+ import glob
17
18
  import shutil
18
19
  import subprocess
19
20
  import traceback
@@ -34,7 +35,7 @@ class Vappman:
34
35
  spin.add_key('help_mode', '? - toggle help screen', vals=[False, True])
35
36
 
36
37
  # EXPAND
37
- other = 'airbou/qscU'
38
+ other = 'airtbou/qxscU'
38
39
  other_keys = set(ord(x) for x in other)
39
40
  other_keys.add(cs.KEY_ENTER)
40
41
  other_keys.add(27) # ESCAPE
@@ -49,6 +50,9 @@ class Vappman:
49
50
  self.check_preqreqs()
50
51
  self.apps = self.cmd_dict('appman list')
51
52
  self.installs = self.get_installed() # dict keyed by app
53
+ self.appman_dir = self.get_appman_dir()
54
+ self.dot_desktop_dir = self.get_dot_desktop_dir()
55
+ self.terminal_emulator = None
52
56
  self.win = Window(head_line=True, body_rows=len(self.apps)+20, head_rows=10,
53
57
  keys=spin.keys ^ other_keys, mod_pick=self.mod_pick)
54
58
 
@@ -94,6 +98,43 @@ class Vappman:
94
98
  rv = self.cmd_dict('appman files --byname')
95
99
  return rv
96
100
 
101
+ @staticmethod
102
+ def get_appman_dir():
103
+ """ Try to figure out where the apps are stored. """
104
+
105
+ appman_dir = None, None
106
+ try:
107
+ config_dir = os.getenv('XDG_CONFIG_HOME')
108
+ if not config_dir:
109
+ config_dir = os.path.join(os.getenv('HOME'), '.config')
110
+ config_file = os.path.join(config_dir, 'appman', 'appman-config')
111
+ with open(config_file, 'r', encoding='utf-8') as fh:
112
+ appman_dir = fh.read().strip()
113
+ appman_dir = os.path.join(os.getenv('HOME'), appman_dir)
114
+ os.listdir(appman_dir)
115
+ return appman_dir
116
+ except Exception as exc:
117
+ print(f'NOTE: cannot get appman dir; tried below {appman_dir!r}; {exc}')
118
+ print(' Check if contents of ~/config/appman/appman-config'
119
+ + ' is the subdir of $HOME w your appman apps')
120
+ return None
121
+
122
+ @staticmethod
123
+ def get_dot_desktop_dir():
124
+ """ Try to figure out where the .desktop files are stored. """
125
+
126
+ try:
127
+ data_dir = os.getenv('XDG_DATA_HOME')
128
+ if not data_dir:
129
+ data_dir = os.path.join(os.getenv('HOME'), '.local', 'share')
130
+ dot_dir = os.path.join(data_dir, 'applications')
131
+ os.listdir(dot_dir)
132
+ return dot_dir
133
+ except Exception as exc:
134
+ print(f'NOTE: cannot get .desktop dir; tried below {data_dir!r}; {exc}')
135
+ print(' Check if contents of ~/.local/share/applications for .desktop files')
136
+ return None
137
+
97
138
  def main_loop(self):
98
139
  """ TBD """
99
140
 
@@ -106,20 +147,21 @@ class Vappman:
106
147
  # EXPAND
107
148
  lines = [
108
149
  'ALWAYS AVAILABLE:',
109
- ' q - quit program (CTL-C disabled)',
110
150
  ' a - about (more info about app)',
111
151
  ' s - sync (update appman itself)',
112
152
  ' c - clean (remove unneeded files/folters)',
113
153
  ' U - update ALL installed apps',
114
- ' / - filter apps',
115
- ' ENTER = install, remove, or return from help',
154
+ ' q or x - quit program (CTL-C disabled)',
155
+ ' / - filter apps by keywords or regex',
116
156
  ' ESC = clear filter and jump to top',
157
+ ' ENTER = install, remove, or return from help',
117
158
  'CONTEXT SENSITIVE:',
118
159
  ' i - install uninstalled app',
119
160
  ' r - remove installed app',
120
- ' b - backup installed app',
121
161
  ' u - update installed app',
162
+ ' b - backup installed app',
122
163
  ' o - overwrite app from its backup',
164
+ ' t - test by opening a terminal emulator and launching the app',
123
165
 
124
166
  ]
125
167
  for line in lines:
@@ -127,6 +169,12 @@ class Vappman:
127
169
  else:
128
170
  def wanted(line):
129
171
  return not self.filter or self.filter.search(line)
172
+ def version_of(app):
173
+ # ◆ krita | 5.2.2 | appimage-type2 | 355 MiB
174
+ fields = self.installs[app].split('|')
175
+ if len(fields) >= 2:
176
+ return fields[1].strip()
177
+ return '?version?'
130
178
 
131
179
  # self.win.set_pick_mode(self.opts.pick_mode, self.opts.pick_size)
132
180
  self.win.set_pick_mode(True)
@@ -135,7 +183,7 @@ class Vappman:
135
183
  if app in self.apps:
136
184
  line = self.apps[app]
137
185
  if wanted(line[2:]):
138
- line = '✔✔✔' + line[1:]
186
+ line = f'✔✔✔ {app} [{version_of(app)}] :{line.split(':', maxsplit=1)[1]}'
139
187
  self.win.add_body(line)
140
188
  for app, line in self.apps.items():
141
189
  if app not in self.installs and wanted(line[2:]):
@@ -155,7 +203,7 @@ class Vappman:
155
203
  else:
156
204
  line += f' {key}:{verb}'
157
205
  # or EXPAND
158
- line += f' ?:help quit about sync clean Upd /{self.prev_filter} '
206
+ line += f' about sync clean Upd quit ?:help /{self.prev_filter} '
159
207
  # for action in self.actions:
160
208
  # line += f' {action[0]}:{action}'
161
209
  return line[1:]
@@ -171,9 +219,10 @@ class Vappman:
171
219
  # EXPAND
172
220
  if self.pick_is_installed:
173
221
  actions['r'] = 'rmv'
222
+ actions['u'] = 'upd'
174
223
  actions['b'] = 'bkup'
175
224
  actions['o'] = 'overwr'
176
- actions['u'] = 'upd'
225
+ actions['t'] = 'test'
177
226
  else:
178
227
  actions['i'] = 'install'
179
228
 
@@ -192,6 +241,9 @@ class Vappman:
192
241
  wds = parts[0].split()
193
242
  this.win.head.pad.move(0, 0)
194
243
  for wd in wds:
244
+ if wd[0]in ('<', '|', '❚'):
245
+ this.win.add_header(wd + ' ', resume=True)
246
+ continue
195
247
  if wd:
196
248
  this.win.add_header(wd[0], attr=cs.A_BOLD|cs.A_UNDERLINE, resume=True)
197
249
  if wd[1:]:
@@ -213,6 +265,95 @@ class Vappman:
213
265
  self.installs = self.get_installed()
214
266
  Window._start_curses()
215
267
 
268
+ @staticmethod
269
+ def launch_desktop_file(desktop_file_path):
270
+ """ Launch the .desktop file using xdg-open in a detached process """
271
+ try:
272
+ trial = ['xdg-open', desktop_file_path]
273
+ subprocess.Popen(trial,
274
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, close_fds=True)
275
+ return None
276
+ except Exception:
277
+ return trial
278
+
279
+ def launch_in_terminal(self, executable):
280
+ """ Find a terminal emulator"""
281
+ if not self.terminal_emulator:
282
+ maybes = [
283
+ [ 'konsole', '--noclose', '-e', '"{command}"'],
284
+ [ 'gnome-terminal', '--', 'bash', '-c', '"{command}"; exec bash' ],
285
+ [ 'xfce4-terminal', '--hold', '--command="{command}"' ],
286
+ [ 'lxterminal', '-e', """bash -c '"{command}"; echo; read -p "Press Enter to close..."'"""],
287
+ # [ terminator', ],
288
+ # [ alacritty', ],
289
+ # [ termite', ],
290
+ # [ urxvt', ],
291
+ # [ sakura', ],
292
+ # [ tilix', ],
293
+ # [ kitty', ],
294
+ # [ hyper', ],
295
+ # [ guake', ],
296
+ # [ yakuake', ],
297
+ ]
298
+ for maybe in maybes:
299
+ if shutil.which(maybe[0]):
300
+ self.terminal_emulator = maybe
301
+ break
302
+ if self.terminal_emulator:
303
+ try:
304
+ trial = []
305
+ for wd in self.terminal_emulator:
306
+ trial.append(wd.replace('{command}', executable))
307
+ subprocess.Popen(trial)
308
+ return None
309
+ except Exception:
310
+ return trial
311
+ return trial
312
+
313
+ def launch_app(self, app):
314
+ """ Try to run an app."""
315
+ # First dig out where it might be installed as a .desktop file
316
+ # by searching the 'remove' script
317
+ def get_unique_words_from_file(file_path):
318
+ seen_words = set()
319
+ with open(file_path, 'r', encoding='utf-8') as file:
320
+ for line in file:
321
+ line_words = line.split()
322
+ for word in line_words:
323
+ if word not in seen_words:
324
+ seen_words.add(word)
325
+ return seen_words
326
+
327
+ failures = []
328
+ executables = []
329
+ try:
330
+ for globname in get_unique_words_from_file(
331
+ os.path.join(self.appman_dir, app, 'remove')):
332
+ results = glob.glob(globname)
333
+ if '/share/applications/AM-ZZZ' in globname:
334
+ for result in results:
335
+ if result.endswith('.desktop'):
336
+ failure = self.launch_desktop_file(result)
337
+ if failure:
338
+ failures.append(' '.join(failure))
339
+ return
340
+ elif '/.local/bin/' in globname:
341
+ for result in results:
342
+ if os.access(result, os.X_OK):
343
+ executables.append(result)
344
+ for executable in executables:
345
+ failure = self.launch_in_terminal(executable)
346
+ if failure:
347
+ failures.append(' '.join(failure))
348
+ return
349
+ except Exception as exc:
350
+ failures += f'cannot find .desktop/executable to run [{exc}]'
351
+ if failures:
352
+ message = ' '.join([f'Cannot launch {app}'] + failures)
353
+ self.win.alert(message=message)
354
+
355
+ self.launch_app(self.pick_app)
356
+
216
357
  def do_key(self, key):
217
358
  """ TBD """
218
359
  if not key:
@@ -222,21 +363,21 @@ class Vappman:
222
363
  self.opts.help_mode = False
223
364
  return True
224
365
  if self.pick_is_installed:
225
- key = ord('r') # removed installed app
366
+ key = ord('r') # remove installed app
226
367
  else:
227
368
  key = ord('i') # install uninstalled app
228
369
 
229
370
  if key in self.spin.keys:
230
371
  value = self.spin.do_key(key, self.win)
231
372
  return value
232
-
373
+
233
374
  if key == 27: # ESCAPE
234
375
  self.prev_filter = ''
235
376
  self.filter = None
236
377
  self.win.pick_pos = 0
237
378
  return None
238
379
 
239
- if key == ord('q'):
380
+ if key in (ord('q'), ord('x')):
240
381
  self.win.stop_curses()
241
382
  os.system('clear; stty sane')
242
383
  sys.exit(0)
@@ -249,6 +390,10 @@ class Vappman:
249
390
  self.run_appman(f'appman remove {self.pick_app}')
250
391
  return None
251
392
 
393
+ if key == ord('t') and self.pick_is_installed:
394
+ self.launch_app(self.pick_app)
395
+ return None
396
+
252
397
  if key == ord('s'):
253
398
  self.run_appman('appman sync')
254
399
  if key == ord('c'):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vappman
3
- Version: 0.7
3
+ Version: 0.9
4
4
  Summary: A visual wrapper for appman
5
5
  Author-email: Joe Defen <joedef@google.com>
6
6
  License: MIT
@@ -41,7 +41,7 @@ But it does NOT cover:
41
41
  * (-v) version of appman
42
42
  * --force-latest to get the most recent stable release AND
43
43
  all other options and unmentioned commands.
44
-
44
+
45
45
  ## Usage
46
46
  * Run `vappman` from the command line.
47
47
  * It presents some keys available on the top line.
@@ -67,13 +67,15 @@ But it does NOT cover:
67
67
  * `ESC` clears the filter and jumps to the top of the listing.
68
68
  * Each time the filter is changed, the position jumps to the top of the listing.
69
69
  * Use `i` to install apps, and `r` to remove apps. When you install or remove an app, `appman` drops out of `curses` mode, runs the `appman` command so you can see the result, and then prompts your to hit ENTER to return to `vappman.
70
+ * Use `t` to "test" an installed app. This launches a terminal emulator and then the app so you can see issues. This is not for daily use obviously, but for after install or when having unknown issues and you wish to start the investigation.
70
71
 
71
- ## Example Screenshot
72
+ ## Example Screenshot (of v0.7 ... current release will vary slightly)
72
73
  ![vappman-with-filter](https://github.com/joedefen/vappman/blob/main/images/vappman-with-filter.png?raw=true).
73
74
 
74
75
  ---
75
76
 
76
77
  NOTES: in this example:
78
+ * keys to the left of `❚` apply to the highlighted app; keys to the right apply globally.
77
79
  * the filter is `card` so it shows app lines with words starting with `card`.
78
80
  * the reverse video, current position is on `glabels`;
79
81
  thus if `i` (or ENTER) is typed, `appman install glabels` is run.
@@ -81,6 +83,8 @@ NOTES: in this example:
81
83
  all the filtered apps; otherwise, the decoration suggests where you are in the
82
84
  partial view of the filtered apps.
83
85
  * the matching installed app has the '✔✔✔' prefix.
84
- * the fixed top line shows some of the available action keys (e.g., `q` quits the app)
86
+ * the fixed top line shows mos of the available action keys (e.g., `q` quits the app)
85
87
  * use `?` to open the help screen describing all keys (including navigation)
86
88
 
89
+ ## Screen Recording (Intro to vappman based on v0.7)
90
+ [![Screen Recording](https://i9.ytimg.com/vi_webp/NUHYN9_DZtA/mq3.webp?sqp=CMTu4LMG-oaymwEmCMACELQB8quKqQMa8AEB-AHqBYAC4AOKAgwIABABGEogZShRMA8=&rs=AOn4CLBaBrOpAhJkRIQQNNdCzYaqpOYl-Q)](https://youtu.be/NUHYN9_DZtA)
File without changes
File without changes
File without changes