mirror of
https://github.com/XuehaiPan/nvitop.git
synced 2026-05-22 22:01:57 -06:00
feat(gui): support process tagging in main screen
Signed-off-by: Xuehai Pan <XuehaiPan@pku.edu.cn>
This commit is contained in:
parent
011913880e
commit
d8a47ac6ca
4 changed files with 69 additions and 40 deletions
|
|
@ -173,6 +173,9 @@ class MainScreen(DisplayableContainer): # pylint: disable=too-many-instance-att
|
|||
self.root.update_size()
|
||||
self.root.need_redraw = True
|
||||
|
||||
def screen_move(direction):
|
||||
self.move(direction)
|
||||
|
||||
def host_left():
|
||||
self.process_panel.host_offset -= 2
|
||||
|
||||
|
|
@ -191,8 +194,9 @@ class MainScreen(DisplayableContainer): # pylint: disable=too-many-instance-att
|
|||
def select_clear():
|
||||
self.selected.clear()
|
||||
|
||||
def screen_move(direction):
|
||||
self.move(direction)
|
||||
def tag():
|
||||
self.selected.tag()
|
||||
select_move(direction=+1)
|
||||
|
||||
def terminate():
|
||||
self.selected.terminate()
|
||||
|
|
@ -227,6 +231,13 @@ class MainScreen(DisplayableContainer): # pylint: disable=too-many-instance-att
|
|||
self.root.keymaps.copy('main', 'r', '<C-r>')
|
||||
self.root.keymaps.copy('main', 'r', '<F5>')
|
||||
|
||||
self.root.keymaps.bind('main', '<PageUp>', partial(screen_move, direction=-1))
|
||||
self.root.keymaps.copy('main', '<PageUp>', '[')
|
||||
self.root.keymaps.copy('main', '<PageUp>', '<A-K>')
|
||||
self.root.keymaps.bind('main', '<PageDown>', partial(screen_move, direction=+1))
|
||||
self.root.keymaps.copy('main', '<PageDown>', ']')
|
||||
self.root.keymaps.copy('main', '<PageDown>', '<A-J>')
|
||||
|
||||
self.root.keymaps.bind('main', '<Left>', host_left)
|
||||
self.root.keymaps.copy('main', '<Left>', '<A-h>')
|
||||
self.root.keymaps.bind('main', '<Right>', host_right)
|
||||
|
|
@ -244,13 +255,7 @@ class MainScreen(DisplayableContainer): # pylint: disable=too-many-instance-att
|
|||
self.root.keymaps.bind('main', '<Home>', partial(select_move, direction=-(1 << 20)))
|
||||
self.root.keymaps.bind('main', '<End>', partial(select_move, direction=+(1 << 20)))
|
||||
self.root.keymaps.bind('main', '<Esc>', select_clear)
|
||||
|
||||
self.root.keymaps.bind('main', '<PageUp>', partial(screen_move, direction=-1))
|
||||
self.root.keymaps.copy('main', '<PageUp>', '[')
|
||||
self.root.keymaps.copy('main', '<PageUp>', '<A-K>')
|
||||
self.root.keymaps.bind('main', '<PageDown>', partial(screen_move, direction=+1))
|
||||
self.root.keymaps.copy('main', '<PageDown>', ']')
|
||||
self.root.keymaps.copy('main', '<PageDown>', '<A-J>')
|
||||
self.root.keymaps.bind('main', '<Space>', tag)
|
||||
|
||||
self.root.keymaps.bind('main', 'T', terminate)
|
||||
self.root.keymaps.bind('main', 'K', kill)
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ class ProcessPanel(Displayable): # pylint: disable=too-many-instance-attributes
|
|||
|
||||
if self.selected.is_set():
|
||||
identity = self.selected.identity
|
||||
self.selected.clear()
|
||||
self.selected.reset()
|
||||
for i, process in enumerate(snapshots):
|
||||
if process._ident == identity: # pylint: disable=protected-access
|
||||
self.selected.index = i
|
||||
|
|
@ -422,7 +422,7 @@ class ProcessPanel(Displayable): # pylint: disable=too-many-instance-attributes
|
|||
self.color_at(self.y + 3, self.x + offset + column_width - 1, width=1, attr='bold')
|
||||
|
||||
if self.y_mouse is not None:
|
||||
self.selected.clear()
|
||||
self.selected.reset()
|
||||
|
||||
self.selected.within_window = False
|
||||
if len(self.snapshots) > 0:
|
||||
|
|
@ -476,7 +476,11 @@ class ProcessPanel(Displayable): # pylint: disable=too-many-instance-attributes
|
|||
|
||||
if self.selected.is_same(process):
|
||||
self.color_at(
|
||||
y, self.x + 1, width=self.width - 2, fg='cyan', attr='bold | reverse'
|
||||
y,
|
||||
self.x + 1,
|
||||
width=self.width - 2,
|
||||
fg='yellow' if self.selected.is_tagged(process) else 'cyan',
|
||||
attr='bold | reverse',
|
||||
)
|
||||
self.selected.within_window = (
|
||||
self.root.y <= y < self.root.termsize[0] and self.width >= 79
|
||||
|
|
@ -485,8 +489,10 @@ class ProcessPanel(Displayable): # pylint: disable=too-many-instance-attributes
|
|||
if self.selected.is_same_on_host(process):
|
||||
self.addstr(y, self.x + 1, '=', self.get_fg_bg_attr(attr='bold | blink'))
|
||||
self.color_at(y, self.x + 2, width=3, fg=color)
|
||||
if str(process.username) != USERNAME and not SUPERUSER:
|
||||
self.color_at(y, self.x + 5, width=self.width - 6, attr='dim')
|
||||
if self.selected.is_tagged(process):
|
||||
self.color_at(y, self.x + 5, width=self.width - 6, fg='yellow', attr='bold')
|
||||
elif str(process.username) != USERNAME and not SUPERUSER:
|
||||
self.color_at(y, self.x + 5, width=self.width - 6, attr='dim')
|
||||
if is_zombie or no_permissions:
|
||||
self.color_at(y, self.x + 38 + command_offset, width=14, fg='yellow')
|
||||
elif is_gone:
|
||||
|
|
@ -498,7 +504,7 @@ class ProcessPanel(Displayable): # pylint: disable=too-many-instance-attributes
|
|||
self.addstr(self.y + 5, self.x, '│ {} │'.format(message.ljust(self.width - 4)))
|
||||
|
||||
text_offset = self.x + self.width - 47
|
||||
if self.selected.owned() and self.selected.within_window:
|
||||
if len(self.selected.tagged) > 0 or (self.selected.owned() and self.selected.within_window):
|
||||
self.addstr(self.y, text_offset, '(Press ^C(INT)/T(TERM)/K(KILL) to send signals)')
|
||||
self.color_at(self.y, text_offset + 7, width=2, fg='magenta', attr='bold | italic')
|
||||
self.color_at(self.y, text_offset + 10, width=3, fg='red', attr='bold')
|
||||
|
|
|
|||
|
|
@ -6,12 +6,15 @@
|
|||
import signal
|
||||
import time
|
||||
from collections import namedtuple
|
||||
from operator import xor
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
from nvitop.gui.library import LARGE_INTEGER, NA, SUPERUSER, USERNAME, Snapshot, host
|
||||
|
||||
|
||||
class Selected:
|
||||
def __init__(self, panel):
|
||||
self.tagged = WeakValueDictionary()
|
||||
self.panel = panel
|
||||
self.index = None
|
||||
self.within_window = True
|
||||
|
|
@ -86,16 +89,35 @@ class Selected:
|
|||
return True
|
||||
return self.username == USERNAME
|
||||
|
||||
def send_signal(self, sig):
|
||||
if self.owned() and self.within_window:
|
||||
def tag(self):
|
||||
if self.is_set():
|
||||
try:
|
||||
self.process.send_signal(sig)
|
||||
del self.tagged[self.pid]
|
||||
except KeyError:
|
||||
self.tagged[self.pid] = self.process
|
||||
|
||||
def foreach(self, func):
|
||||
if len(self.tagged) > 0:
|
||||
processes = tuple(self.tagged.values())
|
||||
elif self.owned() and self.within_window:
|
||||
processes = (self.process,)
|
||||
else:
|
||||
return
|
||||
|
||||
for process in processes:
|
||||
try:
|
||||
func(process)
|
||||
except host.PsutilError:
|
||||
pass
|
||||
else:
|
||||
time.sleep(0.5)
|
||||
if not self.process.is_running():
|
||||
self.clear()
|
||||
flag = True
|
||||
|
||||
if flag:
|
||||
time.sleep(0.5)
|
||||
self.clear()
|
||||
|
||||
def send_signal(self, sig):
|
||||
self.foreach(lambda process: process.send_signal(sig))
|
||||
|
||||
def interrupt(self):
|
||||
try:
|
||||
|
|
@ -108,29 +130,21 @@ class Selected:
|
|||
pass
|
||||
|
||||
def terminate(self):
|
||||
if self.owned() and self.within_window:
|
||||
try:
|
||||
self.process.terminate()
|
||||
except host.PsutilError:
|
||||
pass
|
||||
else:
|
||||
time.sleep(0.5)
|
||||
self.clear()
|
||||
self.foreach(lambda process: process.terminate())
|
||||
|
||||
def kill(self):
|
||||
if self.owned() and self.within_window:
|
||||
try:
|
||||
self.process.kill()
|
||||
except host.PsutilError:
|
||||
pass
|
||||
else:
|
||||
time.sleep(0.5)
|
||||
self.clear()
|
||||
self.foreach(lambda process: process.kill())
|
||||
|
||||
def reset(self):
|
||||
self.index = None
|
||||
self.within_window = True
|
||||
self._process = None
|
||||
self._username = None
|
||||
self._ident = None
|
||||
|
||||
def clear(self):
|
||||
self.__init__(self.panel) # pylint: disable=unnecessary-dunder-call
|
||||
|
||||
reset = clear
|
||||
self.tagged.clear()
|
||||
self.reset()
|
||||
|
||||
def is_set(self):
|
||||
return self.process is not None
|
||||
|
|
@ -155,5 +169,8 @@ class Selected:
|
|||
|
||||
return False
|
||||
|
||||
def is_tagged(self, process):
|
||||
return process.pid in self.tagged
|
||||
|
||||
|
||||
Order = namedtuple('Order', ['key', 'reverse', 'offset', 'column', 'previous', 'next'])
|
||||
|
|
|
|||
|
|
@ -252,6 +252,7 @@ class Top(DisplayableContainer): # pylint: disable=too-many-instance-attributes
|
|||
|
||||
if not self.treeview_screen.selected.is_set():
|
||||
self.treeview_screen.selected.process = self.main_screen.selected.process
|
||||
self.main_screen.selected.clear()
|
||||
|
||||
def show_environ():
|
||||
show_screen(self.environ_screen, focused=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue