Speed up device detection on windows. Difference will be noticeable for all the EB600 clones in particular

This commit is contained in:
Kovid Goyal 2010-01-02 19:42:45 -07:00
parent 0496407296
commit f061a40650
7 changed files with 222 additions and 241 deletions

View File

@ -37,6 +37,10 @@ def debug(ioreg_to_tmp=False, buf=None):
if buf is None: if buf is None:
buf = StringIO() buf = StringIO()
sys.stdout = sys.stderr = buf sys.stdout = sys.stderr = buf
if iswindows:
import pythoncom
pythoncom.CoInitialize()
try: try:
out = partial(prints, file=buf) out = partial(prints, file=buf)
out('Version:', __version__) out('Version:', __version__)
@ -50,29 +54,25 @@ def debug(ioreg_to_tmp=False, buf=None):
d[i] = hex(d[i]) d[i] = hex(d[i])
out('USB devices on system:') out('USB devices on system:')
out(pprint.pformat(devices)) out(pprint.pformat(devices))
wmi = Wmi =None
if iswindows: if iswindows:
if iswindows: wmi = __import__('wmi', globals(), locals(), [], -1)
import pythoncom Wmi = wmi.WMI(find_classes=False)
pythoncom.CoInitialize() drives = []
out('Drives detected:')
out('\t', '(ID, Partitions, Drive letter)')
for drive in Wmi.Win32_DiskDrive():
if drive.Partitions == 0:
continue
try: try:
wmi = __import__('wmi', globals(), locals(), [], -1) partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
drives = [] logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
out('Drives detected:') prefix = logical_disk.DeviceID+os.sep
out('\t', '(ID, Partitions, Drive letter)') drives.append((str(drive.PNPDeviceID), drive.Index, prefix))
for drive in wmi.WMI(find_classes=False).Win32_DiskDrive(): except IndexError:
if drive.Partitions == 0: drives.append((str(drive.PNPDeviceID), 'No mount points found'))
continue for drive in drives:
try: out('\t', drive)
partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
prefix = logical_disk.DeviceID+os.sep
drives.append((str(drive.PNPDeviceID), drive.Index, prefix))
except IndexError:
drives.append((str(drive.PNPDeviceID), 'No mount points found'))
for drive in drives:
out('\t', drive)
finally:
pythoncom.CoUninitialize()
ioreg = None ioreg = None
if isosx: if isosx:
@ -81,17 +81,26 @@ def debug(ioreg_to_tmp=False, buf=None):
ioreg = Device.run_ioreg() ioreg = Device.run_ioreg()
ioreg = 'Output from mount:\n\n'+mount+'\n\n'+ioreg ioreg = 'Output from mount:\n\n'+mount+'\n\n'+ioreg
connected_devices = [] connected_devices = []
s.wmi = Wmi
for dev in device_plugins(): for dev in device_plugins():
owmi = getattr(dev, 'wmi', None)
dev.wmi = Wmi
out('Looking for', dev.__class__.__name__) out('Looking for', dev.__class__.__name__)
connected, det = s.is_device_connected(dev, debug=True) connected, det = s.is_device_connected(dev, debug=True)
if connected: if connected:
connected_devices.append((dev, det)) connected_devices.append((dev, det))
dev.wmi = owmi
errors = {} errors = {}
success = False success = False
out('Devices possibly connected:', end=' ')
for dev, det in connected_devices: for dev, det in connected_devices:
out('Device possibly connected:', dev.__class__.name) out(dev.name, end=', ')
out('Trying to open device...', end=' ') out(' ')
for dev, det in connected_devices:
out('Trying to open', dev.name, '...', end=' ')
owmi = getattr(dev, 'wmi', None)
dev.wmi = Wmi
try: try:
dev.reset(detected_device=det) dev.reset(detected_device=det)
dev.open() dev.open()
@ -101,6 +110,8 @@ def debug(ioreg_to_tmp=False, buf=None):
errors[dev] = traceback.format_exc() errors[dev] = traceback.format_exc()
out('failed') out('failed')
continue continue
finally:
dev.wmi = owmi
success = True success = True
if hasattr(dev, '_main_prefix'): if hasattr(dev, '_main_prefix'):
out('Main memory:', repr(dev._main_prefix)) out('Main memory:', repr(dev._main_prefix))
@ -128,4 +139,7 @@ def debug(ioreg_to_tmp=False, buf=None):
finally: finally:
sys.stdout = oldo sys.stdout = oldo
sys.stderr = olde sys.stderr = olde
if iswindows:
import pythoncom
pythoncom.CoUninitialize()

View File

@ -43,8 +43,7 @@ class DevicePlugin(Plugin):
#: Icon for this device #: Icon for this device
icon = I('reader.svg') icon = I('reader.svg')
@classmethod def test_bcd_windows(self, device_id, bcd):
def test_bcd_windows(cls, device_id, bcd):
if bcd is None or len(bcd) == 0: if bcd is None or len(bcd) == 0:
return True return True
for c in bcd: for c in bcd:
@ -54,30 +53,28 @@ class DevicePlugin(Plugin):
return True return True
return False return False
@classmethod def print_usb_device_info(self, info):
def print_usb_device_info(cls, info):
try: try:
print '\t', repr(info) print '\t', repr(info)
except: except:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
@classmethod def is_usb_connected_windows(self, devices_on_system, pnp_id_iterator, debug=False):
def is_usb_connected_windows(cls, devices_on_system, debug=False):
def id_iterator(): def id_iterator():
if hasattr(cls.VENDOR_ID, 'keys'): if hasattr(self.VENDOR_ID, 'keys'):
for vid in cls.VENDOR_ID: for vid in self.VENDOR_ID:
vend = cls.VENDOR_ID[vid] vend = self.VENDOR_ID[vid]
for pid in vend: for pid in vend:
bcd = vend[pid] bcd = vend[pid]
yield vid, pid, bcd yield vid, pid, bcd
else: else:
vendors = cls.VENDOR_ID if hasattr(cls.VENDOR_ID, '__len__') else [cls.VENDOR_ID] vendors = self.VENDOR_ID if hasattr(self.VENDOR_ID, '__len__') else [self.VENDOR_ID]
products = cls.PRODUCT_ID if hasattr(cls.PRODUCT_ID, '__len__') else [cls.PRODUCT_ID] products = self.PRODUCT_ID if hasattr(self.PRODUCT_ID, '__len__') else [self.PRODUCT_ID]
for vid in vendors: for vid in vendors:
for pid in products: for pid in products:
yield vid, pid, cls.BCD yield vid, pid, self.BCD
for vendor_id, product_id, bcd in id_iterator(): for vendor_id, product_id, bcd in id_iterator():
vid, pid = 'vid_%4.4x'%vendor_id, 'pid_%4.4x'%product_id vid, pid = 'vid_%4.4x'%vendor_id, 'pid_%4.4x'%product_id
@ -85,15 +82,14 @@ class DevicePlugin(Plugin):
for device_id in devices_on_system: for device_id in devices_on_system:
if (vid in device_id or vidd in device_id) and \ if (vid in device_id or vidd in device_id) and \
(pid in device_id or pidd in device_id) and \ (pid in device_id or pidd in device_id) and \
cls.test_bcd_windows(device_id, bcd): self.test_bcd_windows(device_id, bcd):
if debug: if debug:
cls.print_usb_device_info(device_id) self.print_usb_device_info(device_id)
if cls.can_handle(device_id): if self.can_handle_windows(device_id, pnp_id_iterator, debug=debug):
return True return True
return False return False
@classmethod def test_bcd(self, bcdDevice, bcd):
def test_bcd(cls, bcdDevice, bcd):
if bcd is None or len(bcd) == 0: if bcd is None or len(bcd) == 0:
return True return True
for c in bcd: for c in bcd:
@ -101,24 +97,24 @@ class DevicePlugin(Plugin):
return True return True
return False return False
@classmethod def is_usb_connected(self, devices_on_system, pnp_id_iterator, debug=False):
def is_usb_connected(cls, devices_on_system, debug=False):
''' '''
Return True, device_info if a device handled by this plugin is currently connected. Return True, device_info if a device handled by this plugin is currently connected.
:param devices_on_system: List of devices currently connected :param devices_on_system: List of devices currently connected
''' '''
if iswindows: if iswindows:
return cls.is_usb_connected_windows(devices_on_system, debug=debug), None return self.is_usb_connected_windows(devices_on_system,
pnp_id_iterator, debug=debug), None
vendors_on_system = set([x[0] for x in devices_on_system]) vendors_on_system = set([x[0] for x in devices_on_system])
vendors = cls.VENDOR_ID if hasattr(cls.VENDOR_ID, '__len__') else [cls.VENDOR_ID] vendors = self.VENDOR_ID if hasattr(self.VENDOR_ID, '__len__') else [self.VENDOR_ID]
if hasattr(cls.VENDOR_ID, 'keys'): if hasattr(self.VENDOR_ID, 'keys'):
products = [] products = []
for ven in cls.VENDOR_ID: for ven in self.VENDOR_ID:
products.extend(cls.VENDOR_ID[ven].keys()) products.extend(self.VENDOR_ID[ven].keys())
else: else:
products = cls.PRODUCT_ID if hasattr(cls.PRODUCT_ID, '__len__') else [cls.PRODUCT_ID] products = self.PRODUCT_ID if hasattr(self.PRODUCT_ID, '__len__') else [self.PRODUCT_ID]
for vid in vendors: for vid in vendors:
if vid in vendors_on_system: if vid in vendors_on_system:
@ -126,14 +122,14 @@ class DevicePlugin(Plugin):
cvid, pid, bcd = dev[:3] cvid, pid, bcd = dev[:3]
if cvid == vid: if cvid == vid:
if pid in products: if pid in products:
if hasattr(cls.VENDOR_ID, 'keys'): if hasattr(self.VENDOR_ID, 'keys'):
cbcd = cls.VENDOR_ID[vid][pid] cbcd = self.VENDOR_ID[vid][pid]
else: else:
cbcd = cls.BCD cbcd = self.BCD
if cls.test_bcd(bcd, cbcd): if self.test_bcd(bcd, cbcd):
if debug: if debug:
cls.print_usb_device_info(dev) self.print_usb_device_info(dev)
if cls.can_handle(dev, debug=debug): if self.can_handle(dev, debug=debug):
return True, dev return True, dev
return False, None return False, None
@ -151,25 +147,30 @@ class DevicePlugin(Plugin):
""" """
raise NotImplementedError() raise NotImplementedError()
@classmethod def can_handle_windows(self, device_id, pnp_id_iterator, debug=False):
def get_fdi(cls):
'''Return the FDI description of this device for HAL on linux.'''
return ''
@classmethod
def can_handle(cls, device_info, debug=False):
''' '''
Optional method to perform further checks on a device to see if this driver Optional method to perform further checks on a device to see if this driver
is capable of handling it. If it is not it should return False. This method is capable of handling it. If it is not it should return False. This method
is only called after the vendor, product ids and the bcd have matched, so is only called after the vendor, product ids and the bcd have matched, so
it can do some relatively time intensive checks. The default implementation it can do some relatively time intensive checks. The default implementation
returns True. returns True. This method is called only on windows. See also
:method:`can_handle`.
:param device_info: On windows a device ID string. On Unix a tuple of :param device_info: On windows a device ID string. On Unix a tuple of
``(vendor_id, product_id, bcd)``. ``(vendor_id, product_id, bcd)``.
''' '''
return True return True
def can_handle(self, device_info, debug=False):
'''
Unix version of :method:`can_handle_windows`
:param device_info: Is a tupe of (vid, pid, bcd, manufacturer, product,
serial number)
'''
return True
def open(self): def open(self):
''' '''
Perform any device specific initialization. Called after the device is Perform any device specific initialization. Called after the device is

View File

@ -197,15 +197,15 @@ def main():
command = args[0] command = args[0]
args = args[1:] args = args[1:]
dev = None dev = None
_wmi = None scanner = DeviceScanner()
if iswindows: if iswindows:
import wmi, pythoncom import wmi, pythoncom
pythoncom.CoInitialize() pythoncom.CoInitialize()
_wmi = wmi.WMI() scanner.wmi = wmi.WMI(find_classes=False)
scanner = DeviceScanner(_wmi)
scanner.scan() scanner.scan()
connected_devices = [] connected_devices = []
for d in device_plugins(): for d in device_plugins():
d.wmi = scanner.wmi
ok, det = scanner.is_device_connected(d) ok, det = scanner.is_device_connected(d)
if ok: if ok:
dev = d dev = d

View File

@ -85,13 +85,26 @@ class DeviceScanner(object):
raise RuntimeError('DeviceScanner requires the /sys filesystem to work.') raise RuntimeError('DeviceScanner requires the /sys filesystem to work.')
self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner
self.devices = [] self.devices = []
self.wmi = None
self.pnp_ids = set([])
self.rescan_pnp_ids = True
def scan(self): def scan(self):
'''Fetch list of connected USB devices from operating system''' '''Fetch list of connected USB devices from operating system'''
self.devices = self.scanner() self.devices = self.scanner()
if self.rescan_pnp_ids:
self.pnp_ids = set([])
def pnp_id_iterator(self):
if self.wmi is not None and not self.pnp_ids:
for drive in self.wmi.Win32_DiskDrive():
if drive.Partitions > 0:
self.pnp_ids.add(str(drive.PNPDeviceID))
for x in self.pnp_ids:
yield x
def is_device_connected(self, device, debug=False): def is_device_connected(self, device, debug=False):
return device.is_usb_connected(self.devices, debug=debug) return device.is_usb_connected(self.devices, self.pnp_id_iterator, debug=debug)
def main(args=sys.argv): def main(args=sys.argv):

View File

@ -22,7 +22,7 @@ from itertools import repeat
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
from calibre.devices.errors import DeviceError, FreeSpaceError from calibre.devices.errors import DeviceError, FreeSpaceError
from calibre.devices.usbms.deviceconfig import DeviceConfig from calibre.devices.usbms.deviceconfig import DeviceConfig
from calibre.constants import iswindows, islinux, isosx, __appname__, plugins from calibre.constants import iswindows, islinux, isosx, plugins
from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
if isosx: if isosx:
@ -83,53 +83,6 @@ class Device(DeviceConfig, DevicePlugin):
EBOOK_DIR_CARD_B = '' EBOOK_DIR_CARD_B = ''
DELETE_EXTS = [] DELETE_EXTS = []
FDI_TEMPLATE = \
'''
<device>
<match key="info.category" string="volume">
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
%(BCD_start)s
<match key="@info.parent:storage.lun" int="%(lun0)d">
<merge key="volume.label" type="string">%(main_memory)s</merge>
<merge key="%(app)s.mainvolume" type="string">%(deviceclass)s</merge>
</match>
%(BCD_end)s
</match>
</match>
</match>
</device>
<device>
<match key="info.category" string="volume">
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
%(BCD_start)s
<match key="@info.parent:storage.lun" int="%(lun1)d">
<merge key="volume.label" type="string">%(storage_card)s</merge>
<merge key="%(app)s.cardvolume" type="string">%(deviceclass)s</merge>
</match>
%(BCD_end)s
</match>
</match>
</match>
</device>
<device>
<match key="info.category" string="volume">
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
%(BCD_start)s
<match key="@info.parent:storage.lun" int="%(lun2)d">
<merge key="volume.label" type="string">%(storage_card)s</merge>
<merge key="%(app)s.cardvolume" type="string">%(deviceclass)s</merge>
</match>
%(BCD_end)s
</match>
</match>
</match>
</device>
'''
FDI_LUNS = {'lun0':0, 'lun1':1, 'lun2':2}
FDI_BCD_TEMPLATE = '<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.device_revision_bcd" int="%(bcd)s">'
def reset(self, key='-1', log_packets=False, report_progress=None, def reset(self, key='-1', log_packets=False, report_progress=None,
detected_device=None): detected_device=None):
@ -138,6 +91,7 @@ class Device(DeviceConfig, DevicePlugin):
self.detected_device = USBDevice(detected_device) self.detected_device = USBDevice(detected_device)
except: # On windows detected_device is None except: # On windows detected_device is None
self.detected_device = None self.detected_device = None
self.set_progress_reporter(report_progress)
@classmethod @classmethod
def get_gui_name(cls): def get_gui_name(cls):
@ -146,37 +100,11 @@ class Device(DeviceConfig, DevicePlugin):
x = cls.__name__ x = cls.__name__
return x return x
@classmethod
def get_fdi(cls):
fdi = ''
for vid in cls.VENDOR_ID:
for pid in cls.PRODUCT_ID:
fdi_base_values = dict(
app=__appname__,
deviceclass=cls.__name__,
vendor_id=hex(vid),
product_id=hex(pid),
main_memory=cls.MAIN_MEMORY_VOLUME_LABEL,
storage_card=cls.STORAGE_CARD_VOLUME_LABEL,
)
fdi_base_values.update(cls.FDI_LUNS)
if cls.BCD is None:
fdi_base_values['BCD_start'] = ''
fdi_base_values['BCD_end'] = ''
fdi += cls.FDI_TEMPLATE % fdi_base_values
else:
for bcd in cls.BCD:
fdi_bcd_values = fdi_base_values
fdi_bcd_values['BCD_start'] = cls.FDI_BCD_TEMPLATE % dict(bcd=hex(bcd))
fdi_bcd_values['BCD_end'] = '</match>'
fdi += cls.FDI_TEMPLATE % fdi_bcd_values
return fdi
def set_progress_reporter(self, report_progress): def set_progress_reporter(self, report_progress):
self.report_progress = report_progress self.report_progress = report_progress
self.report_progress = report_progress
if self.report_progress is None:
self.report_progress = lambda x, y: x
def card_prefix(self, end_session=True): def card_prefix(self, end_session=True):
return (self._card_a_prefix, self._card_b_prefix) return (self._card_a_prefix, self._card_b_prefix)
@ -239,9 +167,7 @@ class Device(DeviceConfig, DevicePlugin):
def windows_filter_pnp_id(self, pnp_id): def windows_filter_pnp_id(self, pnp_id):
return False return False
def windows_match_device(self, drive, attr): def windows_match_device(self, pnp_id, attr):
pnp_id = (str(drive.PNPDeviceID) if not isinstance(drive, basestring)
else str(drive)).upper()
device_id = getattr(self, attr) device_id = getattr(self, attr)
def test_vendor(): def test_vendor():
@ -292,6 +218,12 @@ class Device(DeviceConfig, DevicePlugin):
''' '''
return drives return drives
def can_handle_windows(self, device_id, pnp_id_iterator, debug=False):
for pnp_id in pnp_id_iterator():
if self.windows_match_device(pnp_id, 'WINDOWS_MAIN_MEM'):
return True
return False
def open_windows(self): def open_windows(self):
def matches_q(drive, attr): def matches_q(drive, attr):
@ -307,14 +239,14 @@ class Device(DeviceConfig, DevicePlugin):
time.sleep(8) time.sleep(8)
drives = {} drives = {}
wmi = __import__('wmi', globals(), locals(), [], -1) c = self.wmi
c = wmi.WMI(find_classes=False)
for drive in c.Win32_DiskDrive(): for drive in c.Win32_DiskDrive():
if self.windows_match_device(drive, 'WINDOWS_CARD_A_MEM') and not drives.get('carda', None): pnp_id = str(drive.PNPDeviceID)
if self.windows_match_device(pnp_id, 'WINDOWS_CARD_A_MEM') and not drives.get('carda', None):
drives['carda'] = self.windows_get_drive_prefix(drive) drives['carda'] = self.windows_get_drive_prefix(drive)
elif self.windows_match_device(drive, 'WINDOWS_CARD_B_MEM') and not drives.get('cardb', None): elif self.windows_match_device(pnp_id, 'WINDOWS_CARD_B_MEM') and not drives.get('cardb', None):
drives['cardb'] = self.windows_get_drive_prefix(drive) drives['cardb'] = self.windows_get_drive_prefix(drive)
elif self.windows_match_device(drive, 'WINDOWS_MAIN_MEM') and not drives.get('main', None): elif self.windows_match_device(pnp_id, 'WINDOWS_MAIN_MEM') and not drives.get('main', None):
drives['main'] = self.windows_get_drive_prefix(drive) drives['main'] = self.windows_get_drive_prefix(drive)
if 'main' in drives.keys() and 'carda' in drives.keys() and \ if 'main' in drives.keys() and 'carda' in drives.keys() and \

View File

@ -72,15 +72,12 @@ class DeviceManager(Thread):
def __init__(self, connected_slot, job_manager, sleep_time=2): def __init__(self, connected_slot, job_manager, sleep_time=2):
''' '''
@param sleep_time: Time to sleep between device probes in secs :sleep_time: Time to sleep between device probes in secs
@type sleep_time: integer
''' '''
Thread.__init__(self) Thread.__init__(self)
self.setDaemon(True) self.setDaemon(True)
# [Device driver, Showing in GUI, Ejected] # [Device driver, Showing in GUI, Ejected]
self.devices = [[d, False, False] for d in device_plugins()] self.devices = list(device_plugins())
self.device = None
self.device_class = None
self.sleep_time = sleep_time self.sleep_time = sleep_time
self.connected_slot = connected_slot self.connected_slot = connected_slot
self.jobs = Queue.Queue(0) self.jobs = Queue.Queue(0)
@ -88,75 +85,83 @@ class DeviceManager(Thread):
self.job_manager = job_manager self.job_manager = job_manager
self.current_job = None self.current_job = None
self.scanner = DeviceScanner() self.scanner = DeviceScanner()
self.wmi = None
self.connected_device = None
self.ejected_devices = set([])
def report_progress(self, *args):
pass
@property
def is_device_connected(self):
return self.connected_device is not None
@property
def device(self):
return self.connected_device
def do_connect(self, connected_devices): def do_connect(self, connected_devices):
if iswindows: for dev, detected_device in connected_devices:
import pythoncom dev.reset(detected_device=detected_device,
pythoncom.CoInitialize() report_progress=self.report_progress)
try: try:
for dev, detected_device in connected_devices: dev.open()
dev.reset(detected_device=detected_device) except:
try: print 'Unable to open device', dev
dev.open() traceback.print_exc()
except: continue
print 'Unable to open device', dev self.connected_device = dev
traceback.print_exc() self.connected_slot(True)
continue return True
self.device = dev
self.device_class = dev.__class__
self.connected_slot(True)
return True
finally:
if iswindows:
pythoncom.CoUninitialize()
return False return False
def connected_device_removed(self):
while True:
try:
job = self.jobs.get_nowait()
job.abort(Exception(_('Device no longer connected.')))
except Queue.Empty:
break
try:
self.connected_device.post_yank_cleanup()
except:
pass
if self.connected_device in self.ejected_devices:
self.ejected_devices.remove(self.connected_device)
else:
self.connected_slot(False)
self.connected_device = None
def detect_device(self): def detect_device(self):
self.scanner.rescan_pnp_ids = not self.is_device_connected
self.scanner.scan() self.scanner.scan()
connected_devices = [] if self.is_device_connected:
for device in self.devices: connected, detected_device = \
connected, detected_device = self.scanner.is_device_connected(device[0]) self.scanner.is_device_connected(self.connected_device)
if connected and not device[1] and not device[2]: if not connected:
# If connected and not showing in GUI and not ejected self.connected_device_removed()
connected_devices.append((device[0], detected_device)) else:
device[1] = True possibly_connected_devices = []
elif not connected and device[1]: for device in self.devices:
# Disconnected but showing in GUI if device in self.ejected_devices:
while True: continue
try: possibly_connected, detected_device = \
job = self.jobs.get_nowait() self.scanner.is_device_connected(device)
job.abort(Exception(_('Device no longer connected.'))) if possibly_connected:
except Queue.Empty: possibly_connected_devices.append((device, detected_device))
break if possibly_connected_devices:
try: if not self.do_connect(possibly_connected_devices):
self.device.post_yank_cleanup() print 'Connect to device failed, retying in 5 seconds...'
except: time.sleep(5)
pass if not self.do_connect(possibly_connected_devices):
device[2] = False print 'Device connect failed again, giving up'
self.device = None
self.connected_slot(False)
device[1] ^= True
if connected_devices:
if not self.do_connect(connected_devices):
print 'Connect to device failed, retying in 5 seconds...'
time.sleep(5)
if not self.do_connect(connected_devices):
print 'Device connect failed again, giving up'
def umount_device(self): def umount_device(self):
if self.device is not None: if self.is_device_connected:
self.device.eject() self.connected_device.eject()
dev = None self.ejected_devices.add(self.connected_device)
for x in self.devices:
if x[0] is self.device:
dev = x
break
if dev is not None:
dev[2] = True
self.connected_slot(False) self.connected_slot(False)
def next(self): def next(self):
if not self.jobs.empty(): if not self.jobs.empty():
try: try:
@ -165,18 +170,31 @@ class DeviceManager(Thread):
pass pass
def run(self): def run(self):
while self.keep_going: if iswindows:
self.detect_device() import pythoncom
while True: pythoncom.CoInitialize()
job = self.next() wmi = __import__('wmi', globals(), locals(), [], -1)
if job is not None: self.wmi = wmi.WMI(find_classes=False)
self.current_job = job self.scanner.wmi = self.wmi
self.device.set_progress_reporter(job.report_progress) for x in self.devices:
self.current_job.run() x[0].wmi = self.wmi
self.current_job = None try:
else: while self.keep_going:
break self.detect_device()
time.sleep(self.sleep_time) while True:
job = self.next()
if job is not None:
self.current_job = job
self.device.set_progress_reporter(job.report_progress)
self.current_job.run()
self.current_job = None
else:
break
time.sleep(self.sleep_time)
finally:
if iswindows:
pythoncom.CoUninitialize()
def create_job(self, func, done, description, args=[], kwargs={}): def create_job(self, func, done, description, args=[], kwargs={}):
job = DeviceJob(func, done, self.job_manager, job = DeviceJob(func, done, self.job_manager,
@ -495,7 +513,7 @@ class DeviceGUI(object):
fmt = None fmt = None
if specific: if specific:
d = ChooseFormatDialog(self, _('Choose format to send to device'), d = ChooseFormatDialog(self, _('Choose format to send to device'),
self.device_manager.device_class.settings().format_map) self.device_manager.device.settings().format_map)
d.exec_() d.exec_()
fmt = d.format().lower() fmt = d.format().lower()
dest, sub_dest = dest.split(':') dest, sub_dest = dest.split(':')
@ -637,7 +655,7 @@ class DeviceGUI(object):
p = QPixmap() p = QPixmap()
p.loadFromData(data) p.loadFromData(data)
if not p.isNull(): if not p.isNull():
ht = self.device_manager.device_class.THUMBNAIL_HEIGHT \ ht = self.device_manager.device.THUMBNAIL_HEIGHT \
if self.device_manager else DevicePlugin.THUMBNAIL_HEIGHT if self.device_manager else DevicePlugin.THUMBNAIL_HEIGHT
p = p.scaledToHeight(ht, Qt.SmoothTransformation) p = p.scaledToHeight(ht, Qt.SmoothTransformation)
return (p.width(), p.height(), pixmap_to_data(p)) return (p.width(), p.height(), pixmap_to_data(p))
@ -675,10 +693,11 @@ class DeviceGUI(object):
def sync_news(self, send_ids=None, do_auto_convert=True): def sync_news(self, send_ids=None, do_auto_convert=True):
if self.device_connected: if self.device_connected:
settings = self.device_manager.device.settings()
ids = list(dynamic.get('news_to_be_synced', set([]))) if send_ids is None else send_ids ids = list(dynamic.get('news_to_be_synced', set([]))) if send_ids is None else send_ids
ids = [id for id in ids if self.library_view.model().db.has_id(id)] ids = [id for id in ids if self.library_view.model().db.has_id(id)]
files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids( files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(
ids, self.device_manager.device_class.settings().format_map, ids, settings.format_map,
exclude_auto=do_auto_convert) exclude_auto=do_auto_convert)
auto = [] auto = []
if do_auto_convert and _auto_ids: if do_auto_convert and _auto_ids:
@ -687,12 +706,12 @@ class DeviceGUI(object):
formats = [] if dbfmts is None else \ formats = [] if dbfmts is None else \
[f.lower() for f in dbfmts.split(',')] [f.lower() for f in dbfmts.split(',')]
if set(formats).intersection(available_input_formats()) \ if set(formats).intersection(available_input_formats()) \
and set(self.device_manager.device_class.settings().format_map).intersection(available_output_formats()): and set(settings.format_map).intersection(available_output_formats()):
auto.append(id) auto.append(id)
if auto: if auto:
format = None format = None
for fmt in self.device_manager.device_class.settings().format_map: for fmt in settings.format_map:
if fmt in list(set(self.device_manager.device_class.settings().format_map).intersection(set(available_output_formats()))): if fmt in list(set(settings.format_map).intersection(set(available_output_formats()))):
format = fmt format = fmt
break break
if format is not None: if format is not None:
@ -738,8 +757,10 @@ class DeviceGUI(object):
if not self.device_manager or not ids or len(ids) == 0: if not self.device_manager or not ids or len(ids) == 0:
return return
settings = self.device_manager.device.settings()
_files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(ids, _files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(ids,
self.device_manager.device_class.settings().format_map, settings.format_map,
paths=True, set_metadata=True, paths=True, set_metadata=True,
specific_format=specific_format, specific_format=specific_format,
exclude_auto=do_auto_convert) exclude_auto=do_auto_convert)
@ -790,21 +811,21 @@ class DeviceGUI(object):
formats = self.library_view.model().db.formats(id, index_is_id=True) formats = self.library_view.model().db.formats(id, index_is_id=True)
formats = formats.split(',') if formats is not None else [] formats = formats.split(',') if formats is not None else []
formats = [f.lower().strip() for f in formats] formats = [f.lower().strip() for f in formats]
if list(set(formats).intersection(available_input_formats())) != [] and list(set(self.device_manager.device_class.settings().format_map).intersection(available_output_formats())) != []: if list(set(formats).intersection(available_input_formats())) != [] and list(set(settings.format_map).intersection(available_output_formats())) != []:
auto.append(id) auto.append(id)
else: else:
bad.append(self.library_view.model().db.title(id, index_is_id=True)) bad.append(self.library_view.model().db.title(id, index_is_id=True))
else: else:
if specific_format in list(set(self.device_manager.device_class.settings().format_map).intersection(set(available_output_formats()))): if specific_format in list(set(settings.format_map).intersection(set(available_output_formats()))):
auto.append(id) auto.append(id)
else: else:
bad.append(self.library_view.model().db.title(id, index_is_id=True)) bad.append(self.library_view.model().db.title(id, index_is_id=True))
if auto != []: if auto != []:
format = specific_format if specific_format in list(set(self.device_manager.device_class.settings().format_map).intersection(set(available_output_formats()))) else None format = specific_format if specific_format in list(set(settings.format_map).intersection(set(available_output_formats()))) else None
if not format: if not format:
for fmt in self.device_manager.device_class.settings().format_map: for fmt in settings.format_map:
if fmt in list(set(self.device_manager.device_class.settings().format_map).intersection(set(available_output_formats()))): if fmt in list(set(settings.format_map).intersection(set(available_output_formats()))):
format = fmt format = fmt
break break
if not format: if not format:

View File

@ -840,11 +840,11 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
return return
mainlist, cardalist, cardblist = job.result mainlist, cardalist, cardblist = job.result
self.memory_view.set_database(mainlist) self.memory_view.set_database(mainlist)
self.memory_view.set_editable(self.device_manager.device_class.CAN_SET_METADATA) self.memory_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
self.card_a_view.set_database(cardalist) self.card_a_view.set_database(cardalist)
self.card_a_view.set_editable(self.device_manager.device_class.CAN_SET_METADATA) self.card_a_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
self.card_b_view.set_database(cardblist) self.card_b_view.set_database(cardblist)
self.card_b_view.set_editable(self.device_manager.device_class.CAN_SET_METADATA) self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
for view in (self.memory_view, self.card_a_view, self.card_b_view): for view in (self.memory_view, self.card_a_view, self.card_b_view):
view.sortByColumn(3, Qt.DescendingOrder) view.sortByColumn(3, Qt.DescendingOrder)
view.read_settings() view.read_settings()