Statistics
| Branch: | Tag: | Revision:

root / host / utils / usrp_n2xx_net_burner_gui.py @ 0df49b75

History | View | Annotate | Download (8.43 KB)

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

    
19
import usrp_n2xx_net_burner #import implementation
20
try:
21
    import tkinter, tkinter.filedialog, tkinter.font, tkinter.messagebox
22
except ImportError:
23
    import tkFileDialog, tkFont, tkMessageBox
24
    import Tkinter as tkinter
25
    tkinter.filedialog = tkFileDialog
26
    tkinter.font = tkFont
27
    tkinter.messagebox = tkMessageBox
28
import os
29

    
30
class BinFileEntry(tkinter.Frame):
31
    """
32
    Simple file entry widget for getting the file path of bin files.
33
    Combines a label, entry, and button with file dialog callback.
34
    """
35

    
36
    def __init__(self, root, what, def_path=''):
37
        self._what = what
38
        tkinter.Frame.__init__(self, root)
39
        tkinter.Label(self, text=what+":").pack(side=tkinter.LEFT)
40
        self._entry = tkinter.Entry(self, width=50)
41
        self._entry.insert(tkinter.END, def_path)
42
        self._entry.pack(side=tkinter.LEFT)
43
        tkinter.Button(self, text="...", command=self._button_cb).pack(side=tkinter.LEFT)
44

    
45
    def _button_cb(self):
46
        filename = tkinter.filedialog.askopenfilename(
47
            parent=self,
48
            filetypes=[('bin files', '*.bin'), ('all files', '*.*')],
49
            title="Select bin file for %s"%self._what,
50
            initialdir=os.path.dirname(self.get_filename()),
51
        )
52

    
53
        # open file on your own
54
        if filename:
55
            self._entry.delete(0, tkinter.END)
56
            self._entry.insert(0, filename)
57

    
58
    def get_filename(self):
59
        return self._entry.get()
60

    
61
class ProgressBar(tkinter.Canvas):
62
    """
63
    A simple implementation of a progress bar.
64
    Draws rectangle that fills from left to right.
65
    """
66

    
67
    def __init__(self, root, width=500, height=20):
68
        self._width = width
69
        self._height = height
70
        tkinter.Canvas.__init__(self, root, relief="sunken", borderwidth=2, width=self._width-2, height=self._height-2)
71
        self._last_fill_pixels = None
72
        self.set(0.0)
73

    
74
    def set(self, frac):
75
        """
76
        Update the progress where fraction is between 0.0 and 1.0
77
        """
78
        #determine the number of pixels to draw
79
        fill_pixels = int(round(self._width*frac))
80
        if fill_pixels == self._last_fill_pixels: return
81
        self._last_fill_pixels = fill_pixels
82

    
83
        #draw a rectangle representing the progress
84
        if frac: self.create_rectangle(0, 0, fill_pixels, self._height, fill="#357EC7")
85
        else:    self.create_rectangle(0, 0, self._width, self._height, fill="#E8E8E8")
86

    
87
class DeviceEntryWidget(tkinter.Frame):
88
    """
89
    Simple entry widget for getting the network device name.
90
    Combines a label, entry, and helpful text box with hints.
91
    """
92

    
93
    def __init__(self, root, text=''):
94
        tkinter.Frame.__init__(self, root)
95

    
96
        tkinter.Button(self, text="Rescan for Devices", command=self._reload_cb).pack()
97

    
98
        self._hints = tkinter.Listbox(self)
99
        self._hints.bind("<<ListboxSelect>>", self._listbox_cb)
100
        self._reload_cb()
101
        self._hints.pack(expand=tkinter.YES, fill=tkinter.X)
102

    
103
        frame = tkinter.Frame(self)
104
        frame.pack()
105

    
106
        tkinter.Label(frame, text="Network Address:").pack(side=tkinter.LEFT)
107
        self._entry = tkinter.Entry(frame, width=50)
108
        self._entry.insert(tkinter.END, text)
109
        self._entry.pack(side=tkinter.LEFT)
110

    
111
    def _reload_cb(self):
112
        self._hints.delete(0, tkinter.END)
113
        for hint in usrp_n2xx_net_burner.enumerate_devices():
114
            self._hints.insert(tkinter.END, hint)
115

    
116
    def _listbox_cb(self, event):
117
        try:
118
            sel = self._hints.get(self._hints.curselection()[0])
119
            self._entry.delete(0, tkinter.END)
120
            self._entry.insert(0, sel)
121
        except Exception as e: print(e)
122

    
123
    def get_devname(self):
124
        return self._entry.get()
125

    
126
class SectionLabel(tkinter.Label):
127
    """
128
    Make a text label with bold font.
129
    """
130

    
131
    def __init__(self, root, text):
132
        tkinter.Label.__init__(self, root, text=text)
133

    
134
        #set the font bold
135
        f = tkinter.font.Font(font=self['font'])
136
        f['weight'] = 'bold'
137
        self['font'] = f.name
138

    
139
class USRPN2XXNetBurnerApp(tkinter.Frame):
140
    """
141
    The top level gui application for the usrp-n2xx network burner.
142
    Creates entry widgets and button with callback to write images.
143
    """
144

    
145
    def __init__(self, root, addr, fw, fpga):
146

    
147
        tkinter.Frame.__init__(self, root)
148

    
149
        #pack the file entry widgets
150
        SectionLabel(self, text="Select Images").pack(pady=5)
151
        self._fw_img_entry = BinFileEntry(self, "Firmware Image", def_path=fw)
152
        self._fw_img_entry.pack()
153
        self._fpga_img_entry = BinFileEntry(self, "FPGA Image", def_path=fpga)
154
        self._fpga_img_entry.pack()
155

    
156
        #pack the destination entry widget
157
        SectionLabel(self, text="Select Device").pack(pady=5)
158
        self._net_dev_entry = DeviceEntryWidget(self, text=addr)
159
        self._net_dev_entry.pack()
160

    
161
        #the do it button
162
        SectionLabel(self, text="").pack(pady=5)
163
        button = tkinter.Button(self, text="Burn Images", command=self._burn)
164
        self._enable_input = lambda: button.configure(state=tkinter.NORMAL)
165
        self._disable_input = lambda: button.configure(state=tkinter.DISABLED)
166
        button.pack()
167

    
168
        #a progress bar to monitor the status
169
        progress_frame = tkinter.Frame(self)
170
        progress_frame.pack()
171
        self._status = tkinter.StringVar()
172
        tkinter.Label(progress_frame, textvariable=self._status).pack(side=tkinter.LEFT)
173
        self._pbar = ProgressBar(progress_frame)
174
        self._pbar.pack(side=tkinter.RIGHT, expand=True)
175

    
176
    def _burn(self):
177
        #grab strings from the gui
178
        fw = self._fw_img_entry.get_filename()
179
        fpga = self._fpga_img_entry.get_filename()
180
        addr = self._net_dev_entry.get_devname()
181

    
182
        #check input
183
        if not addr:
184
            tkinter.messagebox.showerror('Error:', 'No address specified!')
185
            return
186
        if not fw and not fpga:
187
            tkinter.messagebox.showerror('Error:', 'No images specified!')
188
            return
189
        if fw and not os.path.exists(fw):
190
            tkinter.messagebox.showerror('Error:', 'Firmware image not found!')
191
            return
192
        if fpga and not os.path.exists(fpga):
193
            tkinter.messagebox.showerror('Error:', 'FPGA image not found!')
194
            return
195

    
196
        self._disable_input()
197
        try:
198
            #make a new burner object and attempt the burner operation
199
            burner = usrp_n2xx_net_burner.burner_socket(addr=addr)
200

    
201
            for (image_type, fw_img, fpga_img) in (('FPGA', '', fpga), ('Firmware', fw, '')):
202
                #setup callbacks that update the gui
203
                def status_cb(status):
204
                    self._pbar.set(0.0) #status change, reset the progress
205
                    self._status.set("%s %s "%(status.title(), image_type))
206
                    self.update()
207
                def progress_cb(progress):
208
                    self._pbar.set(progress)
209
                    self.update()
210
                burner.set_callbacks(progress_cb=progress_cb, status_cb=status_cb)
211
                burner.burn_fw(fw=fw_img, fpga=fpga_img, reset=False, safe=False)
212

    
213
            if tkinter.messagebox.askyesno("Burn was successful!", "Reset the device?"):
214
                burner.reset_usrp()
215

    
216
        except Exception as e:
217
            tkinter.messagebox.showerror('Verbose:', 'Error: %s'%str(e))
218

    
219
        #reset the progress bar
220
        self._pbar.set(0.0)
221
        self._status.set("")
222
        self._enable_input()
223

    
224
########################################################################
225
# main
226
########################################################################
227
if __name__=='__main__':
228
    options = usrp_n2xx_net_burner.get_options()
229
    root = tkinter.Tk()
230
    root.title('USRP-N2XX Net Burner')
231
    USRPN2XXNetBurnerApp(root, addr=options.addr, fw=options.fw, fpga=options.fpga).pack()
232
    root.mainloop()
233
    exit()