~ubuntu-branches/ubuntu/jaunty/calibre/jaunty-backports

« back to all changes in this revision

Viewing changes to src/calibre/gui2/device.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-01-20 17:14:02 UTC
  • Revision ID: james.westby@ubuntu.com-20090120171402-8y3znf6nokwqe80k
Tags: upstream-0.4.125+dfsg
ImportĀ upstreamĀ versionĀ 0.4.125+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
__license__   = 'GPL v3'
 
2
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
 
3
import os, traceback, Queue, time
 
4
from threading import Thread
 
5
 
 
6
from calibre.devices import devices
 
7
from calibre.parallel import Job
 
8
from calibre.devices.scanner import DeviceScanner
 
9
 
 
10
        
 
11
class DeviceJob(Job):
 
12
    
 
13
    def __init__(self, func, *args, **kwargs):
 
14
        Job.__init__(self, *args, **kwargs)
 
15
        self.func = func
 
16
        
 
17
    def run(self):
 
18
        self.start_work()
 
19
        try:
 
20
            self.result = self.func(*self.args, **self.kwargs)
 
21
        except (Exception, SystemExit), err:
 
22
            self.exception = err
 
23
            self.traceback = traceback.format_exc()
 
24
        finally:
 
25
            self.job_done()
 
26
        
 
27
 
 
28
class DeviceManager(Thread):
 
29
    '''
 
30
    Worker thread that polls the USB ports for devices. Emits the
 
31
    signal connected(PyQt_PyObject, PyQt_PyObject) on connection and
 
32
    disconnection events.
 
33
    '''
 
34
    def __init__(self, connected_slot, job_manager, sleep_time=2):
 
35
        '''        
 
36
        @param sleep_time: Time to sleep between device probes in millisecs
 
37
        @type sleep_time: integer
 
38
        '''
 
39
        Thread.__init__(self)
 
40
        self.setDaemon(True)
 
41
        self.devices        = [[d, False] for d in devices()]
 
42
        self.device         = None
 
43
        self.device_class   = None
 
44
        self.sleep_time     = sleep_time
 
45
        self.connected_slot = connected_slot
 
46
        self.jobs           = Queue.Queue(0)
 
47
        self.keep_going     = True
 
48
        self.job_manager    = job_manager
 
49
        self.current_job    = None
 
50
        self.scanner        = DeviceScanner()
 
51
        
 
52
    def detect_device(self):
 
53
        self.scanner.scan()
 
54
        for device in self.devices:
 
55
            connected = self.scanner.is_device_connected(device[0])
 
56
            if connected and not device[1]:
 
57
                try:
 
58
                    dev = device[0]()
 
59
                    dev.open()
 
60
                    self.device       = dev
 
61
                    self.device_class = dev.__class__
 
62
                    self.connected_slot(True)
 
63
                except:
 
64
                    print 'Unable to open device'
 
65
                    traceback.print_exc()
 
66
                finally:                        
 
67
                    device[1] = True                    
 
68
            elif not connected and device[1]:
 
69
                while True:
 
70
                    try:
 
71
                        job = self.jobs.get_nowait()
 
72
                        job.abort(Exception(_('Device no longer connected.')))
 
73
                    except Queue.Empty:
 
74
                        break
 
75
                self.device = None
 
76
                self.connected_slot(False)
 
77
                device[1] ^= True
 
78
                
 
79
    def next(self):
 
80
        if not self.jobs.empty():
 
81
            try:
 
82
                return self.jobs.get_nowait()
 
83
            except Queue.Empty:
 
84
                pass
 
85
    
 
86
    def run(self):
 
87
        while self.keep_going:
 
88
            self.detect_device()
 
89
            while True:
 
90
                job = self.next()
 
91
                if job is not None:
 
92
                    self.current_job = job
 
93
                    self.device.set_progress_reporter(job.update_status)
 
94
                    self.current_job.run()
 
95
                    self.current_job = None
 
96
                else:
 
97
                    break 
 
98
            time.sleep(self.sleep_time)
 
99
            
 
100
    def create_job(self, func, done, description, args=[], kwargs={}):
 
101
        job = DeviceJob(func, done, self.job_manager, 
 
102
                        args=args, kwargs=kwargs, description=description)
 
103
        self.job_manager.add_job(job)
 
104
        self.jobs.put(job)
 
105
        return job
 
106
    
 
107
    def _get_device_information(self):
 
108
        info = self.device.get_device_information(end_session=False)
 
109
        info = [i.replace('\x00', '').replace('\x01', '') for i in info]
 
110
        cp = self.device.card_prefix(end_session=False)
 
111
        fs = self.device.free_space()
 
112
        return info, cp, fs
 
113
    
 
114
    def get_device_information(self, done):
 
115
        '''Get device information and free space on device'''
 
116
        return self.create_job(self._get_device_information, done, 
 
117
                    description=_('Get device information'))
 
118
        
 
119
    
 
120
    def _books(self):
 
121
        '''Get metadata from device'''
 
122
        mainlist = self.device.books(oncard=False, end_session=False)
 
123
        cardlist = self.device.books(oncard=True)
 
124
        return (mainlist, cardlist)
 
125
    
 
126
    def books(self, done):
 
127
        '''Return callable that returns the list of books on device as two booklists'''
 
128
        return self.create_job(self._books, done, description=_('Get list of books on device'))
 
129
    
 
130
    def _sync_booklists(self, booklists):
 
131
        '''Sync metadata to device'''
 
132
        self.device.sync_booklists(booklists, end_session=False)
 
133
        return self.device.card_prefix(end_session=False), self.device.free_space()
 
134
    
 
135
    def sync_booklists(self, done, booklists):
 
136
        return self.create_job(self._sync_booklists, done, args=[booklists],
 
137
                        description=_('Send metadata to device'))
 
138
    
 
139
    def _upload_books(self, files, names, on_card=False):
 
140
        '''Upload books to device: '''
 
141
        return self.device.upload_books(files, names, on_card, end_session=False)
 
142
        
 
143
    def upload_books(self, done, files, names, on_card=False, titles=None):
 
144
        desc = _('Upload %d books to device')%len(names)
 
145
        if titles:
 
146
            desc += u':' + u', '.join(titles)
 
147
        return self.create_job(self._upload_books, done, args=[files, names], 
 
148
                kwargs={'on_card':on_card}, description=desc)
 
149
        
 
150
    def add_books_to_metadata(self, locations, metadata, booklists):
 
151
        self.device.add_books_to_metadata(locations, metadata, booklists)
 
152
    
 
153
    def _delete_books(self, paths):
 
154
        '''Remove books from device'''
 
155
        self.device.delete_books(paths, end_session=True)
 
156
        
 
157
    def delete_books(self, done, paths):
 
158
        return self.create_job(self._delete_books, done, args=[paths],
 
159
                        description=_('Delete books from device'))
 
160
    
 
161
    def remove_books_from_metadata(self, paths, booklists):
 
162
        self.device.remove_books_from_metadata(paths, booklists)
 
163
        
 
164
    def _save_books(self, paths, target):
 
165
        '''Copy books from device to disk'''
 
166
        for path in paths:
 
167
            name = path.rpartition('/')[2]
 
168
            f = open(os.path.join(target, name), 'wb')
 
169
            self.device.get_file(path, f)
 
170
            f.close()
 
171
        
 
172
    def save_books(self, done, paths, target):
 
173
        return self.create_job(self._save_books, done, args=[paths, target],
 
174
                        description=_('Download books from device'))
 
175
    
 
176
    def _view_book(self, path, target):
 
177
        f = open(target, 'wb')
 
178
        self.device.get_file(path, f)
 
179
        f.close()
 
180
        return target
 
181
    
 
182
    def view_book(self, done, path, target):
 
183
        return self.create_job(self._view_book, done, args=[path, target],
 
184
                        description=_('View book on device'))
 
185
        
 
 
b'\\ No newline at end of file'