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.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):
|
||||
if not isinstance(path, unicode):
|
||||
path = path.decode(sys.getfilesystemencoding())
|
||||
|
@ -35,7 +35,7 @@ class PageSetupWidget(Widget, Ui_Form):
|
||||
TITLE = _('Page Setup')
|
||||
|
||||
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',
|
||||
'input_profile', 'output_profile']
|
||||
)
|
||||
|
@ -1,7 +1,6 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import os, re, time, textwrap, sys, cStringIO
|
||||
from binascii import hexlify, unhexlify
|
||||
import os, re, time, textwrap
|
||||
|
||||
from PyQt4.Qt import QDialog, QMessageBox, QListWidgetItem, QIcon, \
|
||||
QDesktopServices, QVBoxLayout, QLabel, QPlainTextEdit, \
|
||||
@ -12,7 +11,6 @@ from PyQt4.Qt import QDialog, QMessageBox, QListWidgetItem, QIcon, \
|
||||
|
||||
from calibre.constants import islinux, iswindows
|
||||
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, \
|
||||
ALL_COLUMNS, NONE, info_dialog, choose_files
|
||||
from calibre.utils.config import prefs
|
||||
@ -202,34 +200,6 @@ class CategoryModel(QStringListModel):
|
||||
return self.icons[index.row()]
|
||||
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):
|
||||
|
||||
def __init__(self, accounts):
|
||||
@ -477,32 +447,19 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
self.stackedWidget.insertWidget(2, self.conversion_options)
|
||||
|
||||
def setup_email_page(self):
|
||||
opts = smtp_prefs().parse()
|
||||
if opts.from_:
|
||||
self.email_from.setText(opts.from_)
|
||||
def x():
|
||||
if self._email_accounts.account_order:
|
||||
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_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.add_email_account)
|
||||
self.connect(self.email_make_default, SIGNAL('clicked(bool)'),
|
||||
lambda c: self._email_accounts.make_default(self.email_view.currentIndex()))
|
||||
self.email_view.resizeColumnsToContents()
|
||||
self.connect(self.test_email_button, SIGNAL('clicked(bool)'),
|
||||
self.test_email)
|
||||
self.connect(self.email_remove, SIGNAL('clicked()'),
|
||||
self.remove_email_account)
|
||||
|
||||
@ -516,68 +473,14 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
idx = self.email_view.currentIndex()
|
||||
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):
|
||||
from_ = unicode(self.email_from.text()).strip()
|
||||
if self._email_accounts.accounts 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_()
|
||||
to_set = bool(self._email_accounts.accounts)
|
||||
if not self.send_email_widget.set_email_settings(to_set):
|
||||
return False
|
||||
conf = smtp_prefs()
|
||||
conf.set('from_', from_)
|
||||
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
|
||||
|
||||
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):
|
||||
path = unicode(self.plugin_path.text())
|
||||
@ -722,7 +625,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
|
||||
def browse(self):
|
||||
dir = choose_dir(self, 'database location dialog',
|
||||
_('Select database location'))
|
||||
_('Select location for books'))
|
||||
if dir:
|
||||
self.location.setText(dir)
|
||||
|
||||
|
@ -541,38 +541,7 @@
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_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">
|
||||
<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">
|
||||
<item>
|
||||
<widget class="QTableView" name="email_view">
|
||||
@ -640,195 +609,18 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" 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>
|
||||
<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="title">
|
||||
<string>Mail &Server</string>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</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="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 row="2" column="0" colspan="2">
|
||||
<widget class="SendEmail" name="send_email_widget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -1062,7 +854,8 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_13">
|
||||
<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 name="wordWrap">
|
||||
<bool>true</bool>
|
||||
@ -1216,6 +1009,14 @@
|
||||
</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>
|
||||
<include location="../images.qrc"/>
|
||||
</resources>
|
||||
|
@ -11,11 +11,11 @@ from PyQt4.Qt import Qt, SIGNAL, QObject, QCoreApplication, QUrl, QTimer, \
|
||||
QModelIndex, QPixmap, QColor, QPainter, QMenu, QIcon, \
|
||||
QToolButton, QDialog, QDesktopServices, QFileDialog, \
|
||||
QSystemTrayIcon, QApplication, QKeySequence, QAction, \
|
||||
QProgressDialog, QMessageBox, QStackedLayout
|
||||
QMessageBox, QStackedLayout
|
||||
from PyQt4.QtSvg import QSvgRenderer
|
||||
|
||||
from calibre import __version__, __appname__, sanitize_file_name, \
|
||||
iswindows, isosx, prints
|
||||
iswindows, isosx, prints, patheq
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.utils.config import prefs, dynamic
|
||||
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
|
||||
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
|
||||
from calibre.gui2.widgets import ProgressIndicator
|
||||
from calibre.gui2.wizard import move_library
|
||||
from calibre.gui2.dialogs.scheduler import Scheduler
|
||||
from calibre.gui2.update import CheckForUpdates
|
||||
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,
|
||||
SIGNAL('triggered(bool)'), self.convert_single)
|
||||
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).\
|
||||
setPopupMode(QToolButton.MenuButtonPopup)
|
||||
self.tool_bar.widgetForAction(self.action_edit).\
|
||||
@ -311,6 +320,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
setPopupMode(QToolButton.MenuButtonPopup)
|
||||
self.tool_bar.widgetForAction(self.action_view).\
|
||||
setPopupMode(QToolButton.MenuButtonPopup)
|
||||
self.tool_bar.widgetForAction(self.action_preferences).\
|
||||
setPopupMode(QToolButton.MenuButtonPopup)
|
||||
self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
|
||||
|
||||
self.connect(self.preferences_action, SIGNAL('triggered(bool)'),
|
||||
@ -1376,56 +1387,27 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
self.save_menu.actions()[2].setText(
|
||||
_('Save only %s format to disk')%
|
||||
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'):
|
||||
set_sidebar_directories(d.directories)
|
||||
self.library_view.model().read_config()
|
||||
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 #################################
|
||||
@ -1652,6 +1634,17 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
self.hide()
|
||||
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):
|
||||
self.write_settings()
|
||||
@ -1726,12 +1719,19 @@ def init_qt(args):
|
||||
|
||||
def run_gui(opts, args, actions, listener, app):
|
||||
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)
|
||||
sys.excepthook = main.unhandled_exception
|
||||
if len(args) > 1:
|
||||
args[1] = os.path.abspath(args[1])
|
||||
main.add_filesystem_book(args[1])
|
||||
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):
|
||||
e = sys.executable if getattr(sys, 'froze', False) else sys.argv[0]
|
||||
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:
|
||||
self.notify('add', [id])
|
||||
|
||||
def move_library_to(self, newloc, progress=None):
|
||||
header = _(u'<p>Copying books to %s<br><center>')%newloc
|
||||
def move_library_to(self, newloc, progress=lambda x: x):
|
||||
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):
|
||||
os.makedirs(newloc)
|
||||
old_dirs = set([])
|
||||
for i, book in enumerate(books):
|
||||
if progress is not None:
|
||||
progress.setLabelText(header+_(u'Copying <b>%s</b>')%book[2])
|
||||
path = book[1]
|
||||
if not path:
|
||||
continue
|
||||
@ -1308,8 +1299,7 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
if os.path.exists(srcdir):
|
||||
shutil.copytree(srcdir, tdir)
|
||||
old_dirs.add(srcdir)
|
||||
if progress is not None:
|
||||
progress.setValue(i+1)
|
||||
progress(book[2])
|
||||
|
||||
dbpath = os.path.join(newloc, os.path.basename(self.dbpath))
|
||||
shutil.copyfile(self.dbpath, dbpath)
|
||||
@ -1323,10 +1313,6 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
shutil.rmtree(dir)
|
||||
except:
|
||||
pass
|
||||
if progress is not None:
|
||||
progress.reset()
|
||||
progress.hide()
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
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'),
|
||||
('Python Imaging Library', '1.1.6', 'imaging', 'python-imaging', 'python-imaging'),
|
||||
('libusb', '0.1.12', None, None, None),
|
||||
('Qt', '4.4.0', 'qt', 'libqt4-core libqt4-gui', 'qt4'),
|
||||
('PyQt', '4.4.2', 'PyQt4', 'python-qt4', 'PyQt4'),
|
||||
('Qt', '4.5.0', 'qt', 'libqt4-core libqt4-gui', 'qt4'),
|
||||
('PyQt', '4.5.0', 'PyQt4', 'python-qt4', 'PyQt4'),
|
||||
('python-mechanize', '0.1.11', 'dev-python/mechanize', 'python-mechanize', 'python-mechanize'),
|
||||
('ImageMagick', '6.3.5', 'imagemagick', 'imagemagick', 'ImageMagick'),
|
||||
('xdg-utils', '1.0.2', 'xdg-utils', 'xdg-utils', 'xdg-utils'),
|
||||
|
@ -27,6 +27,9 @@ PARALLEL_FUNCS = {
|
||||
'gui_convert' :
|
||||
('calibre.gui2.convert.gui_conversion', 'gui_convert', 'notification'),
|
||||
|
||||
'move_library' :
|
||||
('calibre.library.move', 'move_library', 'notification'),
|
||||
|
||||
'read_metadata' :
|
||||
('calibre.ebooks.metadata.worker', 'read_metadata_', 'notification'),
|
||||
|
||||
@ -36,6 +39,8 @@ PARALLEL_FUNCS = {
|
||||
'write_pdf_metadata' :
|
||||
('calibre.utils.podofo.__init__', 'set_metadata_', None),
|
||||
|
||||
'write_pdf_first_page' :
|
||||
('calibre.utils.podofo.__init__', 'write_first_page_', None),
|
||||
|
||||
'save_book' :
|
||||
('calibre.ebooks.metadata.worker', 'save_book', 'notification'),
|
||||
|
@ -398,7 +398,7 @@ class RecursiveFetcher(object):
|
||||
len(re.compile('<!--.*?-->', re.DOTALL).sub('', dsrc).strip()) == 0:
|
||||
raise ValueError('No content at URL %s'%iurl)
|
||||
if self.encoding is not None:
|
||||
dsrc = dsrc.decode(self.encoding, 'ignore')
|
||||
dsrc = dsrc.decode(self.encoding, 'replace')
|
||||
else:
|
||||
dsrc = xml_to_unicode(dsrc, self.verbose)[0]
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user