diff --git a/recipes/icons/replicavedetelor.png b/recipes/icons/replicavedetelor.png new file mode 100644 index 0000000000..57ba42c64c Binary files /dev/null and b/recipes/icons/replicavedetelor.png differ diff --git a/recipes/replicavedetelor.recipe b/recipes/replicavedetelor.recipe new file mode 100644 index 0000000000..b6f1a51d67 --- /dev/null +++ b/recipes/replicavedetelor.recipe @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = u'2011, ' +''' +replicavedetelor.ro +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class ReplicaVedetelor(BasicNewsRecipe): + title = u'Replica Vedetelor' + __author__ = u'Silviu Cotoara' + description = u'Ofer\u0103 vedetelor dreptul la replic\u0103' + publisher = 'Replica Vedetelor' + oldest_article = 5 + language = 'ro' + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + category = 'Ziare,Reviste,Vedete' + encoding = 'utf-8' + cover_url = 'http://www.webart-software.eu/_pics/lucrari_referinta/medium/84/1-Replica-Vedetelor.jpg' + + conversion_options = { + 'comments' : description + ,'tags' : category + ,'language' : language + ,'publisher' : publisher + } + + keep_only_tags = [ + dict(name='div', attrs={'id':'zona-continut'}) + ] + + remove_tags = [ + dict(name='ul', attrs={'id':['lista-imagini']}) + , dict(name='form', attrs={'id':['f-trimite-unui-prieten']}) + + ] + + remove_tags_after = [ + dict(name='form', attrs={'id':['f-trimite-unui-prieten']}) + ] + + feeds = [ + (u'Feeds', u'http://www.replicavedetelor.ro/feed') + ] + + def preprocess_html(self, soup): + return self.adeify_images(soup) + diff --git a/recipes/welt_der_physik.recipe b/recipes/welt_der_physik.recipe new file mode 100644 index 0000000000..b3ce9cce1b --- /dev/null +++ b/recipes/welt_der_physik.recipe @@ -0,0 +1,20 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1303841067(BasicNewsRecipe): + + title = u'Welt der Physik' + __author__ = 'schuster' + remove_tags_befor = [dict(name='div', attrs={'class':'inhalt_bild_text_printonly'})] + remove_tags_after = [dict(name='span', attrs={'class':'clearinhalt_bild'})] + remove_tags = [dict(attrs={'class':['invisible', 'searchfld', 'searchbtn', 'topnavi', 'topsearch']}), + dict(id=['naservice', 'phservicemenu', '',]), + dict(name=['naservice'])] + oldest_article = 7 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + language = 'de' + remove_javascript = True + + + feeds = [(u'Nachrichten und Neuigkeiten', u'http://www.weltderphysik.de/rss/alles.xml')] diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index c1da8391e0..1cfc74c4d3 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1096,6 +1096,11 @@ class StoreAmazonKindleStore(StoreBase): description = _('Kindle books from Amazon') actual_plugin = 'calibre.gui2.store.amazon_plugin:AmazonKindleStore' +class StoreAmazonDEKindleStore(StoreBase): + name = 'Amazon DE Kindle' + description = _('Kindle eBooks') + actual_plugin = 'calibre.gui2.store.amazon_de_plugin:AmazonDEKindleStore' + class StoreAmazonUKKindleStore(StoreBase): name = 'Amazon UK Kindle' description = _('Kindle books from Amazon.uk') @@ -1111,6 +1116,11 @@ class StoreBNStore(StoreBase): description = _('Books, Textbooks, eBooks, Toys, Games and More.') actual_plugin = 'calibre.gui2.store.bn_plugin:BNStore' +class StoreBeamEBooksDEStore(StoreBase): + name = 'Beam EBooks DE' + description = _('der eBook Shop') + actual_plugin = 'calibre.gui2.store.beam_ebooks_de_plugin:BeamEBooksDEStore' + class StoreBeWriteStore(StoreBase): name = 'BeWrite Books' description = _('Publishers of fine books.') @@ -1126,7 +1136,12 @@ class StoreEbookscomStore(StoreBase): description = _('The digital bookstore.') actual_plugin = 'calibre.gui2.store.ebooks_com_plugin:EbookscomStore' -class StoreEHarlequinStoretore(StoreBase): +class StoreEPubBuyDEStore(StoreBase): + name = 'EPUBBuy DE' + description = _('EPUBReaders eBook Shop') + actual_plugin = 'calibre.gui2.store.epubbuy_de_plugin:EPubBuyDEStore' + +class StoreEHarlequinStore(StoreBase): name = 'eHarlequin' description = _('entertain, enrich, inspire.') actual_plugin = 'calibre.gui2.store.eharlequin_plugin:EHarlequinStore' @@ -1136,6 +1151,11 @@ class StoreFeedbooksStore(StoreBase): description = _('Read anywhere.') actual_plugin = 'calibre.gui2.store.feedbooks_plugin:FeedbooksStore' +class StoreFoylesUKStore(StoreBase): + name = 'Foyles UK' + description = _('Foyles of London, online') + actual_plugin = 'calibre.gui2.store.foyles_uk_plugin:FoylesUKStore' + class StoreGutenbergStore(StoreBase): name = 'Project Gutenberg' description = _('The first producer of free ebooks.') @@ -1171,22 +1191,23 @@ class StoreWaterstonesUKStore(StoreBase): description = _('Feel every word') actual_plugin = 'calibre.gui2.store.waterstones_uk_plugin:WaterstonesUKStore' -class StoreFoylesUKStore(StoreBase): - name = 'Foyles UK' - description = _('Foyles of London, online') - actual_plugin = 'calibre.gui2.store.foyles_uk_plugin:FoylesUKStore' +class StoreWeightlessBooksStore(StoreBase): + name = 'Weightless Books' + description = '(e)Books That Don\'t Weigh You Down' + actual_plugin = 'calibre.gui2.store.weightless_books_plugin:WeightlessBooksStore' -class AmazonDEKindleStore(StoreBase): - name = 'Amazon DE Kindle' - description = _('Kindle eBooks') - actual_plugin = 'calibre.gui2.store.amazon_de_plugin:AmazonDEKindleStore' +class StoreWizardsTowerBooksStore(StoreBase): + name = 'Wizards Tower Books' + description = 'Wizard\'s Tower Press' + actual_plugin = 'calibre.gui2.store.wizards_tower_books_plugin:WizardsTowerBooksStore' -plugins += [StoreAmazonKindleStore, AmazonDEKindleStore, StoreAmazonUKKindleStore, +plugins += [StoreAmazonKindleStore, StoreAmazonDEKindleStore, StoreAmazonUKKindleStore, StoreBaenWebScriptionStore, StoreBNStore, - StoreBeWriteStore, StoreDieselEbooksStore, StoreEbookscomStore, - StoreEHarlequinStoretore, StoreFeedbooksStore, + StoreBeamEBooksDEStore, StoreBeWriteStore, + StoreDieselEbooksStore, StoreEbookscomStore, StoreEPubBuyDEStore, + StoreEHarlequinStore, StoreFeedbooksStore, StoreFoylesUKStore, StoreGutenbergStore, StoreKoboStore, StoreManyBooksStore, StoreMobileReadStore, StoreOpenLibraryStore, StoreSmashwordsStore, - StoreWaterstonesUKStore] + StoreWaterstonesUKStore, StoreWeightlessBooksStore, StoreWizardsTowerBooksStore] # }}} diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 3a0254ef20..f5c9a74733 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -38,7 +38,7 @@ class KOBO(USBMS): VENDOR_ID = [0x2237] PRODUCT_ID = [0x4161] - BCD = [0x0110] + BCD = [0x0110, 0x0323] VENDOR_NAME = ['KOBO_INC', 'KOBO'] WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['.KOBOEREADER', 'EREADER'] diff --git a/src/calibre/gui2/actions/store.py b/src/calibre/gui2/actions/store.py index 90f2cac3cd..1989250bc8 100644 --- a/src/calibre/gui2/actions/store.py +++ b/src/calibre/gui2/actions/store.py @@ -27,7 +27,7 @@ class StoreAction(InterfaceAction): self.store_menu.clear() self.store_menu.addAction(_('Search'), self.search) self.store_menu.addSeparator() - for n, p in self.gui.istores.items(): + for n, p in sorted(self.gui.istores.items(), key=lambda x: x[0].lower()): self.store_menu.addAction(n, partial(self.open_store, p)) self.qaction.setMenu(self.store_menu) diff --git a/src/calibre/gui2/store/__init__.py b/src/calibre/gui2/store/__init__.py index 214ede3372..d58ccbda84 100644 --- a/src/calibre/gui2/store/__init__.py +++ b/src/calibre/gui2/store/__init__.py @@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en' class StorePlugin(object): # {{{ ''' A plugin representing an online ebook repository (store). The store can - be a comercial store that sells ebooks or a source of free downloadable + be a commercial store that sells ebooks or a source of free downloadable ebooks. Note that this class is the base class for these plugins, however, to @@ -43,6 +43,8 @@ class StorePlugin(object): # {{{ The easiest way to handle affiliate money payouts is to randomly select between the author's affiliate id and calibre's affiliate id so that 70% of the time the author's id is used. + + See declined.txt for a list of stores that do not want to be included. ''' def __init__(self, gui, name): diff --git a/src/calibre/gui2/store/beam_ebooks_de_plugin.py b/src/calibre/gui2/store/beam_ebooks_de_plugin.py new file mode 100644 index 0000000000..b589a8c310 --- /dev/null +++ b/src/calibre/gui2/store/beam_ebooks_de_plugin.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +import urllib2 +from contextlib import closing + +from lxml import html + +from PyQt4.Qt import QUrl + +from calibre import browser +from calibre.gui2 import open_url +from calibre.gui2.store import StorePlugin +from calibre.gui2.store.basic_config import BasicStoreConfig +from calibre.gui2.store.search_result import SearchResult +from calibre.gui2.store.web_store_dialog import WebStoreDialog + +class BeamEBooksDEStore(BasicStoreConfig, StorePlugin): + + def open(self, parent=None, detail_item=None, external=False): + url = 'http://klick.affiliwelt.net/klick.php?bannerid=10072&pid=32307&prid=908' + url_details = ('http://klick.affiliwelt.net/klick.php?' + 'bannerid=10730&pid=32307&prid=908&prodid={0}') + + if external or self.config.get('open_external', False): + if detail_item: + url = url_details.format(detail_item) + open_url(QUrl(url)) + else: + detail_url = None + if detail_item: + detail_url = url_details.format(detail_item) + d = WebStoreDialog(self.gui, url, parent, detail_url) + d.setWindowTitle(self.name) + d.set_tags(self.config.get('tags', '')) + d.exec_() + + def search(self, query, max_results=10, timeout=60): + url = 'http://www.beam-ebooks.de/suchergebnis.php?Type=&sw=' + urllib2.quote(query) + br = browser() + + counter = max_results + with closing(br.open(url, timeout=timeout)) as f: + doc = html.fromstring(f.read()) + for data in doc.xpath('//table[tr/td/div[@class="stil2"]]'): + if counter <= 0: + break + + id = ''.join(data.xpath('./tr/td/div[@class="stil2"]/a/@href')).strip() + if not id: + continue + id = id[7:] + cover_url = ''.join(data.xpath('./tr/td[1]/a/img/@src')) + if cover_url: + cover_url = 'http://www.beam-ebooks.de' + cover_url + title = ''.join(data.xpath('./tr/td/div[@class="stil2"]/a/b/text()')) + author = ' '.join(data.xpath('./tr/td/div[@class="stil2"]/' + 'child::b/text()' + '|' + './tr/td/div[@class="stil2"]/' + 'child::strong/text()')) + price = ''.join(data.xpath('./tr/td[3]/text()')) + pdf = data.xpath( + 'boolean(./tr/td[3]/a/img[contains(@alt, "PDF")]/@alt)') + epub = data.xpath( + 'boolean(./tr/td[3]/a/img[contains(@alt, "ePub")]/@alt)') + mobi = data.xpath( + 'boolean(./tr/td[3]/a/img[contains(@alt, "Mobipocket")]/@alt)') + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url + s.title = title.strip() + s.author = author.strip() + s.price = price + s.drm = SearchResult.DRM_UNLOCKED + s.detail_item = id + formats = [] + if epub: + formats.append('ePub') + if pdf: + formats.append('PDF') + if mobi: + formats.append('MOBI') + s.formats = ', '.join(formats) + + yield s diff --git a/src/calibre/gui2/store/declined.txt b/src/calibre/gui2/store/declined.txt new file mode 100644 index 0000000000..2b0e5caed2 --- /dev/null +++ b/src/calibre/gui2/store/declined.txt @@ -0,0 +1,5 @@ +This is a list of stores that objected, declined +or asked not to be included in the store integration. + +* Borders (http://www.borders.com/) +* WH Smith (http://www.whsmith.co.uk/) diff --git a/src/calibre/gui2/store/epubbuy_de_plugin.py b/src/calibre/gui2/store/epubbuy_de_plugin.py new file mode 100644 index 0000000000..242ef76793 --- /dev/null +++ b/src/calibre/gui2/store/epubbuy_de_plugin.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +import urllib2 +from contextlib import closing + +from lxml import html + +from PyQt4.Qt import QUrl + +from calibre import browser +from calibre.gui2 import open_url +from calibre.gui2.store import StorePlugin +from calibre.gui2.store.basic_config import BasicStoreConfig +from calibre.gui2.store.search_result import SearchResult +from calibre.gui2.store.web_store_dialog import WebStoreDialog + +class EPubBuyDEStore(BasicStoreConfig, StorePlugin): + + def open(self, parent=None, detail_item=None, external=False): + url = 'http://klick.affiliwelt.net/klick.php?bannerid=47653&pid=32307&prid=2627' + url_details = ('http://klick.affiliwelt.net/klick.php?bannerid=47653' + '&pid=32307&prid=2627&prodid={0}') + + if external or self.config.get('open_external', False): + if detail_item: + url = url_details.format(detail_item) + open_url(QUrl(url)) + else: + detail_url = None + if detail_item: + detail_url = url_details.format(detail_item) + d = WebStoreDialog(self.gui, url, parent, detail_url) + d.setWindowTitle(self.name) + d.set_tags(self.config.get('tags', '')) + d.exec_() + + def search(self, query, max_results=10, timeout=60): + url = 'http://www.epubbuy.com/search.php?search_query=' + urllib2.quote(query) + br = browser() + + counter = max_results + with closing(br.open(url, timeout=timeout)) as f: + doc = html.fromstring(f.read()) + for data in doc.xpath('//li[contains(@class, "ajax_block_product")]'): + if counter <= 0: + break + + id = ''.join(data.xpath('./div[@class="center_block"]' + '/p[contains(text(), "artnr:")]/text()')).strip() + if not id: + continue + id = id[6:].strip() + if not id: + continue + cover_url = ''.join(data.xpath('./div[@class="center_block"]' + '/a[@class="product_img_link"]/img/@src')) + if cover_url: + cover_url = 'http://www.epubbuy.com' + cover_url + title = ''.join(data.xpath('./div[@class="center_block"]' + '/a[@class="product_img_link"]/@title')) + author = ''.join(data.xpath('./div[@class="center_block"]/a[2]/text()')) + price = ''.join(data.xpath('.//span[@class="price"]/text()')) + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url + s.title = title.strip() + s.author = author.strip() + s.price = price + s.drm = SearchResult.DRM_UNLOCKED + s.detail_item = id + s.formats = 'ePub' + + yield s diff --git a/src/calibre/gui2/store/foyles_uk_plugin.py b/src/calibre/gui2/store/foyles_uk_plugin.py index ca35fb6bb2..1a997cd671 100644 --- a/src/calibre/gui2/store/foyles_uk_plugin.py +++ b/src/calibre/gui2/store/foyles_uk_plugin.py @@ -73,6 +73,6 @@ class FoylesUKStore(BasicStoreConfig, StorePlugin): s.price = price s.detail_item = id s.drm = SearchResult.DRM_LOCKED - s.formats = 'EPUB' + s.formats = 'ePub' yield s diff --git a/src/calibre/gui2/store/search/search.py b/src/calibre/gui2/store/search/search.py index 07d4afca54..62e4e97f11 100644 --- a/src/calibre/gui2/store/search/search.py +++ b/src/calibre/gui2/store/search/search.py @@ -47,7 +47,7 @@ class SearchDialog(QDialog, Ui_Dialog): # per search basis. stores_group_layout = QVBoxLayout() self.stores_group.setLayout(stores_group_layout) - for x in self.store_plugins: + for x in sorted(self.store_plugins.keys(), key=lambda x: x.lower()): cbox = QCheckBox(x) cbox.setChecked(True) stores_group_layout.addWidget(cbox) diff --git a/src/calibre/gui2/store/waterstones_uk_plugin.py b/src/calibre/gui2/store/waterstones_uk_plugin.py index d422165c47..a5065128ba 100644 --- a/src/calibre/gui2/store/waterstones_uk_plugin.py +++ b/src/calibre/gui2/store/waterstones_uk_plugin.py @@ -76,7 +76,7 @@ class WaterstonesUKStore(BasicStoreConfig, StorePlugin): s.detail_item = id formats = [] if epub: - formats.append('EPUB') + formats.append('ePub') if pdf: formats.append('PDF') s.formats = ', '.join(formats) diff --git a/src/calibre/gui2/store/weightless_books_plugin.py b/src/calibre/gui2/store/weightless_books_plugin.py new file mode 100644 index 0000000000..3fa1c76851 --- /dev/null +++ b/src/calibre/gui2/store/weightless_books_plugin.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +import urllib +from contextlib import closing + +from lxml import html + +from PyQt4.Qt import QUrl + +from calibre import browser, url_slash_cleaner +from calibre.gui2 import open_url +from calibre.gui2.store import StorePlugin +from calibre.gui2.store.basic_config import BasicStoreConfig +from calibre.gui2.store.search_result import SearchResult +from calibre.gui2.store.web_store_dialog import WebStoreDialog + +class WeightlessBooksStore(BasicStoreConfig, StorePlugin): + + def open(self, parent=None, detail_item=None, external=False): + url = 'http://weightlessbooks.com/' + + if external or self.config.get('open_external', False): + open_url(QUrl(url_slash_cleaner(detail_item if detail_item else url))) + else: + d = WebStoreDialog(self.gui, url, parent, detail_item) + d.setWindowTitle(self.name) + d.set_tags(self.config.get('tags', '')) + d.exec_() + + def search(self, query, max_results=10, timeout=60): + url = 'http://weightlessbooks.com/?s=' + urllib.quote_plus(query) + + br = browser() + + counter = max_results + with closing(br.open(url, timeout=timeout)) as f: + doc = html.fromstring(f.read()) + for data in doc.xpath('//li[@id="product"]'): + if counter <= 0: + break + + id = ''.join(data.xpath('.//div[@class="cover"]/a/@href')) + if not id: + continue + + cover_url = ''.join(data.xpath('.//div[@class="cover"]/a/img/@src')) + + price = ''.join(data.xpath('.//div[@class="buy_buttons"]/b[1]/text()')) + if not price: + continue + + formats = ', '.join(data.xpath('.//select[@class="eStore_variation"]//option//text()')) + formats = formats.upper() + + title = ''.join(data.xpath('.//h3/a/text()')) + author = ''.join(data.xpath('.//h3//text()')) + author = author.replace(title, '') + + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url + s.title = title.strip() + s.author = author.strip() + s.price = price.strip() + s.detail_item = id.strip() + s.drm = SearchResult.DRM_UNLOCKED + s.formats = formats + + yield s diff --git a/src/calibre/gui2/store/wizards_tower_books_plugin.py b/src/calibre/gui2/store/wizards_tower_books_plugin.py new file mode 100644 index 0000000000..56bb00ff7e --- /dev/null +++ b/src/calibre/gui2/store/wizards_tower_books_plugin.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +import urllib +from contextlib import closing + +from lxml import html + +from PyQt4.Qt import QUrl + +from calibre import browser, url_slash_cleaner +from calibre.gui2 import open_url +from calibre.gui2.store import StorePlugin +from calibre.gui2.store.basic_config import BasicStoreConfig +from calibre.gui2.store.search_result import SearchResult +from calibre.gui2.store.web_store_dialog import WebStoreDialog + +class WizardsTowerBooksStore(BasicStoreConfig, StorePlugin): + + url = 'http://www.wizardstowerbooks.com/' + + def open(self, parent=None, detail_item=None, external=False): + if detail_item: + detail_item = self.url + detail_item + + if external or self.config.get('open_external', False): + open_url(QUrl(url_slash_cleaner(detail_item if detail_item else url))) + else: + d = WebStoreDialog(self.gui, self.url, parent, detail_item) + d.setWindowTitle(self.name) + d.set_tags(self.config.get('tags', '')) + d.exec_() + + def search(self, query, max_results=10, timeout=60): + url = 'http://www.wizardstowerbooks.com/search.html?for=' + urllib.quote(query) + + br = browser() + + counter = max_results + with closing(br.open(url, timeout=timeout)) as f: + doc = html.fromstring(f.read()) + for data in doc.xpath('//table[@class="gridp"]//td'): + if counter <= 0: + break + + id = ''.join(data.xpath('.//span[@class="prti"]/a/@href')) + id = id.strip() + if not id: + continue + + cover_url = ''.join(data.xpath('.//div[@class="prim"]/a/img/@src')) + cover_url = url_slash_cleaner(self.url + cover_url.strip()) + + price = ''.join(data.xpath('.//font[@class="selling_price"]//text()')) + price = price.strip() + if not price: + continue + + title = ''.join(data.xpath('.//span[@class="prti"]/a/b/text()')) + author = ''.join(data.xpath('.//p[@class="last"]/text()')) + a, b, author = author.partition(' by ') + + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url + s.title = title.strip() + s.author = author.strip() + s.price = price.strip() + s.detail_item = id.strip() + s.drm = SearchResult.DRM_UNLOCKED + + yield s + + def get_details(self, search_result, timeout): + br = browser() + with closing(br.open(url_slash_cleaner(self.url + search_result.detail_item), timeout=timeout)) as nf: + idata = html.fromstring(nf.read()) + + formats = ', '.join(idata.xpath('//select[@id="N1_"]//option//text()')) + search_result.formats = formats.upper() + + return True diff --git a/src/calibre/gui2/wizard/__init__.py b/src/calibre/gui2/wizard/__init__.py index a32347dc72..6b1a793fc8 100644 --- a/src/calibre/gui2/wizard/__init__.py +++ b/src/calibre/gui2/wizard/__init__.py @@ -435,7 +435,7 @@ class DevicePage(QWizardPage, DeviceUI): self.registerField("device", self.device_view) def initializePage(self): - self.label.setText(_('Choose you e-book device. If your device is' + self.label.setText(_('Choose your e-book device. If your device is' ' not in the list, choose a "%s" device.')%Device.manufacturer) self.man_model = ManufacturerModel() self.manufacturer_view.setModel(self.man_model)