Project

General

Profile

Bug #2019 » clone.py

Filippi Marco, 12/29/2014 02:31 AM

 
1
# Copyright 2008 Dan Smith <dsmith@danplanet.com>
2
#
3
# This program is free software: you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation, either version 3 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
15

    
16
import threading
17
import os
18

    
19
import gtk
20
import gobject
21

    
22
from chirp import platform, directory, detect, chirp_common
23
from chirpui import miscwidgets, cloneprog, inputdialog, common, config
24

    
25
AUTO_DETECT_STRING = "Auto Detect (Icom Only)"
26

    
27
class CloneSettings:
28
    def __init__(self):
29
        self.port = None
30
        self.radio_class = None
31

    
32
    def __str__(self):
33
        s = ""
34
        if self.radio_class:
35
            return _("{vendor} {model} on {port}").format(\
36
                vendor=self.radio_class.VENDOR,
37
                model=self.radio_class.MODEL,
38
                port=self.port)
39

    
40
class CloneSettingsDialog(gtk.Dialog):
41
    def __make_field(self, label, widget):
42
        l = gtk.Label(label)
43
        self.__table.attach(l, 0, 1, self.__row, self.__row+1)
44
        self.__table.attach(widget, 1, 2, self.__row, self.__row+1)
45
        self.__row += 1
46

    
47
        l.show()
48
        widget.show()
49

    
50
    def __make_port(self, port):
51
        conf = config.get("state")
52

    
53
        ports = platform.get_platform().list_serial_ports()
54
        if not port:
55
            if conf.get("last_port"):
56
                port = conf.get("last_port")
57
            elif ports:
58
                port = ports[0]
59
            else:
60
                port = ""
61
            if not port in ports:
62
                ports.insert(0, port)
63

    
64
        return miscwidgets.make_choice(ports, True, port)
65

    
66
    def __make_model(self):
67
        return miscwidgets.make_choice([], False)
68

    
69
    def __make_vendor(self, model):
70
        vendors = {}
71
        for rclass in sorted(directory.DRV_TO_RADIO.values()):
72
            if not issubclass(rclass, chirp_common.CloneModeRadio) and \
73
                    not issubclass(rclass, chirp_common.LiveRadio):
74
                continue
75

    
76
            if not vendors.has_key(rclass.VENDOR):
77
                vendors[rclass.VENDOR] = []
78

    
79
            vendors[rclass.VENDOR].append(rclass)
80

    
81
        self.__vendors = vendors
82

    
83
        conf = config.get("state")
84
        if not conf.get("last_vendor"):
85
            conf.set("last_vendor", sorted(vendors.keys())[0])
86

    
87
        last_vendor = conf.get("last_vendor")
88
        if last_vendor not in vendors.keys():
89
            last_vendor = vendors.keys()[0]
90

    
91
        v = miscwidgets.make_choice(sorted(vendors.keys()), False, last_vendor)
92

    
93
        def _changed(box, vendors, model):
94
            models = vendors[box.get_active_text()]
95

    
96
            added_models = []
97

    
98
            model.get_model().clear()
99
            for rclass in sorted(models, key=lambda c: c.__name__):
100
                if rclass.MODEL not in added_models:
101
                    model.append_text(rclass.MODEL)
102
                    added_models.append(rclass.MODEL)
103

    
104
            if box.get_active_text() in detect.DETECT_FUNCTIONS:
105
                model.insert_text(0, _("Detect"))
106
                added_models.insert(0, _("Detect"))
107

    
108
            model_names = [x.MODEL for x in models]
109
            if conf.get("last_model") in model_names:
110
                model.set_active(added_models.index(conf.get("last_model")))
111
            else:
112
                model.set_active(0)
113

    
114
        v.connect("changed", _changed, vendors, model)
115
        _changed(v, vendors, model)
116

    
117
        return v
118

    
119
    def __make_ui(self, settings):
120
        self.__table = gtk.Table(3, 2)
121
        self.__table.set_row_spacings(3)
122
        self.__table.set_col_spacings(10)
123
        self.__row = 0
124

    
125
        self.__port = self.__make_port(settings and settings.port or None)
126
        self.__modl = self.__make_model()
127
        self.__vend = self.__make_vendor(self.__modl)
128

    
129
        self.__make_field(_("Port"), self.__port)
130
        self.__make_field(_("Vendor"), self.__vend)
131
        self.__make_field(_("Model"), self.__modl)
132

    
133
        if settings and settings.radio_class:
134
            common.combo_select(self.__vend, settings.radio_class.VENDOR)
135
            self.__modl.get_model().clear()
136
            self.__modl.append_text(settings.radio_class.MODEL)
137
            common.combo_select(self.__modl, settings.radio_class.MODEL)
138
            self.__vend.set_sensitive(False)
139
            self.__modl.set_sensitive(False)
140
            self.__prompt_force_after = gtk.CheckButton()
141
            try:
142
                self.__prompt_force_after.set_tooltip_text(
143
                    _("Try this if download work but upload fails immediately"))
144
            except:
145
                pass
146
            self.__prompt_force_after.set_active(
147
                    config.get("upload_prompt").get_bool("force_after"))
148
            self.__make_field(
149
                    _("Force alternative\nopen schema"),
150
                    self.__prompt_force_after)
151

    
152
        self.__table.show()
153
        self.vbox.pack_start(self.__table, 1, 1, 1)
154

    
155
    def __init__(self, settings=None, parent=None, title=_("Radio")):
156
        buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
157
                   gtk.STOCK_OK, gtk.RESPONSE_OK)
158
        gtk.Dialog.__init__(self, title,
159
                            parent=parent,
160
                            flags=gtk.DIALOG_MODAL)
161
        self.__make_ui(settings)
162
        self.__cancel_button = self.add_button(gtk.STOCK_CANCEL,
163
                                               gtk.RESPONSE_CANCEL)
164
        self.__okay_button = self.add_button(gtk.STOCK_OK,
165
                                             gtk.RESPONSE_OK)
166
        self.__okay_button.grab_default()
167
        self.__okay_button.grab_focus()
168

    
169
    def run(self):
170
        r = gtk.Dialog.run(self)
171
        if r != gtk.RESPONSE_OK:
172
            return None
173

    
174
        vendor = self.__vend.get_active_text()
175
        model = self.__modl.get_active_text()
176

    
177
        cs = CloneSettings()
178
        cs.port = self.__port.get_active_text()
179
        if model == _("Detect"):
180
            try:
181
                cs.radio_class = detect.DETECT_FUNCTIONS[vendor](cs.port)
182
                if not cs.radio_class:
183
                    raise Exception(_("Unable to detect radio on {port}").format(port=cs.port))
184
            except Exception, e:
185
                d = inputdialog.ExceptionDialog(e)
186
                d.run()
187
                d.destroy()
188
                return None
189
        else:
190
            for rclass in directory.DRV_TO_RADIO.values():
191
                if rclass.MODEL == model:
192
                    cs.radio_class = rclass
193
                    break
194
            if not cs.radio_class:
195
                common.show_error(_("Internal error: Unable to upload to {model}").format(model=model))
196
                print self.__vendors
197
                return None
198

    
199
        conf = config.get("state")
200
        conf.set("last_port", cs.port)
201
        conf.set("last_vendor", cs.radio_class.VENDOR)
202
        conf.set("last_model", model)
203
        try:
204
            if self.__prompt_force_after != None:
205
                config.get("upload_prompt").set_bool("force_after", self.__prompt_force_after.get_active())
206
        except:
207
                pass
208

    
209
        return cs
210

    
211
class CloneCancelledException(Exception):
212
    pass
213

    
214
class CloneThread(threading.Thread):
215
    def __status(self, status):
216
        gobject.idle_add(self.__progw.status, status)
217

    
218
    def __init__(self, radio, direction, cb=None, parent=None):
219
        threading.Thread.__init__(self)
220

    
221
        self.__radio = radio
222
        self.__out = direction == "out"
223
        self.__cback = cb
224
        self.__cancelled = False
225

    
226
        self.__progw = cloneprog.CloneProg(parent=parent, cancel=self.cancel)
227

    
228
    def cancel(self):
229
        self.__radio.pipe.close()
230
        self.__cancelled = True
231

    
232
    def run(self):
233
        print "Clone thread started"
234

    
235
        gobject.idle_add(self.__progw.show)
236

    
237
        self.__radio.status_fn = self.__status
238

    
239
        try:
240
            if self.__out:
241
                self.__radio.sync_out()
242
            else:
243
                self.__radio.sync_in()
244

    
245
            emsg = None
246
        except Exception, e:
247
            common.log_exception()
248
            print _("Clone failed: {error}").format(error=e)
249
            emsg = e
250

    
251
        gobject.idle_add(self.__progw.hide)
252

    
253
        # NB: Compulsory close of the radio's serial connection
254
        self.__radio.pipe.close()
255

    
256
        print "Clone thread ended"
257

    
258
        if self.__cback and not self.__cancelled:
259
            gobject.idle_add(self.__cback, self.__radio, emsg)
260

    
261
if __name__ == "__main__":
262
    d = CloneSettingsDialog("/dev/ttyUSB0")
263
    r = d.run()
264
    print r
(2-2/3)