#!/usr/bin/python3
## Printing troubleshooter
## Copyright (C) 2008, 2009, 2010, 2012 Red Hat, Inc.
## Author: Tim Waugh <twaugh@redhat.com>
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from gi.repository import Gdk
from gi.repository import Gtk
import pprint
import sys
import datetime
import time
import traceback
if __name__ == "__main__":
import os.path
import gettext
gettext.textdomain ('system-config-printer')
if sys.argv[0][0] != '/':
cwd = os.getcwd ()
path = cwd + os.path.sep + sys.argv[0]
else:
path = sys.argv[0]
sub = os.path.dirname (path)
root = os.path.dirname (sub)
sys.path.append (root)
from . import base
from .base import *
class Troubleshooter:
def __init__ (self, quitfn=None, parent=None):
self._in_module_call = False
main = Gtk.Window ()
if parent:
main.set_transient_for (parent)
main.set_position (Gtk.WindowPosition.CENTER_ON_PARENT)
main.set_modal (True)
main.set_title (_("Printing troubleshooter"))
main.set_property ("default-width", 400)
main.set_property ("default-height", 350)
main.connect ("delete_event", self.quit)
self.main = main
self.quitfn = quitfn
vbox = Gtk.VBox ()
main.add (vbox)
ntbk = Gtk.Notebook ()
ntbk.set_border_width (6)
vbox.pack_start (ntbk, True, True, 0)
vbox.pack_start (Gtk.HSeparator (), False, False, 0)
box = Gtk.HButtonBox ()
box.set_border_width (6)
box.set_spacing (3)
box.set_layout (Gtk.ButtonBoxStyle.END)
back = Gtk.Button.new_from_stock (Gtk.STOCK_GO_BACK)
back.connect ('clicked', self._on_back_clicked)
back.set_sensitive (False)
self.back = back
close = Gtk.Button.new_from_stock (Gtk.STOCK_CLOSE)
close.connect ('clicked', self.quit)
self.close = close
cancel = Gtk.Button.new_from_stock (Gtk.STOCK_CANCEL)
cancel.connect ('clicked', self.quit)
self.cancel = cancel
forward = Gtk.Button.new_from_stock (Gtk.STOCK_GO_FORWARD)
forward.connect ('clicked', self._on_forward_clicked)
self.forward = forward
box.pack_start (back, False, False, 0)
box.pack_start (cancel, False, False, 0)
box.pack_start (close, False, False, 0)
box.pack_start (forward, False, False, 0)
vbox.pack_start (box, False, False, 0)
forward.set_property('can-default', True)
forward.set_property('has-default', True)
ntbk.set_current_page (0)
ntbk.set_show_tabs (False)
self.ntbk = ntbk
self.current_page = 0
self.questions = []
self.question_answers = []
# timestamp should be accessible through whole troubleshoot
now = datetime.datetime.fromtimestamp (time.time ())
self.answers = {'error_log_timestamp': now.strftime ("%F %T")}
self.moving_backwards = False
main.show_all ()
def quit (self, *args):
if self._in_module_call:
try:
self.questions[self.current_page].cancel_operation ()
except:
self._report_traceback ()
return
try:
self.questions[self.current_page].disconnect_signals ()
except:
self._report_traceback ()
# Delete the questions so that their __del__ hooks can run.
# Do this in reverse order of creation.
for i in range (len (self.questions)):
self.questions.pop ()
self.main.hide ()
if self.quitfn:
self.quitfn (self)
def get_window (self):
# Any error dialogs etc from the modules need to be able
# to set themselves transient for this window.
return self.main
def no_more_questions (self, question):
page = self.questions.index (question)
debugprint ("Page %d: No more questions." % page)
self.questions = self.questions[:page + 1]
self.question_answers = self.question_answers[:page + 1]
for p in range (self.ntbk.get_n_pages () - 1, page, -1):
self.ntbk.remove_page (p)
self._set_back_forward_buttons ()
def new_page (self, widget, question):
page = len (self.questions)
debugprint ("Page %d: new: %s" % (page, str (question)))
self.questions.append (question)
self.question_answers.append ([])
self.ntbk.insert_page (widget, None, page)
widget.show_all ()
if page == 0:
try:
question.connect_signals (self._set_back_forward_buttons)
except:
self._report_traceback ()
self.ntbk.set_current_page (page)
self.current_page = page
self._set_back_forward_buttons ()
return page
def is_moving_backwards (self):
return self.moving_backwards
def answers_as_text (self):
text = ""
n = 1
for i in range (self.current_page):
answers = self.question_answers[i].copy ()
for hidden in [x for x in answers.keys() if x.startswith ("_")]:
del answers[hidden]
if len (list(answers.keys ())) == 0:
continue
text += "Page %d (%s):" % (n, self.questions[i]) + '\n'
text += pprint.pformat (answers) + '\n'
n += 1
return text.rstrip () + '\n'
def busy (self):
self._in_module_call = True
self.forward.set_sensitive (False)
self.back.set_sensitive (False)
gdkwin = self.get_window ().get_window()
if gdkwin:
gdkwin.set_cursor (Gdk.Cursor.new(Gdk.CursorType.WATCH))
while Gtk.events_pending ():
Gtk.main_iteration ()
def ready (self):
self._in_module_call = False
gdkwin = self.get_window ().get_window()
if gdkwin:
gdkwin.set_cursor (Gdk.Cursor.new(Gdk.CursorType.LEFT_PTR))
self._set_back_forward_buttons ()
def _set_back_forward_buttons (self, *args):
page = self.current_page
self.back.set_sensitive (page != 0)
if len (self.questions) == page + 1:
# Out of questions.
debugprint ("Out of questions")
self.forward.set_sensitive (False)
self.close.show ()
self.cancel.hide ()
else:
can = self._can_click_forward (self.questions[page])
debugprint ("Page %d: can click forward? %s" % (page, can))
self.forward.set_sensitive (can)
self.close.hide ()
self.cancel.show ()
def _on_back_clicked (self, widget):
self.busy ()
self.moving_backwards = True
try:
self.questions[self.current_page].disconnect_signals ()
except:
self._report_traceback ()
self.current_page -= 1
question = self.questions[self.current_page]
while not self._display (question):
# Skip this one.
debugprint ("Page %d: skip" % (self.current_page))
self.current_page -= 1
question = self.questions[self.current_page]
self.ntbk.set_current_page (self.current_page)
answers = {}
for i in range (self.current_page):
answers.update (self.question_answers[i])
self.answers = answers
try:
self.questions[self.current_page].\
connect_signals (self._set_back_forward_buttons)
except:
self._report_traceback ()
self.moving_backwards = False
self.ready ()
def _on_forward_clicked (self, widget):
self.busy ()
answer_dict = self._collect_answer (self.questions[self.current_page])
self.question_answers[self.current_page] = answer_dict
self.answers.update (answer_dict)
try:
self.questions[self.current_page].disconnect_signals ()
except:
self._report_traceback ()
self.current_page += 1
question = self.questions[self.current_page]
while not self._display (question):
# Skip this one, but collect its answers.
answer_dict = self._collect_answer (question)
self.question_answers[self.current_page] = answer_dict
self.answers.update (answer_dict)
debugprint ("Page %d: skip" % (self.current_page))
self.current_page += 1
question = self.questions[self.current_page]
self.ntbk.set_current_page (self.current_page)
try:
question.connect_signals (self._set_back_forward_buttons)
except:
self._report_traceback ()
self.ready ()
if get_debugging ():
self._dump_answers ()
def _dump_answers (self):
debugprint (self.answers_as_text ())
def _report_traceback (self):
try:
print("Traceback:")
(type, value, tb) = sys.exc_info ()
tblast = traceback.extract_tb (tb, limit=None)
if len (tblast):
tblast = tblast[:len (tblast) - 1]
extxt = traceback.format_exception_only (type, value)
for line in traceback.format_tb(tb):
print(line.strip ())
print(extxt[0].strip ())
except:
pass
def _display (self, question):
result = False
try:
result = question.display ()
except:
self._report_traceback ()
question.displayed = result
return result
def _can_click_forward (self, question):
try:
return question.can_click_forward ()
except:
self._report_traceback ()
return True
def _collect_answer (self, question):
answer = {}
try:
answer = question.collect_answer ()
except:
self._report_traceback ()
return answer
QUESTIONS = ["Welcome",
"SchedulerNotRunning",
"CheckLocalServerPublishing",
"ChoosePrinter",
"CheckPrinterSanity",
"CheckPPDSanity",
"LocalOrRemote",
"DeviceListed",
"CheckUSBPermissions",
"RemoteAddress",
"CheckNetworkServerSanity",
"ChooseNetworkPrinter",
"NetworkCUPSPrinterShared",
"QueueNotEnabled",
"QueueRejectingJobs",
"PrinterStateReasons",
"VerifyPackages",
"CheckSELinux",
"ServerFirewalled",
"ErrorLogCheckpoint",
"PrintTestPage",
"ErrorLogFetch",
"PrinterStateReasons",
"ErrorLogParse",
"Locale",
"Shrug"]
def run (quitfn=None, parent=None):
troubleshooter = Troubleshooter (quitfn, parent=parent)
modules_imported = []
for module in QUESTIONS:
try:
if not module in modules_imported:
exec ("from .%s import %s" % (module, module))
modules_imported.append (module)
exec ("%s (troubleshooter)" % module)
except:
troubleshooter._report_traceback ()
return troubleshooter
if __name__ == "__main__":
import getopt
try:
opts, args = getopt.gnu_getopt (sys.argv[1:], '',
['debug'])
for opt, optarg in opts:
if opt == '--debug':
set_debugging (True)
except getopt.GetoptError:
pass
Gdk.threads_init()
run (Gtk.main_quit)
Gdk.threads_enter ()
Gtk.main ()
Gdk.threads_leave ()