Updated to conform to PEP-8, based on suggestions from SublimeLinter.
This commit is contained in:
parent
634e514a6c
commit
24112ed67b
525
crussh.py
525
crussh.py
@ -12,315 +12,318 @@ import json
|
|||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import gtk
|
import gtk
|
||||||
except:
|
except:
|
||||||
print >>sys.stderr, "Missing Python GTK2 bindings."
|
print >>sys.stderr, "Missing Python GTK2 bindings."
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import vte
|
import vte
|
||||||
except:
|
except:
|
||||||
print >>sys.stderr, "Missing Python VTE bindings."
|
print >>sys.stderr, "Missing Python VTE bindings."
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
### Config Dialog ###
|
### Config Dialog ###
|
||||||
class CruSSHConf:
|
class CruSSHConf:
|
||||||
### State Vars ###
|
### State Vars ###
|
||||||
Config = {}
|
Config = {}
|
||||||
MainWin = gtk.Window()
|
MainWin = gtk.Window()
|
||||||
|
|
||||||
### Signal Hooks ###
|
### Signal Hooks ###
|
||||||
def save_hook(self, discard, save_func):
|
def save_hook(self, discard, save_func):
|
||||||
self.MainWin.destroy()
|
self.MainWin.destroy()
|
||||||
if save_func is not None:
|
if save_func is not None:
|
||||||
save_func(self.Config)
|
save_func(self.Config)
|
||||||
|
|
||||||
def font_hook(self, fontbutton):
|
def font_hook(self, fontbutton):
|
||||||
self.Config["font"] = fontbutton.get_font_name()
|
self.Config["font"] = fontbutton.get_font_name()
|
||||||
|
|
||||||
def opacity_hook(self, range):
|
def opacity_hook(self, range):
|
||||||
self.Config["opacity"] = range.get_value()
|
self.Config["opacity"] = range.get_value()
|
||||||
|
|
||||||
def width_hook(self, spinbutton):
|
def width_hook(self, spinbutton):
|
||||||
self.Config["min-width"] = spinbutton.get_value_as_int()
|
self.Config["min-width"] = spinbutton.get_value_as_int()
|
||||||
|
|
||||||
def height_hook(self, spinbutton):
|
def height_hook(self, spinbutton):
|
||||||
self.Config["min-height"] = spinbutton.get_value_as_int()
|
self.Config["min-height"] = spinbutton.get_value_as_int()
|
||||||
|
|
||||||
### GUI Objects ###
|
### GUI Objects ###
|
||||||
def initGUI(self, save_func=None):
|
def initGUI(self, save_func=None):
|
||||||
self.MainWin.set_modal(True)
|
self.MainWin.set_modal(True)
|
||||||
self.MainWin.props.allow_grow = False
|
self.MainWin.props.allow_grow = False
|
||||||
|
|
||||||
MainBox = gtk.VBox(spacing=5)
|
MainBox = gtk.VBox(spacing=5)
|
||||||
MainBox.props.border_width = 5
|
MainBox.props.border_width = 5
|
||||||
self.MainWin.add(MainBox)
|
self.MainWin.add(MainBox)
|
||||||
|
|
||||||
TermConfFrame = gtk.Frame(label="Terminal Options")
|
TermConfFrame = gtk.Frame(label="Terminal Options")
|
||||||
TermConfTable = gtk.Table(3, 2)
|
TermConfTable = gtk.Table(3, 2)
|
||||||
TermConfTable.props.border_width = 5
|
TermConfTable.props.border_width = 5
|
||||||
TermConfTable.props.row_spacing = 5
|
TermConfTable.props.row_spacing = 5
|
||||||
TermConfTable.props.column_spacing = 5
|
TermConfTable.props.column_spacing = 5
|
||||||
TermConfFrame.add(TermConfTable)
|
TermConfFrame.add(TermConfTable)
|
||||||
MainBox.pack_start(TermConfFrame)
|
MainBox.pack_start(TermConfFrame)
|
||||||
|
|
||||||
TermConfTable.attach(gtk.Label("Font:"), 1, 2, 1, 2, gtk.EXPAND)
|
TermConfTable.attach(gtk.Label("Font:"), 1, 2, 1, 2, gtk.EXPAND)
|
||||||
FontConf = gtk.FontButton(fontname=self.Config["font"])
|
FontConf = gtk.FontButton(fontname=self.Config["font"])
|
||||||
FontConf.connect("font-set", self.font_hook)
|
FontConf.connect("font-set", self.font_hook)
|
||||||
TermConfTable.attach(FontConf, 2, 3, 1, 2, gtk.EXPAND)
|
TermConfTable.attach(FontConf, 2, 3, 1, 2, gtk.EXPAND)
|
||||||
|
|
||||||
SizeBox = gtk.HBox()
|
SizeBox = gtk.HBox()
|
||||||
SizeBox.props.spacing = 5
|
SizeBox.props.spacing = 5
|
||||||
TermConfTable.attach(SizeBox, 1, 3, 2, 3)
|
TermConfTable.attach(SizeBox, 1, 3, 2, 3)
|
||||||
SizeBox.pack_start(gtk.Label("Min Width:"), fill=False, expand=False)
|
SizeBox.pack_start(gtk.Label("Min Width:"), fill=False, expand=False)
|
||||||
WidthEntry = gtk.SpinButton(gtk.Adjustment(value=self.Config["min-width"], lower=1, upper=9999, step_incr=1))
|
WidthEntry = gtk.SpinButton(gtk.Adjustment(value=self.Config["min-width"], lower=1, upper=9999, step_incr=1))
|
||||||
WidthEntry.connect("value-changed", self.width_hook)
|
WidthEntry.connect("value-changed", self.width_hook)
|
||||||
SizeBox.pack_start(WidthEntry, fill=False, expand=False)
|
SizeBox.pack_start(WidthEntry, fill=False, expand=False)
|
||||||
SizeBox.pack_start(gtk.Label("Min Height:"), fill=False, expand=False)
|
SizeBox.pack_start(gtk.Label("Min Height:"), fill=False, expand=False)
|
||||||
HeightEntry = gtk.SpinButton(gtk.Adjustment(value=self.Config["min-height"], lower=1, upper=9999, step_incr=1))
|
HeightEntry = gtk.SpinButton(gtk.Adjustment(value=self.Config["min-height"], lower=1, upper=9999, step_incr=1))
|
||||||
HeightEntry.connect("value-changed", self.height_hook)
|
HeightEntry.connect("value-changed", self.height_hook)
|
||||||
SizeBox.pack_start(HeightEntry, fill=False, expand=False)
|
SizeBox.pack_start(HeightEntry, fill=False, expand=False)
|
||||||
|
|
||||||
TermConfTable.attach(gtk.Label("Opacity:"), 1, 2, 3, 4, gtk.EXPAND)
|
TermConfTable.attach(gtk.Label("Opacity:"), 1, 2, 3, 4, gtk.EXPAND)
|
||||||
OpacityAdj = gtk.Adjustment(upper=65535, step_incr=1, value=self.Config["opacity"])
|
OpacityAdj = gtk.Adjustment(upper=65535, step_incr=1, value=self.Config["opacity"])
|
||||||
OpacityScale = gtk.HScale(OpacityAdj)
|
OpacityScale = gtk.HScale(OpacityAdj)
|
||||||
OpacityScale.set_draw_value(False)
|
OpacityScale.set_draw_value(False)
|
||||||
# disconnect this until we get it working.
|
# disconnect this until we get it working.
|
||||||
# OpacityScale.connect("value-changed", self.opacity_hook)
|
# OpacityScale.connect("value-changed", self.opacity_hook)
|
||||||
TermConfTable.attach(OpacityScale, 2, 3, 3, 4)
|
TermConfTable.attach(OpacityScale, 2, 3, 3, 4)
|
||||||
|
|
||||||
ConfirmBox = gtk.HBox(spacing=5)
|
ConfirmBox = gtk.HBox(spacing=5)
|
||||||
CancelButton = gtk.Button(stock=gtk.STOCK_CANCEL)
|
CancelButton = gtk.Button(stock=gtk.STOCK_CANCEL)
|
||||||
ConfirmBox.pack_start(CancelButton, fill=False)
|
ConfirmBox.pack_start(CancelButton, fill=False)
|
||||||
SaveButton = gtk.Button(stock=gtk.STOCK_SAVE)
|
SaveButton = gtk.Button(stock=gtk.STOCK_SAVE)
|
||||||
ConfirmBox.pack_start(SaveButton, fill=False)
|
ConfirmBox.pack_start(SaveButton, fill=False)
|
||||||
MainBox.pack_start(ConfirmBox, fill=False, expand=False)
|
MainBox.pack_start(ConfirmBox, fill=False, expand=False)
|
||||||
|
|
||||||
# wire up behaviour
|
# wire up behaviour
|
||||||
CancelButton.connect("clicked", lambda discard: self.MainWin.destroy())
|
CancelButton.connect("clicked", lambda discard: self.MainWin.destroy())
|
||||||
SaveButton.connect("clicked", self.save_hook, save_func)
|
SaveButton.connect("clicked", self.save_hook, save_func)
|
||||||
|
|
||||||
self.MainWin.show_all()
|
self.MainWin.show_all()
|
||||||
|
|
||||||
# we'll wire up a supplied save_func that takes the Config dict as an argument.
|
# we'll wire up a supplied save_func that takes the Config dict as an argument.
|
||||||
def __init__(self, config=None, save_func=None):
|
def __init__(self, config=None, save_func=None):
|
||||||
if config is not None:
|
if config is not None:
|
||||||
self.Config = config
|
self.Config = config
|
||||||
|
|
||||||
|
self.initGUI(save_func)
|
||||||
|
|
||||||
self.initGUI(save_func)
|
|
||||||
|
|
||||||
### CruSSH! ###
|
### CruSSH! ###
|
||||||
class CruSSH:
|
class CruSSH:
|
||||||
### Config Vars ###
|
### Config Vars ###
|
||||||
# config defaults
|
# config defaults
|
||||||
Config = {
|
Config = {
|
||||||
"min-width": 80,
|
"min-width": 80,
|
||||||
"min-height": 24,
|
"min-height": 24,
|
||||||
"font": "Ubuntu Mono Bold 10",
|
"font": "Ubuntu Mono Bold 10",
|
||||||
"opacity": 65535
|
"opacity": 65535
|
||||||
}
|
}
|
||||||
|
|
||||||
### State Vars ###
|
### State Vars ###
|
||||||
Terminals = {}
|
Terminals = {}
|
||||||
TermMinWidth = 1
|
TermMinWidth = 1
|
||||||
TermMinHeight = 1
|
TermMinHeight = 1
|
||||||
|
|
||||||
### GUI Objects ###
|
### GUI Objects ###
|
||||||
MainWin = gtk.Window()
|
MainWin = gtk.Window()
|
||||||
ScrollWin = gtk.ScrolledWindow()
|
ScrollWin = gtk.ScrolledWindow()
|
||||||
LayoutTable = gtk.Table()
|
LayoutTable = gtk.Table()
|
||||||
EntryBox = gtk.Entry()
|
EntryBox = gtk.Entry()
|
||||||
|
|
||||||
### Methods ###
|
### Methods ###
|
||||||
def reflowTable(self, cols=1, rows=1):
|
def reflowTable(self, cols=1, rows=1):
|
||||||
# empty table and re-size
|
# empty table and re-size
|
||||||
hosts = sorted(self.Terminals.keys(), reverse=True)
|
hosts = sorted(self.Terminals.keys(), reverse=True)
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
if self.Terminals[host].parent == self.LayoutTable:
|
if self.Terminals[host].parent == self.LayoutTable:
|
||||||
self.LayoutTable.remove(self.Terminals[host])
|
self.LayoutTable.remove(self.Terminals[host])
|
||||||
self.LayoutTable.resize(rows, cols)
|
self.LayoutTable.resize(rows, cols)
|
||||||
# layout terminals
|
# layout terminals
|
||||||
for row in range(rows):
|
for row in range(rows):
|
||||||
for col in range(cols):
|
for col in range(cols):
|
||||||
if len(hosts) > 0:
|
if len(hosts) > 0:
|
||||||
host = hosts.pop()
|
host = hosts.pop()
|
||||||
self.Terminals[host].set_size(self.Config["min-width"], self.Config["min-height"])
|
self.Terminals[host].set_size(self.Config["min-width"], self.Config["min-height"])
|
||||||
self.LayoutTable.attach(self.Terminals[host], col, col+1, row, row+1)
|
self.LayoutTable.attach(self.Terminals[host], col, col + 1, row, row + 1)
|
||||||
|
|
||||||
def reflow(self, force=False):
|
def reflow(self, force=False):
|
||||||
# reconfigure before updating rows and columns
|
# reconfigure before updating rows and columns
|
||||||
self.configTerminals()
|
self.configTerminals()
|
||||||
|
|
||||||
num_terms = len(self.Terminals.keys())
|
num_terms = len(self.Terminals.keys())
|
||||||
if num_terms < 1:
|
if num_terms < 1:
|
||||||
gtk.main_quit()
|
gtk.main_quit()
|
||||||
# main_quit desn't happen immediately
|
# main_quit desn't happen immediately
|
||||||
return False
|
return False
|
||||||
size = self.MainWin.allocation
|
size = self.MainWin.allocation
|
||||||
cols = int(math.floor((size.width + self.LayoutTable.props.column_spacing) / float(self.TermMinWidth)))
|
cols = int(math.floor((size.width + self.LayoutTable.props.column_spacing) / float(self.TermMinWidth)))
|
||||||
if cols < 1 or num_terms == 1:
|
if cols < 1 or num_terms == 1:
|
||||||
cols = 1
|
cols = 1
|
||||||
elif cols > num_terms:
|
elif cols > num_terms:
|
||||||
cols = num_terms
|
cols = num_terms
|
||||||
rows = int(math.ceil(num_terms/float(cols)))
|
rows = int(math.ceil(num_terms / float(cols)))
|
||||||
if rows < 1:
|
if rows < 1:
|
||||||
rows = 1
|
rows = 1
|
||||||
if (self.LayoutTable.props.n_columns != cols) or (self.LayoutTable.props.n_rows != rows) or force:
|
if (self.LayoutTable.props.n_columns != cols) or (self.LayoutTable.props.n_rows != rows) or force:
|
||||||
self.reflowTable(cols, rows)
|
self.reflowTable(cols, rows)
|
||||||
self.MainWin.show_all()
|
self.MainWin.show_all()
|
||||||
|
|
||||||
def configTerminals(self):
|
def configTerminals(self):
|
||||||
for host in self.Terminals:
|
for host in self.Terminals:
|
||||||
terminal = self.Terminals[host]
|
terminal = self.Terminals[host]
|
||||||
# -1 == infinite scrollback
|
# -1 == infinite scrollback
|
||||||
terminal.set_scrollback_lines(-1)
|
terminal.set_scrollback_lines(-1)
|
||||||
terminal.set_size(self.Config["min-width"], self.Config["min-height"])
|
terminal.set_size(self.Config["min-width"], self.Config["min-height"])
|
||||||
terminal.set_font_from_string(self.Config["font"])
|
terminal.set_font_from_string(self.Config["font"])
|
||||||
terminal.set_opacity(int(self.Config["opacity"]))
|
terminal.set_opacity(int(self.Config["opacity"]))
|
||||||
self.TermMinWidth = (terminal.get_char_width() * self.Config["min-width"]) + terminal.get_padding()[0]
|
self.TermMinWidth = (terminal.get_char_width() * self.Config["min-width"]) + terminal.get_padding()[0]
|
||||||
self.TermMinHeight = (terminal.get_char_height() * self.Config["min-height"]) + terminal.get_padding()[1]
|
self.TermMinHeight = (terminal.get_char_height() * self.Config["min-height"]) + terminal.get_padding()[1]
|
||||||
|
|
||||||
def removeTerminal(self, terminal):
|
def removeTerminal(self, terminal):
|
||||||
# brute force search since we don't actually know the hostname from the
|
# brute force search since we don't actually know the hostname from the
|
||||||
# terminal object. this is an infrequent operation, so it should be fine.
|
# terminal object. this is an infrequent operation, so it should be fine.
|
||||||
for host in self.Terminals.keys():
|
for host in self.Terminals.keys():
|
||||||
if terminal == self.Terminals[host]:
|
if terminal == self.Terminals[host]:
|
||||||
self.LayoutTable.remove(self.Terminals[host])
|
self.LayoutTable.remove(self.Terminals[host])
|
||||||
del self.Terminals[host]
|
del self.Terminals[host]
|
||||||
self.reflow(force=True)
|
self.reflow(force=True)
|
||||||
|
|
||||||
def initGUI(self):
|
def initGUI(self):
|
||||||
theme = gtk.icon_theme_get_default()
|
theme = gtk.icon_theme_get_default()
|
||||||
icon_list = []
|
if theme.has_icon("terminal"):
|
||||||
if theme.has_icon("terminal"):
|
icon = theme.lookup_icon("terminal", 128, flags=gtk.ICON_LOOKUP_USE_BUILTIN)
|
||||||
icon = theme.lookup_icon("terminal", 128, flags=gtk.ICON_LOOKUP_USE_BUILTIN)
|
if icon is not None:
|
||||||
if icon is not None:
|
gtk.window_set_default_icon(icon.load_icon())
|
||||||
gtk.window_set_default_icon(icon.load_icon())
|
self.MainWin.set_title("crussh: " + ' '.join(self.Terminals.keys()))
|
||||||
self.MainWin.set_title("crussh: " + ' '.join(self.Terminals.keys()))
|
self.MainWin.set_role(role="crussh_main_win")
|
||||||
self.MainWin.set_role(role="crussh_main_win")
|
self.MainWin.connect("delete-event", lambda window, event: gtk.main_quit())
|
||||||
self.MainWin.connect("delete-event", lambda window, event: gtk.main_quit())
|
MainVBox = gtk.VBox()
|
||||||
MainVBox = gtk.VBox()
|
self.MainWin.add(MainVBox)
|
||||||
self.MainWin.add(MainVBox)
|
|
||||||
|
|
||||||
MainMenuBar = gtk.MenuBar()
|
MainMenuBar = gtk.MenuBar()
|
||||||
MainVBox.pack_start(MainMenuBar, fill=True, expand=False)
|
MainVBox.pack_start(MainMenuBar, fill=True, expand=False)
|
||||||
|
|
||||||
FileItem = gtk.MenuItem(label="File")
|
FileItem = gtk.MenuItem(label="File")
|
||||||
FileMenu = gtk.Menu()
|
FileMenu = gtk.Menu()
|
||||||
FileItem.set_submenu(FileMenu)
|
FileItem.set_submenu(FileMenu)
|
||||||
QuitItem = gtk.MenuItem(label="Quit")
|
QuitItem = gtk.MenuItem(label="Quit")
|
||||||
QuitItem.connect("activate", lambda discard: gtk.main_quit())
|
QuitItem.connect("activate", lambda discard: gtk.main_quit())
|
||||||
FileMenu.append(QuitItem)
|
FileMenu.append(QuitItem)
|
||||||
MainMenuBar.append(FileItem)
|
MainMenuBar.append(FileItem)
|
||||||
|
|
||||||
EditItem = gtk.MenuItem(label="Edit")
|
EditItem = gtk.MenuItem(label="Edit")
|
||||||
EditMenu = gtk.Menu()
|
EditMenu = gtk.Menu()
|
||||||
EditItem.set_submenu(EditMenu)
|
EditItem.set_submenu(EditMenu)
|
||||||
PrefsItem = gtk.MenuItem(label="Preferences")
|
PrefsItem = gtk.MenuItem(label="Preferences")
|
||||||
def save_func(new_config):
|
|
||||||
self.Config = new_config
|
|
||||||
self.reflow(force=True)
|
|
||||||
# save to file last, so it doesn't hold up other GUI actions
|
|
||||||
conf_json = json.dumps(self.Config, sort_keys=True, indent=4)
|
|
||||||
try:
|
|
||||||
conf_file = open(os.path.expanduser("~/.crusshrc"), 'w')
|
|
||||||
conf_file.write(conf_json)
|
|
||||||
conf_file.close()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
PrefsItem.connect("activate", lambda discard: CruSSHConf(self.Config, save_func))
|
|
||||||
EditMenu.append(PrefsItem)
|
|
||||||
MainMenuBar.append(EditItem)
|
|
||||||
|
|
||||||
self.ScrollWin.props.hscrollbar_policy = gtk.POLICY_NEVER
|
def save_func(new_config):
|
||||||
self.ScrollWin.props.vscrollbar_policy = gtk.POLICY_ALWAYS
|
self.Config = new_config
|
||||||
self.ScrollWin.props.shadow_type = gtk.SHADOW_ETCHED_IN
|
self.reflow(force=True)
|
||||||
MainVBox.pack_start(self.ScrollWin)
|
# save to file last, so it doesn't hold up other GUI actions
|
||||||
|
conf_json = json.dumps(self.Config, sort_keys=True, indent=4)
|
||||||
|
try:
|
||||||
|
conf_file = open(os.path.expanduser("~/.crusshrc"), 'w')
|
||||||
|
conf_file.write(conf_json)
|
||||||
|
conf_file.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
PrefsItem.connect("activate", lambda discard: CruSSHConf(self.Config, save_func))
|
||||||
|
EditMenu.append(PrefsItem)
|
||||||
|
MainMenuBar.append(EditItem)
|
||||||
|
|
||||||
self.LayoutTable.set_homogeneous(True)
|
self.ScrollWin.props.hscrollbar_policy = gtk.POLICY_NEVER
|
||||||
self.LayoutTable.set_row_spacings(1)
|
self.ScrollWin.props.vscrollbar_policy = gtk.POLICY_ALWAYS
|
||||||
self.LayoutTable.set_col_spacings(1)
|
self.ScrollWin.props.shadow_type = gtk.SHADOW_ETCHED_IN
|
||||||
self.ScrollWin.add_with_viewport(self.LayoutTable)
|
MainVBox.pack_start(self.ScrollWin)
|
||||||
self.ScrollWin.set_size_request(self.TermMinWidth, self.TermMinHeight)
|
|
||||||
|
|
||||||
# don't display chars while typing.
|
self.LayoutTable.set_homogeneous(True)
|
||||||
self.EntryBox.set_visibility(False)
|
self.LayoutTable.set_row_spacings(1)
|
||||||
self.EntryBox.set_invisible_char(' ')
|
self.LayoutTable.set_col_spacings(1)
|
||||||
# forward key events to all terminals
|
self.ScrollWin.add_with_viewport(self.LayoutTable)
|
||||||
def feed_input(widget, event):
|
self.ScrollWin.set_size_request(self.TermMinWidth, self.TermMinHeight)
|
||||||
if event.type in [gtk.gdk.KEY_PRESS, gtk.gdk.KEY_RELEASE]:
|
|
||||||
# erase buffer on every entry, so that passwords aren't revealed
|
|
||||||
self.EntryBox.props.buffer.delete_text(0, -1)
|
|
||||||
# propagate to every terminal
|
|
||||||
for host in self.Terminals:
|
|
||||||
t_event = event.copy()
|
|
||||||
self.Terminals[host].event(t_event)
|
|
||||||
# this stops regular handler from firing, switching focus.
|
|
||||||
return True
|
|
||||||
self.EntryBox.connect("key_press_event", feed_input)
|
|
||||||
self.EntryBox.connect("key_release_event", feed_input)
|
|
||||||
MainVBox.pack_start(self.EntryBox, False, False)
|
|
||||||
|
|
||||||
# reflow layout on size change.
|
# don't display chars while typing.
|
||||||
self.MainWin.connect("size-allocate", lambda widget, allocation: self.reflow())
|
self.EntryBox.set_visibility(False)
|
||||||
|
self.EntryBox.set_invisible_char(' ')
|
||||||
|
|
||||||
# give EntryBox default focus on init
|
# forward key events to all terminals
|
||||||
self.EntryBox.props.has_focus = True
|
def feed_input(widget, event):
|
||||||
|
if event.type in [gtk.gdk.KEY_PRESS, gtk.gdk.KEY_RELEASE]:
|
||||||
|
# erase buffer on every entry, so that passwords aren't revealed
|
||||||
|
self.EntryBox.props.buffer.delete_text(0, -1)
|
||||||
|
# propagate to every terminal
|
||||||
|
for host in self.Terminals:
|
||||||
|
t_event = event.copy()
|
||||||
|
self.Terminals[host].event(t_event)
|
||||||
|
# this stops regular handler from firing, switching focus.
|
||||||
|
return True
|
||||||
|
self.EntryBox.connect("key_press_event", feed_input)
|
||||||
|
self.EntryBox.connect("key_release_event", feed_input)
|
||||||
|
MainVBox.pack_start(self.EntryBox, False, False)
|
||||||
|
|
||||||
def __init__(self, hosts, ssh_cmd="/usr/bin/ssh", ssh_args=None):
|
# reflow layout on size change.
|
||||||
# load existing config file, if present
|
self.MainWin.connect("size-allocate", lambda widget, allocation: self.reflow())
|
||||||
try:
|
|
||||||
# merge dicts to allow upgrade from old configs
|
|
||||||
new_config = json.load(open(os.path.expanduser('~/.crusshrc')))
|
|
||||||
self.Config.update(new_config)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# init all terminals
|
# give EntryBox default focus on init
|
||||||
for host in hosts:
|
self.EntryBox.props.has_focus = True
|
||||||
terminal = vte.Terminal()
|
|
||||||
# TODO: disable only this terminal widget on child exit
|
|
||||||
# v.connect("child-exited", lambda term: gtk.main_quit())
|
|
||||||
cmd_str = ssh_cmd
|
|
||||||
if ssh_args is not None:
|
|
||||||
cmd_str += " " + ssh_args
|
|
||||||
cmd_str += " " + host
|
|
||||||
cmd = cmd_str.split(' ')
|
|
||||||
terminal.fork_command(command=cmd[0], argv=cmd)
|
|
||||||
self.Terminals[host] = terminal
|
|
||||||
|
|
||||||
# hook terminals so they reflow layout on exit
|
def __init__(self, hosts, ssh_cmd="/usr/bin/ssh", ssh_args=None):
|
||||||
self.Terminals[host].connect("child-exited", self.removeTerminal)
|
# load existing config file, if present
|
||||||
# configure all terminals
|
try:
|
||||||
self.configTerminals()
|
# merge dicts to allow upgrade from old configs
|
||||||
# reflow after reconfig for font size changes
|
new_config = json.load(open(os.path.expanduser('~/.crusshrc')))
|
||||||
self.initGUI()
|
self.Config.update(new_config)
|
||||||
self.reflow(force=True)
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# init all terminals
|
||||||
|
for host in hosts:
|
||||||
|
terminal = vte.Terminal()
|
||||||
|
# TODO: disable only this terminal widget on child exit
|
||||||
|
# v.connect("child-exited", lambda term: gtk.main_quit())
|
||||||
|
cmd_str = ssh_cmd
|
||||||
|
if ssh_args is not None:
|
||||||
|
cmd_str += " " + ssh_args
|
||||||
|
cmd_str += " " + host
|
||||||
|
cmd = cmd_str.split(' ')
|
||||||
|
terminal.fork_command(command=cmd[0], argv=cmd)
|
||||||
|
self.Terminals[host] = terminal
|
||||||
|
|
||||||
|
# hook terminals so they reflow layout on exit
|
||||||
|
self.Terminals[host].connect("child-exited", self.removeTerminal)
|
||||||
|
# configure all terminals
|
||||||
|
self.configTerminals()
|
||||||
|
# reflow after reconfig for font size changes
|
||||||
|
self.initGUI()
|
||||||
|
self.reflow(force=True)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
### Parse CLI Args ###
|
### Parse CLI Args ###
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Connect to multiple hosts in parallel.",
|
description="Connect to multiple hosts in parallel.",
|
||||||
usage="%(prog)s [OPTIONS] [--] HOST [HOST ...]",
|
usage="%(prog)s [OPTIONS] [--] HOST [HOST ...]",
|
||||||
epilog="* NOTE: You can pass options to ssh if you add '--' before your list of hosts")
|
epilog="* NOTE: You can pass options to ssh if you add '--' before your list of hosts")
|
||||||
parser.add_argument("--ssh", dest='ssh', default="/usr/bin/ssh",
|
parser.add_argument("--ssh", dest='ssh', default="/usr/bin/ssh",
|
||||||
help="specify the SSH executable to use (default: %(default)s)")
|
help="specify the SSH executable to use (default: %(default)s)")
|
||||||
(args, hosts) = parser.parse_known_args()
|
(args, hosts) = parser.parse_known_args()
|
||||||
|
|
||||||
if len(hosts) == 0:
|
if len(hosts) == 0:
|
||||||
parser.print_usage()
|
parser.print_usage()
|
||||||
parser.exit(2)
|
parser.exit(2)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
offset = hosts.index("--")
|
offset = hosts.index("--")
|
||||||
except:
|
except:
|
||||||
ssh_args = None
|
ssh_args = None
|
||||||
else:
|
else:
|
||||||
ssh_args = " ".join(hosts[0:offset])
|
ssh_args = " ".join(hosts[0:offset])
|
||||||
hosts = hosts[offset + 1:]
|
hosts = hosts[offset + 1:]
|
||||||
|
|
||||||
### Start Execution ###
|
### Start Execution ###
|
||||||
crussh = CruSSH(hosts, args.ssh, ssh_args)
|
crussh = CruSSH(hosts, args.ssh, ssh_args)
|
||||||
gtk.main()
|
gtk.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user