def _show_about(): """ Show the about text. Arguments: None Return: None """ text = ( "\n" "TranSPHIRE is supposed to help with the cryo-EM data collection\n" "Copyright (C) 2017 Markus Stabrin\n" "\n" "This program is free software: you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation, either version 3 of the License, or\n" "(at your option) any later version.\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License\n" "along with this program. If not, see <http://www.gnu.org/licenses/>.\n" ) tu.message(text)
def accept(self): """ Modified accept event to check if the sudopassword is valid. Arguments: None Returns: None """ self.password = self.textValue() child = pe.spawnu('sudo -S -k ls') child.sendline(self.password) try: idx = child.expect([pe.EOF, 'sudo: 1 incorrect password attempt', 'Sorry, try again'], timeout=10) except pe.exceptions.TIMEOUT: print('Wrong sudo password!') tu.message('Wrong sudo password!') return None else: if idx == 0: super().accept() else: print('Wrong sudo password!') tu.message('Wrong sudo password!') return None
def _specify_hdd(self): """ Specify which HDD to unmount. Arguments: None Return: HDD text """ text, result = QInputDialog().getText( self, 'Specify hdd', 'choose hdd to unmount,\n"all" will unmount all mounted hdd\'s', QLineEdit.Normal, 'all' ) if result: text = text.lower() if text.startswith('hdd_'): return text elif text.startswith('hdd '): return text elif text == 'all': return text else: tu.message('Input needs to be "all" or "hdd_{number}" or "hdd {number}') return None else: tu.message('Input needs to be "all" or "hdd_{number} or "hdd {number}"') return None
def mount(self): """ Mount device preparation Arguments: None Return: None """ # Mount external hdd if self.typ == 'Copy_hdd': self.mount_worker.sig_mount_hdd.emit(self.mount_folder) else: user, password, folder = self._check_user( login=bool(self.login == 'True') ) if user: self.mount_worker.sig_mount.emit( self.mount_folder, user, password, folder, self.ip_adress, self.protocol, self.domain, self.version, self.sec, self.gid, ) else: print('Wrong identity') tu.message('Wrong identity, check command line for more information')
def get_settings(self): """ Get the settings from the child widgets. Arguments: None Return: List of settings """ settings_list = [] error_occured = False for entry in self.content: widget = entry['widget'] widget_2 = entry['widget_2'] settings = copy.deepcopy(entry['settings']) dtype = settings['dtype'] key = settings['name'] if isinstance(widget, QComboBox): value = widget.currentText() elif isinstance(widget, QLineEdit): value = widget.text() else: message = '{0}: Type {1} not known!'.format(key, type(widget)) print(message) tu.message(message) sys.exit() if isinstance(widget_2, QComboBox): settings['widget_2'] = widget_2.currentText() widget_2.setStyleSheet(tu.get_style(typ='pass')) else: assert widget_2 is None settings['widget_2'] = widget_2 if value: if tu.check_instance(value=value, typ=dtype): widget.setStyleSheet(tu.get_style(typ='pass')) else: widget.setStyleSheet(tu.get_style(typ='error')) message = '{0}: {1} needs to be {2}'.format( key, value, dtype) print(message) tu.message(message) error_occured = True else: pass if key == 'Typ' and not widget.isEnabled(): value = 'Copy_hdd' elif key == 'Protocol' and not widget.isEnabled(): value = 'hdd' else: pass settings_list.append({key: [value, settings]}) if error_occured: return None else: return settings_list
def umount(self): """ Umount device signal preparation Arguments: None Return: None """ if self.typ == 'Copy_hdd': hdd_folder = '{0}/{1}'.format( self.mount_worker.mount_directory, self.mount_folder ) if not os.path.exists(hdd_folder): self.mount_worker.sig_info.emit( 'First mount {0}'.format(self.mount_folder) ) return None else: pass folders = os.listdir(hdd_folder) hdd_name = self._specify_hdd() if hdd_name is None: return None else: hdd_name = hdd_name.replace(' ', '_').upper() if hdd_name == 'ALL': for entry in folders: self.mount_worker.sig_umount.emit( self.mount_folder, entry, self.thread_object ) elif hdd_name.startswith('HDD_'): if hdd_name in folders: self.mount_worker.sig_umount.emit( self.mount_folder, hdd_name, self.thread_object ) else: tu.message('{0} is not mounted!'.format(hdd_name)) else: print('Unmount hdd: Unreachable code') print(hdd_name, self.mount_folder, folders) else: self.mount_worker.sig_umount.emit( self.mount_folder, self.mount_folder, self.thread_object )
def _mount_info(text): """ Information for the user Arguments: text - Text to show Returns: None """ tu.message(text)
def _process_error(self, text): """ Error in a process Arguments: text - Text to show Returns: None """ tu.message(text) self.sig_refresh_quota.emit()
def _mount_error(self, text, device): """ Mount was not successfull Arguments: text - Text to show device - Device name Returns: None """ if device != 'None': self.content[device].sig_change_info_name.emit( 'Not connected', 'red') tu.message(text)
def get_settings(self, quiet=False): """ Get the settings as dict. Arguments: quiet - True, if prints should not be shown. Returns: None, if an error occured. Settings as dictionary. """ settings = {} if isinstance(self.edit, QComboBox): value = self.edit.currentText() elif isinstance(self.edit, QLineEdit): value = self.edit.text() else: message = 'Unreachable code! Please contact the TranSPHIRE authors!' print(message) tu.message(message) return None if value: if tu.check_instance(value=value, typ=self.dtype): self.edit.setStyleSheet(tu.get_style(typ='pass')) else: self.edit.setStyleSheet(tu.get_style(typ='error')) message = '{0}: {1} needs to be {2}'.format( self.label.text(), value, self.dtype) if not quiet: print(message) tu.message(message) else: pass return None else: pass settings[self.name] = value return settings
def _mount_success(self, text, device, color): """ Mount was successfull. Arguments: text - Text to show device - Device name color - Color of the text Returns: None """ self.content[device].sig_change_info_name.emit(text, color) if device != 'scratch' and device != 'project': tu.message('{0} Mount/Unmount successfull!'.format(device)) else: pass
def stop_dialog(self): """ Check if the user really wants to stop the process. Arguments: None Return: None """ result = self.continue_dialog( text1='Do you really want to stop?', text2='Do you really want to stop!\nType: YES!') if result: self.stop() else: tu.message('Input needs to be "YES!" to work')
def _modify_settings(self): """ Open default settings dialog. Called when the Default settings button is clicked. Arguments: None Return: None """ temp_save_file = 'temp_reset_save_1' content, apply = DefaultSettings.get_content_default( edit_settings=True, apply=True, settings_folder=self.parent.settings_folder) if not apply: tu.message('Restart GUI to apply saved changes') elif apply: content_gui = tu.get_content_gui(content=content) self.parent.enable(var=False, use_all=True) # result is True if No is chosen result = tu.question( head='Keep current values', text='Do you want to keep your current settings?\n' + 'Otherwhise they will be overwritten by the default values.', parent=self) app = QApplication.instance() app.setStyleSheet( tu.look_and_feel(app=app, default=content['Font'])) if not result: self.parent.save(temp_save_file) self.parent.reset_gui(content_gui=content_gui, content_pipeline=content['Pipeline'], load_file=temp_save_file) else: self.parent.reset_gui(content_gui=content_gui, content_pipeline=content['Pipeline'], load_file=None) else: pass
def closeEvent(self, event): """ Quit threads before close and check if the process is still running Arguments: event - QCloseEvent. Return: None """ if self.content['Button'].start_button.text() == 'Stop': event.ignore() tu.message('First stop the program before closing') return None elif not self.save_temp_settings(): result = tu.question( head='Error saving file!', text='Wrong setting detected! Quit without saving?', ) # Result is true if the answer is No if not result: pass else: event.ignore() return None else: pass self.thread_mount.quit() self.thread_mount.wait() self.thread_process.quit() self.thread_process.wait() self.thread_plot.quit() self.thread_plot.wait() for key in self.content['Mount'].content: thread = self.mount_thread_list[key]['thread'] calculator = self.mount_thread_list[key]['object'] calculator.kill_thread = True thread.quit() thread.wait() message = 'Bye Bye' print(message) super(MainWindow, self).closeEvent(event)
def postprocess_content(self, error_list): """ Do postprocessing of creating GUI content, like connecting signals. Arguments: error_list - List of errors that occured. Return: True, if saving was succesful. """ for entry in error_list: tu.message(entry) self.process_worker.sig_finished.connect(self._finished) self.process_worker.sig_plot_ctf.connect( self.plot_worker.calculate_array_ctf) self.process_worker.sig_plot_motion.connect( self.plot_worker.calculate_array_motion) self.plot_worker.sig_message.connect(lambda msg: tu.message(msg)) self.mount_thread_list = {} for key in self.content['Mount'].content: thread = QThread(self) thread.start() mount_calculator = MountCalculator(name=key) mount_calculator.moveToThread(thread) self.mount_thread_list[key] = { 'thread': thread, 'object': mount_calculator } mount_calculator.sig_finished.connect( self.content['Status'].refresh_quota) mount_calculator.sig_finished.connect(self.abort_finished) self.mount_worker.sig_calculate_ssh_quota.connect( mount_calculator.calculate_ssh_quota) self.mount_worker.sig_calculate_df_quota.connect( mount_calculator.calculate_df_quota) self.mount_worker.sig_calculate_get_quota.connect( mount_calculator.calculate_get_quota) self.content['Mount'].set_threadlist( thread_list=self.mount_thread_list)
def send_to_user(self, user_id, text, name): """ Send a message to all users. Arguments: user_id - User id of the receiving person text - Text of the message name - Name of the sender Returns: None """ try: self.bot.sendMessage( user_id, '{0}\n{1}: {2}'.format(os.uname()[1], name, text) ) except telepot.exception.BotWasBlockedError as err: tu.message('{0}\n{1}\t{2}'.format( os.uname()[1], name, err ))
def add_widget(self): """ Add a FrameWidget widget to the dynamic layout. Called when the add button is clicked. Arguments: None Returns: None """ first = self.first.text() last = self.last.text() name = '{0},{1}'.format(first, last) try: first = int(first) last = int(last) except ValueError: tu.message('Not castable, input needs to be integer value.') return None if first <= 0: tu.message('No values below 1.') return None elif first > last: tu.message('Last must be smaller or equals first.') return None elif name in set(self.content_frames): tu.message('Already got frame combination {0} {1}'.format( first, last)) return None else: pass widget = FrameWidget(first=first, last=last, dose_weight=False, default=False) widget.delete.connect(self._del_widget) self.layout.addWidget(widget) self.content.append(widget) self.content_frames.append(name)
def save(self, file_name=None, temp=False): """ Save GUI status to file. Arguments: file_name - File name to save settings to. temp - File is a temporary save file. Return: True, if saving was succesful. """ if file_name is None: file_name = QFileDialog.getSaveFileName( caption='Save settings', directory=self.path, options=QFileDialog.DontUseNativeDialog, filter="Text files (*.txt)") if QT_VERSION == 4: file_name = file_name elif QT_VERSION == 5: file_name = file_name[0] else: raise ImportError( 'QT version unknown! Please contact the transphire authors!' ) if not file_name: return None else: pass else: pass if file_name.endswith('.txt'): pass else: file_name = '{0}.txt'.format(file_name) # Do not override settings if os.path.exists(file_name): old_filename = file_name for number in range(9999): file_name = '{0}_{1}'.format(old_filename, number) if os.path.exists(file_name): continue else: break else: pass error = False with open(file_name, 'w') as write: for key in self.content: if key == 'Mount': continue else: pass try: settings = self.content[key].get_settings() except AttributeError: continue if settings is not None: write.write('###\t{0}\n'.format(key)) for entry in settings: for key_entry in entry: write.write('{0}\t{1}\n'.format( key_entry, entry[key_entry])) else: error = True message = 'Setting of {0} not valid!'.format(key) tu.message(message) continue write.write('###\tEnd\n') write.write('The\tEnd\n') message_pass = '******'.format(file_name) message_error = 'Invalid setting detected! Saveing failed!' if error: os.remove(file_name) tu.message(message_error) print(message_error) return False else: if temp: pass else: tu.message(message_pass) print(message_pass) return True
def start(self): """ Start TranSPHIRE processing. Arguments: None Return: None """ self.enable(False) settings = {} # Load settings to pass them to the working threads error_list = [] skip_list = ['Mount', 'Notification', 'Path', 'Frames'] for key in self.content: try: settings_widget = self.content[key].get_settings() except AttributeError: continue else: settings[key] = {} if settings_widget is None: self.enable(True) return None elif key == 'Frames': settings_motion = {} else: pass if key == 'Frames': skip_name_list = [] else: skip_name_list = tu.get_function_dict()[key]['allow_empty'] for entry in settings_widget: if key not in skip_list: for name in entry: if not entry[name] and name not in skip_name_list: error_list.append( '{0}:{1} is not allowed to be emtpy!'.format( key, name)) else: pass else: pass for idx, entry in enumerate(settings_widget): if key == 'Frames': settings_motion[idx] = entry else: settings[key].update(entry) if key == 'Frames': settings['motion_frames'] = settings_motion else: pass if error_list: tu.message('\n'.join(error_list)) self.enable(True) return None else: pass # Get mount information for key in settings['Mount']: device_name = key.replace(' ', '_') save_file = os.path.join(self.settings_folder, device_name) try: with open(save_file, 'r') as read: lines = read.readlines() except FileNotFoundError: continue for line in lines: name = line.split('\t')[0] settings['user_{0}'.format(device_name)] = name settings['user_Later'] = None if not re.match(self.project_name_pattern, settings['General']['Project name']): self.enable(True) tu.message( 'Project name needs to match pattern:\n{0}\n For example: {1}'. format(self.project_name_pattern, self.project_name_pattern_example)) return None else: pass # Project folder names settings['project_folder'] = os.path.join( settings['General']['Project directory'], settings['General']['Project name']) settings['compress_folder'] = os.path.join( settings['General']['Project directory'], settings['General']['Project name']) settings['motion_folder'] = os.path.join( settings['General']['Project directory'], settings['General']['Project name']) settings['ctf_folder'] = os.path.join( settings['General']['Project directory'], settings['General']['Project name']) settings['scratch_folder'] = os.path.join( settings['General']['Scratch directory'], settings['General']['Project name']) settings['Copy_hdd_folder'] = self.mount_directory settings['Copy_backup_folder'] = self.mount_directory settings['Copy_work_folder'] = self.mount_directory settings['settings_folder'] = os.path.join(settings['project_folder'], 'settings') settings['queue_folder'] = os.path.join(settings['project_folder'], 'queue') settings['error_folder'] = os.path.join(settings['project_folder'], 'error') # Check for continue mode if os.path.exists(settings['project_folder']): result = self.continue_dialog( text1='Output project folder already exists!', text2='Do you really want to continue the old run?\nType: YES!' ) if result: result_session = self.continue_dialog( text1='Software metafiles', text2='Software metafiles (Atlas, ...) might be already copied!\n' + \ 'Do you want to copy them again?\nType: YES!' ) settings['Copy_software_meta'] = bool(result_session) else: settings['Copy_software_meta'] = True else: settings['Copy_software_meta'] = True result = True # Create project and settings folder for name in [ 'project_folder', 'settings_folder', 'scratch_folder', 'queue_folder', 'error_folder' ]: try: tu.mkdir_p(settings[name]) except FileNotFoundError: tu.message('Project name cannot be empty') self.enable(True) return None # Start or stop procedure if result: self.content['Button'].start_button.setText('Stop') self.plot_worker.settings = settings self.process_worker.sig_start.emit(settings) self.mount_worker.set_settings(settings=settings) self.save( file_name=os.path.join(settings['settings_folder'], settings['General']['Project name'])) self.save_temp_settings() else: tu.message('Input needs to be "YES!" to work') self.enable(True)
def send_notification(self, text): """ Send a notification to specified users. Arguments: text - Text to send Returns: None """ text = '{0}:\n{1}'.format(os.uname()[1], text) chosen_users = self.get_settings() for entry in chosen_users: for key in entry: name, state = entry[key].split('\t') if self.enable_telegram: telegram_name = name[2:] if telegram_name in self.users_telegram and \ state == 'True' and \ name.startswith('T '): user_id = self.users_telegram[telegram_name] try: self.bot.sendMessage(user_id, text) except telepot.exception.BotWasBlockedError as err: if user_id in self.block_list: pass else: self.block_list.append(user_id) tu.message('{0}\n{1}\t{2}'.format( os.uname()[1], name, err )) except telepot.exception.TelegramError as err: if user_id in self.block_list: pass else: self.block_list.append(user_id) tu.message('{0}\n{1}\t{2}'.format( os.uname()[1], name, err )) else: if user_id in self.block_list: self.block_list.remove(user_id) else: pass else: pass else: pass if self.enable_email: email_name = name[2:] if email_name in self.users_email and \ state == 'True' and \ name.startswith('@ '): email = self.users_email[email_name] msg = MIMEText(text) msg['Subject'] = 'Transphire message {0}'.format( os.uname()[1] ) msg['From'] = self.sending_email msg['To'] = email try: server = smtplib.SMTP(self.smtp_server, timeout=10) except smtplib.SMTPServerDisconnected: print('Could not send to:', name) continue try: server.sendmail(self.sending_email, [email], msg.as_string()) except smtplib.SMTPServerDisconnected: print('Could not send to:', name) continue try: server.quit() except smtplib.SMTPServerDisconnected: print('Could not send to:', name) continue else: pass else: pass else: pass