1 # -*- coding: utf-8 -*-
3 # (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 # Authors: Don Welch, Naga Samrat Chowdary Narla
22 #from __future__ import generators
34 from base import device, utils, pml, maint, models, pkit
36 from base.codes import *
37 from ui_utils import *
39 from installer.core_install import *
42 from PyQt4.QtCore import *
43 from PyQt4.QtGui import *
48 from dbus.mainloop.qt import DBusQtMainLoop
49 from dbus import lowlevel
51 log.error("Unable to load DBus libraries. Please check your installation and try again.")
55 # Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
56 # (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
57 warnings.simplefilter("ignore", DeprecationWarning)
61 from devmgr5_base import Ui_MainWindow
64 from faxsetupdialog import FaxSetupDialog
65 from plugindialog import PluginDialog
66 from firmwaredialog import FirmwareDialog
67 from aligndialog import AlignDialog
68 from printdialog import PrintDialog
69 from makecopiesdialog import MakeCopiesDialog
70 from sendfaxdialog import SendFaxDialog
71 from fabwindow import FABWindow
72 from devicesetupdialog import DeviceSetupDialog
73 from printtestpagedialog import PrintTestPageDialog
74 from infodialog import InfoDialog
75 from cleandialog import CleanDialog
76 from colorcaldialog import ColorCalDialog
77 from linefeedcaldialog import LineFeedCalDialog
78 from pqdiagdialog import PQDiagDialog
79 from nodevicesdialog import NoDevicesDialog
80 from aboutdialog import AboutDialog
82 # Other forms and controls
83 from settingsdialog import SettingsDialog
84 from printsettingstoolbox import PrintSettingsToolbox
88 MIN_AUTO_REFRESH_RATE = 5
89 MAX_AUTO_REFRESH_RATE = 60
90 DEF_AUTO_REFRESH_RATE = 30
93 device_list = {} # { Device_URI : device.Device(), ... }
94 model_obj = models.ModelData() # Used to convert dbus xformed data back to plain Python types
97 # ***********************************************************************************
99 # ITEM/UTILITY UI CLASSES
101 # ***********************************************************************************
104 class FuncViewItem(QListWidgetItem):
105 def __init__(self, parent, text, pixmap, tooltip_text, cmd):
106 QListWidgetItem.__init__(self, QIcon(pixmap), text, parent)
107 self.tooltip_text = tooltip_text
110 # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
112 class DeviceViewItem(QListWidgetItem):
113 def __init__(self, parent, text, pixmap, device_uri, is_avail=True):
114 QListWidgetItem.__init__(self, QIcon(pixmap), text, parent)
115 self.device_uri = device_uri
116 self.is_avail = is_avail
117 self.setTextAlignment(Qt.AlignHCenter)
119 # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
121 class PluginInstall(QObject):
122 def __init__(self, parent, plugin_type, plugin_installed):
124 self.plugin_type = plugin_type
125 self.plugin_installed = plugin_installed
129 install_plugin = True
131 if self.plugin_installed:
132 install_plugin = QMessageBox.warning(self.parent,
133 self.parent.windowTitle(),
134 self.__tr("<b>The HPLIP plugin is already installed.</b><p>Do you want to continue and re-install it?"),
137 QMessageBox.NoButton) == QMessageBox.Yes
140 ok, sudo_ok = pkit.run_plugin_command(self.plugin_type == PLUGIN_REQUIRED, self.parent.cur_device.mq['plugin-reason'])
142 QMessageBox.critical(self.parent,
143 self.parent.windowTitle(),
144 self.__tr("<b>Unable to find an appropriate su/sudo utility to run hp-plugin.</b><p>Install kdesu, gnomesu, or gksu.</p>"),
146 QMessageBox.NoButton,
147 QMessageBox.NoButton)
150 def __tr(self,s,c = None):
151 return qApp.translate("DevMgr5",s,c)
154 class DiagnoseQueue(QObject):
155 def __init__(self, parent):
160 ok, output = utils.run('hp-diagnose_queues -r')
162 def __tr(self,s,c = None):
163 return qApp.translate("DevMgr5",s,c)
167 # ***********************************************************************************
171 # ***********************************************************************************
173 class DevMgr5(QMainWindow, Ui_MainWindow):
174 def __init__(self, toolbox_version, initial_device_uri=None,
175 dbus_loop=None, parent=None, name=None, fl=0):
177 QMainWindow.__init__(self, parent)
179 log.debug("Initializing toolbox UI (Qt4)...")
180 log.debug("HPLIP Version: %s" % prop.installed_version)
183 self.toolbox_version = toolbox_version
184 self.initial_device_uri = initial_device_uri
185 self.device_vars = {}
187 self.cur_device = None
188 self.cur_printer = None
189 self.updating = False
190 self.init_failed = False
192 self.Is_autoInstaller_distro = False # True-->tier1(supports auto installation). False--> tier2(manual installation)
194 # Distro insformation
195 core = CoreInstall(MODE_CHECK)
197 self.Is_autoInstaller_distro = core.is_auto_installer_support()
199 self.user_settings = UserSettings()
200 self.user_settings.load()
201 self.user_settings.debug()
202 self.cur_device_uri = self.user_settings.last_used_device_uri
203 installed_version=sys_conf.get('hplip','version')
204 if not utils.Is_HPLIP_older_version( installed_version, self.user_settings.latest_available_version):
205 self.setupUi(self,"",self.Is_autoInstaller_distro)
207 self.setupUi(self, self.user_settings.latest_available_version,self.Is_autoInstaller_distro)
209 # Other initialization
215 cups.setPasswordCallback(showPasswordUI)
217 if not prop.doc_build:
218 self.ContentsAction.setEnabled(False)
220 self.allow_auto_refresh = True
221 QTimer.singleShot(0, self.initalUpdate)
224 # ***********************************************************************************
228 # ***********************************************************************************
230 # TODO: Make sbus init mandatory success, else exit
232 self.dbus_loop = DBusQtMainLoop(set_as_default=True)
233 self.dbus_avail, self.service, self.session_bus = device.init_dbus(self.dbus_loop)
235 if not self.dbus_avail:
236 log.error("dBus initialization error. Exiting.")
237 self.init_failed = True
240 # Receive events from the session bus
241 self.session_bus.add_signal_receiver(self.handleSessionSignal, sender_keyword='sender',
242 destination_keyword='dest', interface_keyword='interface',
243 member_keyword='member', path_keyword='path')
246 def initPixmaps(self):
247 self.func_icons_cached = False
249 self.device_icons = {}
252 self.setWindowIcon(QIcon(load_pixmap('hp_logo', '128x128')))
254 self.fax_icon = load_pixmap("fax2", "other")
258 # Setup device icon list
259 self.DeviceList.setSortingEnabled(True)
260 self.DeviceList.setContextMenuPolicy(Qt.CustomContextMenu)
261 self.setDeviceListViewMode(QListView.IconMode)
263 self.connect(self.ViewAsIconsAction, SIGNAL("triggered()"), lambda: self.setDeviceListViewMode(QListView.IconMode))
264 self.connect(self.ViewAsListAction, SIGNAL("triggered()"), lambda: self.setDeviceListViewMode(QListView.ListMode))
266 self.connect(self.DeviceList, SIGNAL("customContextMenuRequested(const QPoint &)"), self.DeviceList_customContextMenuRequested)
269 self.DeviceRefreshAction.setIcon(QIcon(load_pixmap("refresh1", "16x16")))
270 self.connect(self.DeviceRefreshAction, SIGNAL("triggered()"), self.DeviceRefreshAction_activated)
272 self.RefreshAllAction.setIcon(QIcon(load_pixmap("refresh", "16x16")))
273 self.connect(self.RefreshAllAction, SIGNAL("triggered()"), self.RefreshAllAction_activated)
275 self.SetupDeviceAction.setIcon(QIcon(load_pixmap('list_add', '16x16')))
276 self.connect(self.SetupDeviceAction, SIGNAL("triggered()"), self.SetupDeviceAction_activated)
278 self.RemoveDeviceAction.setIcon(QIcon(load_pixmap('list_remove', '16x16')))
279 self.connect(self.RemoveDeviceAction, SIGNAL("triggered()"), self.RemoveDeviceAction_activated)
281 self.PreferencesAction.setIcon(QIcon(load_pixmap('settings', '16x16')))
282 self.connect(self.PreferencesAction, SIGNAL("triggered()"), self.PreferencesAction_activated)
284 self.ContentsAction.setIcon(QIcon(load_pixmap("help", "16x16")))
285 self.connect(self.ContentsAction, SIGNAL("triggered()"), self.helpContents)
287 self.QuitAction.setIcon(QIcon(load_pixmap("quit", "16x16")))
288 self.connect(self.QuitAction, SIGNAL("triggered()"), self.quit)
290 self.connect(self.AboutAction, SIGNAL("triggered()"), self.helpAbout)
292 self.connect(self.PrintControlPrinterNameCombo, SIGNAL("activated(const QString &)"), self.PrintControlPrinterNameCombo_activated)
293 self.connect(self.PrintSettingsPrinterNameCombo, SIGNAL("activated(const QString &)"), self.PrintSettingsPrinterNameCombo_activated)
297 self.initActionsTab()
299 self.initSuppliesTab()
300 self.initPrintSettingsTab()
301 self.initPrintControlTab()
304 self.connect(self.Tabs,SIGNAL("currentChanged(int)"),self.Tabs_currentChanged)
306 # Resize the splitter so that the device list starts as a single column
307 self.splitter.setSizes([80, 600])
309 # Setup the Device List
310 self.DeviceList.setIconSize(QSize(60, 60))
311 self.connect(self.DeviceList, SIGNAL("currentItemChanged(QListWidgetItem * ,QListWidgetItem *)"),
312 self.DeviceList_currentChanged)
316 self.TabIndex = { 0: self.updateActionsTab,
317 1: self.updateStatusTab,
318 2: self.updateSuppliesTab,
319 3: self.updatePrintSettingsTab,
320 4: self.updatePrintControlTab,
321 5:self.updateHPLIPupgrade,
325 self.docs = "http://hplip.sf.net"
328 g = os.path.join(sys_conf.get('dirs', 'doc'), 'index.html')
329 if os.path.exists(g):
330 self.docs = "file://%s" % g
333 self.support = "https://launchpad.net/hplip"
337 def initalUpdate(self):
345 if self.initial_device_uri is not None:
346 if not self.activateDevice(self.initial_device_uri):
347 log.error("Device %s not found" % self.initial_device_uri)
351 self.getPrinterState()
353 if self.printer_state == cups.IPP_PRINTER_STATE_STOPPED:
354 self.cur_device.sendEvent(EVENT_PRINTER_QUEUE_STOPPED, self.cur_printer)
356 if not self.printer_accepting:
357 self.cur_device.sendEvent(EVENT_PRINTER_QUEUE_REJECTING_JOBS, self.cur_printer)
360 def activateDevice(self, device_uri):
361 log.debug(log.bold("Activate: %s %s %s" % ("*"*20, device_uri, "*"*20)))
363 d = self.DeviceList.item(index) #firstItem()
367 if d.device_uri == device_uri:
369 self.DeviceList.setSelected(d, True)
370 self.DeviceList.setCurrentItem(d)
374 d = self.DeviceList.item(index)
380 # ***********************************************************************************
382 # UPDATES/NOTIFICATIONS
384 # ***********************************************************************************
386 def handleSessionSignal(self, *args, **kwds):
387 if kwds['interface'] == 'com.hplip.Toolbox' and \
388 kwds['member'] == 'Event':
390 log.debug("Handling event...")
391 event = device.Event(*args[:6])
394 if event.event_code < EVENT_MIN_USER_EVENT:
397 elif event.event_code == EVENT_DEVICE_UPDATE_REPLY:
398 log.debug("EVENT_DEVICE_UPDATE_REPLY (%s)" % event.device_uri)
399 dev = self.findDeviceByURI(event.device_uri)
403 self.service.GetStatus(event.device_uri, reply_handler=self.handleStatusReply,
404 error_handler=self.handleStatusError)
406 except dbus.exceptions.DBusException, e:
407 log.error("dbus call to GetStatus() failed.")
409 elif event.event_code == EVENT_USER_CONFIGURATION_CHANGED:
410 log.debug("EVENT_USER_CONFIGURATION_CHANGED")
411 self.user_settings.load()
413 elif event.event_code == EVENT_HISTORY_UPDATE:
414 log.debug("EVENT_HISTORY_UPDATE (%s)" % event.device_uri)
415 dev = self.findDeviceByURI(event.device_uri)
417 self.updateHistory(dev)
419 elif event.event_code == EVENT_SYSTEMTRAY_EXIT:
420 log.debug("EVENT_SYSTEMTRAY_EXIT")
421 log.error("HPLIP Status Service was closed. HPLIP Device Manager will now exit.")
424 elif event.event_code == EVENT_RAISE_DEVICE_MANAGER:
425 log.debug("EVENT_RAISE_DEVICE_MANAGER")
427 self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive)
430 elif event.event_code in (EVENT_DEVICE_START_POLLING,
431 EVENT_DEVICE_STOP_POLLING,
432 EVENT_POLLING_REQUEST):
436 log.error("Unhandled event: %d" % event.event_code)
439 def handleStatusReply(self, device_uri, data):
440 dev = self.findDeviceByURI(device_uri)
444 value = model_obj.convert_data(str(key), str(data[key]))
445 t.setdefault(key, value)
449 dev.__dict__[d.replace('-','_')] = dev.dq[d]
451 self.updateDevice(dev)
454 def handleStatusError(self, e):
458 def updateHistory(self, dev=None):
460 dev = self.cur_device
463 self.service.GetHistory(dev.device_uri, reply_handler=self.handleHistoryReply,
464 error_handler=self.handleHistoryError)
465 except dbus.exceptions.DBusException, e:
466 log.error("dbus call to GetHistory() failed.")
469 def handleHistoryReply(self, device_uri, history):
470 dev = self.findDeviceByURI(device_uri)
476 result.append(device.Event(*tuple(h)))
479 self.error_code = result[0].event_code
481 self.error_code = STATUS_UNKNOWN
483 dev.error_state = STATUS_TO_ERROR_STATE_MAP.get(self.error_code, ERROR_STATE_CLEAR)
486 self.updateDevice(dev)
489 def handleHistoryError(self, e):
493 def sendMessage(self, device_uri, printer_name, event_code, username=prop.username,
496 device.Event(device_uri, printer_name, event_code, username,
497 job_id, title).send_via_dbus(self.session_bus)
500 def timedRefresh(self):
501 if not self.updating and self.user_settings.auto_refresh and self.allow_auto_refresh:
502 log.debug("Refresh timer...")
503 self.cleanupChildren()
505 if self.user_settings.auto_refresh_type == 0:
506 self.requestDeviceUpdate()
511 # ***********************************************************************************
513 # TAB/DEVICE CHANGE SLOTS
515 # ***********************************************************************************
517 def Tabs_currentChanged(self, tab=0):
518 """ Called when the active tab changes.
519 Update newly displayed tab.
521 if self.cur_device is not None:
524 def updateAllTabs(self):
525 for tab in self.TabIndex:
529 def updateCurrentTab(self):
530 log.debug("updateCurrentTab()")
531 self.TabIndex[self.Tabs.currentIndex()]()
535 # ***********************************************************************************
537 # DEVICE ICON LIST/DEVICE UPDATE(S)
539 # ***********************************************************************************
542 def DeviceRefreshAction_activated(self):
543 self.DeviceRefreshAction.setEnabled(False)
544 self.requestDeviceUpdate()
545 self.DeviceRefreshAction.setEnabled(True)
548 def RefreshAllAction_activated(self):
552 def setDeviceListViewMode(self, mode):
553 if mode == QListView.ListMode:
554 self.DeviceList.setViewMode(QListView.ListMode)
555 self.ViewAsListAction.setEnabled(False)
556 self.ViewAsIconsAction.setEnabled(True)
558 self.DeviceList.setViewMode(QListView.IconMode)
559 self.ViewAsListAction.setEnabled(True)
560 self.ViewAsIconsAction.setEnabled(False)
563 def createDeviceIcon(self, dev=None):
565 dev = self.cur_device
569 except AttributeError:
570 dev.icon = "default_printer"
573 self.device_icons[dev.icon]
575 self.device_icons[dev.icon] = load_pixmap(dev.icon, 'devices')
577 pix = self.device_icons[dev.icon]
579 w, h = pix.width(), pix.height()
580 error_state = dev.error_state
583 p.eraseRect(0, 0, icon.width(), icon.height())
584 p.drawPixmap(0, 0, pix)
587 tech_type = dev.tech_type
588 except AttributeError:
589 tech_type = TECH_TYPE_NONE
591 if dev.device_type == DEVICE_TYPE_FAX:
592 p.drawPixmap(w - self.fax_icon.width(), 0, self.fax_icon)
594 if error_state != ERROR_STATE_CLEAR:
595 if tech_type in (TECH_TYPE_COLOR_INK, TECH_TYPE_MONO_INK):
596 status_icon = getStatusOverlayIcon(error_state)[0] # ink
598 status_icon = getStatusOverlayIcon(error_state)[1] # laser
600 if status_icon is not None:
601 p.drawPixmap(0, 0, status_icon)
607 def refreshDeviceList(self):
609 log.debug("Rescanning device list...")
615 self.setWindowTitle(self.__tr("Refreshing Device List - HP Device Manager"))
616 self.statusBar().showMessage(self.__tr("Refreshing device list..."))
618 self.cups_devices = device.getSupportedCUPSDevices(['hp', 'hpfax'])
624 for d in self.cups_devices:
625 if d not in device_list:
628 log.debug("Adds: %s" % ','.join(adds))
631 for d in device_list:
632 if d not in self.cups_devices:
635 log.debug("Removals (1): %s" % ','.join(removals))
638 for d in device_list:
639 if d not in adds and d not in removals:
642 log.debug("Updates: %s" % ','.join(updates))
645 log.debug("adding: %s" % d)
646 # Note: Do not perform any I/O with this device.
647 dev = device.Device(d, service=self.service, disable_dbus=False)
649 if not dev.supported:
650 log.debug("Unsupported model - removing device.")
654 icon = self.createDeviceIcon(dev)
656 if dev.device_type == DEVICE_TYPE_FAX:
657 DeviceViewItem(self.DeviceList, self.__tr("%1 (Fax)").arg(dev.model_ui),
661 DeviceViewItem(self.DeviceList, self.__tr("%1 (Printer)").arg(dev.model_ui),
664 DeviceViewItem(self.DeviceList, dev.model_ui,
669 log.debug("Removals (2): %s" % ','.join(removals))
672 index = self.DeviceList.count()-1
673 item = self.DeviceList.item(index)
674 log.debug("removing: %s" % d)
681 while index >= 0 and item is not None:
682 if item.device_uri == d:
683 self.DeviceList.takeItem(index)
687 item = self.DeviceList.item(index)
691 self.DeviceList.updateGeometry()
695 for tab in self.TabIndex:
696 self.Tabs.setTabEnabled(tab, True)
698 if self.cur_device_uri:
700 item = first_item = self.DeviceList.item(index)
702 while item is not None:
704 if item.device_uri == self.cur_device_uri:
706 self.statusBar().showMessage(self.cur_device_uri)
710 item = self.DeviceList.item(index)
713 self.cur_device = None
714 self.cur_device_uri = ''
716 if self.cur_device is None:
717 i = self.DeviceList.item(0)
719 self.cur_device_uri = i.device_uri
720 self.cur_device = device_list[self.cur_device_uri]
723 self.updatePrinterCombos()
725 if self.cur_device_uri:
726 #user_conf.set('last_used', 'device_uri',self.cur_device_uri)
727 self.user_settings.last_used_device_uri = self.cur_device_uri
728 self.user_settings.save()
730 for d in updates + adds:
731 if d not in removals:
732 self.requestDeviceUpdate(device_list[d])
735 self.cur_device = None
736 self.DeviceRefreshAction.setEnabled(False)
737 self.RemoveDeviceAction.setEnabled(False)
738 self.updating = False
739 self.statusBar().showMessage(self.__tr("Press F6 to refresh."))
741 for tab in self.TabIndex:
742 self.Tabs.setTabEnabled(tab, False)
746 dlg = NoDevicesDialog(self)
750 self.updating = False
753 if current is not None:
754 self.DeviceList.setCurrentItem(current)
756 self.DeviceRefreshAction.setEnabled(True)
758 if self.cur_device is not None:
759 self.RemoveDeviceAction.setEnabled(True)
761 self.statusBar().showMessage(self.cur_device_uri)
762 self.updateWindowTitle()
765 def updateWindowTitle(self):
766 if self.cur_device.device_type == DEVICE_TYPE_FAX:
767 self.setWindowTitle(self.__tr("HP Device Manager - %1 (Fax)").arg(self.cur_device.model_ui))
769 if self.cur_device.fax_type:
770 self.setWindowTitle(self.__tr("HP Device Manager - %1 (Printer)").arg(self.cur_device.model_ui))
772 self.setWindowTitle(self.__tr("HP Device Manager - %1").arg(self.cur_device.model_ui))
774 self.statusBar().showMessage(self.cur_device_uri)
777 def updateDeviceByURI(self, device_uri):
778 return self.updateDevice(self.findDeviceByURI(device_uri))
781 def updateDevice(self, dev=None, update_tab=True):
783 """ Update the device icon and currently displayed tab.
786 dev = self.cur_device
788 log.debug("updateDevice(%s)" % dev.device_uri)
790 item = self.findItem(dev)
793 item.setIcon(QIcon(self.createDeviceIcon(dev)))
795 if dev is self.cur_device and update_tab:
796 self.updatePrinterCombos()
797 self.updateCurrentTab()
798 self.statusBar().showMessage(self.cur_device_uri)
801 def DeviceList_currentChanged(self, i, j):
802 if i is not None and not self.updating:
803 self.cur_device_uri = self.DeviceList.currentItem().device_uri
804 self.cur_device = device_list[self.cur_device_uri]
805 #user_conf.set('last_used', 'device_uri', self.cur_device_uri)
806 self.user_settings.last_used_device_uri = self.cur_device_uri
807 self.user_settings.save()
810 self.updateWindowTitle()
813 def findItem(self, dev):
815 dev = self.cur_device
817 return self.findItemByURI(dev.device_uri)
820 def findItemByURI(self, device_uri):
822 item = self.DeviceList.item(index)
824 while item is not None:
825 if item.device_uri == device_uri:
829 item = self.DeviceList.item(index)
832 def findDeviceByURI(self, device_uri):
834 return device_list[device_uri]
839 def requestDeviceUpdate(self, dev=None, item=None):
840 """ Submit device update request to update thread. """
843 dev = self.cur_device
846 dev.error_state = ERROR_STATE_REFRESHING
847 self.updateDevice(dev, update_tab=False)
849 self.sendMessage(dev.device_uri, '', EVENT_DEVICE_UPDATE_REQUESTED)
852 def rescanDevices(self):
853 """ Rescan and update all devices. """
854 if not self.updating:
855 self.RefreshAllAction.setEnabled(False)
857 self.refreshDeviceList()
859 self.RefreshAllAction.setEnabled(True)
866 # ***********************************************************************************
868 # DEVICE LIST RIGHT CLICK
870 # ***********************************************************************************
872 def DeviceList_customContextMenuRequested(self, p):
876 avail = d.device_state != DEVICE_STATE_NOT_FOUND and d.supported
877 printer = d.device_type == DEVICE_TYPE_PRINTER and avail
879 fax = d.fax_type > FAX_TYPE_NONE and prop.fax_build and d.device_type == DEVICE_TYPE_FAX and \
880 sys.hexversion >= 0x020300f0 and avail
882 scan = d.scan_type > SCAN_TYPE_NONE and prop.scan_build and \
883 printer and self.user_settings.cmd_scan
885 cpy = d.copy_type > COPY_TYPE_NONE and printer
889 item = self.DeviceList.currentItem()
891 if self.cur_device.error_state != ERROR_STATE_ERROR:
893 popup.addAction(self.__tr("Print..."), lambda: self.contextMenuFunc(PrintDialog(self, self.cur_printer)))
896 popup.addAction(self.__tr("Scan..."), lambda: self.contextMenuFunc(self.user_settings.cmd_scan)) #self.ScanButton_clicked)
899 popup.addAction(self.__tr("Make Copies..."), lambda: MakeCopiesDialog(self, self.cur_device_uri)) #self.MakeCopiesButton_clicked)
901 else: # self.cur_device.device_type == DEVICE_TYPE_FAX:
903 popup.addAction(self.__tr("Send Fax..."), lambda: self.contextMenuFunc(SendFaxDialog(self, self.cur_printer, self.cur_device_uri))) #self.SendFaxButton_clicked)
907 if not self.updating:
908 popup.addAction(self.__tr("Refresh Device"), self.requestDeviceUpdate) #self.DeviceRefreshAction_activated)
910 if not self.updating:
911 popup.addAction(self.__tr("Refresh All"), self.rescanDevices) #self.RefreshAllAction_activated)
915 if self.DeviceList.viewMode() == QListView.IconMode:
916 popup.addAction(self.__tr("View as List"), lambda: self.setDeviceListViewMode(QListView.ListMode))
918 popup.addAction(self.__tr("View as Icons"), lambda: self.setDeviceListViewMode(QListView.IconMode))
920 popup.exec_(self.DeviceList.mapToGlobal(p))
923 def contextMenuFunc(self, f):
924 self.sendMessage('', '', EVENT_DEVICE_STOP_POLLING)
928 except AttributeError:
931 if f.split(':')[0] in ('http', 'https', 'file'):
932 log.debug("Opening browser to: %s" % item.cmd)
935 self.runExternalCommand(f)
937 QTimer.singleShot(1000, self.unlockClick)
939 self.sendMessage('', '', EVENT_DEVICE_START_POLLING)
943 # ***********************************************************************************
945 # PRINTER NAME COMBOS
947 # ***********************************************************************************
950 def updatePrinterCombos(self):
951 self.PrintSettingsPrinterNameCombo.clear()
952 self.PrintControlPrinterNameCombo.clear()
954 if self.cur_device is not None and \
955 self.cur_device.supported:
957 self.cur_device.updateCUPSPrinters()
959 for c in self.cur_device.cups_printers:
960 self.PrintSettingsPrinterNameCombo.insertItem(0, c.decode("utf-8"))
961 self.PrintControlPrinterNameCombo.insertItem(0, c.decode("utf-8"))
963 self.cur_printer = unicode(self.PrintSettingsPrinterNameCombo.currentText())
966 def PrintSettingsPrinterNameCombo_activated(self, s):
967 self.cur_printer = unicode(s)
968 self.updateCurrentTab()
971 def PrintControlPrinterNameCombo_activated(self, s):
972 self.cur_printer = unicode(s)
973 self.updateCurrentTab()
977 # ***********************************************************************************
979 # FUNCTIONS/ACTION TAB
981 # ***********************************************************************************
983 def initActionsTab(self):
984 self.click_lock = None
985 self.ActionsList.setIconSize(QSize(32, 32))
986 self.connect(self.ActionsList, SIGNAL("itemClicked(QListWidgetItem *)"), self.ActionsList_clicked)
987 self.connect(self.ActionsList, SIGNAL("itemDoubleClicked(QListWidgetItem *)"), self.ActionsList_clicked)
990 def updateActionsTab(self):
993 self.ActionsList.clear()
998 avail = d.device_state != DEVICE_STATE_NOT_FOUND and d.supported
999 fax = d.fax_type > FAX_TYPE_NONE and prop.fax_build and d.device_type == DEVICE_TYPE_FAX and \
1000 sys.hexversion >= 0x020300f0 and avail
1001 printer = d.device_type == DEVICE_TYPE_PRINTER and avail
1002 scan = d.scan_type > SCAN_TYPE_NONE and prop.scan_build and \
1003 printer and self.user_settings.cmd_scan
1004 cpy = d.copy_type > COPY_TYPE_NONE and printer
1005 req_plugin = d.plugin == PLUGIN_REQUIRED
1006 opt_plugin = d.plugin == PLUGIN_OPTIONAL
1009 back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
1010 device.parseDeviceURI(self.cur_device_uri)
1014 hplip_conf = ConfigParser.ConfigParser()
1015 fp = open("/etc/hp/hplip.conf", "r")
1016 hplip_conf.readfp(fp)
1020 plugin_installed = utils.to_bool(hplip_conf.get("hplip", "plugin"))
1021 except ConfigParser.NoOptionError:
1022 plugin_installed = False
1024 if d.plugin != PLUGIN_NONE:
1025 if req_plugin and plugin_installed:
1026 x = self.__tr("Download and install<br>required plugin (already installed).")
1028 elif req_plugin and not plugin_installed:
1029 x = self.__tr("Download and install<br>required plugin (needs installation).")
1031 elif opt_plugin and plugin_installed:
1032 x = self.__tr("Download and install<br>optional plugin (already installed).")
1034 elif opt_plugin and not plugin_installed:
1035 x = self.__tr("Download and install<br>optional plugin (needs installation).")
1040 # TODO: Cache this data structure
1041 # -- add a field that specifies if the icon should always show, or only when device is avail.
1043 # TODO: Right-click icon/list view menu
1050 self.__tr("Print"), # Text
1052 self.__tr("Print documents or files."), # Tooltip
1053 lambda : PrintDialog(self, self.cur_printer)), # command/action
1058 self.__tr("Scan a document, image, or photograph.<br>"),
1059 self.user_settings.cmd_scan),
1062 self.__tr("Make Copies"),
1064 self.__tr("Make copies on the device controlled by the PC.<br>"),
1065 lambda : MakeCopiesDialog(self, self.cur_device_uri)),
1070 self.__tr("Send Fax"),
1072 self.__tr("Send a fax from the PC."),
1073 lambda : SendFaxDialog(self, self.cur_printer, self.cur_device_uri)),
1076 self.__tr("Fax Setup"),
1078 self.__tr("Fax support must be setup before you can send faxes."),
1079 lambda : FaxSetupDialog(self, self.cur_device_uri)),
1081 (lambda: fax and self.user_settings.cmd_fab,
1082 self.__tr("Fax Address Book"),
1084 self.__tr("Setup fax phone numbers to use when sending faxes from the PC."),
1085 self.user_settings.cmd_fab),
1089 (lambda : d.power_settings != POWER_SETTINGS_NONE and avail,
1090 self.__tr("Device Settings"),
1092 self.__tr("Your device has special device settings.<br>You may alter these settings here."),
1093 lambda : DeviceSetupDialog(self, self.cur_device_uri)),
1096 self.__tr("Print Test Page"),
1098 self.__tr("Print a test page to test the setup of your printer."),
1099 lambda : PrintTestPageDialog(self, self.cur_printer)),
1102 self.__tr("View Printer and Device Information"),
1104 self.__tr("View information about the device and all its CUPS queues."),
1105 lambda : InfoDialog(self, self.cur_device_uri)),
1107 (lambda: printer and d.align_type != ALIGN_TYPE_NONE,
1108 self.__tr("Align Cartridges (Print Heads)"),
1110 self.__tr("This will improve the quality of output when a new cartridge is installed."),
1111 lambda : AlignDialog(self, self.cur_device_uri)),
1113 (lambda: printer and d.clean_type != CLEAN_TYPE_NONE,
1114 self.__tr("Clean Cartridges"),
1116 self.__tr("You only need to perform this action if you are<br>having problems with poor printout quality due to clogged ink nozzles."),
1117 lambda : CleanDialog(self, self.cur_device_uri)),
1119 (lambda: printer and d.color_cal_type != COLOR_CAL_TYPE_NONE and d.color_cal_type == COLOR_CAL_TYPE_TYPHOON,
1120 self.__tr("Color Calibration"),
1122 self.__tr("Use this procedure to optimimize your printer's color output<br>(requires glossy photo paper)."),
1123 lambda : ColorCalDialog(self, self.cur_device_uri)),
1125 (lambda: printer and d.color_cal_type != COLOR_CAL_TYPE_NONE and d.color_cal_type != COLOR_CAL_TYPE_TYPHOON,
1126 self.__tr("Color Calibration"),
1128 self.__tr("Use this procedure to optimimize your printer's color output."),
1129 lambda : ColorCalDialog(self, self.cur_device_uri)),
1131 (lambda: printer and d.linefeed_cal_type != LINEFEED_CAL_TYPE_NONE,
1132 self.__tr("Line Feed Calibration"),
1134 self.__tr("Use line feed calibration to optimize print quality<br>(to remove gaps in the printed output)."),
1135 lambda : LineFeedCalDialog(self, self.cur_device_uri)),
1137 (lambda: printer and d.pq_diag_type != PQ_DIAG_TYPE_NONE,
1138 self.__tr("Print Diagnostic Page"),
1140 self.__tr("Your printer can print a test page <br>to help diagnose print quality problems."),
1141 lambda : PQDiagDialog(self, self.cur_device_uri)),
1143 (lambda: printer and d.wifi_config >= WIFI_CONFIG_USB_XML and bus == 'usb',
1144 self.__tr("Wireless/wifi setup using USB"),
1146 self.__tr("Configure your wireless capable printer using a temporary USB connection."),
1147 'hp-wificonfig -d %s' % self.cur_device_uri),
1151 (lambda : printer and d.fw_download ,
1152 self.__tr("Download Firmware"),
1154 self.__tr("Download firmware to your printer <br>(required on some devices after each power-up)."),
1155 lambda : FirmwareDialog(self, self.cur_device_uri)),
1159 (lambda : printer and req_plugin,
1160 self.__tr("Install Required Plugin"),
1163 lambda : PluginInstall(self, d.plugin, plugin_installed)),
1165 (lambda : printer and opt_plugin,
1166 self.__tr("Install Optional Plugin"),
1169 lambda : PluginInstall(self, d.plugin, plugin_installed)),
1173 self.__tr("Diagnose Queues"),
1175 self.__tr("Diagnose Print/Fax Queues."),
1176 lambda : DiagnoseQueue(self)),
1180 (lambda : printer and d.embedded_server_type > EWS_NONE and bus == 'net',
1181 self.__tr("Open printer's web page in a browser"),
1183 self.__tr("The printer's web page has supply, status, and other information."),
1189 self.__tr("Visit HPLIP Support Website"),
1191 self.__tr("Visit HPLIP Support Website."),
1197 self.__tr("View HPLIP help."),
1202 if not self.func_icons_cached:
1203 for filter, text, icon, tooltip, cmd in self.ICONS:
1204 self.func_icons[icon] = load_pixmap(icon, '32x32')
1205 self.func_icons_cached = True
1207 for filter, text, icon, tooltip, cmd in self.ICONS:
1208 if filter is not None:
1212 FuncViewItem(self.ActionsList, text,
1213 self.func_icons[icon],
1220 def ActionsList_clicked(self, item):
1221 if item is not None and self.click_lock is not item:
1222 self.click_lock = item
1224 if item.cmd and callable(item.cmd):
1226 self.sendMessage('', '', EVENT_DEVICE_STOP_POLLING)
1230 self.sendMessage('', '', EVENT_DEVICE_START_POLLING)
1234 if item.cmd.split(':')[0] in ('http', 'https', 'file'):
1235 log.debug("Opening browser to: %s" % item.cmd)
1236 utils.openURL(item.cmd)
1238 self.runExternalCommand(item.cmd)
1240 QTimer.singleShot(1000, self.unlockClick)
1243 def unlockClick(self):
1244 self.click_lock = None
1248 def ActionsList_customContextMenuRequested(self, p):
1253 # ***********************************************************************************
1257 # ***********************************************************************************
1259 def initStatusTab(self):
1260 self.StatusTable.setColumnCount(0)
1261 self.status_headers = [self.__tr(""), self.__tr("Status"), self.__tr("Date and Time"),
1262 self.__tr("Code"), self.__tr("Job ID"), self.__tr("Description")]
1265 def updateStatusTab(self):
1266 self.updateStatusLCD()
1267 self.updateStatusTable()
1270 def updateStatusLCD(self):
1271 if self.cur_device is not None and \
1272 self.cur_device.hist and \
1273 self.cur_device.supported:
1275 dq = self.cur_device.dq
1277 if dq.get('panel', 0) == 1:
1278 line1 = dq.get('panel-line1', '')
1279 line2 = dq.get('panel-line2', '')
1282 line1 = device.queryString(self.cur_device.hist[0].event_code)
1283 except (AttributeError, TypeError):
1288 self.drawStatusLCD(line1, line2)
1291 if self.cur_device.status_type == STATUS_TYPE_NONE:
1292 self.drawStatusLCD(self.__tr("Status information not"), self.__tr("available for this device."))
1294 elif not self.cur_device.supported:
1295 self.drawStatusLCD(self.__tr("Device not supported."))
1297 elif not self.cur_device.hist:
1298 self.drawStatusLCD(self.__tr("No status history available."))
1301 self.drawStatusLCD()
1304 def drawStatusLCD(self, line1='', line2=''):
1305 pm = load_pixmap('panel_lcd', 'other')
1309 p.setPen(QColor(0, 0, 0))
1310 p.setFont(self.font())
1312 x, y_line1, y_line2 = 10, 17, 33
1314 # TODO: Scroll long lines
1316 p.drawText(x, y_line1, line1)
1319 p.drawText(x, y_line2, line2)
1323 self.LCD.setPixmap(pm)
1327 def updateStatusTable(self):
1328 self.StatusTable.clear()
1329 flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
1332 hist = self.cur_device.hist[:]
1335 self.StatusTable.setRowCount(len(hist))
1336 self.StatusTable.setColumnCount(len(self.status_headers))
1337 self.StatusTable.setHorizontalHeaderLabels(self.status_headers)
1338 self.StatusTable.verticalHeader().hide()
1339 self.StatusTable.horizontalHeader().show()
1348 ess = device.queryString(e.event_code, 0)
1349 esl = device.queryString(e.event_code, 1)
1352 desc = self.__tr("(most recent)")
1355 desc = getTimeDeltaDesc(e.timedate)
1358 dt.setTime_t(int(e.timedate)) #, Qt.LocalTime)
1360 # TODO: In Qt4.x, use QLocale.toString(date, format)
1361 tt = QString("%1 %2").arg(dt.toString()).arg(desc)
1364 job_id = unicode(e.job_id)
1368 error_state = STATUS_TO_ERROR_STATE_MAP.get(e.event_code, ERROR_STATE_CLEAR)
1369 tech_type = self.cur_device.tech_type
1371 if tech_type in (TECH_TYPE_COLOR_INK, TECH_TYPE_MONO_INK):
1372 status_pix = getStatusListIcon(error_state)[0] # ink
1374 status_pix = getStatusListIcon(error_state)[1] # laser
1376 event_code = unicode(e.event_code)
1378 i = QTableWidgetItem(QIcon(status_pix), self.__tr(""))
1380 self.StatusTable.setItem(row, 0, i)
1382 for col, t in [(1, ess), (2, tt), (3, event_code), (4, job_id), (5, esl)]:
1383 i = QTableWidgetItem(QString(t))
1386 self.StatusTable.setItem(row, col, i)
1390 self.StatusTable.resizeColumnsToContents()
1391 self.StatusTable.setColumnWidth(0, 24)
1394 self.StatusTable.setRowCount(1)
1395 self.StatusTable.setColumnCount(2)
1396 self.StatusTable.setHorizontalHeaderLabels(["", ""])
1397 self.StatusTable.verticalHeader().hide()
1398 self.StatusTable.horizontalHeader().hide()
1400 flags = Qt.ItemIsEnabled
1402 pixmap = getStatusListIcon(ERROR_STATE_ERROR)[0]
1403 i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
1405 self.StatusTable.setItem(row, 0, i)
1407 i = QTableWidgetItem(self.__tr("Status information not available for this device."))
1409 self.StatusTable.setItem(0, 1, i)
1411 self.StatusTable.resizeColumnsToContents()
1412 self.StatusTable.setColumnWidth(0, 24)
1415 # ***********************************************************************************
1419 # ***********************************************************************************
1421 def initSuppliesTab(self):
1422 self.pix_battery = load_pixmap('battery', '16x16')
1425 light_yellow = "#ffffcc"
1427 light_cyan = "#ccffff"
1429 light_magenta = "#ffccff"
1432 dark_grey = "#808080"
1433 light_grey = "#c0c0c0"
1435 self.TYPE_TO_PIX_MAP = {
1436 AGENT_TYPE_UNSPECIFIED : [black],
1437 AGENT_TYPE_BLACK: [black],
1438 AGENT_TYPE_BLACK_B8800: [black],
1439 AGENT_TYPE_CMY: [cyan, magenta, yellow],
1440 AGENT_TYPE_KCM: [light_cyan, light_magenta, light_yellow],
1441 AGENT_TYPE_GGK: [dark_grey],
1442 AGENT_TYPE_YELLOW: [yellow],
1443 AGENT_TYPE_MAGENTA: [magenta],
1444 AGENT_TYPE_CYAN : [cyan],
1445 AGENT_TYPE_CYAN_LOW: [light_cyan],
1446 AGENT_TYPE_YELLOW_LOW: [light_yellow],
1447 AGENT_TYPE_MAGENTA_LOW: [light_magenta],
1448 AGENT_TYPE_BLUE: [blue],
1449 AGENT_TYPE_KCMY_CM: [yellow, cyan, magenta],
1450 AGENT_TYPE_LC_LM: [light_cyan, light_magenta],
1451 #AGENT_TYPE_Y_M: [yellow, magenta],
1452 #AGENT_TYPE_C_K: [black, cyan],
1453 AGENT_TYPE_LG_PK: [light_grey, dark_grey],
1454 AGENT_TYPE_LG: [light_grey],
1455 AGENT_TYPE_G: [dark_grey],
1456 AGENT_TYPE_PG: [light_grey],
1457 AGENT_TYPE_C_M: [cyan, magenta],
1458 AGENT_TYPE_K_Y: [black, yellow],
1461 self.supplies_headers = [self.__tr(""), self.__tr("Description"),
1462 self.__tr("HP Part No."), self.__tr("Approx. Level"),
1463 self.__tr("Status")]
1466 def updateSuppliesTab(self):
1468 flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
1471 self.SuppliesTable.clear()
1472 self.SuppliesTable.setRowCount(0)
1473 self.SuppliesTable.setColumnCount(0)
1475 if self.cur_device is not None and \
1476 self.cur_device.supported and \
1477 self.cur_device.status_type != STATUS_TYPE_NONE and \
1478 self.cur_device.device_state != DEVICE_STATE_NOT_FOUND:
1481 self.cur_device.sorted_supplies
1482 except AttributeError:
1483 self.cur_device.sorted_supplies = []
1485 if not self.cur_device.sorted_supplies:
1489 agent_type = int(self.cur_device.dq['agent%d-type' % a])
1490 agent_kind = int(self.cur_device.dq['agent%d-kind' % a])
1491 agent_sku = self.cur_device.dq['agent%d-sku' % a]
1495 self.cur_device.sorted_supplies.append((a, agent_kind, agent_type, agent_sku))
1499 self.cur_device.sorted_supplies.sort(lambda x, y: cmp(x[1], y[1]) or cmp(x[3], y[3]))
1501 self.SuppliesTable.setRowCount(len(self.cur_device.sorted_supplies))
1502 self.SuppliesTable.setColumnCount(len(self.supplies_headers))
1503 self.SuppliesTable.setHorizontalHeaderLabels(self.supplies_headers)
1504 self.SuppliesTable.verticalHeader().hide()
1505 self.SuppliesTable.horizontalHeader().show()
1506 self.SuppliesTable.setIconSize(QSize(100, 18))
1508 for row, x in enumerate(self.cur_device.sorted_supplies):
1509 a, agent_kind, agent_type, agent_sku = x
1511 agent_level = int(self.cur_device.dq['agent%d-level' % a])
1512 agent_desc = self.cur_device.dq['agent%d-desc' % a]
1513 agent_health_desc = self.cur_device.dq['agent%d-health-desc' % a]
1518 if agent_kind in (AGENT_KIND_SUPPLY,
1520 AGENT_KIND_HEAD_AND_SUPPLY,
1521 AGENT_KIND_TONER_CARTRIDGE,
1522 AGENT_KIND_MAINT_KIT,
1524 AGENT_KIND_INT_BATTERY,
1525 AGENT_KIND_DRUM_KIT,
1528 level_pixmap = self.createStatusLevelGraphic(agent_level, agent_type)
1532 if agent_kind in (AGENT_KIND_SUPPLY,
1534 AGENT_KIND_HEAD_AND_SUPPLY,
1535 AGENT_KIND_TONER_CARTRIDGE,
1536 #AGENT_KIND_MAINT_KIT,
1537 #AGENT_KIND_ADF_KIT,
1538 AGENT_KIND_INT_BATTERY,
1539 #AGENT_KIND_DRUM_KIT,
1542 pixmap = self.getStatusIcon(agent_kind, agent_type)
1544 if pixmap is not None:
1545 i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
1547 self.SuppliesTable.setItem(row, 0, i)
1549 for col, t in [(1, agent_desc), (2, agent_sku), (4, agent_health_desc)]:
1550 i = QTableWidgetItem(QString(t))
1552 self.SuppliesTable.setItem(row, col, i)
1554 if level_pixmap is not None:
1555 i = QTableWidgetItem(QIcon(level_pixmap), self.__tr(""))
1557 self.SuppliesTable.setItem(row, 3, i)
1559 self.SuppliesTable.resizeColumnsToContents()
1560 self.SuppliesTable.setColumnWidth(0, 24)
1561 self.SuppliesTable.setColumnWidth(3, 120)
1563 else: # No supplies info
1564 log.warning("Supplies information not available for this device.")
1565 flags = Qt.ItemIsEnabled
1566 self.SuppliesTable.setRowCount(1)
1567 self.SuppliesTable.setColumnCount(2)
1568 self.SuppliesTable.setHorizontalHeaderLabels(["", ""])
1569 self.SuppliesTable.verticalHeader().hide()
1570 self.SuppliesTable.horizontalHeader().hide()
1572 i = QTableWidgetItem(self.__tr("Supplies information not available for this device."))
1574 self.SuppliesTable.setItem(0, 1, i)
1576 pixmap = getStatusListIcon(ERROR_STATE_ERROR)[0]
1577 i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
1579 self.SuppliesTable.setItem(0, 0, i)
1581 self.SuppliesTable.resizeColumnsToContents()
1582 self.SuppliesTable.setColumnWidth(0, 24)
1588 def getStatusIcon(self, agent_kind, agent_type):
1589 if agent_kind in (AGENT_KIND_SUPPLY,
1591 AGENT_KIND_HEAD_AND_SUPPLY,
1592 AGENT_KIND_TONER_CARTRIDGE):
1594 map = self.TYPE_TO_PIX_MAP[agent_type]
1596 if isinstance(map, list):
1598 pix = QPixmap(16, 16)
1599 pix.fill(QColor(0, 0, 0, 0))
1603 p.setRenderHint(QPainter.Antialiasing)
1606 p.setPen(QColor(map[0]))
1607 p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
1608 p.drawPie(2, 2, 10, 10, 0, 5760)
1611 p.setPen(QColor(map[0]))
1612 p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
1613 p.drawPie(2, 4, 8, 8, 0, 5760)
1615 p.setPen(QColor(map[1]))
1616 p.setBrush(QBrush(QColor(map[1]), Qt.SolidPattern))
1617 p.drawPie(6, 4, 8, 8, 0, 5760)
1620 p.setPen(QColor(map[2]))
1621 p.setBrush(QBrush(QColor(map[2]), Qt.SolidPattern))
1622 p.drawPie(6, 6, 8, 8, 0, 5760)
1624 p.setPen(QColor(map[1]))
1625 p.setBrush(QBrush(QColor(map[1]), Qt.SolidPattern))
1626 p.drawPie(2, 6, 8, 8, 0, 5760)
1628 p.setPen(QColor(map[0]))
1629 p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
1630 p.drawPie(4, 2, 8, 8, 0, 5760)
1638 elif agent_kind == AGENT_KIND_INT_BATTERY:
1639 return self.pix_battery
1642 def createStatusLevelGraphic(self, percent, agent_type, w=100, h=18):
1649 px.fill(QColor(0, 0, 0, 0))
1652 pp.setRenderHint(QPainter.Antialiasing)
1655 map = self.TYPE_TO_PIX_MAP[agent_type]
1658 if map_len == 1 or map_len > 3:
1659 pp.fillRect(0, 0, fw, h, QBrush(QColor(map[0])))
1663 pp.fillRect(0, 0, fw, h2, QBrush(QColor(map[0])))
1664 pp.fillRect(0, h2, fw, h, QBrush(QColor(map[1])))
1669 pp.fillRect(0, 0, fw, h3, QBrush(QColor(map[0])))
1670 pp.fillRect(0, h3, fw, h23, QBrush(QColor(map[1])))
1671 pp.fillRect(0, h23, fw, h, QBrush(QColor(map[2])))
1674 pp.drawRect(0, 0, w, h)
1676 if percent > 75 and agent_type in \
1677 (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
1683 pp.drawLine(w1, 0, w1, h6)
1684 pp.drawLine(w1, h, w1, h-h6)
1686 if percent > 50 and agent_type in \
1687 (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
1693 pp.drawLine(w2, 0, w2, h4)
1694 pp.drawLine(w2, h, w2, h-h4)
1696 if percent > 25 and agent_type in \
1697 (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
1702 pp.drawLine(w4, 0, w4, h6)
1703 pp.drawLine(w4, h, w4, h-h6)
1711 # ***********************************************************************************
1713 # PRINTER SETTINGS TAB
1715 # ***********************************************************************************
1717 def initPrintSettingsTab(self):
1721 def updatePrintSettingsTab(self):
1724 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1725 self.PrintSettingsPrinterNameLabel.setText(self.__tr("Printer Name:"))
1727 self.PrintSettingsPrinterNameLabel.setText(self.__tr("Fax Name:"))
1729 self.PrintSettingsToolbox.updateUi(self.cur_device, self.cur_printer)
1734 # ***********************************************************************************
1736 # PRINTER CONTROL TAB
1738 # ***********************************************************************************
1740 def initPrintControlTab(self):
1741 self.JOB_STATES = { cups.IPP_JOB_PENDING : self.__tr("Pending"),
1742 cups.IPP_JOB_HELD : self.__tr("On hold"),
1743 cups.IPP_JOB_PROCESSING : self.__tr("Printing"),
1744 cups.IPP_JOB_STOPPED : self.__tr("Stopped"),
1745 cups.IPP_JOB_CANCELLED : self.__tr("Canceled"),
1746 cups.IPP_JOB_ABORTED : self.__tr("Aborted"),
1747 cups.IPP_JOB_COMPLETED : self.__tr("Completed"),
1750 self.CancelJobButton.setIcon(QIcon(load_pixmap('cancel', '16x16')))
1751 self.RefreshButton.setIcon(QIcon(load_pixmap('refresh', '16x16')))
1753 self.JOB_STATE_ICONS = { cups.IPP_JOB_PENDING: QIcon(load_pixmap("busy", "16x16")),
1754 cups.IPP_JOB_HELD : QIcon(load_pixmap("busy", "16x16")),
1755 cups.IPP_JOB_PROCESSING : QIcon(load_pixmap("print", "16x16")),
1756 cups.IPP_JOB_STOPPED : QIcon(load_pixmap("warning", "16x16")),
1757 cups.IPP_JOB_CANCELLED : QIcon(load_pixmap("warning", "16x16")),
1758 cups.IPP_JOB_ABORTED : QIcon(load_pixmap("error", "16x16")),
1759 cups.IPP_JOB_COMPLETED : QIcon(load_pixmap("ok", "16x16")),
1762 self.connect(self.StartStopButton, SIGNAL("clicked()"), self.StartStopButton_clicked)
1763 self.connect(self.AcceptRejectButton, SIGNAL("clicked()"), self.AcceptRejectButton_clicked)
1764 self.connect(self.SetDefaultButton, SIGNAL("clicked()"), self.SetDefaultButton_clicked)
1765 self.connect(self.CancelJobButton, SIGNAL("clicked()"), self.CancelJobButton_clicked)
1766 self.connect(self.RefreshButton, SIGNAL("clicked()"), self.RefreshButton_clicked)
1768 self.job_headers = [self.__tr("Status"), self.__tr("Title/Description"), self.__tr("Job ID")]
1770 # TODO: Check queues at startup and send events if stopped or rejecting
1773 def initUpgradeTab(self):
1774 self.connect(self.InstallLatestButton, SIGNAL("clicked()"), self.InstallLatestButton_clicked)
1775 self.InstallLatestButton_lock = False
1778 def InstallLatestButton_clicked(self):
1779 if self.InstallLatestButton_lock is True:
1781 if self.Is_autoInstaller_distro:
1782 self.InstallLatestButton.setEnabled(False)
1783 terminal_cmd = utils.get_terminal()
1784 if terminal_cmd is not None and utils.which("hp-upgrade"):
1785 cmd = terminal_cmd + " 'hp-upgrade'"
1786 log.debug("cmd = %s " %cmd)
1789 log.error("Failed to run hp-upgrade command from terminal =%s "%terminal_cmd)
1790 self.InstallLatestButton.setEnabled(True)
1792 self.InstallLatestButton_lock = True
1793 utils.openURL("http://hplipopensource.com/hplip-web/install/manual/index.html")
1794 QTimer.singleShot(1000, self.InstallLatestButton_unlock)
1797 def InstallLatestButton_unlock(self):
1798 self.InstallLatestButton_lock = False
1801 def CancelJobButton_clicked(self):
1802 item = self.JobTable.currentItem()
1804 if item is not None:
1805 job_id, ok = item.data(Qt.UserRole).toInt()
1807 self.cur_device.cancelJob(job_id)
1808 QTimer.singleShot(1000, self.updatePrintControlTab)
1811 def RefreshButton_clicked(self):
1812 self.updatePrintControlTab()
1814 def updateHPLIPupgrade(self):
1815 self.initUpgradeTab()
1820 def updatePrintControlTab(self):
1821 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1822 self.PrintControlPrinterNameLabel.setText(self.__tr("Printer Name:"))
1824 self.PrintControlPrinterNameLabel.setText(self.__tr("Fax Name:"))
1826 self.JobTable.clear()
1827 self.JobTable.setRowCount(0)
1828 self.JobTable.setColumnCount(0)
1829 self.updatePrintController()
1830 flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
1831 jobs = cups.getJobs()
1834 if j.dest.decode('utf-8') == unicode(self.cur_printer):
1838 self.CancelJobButton.setEnabled(True)
1839 self.JobTable.setRowCount(num_jobs)
1840 self.JobTable.setColumnCount(len(self.job_headers))
1841 self.JobTable.setHorizontalHeaderLabels(self.job_headers)
1843 for row, j in enumerate(jobs):
1844 if j.dest.decode('utf-8') == unicode(self.cur_printer):
1845 i = QTableWidgetItem(self.JOB_STATE_ICONS[j.state], self.JOB_STATES[j.state])
1846 i.setData(Qt.UserRole, QVariant(j.id))
1848 self.JobTable.setItem(row, 0, i)
1850 i = QTableWidgetItem(j.title)
1852 self.JobTable.setItem(row, 1, i)
1854 i = QTableWidgetItem(unicode(j.id))
1856 self.JobTable.setItem(row, 2, i)
1859 self.JobTable.setCurrentCell(0, 0)
1860 self.JobTable.resizeColumnsToContents()
1863 self.CancelJobButton.setEnabled(False)
1866 def getPrinterState(self):
1867 self.printer_state = cups.IPP_PRINTER_STATE_IDLE
1868 self.printer_accepting = True
1869 cups_printers = cups.getPrinters()
1871 for p in cups_printers:
1872 if p.name.decode('utf-8') == self.cur_printer:
1873 self.printer_state = p.state
1874 self.printer_accepting = p.accepting
1878 def updatePrintController(self):
1880 self.SetDefaultButton.setText(self.__tr("Set as Default"))
1882 default_printer = cups.getDefaultPrinter()
1883 if default_printer is not None:
1884 default_printer = default_printer.decode('utf8')
1886 if default_printer == self.cur_printer:
1887 self.SetDefaultLabel.setText(self.__tr("Default Printer"))
1888 self.SetDefaultIcon.setPixmap(load_pixmap("ok", "16x16"))
1889 self.SetDefaultButton.setEnabled(False)
1892 self.SetDefaultLabel.setText(self.__tr("Not Default Printer"))
1893 self.SetDefaultIcon.setPixmap(load_pixmap("info", "16x16"))
1894 self.SetDefaultButton.setEnabled(True)
1896 self.getPrinterState()
1899 if self.printer_state == cups.IPP_PRINTER_STATE_IDLE:
1900 self.StartStopLabel.setText(self.__tr("Started/Idle"))
1901 self.StartStopIcon.setPixmap(load_pixmap("idle", "16x16"))
1903 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1904 self.StartStopButton.setText(self.__tr("Stop Printer"))
1907 self.StartStopButton.setText(self.__tr("Stop Fax"))
1909 elif self.printer_state == cups.IPP_PRINTER_STATE_PROCESSING:
1910 self.StartStopLabel.setText(self.__tr("Started/Processing"))
1911 self.StartStopIcon.setPixmap(load_pixmap("busy", "16x16"))
1913 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1914 self.StartStopButton.setText(self.__tr("Stop Printer"))
1917 self.StartStopButton.setText(self.__tr("Stop Fax"))
1919 self.StartStopLabel.setText(self.__tr("Stopped"))
1920 self.StartStopIcon.setPixmap(load_pixmap("warning", "16x16"))
1922 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1923 self.StartStopButton.setText(self.__tr("Start Printer"))
1926 self.StartStopButton.setText(self.__tr("Start Fax"))
1929 if self.printer_accepting:
1930 self.AcceptRejectLabel.setText(self.__tr("Accepting Jobs"))
1931 self.AcceptRejectIcon.setPixmap(load_pixmap("idle", "16x16"))
1932 self.AcceptRejectButton.setText(self.__tr("Reject Jobs"))
1935 self.AcceptRejectLabel.setText(self.__tr("Rejecting Jobs"))
1936 self.AcceptRejectIcon.setPixmap(load_pixmap("warning", "16x16"))
1937 self.AcceptRejectButton.setText(self.__tr("Accept Jobs"))
1941 def StartStopButton_clicked(self):
1944 if self.printer_state in (cups.IPP_PRINTER_STATE_IDLE, cups.IPP_PRINTER_STATE_PROCESSING):
1945 result = cups.stop(self.cur_printer)
1947 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1948 e = EVENT_PRINTER_QUEUE_STOPPED
1950 e = EVENT_FAX_QUEUE_STOPPED
1953 result = cups.start(self.cur_printer)
1955 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1956 e = EVENT_PRINTER_QUEUE_STARTED
1958 e = EVENT_FAX_QUEUE_STARTED
1961 self.updatePrintController()
1962 self.cur_device.sendEvent(e, self.cur_printer)
1964 if os.geteuid!=0 and utils.addgroup()!=[]:
1965 FailureUI(self, self.__tr("<b>Start/Stop printer queue operation fails. Could not connect to CUPS Server</b><p>Is user added to %s group(s)" %utils.list_to_string(utils.addgroup())))
1972 def AcceptRejectButton_clicked(self):
1975 if self.printer_accepting:
1976 result = cups.reject(self.cur_printer)
1978 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1979 e = EVENT_PRINTER_QUEUE_REJECTING_JOBS
1981 e = EVENT_FAX_QUEUE_REJECTING_JOBS
1984 result = cups.accept(self.cur_printer)
1986 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1987 e = EVENT_PRINTER_QUEUE_ACCEPTING_JOBS
1989 e = EVENT_FAX_QUEUE_ACCEPTING_JOBS
1992 self.updatePrintController()
1993 self.cur_device.sendEvent(e, self.cur_printer)
1995 if os.geteuid!=0 and utils.addgroup()!=[]:
1996 FailureUI(self, self.__tr("<b>Accept/Reject printer queue operation fails. Could not connect to CUPS Server</b><p>Is user added to %s group(s)" %utils.list_to_string(utils.addgroup())))
2003 def SetDefaultButton_clicked(self):
2006 result = cups.setDefaultPrinter(self.cur_printer.encode('utf8'))
2008 if os.geteuid!=0 and utils.addgroup()!=[]:
2009 FailureUI(self, self.__tr("<b>Set printer queue as default operation fails. Could not connect to CUPS Server</b><p>Is user added to %s group(s)" %utils.list_to_string(utils.addgroup())))
2011 self.updatePrintController()
2012 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2013 e = EVENT_PRINTER_QUEUE_SET_AS_DEFAULT
2015 e = EVENT_FAX_QUEUE_SET_AS_DEFAULT
2017 self.cur_device.sendEvent(e, self.cur_printer)
2024 def cancelCheckedJobs(self):
2027 item = self.JobTable.firstChild()
2028 while item is not None:
2030 self.cur_device.cancelJob(item.job_id)
2032 item = item.nextSibling()
2038 self.updatePrintControlTab()
2043 # ***********************************************************************************
2045 # EXIT/CHILD CLEANUP
2047 # ***********************************************************************************
2049 def closeEvent(self, event):
2055 self.cleanupChildren()
2058 def cleanupChildren(self):
2059 log.debug("Cleaning up child processes.")
2061 os.waitpid(-1, os.WNOHANG)
2067 self.cleanupChildren()
2071 # ***********************************************************************************
2073 # DEVICE SETTINGS PLUGIN
2075 # ***********************************************************************************
2078 # ***********************************************************************************
2082 # ***********************************************************************************
2084 def PreferencesAction_activated(self, tab_to_show=0):
2085 dlg = SettingsDialog(self)
2086 dlg.TabWidget.setCurrentIndex(tab_to_show)
2088 if dlg.exec_() == QDialog.Accepted:
2089 self.user_settings.load()
2091 if self.cur_device is not None:
2092 self.cur_device.sendEvent(EVENT_USER_CONFIGURATION_CHANGED, self.cur_printer)
2095 # ***********************************************************************************
2099 # ***********************************************************************************
2101 def SetupDeviceAction_activated(self):
2102 if utils.which('hp-setup'):
2103 cmd = 'hp-setup --gui'
2105 cmd = 'python ./setup.py --gui'
2108 utils.run(cmd, log_output=True, password_func=None, timeout=1)
2109 self.rescanDevices()
2110 self.updatePrinterCombos()
2113 def RemoveDeviceAction_activated(self):
2114 if utils.which('hp-setup'):
2115 cmd = 'hp-setup --gui --remove'
2117 cmd = 'python ./setup.py --gui --remove'
2119 if self.cur_device_uri is not None:
2120 cmd += ' --device=%s' % self.cur_device_uri
2123 utils.run(cmd, log_output=True, password_func=None, timeout=1)
2124 self.rescanDevices()
2125 self.updatePrinterCombos()
2128 # ***********************************************************************************
2132 # ***********************************************************************************
2134 def runExternalCommand(self, cmd, macro_char='%'):
2139 FailureUI(self.__tr("<p><b>Unable to run command. No command specified.</b><p>Use <pre>Configure...</pre> to specify a command to run."))
2140 log.error("No command specified. Use settings to configure commands.")
2142 log.debug("Run: %s %s (%s) %s" % ("*"*20, cmd, self.cur_device_uri, "*"*20))
2146 cmd = ''.join([self.cur_device.device_vars.get(x, x) \
2147 for x in cmd.split(macro_char)])
2148 except AttributeError:
2153 path = cmd.split()[0]
2159 self.cleanupChildren()
2160 os.spawnvp(os.P_NOWAIT, path, args)
2161 qApp.processEvents()
2167 def helpContents(self):
2168 utils.openURL(self.docs)
2171 def helpAbout(self):
2172 dlg = AboutDialog(self, prop.version, self.toolbox_version + " (Qt4)")
2176 def __tr(self,s,c = None):
2177 return qApp.translate("DevMgr5",s,c)
2180 # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2182 class PasswordDialog(QDialog):
2183 def __init__(self, prompt, parent=None, name=None, modal=0, fl=0):
2184 QDialog.__init__(self, parent)
2185 self.prompt = prompt
2187 Layout= QGridLayout(self)
2188 Layout.setMargin(11)
2189 Layout.setSpacing(6)
2191 self.PromptTextLabel = QLabel(self)
2192 Layout.addWidget(self.PromptTextLabel,0,0,1,3)
2194 self.UsernameTextLabel = QLabel(self)
2195 Layout.addWidget(self.UsernameTextLabel,1,0)
2197 self.UsernameLineEdit = QLineEdit(self)
2198 self.UsernameLineEdit.setEchoMode(QLineEdit.Normal)
2199 Layout.addWidget(self.UsernameLineEdit,1,1,1,2)
2201 self.PasswordTextLabel = QLabel(self)
2202 Layout.addWidget(self.PasswordTextLabel,2,0)
2204 self.PasswordLineEdit = QLineEdit(self)
2205 self.PasswordLineEdit.setEchoMode(QLineEdit.Password)
2206 Layout.addWidget(self.PasswordLineEdit,2,1,1,2)
2208 self.OkPushButton = QPushButton(self)
2209 Layout.addWidget(self.OkPushButton,3,2)
2211 self.languageChange()
2213 self.resize(QSize(420,163).expandedTo(self.minimumSizeHint()))
2215 self.connect(self.OkPushButton, SIGNAL("clicked()"), self.accept)
2216 self.connect(self.PasswordLineEdit, SIGNAL("returnPressed()"), self.accept)
2219 def getUsername(self):
2220 return unicode(self.UsernameLineEdit.text())
2223 def getPassword(self):
2224 return unicode(self.PasswordLineEdit.text())
2227 def languageChange(self):
2228 self.setWindowTitle(self.__tr("HP Device Manager - Enter Username/Password"))
2229 self.PromptTextLabel.setText(self.__tr(self.prompt))
2230 self.UsernameTextLabel.setText(self.__tr("Username:"))
2231 self.PasswordTextLabel.setText(self.__tr("Password:"))
2232 self.OkPushButton.setText(self.__tr("OK"))
2235 def __tr(self,s,c = None):
2236 return qApp.translate("DevMgr5",s,c)
2238 # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2240 def showPasswordUI(prompt):
2242 dlg = PasswordDialog(prompt, None)
2244 if dlg.exec_() == QDialog.Accepted:
2245 return (dlg.getUsername(), dlg.getPassword())
2253 def openEWS(host, zc):
2255 status, ip = hpmudext.get_zc_ip_address(zc)
2256 if status != hpmudext.HPMUD_R_OK:
2257 ip = "hplipopensource.com"
2260 return "http://%s" % ip