|
# hgcmd.py - A simple dialog to execute random command for TortoiseHg
#
# Copyright 2007 TK Soh <teekaysoh@gmail.com>
# Copyright 2007 Steve Borho <steve@borho.org>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
import gtk
import gobject
import pango
import os
import sys
import threading
import Queue
from mercurial import ui
from tortoisehg.util.i18n import _
from tortoisehg.util import shlib, hglib
from tortoisehg.hgtk import gtklib, hgthread
class CmdDialog(gtk.Dialog):
def __init__(self, cmdline, progressbar=True, text=None):
if type(cmdline) is tuple:
self.cmdlist = list(cmdline)[1:]
cmdline = cmdline[0]
else:
self.cmdlist = []
title = text or ' '.join(cmdline)
if len(title) > 80:
title = hglib.tounicode(title)[:80] + '...'
title = hglib.toutf(title.replace('\n', ' '))
gtk.Dialog.__init__(self, title=title, flags=gtk.DIALOG_MODAL)
self.set_has_separator(False)
gtklib.set_tortoise_icon(self, 'hg.ico')
gtklib.set_tortoise_keys(self)
self.cmdline = cmdline
self.returncode = None
self.hgthread = None
self.set_default_size(520, 400)
self._button_stop = gtk.Button(_('Stop'))
self._button_stop.connect('clicked', self._on_stop_clicked)
self.action_area.pack_start(self._button_stop)
self._button_ok = gtk.Button(_('Close'))
self._button_ok.connect('clicked', self._on_ok_clicked)
self.action_area.pack_start(self._button_ok)
self.connect('thg-accept', self._on_ok_clicked)
self.connect('delete-event', self._delete)
self.connect('response', self._response)
self.pbar = None
if progressbar:
self.last_pbar_update = 0
hbox = gtk.HBox()
self.status_text = gtk.Label()
self.status_text.set_text(title)
self.status_text.set_alignment(0, 0.5)
self.status_text.set_ellipsize(pango.ELLIPSIZE_END)
hbox.pack_start(self.status_text, True, True, 3)
# Create a centering alignment object
align = gtk.Alignment(0.0, 0.0, 1, 0)
hbox.pack_end(align, False, False, 3)
align.show()
# create the progress bar
self.pbar = gtk.ProgressBar()
align.add(self.pbar)
self.pbar.pulse()
self.pbar.show()
self.vbox.pack_start(hbox, False, False, 3)
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_shadow_type(gtk.SHADOW_ETCHED_IN)
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.textview = gtk.TextView(buffer=None)
self.textview.set_editable(False)
fontlog = hglib.getfontconfig()['fontlog']
self.textview.modify_font(pango.FontDescription(fontlog))
scrolledwindow.add(self.textview)
self.textbuffer = self.textview.get_buffer()
gtklib.configstyles(ui.ui())
for tag, argdict in gtklib.TextBufferTags.iteritems():
self.textbuffer.create_tag(tag, **argdict)
self.vbox.pack_start(scrolledwindow, True, True)
self.connect('map_event', self._on_window_map_event)
self.show_all()
def _on_ok_clicked(self, button):
""" Ok button clicked handler. """
self.response(gtk.RESPONSE_ACCEPT)
def _on_stop_clicked(self, button):
if self.hgthread:
try:
self.hgthread.terminate()
except ValueError:
pass # race, thread was already terminated
def _delete(self, widget, event):
return True
def _response(self, widget, response_id):
if self.hgthread and self.hgthread.isAlive():
widget.emit_stop_by_name('response')
def _on_window_map_event(self, event, param):
if self.hgthread:
return
# Replace stdout file descriptor with our own pipe
def pollstdout(*args):
while True:
# blocking read of stdout pipe
o = os.read(self.readfd, 1024)
if o:
self.stdoutq.put(o)
else:
break
self.stdoutq = Queue.Queue()
if os.name == 'nt':
# Only capture stdout on Windows. This causes hard crashes
# on some other platforms. See issue #783
self.readfd, writefd = os.pipe()
self.oldstdout = os.dup(sys.__stdout__.fileno())
os.dup2(writefd, sys.__stdout__.fileno())
thread = threading.Thread(target=pollstdout, args=[])
thread.start()
self.hgthread = hgthread.HgThread(self.cmdline[1:])
self.hgthread.start()
self._button_ok.set_sensitive(False)
self._button_stop.set_sensitive(True)
gobject.timeout_add(10, self.process_queue)
def write(self, msg, append=True):
msg = hglib.toutf(msg)
if append:
enditer = self.textbuffer.get_end_iter()
self.textbuffer.insert(enditer, msg)
else:
self.textbuffer.set_text(msg)
def process_queue(self):
"""
Handle all the messages currently in the queue (if any).
"""
self.hgthread.process_dialogs()
enditer = self.textbuffer.get_end_iter()
while self.hgthread.geterrqueue().qsize():
try:
tags = gtklib.gettags('ui.error')
msg = hglib.toutf(self.hgthread.geterrqueue().get(0))
self.textbuffer.insert_with_tags_by_name(enditer, msg, *tags)
self.textview.scroll_to_mark(self.textbuffer.get_insert(), 0)
except Queue.Empty:
pass
while self.stdoutq.qsize():
try:
tags = gtklib.gettags('ui.error')
msg = hglib.toutf(self.stdoutq.get(0))
self.textbuffer.insert_with_tags_by_name(enditer, msg, *tags)
self.textview.scroll_to_mark(self.textbuffer.get_insert(), 0)
except Queue.Empty:
pass
while self.hgthread.getqueue().qsize():
try:
msg, label = self.hgthread.getqueue().get(0)
msg = hglib.toutf(msg)
tags = gtklib.gettags(label)
if tags:
self.textbuffer.insert_with_tags_by_name(enditer, msg, *tags)
else:
self.textbuffer.insert(enditer, msg)
self.textview.scroll_to_mark(self.textbuffer.get_insert(), 0)
except Queue.Empty:
pass
self.update_progress()
if not self.hgthread.isAlive():
self.returncode = self.hgthread.return_code()
if self.returncode is None:
self.write(_('\n[command interrupted]'))
elif self.returncode == 0 and self.cmdlist:
cmdline = self.cmdlist.pop(0)
text = '\n' + hglib.toutf(' '.join(cmdline)) + '\n'
self.textbuffer.insert(enditer, text)
self.textview.scroll_to_mark(self.textbuffer.get_insert(), 0)
self.hgthread = hgthread.HgThread(cmdline[1:])
self.hgthread.start()
return True
self._button_stop.set_sensitive(False)
self._button_ok.set_sensitive(True)
self._button_ok.grab_focus()
if os.name == 'nt':
os.dup2(self.oldstdout, sys.__stdout__.fileno())
os.close(self.oldstdout)
return False # Stop polling this function
else:
return True
def update_progress(self):
if not self.pbar:
return # progress bar not enabled
if not self.hgthread.isAlive():
self.pbar.unmap()
else:
# pulse the progress bar every ~100ms
tm = shlib.get_system_times()[4]
if tm - self.last_pbar_update < 0.100:
return
self.last_pbar_update = tm
self.pbar.pulse()
def return_code(self):
if self.hgthread:
return self.hgthread.return_code()
else:
return False
def get_buffer(self):
start = self.textbuffer.get_start_iter()
end = self.textbuffer.get_end_iter()
return self.textbuffer.get_text(start, end)
# CmdWidget style constants
STYLE_NORMAL = 'normal' # pbar + embedded log viewer
STYLE_COMPACT = 'compact' # pbar + popup log viewer
class CmdWidget(gtk.VBox):
def __init__(self, style=STYLE_NORMAL, tooltips=None, logsize=None):
"""
style: String. Predefined constans of CmdWidget style. Two styles,
STYLE_NORMAL (progress bar + popup log viewer) and
STYLE_COMPACT (progress bar + embedded log viewer) are
available. Default: STYLE_NORMAL.
tooltips: Reference. gtk.Tooltips instance to show tooltips of several
buttons. If omitted, a new instance of gtk.Tooltips will be
created. Default: None.
logsize: Tuple or list containing two numbers. Specify the size of the
embedded log viewer. size[0] = width, size[1] = height.
If you pass -1 as width or height size, it will be set to
the natural size of the widget. Default: tuple(-1, 180).
"""
gtk.VBox.__init__(self)
self.hgthread = None
self.last_pbar_update = 0
self.useraborted = False
self.is_normal = style == STYLE_NORMAL
self.is_compact = style == STYLE_COMPACT
# tooltips
if tooltips is None:
tooltips = gtklib.Tooltips()
# log viewer
if self.is_normal:
self.log = CmdLogWidget()
if logsize is None:
logsize = (-1, 180)
self.log.set_size_request(logsize[0], logsize[1])
self.log.size_request()
self.pack_start(self.log)
elif self.is_compact:
self.dlg = CmdLogDialog()
def close_hook(dialog):
self.show_log(False)
return False
self.dlg.set_close_hook(close_hook)
self.log = self.dlg.get_logwidget()
else:
raise _('unknown CmdWidget style: %s') % style
# progress status frame
statframe = gtk.Frame()
statframe.set_border_width(4)
statframe.set_shadow_type(gtk.SHADOW_NONE)
self.pack_start(statframe, False, False)
# progress bar box
self.progbox = progbox = gtklib.SlimToolbar(tooltips)
self.pack_start(progbox, False, False)
def add_button(stock_id, tooltip, handler, toggle=False):
btn = progbox.append_button(stock_id, tooltip, toggle)
btn.connect('clicked', handler)
return btn
## log toggle button
self.log_btn = add_button(gtk.STOCK_JUSTIFY_LEFT,
_('Toggle log window'), self.log_toggled, toggle=True)
## result frame
self.rframe = gtk.Frame()
progbox.append_widget(self.rframe, expand=True)
self.rframe.set_shadow_type(gtk.SHADOW_IN)
rhbox = gtk.HBox()
self.rframe.add(rhbox)
### result label
self.rlabel = gtk.Label()
rhbox.pack_end(self.rlabel, True, True, 2)
self.rlabel.set_alignment(0, 0.5)
def after_init():
# result icons
self.icons = {}
def add_icon(name, stock):
img = gtk.Image()
rhbox.pack_start(img, False, False, 2)
img.set_from_stock(stock, gtk.ICON_SIZE_SMALL_TOOLBAR)
self.icons[name] = img
add_icon('succeed', gtk.STOCK_APPLY)
add_icon('error', gtk.STOCK_DIALOG_ERROR)
# progres status label
self.progstat = gtk.Label()
statframe.add(self.progstat)
self.progstat.set_alignment(0, 0.5)
self.progstat.set_ellipsize(pango.ELLIPSIZE_END)
self.progstat.hide()
# progress bar
self.pbar = gtk.ProgressBar()
progbox.append_widget(self.pbar, expand=True)
self.pbar.hide()
# stop & close buttons
if self.is_compact:
self.stop_btn = add_button(gtk.STOCK_STOP,
_('Stop transaction'), self.stop_clicked)
self.close_btn = add_button(gtk.STOCK_CLOSE,
_('Close this'), self.close_clicked)
self.set_buttons(stop=False)
if self.is_normal:
self.show_log(False)
if self.is_compact:
self.set_pbar(False)
gtklib.idle_add_single_call(after_init)
### public functions ###
def execute(self, cmdline, callback, *args, **kargs):
"""
Execute passed command line using 'hgthread'.
When the command terminates, the callback function is invoked
with its return code.
cmdline: command line string.
callback: function invoked after the command terminates.
def callback(returncode, useraborted, ...)
returncode: See the description of 'hgthread'.
useraborted: Whether the command was aborted by the user.
"""
if self.hgthread:
return
# clear previous logs
self.log.clear()
# prepare UI
self.inprogress = False
self.switch_to(working=True)
self.set_buttons(stop=True, close=False)
self.already_opened = self.get_pbar()
if not self.already_opened:
def is_done():
# show progress bar if it's still working
if self.hgthread and self.hgthread.isAlive():
self.set_pbar(True)
return False
gobject.timeout_add(500, is_done)
# thread start
self.hgthread = hgthread.HgThread(cmdline[1:])
self.hgthread.start()
gobject.timeout_add(10, self.process_queue, callback, args, kargs)
def is_alive(self):
"""
Return whether the thread is alive.
"""
return self.hgthread and self.hgthread.isAlive()
def stop(self):
"""
Terminate the thread forcibly.
"""
if self.hgthread:
self.useraborted = True
try:
self.hgthread.terminate()
except ValueError:
pass # race, thread was already terminated
self.set_pbar(True)
self.set_buttons(stop=False, close=True)
def set_result(self, text, style=None):
"""
Put text message and icon in the progress bar.
style: String. If passed 'succeed', 'green' or 'ok', green
text and succeed icon will be shown. If passed 'error',
'failed', 'fail' or 'red', red text and error icon will be shown.
"""
markup = '<span foreground="%s" weight="%s">%%s</span>'
if style in ('succeed', 'green', 'ok'):
markup = markup % (gtklib.DGREEN, 'bold')
icons = {'succeed': True}
elif style in ('error', 'failed', 'fail', 'red'):
markup = markup % (gtklib.DRED, 'bold')
icons = {'error': True}
else:
markup = markup % (gtklib.NORMAL, 'normal')
icons = {}
text = gtklib.markup_escape_text(text)
self.rlabel.set_markup(markup % text)
self.set_icons(**icons)
def set_pbar(self, visible):
"""
Set visible property of the progress bar box.
visible: if True, the progress bar box is shown.
"""
if hasattr(self, 'progbox'):
self.progbox.set_property('visible', visible)
def get_pbar(self):
"""
Return 'visible' property of the progress bar box.
If there is no progress bar, it returns False.
"""
if hasattr(self, 'progbox'):
return self.progbox.get_property('visible')
return False
def set_buttons(self, log=None, stop=None, close=None):
"""
Set visible properties of buttons on the progress bar box.
If all arguments are omitted, it does nothing.
log: if True, log button is shown. (default: None)
stop: if True, stop button is shown. (default: None)
close: if True, close button is shown. (default: None)
"""
if not log is None and hasattr(self, 'log_btn'):
self.log_btn.set_property('visible', log)
if not stop is None and hasattr(self, 'stop_btn'):
self.stop_btn.set_property('visible', stop)
if not close is None and hasattr(self, 'close_btn'):
self.close_btn.set_property('visible', close)
def show_log(self, visible=True):
"""
Show/hide log viewer.
"""
if self.is_normal:
self.log.set_property('visible', visible)
elif self.is_compact:
if visible:
if self.dlg.get_property('visible'):
self.dlg.present()
else:
self.dlg.show_all()
else:
self.dlg.hide()
else:
raise _('invalid state')
# change toggle button state
if self.log_btn.get_active() != visible:
self.log_btn.handler_block_by_func(self.log_toggled)
self.log_btn.set_active(visible)
self.log_btn.handler_unblock_by_func(self.log_toggled)
def is_show_log(self):
"""
Return visible state of log viewer.
"""
if self.is_normal:
return self.log.get_property('visible')
elif self.is_compact:
return self.dlg.get_property('visible')
else:
raise _('invalid state')
### internal use functions ###
def clear_progress(self):
self.pbar.set_fraction(0)
self.pbar.set_text('')
self.progstat.set_text('')
self.inprogress = False
def update_progress(self):
if not self.pbar:
return
if not self.hgthread.isAlive():
self.clear_progress()
return
data = None
while self.hgthread.getprogqueue().qsize():
try:
data = self.hgthread.getprogqueue().get_nowait()
except Queue.Empty:
pass
counting = False
if data:
topic, item, pos, total, unit = data
if pos is None:
self.clear_progress()
return
if total is None:
count = '%d' % pos
counting = True
else:
self.pbar.set_fraction(float(pos) / float(total))
count = '%d / %d' % (pos, total)
if unit:
count += ' ' + unit
self.pbar.set_text(count)
if item:
status = '%s: %s' % (topic, item)
else:
status = _('Status: %s') % topic
self.progstat.set_text(status)
if self.progstat.get_property('visible') is False:
self.progstat.show()
self.inprogress = True
if not self.inprogress or counting:
# pulse the progress bar every ~100ms
tm = shlib.get_system_times()[4]
if tm - self.last_pbar_update < 0.100:
return
self.last_pbar_update = tm
self.pbar.pulse()
def process_queue(self, callback, args, kargs):
# process queue
self.hgthread.process_dialogs()
# output to buffer
while self.hgthread.geterrqueue().qsize():
try:
msg = self.hgthread.geterrqueue().get(0)
self.log.append(hglib.toutf(msg), error=True)
except Queue.Empty:
pass
while self.hgthread.getqueue().qsize():
try:
msg, label = self.hgthread.getqueue().get(0)
self.log.append(hglib.toutf(msg))
except Queue.Empty:
pass
# update progress bar
self.update_progress()
# check thread
if not self.hgthread.isAlive():
returncode = self.hgthread.return_code()
if returncode == 0 and not self.already_opened:
self.set_pbar(False)
else:
self.set_pbar(True)
self.switch_to(ready=True)
self.set_buttons(stop=False, close=True)
if returncode is None:
self.log.append(_('\n[command interrupted]'))
if not returncode == 0:
self.show_log()
self.hgthread = None
def call_callback():
callback(returncode, self.useraborted, *args, **kargs)
self.useraborted = False
gtklib.idle_add_single_call(call_callback)
return False # Stop polling this function
else:
return True # Continue polling
def set_icons(self, **kargs):
for name, img in self.icons.items():
visible = kargs.get(name, False)
img.set_property('visible', visible)
def switch_to(self, ready=False, working=False):
if ready:
self.rframe.show()
self.pbar.hide()
elif working:
self.rframe.hide()
self.pbar.show()
### signal handlers ###
def log_toggled(self, button):
self.show_log(button.get_active())
def stop_clicked(self, button):
self.stop()
def close_clicked(self, button):
self.set_pbar(False)
class CmdLogWidget(gtk.VBox):
def __init__(self):
gtk.VBox.__init__(self)
# scrolled pane
pane = gtk.ScrolledWindow()
pane.set_shadow_type(gtk.SHADOW_ETCHED_IN)
pane.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.add(pane)
# log textview
self.textview = gtk.TextView(buffer=None)
self.textview.set_editable(False)
fontlog = hglib.getfontconfig()['fontlog']
self.textview.modify_font(pango.FontDescription(fontlog))
pane.add(self.textview)
# text buffer
self.buffer = self.textview.get_buffer()
for tag, argdict in gtklib.TextBufferTags.iteritems():
self.buffer.create_tag(tag, **argdict)
### public functions ###
def append(self, text, error=False):
"""
Insert text at the end of the TextView.
text: string you want to append.
error: if True, append text with 'error' tag. (default: False)
"""
enditer = self.buffer.get_end_iter()
if error:
tags = gtklib.gettags('ui.error')
self.buffer.insert_with_tags_by_name(enditer, text, *tags)
else:
self.buffer.insert(enditer, text)
self.textview.scroll_to_mark(self.buffer.get_insert(), 0)
def clear(self):
"""
Clear all text in the TextView.
"""
self.buffer.delete(self.buffer.get_start_iter(),
self.buffer.get_end_iter())
class CmdLogDialog(gtk.Window):
def __init__(self, title=_('Command Log')):
gtk.Window.__init__(self, type=gtk.WINDOW_TOPLEVEL)
gtklib.set_tortoise_icon(self, 'hg.ico')
accelgroup, mod = gtklib.set_tortoise_keys(self)
self.set_title(title)
self.set_default_size(320, 240)
self.connect('delete-event', self.should_live)
# accelerators
key, modifier = gtk.accelerator_parse('Escape')
self.add_accelerator('thg-close', accelgroup, key, modifier,
gtk.ACCEL_VISIBLE)
self.connect('thg-close', self.should_live)
self.connect('thg-exit', self.should_live)
# log viewer
self.log = CmdLogWidget()
self.add(self.log)
# change window decorations
self.realize()
if self.window:
self.window.set_decorations(gtk.gdk.DECOR_BORDER | \
gtk.gdk.DECOR_RESIZEH | \
gtk.gdk.DECOR_TITLE | \
gtk.gdk.DECOR_MENU | \
gtk.gdk.DECOR_MINIMIZE)
### public functions ###
def get_logwidget(self):
"""
Return CmdLogWidget instance.
"""
return self.log
def set_close_hook(self, hook):
"""
Set hook function.
hook: the function called when this dialog is closed.
def close_hook(dialog)
where 'dialog' is an instance of the CmdLogDialog class.
The hook function should return True or False.
If True is returned, closing the dialog is prevented.
"""
self.close_hook = hook
### signal handlers ###
def should_live(self, *args):
if hasattr(self, 'close_hook'):
if self.close_hook(self):
self.hide()
return True
# Structured log types for CmdRunner
LOG_NORMAL = 0
LOG_ERROR = 1
class CmdRunner(object):
"""
Interactive command runner without GUI.
By default, there is no GUI (as opposed to CmdDialog).
If user interaction is needed (e.g. HTTPS auth), a simple
input dialog will be shown.
"""
def __init__(self):
self.hgthread = None
self.dlg = CmdLogDialog()
def close_hook(dialog):
self.show_log(False)
return False
self.dlg.set_close_hook(close_hook)
self.clear_buffers()
### public functions ###
def execute(self, cmdline, callback, *args, **kargs):
"""
Execute passed command line using 'hgthread'.
When the command terminates, the callback function is invoked
with its return code.
cmdline: a list of command line arguments or its tuple/list.
All command line arguments must be string, not int or long.
callback: function invoked after the command terminates.
def callback(returncode, useraborted, ...)
returncode: See the description of 'hgthread'.
useraborted: Whether the command was aborted by the user.
return: True if the command was started,
False if a command is already running.
"""
if self.hgthread:
return False
# clear previous logs
self.clear_buffers()
# normalize 'cmdline' arguments
if isinstance(cmdline[0], basestring):
cmdline = (cmdline,)
self.cmdlist = list(cmdline)
# execute cmdline
self.execute_next(callback, args, kargs)
return True
def is_alive(self):
"""
Return whether the thread is alive.
"""
return self.hgthread and self.hgthread.isAlive()
def stop(self):
"""
Terminate the thread forcibly.
"""
if self.hgthread:
try:
self.hgthread.terminate()
except ValueError:
pass # race, thread was already terminated
def get_buffer(self):
"""
Return buffer containing all messages.
Note that the buffer will be cleared when the 'execute' method
is called, so you need to store this before next execution.
"""
return ''.join([chunk[0] for chunk in self.buffer])
def get_raw_buffer(self):
"""
Return structured buffer.
Note that the buffer will be cleared when the 'execute' method
is called, so you need to store this before next execution.
"""
return self.buffer
def get_msg_buffer(self):
"""
Return buffer with regular messages.
Note that the buffer will be cleared when the 'execute' method
is called, so you need to store this before next execution.
"""
return ''.join([chunk[0] for chunk in self.buffer \
if chunk[1] == LOG_NORMAL])
def get_err_buffer(self):
"""
Return buffer with error messages.
Note that the buffer will be cleared when the 'execute' method
is called, so you need to store this before next execution.
"""
return ''.join([chunk[0] for chunk in self.buffer \
if chunk[1] == LOG_ERROR])
def set_title(self, title):
"""
Set the title of the command log window.
"""
self.dlg.set_title(title)
### internal use functions ###
def clear_buffers(self):
"""
Clear both message and error buffers.
"""
self.buffer = []
self.dlg.log.clear()
def show_log(self, visible=True):
if visible:
if self.dlg.get_property('visible'):
self.dlg.present()
else:
self.dlg.show_all()
else:
self.dlg.hide()
def execute_next(self, callback, args, kargs):
if not self.cmdlist:
return
cmdline = self.cmdlist.pop(0)
self.hgthread = hgthread.HgThread(cmdline[1:])
self.hgthread.start()
gobject.timeout_add(10, self.process_queue, callback, args, kargs)
def process_queue(self, callback, args, kargs):
# process queue
self.hgthread.process_dialogs()
# receive messages from queue
while self.hgthread.getqueue().qsize():
try:
msg = hglib.toutf(self.hgthread.getqueue().get(0)[0])
self.buffer.append((msg, LOG_NORMAL))
self.dlg.log.append(msg)
except Queue.Empty:
pass
while self.hgthread.geterrqueue().qsize():
try:
msg = hglib.toutf(self.hgthread.geterrqueue().get(0))
self.buffer.append((msg, LOG_ERROR))
self.dlg.log.append(msg, error=True)
except Queue.Empty:
pass
# check thread state
if not self.hgthread.isAlive():
returncode = self.hgthread.return_code()
self.hgthread = None
if len(self.get_err_buffer()) > 0:
self.show_log(True)
if returncode == 0 and self.cmdlist:
def call_next():
self.execute_next(callback, args, kargs)
gtklib.idle_add_single_call(call_next)
else:
def call_callback():
callback(returncode, self.get_buffer(), *args, **kargs)
gtklib.idle_add_single_call(call_callback)
return False # Stop polling this function
else:
return True # Continue polling
|
Loading...