mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
calibre now has a system tray icon
This commit is contained in:
parent
bd1d6ca3f3
commit
e9f3b6b735
@ -5,7 +5,8 @@ from xml.parsers.expat import ExpatError
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, QUrl, QTimer
|
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, QUrl, QTimer
|
||||||
from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \
|
from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \
|
||||||
QToolButton, QDialog, QDesktopServices, QFileDialog
|
QToolButton, QDialog, QDesktopServices, QFileDialog, \
|
||||||
|
QSystemTrayIcon, QApplication
|
||||||
from PyQt4.QtSvg import QSvgRenderer
|
from PyQt4.QtSvg import QSvgRenderer
|
||||||
|
|
||||||
from calibre import __version__, __appname__, islinux, sanitize_file_name, \
|
from calibre import __version__, __appname__, islinux, sanitize_file_name, \
|
||||||
@ -20,12 +21,12 @@ from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
|
|||||||
pixmap_to_data, choose_dir, ORG_NAME, \
|
pixmap_to_data, choose_dir, ORG_NAME, \
|
||||||
set_sidebar_directories, Dispatcher, \
|
set_sidebar_directories, Dispatcher, \
|
||||||
SingleApplication, Application, available_height, \
|
SingleApplication, Application, available_height, \
|
||||||
max_available_height, config
|
max_available_height, config, info_dialog
|
||||||
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
|
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
|
||||||
from calibre.library.database import LibraryDatabase
|
from calibre.library.database import LibraryDatabase
|
||||||
from calibre.gui2.dialogs.scheduler import Scheduler
|
from calibre.gui2.dialogs.scheduler import Scheduler
|
||||||
from calibre.gui2.update import CheckForUpdates
|
from calibre.gui2.update import CheckForUpdates
|
||||||
from calibre.gui2.main_window import MainWindow, option_parser
|
from calibre.gui2.main_window import MainWindow, option_parser as _option_parser
|
||||||
from calibre.gui2.main_ui import Ui_MainWindow
|
from calibre.gui2.main_ui import Ui_MainWindow
|
||||||
from calibre.gui2.device import DeviceManager
|
from calibre.gui2.device import DeviceManager
|
||||||
from calibre.gui2.status import StatusBar
|
from calibre.gui2.status import StatusBar
|
||||||
@ -91,7 +92,24 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
self.device_connected = False
|
self.device_connected = False
|
||||||
self.viewers = collections.deque()
|
self.viewers = collections.deque()
|
||||||
self.content_server = None
|
self.content_server = None
|
||||||
|
self.system_tray_icon = QSystemTrayIcon(QIcon(':/library'), self)
|
||||||
|
if opts.no_systray:
|
||||||
|
self.system_tray_icon.hide()
|
||||||
|
else:
|
||||||
|
self.system_tray_icon.show()
|
||||||
|
self.system_tray_menu = QMenu()
|
||||||
|
self.restore_action = self.system_tray_menu.addAction(QIcon(':/images/page.svg'), _('&Restore'))
|
||||||
|
self.donate_action = self.system_tray_menu.addAction(QIcon(':/images/donate.svg'), _('&Donate'))
|
||||||
|
self.quit_action = self.system_tray_menu.addAction(QIcon(':/images/window-close.svg'), _('&Quit'))
|
||||||
|
|
||||||
|
self.system_tray_icon.setContextMenu(self.system_tray_menu)
|
||||||
|
self.connect(self.quit_action, SIGNAL('triggered(bool)'), self.quit)
|
||||||
|
self.connect(self.donate_action, SIGNAL('triggered(bool)'), self.donate)
|
||||||
|
self.connect(self.restore_action, SIGNAL('triggered(bool)'), lambda c : self.show())
|
||||||
|
def sta(r):
|
||||||
|
if r == QSystemTrayIcon.Trigger:
|
||||||
|
self.hide() if self.isVisible() else self.show()
|
||||||
|
self.connect(self.system_tray_icon, SIGNAL('activated(QSystemTrayIcon::ActivationReason)'), sta)
|
||||||
####################### Location View ########################
|
####################### Location View ########################
|
||||||
QObject.connect(self.location_view, SIGNAL('location_selected(PyQt_PyObject)'),
|
QObject.connect(self.location_view, SIGNAL('location_selected(PyQt_PyObject)'),
|
||||||
self.location_selected)
|
self.location_selected)
|
||||||
@ -109,7 +127,7 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
self.update_found)
|
self.update_found)
|
||||||
self.update_checker.start()
|
self.update_checker.start()
|
||||||
####################### Status Bar #####################
|
####################### Status Bar #####################
|
||||||
self.status_bar = StatusBar(self.jobs_dialog)
|
self.status_bar = StatusBar(self.jobs_dialog, self.system_tray_icon)
|
||||||
self.setStatusBar(self.status_bar)
|
self.setStatusBar(self.status_bar)
|
||||||
QObject.connect(self.job_manager, SIGNAL('job_added(int)'), self.status_bar.job_added,
|
QObject.connect(self.job_manager, SIGNAL('job_added(int)'), self.status_bar.job_added,
|
||||||
Qt.QueuedConnection)
|
Qt.QueuedConnection)
|
||||||
@ -1239,20 +1257,51 @@ in which you want to store your books files. Any existing books will be automati
|
|||||||
if self.device_connected:
|
if self.device_connected:
|
||||||
self.memory_view.write_settings()
|
self.memory_view.write_settings()
|
||||||
|
|
||||||
def closeEvent(self, e):
|
def quit(self, checked):
|
||||||
msg = 'There are active jobs. Are you sure you want to quit?'
|
if self.shutdown():
|
||||||
|
QApplication.instance().quit()
|
||||||
|
|
||||||
|
def donate(self):
|
||||||
|
BUTTON = '''
|
||||||
|
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
|
||||||
|
<input type="hidden" name="cmd" value="_s-xclick">
|
||||||
|
<input type="hidden" name="hosted_button_id" value="1335186">
|
||||||
|
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="">
|
||||||
|
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
||||||
|
</form>
|
||||||
|
'''
|
||||||
|
MSG = _('is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development.')
|
||||||
|
HTML = '''
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Donate to support calibre</title>
|
||||||
|
</head>
|
||||||
|
<body style="background:white">
|
||||||
|
<div><a href="http://calibre.kovidgoyal.net"><img style="border:0px" src="http://calibre.kovidgoyal.net/chrome/site/calibre_banner.png" alt="calibre" /></a></div>
|
||||||
|
<p>Calibre %s</p>
|
||||||
|
%s
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''%(MSG, BUTTON)
|
||||||
|
pt = PersistentTemporaryFile('_donate.htm')
|
||||||
|
pt.write(HTML)
|
||||||
|
pt.close()
|
||||||
|
QDesktopServices.openUrl(QUrl.fromLocalFile(pt.name))
|
||||||
|
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
msg = _('There are active jobs. Are you sure you want to quit?')
|
||||||
if self.job_manager.has_device_jobs():
|
if self.job_manager.has_device_jobs():
|
||||||
msg = '<p>'+__appname__ + ' is communicating with the device!<br>'+\
|
msg = '<p>'+__appname__ + _(''' is communicating with the device!<br>
|
||||||
'Quitting may cause corruption on the device.<br>'+\
|
'Quitting may cause corruption on the device.<br>
|
||||||
'Are you sure you want to quit?'
|
'Are you sure you want to quit?''')+'</p>'
|
||||||
if self.job_manager.has_jobs():
|
if self.job_manager.has_jobs():
|
||||||
d = QMessageBox(QMessageBox.Warning, 'WARNING: Active jobs', msg,
|
d = QMessageBox(QMessageBox.Warning, _('WARNING: Active jobs'), msg,
|
||||||
QMessageBox.Yes|QMessageBox.No, self)
|
QMessageBox.Yes|QMessageBox.No, self)
|
||||||
d.setIconPixmap(QPixmap(':/images/dialog_warning.svg'))
|
d.setIconPixmap(QPixmap(':/images/dialog_warning.svg'))
|
||||||
d.setDefaultButton(QMessageBox.No)
|
d.setDefaultButton(QMessageBox.No)
|
||||||
if d.exec_() != QMessageBox.Yes:
|
if d.exec_() != QMessageBox.Yes:
|
||||||
e.ignore()
|
return False
|
||||||
return
|
|
||||||
|
|
||||||
self.job_manager.terminate_all_jobs()
|
self.job_manager.terminate_all_jobs()
|
||||||
self.write_settings()
|
self.write_settings()
|
||||||
@ -1269,7 +1318,22 @@ in which you want to store your books files. Any existing books will be automati
|
|||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
self.hide()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def closeEvent(self, e):
|
||||||
|
if self.system_tray_icon.isVisible():
|
||||||
|
if not dynamic['systray_msg'] and not isosx:
|
||||||
|
info_dialog(self, 'calibre', 'calibre '+_('will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray.')).exec_()
|
||||||
|
dynamic['systray_msg'] = True
|
||||||
|
self.hide()
|
||||||
|
e.ignore()
|
||||||
|
else:
|
||||||
|
if self.shutdown():
|
||||||
e.accept()
|
e.accept()
|
||||||
|
else:
|
||||||
|
e.ignore()
|
||||||
|
|
||||||
def update_found(self, version):
|
def update_found(self, version):
|
||||||
os = 'windows' if iswindows else 'osx' if isosx else 'linux'
|
os = 'windows' if iswindows else 'osx' if isosx else 'linux'
|
||||||
@ -1286,12 +1350,8 @@ in which you want to store your books files. Any existing books will be automati
|
|||||||
dynamic.set('update to version %s'%version, False)
|
dynamic.set('update to version %s'%version, False)
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def option_parser():
|
||||||
from calibre.utils.lock import singleinstance
|
parser = _option_parser('''\
|
||||||
|
|
||||||
pid = os.fork() if False and islinux else -1
|
|
||||||
if pid <= 0:
|
|
||||||
parser = option_parser('''\
|
|
||||||
%prog [opts] [path_to_ebook]
|
%prog [opts] [path_to_ebook]
|
||||||
|
|
||||||
Launch the main calibre Graphical User Interface and optionally add the ebook at
|
Launch the main calibre Graphical User Interface and optionally add the ebook at
|
||||||
@ -1301,6 +1361,16 @@ path_to_ebook to the database.
|
|||||||
help=_('Use the library located at the specified path.'))
|
help=_('Use the library located at the specified path.'))
|
||||||
parser.add_option('-v', '--verbose', default=0, action='count',
|
parser.add_option('-v', '--verbose', default=0, action='count',
|
||||||
help=_('Log debugging information to console'))
|
help=_('Log debugging information to console'))
|
||||||
|
parser.add_option('--no-systray', default=False, action='store_true',
|
||||||
|
help=_('Disable system tray icon'))
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def main(args=sys.argv):
|
||||||
|
from calibre.utils.lock import singleinstance
|
||||||
|
|
||||||
|
pid = os.fork() if False and islinux else -1
|
||||||
|
if pid <= 0:
|
||||||
|
parser = option_parser()
|
||||||
opts, args = parser.parse_args(args)
|
opts, args = parser.parse_args(args)
|
||||||
if opts.with_library is not None and os.path.isdir(opts.with_library):
|
if opts.with_library is not None and os.path.isdir(opts.with_library):
|
||||||
prefs.set('library_path', opts.with_library)
|
prefs.set('library_path', opts.with_library)
|
||||||
|
@ -164,8 +164,9 @@ class TagViewButton(QToolButton):
|
|||||||
|
|
||||||
|
|
||||||
class StatusBar(QStatusBar):
|
class StatusBar(QStatusBar):
|
||||||
def __init__(self, jobs_dialog):
|
def __init__(self, jobs_dialog, systray=None):
|
||||||
QStatusBar.__init__(self)
|
QStatusBar.__init__(self)
|
||||||
|
self.systray = systray
|
||||||
self.movie_button = MovieButton(QMovie(':/images/jobs-animated.mng'), jobs_dialog)
|
self.movie_button = MovieButton(QMovie(':/images/jobs-animated.mng'), jobs_dialog)
|
||||||
self.cover_flow_button = CoverFlowButton()
|
self.cover_flow_button = CoverFlowButton()
|
||||||
self.tag_view_button = TagViewButton()
|
self.tag_view_button = TagViewButton()
|
||||||
@ -180,6 +181,11 @@ class StatusBar(QStatusBar):
|
|||||||
def reset_info(self):
|
def reset_info(self):
|
||||||
self.book_info.show_data({})
|
self.book_info.show_data({})
|
||||||
|
|
||||||
|
def showMessage(self, msg, timeout=0):
|
||||||
|
if self.systray is not None:
|
||||||
|
self.systray.showMessage('calibre', msg, self.systray.Information, 10000)
|
||||||
|
return QStatusBar.showMessage(self, msg, timeout)
|
||||||
|
|
||||||
def jobs(self):
|
def jobs(self):
|
||||||
src = qstring_to_unicode(self.movie_button.jobs.text())
|
src = qstring_to_unicode(self.movie_button.jobs.text())
|
||||||
return int(re.search(r'\d+', src).group())
|
return int(re.search(r'\d+', src).group())
|
||||||
|
@ -184,6 +184,7 @@ def setup_completion(fatal_errors):
|
|||||||
from calibre.ebooks.epub.from_feeds import option_parser as feeds2epub
|
from calibre.ebooks.epub.from_feeds import option_parser as feeds2epub
|
||||||
from calibre.ebooks.epub.from_any import option_parser as any2epub
|
from calibre.ebooks.epub.from_any import option_parser as any2epub
|
||||||
from calibre.ebooks.epub.from_comic import option_parser as comic2epub
|
from calibre.ebooks.epub.from_comic import option_parser as comic2epub
|
||||||
|
from calibre.gui2.main import option_parser as guiop
|
||||||
any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip',
|
any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip',
|
||||||
'txt', 'lit', 'rtf', 'pdf', 'prc', 'mobi', 'fb2', 'odt']
|
'txt', 'lit', 'rtf', 'pdf', 'prc', 'mobi', 'fb2', 'odt']
|
||||||
f = open_file('/etc/bash_completion.d/libprs500')
|
f = open_file('/etc/bash_completion.d/libprs500')
|
||||||
@ -204,6 +205,7 @@ def setup_completion(fatal_errors):
|
|||||||
f.write(opts_and_exts('fb22lrf', htmlop, ['fb2']))
|
f.write(opts_and_exts('fb22lrf', htmlop, ['fb2']))
|
||||||
f.write(opts_and_exts('pdf2lrf', htmlop, ['pdf']))
|
f.write(opts_and_exts('pdf2lrf', htmlop, ['pdf']))
|
||||||
f.write(opts_and_exts('any2lrf', htmlop, any_formats))
|
f.write(opts_and_exts('any2lrf', htmlop, any_formats))
|
||||||
|
f.write(opts_and_exts('calibre', guiop, any_formats))
|
||||||
f.write(opts_and_exts('any2lrf', any2epub, any_formats))
|
f.write(opts_and_exts('any2lrf', any2epub, any_formats))
|
||||||
f.write(opts_and_exts('lrf2lrs', lrf2lrsop, ['lrf']))
|
f.write(opts_and_exts('lrf2lrs', lrf2lrsop, ['lrf']))
|
||||||
f.write(opts_and_exts('lrf-meta', metaop, ['lrf']))
|
f.write(opts_and_exts('lrf-meta', metaop, ['lrf']))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user