Back to index

system-config-printer  1.3.9+20120706
applet.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 
00003 ## Copyright (C) 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
00004 ## Author: Tim Waugh <twaugh@redhat.com>
00005 
00006 ## This program is free software; you can redistribute it and/or modify
00007 ## it under the terms of the GNU General Public License as published by
00008 ## the Free Software Foundation; either version 2 of the License, or
00009 ## (at your option) any later version.
00010 
00011 ## This program is distributed in the hope that it will be useful,
00012 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 ## GNU General Public License for more details.
00015 
00016 ## You should have received a copy of the GNU General Public License
00017 ## along with this program; if not, write to the Free Software
00018 ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019 
00020 import cups
00021 cups.require ("1.9.42")
00022 import sys
00023 from debug import *
00024 
00025 import dbus
00026 import dbus.glib
00027 import dbus.service
00028 import gobject
00029 import time
00030 import locale
00031 import gettext
00032 import cupshelpers.installdriver
00033 from gettext import gettext as _
00034 DOMAIN="system-config-printer"
00035 gettext.textdomain (DOMAIN)
00036 try:
00037     locale.setlocale (locale.LC_ALL, "")
00038 except locale.Error, e:
00039     import os
00040     os.environ['LC_ALL'] = 'C'
00041     locale.setlocale (locale.LC_ALL, "")
00042 
00043 try:
00044     import pynotify
00045 except RuntimeError, e:
00046     print "%s:" % DOMAIN, e
00047     print "This is a graphical application and requires DISPLAY to be set."
00048     sys.exit (1)
00049 
00050 APPDIR="/usr/share/system-config-printer"
00051 ICON="printer"
00052 
00053 # We need to call pynotify.init before we can check the server for caps
00054 pynotify.init('System Config Printer Notification')
00055 
00056 # D-Bus APIs of other objects we'll use.
00057 PRINTING_BUS="org.fedoraproject.Config.Printing"
00058 PRINTING_PATH="/org/fedoraproject/Config/Printing"
00059 PRINTING_IFACE="org.fedoraproject.Config.Printing"
00060 NEWPRINTERDIALOG_IFACE=PRINTING_IFACE + ".NewPrinterDialog"
00061 PRINTERPROPERTIESDIALOG_IFACE=PRINTING_IFACE + ".PrinterPropertiesDialog"
00062 
00063 ####
00064 #### NewPrinterNotification DBus server (the 'new' way).
00065 ####
00066 PDS_PATH="/com/redhat/NewPrinterNotification"
00067 PDS_IFACE="com.redhat.NewPrinterNotification"
00068 PDS_OBJ="com.redhat.NewPrinterNotification"
00069 class NewPrinterNotification(dbus.service.Object):
00070     STATUS_SUCCESS = 0
00071     STATUS_MODEL_MISMATCH = 1
00072     STATUS_GENERIC_DRIVER = 2
00073     STATUS_NO_DRIVER = 3
00074 
00075     def __init__ (self, system_bus, session_bus):
00076         self.system_bus = system_bus
00077         self.session_bus = session_bus
00078         self.getting_ready = 0
00079         bus_name = dbus.service.BusName (PDS_OBJ, bus=system_bus)
00080         dbus.service.Object.__init__ (self, bus_name, PDS_PATH)
00081         self.notification = None
00082 
00083     @dbus.service.method(PDS_IFACE, in_signature='', out_signature='')
00084     def GetReady (self):
00085         TIMEOUT=1200000
00086         if self.getting_ready == 0:
00087             n = pynotify.Notification (_("Configuring new printer"),
00088                                        _("Please wait..."),
00089                                        'printer')
00090             n.set_timeout (TIMEOUT + 5000)
00091             n.set_data ('closed', False)
00092             n.connect ('closed', self.on_notification_closed)
00093             n.show ()
00094             self.notification = n
00095 
00096         self.getting_ready += 1
00097         gobject.timeout_add_seconds (TIMEOUT, self.timeout_ready)
00098 
00099     def on_notification_closed (self, notification):
00100         notification.set_data ('closed', True)
00101 
00102     def timeout_ready (self):
00103         if self.getting_ready > 0:
00104             self.getting_ready -= 1
00105         if (self.getting_ready == 0 and
00106             self.notification and
00107             not self.notification.get_data ('closed')):
00108             self.notification.close ()
00109 
00110         return False
00111 
00112     @dbus.service.method(PDS_IFACE, in_signature='isssss', out_signature='')
00113     def NewPrinter (self, status, name, mfg, mdl, des, cmd):
00114         if name.find("/") >= 0:
00115             # name is a URI, no queue was generated, because no suitable
00116             # driver was found
00117             title = _("Missing printer driver")
00118             devid = "MFG:%s;MDL:%s;DES:%s;CMD:%s;" % (mfg, mdl, des, cmd)
00119             if (mfg and mdl) or des:
00120                 if (mfg and mdl):
00121                     device = "%s %s" % (mfg, mdl)
00122                 else:
00123                     device = des
00124                 text = _("No printer driver for %s.") % device
00125             else:
00126                 text = _("No driver for this printer.")
00127             n = pynotify.Notification (title, text, 'printer')
00128             if "actions" in pynotify.get_server_caps():
00129                 n.set_urgency (pynotify.URGENCY_CRITICAL)
00130                 n.set_timeout (pynotify.EXPIRES_NEVER)
00131                 n.add_action ("setup-printer", _("Search"),
00132                               lambda x, y:
00133                                   self.setup_printer (x, y, name, devid))
00134             else:
00135                 self.setup_printer (None, None, name, devid)
00136 
00137         else:
00138             # name is the name of the queue which hal_lpadmin has set up
00139             # automatically.
00140             c = cups.Connection ()
00141             try:
00142                 printer = c.getPrinters ()[name]
00143             except KeyError:
00144                 return
00145 
00146             try:
00147                 filename = c.getPPD (name)
00148             except cups.IPPError:
00149                 return
00150 
00151             del c
00152 
00153             # Check for missing packages
00154             cups.ppdSetConformance (cups.PPD_CONFORM_RELAXED)
00155             ppd = cups.PPD (filename)
00156             import os
00157             os.unlink (filename)
00158             import sys
00159             sys.path.append (APPDIR)
00160             import cupshelpers
00161             (missing_pkgs,
00162              missing_exes) = cupshelpers.missingPackagesAndExecutables (ppd)
00163 
00164             from cupshelpers.ppds import ppdMakeModelSplit
00165             (make, model) = ppdMakeModelSplit (printer['printer-make-and-model'])
00166             driver = make + " " + model
00167             if status < self.STATUS_GENERIC_DRIVER:
00168                 title = _("Printer added")
00169             else:
00170                 title = _("Missing printer driver")
00171 
00172             if len (missing_pkgs) > 0:
00173                 pkgs = reduce (lambda x,y: x + ", " + y, missing_pkgs)
00174                 title = _("Install printer driver")
00175                 text = _("`%s' requires driver installation: %s.") % (name, pkgs)
00176                 n = pynotify.Notification (title, text)
00177                 import installpackage
00178                 if "actions" in pynotify.get_server_caps():
00179                     try:
00180                         self.packagekit = installpackage.PackageKit ()
00181                         n.set_timeout (pynotify.EXPIRES_NEVER)
00182                         n.add_action ("install-driver", _("Install"),
00183                                       lambda x, y:
00184                                           self.install_driver (x, y,
00185                                                                missing_pkgs))
00186                     except:
00187                         pass
00188                 else:
00189                     try:
00190                         self.packagekit = installpackage.PackageKit ()
00191                         self.packagekit.InstallPackageName (0, 0,
00192                                                             missing_pkgs[0])
00193                     except:
00194                         pass
00195 
00196             elif status == self.STATUS_SUCCESS:
00197                 devid = "MFG:%s;MDL:%s;DES:%s;CMD:%s;" % (mfg, mdl, des, cmd)
00198                 text = _("`%s' is ready for printing.") % name
00199                 n = pynotify.Notification (title, text)
00200                 if "actions" in pynotify.get_server_caps():
00201                     n.set_urgency (pynotify.URGENCY_NORMAL)
00202                     n.add_action ("test-page", _("Print test page"),
00203                                   lambda x, y:
00204                                       self.print_test_page (x, y, name))
00205                     n.add_action ("configure", _("Configure"),
00206                                   lambda x, y: self.configure (x, y, name))
00207             else: # Model mismatch
00208                 devid = "MFG:%s;MDL:%s;DES:%s;CMD:%s;" % (mfg, mdl, des, cmd)
00209                 text = (_("`%s' has been added, using the `%s' driver.") %
00210                         (name, driver))
00211                 n = pynotify.Notification (title, text, 'printer')
00212                 if "actions" in pynotify.get_server_caps():
00213                     n.set_urgency (pynotify.URGENCY_CRITICAL)
00214                     n.add_action ("test-page", _("Print test page"),
00215                                   lambda x, y:
00216                                       self.print_test_page (x, y, name, devid))
00217                     n.add_action ("find-driver", _("Find driver"),
00218                                   lambda x, y: 
00219                                   self.find_driver (x, y, name, devid))
00220                     n.set_timeout (pynotify.EXPIRES_NEVER)
00221                 else:
00222                     self.configure (None, None, name)
00223 
00224         self.timeout_ready ()
00225         n.show ()
00226         self.notification = n
00227 
00228     def print_test_page (self, notification, action, name):
00229         path = self.configure (None, None, name)
00230         obj = self.session_bus.get_object (PRINTING_BUS, path)
00231         iface = dbus.Interface (obj, PRINTERPROPERTIESDIALOG_IFACE)
00232         iface.PrintTestPage ()
00233 
00234     def configure (self, notification, action, name):
00235         obj = self.session_bus.get_object (PRINTING_BUS, PRINTING_PATH)
00236         iface = dbus.Interface (obj, PRINTING_IFACE)
00237         return iface.PrinterPropertiesDialog (dbus.UInt32(0), name)
00238 
00239     def get_newprinterdialog_interface (self):
00240         obj = self.session_bus.get_object (PRINTING_BUS, PRINTING_PATH)
00241         iface = dbus.Interface (obj, PRINTING_IFACE)
00242         path = iface.NewPrinterDialog ()
00243         obj = self.session_bus.get_object (PRINTING_BUS, path)
00244         iface = dbus.Interface (obj, NEWPRINTERDIALOG_IFACE)
00245         return iface
00246 
00247     def ignore_dbus_replies (self, *args):
00248         pass
00249 
00250     def find_driver (self, notification, action, name, devid = ""):
00251         try:
00252             iface = self.get_newprinterdialog_interface ()
00253             iface.ChangePPD (dbus.UInt32(0), name, devid,
00254                              reply_handler=self.ignore_dbus_replies,
00255                              error_handler=self.ignore_dbus_replies)
00256         except dbus.DBusException:
00257             pass
00258 
00259     def setup_printer (self, notification, action, uri, devid = ""):
00260         try:
00261             iface = self.get_newprinterdialog_interface ()
00262             iface.NewPrinterFromDevice (dbus.UInt32(0), uri, devid,
00263                                         reply_handler=self.ignore_dbus_replies,
00264                                         error_handler=self.ignore_dbus_replies)
00265         except dbus.DBusException:
00266             pass
00267 
00268     def install_driver (self, notification, action, missing_pkgs):
00269         try:
00270             self.packagekit.InstallPackageName (0, 0, missing_pkgs[0])
00271         except:
00272             pass
00273 
00274     def collect_exit_code (self, pid):
00275         # We do this with timers instead of signals because we already
00276         # have gobject imported, but don't (yet) import signal;
00277         # let's try not to inflate the process size.
00278         import os
00279         try:
00280             print "Waiting for child %d" % pid
00281             (pid, status) = os.waitpid (pid, os.WNOHANG)
00282             if pid == 0:
00283                 # Run this timer again.
00284                 return True
00285         except OSError:
00286             pass
00287 
00288         return False
00289 
00290 PROGRAM_NAME="system-config-printer-applet"
00291 def show_help ():
00292     print "usage: %s [--help|--version|--debug]" % PROGRAM_NAME
00293 
00294 def show_version ():
00295     import config
00296     print "%s %s" % (PROGRAM_NAME, config.VERSION)
00297     
00298 ####
00299 #### Main program entry
00300 ####
00301 
00302 def monitor_session (*args):
00303     pass
00304 
00305 def any_jobs ():
00306     try:
00307         c = cups.Connection ()
00308         jobs = c.getJobs (my_jobs=True, limit=1)
00309         if len (jobs):
00310             return True
00311     except:
00312         pass
00313 
00314     return False
00315 
00316 class RunLoop:
00317     DBUS_PATH="/com/redhat/PrinterSpooler"
00318     DBUS_IFACE="com.redhat.PrinterSpooler"
00319 
00320     def __init__ (self, session_bus, system_bus, loop):
00321         self.system_bus = system_bus
00322         self.session_bus = session_bus
00323         self.loop = loop
00324         self.timer = None
00325         system_bus.add_signal_receiver (self.handle_dbus_signal,
00326                                         path=self.DBUS_PATH,
00327                                         dbus_interface=self.DBUS_IFACE)
00328         self.check_for_jobs ()
00329 
00330     def remove_signal_receiver (self):
00331         self.system_bus.remove_signal_receiver (self.handle_dbus_signal,
00332                                                 path=self.DBUS_PATH,
00333                                                 dbus_interface=self.DBUS_IFACE)
00334 
00335     def run (self):
00336         self.loop.run ()
00337 
00338     def __del__ (self):
00339         self.remove_signal_receiver ()
00340         if self.timer:
00341             gobject.source_remove (self.timer)
00342 
00343     def handle_dbus_signal (self, *args):
00344         if self.timer:
00345             gobject.source_remove (self.timer)
00346         self.timer = gobject.timeout_add (200, self.check_for_jobs)
00347 
00348     def check_for_jobs (self, *args):
00349         debugprint ("checking for jobs")
00350         if any_jobs ():
00351             if self.timer != None:
00352                 gobject.source_remove (self.timer)
00353 
00354             self.remove_signal_receiver ()
00355 
00356             # Start the job applet.
00357             debugprint ("Starting job applet")
00358             try:
00359                 obj = self.session_bus.get_object (PRINTING_BUS, PRINTING_PATH)
00360                 iface = dbus.Interface (obj, PRINTING_IFACE)
00361                 path = iface.JobApplet ()
00362                 debugprint ("Job applet is %s" % path)
00363             except dbus.DBusException, e:
00364                 try:
00365                     print e
00366                 except:
00367                     pass
00368 
00369         # Don't run this timer again.
00370         return False
00371 
00372 if __name__ == '__main__':
00373     import sys, getopt
00374     try:
00375         opts, args = getopt.gnu_getopt (sys.argv[1:], '',
00376                                         ['debug',
00377                                          'help',
00378                                          'version'])
00379     except getopt.GetoptError:
00380         show_help ()
00381         sys.exit (1)
00382 
00383     for opt, optarg in opts:
00384         if opt == "--help":
00385             show_help ()
00386             sys.exit (0)
00387         if opt == "--version":
00388             show_version ()
00389             sys.exit (0)
00390         elif opt == "--debug":
00391             set_debugging (True)
00392 
00393     # Must be done before connecting to D-Bus (for some reason).
00394     if not pynotify.init (PROGRAM_NAME):
00395         try:
00396             print >> sys.stderr, ("%s: unable to initialize pynotify" %
00397                                   PROGRAM_NAME)
00398         except:
00399             pass
00400 
00401     system_bus = session_bus = None
00402     try:
00403         system_bus = dbus.SystemBus()
00404     except:
00405         try:
00406             print >> sys.stderr, ("%s: failed to connect to system D-Bus" %
00407                                   PROGRAM_NAME)
00408         finally:
00409             sys.exit (1)
00410 
00411     try:
00412         session_bus = dbus.SessionBus()
00413         # Stop running when the session ends.
00414         session_bus.add_signal_receiver (monitor_session)
00415     except:
00416         try:
00417             print >> sys.stderr, ("%s: failed to connect to "
00418                                   "session D-Bus" % PROGRAM_NAME)
00419         finally:
00420             sys.exit (1)
00421 
00422     try:
00423         NewPrinterNotification(system_bus, session_bus)
00424     except:
00425         try:
00426             print >> sys.stderr, ("%s: failed to start "
00427                                   "NewPrinterNotification service" %
00428                                   PROGRAM_NAME)
00429         except:
00430             pass
00431 
00432     try:
00433         cupshelpers.installdriver.set_debugprint_fn (debugprint)
00434         cupshelpers.installdriver.PrinterDriversInstaller(system_bus)
00435     except Exception, e:
00436         try:
00437             print >> sys.stderr, ("%s: failed to start "
00438                                   "PrinterDriversInstaller service: "
00439                                   "%s" % (PROGRAM_NAME, e))
00440         except:
00441             pass
00442 
00443     loop = gobject.MainLoop ()
00444     runloop = RunLoop (session_bus, system_bus, loop)
00445     try:
00446         runloop.run ()
00447     except KeyboardInterrupt:
00448         pass