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,16 +54,14 @@ 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:
import pythoncom
pythoncom.CoInitialize()
try:
wmi = __import__('wmi', globals(), locals(), [], -1) wmi = __import__('wmi', globals(), locals(), [], -1)
Wmi = wmi.WMI(find_classes=False)
drives = [] drives = []
out('Drives detected:') out('Drives detected:')
out('\t', '(ID, Partitions, Drive letter)') out('\t', '(ID, Partitions, Drive letter)')
for drive in wmi.WMI(find_classes=False).Win32_DiskDrive(): for drive in Wmi.Win32_DiskDrive():
if drive.Partitions == 0: if drive.Partitions == 0:
continue continue
try: try:
@ -71,8 +73,6 @@ def debug(ioreg_to_tmp=False, buf=None):
drives.append((str(drive.PNPDeviceID), 'No mount points found')) drives.append((str(drive.PNPDeviceID), 'No mount points found'))
for drive in drives: for drive in drives:
out('\t', drive) 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,41 +85,37 @@ 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:
import pythoncom
pythoncom.CoInitialize()
try:
for dev, detected_device in connected_devices: for dev, detected_device in connected_devices:
dev.reset(detected_device=detected_device) dev.reset(detected_device=detected_device,
report_progress=self.report_progress)
try: try:
dev.open() dev.open()
except: except:
print 'Unable to open device', dev print 'Unable to open device', dev
traceback.print_exc() traceback.print_exc()
continue continue
self.device = dev self.connected_device = dev
self.device_class = dev.__class__
self.connected_slot(True) self.connected_slot(True)
return True return True
finally:
if iswindows:
pythoncom.CoUninitialize()
return False return False
def connected_device_removed(self):
def detect_device(self):
self.scanner.scan()
connected_devices = []
for device in self.devices:
connected, detected_device = self.scanner.is_device_connected(device[0])
if connected and not device[1] and not device[2]:
# If connected and not showing in GUI and not ejected
connected_devices.append((device[0], detected_device))
device[1] = True
elif not connected and device[1]:
# Disconnected but showing in GUI
while True: while True:
try: try:
job = self.jobs.get_nowait() job = self.jobs.get_nowait()
@ -130,33 +123,45 @@ class DeviceManager(Thread):
except Queue.Empty: except Queue.Empty:
break break
try: try:
self.device.post_yank_cleanup() self.connected_device.post_yank_cleanup()
except: except:
pass pass
device[2] = False if self.connected_device in self.ejected_devices:
self.device = None self.ejected_devices.remove(self.connected_device)
else:
self.connected_slot(False) self.connected_slot(False)
device[1] ^= True self.connected_device = None
if connected_devices:
if not self.do_connect(connected_devices): def detect_device(self):
self.scanner.rescan_pnp_ids = not self.is_device_connected
self.scanner.scan()
if self.is_device_connected:
connected, detected_device = \
self.scanner.is_device_connected(self.connected_device)
if not connected:
self.connected_device_removed()
else:
possibly_connected_devices = []
for device in self.devices:
if device in self.ejected_devices:
continue
possibly_connected, detected_device = \
self.scanner.is_device_connected(device)
if possibly_connected:
possibly_connected_devices.append((device, detected_device))
if possibly_connected_devices:
if not self.do_connect(possibly_connected_devices):
print 'Connect to device failed, retying in 5 seconds...' print 'Connect to device failed, retying in 5 seconds...'
time.sleep(5) time.sleep(5)
if not self.do_connect(connected_devices): if not self.do_connect(possibly_connected_devices):
print 'Device connect failed again, giving up' 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,6 +170,15 @@ class DeviceManager(Thread):
pass pass
def run(self): def run(self):
if iswindows:
import pythoncom
pythoncom.CoInitialize()
wmi = __import__('wmi', globals(), locals(), [], -1)
self.wmi = wmi.WMI(find_classes=False)
self.scanner.wmi = self.wmi
for x in self.devices:
x[0].wmi = self.wmi
try:
while self.keep_going: while self.keep_going:
self.detect_device() self.detect_device()
while True: while True:
@ -177,6 +191,10 @@ class DeviceManager(Thread):
else: else:
break break
time.sleep(self.sleep_time) 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()