mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-10-31 10:37:00 -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() | ||||
|         model_name = cls.get_gui_name() | ||||
|         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