mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Welcome wizard
This commit is contained in:
parent
496e0dccd0
commit
8072415891
@ -45,6 +45,13 @@ def to_unicode(raw, encoding='utf-8', errors='strict'):
|
|||||||
return raw
|
return raw
|
||||||
return raw.decode(encoding, errors)
|
return raw.decode(encoding, errors)
|
||||||
|
|
||||||
|
def patheq(p1, p2):
|
||||||
|
p = os.path
|
||||||
|
d = lambda x : p.normcase(p.normpath(p.realpath(p.normpath(x))))
|
||||||
|
if not p1 or not p2:
|
||||||
|
return False
|
||||||
|
return d(p1) == d(p2)
|
||||||
|
|
||||||
def unicode_path(path, abs=False):
|
def unicode_path(path, abs=False):
|
||||||
if not isinstance(path, unicode):
|
if not isinstance(path, unicode):
|
||||||
path = path.decode(sys.getfilesystemencoding())
|
path = path.decode(sys.getfilesystemencoding())
|
||||||
|
@ -35,7 +35,7 @@ class PageSetupWidget(Widget, Ui_Form):
|
|||||||
TITLE = _('Page Setup')
|
TITLE = _('Page Setup')
|
||||||
|
|
||||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||||
Widget.__init__(self, parent, 'lrf_output',
|
Widget.__init__(self, parent, 'page_setup',
|
||||||
['margin_top', 'margin_left', 'margin_right', 'margin_bottom',
|
['margin_top', 'margin_left', 'margin_right', 'margin_bottom',
|
||||||
'input_profile', 'output_profile']
|
'input_profile', 'output_profile']
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
import os, re, time, textwrap, sys, cStringIO
|
import os, re, time, textwrap
|
||||||
from binascii import hexlify, unhexlify
|
|
||||||
|
|
||||||
from PyQt4.Qt import QDialog, QMessageBox, QListWidgetItem, QIcon, \
|
from PyQt4.Qt import QDialog, QMessageBox, QListWidgetItem, QIcon, \
|
||||||
QDesktopServices, QVBoxLayout, QLabel, QPlainTextEdit, \
|
QDesktopServices, QVBoxLayout, QLabel, QPlainTextEdit, \
|
||||||
@ -12,7 +11,6 @@ from PyQt4.Qt import QDialog, QMessageBox, QListWidgetItem, QIcon, \
|
|||||||
|
|
||||||
from calibre.constants import islinux, iswindows
|
from calibre.constants import islinux, iswindows
|
||||||
from calibre.gui2.dialogs.config_ui import Ui_Dialog
|
from calibre.gui2.dialogs.config_ui import Ui_Dialog
|
||||||
from calibre.gui2.dialogs.test_email_ui import Ui_Dialog as TE_Dialog
|
|
||||||
from calibre.gui2 import qstring_to_unicode, choose_dir, error_dialog, config, \
|
from calibre.gui2 import qstring_to_unicode, choose_dir, error_dialog, config, \
|
||||||
ALL_COLUMNS, NONE, info_dialog, choose_files
|
ALL_COLUMNS, NONE, info_dialog, choose_files
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
@ -202,34 +200,6 @@ class CategoryModel(QStringListModel):
|
|||||||
return self.icons[index.row()]
|
return self.icons[index.row()]
|
||||||
return QStringListModel.data(self, index, role)
|
return QStringListModel.data(self, index, role)
|
||||||
|
|
||||||
class TestEmail(QDialog, TE_Dialog):
|
|
||||||
|
|
||||||
def __init__(self, accounts, parent):
|
|
||||||
QDialog.__init__(self, parent)
|
|
||||||
TE_Dialog.__init__(self)
|
|
||||||
self.setupUi(self)
|
|
||||||
opts = smtp_prefs().parse()
|
|
||||||
self.test_func = parent.test_email_settings
|
|
||||||
self.connect(self.test_button, SIGNAL('clicked(bool)'), self.test)
|
|
||||||
self.from_.setText(unicode(self.from_.text())%opts.from_)
|
|
||||||
if accounts:
|
|
||||||
self.to.setText(list(accounts.keys())[0])
|
|
||||||
if opts.relay_host:
|
|
||||||
self.label.setText(_('Using: %s:%s@%s:%s and %s encryption')%
|
|
||||||
(opts.relay_username, unhexlify(opts.relay_password),
|
|
||||||
opts.relay_host, opts.relay_port, opts.encryption))
|
|
||||||
|
|
||||||
def test(self):
|
|
||||||
self.log.setPlainText(_('Sending...'))
|
|
||||||
self.test_button.setEnabled(False)
|
|
||||||
try:
|
|
||||||
tb = self.test_func(unicode(self.to.text()))
|
|
||||||
if not tb:
|
|
||||||
tb = _('Mail successfully sent')
|
|
||||||
self.log.setPlainText(tb)
|
|
||||||
finally:
|
|
||||||
self.test_button.setEnabled(True)
|
|
||||||
|
|
||||||
class EmailAccounts(QAbstractTableModel):
|
class EmailAccounts(QAbstractTableModel):
|
||||||
|
|
||||||
def __init__(self, accounts):
|
def __init__(self, accounts):
|
||||||
@ -477,32 +447,19 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
self.stackedWidget.insertWidget(2, self.conversion_options)
|
self.stackedWidget.insertWidget(2, self.conversion_options)
|
||||||
|
|
||||||
def setup_email_page(self):
|
def setup_email_page(self):
|
||||||
opts = smtp_prefs().parse()
|
def x():
|
||||||
if opts.from_:
|
if self._email_accounts.account_order:
|
||||||
self.email_from.setText(opts.from_)
|
return self._email_accounts.account_order[0]
|
||||||
|
self.send_email_widget.initialize(x)
|
||||||
|
opts = self.send_email_widget.smtp_opts
|
||||||
self._email_accounts = EmailAccounts(opts.accounts)
|
self._email_accounts = EmailAccounts(opts.accounts)
|
||||||
self.email_view.setModel(self._email_accounts)
|
self.email_view.setModel(self._email_accounts)
|
||||||
if opts.relay_host:
|
|
||||||
self.relay_host.setText(opts.relay_host)
|
|
||||||
self.relay_port.setValue(opts.relay_port)
|
|
||||||
if opts.relay_username:
|
|
||||||
self.relay_username.setText(opts.relay_username)
|
|
||||||
if opts.relay_password:
|
|
||||||
self.relay_password.setText(unhexlify(opts.relay_password))
|
|
||||||
(self.relay_tls if opts.encryption == 'TLS' else self.relay_ssl).setChecked(True)
|
|
||||||
self.connect(self.relay_use_gmail, SIGNAL('clicked(bool)'),
|
|
||||||
self.create_gmail_relay)
|
|
||||||
self.connect(self.relay_show_password, SIGNAL('stateChanged(int)'),
|
|
||||||
lambda
|
|
||||||
state:self.relay_password.setEchoMode(self.relay_password.Password if
|
|
||||||
state == 0 else self.relay_password.Normal))
|
|
||||||
self.connect(self.email_add, SIGNAL('clicked(bool)'),
|
self.connect(self.email_add, SIGNAL('clicked(bool)'),
|
||||||
self.add_email_account)
|
self.add_email_account)
|
||||||
self.connect(self.email_make_default, SIGNAL('clicked(bool)'),
|
self.connect(self.email_make_default, SIGNAL('clicked(bool)'),
|
||||||
lambda c: self._email_accounts.make_default(self.email_view.currentIndex()))
|
lambda c: self._email_accounts.make_default(self.email_view.currentIndex()))
|
||||||
self.email_view.resizeColumnsToContents()
|
self.email_view.resizeColumnsToContents()
|
||||||
self.connect(self.test_email_button, SIGNAL('clicked(bool)'),
|
|
||||||
self.test_email)
|
|
||||||
self.connect(self.email_remove, SIGNAL('clicked()'),
|
self.connect(self.email_remove, SIGNAL('clicked()'),
|
||||||
self.remove_email_account)
|
self.remove_email_account)
|
||||||
|
|
||||||
@ -516,68 +473,14 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
idx = self.email_view.currentIndex()
|
idx = self.email_view.currentIndex()
|
||||||
self._email_accounts.remove(idx)
|
self._email_accounts.remove(idx)
|
||||||
|
|
||||||
def create_gmail_relay(self, *args):
|
|
||||||
self.relay_username.setText('@gmail.com')
|
|
||||||
self.relay_password.setText('')
|
|
||||||
self.relay_host.setText('smtp.gmail.com')
|
|
||||||
self.relay_port.setValue(587)
|
|
||||||
self.relay_tls.setChecked(True)
|
|
||||||
|
|
||||||
info_dialog(self, _('Finish gmail setup'),
|
|
||||||
_('Dont forget to enter your gmail username and password')).exec_()
|
|
||||||
self.relay_username.setFocus(Qt.OtherFocusReason)
|
|
||||||
self.relay_username.setCursorPosition(0)
|
|
||||||
|
|
||||||
def set_email_settings(self):
|
def set_email_settings(self):
|
||||||
from_ = unicode(self.email_from.text()).strip()
|
to_set = bool(self._email_accounts.accounts)
|
||||||
if self._email_accounts.accounts and not from_:
|
if not self.send_email_widget.set_email_settings(to_set):
|
||||||
error_dialog(self, _('Bad configuration'),
|
|
||||||
_('You must set the From email address')).exec_()
|
|
||||||
return False
|
|
||||||
username = unicode(self.relay_username.text()).strip()
|
|
||||||
password = unicode(self.relay_password.text()).strip()
|
|
||||||
host = unicode(self.relay_host.text()).strip()
|
|
||||||
if host and not (username and password):
|
|
||||||
error_dialog(self, _('Bad configuration'),
|
|
||||||
_('You must set the username and password for '
|
|
||||||
'the mail server.')).exec_()
|
|
||||||
return False
|
return False
|
||||||
conf = smtp_prefs()
|
conf = smtp_prefs()
|
||||||
conf.set('from_', from_)
|
|
||||||
conf.set('accounts', self._email_accounts.accounts)
|
conf.set('accounts', self._email_accounts.accounts)
|
||||||
conf.set('relay_host', host if host else None)
|
|
||||||
conf.set('relay_port', self.relay_port.value())
|
|
||||||
conf.set('relay_username', username if username else None)
|
|
||||||
conf.set('relay_password', hexlify(password))
|
|
||||||
conf.set('encryption', 'TLS' if self.relay_tls.isChecked() else 'SSL')
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def test_email(self, *args):
|
|
||||||
if self.set_email_settings():
|
|
||||||
TestEmail(self._email_accounts.accounts, self).exec_()
|
|
||||||
|
|
||||||
def test_email_settings(self, to):
|
|
||||||
opts = smtp_prefs().parse()
|
|
||||||
from calibre.utils.smtp import sendmail, create_mail
|
|
||||||
buf = cStringIO.StringIO()
|
|
||||||
oout, oerr = sys.stdout, sys.stderr
|
|
||||||
sys.stdout = sys.stderr = buf
|
|
||||||
tb = None
|
|
||||||
try:
|
|
||||||
msg = create_mail(opts.from_, to, 'Test mail from calibre',
|
|
||||||
'Test mail from calibre')
|
|
||||||
sendmail(msg, from_=opts.from_, to=[to],
|
|
||||||
verbose=3, timeout=30, relay=opts.relay_host,
|
|
||||||
username=opts.relay_username,
|
|
||||||
password=unhexlify(opts.relay_password),
|
|
||||||
encryption=opts.encryption, port=opts.relay_port)
|
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
tb = traceback.format_exc()
|
|
||||||
tb += '\n\nLog:\n' + buf.getvalue()
|
|
||||||
finally:
|
|
||||||
sys.stdout, sys.stderr = oout, oerr
|
|
||||||
return tb
|
|
||||||
|
|
||||||
def add_plugin(self):
|
def add_plugin(self):
|
||||||
path = unicode(self.plugin_path.text())
|
path = unicode(self.plugin_path.text())
|
||||||
@ -722,7 +625,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
def browse(self):
|
def browse(self):
|
||||||
dir = choose_dir(self, 'database location dialog',
|
dir = choose_dir(self, 'database location dialog',
|
||||||
_('Select database location'))
|
_('Select location for books'))
|
||||||
if dir:
|
if dir:
|
||||||
self.location.setText(dir)
|
self.location.setText(dir)
|
||||||
|
|
||||||
|
@ -541,38 +541,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="page_6">
|
<widget class="QWidget" name="page_6">
|
||||||
<layout class="QGridLayout" name="gridLayout_6">
|
<layout class="QGridLayout" name="gridLayout_6">
|
||||||
<item row="0" column="0" colspan="2">
|
|
||||||
<widget class="QLabel" name="label_22">
|
|
||||||
<property name="text">
|
|
||||||
<string>calibre can send your books to you (or your reader) by email</string>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0" colspan="2">
|
<item row="1" column="0" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_15">
|
|
||||||
<property name="text">
|
|
||||||
<string>Send email &from:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>email_from</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="email_from">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><p>This is what will be present in the From: field of emails sent by calibre.<br> Set it to your email address</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTableView" name="email_view">
|
<widget class="QTableView" name="email_view">
|
||||||
@ -640,195 +609,18 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="0" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox_5">
|
<widget class="QLabel" name="label_22">
|
||||||
<property name="toolTip">
|
|
||||||
<string><p>A mail server is useful if the service you are sending mail to only accepts email from well know mail services.</string>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>Mail &Server</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
|
||||||
<item row="0" column="0" colspan="4">
|
|
||||||
<widget class="QLabel" name="label_16">
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>calibre can <b>optionally</b> use a server to send mail</string>
|
<string>calibre can send your books to you (or your reader) by email</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="2" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_17">
|
<widget class="SendEmail" name="send_email_widget" native="true"/>
|
||||||
<property name="text">
|
|
||||||
<string>&Hostname:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>relay_host</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1" colspan="2">
|
|
||||||
<widget class="QLineEdit" name="relay_host">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>The hostname of your mail server. For e.g. smtp.gmail.com</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="3">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_11">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_18">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Port:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>relay_port</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QSpinBox" name="relay_port">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>The port your mail server listens for connections on. The default is 25</string>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>65555</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<number>25</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_19">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Username:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>relay_username</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1" colspan="2">
|
|
||||||
<widget class="QLineEdit" name="relay_username">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Your username on the mail server</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_20">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Password:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>relay_password</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1" colspan="2">
|
|
||||||
<widget class="QLineEdit" name="relay_password">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Your password on the mail server</string>
|
|
||||||
</property>
|
|
||||||
<property name="echoMode">
|
|
||||||
<enum>QLineEdit::Password</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="3">
|
|
||||||
<widget class="QCheckBox" name="relay_show_password">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Show</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="label_21">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Encryption:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>relay_tls</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QRadioButton" name="relay_tls">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Use TLS encryption when connecting to the mail server. This is the most common.</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>&TLS</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="2" colspan="2">
|
|
||||||
<widget class="QRadioButton" name="relay_ssl">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Use SSL encryption when connecting to the mail server.</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>&SSL</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="4">
|
|
||||||
<spacer name="horizontalSpacer_3">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="relay_use_gmail">
|
|
||||||
<property name="text">
|
|
||||||
<string>Use Gmail</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../images.qrc">
|
|
||||||
<normaloff>:/images/gmail_logo.png</normaloff>:/images/gmail_logo.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="iconSize">
|
|
||||||
<size>
|
|
||||||
<width>48</width>
|
|
||||||
<height>48</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolButtonStyle">
|
|
||||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="test_email_button">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Test email</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@ -1062,7 +854,8 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_13">
|
<widget class="QLabel" name="label_13">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>If you want to use the content server to access your ebook collection on your iphone with Stanza, you will need to add the URL http://myhostname:8080/stanza as a new catalog in the stanza reader on your iphone. Here myhostname should be the fully qualified hostname or the IP address of this computer.</string>
|
<string><p>Remember to leave calibre running as the server only runs as long as calibre is running.
|
||||||
|
<p>Stanza should see your calibre collection automatically. If not, try adding the URL http://myhostname:8080 as a new catalog in the Stanza reader on your iPhone. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -1216,6 +1009,14 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>SendEmail</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>calibre/gui2/wizard/send_email.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../images.qrc"/>
|
<include location="../images.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -11,11 +11,11 @@ from PyQt4.Qt import Qt, SIGNAL, QObject, QCoreApplication, QUrl, QTimer, \
|
|||||||
QModelIndex, QPixmap, QColor, QPainter, QMenu, QIcon, \
|
QModelIndex, QPixmap, QColor, QPainter, QMenu, QIcon, \
|
||||||
QToolButton, QDialog, QDesktopServices, QFileDialog, \
|
QToolButton, QDialog, QDesktopServices, QFileDialog, \
|
||||||
QSystemTrayIcon, QApplication, QKeySequence, QAction, \
|
QSystemTrayIcon, QApplication, QKeySequence, QAction, \
|
||||||
QProgressDialog, QMessageBox, QStackedLayout
|
QMessageBox, QStackedLayout
|
||||||
from PyQt4.QtSvg import QSvgRenderer
|
from PyQt4.QtSvg import QSvgRenderer
|
||||||
|
|
||||||
from calibre import __version__, __appname__, sanitize_file_name, \
|
from calibre import __version__, __appname__, sanitize_file_name, \
|
||||||
iswindows, isosx, prints
|
iswindows, isosx, prints, patheq
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre.utils.config import prefs, dynamic
|
from calibre.utils.config import prefs, dynamic
|
||||||
from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
|
from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
|
||||||
@ -27,6 +27,7 @@ from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
|
|||||||
available_width, GetMetadata
|
available_width, GetMetadata
|
||||||
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
|
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
|
||||||
from calibre.gui2.widgets import ProgressIndicator
|
from calibre.gui2.widgets import ProgressIndicator
|
||||||
|
from calibre.gui2.wizard import move_library
|
||||||
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 as _option_parser
|
from calibre.gui2.main_window import MainWindow, option_parser as _option_parser
|
||||||
@ -297,6 +298,14 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
QObject.connect(self.action_convert,
|
QObject.connect(self.action_convert,
|
||||||
SIGNAL('triggered(bool)'), self.convert_single)
|
SIGNAL('triggered(bool)'), self.convert_single)
|
||||||
self.convert_menu = cm
|
self.convert_menu = cm
|
||||||
|
pm = QMenu()
|
||||||
|
pm.addAction(self.action_preferences)
|
||||||
|
pm.addAction(_('Run welcome wizard'))
|
||||||
|
self.connect(pm.actions()[1], SIGNAL('triggered(bool)'),
|
||||||
|
self.run_wizard)
|
||||||
|
self.action_preferences.setMenu(pm)
|
||||||
|
self.preferences_menu = pm
|
||||||
|
|
||||||
self.tool_bar.widgetForAction(self.action_news).\
|
self.tool_bar.widgetForAction(self.action_news).\
|
||||||
setPopupMode(QToolButton.MenuButtonPopup)
|
setPopupMode(QToolButton.MenuButtonPopup)
|
||||||
self.tool_bar.widgetForAction(self.action_edit).\
|
self.tool_bar.widgetForAction(self.action_edit).\
|
||||||
@ -311,6 +320,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
setPopupMode(QToolButton.MenuButtonPopup)
|
setPopupMode(QToolButton.MenuButtonPopup)
|
||||||
self.tool_bar.widgetForAction(self.action_view).\
|
self.tool_bar.widgetForAction(self.action_view).\
|
||||||
setPopupMode(QToolButton.MenuButtonPopup)
|
setPopupMode(QToolButton.MenuButtonPopup)
|
||||||
|
self.tool_bar.widgetForAction(self.action_preferences).\
|
||||||
|
setPopupMode(QToolButton.MenuButtonPopup)
|
||||||
self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
|
self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
|
||||||
|
|
||||||
self.connect(self.preferences_action, SIGNAL('triggered(bool)'),
|
self.connect(self.preferences_action, SIGNAL('triggered(bool)'),
|
||||||
@ -1376,56 +1387,27 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.save_menu.actions()[2].setText(
|
self.save_menu.actions()[2].setText(
|
||||||
_('Save only %s format to disk')%
|
_('Save only %s format to disk')%
|
||||||
prefs['output_format'].upper())
|
prefs['output_format'].upper())
|
||||||
if self.library_path != d.database_location:
|
|
||||||
try:
|
|
||||||
newloc = d.database_location
|
|
||||||
if not os.path.exists(os.path.join(newloc, 'metadata.db')):
|
|
||||||
if os.access(self.library_path, os.R_OK):
|
|
||||||
pd = QProgressDialog('', '', 0, 100, self)
|
|
||||||
pd.setWindowModality(Qt.ApplicationModal)
|
|
||||||
pd.setCancelButton(None)
|
|
||||||
pd.setWindowTitle(_('Copying database'))
|
|
||||||
pd.show()
|
|
||||||
self.status_bar.showMessage(
|
|
||||||
_('Copying library to ')+newloc)
|
|
||||||
self.setCursor(Qt.BusyCursor)
|
|
||||||
self.library_view.setEnabled(False)
|
|
||||||
self.library_view.model().db.move_library_to(
|
|
||||||
newloc, pd)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
db = LibraryDatabase2(newloc)
|
|
||||||
self.library_view.set_database(db)
|
|
||||||
except Exception, err:
|
|
||||||
traceback.print_exc()
|
|
||||||
d = error_dialog(self, _('Invalid database'),
|
|
||||||
_('<p>An invalid database already exists at '
|
|
||||||
'%s, delete it before trying to move the '
|
|
||||||
'existing database.<br>Error: %s')%(newloc,
|
|
||||||
str(err)))
|
|
||||||
d.exec_()
|
|
||||||
self.library_path = \
|
|
||||||
self.library_view.model().db.library_path
|
|
||||||
prefs['library_path'] = self.library_path
|
|
||||||
except Exception, err:
|
|
||||||
traceback.print_exc()
|
|
||||||
d = error_dialog(self, _('Could not move database'),
|
|
||||||
unicode(err))
|
|
||||||
d.exec_()
|
|
||||||
finally:
|
|
||||||
self.unsetCursor()
|
|
||||||
self.library_view.setEnabled(True)
|
|
||||||
self.status_bar.clearMessage()
|
|
||||||
self.search.clear_to_help()
|
|
||||||
self.status_bar.reset_info()
|
|
||||||
self.library_view.sortByColumn(3, Qt.DescendingOrder)
|
|
||||||
self.library_view.resizeRowsToContents()
|
|
||||||
if hasattr(d, 'directories'):
|
if hasattr(d, 'directories'):
|
||||||
set_sidebar_directories(d.directories)
|
set_sidebar_directories(d.directories)
|
||||||
self.library_view.model().read_config()
|
self.library_view.model().read_config()
|
||||||
self.create_device_menu()
|
self.create_device_menu()
|
||||||
|
|
||||||
|
|
||||||
|
if not patheq(self.library_path, d.database_location):
|
||||||
|
newloc = d.database_location
|
||||||
|
move_library(self.library_path, newloc, self,
|
||||||
|
self.library_moved)
|
||||||
|
|
||||||
|
|
||||||
|
def library_moved(self, newloc):
|
||||||
|
if newloc is None: return
|
||||||
|
db = LibraryDatabase2(newloc)
|
||||||
|
self.library_view.set_database(db)
|
||||||
|
self.status_bar.clearMessage()
|
||||||
|
self.search.clear_to_help()
|
||||||
|
self.status_bar.reset_info()
|
||||||
|
self.library_view.sortByColumn(3, Qt.DescendingOrder)
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
################################ Book info #################################
|
################################ Book info #################################
|
||||||
@ -1652,6 +1634,17 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.hide()
|
self.hide()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def run_wizard(self, *args):
|
||||||
|
if self.confirm_quit():
|
||||||
|
self.run_wizard_b4_shutdown = True
|
||||||
|
self.restart_after_quit = True
|
||||||
|
try:
|
||||||
|
self.shutdown(write_settings=False)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
QApplication.instance().quit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def closeEvent(self, e):
|
def closeEvent(self, e):
|
||||||
self.write_settings()
|
self.write_settings()
|
||||||
@ -1726,12 +1719,19 @@ def init_qt(args):
|
|||||||
|
|
||||||
def run_gui(opts, args, actions, listener, app):
|
def run_gui(opts, args, actions, listener, app):
|
||||||
initialize_file_icon_provider()
|
initialize_file_icon_provider()
|
||||||
|
if not dynamic.get('welcome_wizard_was_run', False):
|
||||||
|
from calibre.gui2.wizard import wizard
|
||||||
|
wizard().exec_()
|
||||||
|
dynamic.set('welcome_wizard_was_run', True)
|
||||||
main = Main(listener, opts, actions)
|
main = Main(listener, opts, actions)
|
||||||
sys.excepthook = main.unhandled_exception
|
sys.excepthook = main.unhandled_exception
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
args[1] = os.path.abspath(args[1])
|
args[1] = os.path.abspath(args[1])
|
||||||
main.add_filesystem_book(args[1])
|
main.add_filesystem_book(args[1])
|
||||||
ret = app.exec_()
|
ret = app.exec_()
|
||||||
|
if getattr(main, 'run_wizard_b4_shutdown', False):
|
||||||
|
from calibre.gui2.wizard import wizard
|
||||||
|
wizard().exec_()
|
||||||
if getattr(main, 'restart_after_quit', False):
|
if getattr(main, 'restart_after_quit', False):
|
||||||
e = sys.executable if getattr(sys, 'froze', False) else sys.argv[0]
|
e = sys.executable if getattr(sys, 'froze', False) else sys.argv[0]
|
||||||
print 'Restarting with:', e, sys.argv
|
print 'Restarting with:', e, sys.argv
|
||||||
|
500
src/calibre/gui2/wizard/__init__.py
Normal file
500
src/calibre/gui2/wizard/__init__.py
Normal file
@ -0,0 +1,500 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import os, traceback, re
|
||||||
|
from Queue import Empty, Queue
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
|
||||||
|
from PyQt4.Qt import QWizard, QWizardPage, QPixmap, Qt, QAbstractListModel, \
|
||||||
|
QVariant, QItemSelectionModel, SIGNAL, QObject, QTimer
|
||||||
|
from calibre import __appname__, patheq
|
||||||
|
from calibre.library.database2 import LibraryDatabase2
|
||||||
|
from calibre.library.move import MoveLibrary
|
||||||
|
from calibre.resources import server_resources
|
||||||
|
from calibre.gui2.wizard.send_email import smtp_prefs
|
||||||
|
from calibre.gui2.wizard.device_ui import Ui_WizardPage as DeviceUI
|
||||||
|
from calibre.gui2.wizard.library_ui import Ui_WizardPage as LibraryUI
|
||||||
|
from calibre.gui2.wizard.finish_ui import Ui_WizardPage as FinishUI
|
||||||
|
from calibre.gui2.wizard.kindle_ui import Ui_WizardPage as KindleUI
|
||||||
|
from calibre.gui2.wizard.stanza_ui import Ui_WizardPage as StanzaUI
|
||||||
|
|
||||||
|
from calibre.utils.config import dynamic, prefs
|
||||||
|
from calibre.gui2 import NONE, choose_dir, error_dialog
|
||||||
|
from calibre.gui2.dialogs.progress import ProgressDialog
|
||||||
|
|
||||||
|
class Device(object):
|
||||||
|
|
||||||
|
output_profile = 'default'
|
||||||
|
output_format = 'EPUB'
|
||||||
|
name = _('Default')
|
||||||
|
manufacturer = _('Default')
|
||||||
|
id = 'default'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_output_profile(cls):
|
||||||
|
if cls.output_profile:
|
||||||
|
from calibre.ebooks.conversion.config import load_defaults, save_defaults
|
||||||
|
recs = load_defaults('page_setup')
|
||||||
|
recs['output_profile'] = cls.output_profile
|
||||||
|
save_defaults('page_setup', recs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_output_format(cls):
|
||||||
|
if cls.output_format:
|
||||||
|
prefs.set('output_format', cls.output_format)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def commit(cls):
|
||||||
|
cls.set_output_profile()
|
||||||
|
cls.set_output_format()
|
||||||
|
|
||||||
|
class Kindle(Device):
|
||||||
|
|
||||||
|
output_profile = 'kindle'
|
||||||
|
output_format = 'MOBI'
|
||||||
|
name = 'Kindle 1 or 2'
|
||||||
|
manufacturer = 'Amazon'
|
||||||
|
id = 'kindle'
|
||||||
|
|
||||||
|
class Sony500(Device):
|
||||||
|
|
||||||
|
output_profile = 'sony'
|
||||||
|
name = 'SONY PRS 500'
|
||||||
|
output_format = 'LRF'
|
||||||
|
manufacturer = 'SONY'
|
||||||
|
id = 'prs500'
|
||||||
|
|
||||||
|
class Sony505(Sony500):
|
||||||
|
|
||||||
|
output_format = 'EPUB'
|
||||||
|
name = 'SONY PRS 505/700'
|
||||||
|
id = 'prs505'
|
||||||
|
|
||||||
|
class CybookG3(Device):
|
||||||
|
|
||||||
|
name = 'Cybook Gen 3'
|
||||||
|
output_format = 'MOBI'
|
||||||
|
output_profile = 'cybookg3'
|
||||||
|
manufacturer = 'Booken'
|
||||||
|
id = 'cybookg3'
|
||||||
|
|
||||||
|
class BeBook(Device):
|
||||||
|
|
||||||
|
name = 'BeBook or BeBook Mini'
|
||||||
|
output_format = 'EPUB'
|
||||||
|
output_profile = 'sony'
|
||||||
|
manufacturer = 'Endless Ideas'
|
||||||
|
id = 'bebook'
|
||||||
|
|
||||||
|
class iPhone(Device):
|
||||||
|
|
||||||
|
name = 'iPhone/iTouch + Stanza'
|
||||||
|
output_format = 'EPUB'
|
||||||
|
manufacturer = 'Apple'
|
||||||
|
id = 'iphone'
|
||||||
|
|
||||||
|
class Hanlin(Device):
|
||||||
|
|
||||||
|
name = 'Hanlin V3'
|
||||||
|
output_format = 'MOBI'
|
||||||
|
output_profile = 'hanlinv3'
|
||||||
|
manufacturer = 'Hanlin'
|
||||||
|
id = 'hanlinv3'
|
||||||
|
|
||||||
|
def get_devices():
|
||||||
|
for x in globals().values():
|
||||||
|
if isinstance(x, type) and issubclass(x, Device):
|
||||||
|
yield x
|
||||||
|
|
||||||
|
def get_manufacturers():
|
||||||
|
mans = set([])
|
||||||
|
for x in get_devices():
|
||||||
|
mans.add(x.manufacturer)
|
||||||
|
mans.remove(_('Default'))
|
||||||
|
return [_('Default')] + sorted(mans)
|
||||||
|
|
||||||
|
def get_devices_of(manufacturer):
|
||||||
|
ans = [d for d in get_devices() if d.manufacturer == manufacturer]
|
||||||
|
return sorted(ans, cmp=lambda x,y:cmp(x.name, y.name))
|
||||||
|
|
||||||
|
class ManufacturerModel(QAbstractListModel):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QAbstractListModel.__init__(self)
|
||||||
|
self.manufacturers = get_manufacturers()
|
||||||
|
|
||||||
|
def rowCount(self, p):
|
||||||
|
return len(self.manufacturers)
|
||||||
|
|
||||||
|
def columnCount(self, p):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role == Qt.DisplayRole:
|
||||||
|
return QVariant(self.manufacturers[index.row()])
|
||||||
|
if role == Qt.UserRole:
|
||||||
|
return self.manufacturers[index.row()]
|
||||||
|
return NONE
|
||||||
|
|
||||||
|
def index_of(self, man):
|
||||||
|
for i, x in enumerate(self.manufacturers):
|
||||||
|
if x == man:
|
||||||
|
return self.index(i)
|
||||||
|
|
||||||
|
class DeviceModel(QAbstractListModel):
|
||||||
|
|
||||||
|
def __init__(self, manufacturer):
|
||||||
|
QAbstractListModel.__init__(self)
|
||||||
|
self.devices = get_devices_of(manufacturer)
|
||||||
|
|
||||||
|
def rowCount(self, p):
|
||||||
|
return len(self.devices)
|
||||||
|
|
||||||
|
def columnCount(self, p):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role == Qt.DisplayRole:
|
||||||
|
return QVariant(self.devices[index.row()].name)
|
||||||
|
if role == Qt.UserRole:
|
||||||
|
return self.devices[index.row()]
|
||||||
|
return NONE
|
||||||
|
|
||||||
|
def index_of(self, dev):
|
||||||
|
for i, device in enumerate(self.devices):
|
||||||
|
if device is dev:
|
||||||
|
return self.index(i)
|
||||||
|
|
||||||
|
class KindlePage(QWizardPage, KindleUI):
|
||||||
|
|
||||||
|
ID = 3
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QWizardPage.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
def initializePage(self):
|
||||||
|
opts = smtp_prefs().parse()
|
||||||
|
for x in opts.accounts.keys():
|
||||||
|
if x.strip().endswith('@kindle.com'):
|
||||||
|
self.to_address.setText(x)
|
||||||
|
def x():
|
||||||
|
t = unicode(self.to_address.text())
|
||||||
|
if t.strip():
|
||||||
|
return t.strip()
|
||||||
|
|
||||||
|
self.send_email_widget.initialize(x)
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
x = unicode(self.to_address.text()).strip()
|
||||||
|
parts = x.split('@')
|
||||||
|
if len(parts) < 2 or not parts[0]: return
|
||||||
|
|
||||||
|
if self.send_email_widget.set_email_settings(True):
|
||||||
|
conf = smtp_prefs()
|
||||||
|
accounts = conf.get('accounts', {})
|
||||||
|
if not accounts: accounts = {}
|
||||||
|
for y in accounts.values():
|
||||||
|
y[2] = False
|
||||||
|
accounts[x] = ['AZW, MOBI, TPZ, PRC, AZW1', True, True]
|
||||||
|
conf.set('accounts', accounts)
|
||||||
|
|
||||||
|
def nextId(self):
|
||||||
|
return FinishPage.ID
|
||||||
|
|
||||||
|
class StanzaPage(QWizardPage, StanzaUI):
|
||||||
|
|
||||||
|
ID = 5
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QWizardPage.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.connect(self.content_server, SIGNAL('stateChanged(int)'), self.set_port)
|
||||||
|
|
||||||
|
def initializePage(self):
|
||||||
|
from calibre.gui2 import config
|
||||||
|
yes = config['autolaunch_server']
|
||||||
|
self.content_server.setChecked(yes)
|
||||||
|
self.set_port()
|
||||||
|
|
||||||
|
def nextId(self):
|
||||||
|
return FinishPage.ID
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
p = self.set_port()
|
||||||
|
if p is not None:
|
||||||
|
from calibre.library import server_config
|
||||||
|
c = server_config()
|
||||||
|
c.set('port', p)
|
||||||
|
|
||||||
|
|
||||||
|
def set_port(self, *args):
|
||||||
|
if not self.content_server.isChecked(): return
|
||||||
|
import socket
|
||||||
|
s = socket.socket()
|
||||||
|
with closing(s):
|
||||||
|
for p in range(8080, 8100):
|
||||||
|
try:
|
||||||
|
s.bind(('0.0.0.0', p))
|
||||||
|
t = unicode(self.instructions.text())
|
||||||
|
t = re.sub(r':\d+', ':'+str(p), t)
|
||||||
|
self.instructions.setText(t)
|
||||||
|
return p
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DevicePage(QWizardPage, DeviceUI):
|
||||||
|
|
||||||
|
ID = 2
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QWizardPage.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.registerField("manufacturer", self.manufacturer_view)
|
||||||
|
self.registerField("device", self.device_view)
|
||||||
|
|
||||||
|
def initializePage(self):
|
||||||
|
self.man_model = ManufacturerModel()
|
||||||
|
self.manufacturer_view.setModel(self.man_model)
|
||||||
|
previous = dynamic.get('welcome_wizard_device', False)
|
||||||
|
if previous:
|
||||||
|
previous = [x for x in get_devices() if \
|
||||||
|
x.id == previous]
|
||||||
|
if not previous:
|
||||||
|
previous = [Device]
|
||||||
|
previous = previous[0]
|
||||||
|
else:
|
||||||
|
previous = Device
|
||||||
|
idx = self.man_model.index_of(previous.manufacturer)
|
||||||
|
if idx is None:
|
||||||
|
idx = self.man_model.index_of(Device.manufacturer)
|
||||||
|
previous = Device
|
||||||
|
self.manufacturer_view.selectionModel().select(idx,
|
||||||
|
QItemSelectionModel.Select)
|
||||||
|
self.dev_model = DeviceModel(self.man_model.data(idx, Qt.UserRole))
|
||||||
|
idx = self.dev_model.index_of(previous)
|
||||||
|
self.device_view.setModel(self.dev_model)
|
||||||
|
self.device_view.selectionModel().select(idx,
|
||||||
|
QItemSelectionModel.Select)
|
||||||
|
self.connect(self.manufacturer_view.selectionModel(),
|
||||||
|
SIGNAL('selectionChanged(QItemSelection,QItemSelection)'),
|
||||||
|
self.manufacturer_changed)
|
||||||
|
|
||||||
|
def manufacturer_changed(self, current, previous):
|
||||||
|
new = list(current.indexes())[0]
|
||||||
|
man = self.man_model.data(new, Qt.UserRole)
|
||||||
|
self.dev_model = DeviceModel(man)
|
||||||
|
self.device_view.setModel(self.dev_model)
|
||||||
|
self.device_view.selectionModel().select(self.dev_model.index(0),
|
||||||
|
QItemSelectionModel.Select)
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
idx = list(self.device_view.selectionModel().selectedIndexes())[0]
|
||||||
|
dev = self.dev_model.data(idx, Qt.UserRole)
|
||||||
|
dev.commit()
|
||||||
|
dynamic.set('welcome_wizard_device', dev.id)
|
||||||
|
|
||||||
|
def nextId(self):
|
||||||
|
idx = list(self.device_view.selectionModel().selectedIndexes())[0]
|
||||||
|
dev = self.dev_model.data(idx, Qt.UserRole)
|
||||||
|
if dev is Kindle:
|
||||||
|
return KindlePage.ID
|
||||||
|
if dev is iPhone:
|
||||||
|
return StanzaPage.ID
|
||||||
|
return FinishPage.ID
|
||||||
|
|
||||||
|
class MoveMonitor(QObject):
|
||||||
|
|
||||||
|
def __init__(self, worker, rq, callback, parent):
|
||||||
|
QObject.__init__(self, parent)
|
||||||
|
self.worker = worker
|
||||||
|
self.rq = rq
|
||||||
|
self.callback = callback
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
self.worker.start()
|
||||||
|
self.dialog = ProgressDialog(_('Moving library...'), '',
|
||||||
|
max=self.worker.total, parent=parent)
|
||||||
|
self.dialog.button_box.setDisabled(True)
|
||||||
|
self.dialog.setModal(True)
|
||||||
|
self.dialog.show()
|
||||||
|
self.timer = QTimer(self)
|
||||||
|
self.connect(self.timer, SIGNAL('timeout()'), self.check)
|
||||||
|
self.timer.start(200)
|
||||||
|
|
||||||
|
def check(self):
|
||||||
|
if self.worker.is_alive():
|
||||||
|
self.update()
|
||||||
|
else:
|
||||||
|
self.timer.stop()
|
||||||
|
self.dialog.hide()
|
||||||
|
if self.worker.failed:
|
||||||
|
error_dialog(self.parent, _('Failed to move library'),
|
||||||
|
_('Failed to move library'), self.worker.details, show=True)
|
||||||
|
return self.callback(None)
|
||||||
|
else:
|
||||||
|
return self.callback(self.worker.to)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
try:
|
||||||
|
title = self.rq.get_nowait()[-1]
|
||||||
|
self.dialog.value += 1
|
||||||
|
self.dialog.set_msg(_('Copied') + ' '+title)
|
||||||
|
except Empty:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Callback(object):
|
||||||
|
|
||||||
|
def __init__(self, callback):
|
||||||
|
self.callback = callback
|
||||||
|
|
||||||
|
def __call__(self, newloc):
|
||||||
|
if newloc is not None:
|
||||||
|
prefs['library_path'] = newloc
|
||||||
|
self.callback(newloc)
|
||||||
|
|
||||||
|
_mm = None
|
||||||
|
def move_library(oldloc, newloc, parent, callback_on_complete):
|
||||||
|
callback = Callback(callback_on_complete)
|
||||||
|
try:
|
||||||
|
if not os.path.exists(os.path.join(newloc, 'metadata.db')):
|
||||||
|
if oldloc and os.access(os.path.join(oldloc, 'metadata.db'), os.R_OK):
|
||||||
|
# Move old library to new location
|
||||||
|
try:
|
||||||
|
db = LibraryDatabase2(oldloc)
|
||||||
|
except:
|
||||||
|
return move_library(None, newloc, parent,
|
||||||
|
callback)
|
||||||
|
else:
|
||||||
|
rq = Queue()
|
||||||
|
m = MoveLibrary(oldloc, newloc, db.data.count(), rq)
|
||||||
|
global _mm
|
||||||
|
_mm = MoveMonitor(m, rq, callback, parent)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# Create new library at new location
|
||||||
|
db = LibraryDatabase2(newloc)
|
||||||
|
callback(newloc)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Try to load existing library at new location
|
||||||
|
try:
|
||||||
|
ndb = LibraryDatabase2(newloc)
|
||||||
|
except Exception, err:
|
||||||
|
det = traceback.format_exc()
|
||||||
|
error_dialog(parent, _('Invalid database'),
|
||||||
|
_('<p>An invalid library already exists at '
|
||||||
|
'%s, delete it before trying to move the '
|
||||||
|
'existing library.<br>Error: %s')%(newloc,
|
||||||
|
str(err)), det, show=True)
|
||||||
|
callback(None)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
callback(newloc)
|
||||||
|
return
|
||||||
|
except Exception, err:
|
||||||
|
det = traceback.format_exc()
|
||||||
|
error_dialog(parent, _('Could not move library'),
|
||||||
|
unicode(err), det, show=True)
|
||||||
|
callback(None)
|
||||||
|
|
||||||
|
class LibraryPage(QWizardPage, LibraryUI):
|
||||||
|
|
||||||
|
ID = 1
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QWizardPage.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.registerField('library_location', self.location)
|
||||||
|
self.connect(self.button_change, SIGNAL('clicked()'), self.change)
|
||||||
|
|
||||||
|
def change(self):
|
||||||
|
dir = choose_dir(self, 'database location dialog',
|
||||||
|
_('Select location for books'))
|
||||||
|
if dir:
|
||||||
|
self.location.setText(dir)
|
||||||
|
|
||||||
|
def initializePage(self):
|
||||||
|
lp = prefs['library_path']
|
||||||
|
if not lp:
|
||||||
|
lp = os.path.expanduser('~')
|
||||||
|
self.location.setText(lp)
|
||||||
|
|
||||||
|
def isComplete(self):
|
||||||
|
lp = unicode(self.location.text())
|
||||||
|
return lp and os.path.exists(lp) and os.path.isdir(lp) and os.access(lp,
|
||||||
|
os.W_OK)
|
||||||
|
|
||||||
|
def commit(self, completed):
|
||||||
|
oldloc = prefs['library_path']
|
||||||
|
newloc = unicode(self.location.text())
|
||||||
|
if not patheq(oldloc, newloc):
|
||||||
|
move_library(oldloc, newloc, self.wizard(), completed)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def nextId(self):
|
||||||
|
return DevicePage.ID
|
||||||
|
|
||||||
|
class FinishPage(QWizardPage, FinishUI):
|
||||||
|
|
||||||
|
ID = 4
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QWizardPage.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
def nextId(self):
|
||||||
|
return -1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Wizard(QWizard):
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QWizard.__init__(self, parent)
|
||||||
|
self.setWindowTitle(__appname__+' '+_('welcome wizard'))
|
||||||
|
p = QPixmap()
|
||||||
|
p.loadFromData(server_resources['calibre.png'])
|
||||||
|
self.setPixmap(self.LogoPixmap, p.scaledToHeight(80,
|
||||||
|
Qt.SmoothTransformation))
|
||||||
|
self.device_page = DevicePage()
|
||||||
|
self.library_page = LibraryPage()
|
||||||
|
self.finish_page = FinishPage()
|
||||||
|
self.kindle_page = KindlePage()
|
||||||
|
self.stanza_page = StanzaPage()
|
||||||
|
self.setPage(self.library_page.ID, self.library_page)
|
||||||
|
self.setPage(self.device_page.ID, self.device_page)
|
||||||
|
self.setPage(self.finish_page.ID, self.finish_page)
|
||||||
|
self.setPage(self.kindle_page.ID, self.kindle_page)
|
||||||
|
self.setPage(self.stanza_page.ID, self.stanza_page)
|
||||||
|
|
||||||
|
self.device_extra_page = None
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
self.device_page.commit()
|
||||||
|
if not self.library_page.commit(self.completed):
|
||||||
|
self.completed(None)
|
||||||
|
|
||||||
|
def completed(self, newloc):
|
||||||
|
return QWizard.accept(self)
|
||||||
|
|
||||||
|
def wizard(parent=None):
|
||||||
|
w = Wizard(parent)
|
||||||
|
return w
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from PyQt4.Qt import QApplication
|
||||||
|
app = QApplication([])
|
||||||
|
wizard().exec_()
|
||||||
|
|
75
src/calibre/gui2/wizard/device.ui
Normal file
75
src/calibre/gui2/wizard/device.ui
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>WizardPage</class>
|
||||||
|
<widget class="QWizardPage" name="WizardPage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Welcome to calibre</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="../images.qrc">
|
||||||
|
<normaloff>:/images/wizard.svg</normaloff>:/images/wizard.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Welcome to calibre</string>
|
||||||
|
</property>
|
||||||
|
<property name="subTitle">
|
||||||
|
<string>The one stop solution to all your e-book needs.</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Choose your book reader. This will set the conversion options to produce books optimized for your device.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>&Manufacturers</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QListView" name="manufacturer_view">
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="title">
|
||||||
|
<string>&Devices</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QListView" name="device_view">
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../images.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
108
src/calibre/gui2/wizard/finish.ui
Normal file
108
src/calibre/gui2/wizard/finish.ui
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>WizardPage</class>
|
||||||
|
<widget class="QWizardPage" name="WizardPage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Welcome to calibre</string>
|
||||||
|
</property>
|
||||||
|
<property name="subTitle">
|
||||||
|
<string>The one stop solution to all your e-book needs.</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string><h2>Congratulations!</h2> You have succesfully setup calibre. Press the Finish button to apply your settings.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>56</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string><h2>Demo videos</h2>Videos demonstrating the various features of calibre are available <a href="http://calibre.kovidgoyal.net/downloads/videos/">online</a>.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>56</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string><h2>User Manual</h2>A User Manual is also available <a href="http://calibre.kovidgoyal.net/user_manual">online</a>.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
80
src/calibre/gui2/wizard/kindle.ui
Normal file
80
src/calibre/gui2/wizard/kindle.ui
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>WizardPage</class>
|
||||||
|
<widget class="QWizardPage" name="WizardPage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Welcome to calibre</string>
|
||||||
|
</property>
|
||||||
|
<property name="subTitle">
|
||||||
|
<string>The one stop solution to all your e-book needs.</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string><p>calibre can automatically send books by email to your Kindle. To do that you have to setup email delivery below. The easiest way is to setup a free <a href="http://gmail.com">gmail account</a> and click the Use gmail button below. You will also have to register your gmail address in your Amazon account.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Kindle email:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>to_address</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="to_address"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="SendEmail" name="send_email_widget" native="true"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>SendEmail</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>calibre/gui2/wizard/send_email.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
74
src/calibre/gui2/wizard/library.ui
Normal file
74
src/calibre/gui2/wizard/library.ui
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>WizardPage</class>
|
||||||
|
<widget class="QWizardPage" name="WizardPage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>481</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Welcome to calibre</string>
|
||||||
|
</property>
|
||||||
|
<property name="subTitle">
|
||||||
|
<string>The one stop solution to all your e-book needs.</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Choose a location for your books. When you add books to calibre, they will be stored here:</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLineEdit" name="location">
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QPushButton" name="button_change">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Change</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>If you have an existing calibre library, it will be copied to the new location. If a calibre library already exists at the new location, calibre will switch to using it.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
143
src/calibre/gui2/wizard/send_email.py
Normal file
143
src/calibre/gui2/wizard/send_email.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import cStringIO, sys
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
|
||||||
|
from PyQt4.Qt import QWidget, SIGNAL, QDialog, Qt
|
||||||
|
|
||||||
|
from calibre.gui2.wizard.send_email_ui import Ui_Form
|
||||||
|
from calibre.utils.smtp import config as smtp_prefs
|
||||||
|
from calibre.gui2.dialogs.test_email_ui import Ui_Dialog as TE_Dialog
|
||||||
|
from calibre.gui2 import error_dialog, info_dialog
|
||||||
|
|
||||||
|
class TestEmail(QDialog, TE_Dialog):
|
||||||
|
|
||||||
|
def __init__(self, pa, parent):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
TE_Dialog.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
opts = smtp_prefs().parse()
|
||||||
|
self.test_func = parent.test_email_settings
|
||||||
|
self.connect(self.test_button, SIGNAL('clicked(bool)'), self.test)
|
||||||
|
self.from_.setText(unicode(self.from_.text())%opts.from_)
|
||||||
|
if pa:
|
||||||
|
self.to.setText(pa)
|
||||||
|
if opts.relay_host:
|
||||||
|
self.label.setText(_('Using: %s:%s@%s:%s and %s encryption')%
|
||||||
|
(opts.relay_username, unhexlify(opts.relay_password),
|
||||||
|
opts.relay_host, opts.relay_port, opts.encryption))
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
self.log.setPlainText(_('Sending...'))
|
||||||
|
self.test_button.setEnabled(False)
|
||||||
|
try:
|
||||||
|
tb = self.test_func(unicode(self.to.text()))
|
||||||
|
if not tb:
|
||||||
|
tb = _('Mail successfully sent')
|
||||||
|
self.log.setPlainText(tb)
|
||||||
|
finally:
|
||||||
|
self.test_button.setEnabled(True)
|
||||||
|
|
||||||
|
|
||||||
|
class SendEmail(QWidget, Ui_Form):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
def initialize(self, preferred_to_address):
|
||||||
|
self.preferred_to_address = preferred_to_address
|
||||||
|
opts = smtp_prefs().parse()
|
||||||
|
self.smtp_opts = opts
|
||||||
|
if opts.from_:
|
||||||
|
self.email_from.setText(opts.from_)
|
||||||
|
if opts.relay_host:
|
||||||
|
self.relay_host.setText(opts.relay_host)
|
||||||
|
self.relay_port.setValue(opts.relay_port)
|
||||||
|
if opts.relay_username:
|
||||||
|
self.relay_username.setText(opts.relay_username)
|
||||||
|
if opts.relay_password:
|
||||||
|
self.relay_password.setText(unhexlify(opts.relay_password))
|
||||||
|
(self.relay_tls if opts.encryption == 'TLS' else self.relay_ssl).setChecked(True)
|
||||||
|
self.connect(self.relay_use_gmail, SIGNAL('clicked(bool)'),
|
||||||
|
self.create_gmail_relay)
|
||||||
|
self.connect(self.relay_show_password, SIGNAL('stateChanged(int)'),
|
||||||
|
lambda
|
||||||
|
state:self.relay_password.setEchoMode(self.relay_password.Password if
|
||||||
|
state == 0 else self.relay_password.Normal))
|
||||||
|
self.connect(self.test_email_button, SIGNAL('clicked(bool)'),
|
||||||
|
self.test_email)
|
||||||
|
|
||||||
|
|
||||||
|
def test_email(self, *args):
|
||||||
|
pa = self.preferred_to_address()
|
||||||
|
to_set = pa is not None
|
||||||
|
if self.set_email_settings(to_set):
|
||||||
|
TestEmail(pa, self).exec_()
|
||||||
|
|
||||||
|
def test_email_settings(self, to):
|
||||||
|
opts = smtp_prefs().parse()
|
||||||
|
from calibre.utils.smtp import sendmail, create_mail
|
||||||
|
buf = cStringIO.StringIO()
|
||||||
|
oout, oerr = sys.stdout, sys.stderr
|
||||||
|
sys.stdout = sys.stderr = buf
|
||||||
|
tb = None
|
||||||
|
try:
|
||||||
|
msg = create_mail(opts.from_, to, 'Test mail from calibre',
|
||||||
|
'Test mail from calibre')
|
||||||
|
sendmail(msg, from_=opts.from_, to=[to],
|
||||||
|
verbose=3, timeout=30, relay=opts.relay_host,
|
||||||
|
username=opts.relay_username,
|
||||||
|
password=unhexlify(opts.relay_password),
|
||||||
|
encryption=opts.encryption, port=opts.relay_port)
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
tb = traceback.format_exc()
|
||||||
|
tb += '\n\nLog:\n' + buf.getvalue()
|
||||||
|
finally:
|
||||||
|
sys.stdout, sys.stderr = oout, oerr
|
||||||
|
return tb
|
||||||
|
|
||||||
|
def create_gmail_relay(self, *args):
|
||||||
|
self.relay_username.setText('@gmail.com')
|
||||||
|
self.relay_password.setText('')
|
||||||
|
self.relay_host.setText('smtp.gmail.com')
|
||||||
|
self.relay_port.setValue(587)
|
||||||
|
self.relay_tls.setChecked(True)
|
||||||
|
|
||||||
|
info_dialog(self, _('Finish gmail setup'),
|
||||||
|
_('Dont forget to enter your gmail username and password')).exec_()
|
||||||
|
self.relay_username.setFocus(Qt.OtherFocusReason)
|
||||||
|
self.relay_username.setCursorPosition(0)
|
||||||
|
|
||||||
|
def set_email_settings(self, to_set):
|
||||||
|
from_ = unicode(self.email_from.text()).strip()
|
||||||
|
if to_set and not from_:
|
||||||
|
error_dialog(self, _('Bad configuration'),
|
||||||
|
_('You must set the From email address')).exec_()
|
||||||
|
return False
|
||||||
|
username = unicode(self.relay_username.text()).strip()
|
||||||
|
password = unicode(self.relay_password.text()).strip()
|
||||||
|
host = unicode(self.relay_host.text()).strip()
|
||||||
|
if host and not (username and password):
|
||||||
|
error_dialog(self, _('Bad configuration'),
|
||||||
|
_('You must set the username and password for '
|
||||||
|
'the mail server.')).exec_()
|
||||||
|
return False
|
||||||
|
conf = smtp_prefs()
|
||||||
|
conf.set('from_', from_)
|
||||||
|
conf.set('relay_host', host if host else None)
|
||||||
|
conf.set('relay_port', self.relay_port.value())
|
||||||
|
conf.set('relay_username', username if username else None)
|
||||||
|
conf.set('relay_password', hexlify(password))
|
||||||
|
conf.set('encryption', 'TLS' if self.relay_tls.isChecked() else 'SSL')
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
234
src/calibre/gui2/wizard/send_email.ui
Normal file
234
src/calibre/gui2/wizard/send_email.ui
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Form</class>
|
||||||
|
<widget class="QWidget" name="Form">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>585</width>
|
||||||
|
<height>238</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_15">
|
||||||
|
<property name="text">
|
||||||
|
<string>Send email &from:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>email_from</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="email_from">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><p>This is what will be present in the From: field of emails sent by calibre.<br> Set it to your email address</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QGroupBox" name="groupBox_5">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><p>A mail server is useful if the service you are sending mail to only accepts email from well know mail services.</string>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Mail &Server</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="0" column="0" colspan="4">
|
||||||
|
<widget class="QLabel" name="label_16">
|
||||||
|
<property name="text">
|
||||||
|
<string>calibre can <b>optionally</b> use a server to send mail</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_17">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Hostname:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>relay_host</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="relay_host">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>The hostname of your mail server. For e.g. smtp.gmail.com</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="3">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_11">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_18">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Port:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>relay_port</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="relay_port">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>The port your mail server listens for connections on. The default is 25</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65555</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>25</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_19">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Username:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>relay_username</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="relay_username">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Your username on the mail server</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_20">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Password:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>relay_password</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="relay_password">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Your password on the mail server</string>
|
||||||
|
</property>
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="3">
|
||||||
|
<widget class="QCheckBox" name="relay_show_password">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Show</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_21">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Encryption:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>relay_tls</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QRadioButton" name="relay_tls">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Use TLS encryption when connecting to the mail server. This is the most common.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&TLS</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="2" colspan="2">
|
||||||
|
<widget class="QRadioButton" name="relay_ssl">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Use SSL encryption when connecting to the mail server.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&SSL</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="4">
|
||||||
|
<spacer name="horizontalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="relay_use_gmail">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use Gmail</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../images.qrc">
|
||||||
|
<normaloff>:/images/gmail_logo.png</normaloff>:/images/gmail_logo.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>48</width>
|
||||||
|
<height>48</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="test_email_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Test email</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../images.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
97
src/calibre/gui2/wizard/stanza.ui
Normal file
97
src/calibre/gui2/wizard/stanza.ui
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>WizardPage</class>
|
||||||
|
<widget class="QWizardPage" name="WizardPage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Welcome to calibre</string>
|
||||||
|
</property>
|
||||||
|
<property name="subTitle">
|
||||||
|
<string>The one stop solution to all your e-book needs.</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string><p>If you use the <a href="http://www.lexcycle.com/download">Stanza</a> e-book app on your iPhone/iTouch, you can access your calibre book collection directly on the device. To do this you have to turn on the calibre content server.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="content_server">
|
||||||
|
<property name="text">
|
||||||
|
<string>Turn on the &content server</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="instructions">
|
||||||
|
<property name="text">
|
||||||
|
<string><p>Remember to leave calibre running as the server only runs as long as calibre is running.
|
||||||
|
<p>Stanza should see your calibre collection automatically. If not, try adding the URL http://myhostname:8080 as a new catalog in the Stanza reader on your iPhone. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -1282,21 +1282,12 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
if notify:
|
if notify:
|
||||||
self.notify('add', [id])
|
self.notify('add', [id])
|
||||||
|
|
||||||
def move_library_to(self, newloc, progress=None):
|
def move_library_to(self, newloc, progress=lambda x: x):
|
||||||
header = _(u'<p>Copying books to %s<br><center>')%newloc
|
|
||||||
books = self.conn.get('SELECT id, path, title FROM books')
|
books = self.conn.get('SELECT id, path, title FROM books')
|
||||||
if progress is not None:
|
|
||||||
progress.setValue(0)
|
|
||||||
progress.setLabelText(header)
|
|
||||||
QCoreApplication.processEvents()
|
|
||||||
progress.setAutoReset(False)
|
|
||||||
progress.setRange(0, len(books))
|
|
||||||
if not os.path.exists(newloc):
|
if not os.path.exists(newloc):
|
||||||
os.makedirs(newloc)
|
os.makedirs(newloc)
|
||||||
old_dirs = set([])
|
old_dirs = set([])
|
||||||
for i, book in enumerate(books):
|
for i, book in enumerate(books):
|
||||||
if progress is not None:
|
|
||||||
progress.setLabelText(header+_(u'Copying <b>%s</b>')%book[2])
|
|
||||||
path = book[1]
|
path = book[1]
|
||||||
if not path:
|
if not path:
|
||||||
continue
|
continue
|
||||||
@ -1308,8 +1299,7 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
if os.path.exists(srcdir):
|
if os.path.exists(srcdir):
|
||||||
shutil.copytree(srcdir, tdir)
|
shutil.copytree(srcdir, tdir)
|
||||||
old_dirs.add(srcdir)
|
old_dirs.add(srcdir)
|
||||||
if progress is not None:
|
progress(book[2])
|
||||||
progress.setValue(i+1)
|
|
||||||
|
|
||||||
dbpath = os.path.join(newloc, os.path.basename(self.dbpath))
|
dbpath = os.path.join(newloc, os.path.basename(self.dbpath))
|
||||||
shutil.copyfile(self.dbpath, dbpath)
|
shutil.copyfile(self.dbpath, dbpath)
|
||||||
@ -1323,10 +1313,6 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
shutil.rmtree(dir)
|
shutil.rmtree(dir)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
if progress is not None:
|
|
||||||
progress.reset()
|
|
||||||
progress.hide()
|
|
||||||
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for record in self.data._data:
|
for record in self.data._data:
|
||||||
|
63
src/calibre/library/move.py
Normal file
63
src/calibre/library/move.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import time, os
|
||||||
|
from threading import Thread
|
||||||
|
from Queue import Empty
|
||||||
|
|
||||||
|
from calibre.library.database2 import LibraryDatabase2
|
||||||
|
from calibre.utils.ipc.server import Server
|
||||||
|
from calibre.utils.ipc.job import ParallelJob
|
||||||
|
|
||||||
|
|
||||||
|
def move_library(from_, to, notification = lambda x:x):
|
||||||
|
time.sleep(1)
|
||||||
|
old = LibraryDatabase2(from_)
|
||||||
|
old.move_library_to(to, notification)
|
||||||
|
return True
|
||||||
|
|
||||||
|
class MoveLibrary(Thread):
|
||||||
|
|
||||||
|
def __init__(self, from_, to, count, result_queue):
|
||||||
|
Thread.__init__(self)
|
||||||
|
self.total = count
|
||||||
|
self.result_queue = result_queue
|
||||||
|
self.from_ = from_
|
||||||
|
self.to = to
|
||||||
|
self.count = 0
|
||||||
|
self.failed = False
|
||||||
|
self.details = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
job = ParallelJob('move_library',
|
||||||
|
'Move library from %s to %s'%(self.from_, self.to),
|
||||||
|
lambda x,y:x,
|
||||||
|
args=[self.from_, self.to])
|
||||||
|
server = Server(pool_size=1)
|
||||||
|
server.add_job(job)
|
||||||
|
|
||||||
|
while not job.is_finished:
|
||||||
|
time.sleep(0.2)
|
||||||
|
job.update(consume_notifications=False)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
title = job.notifications.get_nowait()[0]
|
||||||
|
self.count += 1
|
||||||
|
self.result_queue.put((float(self.count)/self.total, title))
|
||||||
|
except Empty:
|
||||||
|
break
|
||||||
|
|
||||||
|
job.update()
|
||||||
|
server.close()
|
||||||
|
if not job.result:
|
||||||
|
self.failed = True
|
||||||
|
self.details = job.details
|
||||||
|
|
||||||
|
if os.path.exists(job.log_path):
|
||||||
|
os.remove(job.log_path)
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
@ -9,8 +9,8 @@ DEPENDENCIES = [
|
|||||||
('setuptools', '0.6c5', 'setuptools', 'python-setuptools', 'python-setuptools-devel'),
|
('setuptools', '0.6c5', 'setuptools', 'python-setuptools', 'python-setuptools-devel'),
|
||||||
('Python Imaging Library', '1.1.6', 'imaging', 'python-imaging', 'python-imaging'),
|
('Python Imaging Library', '1.1.6', 'imaging', 'python-imaging', 'python-imaging'),
|
||||||
('libusb', '0.1.12', None, None, None),
|
('libusb', '0.1.12', None, None, None),
|
||||||
('Qt', '4.4.0', 'qt', 'libqt4-core libqt4-gui', 'qt4'),
|
('Qt', '4.5.0', 'qt', 'libqt4-core libqt4-gui', 'qt4'),
|
||||||
('PyQt', '4.4.2', 'PyQt4', 'python-qt4', 'PyQt4'),
|
('PyQt', '4.5.0', 'PyQt4', 'python-qt4', 'PyQt4'),
|
||||||
('python-mechanize', '0.1.11', 'dev-python/mechanize', 'python-mechanize', 'python-mechanize'),
|
('python-mechanize', '0.1.11', 'dev-python/mechanize', 'python-mechanize', 'python-mechanize'),
|
||||||
('ImageMagick', '6.3.5', 'imagemagick', 'imagemagick', 'ImageMagick'),
|
('ImageMagick', '6.3.5', 'imagemagick', 'imagemagick', 'ImageMagick'),
|
||||||
('xdg-utils', '1.0.2', 'xdg-utils', 'xdg-utils', 'xdg-utils'),
|
('xdg-utils', '1.0.2', 'xdg-utils', 'xdg-utils', 'xdg-utils'),
|
||||||
|
@ -27,6 +27,9 @@ PARALLEL_FUNCS = {
|
|||||||
'gui_convert' :
|
'gui_convert' :
|
||||||
('calibre.gui2.convert.gui_conversion', 'gui_convert', 'notification'),
|
('calibre.gui2.convert.gui_conversion', 'gui_convert', 'notification'),
|
||||||
|
|
||||||
|
'move_library' :
|
||||||
|
('calibre.library.move', 'move_library', 'notification'),
|
||||||
|
|
||||||
'read_metadata' :
|
'read_metadata' :
|
||||||
('calibre.ebooks.metadata.worker', 'read_metadata_', 'notification'),
|
('calibre.ebooks.metadata.worker', 'read_metadata_', 'notification'),
|
||||||
|
|
||||||
@ -36,6 +39,8 @@ PARALLEL_FUNCS = {
|
|||||||
'write_pdf_metadata' :
|
'write_pdf_metadata' :
|
||||||
('calibre.utils.podofo.__init__', 'set_metadata_', None),
|
('calibre.utils.podofo.__init__', 'set_metadata_', None),
|
||||||
|
|
||||||
|
'write_pdf_first_page' :
|
||||||
|
('calibre.utils.podofo.__init__', 'write_first_page_', None),
|
||||||
|
|
||||||
'save_book' :
|
'save_book' :
|
||||||
('calibre.ebooks.metadata.worker', 'save_book', 'notification'),
|
('calibre.ebooks.metadata.worker', 'save_book', 'notification'),
|
||||||
|
@ -398,7 +398,7 @@ class RecursiveFetcher(object):
|
|||||||
len(re.compile('<!--.*?-->', re.DOTALL).sub('', dsrc).strip()) == 0:
|
len(re.compile('<!--.*?-->', re.DOTALL).sub('', dsrc).strip()) == 0:
|
||||||
raise ValueError('No content at URL %s'%iurl)
|
raise ValueError('No content at URL %s'%iurl)
|
||||||
if self.encoding is not None:
|
if self.encoding is not None:
|
||||||
dsrc = dsrc.decode(self.encoding, 'ignore')
|
dsrc = dsrc.decode(self.encoding, 'replace')
|
||||||
else:
|
else:
|
||||||
dsrc = xml_to_unicode(dsrc, self.verbose)[0]
|
dsrc = xml_to_unicode(dsrc, self.verbose)[0]
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user