From e28d3b6286b410ec827c5e4ce1396611e8ec959c Mon Sep 17 00:00:00 2001 From: alessandro90 Date: Sat, 3 Nov 2018 17:44:09 +0100 Subject: [PATCH] Add download db window with connection checks --- audio_player.py | 6 ++- download_db_window.ui | 112 ++++++++++++++++++++++++++++++++++++++++++ download_thread.py | 43 ++++++++++++++++ download_window.py | 63 ++++++++++++++++++++++++ main.py | 97 ++++++++++++++++++++++-------------- main_window.ui | 14 +++++- 6 files changed, 294 insertions(+), 41 deletions(-) create mode 100644 download_db_window.ui create mode 100644 download_thread.py create mode 100644 download_window.py diff --git a/audio_player.py b/audio_player.py index 13005f2..0ecbd01 100644 --- a/audio_player.py +++ b/audio_player.py @@ -15,8 +15,10 @@ class AudioPlayer(QObject): __time_step = 500 # Milliseconds. - def __init__(self, play, pause, stop, volume, audio_progress): + def __init__(self, play, pause, stop, volume, audio_progress, data_folder, audio_folder): super().__init__() + self.__data_folder = data_folder + self.__audio_folder = audio_folder self.__paused = False self.__first_call = True self.__play = play @@ -77,7 +79,7 @@ class AudioPlayer(QObject): def set_audio_player(self, fname = ""): self.__first_call = True self.__reset_audio_widget() - full_name = os.path.join('Data', 'Audio_ogg', fname + '.ogg') + full_name = os.path.join(self.__data_folder, self.__audio_folder, fname + '.ogg') if os.path.exists(full_name): self.__play.setEnabled(True) self.__audio_file = full_name diff --git a/download_db_window.ui b/download_db_window.ui new file mode 100644 index 0000000..2f17663 --- /dev/null +++ b/download_db_window.ui @@ -0,0 +1,112 @@ + + + Form + + + + 0 + 0 + 400 + 151 + + + + Download database + + + QWidget { + background-color: #464646 +} + +QLabel { + color: #ffffff; +} + +QProgressBar { + border: 2px #7a7a7a; + border-radius: 5px; + background-color: #7a7a7a; +} + +QProgressBar::chunk { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #1d5eff, stop:0.5 #4177ff, stop:1 #1d5eff); + border-radius: 5px; +} + + +QMessageBox { + color: #ffffff; +} + +QPushButton { + color: #ffffff; +} + + + + QLayout::SetDefaultConstraint + + + + + + 12 + + + + Downloading database +Please wait + + + Qt::AlignCenter + + + + + + + 0 + + + 0 + + + -1 + + + + + + + + 12 + + + + QPushButton { + background-color: rgb(52,52,52); + color: #FFFFFF; + border: 1px solid gray; + border-radius: 8px; +} + + + + Cancel + + + true + + + false + + + false + + + + + + + + diff --git a/download_thread.py b/download_thread.py new file mode 100644 index 0000000..2a7d0c1 --- /dev/null +++ b/download_thread.py @@ -0,0 +1,43 @@ +from io import BytesIO +from os import mkdir +import os.path +from shutil import rmtree +import urllib +from zipfile import ZipFile +from PyQt5.QtCore import QThread, pyqtSignal + +class DownloadThread(QThread): + no_connection_error = pyqtSignal() + bad_db_download_error = pyqtSignal() + + def __init__(self, db_location, path): + super().__init__() + self.__db_location = db_location + self.__path = path + self.regular_execution = True + self.reason = 0 + + def __del__(self): + self.terminate() + self.wait() + + def run(self): + if os.path.exists(self.__path): + rmtree(self.__path) + try: + db = urllib.request.urlopen(self.__db_location) + # raise urllib.error.URLError('Test') + except urllib.error.URLError: # No internet connection. + self.regular_execution = False + self.no_connection_error.emit() + return + if db.status != 200: + self.regular_execution = False + self.reason = db.reason + self.bad_db_download_error.emit() + return + try: + with ZipFile(BytesIO(db.read())) as zipped: + zipped.extractall() + except: + pass diff --git a/download_window.py b/download_window.py new file mode 100644 index 0000000..d609bcf --- /dev/null +++ b/download_window.py @@ -0,0 +1,63 @@ +from PyQt5 import uic +from PyQt5.QtCore import Qt, pyqtSlot +from PyQt5.QtWidgets import QWidget, QMessageBox +from download_thread import DownloadThread +Ui_Download_window, _ = uic.loadUiType("download_db_window.ui") + +class DownloadWindow(QWidget, Ui_Download_window): + def __init__(self, db_location, data_folder): + super().__init__() + self.setupUi(self) + self.setWindowFlags( + #Qt.Window | + Qt.CustomizeWindowHint | + Qt.WindowTitleHint | + Qt.WindowCloseButtonHint #| + # Qt.WindowStaysOnTopHint + ) + self.everything_ok = True + self.no_internet_msg = QMessageBox(self) + self.no_internet_msg.setWindowTitle("No internet connection") + self.no_internet_msg.setText("Unable to establish an internet connection") + # self.no_internet_msg.buttonClicked.connect(self.close) + self.no_internet_msg.finished.connect(self.close) + + self.bad_db_download_msg = QMessageBox(self) + self.bad_db_download_msg.setWindowTitle("Something wrong") + self.bad_db_download_msg.finished.connect(self.close) + + self.download_thread = DownloadThread(db_location, data_folder) + self.download_thread.finished.connect(self.wait_close) + self.download_thread.no_connection_error.connect(self.show_no_connection_warning) + self.download_thread.bad_db_download_error.connect(self.show_bad_download_warning) + + self.cancel_btn.clicked.connect(self.terminate_process) + + @pyqtSlot() + def show_no_connection_warning(self): + self.bad_db_download_msg.setText(f"Unable to correctly download the database.\nReason: {self.download_thread.reason}") + self.no_internet_msg.show() + self.everything_ok = False + + @pyqtSlot() + def show_bad_download_warning(self): + self.bad_db_download_msg.show() + self.everything_ok = False + + @pyqtSlot() + def terminate_process(self): + if self.download_thread.isRunning(): + self.download_thread.terminate() + self.download_thread.wait() + self.close() + + @pyqtSlot() + def wait_close(self): + if self.download_thread.regular_execution: + self.close() + + def reject(self): + if self.download_thread.isRunning(): + self.download_thread.terminate() + self.download_thread.wait() + super().reject() diff --git a/main.py b/main.py index 1e93fcb..80a5ea0 100644 --- a/main.py +++ b/main.py @@ -13,17 +13,26 @@ from PyQt5.QtWidgets import (QMainWindow, QListWidgetItem,) from PyQt5.QtGui import QPixmap from PyQt5 import uic -from PyQt5.QtCore import QFileInfo, QSize, Qt, pyqtSlot +from PyQt5.QtCore import (QFileInfo, + QSize, + Qt, + pyqtSlot,) from audio_player import AudioPlayer from double_text_button import DoubleTextButton +from download_window import DownloadWindow qt_creator_file = "main_window.ui" - Ui_MainWindow, _ = uic.loadUiType(qt_creator_file) class MyApp(QMainWindow, Ui_MainWindow): + db_location = 'https://aresvalley.com/Storage/Artemis/Database/data.zip' + data_folder = 'Data' + spectra_folder = 'Spectra' + audio_folder = 'Audio' + icons_folder = 'icons_imgs' + Band = namedtuple("Band", ["lower", "upper"]) ELF = Band(0, 30) # Formally it is (3, 30) Hz. SLF = Band(30, 300) @@ -45,8 +54,10 @@ class MyApp(QMainWindow, Ui_MainWindow): super().__init__() self.setupUi(self) self.set_initial_size() + self.download_window = DownloadWindow(self.db_location, self.data_folder) self.show() self.actionExit.triggered.connect(qApp.quit) + self.action_update_database.triggered.connect(self.download_db) self.db_version = None self.db = None self.current_signal_name = '' @@ -286,7 +297,9 @@ class MyApp(QMainWindow, Ui_MainWindow): self.pause, self.stop, self.volume, - self.audio_progress) + self.audio_progress, + self.data_folder, + self.audio_folder) BandLabel = namedtuple("BandLabel", ["left", "center", "right"]) self.band_labels = [ @@ -324,6 +337,14 @@ class MyApp(QMainWindow, Ui_MainWindow): self.upper_freq_filter_unit.setFixedWidth(120) self.lower_freq_confidence.setFixedWidth(120) self.upper_freq_confidence.setFixedWidth(120) + + self.lower_band_spinbox.setFixedWidth(200) + self.upper_band_spinbox.setFixedWidth(200) + self.lower_band_filter_unit.setFixedWidth(120) + self.upper_band_filter_unit.setFixedWidth(120) + self.lower_band_confidence.setFixedWidth(120) + self.upper_band_confidence.setFixedWidth(120) + self.audio_progress.setFixedHeight(20) self.volume.setStyleSheet(""" QSlider::groove:horizontal { @@ -341,6 +362,19 @@ class MyApp(QMainWindow, Ui_MainWindow): } """) + @pyqtSlot() + def download_db(self): + self.download_window.download_thread.finished.connect(self.show_downloaded_signals) + self.download_window.download_thread.start() + self.download_window.show() + + @pyqtSlot() + def show_downloaded_signals(self): + if self.download_window.everything_ok: + self.search_bar.setEnabled(True) + self.load_db() + self.display_signals() + def load_db(self): names = ["name", "inf_freq", @@ -355,7 +389,7 @@ class MyApp(QMainWindow, Ui_MainWindow): "category_code", "acf",] try: - self.db = read_csv(os.path.join('Data', 'db.csv'), + self.db = read_csv(os.path.join(self.data_folder, 'db.csv'), sep = '*', header = None, index_col = 0, @@ -371,27 +405,14 @@ class MyApp(QMainWindow, Ui_MainWindow): box = QMessageBox(self) box.setWindowTitle("No database") box.setText("No database available.\n" - "Go to Updates->Download database.") + "Go to Updates->Update database.") box.show() else: self.signal_names = self.db.index self.total_signals = len(self.signal_names) self.db.fillna("N/A", inplace = True) self.db["url_clicked"] = False - try: - with open(os.path.join('Data', 'verdb.ini'), 'r') as dbver: - self.db_version = int(dbver.read()) - except (FileNotFoundError, ValueError): - box = QMessageBox(self) - box.setWindowTitle("No database version") - box.setText("Unable to detect database version.\n" - "Possible data curruption.\n" - "Go to Updates->Force Download.") - box.show() - self.statusbar.setStyleSheet(f'color: {self.active_color}') - self.statusbar.showMessage("Database version: undefined.") - else: - self.update_status_tip(self.total_signals) + self.update_status_tip(self.total_signals) @staticmethod def connect_to(objects_to_connect, fun_to_connect, fun_args): @@ -556,7 +577,7 @@ class MyApp(QMainWindow, Ui_MainWindow): def frequency_filters_ok(self, signal_name): if not self.apply_remove_freq_filter_btn.isChecked(): return True - undef_freq, _ = self.find_if_undefined(self.db.loc[signal_name]) + undef_freq = self.is_undef_freq(self.db.loc[signal_name]) if undef_freq: if self.include_undef_freqs.isChecked(): return True @@ -593,7 +614,7 @@ class MyApp(QMainWindow, Ui_MainWindow): def band_filters_ok(self, signal_name): if not self.apply_remove_band_filter_btn.isChecked(): return True - _, undef_band = self.find_if_undefined(self.db.loc[signal_name]) + undef_band = self.is_undef_band(self.db.loc[signal_name]) if undef_band: if self.include_undef_bands.isChecked(): return True @@ -652,7 +673,8 @@ class MyApp(QMainWindow, Ui_MainWindow): else: self.url_button.setStyleSheet(f"color: {self.url_button.colors.clicked};") category_code = current_signal.at["category_code"] - undef_freq, undef_band = self.find_if_undefined(current_signal) + undef_freq = self.is_undef_freq(current_signal) + undef_band = self.is_undef_band(current_signal) if not undef_freq: self.freq_lab.setText(self.format_numbers( current_signal.at["inf_freq"], @@ -694,20 +716,16 @@ class MyApp(QMainWindow, Ui_MainWindow): self.audio_widget.set_audio_player() @staticmethod - def find_if_undefined(current_signal): + def is_undef_freq(current_signal): lower_freq = current_signal.at["inf_freq"] - lower_band = current_signal.at["inf_band"] upper_freq = current_signal.at["sup_freq"] + return lower_freq == 'N/A' or upper_freq == 'N/A' + + @staticmethod + def is_undef_band(current_signal): + lower_band = current_signal.at["inf_band"] upper_band = current_signal.at["sup_band"] - if lower_freq == '0' and upper_freq == "100000000000": - undefined_freq = True - else: - undefined_freq = False - if lower_band == '0' and upper_band == '100000000': - undefined_band = True - else: - undefined_band = False - return undefined_freq, undefined_band + return lower_band == 'N/A' or upper_band == 'N/A' @classmethod def format_numbers(cls, lower, upper): @@ -740,13 +758,13 @@ class MyApp(QMainWindow, Ui_MainWindow): return 10**9 def display_spectrogram(self): - default_pic = os.path.join("icons_imgs", "nosignalselected.png") + default_pic = os.path.join(self.icons_folder, "nosignalselected.png") item = self.result_list.currentItem() if item: spectrogram_name = item.text() - path_spectr = os.path.join("Data", "Spectra", spectrogram_name + ".jpg") + path_spectr = os.path.join(self.data_folder, self.spectra_folder, spectrogram_name + ".png") if not QFileInfo(path_spectr).exists(): - path_spectr = os.path.join("icons_imgs", "spectrumnotavailable.png") + path_spectr = os.path.join(self.icons_folder, "spectrumnotavailable.png") else: path_spectr = default_pic self.spectrogram.setPixmap(QPixmap(path_spectr)) @@ -758,7 +776,7 @@ class MyApp(QMainWindow, Ui_MainWindow): label.setStyleSheet(f"color: {color};") def set_band_range(self, current_signal = None): - if current_signal is not None and not self.find_if_undefined(current_signal)[0]: + if current_signal is not None and not self.is_undef_freq(current_signal): lower_freq = int(current_signal.at["inf_freq"]) upper_freq = int(current_signal.at["sup_freq"]) zipped = list(zip(self.bands, self.band_labels)) @@ -791,6 +809,11 @@ class MyApp(QMainWindow, Ui_MainWindow): webbrowser.open(self.db.at[self.current_signal_name, "url"]) self.db.at[self.current_signal_name, "url_clicked"] = True + def closeEvent(self, event): + if self.download_window.isVisible(): + self.download_window.close() + super().closeEvent(event) + if __name__ == '__main__': diff --git a/main_window.ui b/main_window.ui index bd14a26..32fcc1d 100644 --- a/main_window.ui +++ b/main_window.ui @@ -323,7 +323,7 @@ QPushButton:!enabled { QTabWidget::Rounded - 1 + 0 true @@ -4032,11 +4032,15 @@ QMenuBar::item:selected { QMenu::item:selected { background-color: #999999; color: #1d5eff +} + +QMenu { + color: #ffffff; } - color: rgb(255, 255, 255); + File @@ -4047,6 +4051,7 @@ QMenu::item:selected { Updates + @@ -4061,6 +4066,11 @@ QMenu::item:selected { Exit + + + Update database + +