class AudioJackGUI(object):
    def __init__(self, master):
        self.root = master
        self.searching = False
        self.can_download = True
        audiojack.set_useragent('AudioJack', '1.0')
        self.frame = ScrollableFrame(self.root)
        self.frame.setconfig(bg='#0D47A1', width=1280, height=720)
        self.frame.pack(side=TOP, fill=BOTH, expand=1)
        self.label = Label(self.frame.mainframe, text='AudioJack', fg='#ffffff', bg=self.frame.mainframe['background'], font=('Segoe UI', 48))
        self.label.pack()
        self.url_entry = Entry(self.frame.mainframe, width=48, font=('Segoe UI', 20), bg='#1565C0', bd=2, highlightthickness=1, highlightcolor='#1565C0', highlightbackground='#0D47A1', fg='#ffffff', insertbackground='#ffffff', relief=FLAT, insertwidth=1)
        self.url_entry.pack()
        self.submit_button = Button(self.frame.mainframe, width=60, font=('Segoe UI', 16), text='Go!', bd=0, bg='#1E88E5', fg='#ffffff', activebackground='#2196F3', activeforeground='#ffffff', relief=SUNKEN, cursor='hand2', command=self.submit)
        self.submit_button.pack()

        self.search_progress = Progressbar(self.frame.mainframe, orient='horizontal', length=720, maximum=100 ,mode='indeterminate')

        self.error_info = Label(self.frame.mainframe, fg='#ff0000', bg=self.frame.mainframe['background'])

        # Use pack_forget on this to reset the view
        self.contents = Frame(self.frame.mainframe, bg=self.frame.mainframe['background'])

        # Contains results and custom tag options
        self.select_frame = Frame(self.contents, bg=self.frame.mainframe['background'])
        self.select_frame.pack()

        #Search results
        self.results_label = Label(self.select_frame, text='Results:', fg='#ffffff', bg=self.frame.mainframe['background'])
        self.results_frame = Frame(self.select_frame, bg=self.frame.mainframe['background'])
        self.results_label.pack()
        self.results_frame.pack()

        # Downloads
        self.file_label = Label(self.contents, fg='#ffffff', bg=self.frame.mainframe['background'])

    def submit(self):
        self.searching = True

        self.error_info.pack_forget()
        self.error_info.config(text='')
        self.contents.pack_forget()
        self.reset_results_frame()
        self.file_label.config(text='')

        self.results_queue = Queue.Queue()
        t = Thread(target=self.search)
        t.daemon = True
        t.start()
        self.submit_button.pack_forget()
        self.search_progress.pack()
        self.search_progress.start(10)
        self.root.after(100, self.handle_results)

    def search(self):
        url = self.url_entry.get()
        try:
            self.results_queue.put(audiojack.get_results(url))
        except Exception as e:
            self.results_queue.put([])

    def handle_results(self):
        try:
            results = self.results_queue.get(0)
            self.reset_results_frame()
            self.search_progress.pack_forget()
            self.submit_button.pack()
            if results == []:
                self.error_info.config(text='No results found.')
                self.error_info.pack()
            else:
                for i, entry in enumerate(results):
                    self.get_result_box(entry).grid(row=i / 4, column=i % 4)
                self.contents.pack()
                self.select_frame.pack()
        except Queue.Empty:
            self.root.after(100, self.handle_results)

    def reset_results_frame(self):
        for result in self.results_frame.winfo_children():
            result.destroy()

    def get_result_box(self, entry):
        try:
            text ='%s\n%s\n%s' % (entry['title'], entry['artist'], entry['album'])
            raw_image = Image.open(BytesIO(entry['img'].decode('base64')))
            side = self.frame.mainframe.winfo_reqwidth() / 4
            image_data = raw_image.resize((side, side), Image.ANTIALIAS)
            image = ImageTk.PhotoImage(image=image_data)
            frame = Frame(self.results_frame)
            button = Button(frame, fg='#ffffff', text=text, image=image, compound=CENTER, bg=self.frame.mainframe['background'], command=lambda: self.select(entry))
            button.image = image
            button.pack(fill=BOTH, expand=1)
            return frame
        except Exception as e:
            print e
            print type(e)

    def select(self, entry):
        if self.can_download:
            self.can_download = False
            self.searching = False
            self.download_queue = Queue.Queue()
            t = Thread(target=lambda: self.get_select(entry))
            t.daemon = True
            t.start()
            self.root.after(100, self.handle_download)

    def get_select(self, entry):
        try:
            self.download_queue.put(audiojack.select(entry))
        except Exception:
            self.download_queue.put('')

    def handle_download(self):
            try:
                file = self.download_queue.get(0)
                self.can_download = True
                if not self.searching:
                    label_text = 'Downloaded %s' % file
                    self.select_frame.pack_forget()
                    self.file_label.config(text=label_text)
                    self.file_label.pack()
                    self.contents.pack()
            except Queue.Empty:
                self.root.after(100, self.handle_download)
Example #2
0
class FdaAltimeterControl(tk.Toplevel):

    PORT_SELECTION_FRAME_HEIGHT = 80
    SUB_FRAME_X_PADDING = 20

    def __init__(self, parent):
        tk.Toplevel.__init__(self)
        self.parent = parent

        # Do not let user to close this window
        # is a communication with the Altimeter
        # is taking place.
        self.communicating = False
        self.protocol("WM_DELETE_WINDOW", self.close_asked)

        # Setup window content.
        self.initialize()

    def initialize(self):
        self.title(_(u'FlyDream Altimeter - Device Controller'))

        # Set fixed size.
        self.minsize(640, 320)
        self.resizable(False, False)

        # Altimeter local device.
        frame = tk.Frame(self, height=self.PORT_SELECTION_FRAME_HEIGHT)
        frame.pack(fill=tk.BOTH, expand=tk.YES)

        tk.Label(frame, text=_(u'Altimeter plugged on:')).pack(side=tk.LEFT)

        self.detect = tk.Button(frame, text=_(u'Refresh'),
                command=self.refresh_serial_ports)
        self.detect.pack(side=tk.RIGHT)

        self.port = tk.StringVar(self)
        self.ports = tk.OptionMenu(frame, self.port,
                _(u'Detecting serial ports...'))
        self.ports.pack(fill=tk.X, expand=tk.YES)

        # Update possible serial ports.
        self.refresh_serial_ports()

        # Upload altimeter flight data.
        frame = tk.Frame(self, padx=self.SUB_FRAME_X_PADDING)
        frame.pack(fill=tk.BOTH, expand=tk.YES)

        label = tk.Label(frame, anchor=tk.W, text=_(u'Upload'))
        label.pack(side=tk.TOP, fill=tk.X)

        # Setup bold font for titles.
        f = Font(font=label['font'])
        f['weight'] = 'bold'
        label['font'] = f.name

        Separator(frame).pack(fill=tk.X)

        self.progressbar = Progressbar(frame, orient='horizontal',
                mode='determinate')
        self.progressbar.pack(side=tk.BOTTOM, fill=tk.X)

        # Do not show progressbar
        # unless data is uploaded.
        self.hide_progressbar()

        self.upload_info = tk.StringVar()
        self.upload_info.set('/')
        self.info_label = tk.Label(frame, anchor=tk.NE, fg='darkgrey',
                textvariable=self.upload_info)
        self.info_label.pack(side=tk.BOTTOM, fill=tk.BOTH)

        self.label = tk.Label(frame, anchor=tk.W,
                text=_(u'Tell the altimeter to send flight '
                    'data to your computer'))
        self.label.pack(side=tk.LEFT)

        self.upload = tk.Button(frame, text=_(u'Upload data'),
                command=self.upload)
        self.upload.pack(side=tk.RIGHT)

        # Erase altimeter flight data.
        frame = tk.Frame(self, padx=self.SUB_FRAME_X_PADDING)
        frame.pack(fill=tk.BOTH, expand=tk.YES)

        label = tk.Label(frame, anchor=tk.W, text=_(u'Clear'))
        label['font'] = f.name
        label.pack(side=tk.TOP, fill=tk.X)

        Separator(frame).pack(fill=tk.X)

        label = tk.Label(frame, anchor=tk.W,
                text=_(u'Delete all the flight data from your altimeter'))
        label.pack(side=tk.LEFT)

        self.erase = tk.Button(frame, text=_(u'Erase data'),
                command=self.erase)
        self.erase.pack(side=tk.RIGHT)

        # Setup altimeter sampling frequency.
        frame = tk.Frame(self, padx=self.SUB_FRAME_X_PADDING)
        frame.pack(fill=tk.BOTH, expand=tk.YES)

        label = tk.Label(frame, anchor=tk.W, text=_(u'Configure'))
        label['font'] = f.name
        label.pack(side=tk.TOP, fill=tk.X)

        Separator(frame).pack(fill=tk.X)

        self.frequency = tk.StringVar(self)
        self.frequency.set('1')
        self.frequencies = tk.OptionMenu(frame, self.frequency,
                '1', '2', '4', '8')
        self.frequencies.pack(side=tk.LEFT)

        label = tk.Label(frame, text=_(u'records per second'))
        label.pack(side=tk.LEFT)

        self.setup = tk.Button(frame, text=_(u'Set sampling frequency'),
                command=self.set_frequency)
        self.setup.pack(side=tk.RIGHT)

    def allow_user_interactions(self, allow=True):
        state = tk.NORMAL if allow else tk.DISABLED
        self.ports.configure(state=state)
        self.detect.configure(state=state)
        self.upload.configure(state=state)
        self.erase.configure(state=state)
        self.frequencies.configure(state=state)
        self.setup.configure(state=state)

    def close_asked(self):
        if not self.communicating:
            self.destroy()
            return
        else:
            showwarning(_(u'Warning'),
                    _(u"""The application is communicating with the altimeter.

Please wait until communication is finished before closing this window."""))

    def hide_progressbar(self):
        self.progressbar.prev_pack = self.progressbar.pack_info()
        self.progressbar.pack_forget()

        self.update()

        # Reset progressbar for next upload.
        self.progressbar['value'] = 0

    def show_progressbar(self):
        # Well, Tkinter pack does not seem to
        # make it easy to simply hide a widget...
        #
        # I may be missing something.
        prev_info_label_packinfo = self.info_label.pack_info()
        prev_label_packinfo = self.label.pack_info()
        prev_upload_packinfo = self.upload.pack_info()

        self.info_label.pack_forget()
        self.label.pack_forget()
        self.upload.pack_forget()

        self.progressbar.pack(self.progressbar.prev_pack)

        self.info_label.pack(prev_info_label_packinfo)
        self.label.pack(prev_label_packinfo)
        self.upload.pack(prev_upload_packinfo)

        self.update()

    def upload_progressed(self, read, total):
        self.progressbar['maximum'] = total
        self.progressbar['value'] = read

        info = _(u'Please wait, %d bytes read out of %d') % (read, total)
        self.upload_info.set(info)

        self.update()

    def default_filename(self):
        return time.strftime('%Y-%m-%d %H-%M-%S', time.localtime()) \
                + '_flight'

    def upload(self):
        # TODO Add please wait message.

        # Update window state.
        self.show_progressbar()
        self.allow_user_interactions(False)
        self.communicating = True

        # Get flight data.
        port = self.port.get()
        altimeter = Altimeter(port)
        try:
            raw_data = altimeter.upload(self.upload_progressed)
        except FlyDreamAltimeterSerialPortError:
            self.show_unfound_altimeter(port)
        except (FlyDreamAltimeterReadError, FlyDreamAltimeterWriteError) as e:
            self.show_readwrite_error(port, e.message)
        except FlyDreamAltimeterProtocolError as e:
            self.show_protocol_error(port, e.message)
        else:
            # Check received data.
            if len(raw_data.data) == 0:
                showinfo(_(u'Upload Data'),
                        _(u'Altimeter contains no data.'))
            else:
                filename = self.write_flight_data(raw_data)
                self.suggest_open_in_viewer(filename)
        finally:
            # Restore window state.
            self.communicating = False
            self.allow_user_interactions()
            self.hide_progressbar()

        # TODO Update altimeter information.
        self.upload_info.set(_(u'Done'))

    def write_flight_data(self, raw_data):
        # Let user choose a file name.
        #
        # When cancelled, None is returned.
        fname = asksaveasfilename(
                    filetypes=((_(u'Flydream Altimeter Data'), '*.fda'),
                               (_(u'All files'), '*.*')),
                    title=_(u'Save flight data...'),
                    initialdir='~',
                    initialfile=self.default_filename(),
                    defaultextension=RAW_FILE_EXTENSION)

        if fname:
            # Try to remove previous file.
            #
            # User has been warned by asksaveasfilename
            # if she picked an existing file name.
            try:
                os.remove(fname)
            except OSError as e:
                # errno.ENOENT is 'no such file or directory'.
                #
                # Raise if another kind of error occurred.
                if e.errno != errno.ENOENT:
                    raise

            # This method raises an exception if file already exists.
            raw_data.to_file(fname)

        return fname

    def suggest_open_in_viewer(self, filename):
        # User may want to see latest uploaded data.
        if filename:
            reply = askokcancel(_(u'Open in viewer'),
                _(u'Open saved flight data in file viewer?'))
            if reply:
                self.parent.load_file(filename)

    def erase(self):
        reply = askokcancel(_(u'Erase flight data'),
                _(u'Really erase all flight data in your altimeter?'))
        if reply:
            self.do_erase()

    def do_erase(self):
        # TODO Make message stay while erasing is performed.
        showwarning(_(u'Erasing...'),
                _(u'Do not disconnect USB until altimeter blue LED '
                'lights again.'))

        # Update window state.
        self.allow_user_interactions(False)
        self.communicating = True

        # Reset altimeter content.
        port = self.port.get()
        altimeter = Altimeter(port)
        try:
            altimeter.clear()
        except FlyDreamAltimeterSerialPortError:
            self.show_unfound_altimeter(port)
        except (FlyDreamAltimeterReadError, FlyDreamAltimeterWriteError) as e:
            self.show_readwrite_error(port, e.message)
        except FlyDreamAltimeterProtocolError as e:
            self.show_protocol_error(port, e.message)
        else:
            # Update altimeter information.
            self.upload_info.set(_(u'Altimeter content erased'))
        finally:
            # Restore window state.
            self.communicating = False
            self.allow_user_interactions()

    def set_frequency(self):
        # This request is almost immediate,
        # so let's not bother user with yet
        # another message box.

        # Update window state.
        self.allow_user_interactions(False)
        self.communicating = True

        # Change altimeter sampling frequency.
        freq = int(self.frequency.get())
        port = self.port.get()
        altimeter = Altimeter(port)
        try:
            altimeter.setup(freq)
        except FlyDreamAltimeterSerialPortError:
            self.show_unfound_altimeter(port)
        except (FlyDreamAltimeterReadError, FlyDreamAltimeterWriteError) as e:
            self.show_readwrite_error(port, e.message)
        except FlyDreamAltimeterProtocolError as e:
            self.show_protocol_error(port, e.message)
        else:
            # Update altimeter information.
            self.upload_info.set(_(u'Altimeter sampling frequency set'))
        finally:
            # Restore window state.
            self.communicating = False
            self.allow_user_interactions()

    def show_unfound_altimeter(self, port):
        showwarning(_(u'Sampling Frequency'),
                    _(u"""Can not open port: %s

Please ensure that:
 - your altimeter is plugged to the USB adapter.
 - the USB adapter is plugged to your computer.
 - the choosen port is correct.
 - the USB adapter driver is properly installed on your computer, see
   http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx""")
                    % port)

    def show_readwrite_error(self, port, message):
        showwarning(_(u'Read/Write error'),
                    _(u"""With device on: %s

Internal error message: %s

Please ensure that:
 - the choosen port is correct.
 - your altimeter is plugged to the USB adapter.
 - the USB adapter is plugged to your computer.
 - you did not unplugged the altimeter while it was communicating.""")
                    % (port, message))

    def show_protocol_error(self, port, message):
        showwarning(_(u'Protocol error'),
                    _(u"""With device on: %s

Internal error message: %s

Please ensure that:
 - your altimeter is plugged to the USB adapter.
 - you did not unplugged the altimeter while it was communicating.""")
                    % (port, message))

    def refresh_serial_ports(self):
        # Build port list.
        port_list = self.detect_serial_ports()

        # Remove previous items in combobox.
        menu = self.ports['menu']
        menu.delete(0, tk.END)

        # Add new items in combobox.
        self.port.set(port_list[0])
        for pr in port_list:
            menu.add_command(label=pr, command=lambda p=pr: self.port.set(p))

    def detect_serial_ports(self):
        # Try to detect system serial devices.
        port_list = []
        try:
            # Convert to filenames.
            port_list = [info[0].replace('cu', 'tty')
                    for info in list_ports.comports()]
        except Exception as e:
            showwarning(_(u'Serial Ports Detection'),
                    _(u'Error while detecting serial ports: %s') % e.message)

        # Insert default port as first entry.
        default = Altimeter().port
        try:
            # Try to remove default one to prevent duplication.
            port_list.remove(default)
        except ValueError:
            pass
        port_list.insert(0, default)

        return port_list
Example #3
0
class AudioJackGUI(object):
    def __init__(self, master):
        self.root = master
        self.searching = False
        self.can_download = True
        audiojack.set_useragent('AudioJack', '1.0')
        self.frame = ScrollableFrame(self.root)
        self.frame.setconfig(bg='#0D47A1', width=1280, height=720)
        self.frame.pack(side=TOP, fill=BOTH, expand=1)
        self.label = Label(self.frame.mainframe,
                           text='AudioJack',
                           fg='#ffffff',
                           bg=self.frame.mainframe['background'],
                           font=('Segoe UI', 48))
        self.label.pack()
        self.url_entry = Entry(self.frame.mainframe,
                               width=48,
                               font=('Segoe UI', 20),
                               bg='#1565C0',
                               bd=2,
                               highlightthickness=1,
                               highlightcolor='#1565C0',
                               highlightbackground='#0D47A1',
                               fg='#ffffff',
                               insertbackground='#ffffff',
                               relief=FLAT,
                               insertwidth=1)
        self.url_entry.pack()
        self.submit_button = Button(self.frame.mainframe,
                                    width=60,
                                    font=('Segoe UI', 16),
                                    text='Go!',
                                    bd=0,
                                    bg='#1E88E5',
                                    fg='#ffffff',
                                    activebackground='#2196F3',
                                    activeforeground='#ffffff',
                                    relief=SUNKEN,
                                    cursor='hand2',
                                    command=self.submit)
        self.submit_button.pack()

        self.search_progress = Progressbar(self.frame.mainframe,
                                           orient='horizontal',
                                           length=720,
                                           maximum=100,
                                           mode='indeterminate')

        self.error_info = Label(self.frame.mainframe,
                                fg='#ff0000',
                                bg=self.frame.mainframe['background'])

        # Use pack_forget on this to reset the view
        self.contents = Frame(self.frame.mainframe,
                              bg=self.frame.mainframe['background'])

        # Contains results and custom tag options
        self.select_frame = Frame(self.contents,
                                  bg=self.frame.mainframe['background'])
        self.select_frame.pack()

        # Search results
        self.results_queue = Queue.Queue()
        self.results_label = Label(self.select_frame,
                                   text='Results:',
                                   fg='#ffffff',
                                   bg=self.frame.mainframe['background'])
        self.results_frame = Frame(self.select_frame,
                                   bg=self.frame.mainframe['background'])
        self.results_label.pack()
        self.results_frame.pack()

        # Downloads
        self.file_label = Label(self.contents,
                                fg='#ffffff',
                                bg=self.frame.mainframe['background'])

    def submit(self):
        self.searching = True

        self.error_info.pack_forget()
        self.error_info.config(text='')
        self.contents.pack_forget()
        self.reset_results_frame()
        self.file_label.config(text='')

        t = Thread(target=self.search)
        t.daemon = True
        t.start()
        self.submit_button.pack_forget()
        self.search_progress.pack()
        self.search_progress.start(10)
        self.handle_results()

    def search(self):
        url = self.url_entry.get()
        error = [-1]
        try:
            self.results_queue.put(audiojack.get_results(url))
        except DownloadError as e:
            if 'not a valid url' in e.message.lower():
                error.append('Invalid URL.')
            else:
                error.append('Unknown DownloadError.')
            self.results_queue.put(error)
        except NetworkError:
            error.append('Error when retrieving metadata.')
            self.results_queue.put(error)
        except ExtractorError:
            error.append('Unknown ExtractorError.')
            self.results_queue.put(error)

    def handle_results(self):
        try:
            results = self.results_queue.get(0)
            self.reset_results_frame()
            self.search_progress.pack_forget()
            self.submit_button.pack()
            if len(results) == 2 and results[0] == -1:
                self.error_info.config(text=results[1])
                self.error_info.pack()
            else:
                for i, entry in enumerate(results):
                    self.get_result_box(entry).grid(row=i / 4, column=i % 4)
                self.contents.pack()
                self.select_frame.pack()
        except Queue.Empty:
            self.root.after(100, self.handle_results)

    def reset_results_frame(self):
        for result in self.results_frame.winfo_children():
            result.destroy()

    def get_result_box(self, entry):
        try:
            text = '%s\n%s\n%s' % (entry['title'], entry['artist'],
                                   entry['album'])
            raw_image = Image.open(BytesIO(entry['img'].decode('base64')))
            side = self.frame.mainframe.winfo_reqwidth() / 4
            image_data = raw_image.resize((side, side), Image.ANTIALIAS)
            image = ImageTk.PhotoImage(image=image_data)
            frame = Frame(self.results_frame)
            canvas = Canvas(frame,
                            height=side,
                            width=side,
                            cursor='hand2',
                            bd=0,
                            highlightthickness=0)
            canvas.create_image((0, 0), image=image, anchor=NW)
            canvas.image = image
            canvas.text = None
            canvas.pack(fill=BOTH, expand=1)
            canvas.bind('<Enter>',
                        lambda e: self.enter_results_box(canvas, text))
            canvas.bind('<Leave>', lambda e: self.leave_results_box(canvas))
            canvas.bind('<Button-1>', lambda e: self.select(entry))
            return frame
        except Exception as e:
            print e
            print type(e)

    def enter_results_box(self, canvas, text):
        canvas.text = canvas.create_text(
            (canvas.winfo_width() / 2, canvas.winfo_height() / 2),
            text=text,
            justify=CENTER,
            fill='white',
            font=('Segoe UI', 14))

    def leave_results_box(self, canvas):
        canvas.delete(canvas.text)

    def select(self, entry):
        if self.can_download:
            self.can_download = False
            self.searching = False
            self.download_queue = Queue.Queue()
            t = Thread(target=lambda: self.get_select(entry))
            t.daemon = True
            t.start()
            self.handle_download()

    def get_select(self, entry):
        try:
            self.download_queue.put(audiojack.select(entry))
        except Exception:
            self.download_queue.put('')

    def handle_download(self):
        try:
            file = self.download_queue.get(0)
            self.can_download = True
            if not self.searching:
                label_text = 'Downloaded %s' % file
                self.select_frame.pack_forget()
                self.file_label.config(text=label_text)
                self.file_label.pack()
                self.contents.pack()
        except Queue.Empty:
            self.root.after(10, self.handle_download)