mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
More work on the connect to folder dialog
This commit is contained in:
parent
c4de6b9bd9
commit
9684457744
@ -674,7 +674,7 @@ def device_plugins(include_disabled=False):
|
||||
def usbms_plugins(include_disabled=True):
|
||||
from calibre.devices.usbms.driver import USBMS
|
||||
for plugin in device_plugins(include_disabled):
|
||||
if isinstance(plugin, USBMS) and plugin.name not in ('Folder Device Interface',):
|
||||
if isinstance(plugin, USBMS) and plugin.name not in ('Folder Device Interface', 'User Defined USB driver'):
|
||||
yield plugin
|
||||
|
||||
|
||||
|
@ -293,7 +293,7 @@ class INVESBOOK(EB600):
|
||||
|
||||
class BOOQ(EB600):
|
||||
name = 'Booq Device Interface'
|
||||
gui_name = 'bq Reader'
|
||||
gui_name = 'Bq Reader'
|
||||
|
||||
FORMATS = ['epub', 'mobi', 'prc', 'fb2', 'pdf', 'doc', 'rtf', 'txt', 'html']
|
||||
|
||||
@ -330,6 +330,7 @@ class ELONEX(EB600):
|
||||
class POCKETBOOK301(USBMS):
|
||||
|
||||
name = 'PocketBook 301 Device Interface'
|
||||
gui_name = 'PocketBook 301'
|
||||
description = _('Communicate with the PocketBook 301 Reader.')
|
||||
author = 'Kovid Goyal'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
@ -348,7 +349,7 @@ class POCKETBOOK301(USBMS):
|
||||
class POCKETBOOK602(USBMS):
|
||||
|
||||
name = 'PocketBook Pro 602/902 Device Interface'
|
||||
gui_name = 'PocketBook'
|
||||
gui_name = 'PocketBook Pro'
|
||||
description = _('Communicate with the PocketBook 515/602/603/902/903/Pro 912 reader.')
|
||||
author = 'Kovid Goyal'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
@ -371,6 +372,7 @@ class POCKETBOOK602(USBMS):
|
||||
class POCKETBOOK622(POCKETBOOK602):
|
||||
|
||||
name = 'PocketBook 622 Device Interface'
|
||||
gui_name = 'PocketBook 622'
|
||||
description = _('Communicate with the PocketBook 622 and 623 readers.')
|
||||
EBOOK_DIR_MAIN = ''
|
||||
|
||||
@ -385,6 +387,7 @@ class POCKETBOOK622(POCKETBOOK602):
|
||||
class POCKETBOOK360P(POCKETBOOK602):
|
||||
|
||||
name = 'PocketBook 360+ Device Interface'
|
||||
gui_name = 'PocketBook 360+'
|
||||
description = _('Communicate with the PocketBook 360+ reader.')
|
||||
BCD = [0x0323]
|
||||
EBOOK_DIR_MAIN = ''
|
||||
@ -396,7 +399,7 @@ class POCKETBOOK360P(POCKETBOOK602):
|
||||
class POCKETBOOK701(USBMS):
|
||||
|
||||
name = 'PocketBook 701 Device Interface'
|
||||
gui_name = 'PocketBook'
|
||||
gui_name = 'PocketBook 701'
|
||||
description = _('Communicate with the PocketBook 701')
|
||||
author = _('Kovid Goyal')
|
||||
|
||||
@ -428,7 +431,7 @@ class POCKETBOOK701(USBMS):
|
||||
class POCKETBOOK740(USBMS):
|
||||
|
||||
name = 'PocketBook 740 Device Interface'
|
||||
gui_name = 'PocketBook'
|
||||
gui_name = 'PocketBook 740'
|
||||
description = _('Communicate with the PocketBook 740')
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm',
|
||||
|
@ -61,7 +61,7 @@ class KIBANO(N516):
|
||||
|
||||
class THEBOOK(N516):
|
||||
name = 'The Book driver'
|
||||
gui_name = 'The Book'
|
||||
gui_name = 'Book'
|
||||
description = _('Communicate with The Book reader.')
|
||||
author = 'Kovid Goyal'
|
||||
|
||||
|
@ -17,6 +17,10 @@ class ModelMetadata(NamedTuple):
|
||||
bcd: int
|
||||
driver_class: type
|
||||
|
||||
@property
|
||||
def settings_key(self) -> str:
|
||||
return f'{self.manufacturer_name} - {self.model_name}'
|
||||
|
||||
|
||||
class OpenPopupMessage:
|
||||
|
||||
|
@ -82,7 +82,7 @@ def get_files_in(path):
|
||||
class KINDLE(USBMS):
|
||||
|
||||
name = 'Kindle Device Interface'
|
||||
gui_name = 'Amazon Kindle'
|
||||
gui_name = 'Amazon Kindle Keyboard'
|
||||
icon = 'devices/kindle.png'
|
||||
description = _('Communicate with the Kindle e-book reader.')
|
||||
author = 'John Schember'
|
||||
@ -369,6 +369,7 @@ class KINDLE(USBMS):
|
||||
class KINDLE2(KINDLE):
|
||||
|
||||
name = 'Kindle 2/3/4/Touch/PaperWhite/Voyage Device Interface'
|
||||
gui_name = 'Amazon Kindle'
|
||||
description = _('Communicate with the Kindle 2/3/4/Touch/Paperwhite/Voyage e-book reader.')
|
||||
|
||||
FORMATS = ['azw', 'mobi', 'azw3', 'prc', 'azw1', 'tpz', 'azw4', 'kfx', 'pobi', 'pdf', 'txt']
|
||||
@ -649,6 +650,7 @@ class KINDLE2(KINDLE):
|
||||
class KINDLE_DX(KINDLE2):
|
||||
|
||||
name = 'Kindle DX Device Interface'
|
||||
gui_name = 'Amazon Kindle DX'
|
||||
description = _('Communicate with the Kindle DX e-book reader.')
|
||||
|
||||
FORMATS = ['azw', 'mobi', 'prc', 'azw1', 'tpz', 'azw4', 'pobi', 'pdf', 'txt']
|
||||
@ -666,7 +668,7 @@ class KINDLE_FIRE(KINDLE2):
|
||||
|
||||
name = 'Kindle Fire Device Interface'
|
||||
description = _('Communicate with the Kindle Fire')
|
||||
gui_name = 'Fire'
|
||||
gui_name = 'Amazon Kindle Fire'
|
||||
FORMATS = ['azw3', 'azw', 'mobi', 'prc', 'azw1', 'tpz', 'azw4', 'kfx', 'pobi', 'pdf', 'txt']
|
||||
|
||||
PRODUCT_ID = [0x0006]
|
||||
|
@ -34,7 +34,7 @@ class PALMPRE(USBMS):
|
||||
|
||||
class AVANT(USBMS):
|
||||
name = 'Booq Avant Device Interface'
|
||||
gui_name = 'bq Avant'
|
||||
gui_name = 'Bq Avant'
|
||||
description = _('Communicate with the Bq Avant')
|
||||
author = 'Kovid Goyal'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
@ -521,7 +521,7 @@ class WOXTER(USBMS):
|
||||
class POCKETBOOK626(USBMS):
|
||||
|
||||
name = 'PocketBook Touch Lux 2'
|
||||
gui_name = 'PocketBook'
|
||||
gui_name = 'PocketBook Touch Lux 2'
|
||||
description = _('Communicate with the PocketBook Touch Lux 2 and Inkpad X readers')
|
||||
author = 'Kovid Goyal'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
|
@ -18,7 +18,7 @@ from calibre.utils.resources import get_image_path as I
|
||||
class NOOK(USBMS):
|
||||
|
||||
name = 'Nook Device Interface'
|
||||
gui_name = _('The Nook')
|
||||
gui_name = 'B&N Nook'
|
||||
description = _('Communicate with the Nook e-book reader.')
|
||||
author = 'John Schember'
|
||||
icon = 'devices/nook.png'
|
||||
@ -80,6 +80,7 @@ class NOOK(USBMS):
|
||||
|
||||
class NOOK_COLOR(NOOK):
|
||||
name = 'Nook Color Device Interface'
|
||||
gui_name = _('B&N Nook Color')
|
||||
description = _('Communicate with the Nook Color, TSR, Glowlight and Tablet e-book readers.')
|
||||
|
||||
PRODUCT_ID = [
|
||||
|
@ -20,7 +20,7 @@ from calibre.prints import debug_print
|
||||
class PRS505(USBMS):
|
||||
|
||||
name = 'SONY Device Interface'
|
||||
gui_name = 'SONY Reader'
|
||||
gui_name = 'SONY PRS-500'
|
||||
description = _('Communicate with Sony e-book readers older than the'
|
||||
' PRST1.')
|
||||
author = 'Kovid Goyal'
|
||||
|
@ -38,7 +38,7 @@ class ImageWrapper:
|
||||
|
||||
class PRST1(USBMS):
|
||||
name = 'SONY PRST1 and newer Device Interface'
|
||||
gui_name = 'SONY Reader'
|
||||
gui_name = 'SONY PRS-T1'
|
||||
description = _('Communicate with the PRST1 and newer SONY e-book readers')
|
||||
author = 'Kovid Goyal'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
|
@ -150,7 +150,10 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
bcd = cls.BCD
|
||||
return vid or 0, pid or 0, bcd or 0
|
||||
vid, pid, bcd = get_representative_ids()
|
||||
try:
|
||||
model_name = cls.get_gui_name()
|
||||
except TypeError: # The WAYTEQ driver implements this as non classmethod
|
||||
return ()
|
||||
parts = model_name.split(' ', 1)
|
||||
manufacturer = ''
|
||||
if len(parts) > 1:
|
||||
|
@ -1,13 +1,130 @@
|
||||
#!/usr/bin/env python
|
||||
# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from qt.core import QVBoxLayout
|
||||
from qt.core import QAbstractItemView, QGroupBox, QHBoxLayout, QIcon, QLabel, Qt, QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
||||
|
||||
from calibre.gui2.widgets2 import Dialog
|
||||
from calibre.customize.ui import usbms_plugins
|
||||
from calibre.gui2 import Application, choose_dir, gprefs
|
||||
from calibre.gui2.widgets2 import Dialog, HistoryLineEdit2
|
||||
from calibre.utils.icu import primary_sort_key
|
||||
|
||||
|
||||
class ChooseFolder(QWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.folder_edit = fe = HistoryLineEdit2(parent=self)
|
||||
fe.initialize('connect-to-folder')
|
||||
fe.setPlaceholderText(_('Path to folder to connect to'))
|
||||
if len(fe.history):
|
||||
fe.setText(fe.history[0])
|
||||
self.browse_button = bb = QToolButton(self)
|
||||
bb.setIcon(QIcon.ic('mimetypes/dir.png'))
|
||||
bb.clicked.connect(self.browse)
|
||||
self.l = l = QHBoxLayout(self)
|
||||
l.setContentsMargins(0, 0, 0, 0)
|
||||
l.addWidget(fe)
|
||||
l.addWidget(bb)
|
||||
|
||||
def browse(self):
|
||||
ans = choose_dir(self, 'connect-to-folder-browse-history', _('Choose folder to connect to'))
|
||||
if ans:
|
||||
self.folder_edit.setText(ans)
|
||||
|
||||
@property
|
||||
def folder(self):
|
||||
return self.folder_edit.text().strip()
|
||||
|
||||
@folder.setter
|
||||
def folder(self, val):
|
||||
self.folder_edit.setText((val or '').strip())
|
||||
|
||||
def on_accept(self):
|
||||
if self.folder:
|
||||
self.folder_edit.save_history()
|
||||
|
||||
|
||||
class ConnectToFolder(Dialog):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(_('Connect to folder'), 'connect-to-folderx', parent=parent)
|
||||
super().__init__(_('Connect to folder'), 'connect-to-folder', parent=parent)
|
||||
|
||||
def setup_ui(self):
|
||||
self.l = l = QVBoxLayout(self)
|
||||
self.folder_chooser = fc = ChooseFolder(self)
|
||||
l.addWidget(fc)
|
||||
self.la = la = QLabel('<p>' + _(
|
||||
'Choose a device to connect as below. If no device is chosen a generic <i>Folder device</i>'
|
||||
' will be used.'))
|
||||
self.la2 = la2 = QLabel('<p>' + _('<b>WARNING</b>: Connecting as a specific device will work only'
|
||||
' if the chosen folder above contains the actual files from an actual device, as the'
|
||||
' device drivers often expect to find certain device specific files. So only choose'
|
||||
' a device below if you have copied the files from a real device or mounted it at the'
|
||||
' chosen location.'))
|
||||
la.setWordWrap(True), la2.setWordWrap(True)
|
||||
l.addWidget(la)
|
||||
self.devices_group = dg = QGroupBox(_('Connect as device'), self)
|
||||
dg.setCheckable(True)
|
||||
l.addWidget(dg)
|
||||
l.addWidget(self.bb)
|
||||
dg.l = l = QVBoxLayout(dg)
|
||||
self.devices = d = QTreeWidget(self)
|
||||
d.setHeaderHidden(True)
|
||||
d.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
||||
l.addWidget(la2)
|
||||
l.addWidget(d)
|
||||
|
||||
lcd = gprefs.get('last_connected_folder_as_device', None)
|
||||
selected_device = None
|
||||
man_map = {}
|
||||
for cls in usbms_plugins():
|
||||
for model in cls.model_metadata():
|
||||
man_map.setdefault(model.manufacturer_name, []).append(model)
|
||||
for manufacturer in sorted(man_map, key=primary_sort_key):
|
||||
devs = man_map[manufacturer]
|
||||
m = QTreeWidgetItem(d, 0)
|
||||
m.setText(0, manufacturer)
|
||||
m.setFlags(m.flags() & ~Qt.ItemFlag.ItemIsSelectable)
|
||||
flags = m.flags()
|
||||
devs.sort(key=lambda x: primary_sort_key(x.model_name))
|
||||
expanded = False
|
||||
for dev in devs:
|
||||
i = QTreeWidgetItem(m, 1)
|
||||
i.setText(0, dev.model_name)
|
||||
i.setData(0, Qt.ItemDataRole.UserRole, dev)
|
||||
i.setFlags(i.flags() | Qt.ItemFlag.ItemNeverHasChildren)
|
||||
if dev.settings_key == lcd:
|
||||
i.setSelected(True)
|
||||
expanded = True
|
||||
selected_device = i
|
||||
m.setExpanded(expanded)
|
||||
if selected_device is not None:
|
||||
d.scrollToItem(selected_device)
|
||||
dg.setChecked(selected_device is not None)
|
||||
|
||||
@property
|
||||
def model_metadata(self):
|
||||
if self.devices_group.isChecked():
|
||||
for m in range(self.devices.topLevelItemCount()):
|
||||
man = self.devices.topLevelItem(m)
|
||||
for i in range(man.childCount()):
|
||||
item = man.child(i)
|
||||
if item.isSelected():
|
||||
return item.data(0, Qt.ItemDataRole.UserRole)
|
||||
|
||||
def accept(self):
|
||||
self.folder_chooser.on_accept()
|
||||
m = self.model_metadata
|
||||
gprefs.set('last_connected_folder_as_device', None if m is None else m.settings_key)
|
||||
return super().accept()
|
||||
|
||||
@property
|
||||
def ans(self):
|
||||
return self.folder_chooser.folder, self.model_metadata
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = Application([])
|
||||
d = ConnectToFolder()
|
||||
d.exec()
|
||||
print(d.ans)
|
||||
|
Loading…
x
Reference in New Issue
Block a user