Welcome wizard

This commit is contained in:
Kovid Goyal 2009-05-29 00:11:59 -07:00
parent 496e0dccd0
commit 8072415891
20 changed files with 1466 additions and 392 deletions

View File

@ -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())

View File

@ -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']
)

View File

@ -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)

View File

@ -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 &amp;from:</string>
</property>
<property name="buddy">
<cstring>email_from</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="email_from">
<property name="toolTip">
<string>&lt;p&gt;This is what will be present in the From: field of emails sent by calibre.&lt;br&gt; 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>&lt;p&gt;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 &amp;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 &lt;b&gt;optionally&lt;/b&gt; 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>&amp;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>&amp;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>&amp;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>&amp;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>&amp;Show</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>&amp;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>&amp;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>&amp;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>&amp;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>&lt;p&gt;Remember to leave calibre running as the server only runs as long as calibre is running.
&lt;p&gt;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>

View File

@ -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

View 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_()

View 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>&amp;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>&amp;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>

View 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>&lt;h2&gt;Congratulations!&lt;/h2&gt; 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>&lt;h2&gt;Demo videos&lt;/h2&gt;Videos demonstrating the various features of calibre are available &lt;a href=&quot;http://calibre.kovidgoyal.net/downloads/videos/&quot;&gt;online&lt;/a&gt;.</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>&lt;h2&gt;User Manual&lt;/h2&gt;A User Manual is also available &lt;a href=&quot;http://calibre.kovidgoyal.net/user_manual&quot;&gt;online&lt;/a&gt;.</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>

View 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>&lt;p&gt;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 &lt;a href=&quot;http://gmail.com&quot;&gt;gmail account&lt;/a&gt; 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>&amp;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>

View 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>&amp;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>

View 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

View 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 &amp;from:</string>
</property>
<property name="buddy">
<cstring>email_from</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="email_from">
<property name="toolTip">
<string>&lt;p&gt;This is what will be present in the From: field of emails sent by calibre.&lt;br&gt; 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>&lt;p&gt;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 &amp;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 &lt;b&gt;optionally&lt;/b&gt; 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>&amp;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>&amp;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>&amp;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>&amp;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>&amp;Show</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>&amp;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>&amp;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>&amp;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>&amp;Test email</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../images.qrc"/>
</resources>
<connections/>
</ui>

View 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>&lt;p&gt;If you use the &lt;a href=&quot;http://www.lexcycle.com/download&quot;&gt;Stanza&lt;/a&gt; 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 &amp;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>&lt;p&gt;Remember to leave calibre running as the server only runs as long as calibre is running.
&lt;p&gt;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>

View File

@ -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:

View 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

View File

@ -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'),

View File

@ -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'),

View File

@ -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]

2
todo
View File

@ -5,7 +5,5 @@
* Testing framework
* Welcome wizard
* MOBI navigation indexing support