class SetPolicyWidget(QWidget): Sg_view_changed = Signal() def __init__(self, model: SettingsModel, parent=None): super(SetPolicyWidget, self).__init__(parent) self._model = model self.setAccessibleName("InfoBox") self.titolo = QLabel("Politica di gestione conflitti") self.titolo.setAccessibleName('Title2') self.sottotitolo = QLabel("Cambia come vengono gestiti conflitti") self.sottotitolo.setAccessibleName('Sottotitolo') self.spaceLabel = QLabel(" ") self.client = QRadioButton( "Client: le modifiche in locale sovrascrivono quelle presenti nel server" ) self.manual = QRadioButton( "Manuale: verranno salvati entrambi i file, sarà l'utente a decidere cosa mantenere" ) self.Sl_model_changed() layout = QVBoxLayout() layout.addWidget(self.titolo) layout.addWidget(self.sottotitolo) layout.addWidget(self.spaceLabel) sub_layout = QVBoxLayout() sub_layout.addWidget(self.client) sub_layout.addWidget(self.manual) layout.addLayout(sub_layout) self.setLayout(layout) self.client.clicked.connect(self.Sl_client_checked) self.manual.clicked.connect(self.Sl_manual_checked) @Slot() def Sl_client_checked(self): if self.client.isChecked(): self.Sg_view_changed.emit() @Slot() def Sl_manual_checked(self): if self.manual.isChecked(): self.Sg_view_changed.emit() @Slot() def Sl_model_changed(self): if self._model.get_policy() == Policy.Manual: self.client.setChecked(False) self.manual.setChecked(True) else: self.client.setChecked(True) self.manual.setChecked(False)
class SetPathWidget(QWidget): Sg_view_changed = Signal(str) def __init__(self, model: SettingsModel, parent=None): super(SetPathWidget, self).__init__(parent) self._model = model self.setAccessibleName("InfoBox") self.debug = False self.titolo = QLabel() self.titolo.setText("Cartella sincronizzata") self.titolo.setAccessibleName("Title2") self.sottotitolo = QLabel() self.sottotitolo.setAccessibleName('Sottotitolo') self.sottotitolo.setText("Cambia il path della cartella sincronizzata") self.spaceLabel = QLabel(" ") self.path = QLabel() self.path.setText(self._model.get_path()) self.change_path_button = QPushButton("Cambia") self.change_path_button.setMaximumWidth(150) layout = QVBoxLayout() layout.addWidget(self.titolo) layout.addWidget(self.sottotitolo) layout.addWidget(self.spaceLabel) sub_layout = QHBoxLayout() sub_layout.addWidget(self.path) sub_layout.addWidget(self.change_path_button) layout.addLayout(sub_layout) self.setLayout(layout) self.change_path_button.clicked.connect(self.Sl_show_file_dialog) @Slot() def Sl_model_changed(self): self.path.setText(self._model.get_path()) @Slot() def Sl_show_file_dialog(self): dialog = QFileDialog() dialog.setFileMode(QFileDialog.Directory) dialog.setViewMode(QFileDialog.Detail) dialog.setOption(QFileDialog.ShowDirsOnly) dialog.setOption(QFileDialog.DontResolveSymlinks) if dialog.exec(): # Serve per fare in modo che il test abbia una stringa da usare sync_path = ["test"] if self.debug else dialog.selectedFiles() if len(sync_path) == 1: self.Sg_view_changed.emit(sync_path[0])
class SetSyncTimeWidget(QWidget): Sg_view_changed = Signal() def __init__(self, model: SettingsModel, parent=None): super(SetSyncTimeWidget, self).__init__(parent) self._time_list = ["5s", "10s", "15s", "15m", "30m", "45m"] self._time_in_sec = [] for curr_time in self._time_list: time_int = int(re.search(r'\d+', curr_time).group()) if 'm' in curr_time: time_int = time_int * 60 if 'h' in curr_time: time_int = time_int * 3600 self._time_in_sec.append(time_int) self._model = model self.setAccessibleName('InfoBox') self._titolo = QLabel() self._titolo.setText("Finestra Temporale") self._titolo.setAccessibleName('Title2') self.sottotitolo = QLabel() self.sottotitolo.setAccessibleName('Sottotitolo') self.sottotitolo.setText("Seleziona finestra temporale per la sincronizzazione") self.spaceLabel = QLabel(" ") self.time_box = QComboBox() self.time_box.wheelEvent = lambda event: None self.time_box.addItems(self._time_list) self.Sl_model_changed() layout = QVBoxLayout() layout.addWidget(self._titolo) layout.addWidget(self.sottotitolo) layout.addWidget(self.spaceLabel) layout.addWidget(self.time_box) self.setLayout(layout) # connect signal self.time_box.currentIndexChanged.connect(self.Sl_time_checked) @Slot() def Sl_time_checked(self): self.Sg_view_changed.emit() @Slot() def Sl_model_changed(self): time = self._model.get_sync_time() i: int = 0 for el in self._time_in_sec: if time == el: self.time_box.setCurrentIndex(i) i += 1
class SyncWidget(QWidget): Sg_view_changed = Signal() def __init__(self, model: SyncModel, parent=None): super(SyncWidget, self).__init__(parent) self._model = model self.watch_label = QLabel(self) self.watch_label.setAlignment(Qt.AlignCenter) self.watch_label.setText("SYNC") self.watch_label.setAccessibleName("Title") self.running_label = QLabel(self) self.running_label.setAlignment(Qt.AlignCenter) self.running_label.setText("Disattivata") self.running_label.setAccessibleName("Subtitle") self.sync_button = QPushButton(self) self.sync_button.setIcon(QIcon(resource_path('icons/reload.png'))) self.sync_button.setIconSize(QSize(50, 50)) self.sync_button.setCheckable(True) self.sync_button.setAccessibleName('HighlightButton') self.menu_label = QLabel(self) self.menu_label.setAlignment(Qt.AlignCenter) self.menu_label.setText("• • •") # create layout self.layout = QVBoxLayout() self.layout.setAlignment(Qt.AlignCenter) self.layout.addWidget(self.watch_label) self.layout.addWidget(self.running_label) self.layout.addWidget(self.sync_button) self.layout.addWidget(self.menu_label) self.setLayout(self.layout) self.sync_button.clicked.connect(self.Sl_button_clicked) self.Sl_model_changed() @Slot() def Sl_button_clicked(self): self.Sg_view_changed.emit() @Slot() def Sl_model_changed(self): self.sync_button.setChecked(self._model.get_state()) if self._model.get_state(): self.running_label.setText("Attivata") else: self.running_label.setText("Disattivata")
class SetProfileView(QWidget): Sg_profile_logout = Signal() def __init__(self, model: NetworkModel, parent=None): super(SetProfileView, self).__init__(parent) self._model = model self.setAccessibleName("InfoBox") # Titolo self._titolo = QLabel('Profilo') self._titolo.setAccessibleName('Title2') self.username = QLabel() sub_layout_user = QHBoxLayout() sub_layout_user.addWidget(QLabel('Account:')) sub_layout_user.addWidget(self.username) sub_layout_user.addStretch() self.spaceLabel = QLabel(" ") # Pulsante logout self.logout_button = QPushButton('Logout') self.logout_button.setMaximumWidth(150) self.logout_button.clicked.connect(self.Sl_logout) self.buttonLayout = QHBoxLayout() self.buttonLayout.addWidget(self.spaceLabel) self.buttonLayout.addWidget(self.logout_button) self.buttonLayout.addWidget(self.spaceLabel) # Layout layout = QVBoxLayout() layout.addWidget(self._titolo) layout.addWidget(self.spaceLabel) layout.addLayout(sub_layout_user) layout.addLayout(self.buttonLayout) self.setLayout(layout) self.Sl_model_changed() @Slot() def Sl_model_changed(self): if self._model.is_logged: self.username.setText(self._model.get_username()) @Slot() def Sl_logout(self): self.Sg_profile_logout.emit()
class RemoteFileView(QWidget): Sg_update_files_with_new_id = Signal(str) Sg_add_sync_file = Signal(str) Sg_remove_sync_file = Signal(str) Sg_file_status_changed = Signal() def __init__(self, model: MainModel, parent=None): super(RemoteFileView, self).__init__(parent) self.env_settings = QSettings() self._model: RemoteFileModel = model.remote_file_model self.settings_model: SettingsModel = model.settings_model self.title = QLabel("File remoti", self) self.title.setAlignment(Qt.AlignLeft) self.title.setAccessibleName("Title") self.refresh_button = QPushButton("Refresh", self) self.refresh_button.clicked.connect(self.Sl_refresh_button_clicked) # scroll area self.scrollArea = QScrollArea() self.scrollArea.setAccessibleName("FileScroll") self.scrollArea.setWidgetResizable(True) self.scrollArea.horizontalScrollBar().setEnabled(False) # contenitore per file self.fileWindow = QWidget(self) self.fileLayout = FlowLayout() self.fileLayout.setContentsMargins(0, 0, 0, 0) self.fileWindow.setParent(self.scrollArea) self.fileWindow.setLayout(self.fileLayout) self.scrollArea.setWidget(self.fileWindow) header_layout = QHBoxLayout() header_layout.addWidget(self.title) header_layout.addWidget(self.refresh_button) layout = QVBoxLayout() layout.addLayout(header_layout) layout.addWidget(self.scrollArea) self.setLayout(layout) self.Sl_model_changed() @Slot() def Sl_model_changed(self) -> None: data = self._model.get_data() # Pulisco la vista for i in reversed(range(self.fileLayout.count())): self.fileLayout.itemAt(i).widget().setParent(None) # Se model.get_data ha avuto eccezioni di rete restituisce None if data is not None: list_of_files, list_of_dirs = data # Aggiungo le cartelle for i in list_of_dirs: self.fileLayout.addWidget(RemoteDirectoryWidget(i, self)) # Aggiungo i file for i in list_of_files: self.fileLayout.addWidget( RemoteFileWidget(i, self.settings_model)) self.fileLayout._item_list[-1].wid.Sg_add_sync_file.connect( self.Sl_add_sync_file) self.fileLayout._item_list[-1].wid.Sg_remove_sync_file.connect( self.Sl_remove_sync_file) self.Sg_file_status_changed.connect( self.fileLayout._item_list[-1].wid. Sl_on_file_status_changed) @Slot(str) def Sl_update_files_with_new_id(self, id: str) -> None: self.Sg_update_files_with_new_id.emit(id) @Slot() def Sl_refresh_button_clicked(self): self._model.folder_queue = ["LOCAL_ROOT"] self.Sl_model_changed() @Slot() def Sl_add_sync_file(self, id: str) -> None: self.Sg_add_sync_file.emit(id) @Slot() def Sl_remove_sync_file(self, id: str) -> None: self.Sg_remove_sync_file.emit(id) @Slot() def Sl_file_status_changed(self) -> None: self.Sg_file_status_changed.emit()
class LoginScreen(QDialog): Sg_login_success = Signal() Sg_login_failure = Signal() def __init__(self, model: NetworkModel, parent=None): super(LoginScreen, self).__init__(parent) # gestione modello self.model = model # inizializzazione layout self.layout = QVBoxLayout() # label e field self.login_title = QLabel(self) self.login_title.setText("Effettua l'accesso") self.login_title.setAccessibleName('LoginTitle') self.login_label = QLabel(self) self.login_label.setText('Username') self.login_label.setAccessibleName('LoginLabel') self.user_field = QLineEdit(self) self.psw_label = QLabel(self) self.psw_label.setText('Password') self.psw_label.setAccessibleName('LoginLabel') self.psw_field = QLineEdit(self) self.psw_field.setEchoMode(QLineEdit.Password) self.user_field.setText(self.model.get_username()) # pulsante invio form self.login_button = QPushButton(self) self.login_button.setText('Login') # gestione layout self.layout.setAlignment(Qt.AlignCenter) self.layout.addWidget(self.login_title) self.layout.addWidget(self.login_label) self.layout.addWidget(self.user_field) self.layout.addWidget(self.psw_label) self.layout.addWidget(self.psw_field) self.layout.addWidget(self.login_button) self.setLayout(self.layout) setQss("style.qss", self) @Slot() def Sl_model_changed(self): is_logged = self.model.is_logged() if is_logged: self.Sg_login_success.emit() def get_user(self) -> str: return self.user_field.text() def get_psw(self) -> str: return self.psw_field.text() @Slot() def Sl_login_fail(self): self.Sg_login_failure.emit()
class FileView(QWidget): Sg_update_files_with_new_path = Signal(str) def __init__(self, model: FileModel, parent=None): super(FileView, self).__init__(parent) self.env_settings = QSettings() self._model = model self.title = QLabel("File locali", self) self.title.setAlignment(Qt.AlignLeft) self.title.setAccessibleName("Title") # scroll area self.scrollArea = QScrollArea() self.scrollArea.setAccessibleName("FileScroll") self.scrollArea.setWidgetResizable(True) self.scrollArea.horizontalScrollBar().setEnabled(False) # contenitore per file self.fileWindow = QWidget(self) self.fileLayout = FlowLayout() self.fileLayout.setContentsMargins(0, 0, 0, 0) self.show_path_button = QPushButton("Apri file manager", self) self.force_sync_button = QPushButton("Sincronizza ora", self) self.fileWindow.setParent(self.scrollArea) self.fileWindow.setLayout(self.fileLayout) self.scrollArea.setWidget(self.fileWindow) header_layout = QHBoxLayout() header_layout.addWidget(self.title) header_layout.addWidget(self.force_sync_button) header_layout.addWidget(self.show_path_button) layout = QVBoxLayout() layout.addLayout(header_layout) layout.addWidget(self.scrollArea) self.setLayout(layout) self.Sl_model_changed() @Slot() def Sl_show_path_button_clicked(self) -> None: path = QUrl.fromUserInput(self.env_settings.value("sync_path")) QDesktopServices.openUrl(path) @Slot() def Sl_model_changed(self) -> None: list_of_files, list_of_dirs = self._model.get_data() for i in reversed(range(self.fileLayout.count())): self.fileLayout.itemAt(i).widget().setParent(None) for i in list_of_dirs: self.fileLayout.addWidget(LocalDirectoryWidget(i, self)) for i in list_of_files: self.fileLayout.addWidget(LocalFileWidget(i)) @Slot(str) def Sl_update_files_with_new_path(self, path: str) -> None: self.Sg_update_files_with_new_path.emit(path) def toggle_files_update(self, file_path: str, is_sync: bool) -> None: for widget in self.fileLayout._item_list: if type(widget.wid) == LocalFileWidget: if os.path.samefile(widget.wid.path, file_path): widget.wid.show_synced(is_sync)
class SetQuotaDiskWidget(QWidget): Sg_view_changed = Signal() def __init__(self, model: SettingsModel, parent=None): super(SetQuotaDiskWidget, self).__init__(parent) self._model = model self.setAccessibleName("InfoBox") self.title = QLabel() self.title.setText("Spazio di archiviazione") self.title.setAccessibleName("Title2") self.sottotitolo = QLabel() self.sottotitolo.setAccessibleName('Sottotitolo') self.sottotitolo.setText( "Cambia lo spazio di archiviazione destinato alla cartella sincronizzata" ) # Barra riempimento disco self.progress_label = QLabel() self.progress_label.setText("Spazio occupato:") self.disk_progress = QProgressBar() self.disk_progress.setFormat("") self.disk_quota = QLabel() # Modifica spazio dedicato self.spaceLabel = QLabel(" ") self.dedicated_space = QLineEdit() self.dedicated_space.setValidator(QDoubleValidator()) self.sizes_box = QComboBox() self.sizes_box.wheelEvent = lambda event: None _path_size = bitmath.parse_string(model.convert_size(model.get_size())) _disk_free = bitmath.parse_string( model.convert_size(model.get_free_disk())) self.populate_size_box(_path_size, _disk_free) self.change_quota_button = QPushButton("Cambia quota disco") self.change_quota_button.setMaximumWidth(150) self.change_quota_button.clicked.connect( self.Sl_dedicated_space_changed) self.dedicated_space.returnPressed.connect( self.Sl_dedicated_space_changed) self.buttonLayout = QHBoxLayout() self.buttonLayout.addWidget(self.spaceLabel) self.buttonLayout.addWidget(self.change_quota_button) self.buttonLayout.addWidget(self.spaceLabel) set_space_layout = QHBoxLayout() set_space_layout.addWidget(self.dedicated_space) set_space_layout.addWidget(self.sizes_box) quota_layout = QHBoxLayout() quota_layout.setAlignment(Qt.AlignLeft) quota_layout.addWidget(self.progress_label) quota_layout.addWidget(self.disk_quota) # layout disk_layout = QVBoxLayout() disk_layout.setAlignment(Qt.AlignLeft) disk_layout.addWidget(self.title) disk_layout.addWidget(self.sottotitolo) disk_layout.addWidget(self.spaceLabel) disk_layout.addLayout(quota_layout) disk_layout.addWidget(self.disk_progress) disk_layout.addWidget(self.spaceLabel) disk_layout.addLayout(set_space_layout) disk_layout.addLayout(self.buttonLayout) self.setLayout(disk_layout) self.Sl_model_changed() @Slot() def Sl_dedicated_space_changed(self): self.Sg_view_changed.emit() @Slot() def Sl_model_changed(self): """ Slot collegato ai segnali del model, aggiorna la vista con i nuovi valori :return: None """ # Prendo quota disco con unità e il peso della cartella senza unità (Byte default) new_max_quota = self._model.get_quota_disco() _folder_size = self._model.get_size() # Converto ad oggetto bitmath il peso della cartella e la quota disco folder_size_parsed = bitmath.parse_string( self._model.convert_size(_folder_size)) quota_disco_parsed = bitmath.parse_string(new_max_quota) # Imposto la textbox che mi dice quanto peso ho occupato su quello disponibile self.disk_quota.setText( f"{folder_size_parsed} su {new_max_quota} in uso") free_disk_parsed = bitmath.parse_string( self._model.convert_size(self._model.get_free_disk())) # Imposto la textbox che richiede input if not self.dedicated_space.hasFocus(): self.dedicated_space.setText(str(quota_disco_parsed.value)) # Creo i nuovi valori della combobox if not self.sizes_box.hasFocus(): self.populate_size_box(folder_size_parsed, free_disk_parsed) # Imposto l'item in focus della combobox self.sizes_box.setCurrentText(quota_disco_parsed.unit) # Prendo dimensione corrente della sync folder e della quota disco # e metto in proporzione con quotadisco:100=syncfolder:x _progress_bar_max_value = 100 _tmp = folder_size_parsed.to_Byte().value * _progress_bar_max_value _progress_bar_current_percentage = _tmp / quota_disco_parsed.to_Byte( ).value # Inserisco nuovi valori nella progress bar self.disk_progress.setRange(0, _progress_bar_max_value) self.disk_progress.setValue(_progress_bar_current_percentage) # Se la cartella occupa più spazio di quanto voluto allora la porto a quanto occupa if quota_disco_parsed < folder_size_parsed and not self.dedicated_space.hasFocus( ): self.dedicated_space.setText(str(folder_size_parsed.value)) self.sizes_box.setCurrentText(folder_size_parsed.unit) self.Sg_view_changed.emit() def populate_size_box( self, _min: str, _max: str, ) -> None: """ This method populates the size box with only the available units ex hdd has only <1gb so gb will not be used, the current folder is heavier than 1mb so kb will not be used. :param _min: minimum value with unit ex 10 KiB or just 'KiB' :param _max: maximum value with unit ex 10 KiB or just 'KiB' :return: None """ _sizes = "Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB" # Converto in ogni caso a string, in caso in cui venga passato un oggetto # tipo bitmath _min = str(_min) _max = str(_max) # Rimuovo eventuali numeri e caratteri extra, tengo solo l'unità di misura _min = ''.join(i for i in _min if not i.isdigit() and i != '.') _max = ''.join(i for i in _max if not i.isdigit() and i != '.') # Rimuovo possibili spazi ad inizio e fine stringa _min = _min.strip() _max = _max.strip() # Rimuovo dal vettore di possibili unità di misura tutte le unità sotto il lower bound lower_bound = _sizes[_sizes.index(_min):] # Rimuovo dal vettore di possibili unità di misura tutte le unità sopra l'upper bound upper_bound = lower_bound[:lower_bound.index(_max) + 1] # Pulisco il vecchio combo box self.sizes_box.clear() # Inserisco nuovi valori self.sizes_box.addItems(upper_bound)