Advertisement
lpugoy

Insync KDE 4 applet

Jan 16th, 2013
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.24 KB | None | 0 0
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # KDE Insync Applet - beta11
  4. # by Brett Hartshorn : brett@insynchq.com
  5. # Copyright Insynchq Pte. Ltd., ("Insync") | http://www.insynchq.com
  6. # License: GNU GPL v2
  7.  
  8. import os, sys, webbrowser
  9. import functools
  10. from PyQt4 import Qt
  11. from PyQt4.QtCore import QObject, SIGNAL, QString, QUrl, QTimer
  12. from PyQt4.QtGui import QGraphicsLinearLayout, QIcon, QMenu, QWidget, QPushButton, QGraphicsScene
  13. from PyKDE4.kdeui import KStatusNotifierItem
  14. from PyKDE4 import kdecore
  15. import socket, json
  16.  
  17.  
  18. DEBUG = '--debug' in sys.argv
  19.  
  20. def ipc_insync( **kw ):
  21.   #print('<ipc-insync>',kw)
  22.   data = json.dumps( kw )
  23.   socket_file = u'/tmp/insync%r.sock' % os.getuid()
  24.   if not os.path.exists(socket_file):
  25.     print('WARN: insync socket not found')
  26.     return None
  27.   sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  28.   try:
  29.     sock.connect(socket_file)
  30.   except:
  31.     return
  32.  
  33.   sock.send(data)
  34.   res = sock.recv(4096)
  35.   sock.close()
  36.   if res.strip():
  37.     return json.loads(res.strip())
  38.   else:
  39.     return None
  40.  
  41. App = Qt.QApplication(sys.argv)
  42. def on_quit():
  43.   print('<kde applet exit>')
  44.   try: ipc_insync( command='EXIT' )
  45.   except: pass
  46.   App.quit()
  47. QObject.connect(
  48.     App,
  49.     SIGNAL("lastWindowClosed()"),
  50.     on_quit,
  51. )
  52.  
  53.  
  54.  
  55. def open_insync():
  56.   print('<running insync>')
  57.   if DEBUG:
  58.     os.system( 'xterm -e insync --kde --plasmoid &' )
  59.   else:
  60.     os.system( 'insync --kde --plasmoid &' )
  61.  
  62.  
  63.  
  64. STATE_IDLE = 'UNLINKED'
  65. STATE_SYNCED = 'SYNCED'
  66. STATE_SYNCING = 'SYNCING'
  67. STATE_OFFLINE = 'OFFLINE'
  68. STATE_ERROR = 'ERROR'
  69. STATE_ERROR_YELLOW = 'WARNING'
  70.  
  71. ICONS = {
  72.   STATE_OFFLINE: 'applet-offline',
  73.   STATE_IDLE: 'applet-idle',
  74.   STATE_SYNCED: 'alternate-synced',
  75.   STATE_SYNCING: 'alternate-syncing',
  76.   STATE_ERROR: 'alternate-error',
  77.   STATE_ERROR_YELLOW: 'alternate-partial-error'
  78. }
  79.  
  80.  
  81. class Notifer(QObject):
  82.   def poll_status(self):
  83.     status = ipc_insync( command='GET-STATUS' )
  84.     if status == self._prev_status: return
  85.     self._prev_status = status
  86.     name = ICONS[ status ]
  87.     path = '/usr/share/icons/insync/icons/%s.png' %name
  88.     icon = QIcon(path)
  89.     self.tray.setIconByPixmap(icon)
  90.     self.tray.setToolTipSubTitle( status )
  91.  
  92.   def __init__(self, parent=None):
  93.     QObject.__init__(self, parent)
  94.     self._menus = {}
  95.     self._prev_status = None
  96.  
  97.     self.tray = KStatusNotifierItem(self)
  98.     self.tray.setTitle("Insync")
  99.     self.tray.setToolTipTitle("Insync")
  100.     self.tray.setToolTipSubTitle("Status...")
  101.     self.tray.setCategory(KStatusNotifierItem.Communications)
  102.     self.tray.setIconByPixmap(QIcon("/usr/share/icons/insync/icons/applet-idle.png"))
  103.     self.tray.setStatus(KStatusNotifierItem.Active)
  104.     self.tray.setToolTipIconByPixmap(QIcon("/usr/share/icons/insync/icons/insync.png"))
  105.  
  106.  
  107.     #self.widget = self.tray.associatedWidget()
  108.     self.menu = menu = self.tray.contextMenu()
  109.     print(menu, dir(menu))
  110.     self.tray.setAssociatedWidget(menu)   # make KSNI open menu on left or right click
  111.  
  112.     # this will not work if using "tray.setAssociatedWidget(menu)" - this only works with left click anyways.
  113.     #QObject.connect(self.tray,SIGNAL("activateRequested(bool,QPoint)"),self.trayToggle)
  114.     ## if about to show menu, rebuild the sub menus ##
  115.     QObject.connect(menu, SIGNAL("aboutToShow()"),self._rebuild_menu)
  116.  
  117.  
  118.     self.timer = QTimer()
  119.     QObject.connect(self.timer, SIGNAL("timeout()"), self.poll_status)
  120.     self.timer.start(2000)
  121.  
  122.  
  123.  
  124.   ###############################################################
  125.  
  126.   def _rebuild_menu(self):
  127.     print('rebuilding menu')
  128.  
  129.     state = ipc_insync( command='GET-STATE')
  130.     if state:
  131.       self._rebuild_menu_main(state)
  132.     else:
  133.       self._rebuild_menu_open()
  134.  
  135.  
  136.  
  137.   def _rebuild_menu_open(self):
  138.     self.menu.clear(); self._menus = {}
  139.  
  140.     action = self.menu.addAction( QIcon("/usr/share/icons/insync/icons/insync.png"), 'Run Insync' )
  141.     QObject.connect(
  142.         action,
  143.         SIGNAL("triggered()"),
  144.         open_insync,
  145.     )
  146.  
  147.     action = self.menu.addAction( 'Quit')
  148.     QObject.connect( action, SIGNAL("triggered()"), on_quit )
  149.  
  150.  
  151.   def _rebuild_menu_main_helper(self):
  152.     self.menu.clear(); self._menus = {}
  153.     menu = self.menu
  154.  
  155.     #icon = QIcon("/usr/share/icons/oxygen/16x16/categories/applications-internet.png")
  156.     icon = QIcon("/usr/share/icons/oxygen/16x16/places/folder-documents.png")
  157.     action = menu.addAction( icon, 'Open Insync Folder')
  158.     QObject.connect(
  159.         action,
  160.         SIGNAL("triggered()"),
  161.         lambda : ipc_insync( command='OPEN-INSYNC-FOLDER' )
  162.     )
  163.  
  164.  
  165.     icon = QIcon("/usr/share/icons/oxygen/16x16/categories/applications-internet.png")
  166.     action = menu.addAction( icon, 'Go to Insync Web')
  167.     QObject.connect(
  168.         action,
  169.         SIGNAL("triggered()"),
  170.         lambda : webbrowser.open('http://www.insynchq.com')
  171.     )
  172.  
  173.     menu.addSeparator()
  174.  
  175.     ############################## menus ############################
  176.     icon = QIcon("/usr/share/icons/insync/icons/bubble-icon.png")
  177.     self._menus['recent'] = menu.addMenu(icon, 'Recent Changes')
  178.     icon = QIcon("/usr/share/icons/insync/icons/user-icon.png")
  179.     self._menus['actions'] = menu.addMenu(icon, 'Actions Required')
  180.     icon = QIcon("/usr/share/icons/insync/icons/error-icon.png")
  181.     self._menus['errors'] = menu.addMenu(icon, 'Error Messages')
  182.  
  183.     menu.addSeparator()
  184.  
  185.     self._paused = False
  186.     self._pause_action = action = menu.addAction( 'Pause Syncing')
  187.     QObject.connect(
  188.         action,
  189.         SIGNAL("triggered()"),
  190.         self._pause_resume_insync
  191.     )
  192.  
  193.     menu.addSeparator()
  194.  
  195.     self._menus['account'] = menu.addMenu('Account Information')
  196.     #menu.addAction( 'Account Information')
  197.     #menu.addTitle( 'Settings')
  198.     action = menu.addAction( 'Support')
  199.     QObject.connect(
  200.         action,
  201.         SIGNAL("triggered()"),
  202.         lambda : webbrowser.open('http://support.insynchq.com')
  203.     )
  204.  
  205.     action = menu.addAction( 'Quit')
  206.     QObject.connect( action, SIGNAL("triggered()"), on_quit )
  207.  
  208.  
  209.   def _pause_resume_insync( self ):
  210.     action = self._pause_action
  211.     if self._paused:
  212.       self._paused = False
  213.       ipc_insync( command='RESUME' )
  214.       action.setText( 'Pause Syncing')
  215.     else:
  216.       self._paused = True
  217.       ipc_insync( command='PAUSE' )
  218.       action.setText( 'Resume Syncing')
  219.  
  220.   def _rebuild_menu_main(self, state):
  221.     if not self._menus: self._rebuild_menu_main_helper()
  222.     if DEBUG: print(state)
  223.     for menu in self._menus.values(): menu.clear()
  224.  
  225.     for key in 'recent actions errors'.split():
  226.       menu = self._menus[ key ]
  227.       if not state[key]:
  228.         menu.addAction( '(no updates)' )
  229.  
  230.     for key in 'recent actions account'.split():
  231.       if not state[key]: continue
  232.       menu = self._menus[ key ]
  233.       for info, method in state[key]:
  234.         action = menu.addAction( info )
  235.         #callback = eval( 'lambda : ipc_insync( method=%s )' %method)
  236.         callback = functools.partial(ipc_insync, action=method)
  237.  
  238.         QObject.connect(
  239.             action,
  240.             SIGNAL("triggered()"),
  241.             callback,
  242.         )
  243.     ######## add new account #################
  244.     menu = self._menus['account']
  245.     action = menu.addAction('Connect another Google account')
  246.     callback = functools.partial(ipc_insync, command='ADD-ACCOUNT')
  247.     QObject.connect(
  248.         action,
  249.         SIGNAL("triggered()"),
  250.         callback,
  251.     )
  252.  
  253.  
  254.     ##############################
  255.     if state['errors']:
  256.       menu = self._menus['errors']
  257.       assert len(state['errors'])==3
  258.       retry, errors, other = state['errors']
  259.       if retry:
  260.         assert len(retry)==2
  261.         info, method = retry
  262.         action = menu.addAction(info)
  263.         callback = functools.partial(ipc_insync, action=method)
  264.         QObject.connect(
  265.             action,
  266.             SIGNAL("triggered()"),
  267.             callback,
  268.         )
  269.       for err in errors:
  270.         info, method = err
  271.         action = menu.addAction(info)
  272.         callback = functools.partial(ipc_insync, action=method)
  273.         QObject.connect(
  274.             action,
  275.             SIGNAL("triggered()"),
  276.             callback,
  277.         )
  278.  
  279.       if other: menu.addAction( other )
  280.  
  281.  
  282. if __name__ == '__main__':
  283.   notifer = Notifer()
  284.   App.exec_()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement