class NewReminderWindow(QWidget): def __init__(self): QWidget.__init__(self) self.resize(500, 250) self.setWindowTitle('Add New Reminder') self.new_reminder_text_input = QPlainTextEdit() self.new_reminder_text_input.setFixedHeight(90) self.new_reminder_text_input.setFixedWidth(500) self.cancel_button = QPushButton('Cancel') self.cancel_button.setFixedWidth( self.cancel_button.minimumSizeHint().width()) self.cancel_button.clicked.connect(self.on_cancel_clicked) self.save_button = QPushButton('Save') self.save_button.setFixedWidth( (self.save_button.minimumSizeHint().width())) self.v_box = QVBoxLayout() self.h_box1 = QHBoxLayout() self.h_box1.addWidget(self.new_reminder_text_input, alignment=Qt.AlignHCenter) self.h_box2 = QHBoxLayout() self.h_box2.addWidget(self.cancel_button) self.h_box2.addWidget(self.save_button) self.v_box.addLayout(self.h_box1) self.v_box.addLayout(self.h_box2) self.setLayout(self.v_box) @Slot() def on_cancel_clicked(self): self.hide() self.new_reminder_text_input.clear()
class Log(QWidget): def __init__(self, sender, title=''): super(Log, self).__init__() self.main_layout = QVBoxLayout() self.header_layout = QHBoxLayout() title_label = QLabel(title) title_label.setFont(QFont('Poppins', 13)) self.header_layout.addWidget(title_label) holder_label = QLabel(str(sender)) holder_label.setWordWrap(True) self.log_view = QPlainTextEdit() self.log_view.setReadOnly(True) self.main_layout.addLayout(self.header_layout) self.main_layout.addWidget(holder_label) self.main_layout.addWidget(self.log_view) self.setLayout(self.main_layout) self.setStyleSheet(''' QLabel { border: None; } QWidget { color: #e9f4fb; } ''') def log(self, *args): s = '' for arg in args: s += ' ' + str(arg) self.log_view.appendPlainText('> ' + s) def clear(self): self.log_view.clear() def removing(self): # old method, delete later self.remove() def remove(self): self.log_view.setStyleSheet('background: black; color: grey;') remove_button = QPushButton('x') remove_button.clicked.connect(self.remove_clicked) self.header_layout.addWidget(remove_button) def remove_clicked(self): self.setParent(None) # removes the widget
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setObjectName("main_window") self.setEnabled(True) self.resize(608, 248) size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) size_policy.setHorizontalStretch(0) size_policy.setVerticalStretch(0) size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(size_policy) self.setAcceptDrops(True) self.setWindowTitle("FileHookPi") self.central_widget = QWidget(self) self.central_widget.setObjectName("central_widget") self.horizontalLayout = QHBoxLayout(self.central_widget) self.horizontalLayout.setObjectName("horizontalLayout") self.text_box = QPlainTextEdit(self.central_widget) self.text_box.setAcceptDrops(False) self.text_box.setReadOnly(True) self.text_box.setObjectName("text_box") self.horizontalLayout.addWidget(self.text_box) self.buttons_vertical_layout = QVBoxLayout() self.buttons_vertical_layout.setObjectName("buttons_vertical_layout") self.open_file_button = QPushButton(self.central_widget) self.open_file_button.setText("Open File") self.open_file_button.setObjectName("open_file_button") self.open_file_button.clicked.connect(self.select_file) self.buttons_vertical_layout.addWidget(self.open_file_button) self.line_counter = QLineEdit(self.central_widget) size_policy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) size_policy.setHorizontalStretch(0) size_policy.setVerticalStretch(0) size_policy.setHeightForWidth( self.line_counter.sizePolicy().hasHeightForWidth()) self.line_counter.setSizePolicy(size_policy) self.line_counter.setAcceptDrops(False) self.line_counter.setReadOnly(True) self.line_counter.setObjectName("line_counter") self.buttons_vertical_layout.addWidget(self.line_counter) self.next_line_button = QPushButton(self.central_widget) self.next_line_button.setText("Next Line") self.next_line_button.setObjectName("next_line_button") self.next_line_button.clicked.connect(self.advance_line) self.buttons_vertical_layout.addWidget(self.next_line_button) self.horizontalLayout.addLayout(self.buttons_vertical_layout) self.menuBar().addMenu(FileMenu(self)) self.setCentralWidget(self.central_widget) self.current_file = None self.line_count = 0 def select_file(self, *, advance=True): path, *_ = QFileDialog.getOpenFileName(self, "Open File", filter="Text Files (*.txt)") if not path: return self.open_file(path, advance=advance) def open_file(self, path, *, advance=True): if self.current_file is not None: self.cleanup() self.current_file = open(path, "r", encoding="utf-8") self.setWindowFilePath(path) if advance: self.advance_line() def advance_line(self): if self.current_file is None: return try: line = self.current_file.readline() except Exception as exc: message_box = QMessageBox( parent=self, text=f"Failed to read:\n{type(exc).__name__}: {exc}", icon=QMessageBox.Critical) message_box.setInformativeText("Close the file?") message_box.setStandardButtons(QMessageBox.No | QMessageBox.Yes) message_box.setDefaultButton(QMessageBox.Yes) if message_box.exec_() == QMessageBox.Yes: self.cleanup() return if not line: return self.line_count += 1 self.line_counter.setText(str(self.line_count)) QApplication.clipboard().setText(line.rstrip("\n")) self.text_box.setPlainText(line + self.text_box.toPlainText()) def cleanup(self): if self.current_file is not None: self.current_file.close() self.current_file = None self.text_box.clear() self.line_count = 0 self.line_counter.clear() self.setWindowFilePath("") def dragEnterEvent(self, event): urls = event.mimeData().urls() if urls and os.path.isfile(urls[0].path()): event.accept() def dropEvent(self, event): self.open_file(event.mimeData().urls()[0].path()) def closeEvent(self, event): self.cleanup() event.accept()
class Widget(QWidget): movie_url_label_text = "粘贴有 dailymotion 版的連續劇网址 (例子: https://dramaq.de/cn191023b/): " def __init__(self): QWidget.__init__(self) self.q = None self.pool = None self.top = QHBoxLayout() self.top.setMargin(10) self.middle = QVBoxLayout() self.middle.setMargin(10) self.radioButtonDailyMotionDrama = QRadioButton( "有 Dailymotion Drama 的網站") self.radioButtonAny = QRadioButton("其它類型網站 (例如 YouTube)") self.top.addWidget(self.radioButtonDailyMotionDrama) self.top.addWidget(self.radioButtonAny) self.url_label = QLabel() self.url = QLineEdit() self.url_label.setBuddy(self.url) self.middle.addWidget(self.url_label) self.middle.addWidget(self.url) self.browse_folder_label = QLabel("下載到:") self.browseFolder = QPushButton("選擇目錄") self.browse_folder_label.setBuddy(self.browseFolder) self.middle.addWidget(self.browse_folder_label) self.middle.addWidget(self.browseFolder) self.browse_folder_value = "" self.bk_cinemae_spin_from = 1 self.bk_cinemae_spin_to = 1 self.fromEpSpinBox = QSpinBox() self.fromEpSpinBox.setMinimum(1) self.fromEpSpinBox.setMaximum(2147483647) self.fromEpLabel = QLabel("&從第幾集開始下載:") self.fromEpLabel.setBuddy(self.fromEpSpinBox) self.toEpSpinBox = QSpinBox() self.toEpSpinBox.setMinimum(1) self.toEpSpinBox.setMaximum(2147483647) self.toEpLabel = QLabel("&到第幾集停止下載:") self.toEpLabel.setBuddy(self.toEpSpinBox) self.cinema_ly = QHBoxLayout() #self.cinema_ly.setMargin(10) self.cinema_ly.addWidget(self.fromEpLabel) self.cinema_ly.addWidget(self.fromEpSpinBox) self.cinema_ly.addWidget(self.toEpLabel) self.cinema_ly.addWidget(self.toEpSpinBox) self.middle.addLayout(self.cinema_ly) self.add = QPushButton("開始下載") self.add.setEnabled(False) self.middle.addWidget(self.add) self.stop_me = QPushButton("停止下載") self.stop_me.setEnabled(False) self.middle.addWidget(self.stop_me) self.log_area = QPlainTextEdit() self.log_area.setReadOnly(True) self.log_area.setMaximumBlockCount(1000) self.middle.addWidget(self.log_area) #self.table_view.setSizePolicy(size) #self.layout.addWidget(self.table) self.layout = QVBoxLayout() self.layout.addLayout(self.top) self.layout.addLayout(self.middle) self.setLayout(self.layout) self.radioButtonDailyMotionDrama.toggled.connect( self.choose_DailyMotionDrama_widgets) self.radioButtonAny.toggled.connect(self.choose_Any_widgets) self.url.textChanged[str].connect(self.check_disable_download) self.browseFolder.clicked.connect(self.add_folder) self.add.clicked.connect(self.start_download) self.stop_me.clicked.connect(self.stop_download) self.radioButtonDailyMotionDrama.setChecked( True) #set default only after .connect above # TESTING PURPOSE ''' self.url.setText('https://journalflash.com/cn191023b/') self.browse_folder_value = 'C:/Users/Administrator/Documents/duboku' ''' #set current process (not queue that one) log handler: logger = logging.getLogger(__name__) handler2 = LoggerWriter() logger.addHandler(handler2) logger.setLevel(logging.INFO) #DEBUG handler2.emitter.sigLog.connect(self.log_area.appendPlainText) sys.stdout = handler2 #LoggerWriter() #sys.stderr = handler2 #Seems no difference #handler2.emitter.sigLog.emit('hihi') @Slot() def choose_DailyMotionDrama_widgets(self): if self.radioButtonDailyMotionDrama.isChecked(): self.fromEpLabel.setEnabled(True) self.toEpLabel.setEnabled(True) self.fromEpSpinBox.setEnabled(True) self.toEpSpinBox.setEnabled(True) self.fromEpSpinBox.setValue(self.bk_cinemae_spin_from) self.toEpSpinBox.setValue(self.bk_cinemae_spin_to) self.fromEpLabel.setDisabled(True) self.toEpLabel.setDisabled(True) @Slot() def choose_Any_widgets(self): if self.radioButtonAny.isChecked(): self.fromEpSpinBox.setDisabled(True) self.toEpSpinBox.setDisabled(True) self.bk_cinemae_spin_from = self.fromEpSpinBox.value() self.bk_cinemae_spin_to = self.toEpSpinBox.value() self.fromEpSpinBox.setValue(1) self.toEpSpinBox.setValue(1) @Slot() def add_folder(self, s): #fname = QFileDialog.getOpenFileName(self, 'Open file', "c:\'", "Image files (*.jpg *.gif)") #fname = QFileDialog.getOpenFileName(self, 'Open file', '', QFileDialog.ShowDirsOnly) fname = QFileDialog.getExistingDirectory(self, '選擇下載至什麼目錄', '', QFileDialog.ShowDirsOnly) #print('repr: ' + repr(fname)) if fname and fname.strip(): fname = fname.strip() self.browse_folder_value = fname #if getOpenFileName, will return ('/home/xiaobai/Pictures/disco.jpg', 'Image files (*.jpg *.gif)') #, while if getExistingDirectory, will return single path string only self.browseFolder.setText(fname) self.check_disable_download(fname) #else: # print('User cancel') @Slot() def check_disable_download(self, s): if self.url.text() and self.browse_folder_value: self.add.setEnabled(True) else: self.add.setEnabled(False) def task_done(self, retVal): self.add.setEnabled(True) self.stop_me.setEnabled(False) @Slot() def stop_download(self): if self.q: self.q.close() if self.pool: self.pool.terminate() self.add.setEnabled(True) self.stop_me.setEnabled(False) print('下載停止。') @Slot() def start_download(self): if self.fromEpSpinBox.value() > self.toEpSpinBox.value(): self.log_area.setPlainText('[!] 從第幾集必須小於或等於到第幾集。') return #No need worry click twice too fast, it seems already handle by PySide2 self.add.setEnabled(False) self.stop_me.setEnabled(True) self.log_area.clear() dest_full_path = self.browse_folder_value ''' print('dest_full_path: ' + repr(dest_full_path)) print('self.url.text(): ' + repr(self.url.text())) print('self.fromEpSpinBox.value(): ' + repr(self.fromEpSpinBox.value())) print('self.toEpSpinBox.value(): ' + repr(self.toEpSpinBox.value())) ''' import drama_dailymotion_console #Windows can't set like that bcoz not update for args.url, must put explicitly #drama_dailymotion_console.redirect_stdout_to_custom_stdout(arg_url, ...etc, LoggerWriter()) #failed other process handler = LogHandlerOtherProcess() handler.emitter.sigLog.connect(self.log_area.appendPlainText) ''' #ref current process: logger = logging.getLogger(__name__) handler2 = LoggerWriter() logger.addHandler(handler2) logger.setLevel(logging.DEBUG) handler2.emitter.sigLog.connect(self.log_area.appendPlainText) sys.stdout = handler2 #LoggerWriter() #handler2.emitter.sigLog.emit('hihi') ''' #handler = LoggerWriter() #handler.emitter.sigLog.connect(self.log_area.appendPlainText) self.q = multiprocessing.Queue() self.ql = QueueListener(self.q, handler) self.ql.start() self.pool = multiprocessing.Pool(1, worker_init, [self.q]) if self.radioButtonDailyMotionDrama.isChecked(): self.pool.apply_async(drama_dailymotion_console.main, args=(dest_full_path, self.fromEpSpinBox.value(), self.toEpSpinBox.value(), self.url.text(), LoggerWriterOtherProcess(), False), callback=self.task_done) else: self.pool.apply_async(drama_dailymotion_console.main, args=(dest_full_path, self.fromEpSpinBox.value(), self.toEpSpinBox.value(), self.url.text(), LoggerWriterOtherProcess(), True), callback=self.task_done)
class MainWindow(QMainWindow): def __init__(self, application, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.printer_thread = WorkerThread() self.printer_thread.message.connect(self.standardOutputWritten) self.printer_thread.start() self.app = application self.app.setStyle("Fusion") self.__set_interface() self.__set_layouts() self.__set_stylesheet() self.__set_connections() self.__set_params() def closeEvent(self, event): self.printer_thread.stop() def __set_interface(self): self.button_width = 0.35 self.button_height = 0.05 self.setWindowTitle("GSI-RADS") self.__getScreenDimensions() self.setGeometry(self.left, self.top, self.width, self.height) self.setMaximumWidth(self.width) #self.setMaximumHeight(self.height) self.setMinimumWidth(self.width) self.setMinimumHeight(self.height) self.move(self.width / 2, self.height / 2) self.menu_bar = QMenuBar(self) self.menu_bar.setNativeMenuBar( False ) # https://stackoverflow.com/questions/25261760/menubar-not-showing-for-simple-qmainwindow-code-qt-creator-mac-os self.file_menu = self.menu_bar.addMenu('File') self.import_dicom_action = QAction( QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/database-icon.png')), 'Import DICOM', self) self.import_dicom_action.setShortcut('Ctrl+D') self.file_menu.addAction(self.import_dicom_action) self.quit_action = QAction('Quit', self) self.quit_action.setShortcut("Ctrl+Q") self.file_menu.addAction(self.quit_action) self.settings_menu = self.menu_bar.addMenu('Settings') self.settings_seg_menu = self.settings_menu.addMenu("Segmentation...") self.settings_seg_preproc_menu = self.settings_seg_menu.addMenu( "Preprocessing...") self.settings_seg_preproc_menu_p1_action = QAction( "Brain-masking off (P1)", checkable=True) self.settings_seg_preproc_menu_p2_action = QAction( "Brain-masking on (P2)", checkable=True) self.settings_seg_preproc_menu_p2_action.setChecked(True) self.settings_seg_preproc_menu.addAction( self.settings_seg_preproc_menu_p1_action) self.settings_seg_preproc_menu.addAction( self.settings_seg_preproc_menu_p2_action) self.help_menu = self.menu_bar.addMenu('Help') self.readme_action = QAction( QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/readme-icon.jpeg')), 'Tutorial', self) self.readme_action.setShortcut("Ctrl+R") self.help_menu.addAction(self.readme_action) self.about_action = QAction( QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/about-icon.png')), 'About', self) self.about_action.setShortcut("Ctrl+A") self.help_menu.addAction(self.about_action) self.help_action = QAction( QIcon.fromTheme("help-faq"), "Help", self ) # Default icons can be found here: https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html#guidelines self.help_action.setShortcut("Ctrl+J") self.help_menu.addAction(self.help_action) self.input_image_lineedit = QLineEdit() self.input_image_lineedit.setFixedWidth(self.width * (0.93 - self.button_width / 2)) self.input_image_lineedit.setFixedHeight(self.height * self.button_height) self.input_image_lineedit.setReadOnly(True) self.input_image_pushbutton = QPushButton('Input MRI') self.input_image_pushbutton.setFixedWidth(self.height * self.button_width) self.input_image_pushbutton.setFixedHeight(self.height * self.button_height) self.input_segmentation_lineedit = QLineEdit() self.input_segmentation_lineedit.setReadOnly(True) self.input_segmentation_lineedit.setFixedWidth( self.width * (0.93 - self.button_width / 2)) self.input_segmentation_lineedit.setFixedHeight(self.height * self.button_height) self.input_segmentation_pushbutton = QPushButton('Input segmentation') self.input_segmentation_pushbutton.setFixedWidth(self.height * self.button_width) self.input_segmentation_pushbutton.setFixedHeight(self.height * self.button_height) self.output_folder_lineedit = QLineEdit() self.output_folder_lineedit.setReadOnly(True) self.output_folder_lineedit.setFixedWidth( self.width * (0.93 - self.button_width / 2)) self.output_folder_lineedit.setFixedHeight(self.height * self.button_height) self.output_folder_pushbutton = QPushButton('Output destination') self.output_folder_pushbutton.setFixedWidth(self.height * self.button_width) self.output_folder_pushbutton.setFixedHeight(self.height * self.button_height) self.run_button = QPushButton('Run diagnosis') self.run_button.setFixedWidth(self.height * self.button_width) self.run_button.setFixedHeight(self.height * self.button_height) self.main_display_tabwidget = QTabWidget() self.tutorial_textedit = QPlainTextEdit() self.tutorial_textedit.setReadOnly(True) self.tutorial_textedit.setFixedWidth(self.width * 0.97) self.tutorial_textedit.setPlainText( "HOW TO USE THE SOFTWARE: \n" " 1) Click 'Input MRI...' to select from your file explorer the MRI scan to process (unique file).\n" " 1*) Alternatively, Click File > Import DICOM... if you wish to process an MRI scan as a DICOM sequence.\n" " 2) Click 'Output destination' to choose a directory where to save the results \n" " 3) (OPTIONAL) Click 'Input segmentation' to choose a tumor segmentation mask file, if nothing is provided the internal model with generate the segmentation automatically \n" " 4) Click 'Run diagnosis' to perform the analysis. The human-readable version will be displayed in the interface.\n" " \n" "NOTE: \n" "The output folder is populated automatically with the following: \n" " * The diagnosis results in human-readable text (report.txt) and Excel-ready format (report.csv).\n" " * The automatic segmentation masks of the brain and the tumor in the original patient space (input_brain_mask.nii.gz and input_tumor_mask.nii.gz).\n" " * The input volume and tumor segmentation mask in MNI space in the sub-directory named \'registration\'.\n" ) self.main_display_tabwidget.addTab(self.tutorial_textedit, 'Tutorial') self.prompt_lineedit = QPlainTextEdit() self.prompt_lineedit.setReadOnly(True) self.prompt_lineedit.setFixedWidth(self.width * 0.97) self.main_display_tabwidget.addTab(self.prompt_lineedit, 'Logging') self.results_textedit = QPlainTextEdit() self.results_textedit.setReadOnly(True) self.results_textedit.setFixedWidth(self.width * 0.97) self.main_display_tabwidget.addTab(self.results_textedit, 'Results') self.sintef_logo_label = QLabel() self.sintef_logo_label.setPixmap( QPixmap( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/sintef-logo.png'))) self.sintef_logo_label.setFixedWidth(0.95 * (self.width / 3)) self.sintef_logo_label.setFixedHeight( 1 * (self.height * self.button_height)) self.sintef_logo_label.setScaledContents(True) self.stolavs_logo_label = QLabel() self.stolavs_logo_label.setPixmap( QPixmap( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/stolavs-logo.png'))) self.stolavs_logo_label.setFixedWidth(0.95 * (self.width / 3)) self.stolavs_logo_label.setFixedHeight( 1 * (self.height * self.button_height)) self.stolavs_logo_label.setScaledContents(True) self.amsterdam_logo_label = QLabel() self.amsterdam_logo_label.setPixmap( QPixmap( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/amsterdam-logo.png'))) self.amsterdam_logo_label.setFixedWidth(0.95 * (self.width / 3)) self.amsterdam_logo_label.setFixedHeight( 1 * (self.height * self.button_height)) self.amsterdam_logo_label.setScaledContents(True) def __set_layouts(self): self.input_volume_hbox = QHBoxLayout() self.input_volume_hbox.addStretch(1) self.input_volume_hbox.addWidget(self.input_image_lineedit) self.input_volume_hbox.addWidget(self.input_image_pushbutton) self.input_volume_hbox.addStretch(1) self.input_seg_hbox = QHBoxLayout() self.input_seg_hbox.addStretch(1) self.input_seg_hbox.addWidget(self.input_segmentation_lineedit) self.input_seg_hbox.addWidget(self.input_segmentation_pushbutton) self.input_seg_hbox.addStretch(1) self.output_dir_hbox = QHBoxLayout() self.output_dir_hbox.addStretch(1) self.output_dir_hbox.addWidget(self.output_folder_lineedit) self.output_dir_hbox.addWidget(self.output_folder_pushbutton) self.output_dir_hbox.addStretch(1) self.run_action_hbox = QHBoxLayout() self.run_action_hbox.addStretch(1) self.run_action_hbox.addWidget(self.run_button) self.run_action_hbox.addStretch(1) self.dump_area_hbox = QHBoxLayout() self.dump_area_hbox.addStretch(1) self.dump_area_hbox.addWidget(self.main_display_tabwidget) self.dump_area_hbox.addStretch(1) self.logos_hbox = QHBoxLayout() self.logos_hbox.addStretch(1) self.logos_hbox.addWidget(self.sintef_logo_label) self.logos_hbox.addWidget(self.stolavs_logo_label) self.logos_hbox.addWidget(self.amsterdam_logo_label) self.logos_hbox.addStretch(1) self.main_vbox = QVBoxLayout() self.main_vbox.addWidget(self.menu_bar) #self.main_vbox.addStretch(1) self.main_vbox.addLayout(self.input_volume_hbox) self.main_vbox.addLayout(self.output_dir_hbox) self.main_vbox.addLayout(self.input_seg_hbox) self.main_vbox.addLayout(self.run_action_hbox) #self.main_vbox.addStretch(1) self.main_vbox.addLayout(self.dump_area_hbox) self.main_vbox.addLayout(self.logos_hbox) #self.main_vbox.addStretch(1) self.central_label = QLabel() self.central_label.setLayout(self.main_vbox) self.setCentralWidget(self.central_label) def __set_stylesheet(self): self.central_label.setStyleSheet( 'QLabel{background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(207, 209, 207, 255), stop:1 rgba(230, 229, 230, 255));}' ) self.menu_bar.setStyleSheet(get_stylesheet('QMenuBar')) self.input_image_lineedit.setStyleSheet(get_stylesheet('QLineEdit')) self.input_image_pushbutton.setStyleSheet( get_stylesheet('QPushButton')) self.input_segmentation_lineedit.setStyleSheet( get_stylesheet('QLineEdit')) self.input_segmentation_pushbutton.setStyleSheet( get_stylesheet('QPushButton')) self.output_folder_lineedit.setStyleSheet(get_stylesheet('QLineEdit')) self.output_folder_pushbutton.setStyleSheet( get_stylesheet('QPushButton')) self.results_textedit.setStyleSheet(get_stylesheet('QTextEdit')) self.prompt_lineedit.setStyleSheet(get_stylesheet('QTextEdit')) self.run_button.setStyleSheet(get_stylesheet('QPushButton')) def __set_connections(self): self.run_button.clicked.connect(self.diagnose_main_wrapper) self.input_image_pushbutton.clicked.connect( self.run_select_input_image) self.input_segmentation_pushbutton.clicked.connect( self.run_select_input_segmentation) self.output_folder_pushbutton.clicked.connect( self.run_select_output_folder) self.readme_action.triggered.connect(self.readme_action_triggered) self.about_action.triggered.connect(self.about_action_triggered) self.quit_action.triggered.connect(self.quit_action_triggered) self.import_dicom_action.triggered.connect( self.import_dicom_action_triggered) self.help_action.triggered.connect(self.help_action_triggered) self.settings_seg_preproc_menu_p1_action.triggered.connect( self.settings_seg_preproc_menu_p1_action_triggered) self.settings_seg_preproc_menu_p2_action.triggered.connect( self.settings_seg_preproc_menu_p2_action_triggered) def __set_params(self): self.input_image_filepath = '' self.input_annotation_filepath = '' self.output_folderpath = '' def __getScreenDimensions(self): screen = self.app.primaryScreen() size = screen.size() self.left = size.width() / 2 self.top = size.height() / 2 self.width = 0.4 * size.width() self.height = 0.4 * size.height() def readme_action_triggered(self): popup = QMessageBox() popup.setWindowTitle('Tutorial') popup.setText( "HOW TO USE THE SOFTWARE: \n" " 1) Click 'Input MRI...' to select from your file explorer the MRI scan to process (unique file).\n" " 1*) Alternatively, Click File > Import DICOM... if you wish to process an MRI scan as a DICOM sequence.\n" " 2) Click 'Output destination' to choose a directory where to save the results \n" " 3) (OPTIONAL) Click 'Input segmentation' to choose a tumor segmentation mask file, if nothing is provided the internal model with generate the segmentation automatically \n" " 4) Click 'Run diagnosis' to perform the analysis. The human-readable version will be displayed in the interface.\n" " \n" "NOTE: \n" "The output folder is populated automatically with the following: \n" " * The diagnosis results in human-readable text (report.txt) and Excel-ready format (report.csv).\n" " * The automatic segmentation masks of the brain and the tumor in the original patient space (input_brain_mask.nii.gz and input_tumor_mask.nii.gz).\n" " * The input volume and tumor segmentation mask in MNI space in the sub-directory named \'registration\'.\n" ) popup.exec_() def about_action_triggered(self): popup = QMessageBox() popup.setWindowTitle('About') popup.setText( 'Software developed as part of a collaboration between: \n' ' * Departement of Health Research, SINTEF\n' ' * St. Olavs hospital, Trondheim University Hospital\n' ' * Amsterdam University Medical Center\n\n' 'Contact: David Bouget, Andre Pedersen\n\n' 'For questions about the software, please visit:\n' 'https://github.com/SINTEFMedtek/GSI-RADS\n' 'For questions about the methodological aspect, please refer to the original publication:\n' 'https://www.mdpi.com/2072-6694/13/12/2854/review_report') popup.exec_() def quit_action_triggered(self): self.printer_thread.stop() sys.exit() def diagnose_main_wrapper(self): self.run_diagnosis_thread = threading.Thread(target=self.run_diagnosis) self.run_diagnosis_thread.daemon = True # using daemon thread the thread is killed gracefully if program is abruptly closed self.run_diagnosis_thread.start() def run_diagnosis(self): if not os.path.exists(self.input_image_filepath) or not os.path.exists( self.output_folderpath): self.standardOutputWritten( 'Process could not be started - The 1st and 2nd above-fields must be filled in.\n' ) return self.run_button.setEnabled(False) self.prompt_lineedit.clear() self.main_display_tabwidget.setCurrentIndex(1) QApplication.processEvents( ) # to immidiently update GUI after button is clicked self.seg_preprocessing_scheme = 'P1' if self.settings_seg_preproc_menu_p1_action.isChecked( ) else 'P2' try: start_time = time.time() print('Initialize - Begin (Step 0/6)') from diagnosis.main import diagnose_main print('Initialize - End (Step 0/6)') print('Step runtime: {} seconds.'.format( np.round(time.time() - start_time, 3)) + "\n") diagnose_main( input_volume_filename=self.input_image_filepath, input_segmentation_filename=self.input_annotation_filepath, output_folder=self.output_folderpath, preprocessing_scheme=self.seg_preprocessing_scheme) except Exception as e: print('{}'.format(traceback.format_exc())) self.run_button.setEnabled(True) self.standardOutputWritten( 'Process could not be completed - Issue arose.\n') QApplication.processEvents() return self.run_button.setEnabled(True) results_filepath = os.path.join( ResourcesConfiguration.getInstance().output_folder, 'report.txt') self.results_textedit.setPlainText(open(results_filepath, 'r').read()) self.main_display_tabwidget.setCurrentIndex(2) def run_select_input_image(self): input_image_filedialog = QFileDialog() self.input_image_filepath = input_image_filedialog.getOpenFileName( self, 'Select input T1 MRI', '~', "Image files (*.nii *.nii.gz *.nrrd *.mha *.mhd)")[0] self.input_image_lineedit.setText(self.input_image_filepath) def run_select_input_segmentation(self): filedialog = QFileDialog() self.input_annotation_filepath = filedialog.getOpenFileName( self, 'Select input segmentation file', '~', "Image files (*.nii *.nii.gz)")[0] self.input_segmentation_lineedit.setText( self.input_annotation_filepath) def import_dicom_action_triggered(self): filedialog = QFileDialog() filedialog.setFileMode(QFileDialog.DirectoryOnly) self.input_image_filepath = filedialog.getExistingDirectory( self, 'Select DICOM folder', '~') self.input_image_lineedit.setText(self.input_image_filepath) def run_select_output_folder(self): filedialog = QFileDialog() filedialog.setFileMode(QFileDialog.DirectoryOnly) self.output_folderpath = filedialog.getExistingDirectory( self, 'Select output folder', '~') self.output_folder_lineedit.setText(self.output_folderpath) def standardOutputWritten(self, text): self.prompt_lineedit.moveCursor(QTextCursor.End) self.prompt_lineedit.insertPlainText(text) QApplication.processEvents() def help_action_triggered(self): # opens browser with specified url, directs user to Issues section of GitHub repo QDesktopServices.openUrl( QUrl("https://github.com/SINTEFMedtek/GSI-RADS/issues")) def settings_seg_preproc_menu_p1_action_triggered(self, status): if status: self.settings_seg_preproc_menu_p2_action.setChecked(False) else: self.settings_seg_preproc_menu_p2_action.setChecked(True) def settings_seg_preproc_menu_p2_action_triggered(self, status): if status: self.settings_seg_preproc_menu_p1_action.setChecked(False) else: self.settings_seg_preproc_menu_p1_action.setChecked(True)
class Log(QWidget): def __init__(self, sender, title=''): super(Log, self).__init__() self.main_layout = QVBoxLayout() self.header_layout = QHBoxLayout() title_label = QLabel(title) title_label.setFont(QFont('Poppins', 13)) self.header_layout.addWidget(title_label) self.remove_button = QPushButton('x') self.remove_button.clicked.connect(self.remove_clicked) self.header_layout.addWidget(self.remove_button) self.remove_button.hide() holder_label = QLabel(shorten(str(sender), 76)) holder_label.setWordWrap(True) self.log_view = QPlainTextEdit() self.log_view.setReadOnly(True) self.main_layout.addLayout(self.header_layout) self.main_layout.addWidget(holder_label) self.main_layout.addWidget(self.log_view) self.setLayout(self.main_layout) self.enabled_style_sheet = ''' QLabel { border: None; } QWidget { color: #e9f4fb; } ''' self.disabled_style_sheet = ''' QLabel { border: None; } QWidget { color: #e9f4fb; } QPlainTextEdit { background: black; color: grey; } ''' self.setStyleSheet(self.enabled_style_sheet) def log(self, *args): s = '' for arg in args: s += ' ' + str(arg) self.log_view.appendPlainText('> ' + s) def clear(self): self.log_view.clear() def disable(self): self.remove_button.show() self.setStyleSheet(self.disabled_style_sheet) def enable(self): self.remove_button.hide() self.setStyleSheet(self.enabled_style_sheet) self.show() def remove_clicked(self): self.hide()
class VentanaAdministradorDePersonas(QFrame): def __init__(self, parent=None): super(VentanaAdministradorDePersonas, self).__init__(parent) raiz.setWindowTitle("Administracion de personas") self.operacionesConPersonas = OperacionesConPersonas() self.instanciarVentana() def instanciarVentana(self): #Contenedor De todas las cosas de arriba a abajo layoutGeneral = QVBoxLayout() #Cosas de Derecha layoutDerecha = QVBoxLayout() #Ordena cosas de arriba a abajo layoutDerecha.setMargin(20) self.btnAgregarPersona = QPushButton("Agregar persona") self.btnAgregarPersona.setToolTip("Toma los datos ") self.btnAgregarPersona.clicked.connect(self.agregarPersona) layoutDerecha.addWidget(self.btnAgregarPersona) self.btnBuscarPersona = QPushButton("Buscar persona") self.btnBuscarPersona.setToolTip("Busca por cedula a una persona") self.btnBuscarPersona.clicked.connect(self.buscarPersona) layoutDerecha.addWidget(self.btnBuscarPersona) self.btnEliminarPersona = QPushButton("Eliminar persona") self.btnEliminarPersona.setToolTip( "Elimina a la persona que esta en busqueda") self.btnEliminarPersona.clicked.connect(self.eliminarPersona) layoutDerecha.addWidget(self.btnEliminarPersona) self.btnActualizarPersona = QPushButton("Actualizar persona") self.btnActualizarPersona.setToolTip( "Cambia los datos de la persona seleccionada") self.btnActualizarPersona.clicked.connect(self.actualizarPersona) layoutDerecha.addWidget(self.btnActualizarPersona) self.btnLimpiarTexto = QPushButton("Limpiar") self.btnLimpiarTexto.setToolTip( "Limpia los campos de texto para ingresar datos") self.btnLimpiarTexto.clicked.connect(self.limpiarCampos) layoutDerecha.addWidget(self.btnLimpiarTexto) self.btnVolverAlMenu = QPushButton("Volver") self.btnVolverAlMenu.setToolTip("Vuelve al menu principal") self.btnVolverAlMenu.clicked.connect(self.volverAlMenu) layoutDerecha.addWidget(self.btnVolverAlMenu) #Cosas de Izquierda layoutIzquierda = QVBoxLayout() #ordena cosas de arriba a abajo self.lblDNI = QLabel("DNI: ") layoutIzquierda.addWidget(self.lblDNI) self.DNI = QLineEdit() layoutIzquierda.addWidget(self.DNI) self.lblPrimerNombre = QLabel("Primer nombre: ") layoutIzquierda.addWidget(self.lblPrimerNombre) self.PrimerNombre = QLineEdit() layoutIzquierda.addWidget(self.PrimerNombre) self.lblSegundoNombre = QLabel("Segundo nombre: ") layoutIzquierda.addWidget(self.lblSegundoNombre) self.SegundoNombre = QLineEdit() layoutIzquierda.addWidget(self.SegundoNombre) self.lblPrimerApellido = QLabel("Primer apellido: ") layoutIzquierda.addWidget(self.lblPrimerApellido) self.PrimerApellido = QLineEdit() layoutIzquierda.addWidget(self.PrimerApellido) self.lblSegundoApellido = QLabel("Segundo apellido: ") layoutIzquierda.addWidget(self.lblSegundoApellido) self.SegundoApellido = QLineEdit() layoutIzquierda.addWidget(self.SegundoApellido) self.lblFechaDeNacimiento = QLabel("Fecha de nacimiento") layoutIzquierda.addWidget(self.lblFechaDeNacimiento) self.fechaDeNacimiento = QDateEdit() self.fechaDeNacimiento.setDisplayFormat("yyyy-MM-dd") self.fechaDeNacimiento.setCalendarPopup(True) self.fechaDeNacimiento.setDate(date.today()) layoutIzquierda.addWidget(self.fechaDeNacimiento) #Cosas de layout de arriba layoutSuperior = QHBoxLayout() #Ordena cosas de derecha a izquierda layoutSuperior.addLayout(layoutIzquierda) layoutSuperior.addLayout(layoutDerecha) #Cosas layout de abajo layoutInferior = QVBoxLayout() self.mostrarError = QPlainTextEdit(readOnly=True) layoutInferior.addWidget(self.mostrarError) #cosas del layout general layoutGeneral.addLayout(layoutSuperior) layoutGeneral.addLayout(layoutInferior) self.setLayout(layoutGeneral) #Redimensionar raiz.adjustSize() self.adjustSize() def agregarPersona(self): self.limpiarErrores() try: laPersona = self.llenarPersona() salida = self.operacionesConPersonas.insertarPersona(laPersona) self.mostrarError.insertPlainText(str(salida)) self.limpiarCampos( ) #habilitamos la edicion y evitamos dejar datos basura except Exception as e: self.mostrarError.insertPlainText(''.join(e.args)) pass def buscarPersona(self): self.limpiarErrores() try: laPersona = self.operacionesConPersonas.buscarPersonaPorDNI( int(''.join(self.DNI.text()))) if type(laPersona) == type(""): self.mostrarError.insertPlainText(''.join(laPersona)) return None self.llenarCampos(laPersona) except Exception as e: self.mostrarError.insertPlainText(''.join(e.args)) def eliminarPersona(self): self.limpiarErrores() try: laPersona = self.llenarPersona() mensajeDeConfirmacion = "DNI: " + self.DNI.text() opcionElegida = QMessageBox.question( self, "Desea eliminar a la persona?", mensajeDeConfirmacion, QMessageBox.Yes, QMessageBox.No) if opcionElegida == QMessageBox.Yes: respuesta = self.operacionesConPersonas.eliminarPersona( laPersona) if type(respuesta) == type(""): self.mostrarError.insertPlainText(''.join(respuesta)) self.limpiarCampos() return None except Exception as e: self.mostrarError.insertPlainText(''.join(e.args)) def actualizarPersona(self): self.limpiarErrores() try: laPersona = self.llenarPersona() mensajeDeConfirmacion = "DNI: " + self.DNI.text() opcionElegida = QMessageBox.question( self, "Desea eliminar a la persona?", mensajeDeConfirmacion, QMessageBox.Yes, QMessageBox.No) if opcionElegida == QMessageBox.Yes: respuesta = self.operacionesConPersonas.actualizarPersona( laPersona) if type(respuesta) == type(""): self.mostrarError.insertPlainText(''.join(respuesta)) self.limpiarCampos() return None except Exception as e: print(e.args) def llenarCampos(self, laPersona): self.limpiarCampos() self.DNI.setText(str(laPersona.getDNI())) self.DNI.setReadOnly(True) self.PrimerNombre.setText(laPersona.getPrimerNombre()) self.SegundoNombre.setText(laPersona.getSegundoNombre()) self.PrimerApellido.setText(laPersona.getPrimerApellido()) self.SegundoApellido.setText(laPersona.getSegundoApellido()) self.fechaDeNacimiento.setDate(laPersona.getFechaDeNacimiento()) def llenarPersona(self): return Persona(int(''.join(self.DNI.text())), ''.join(self.PrimerNombre.text()), ''.join(self.SegundoNombre.text()), ''.join(self.PrimerApellido.text()), ''.join(self.SegundoApellido.text()), ''.join(self.fechaDeNacimiento.text())) def limpiarCampos(self): self.DNI.clear() self.DNI.setReadOnly(False) self.PrimerNombre.clear() self.SegundoNombre.clear() self.PrimerApellido.clear() self.SegundoApellido.clear() self.fechaDeNacimiento.setDate(date.today()) #limpiar campos de texto y permitir la edicion de todos ellos def limpiarErrores(self): self.mostrarError.clear() def volverAlMenu(self): raiz.setCentralWidget(None) raiz.setCentralWidget(VentanaPrincipal())
class TrainingConsoleWidget(QWidget): """The TrainingConsoleWidget provides a widget for controlling the training status of a model, in a simple way. (Analog of the console output in Keras, but with a few more options). It also can save and show an history of the last trained models. """ training_started = Signal() training_stopped = Signal() class TrainingStatus(Enum): Running = 1 Stopped = 2 Not_Compiled = 3 def __init__(self, parent: "QWidget" = None): super().__init__(parent) # Components self._pretrained_model: Optional["keras.models.Model"] = None self._ttv: Optional["TTVSets"] = None self._hyperparameters: Optional[Dict] = None self._callbacks: List[Callback] = [] self._trained_model: Optional["keras.models.Model"] = None # Widgets self._start_training_button = QPushButton("Start training") self._stop_training_button = QPushButton("Stop training") self._buttons_layout = QHBoxLayout() self._buttons_layout.addWidget(self._start_training_button) self._buttons_layout.addWidget(self._stop_training_button) self._status_label = QLabel() self._batch_progress_bar = QProgressBar() self._epoch_progress_bar = QProgressBar() self.training_output_textbox = QPlainTextEdit() self.training_output_textbox.setReadOnly(True) console_output_group = QGroupBox("Console output") console_output_layout = QVBoxLayout() console_output_layout.setContentsMargins(0, 0, 0, 0) console_output_layout.addWidget(self.training_output_textbox) console_output_group.setLayout(console_output_layout) self._main_layout = QVBoxLayout() self._main_layout.addLayout(self._buttons_layout) self._main_layout.addWidget(self._status_label, Qt.AlignRight) self._main_layout.addWidget(console_output_group) self._main_layout.addWidget(self._batch_progress_bar) self._main_layout.addWidget(self._epoch_progress_bar) self.setLayout(self._main_layout) # Connections self._start_training_button.clicked.connect(self.start_training) self._stop_training_button.clicked.connect(self.stop_training) # Inner workings self.training_status = self.TrainingStatus.Not_Compiled self._training_thread = None @property def training_status(self): """Returns the current status of the training (Running, Stopped...)""" return self._training_status @training_status.setter def training_status(self, new_status): """Changes the training status. Doing so will update the interface accordingly. """ self._training_status = new_status if self._training_status == self.TrainingStatus.Running: self._start_training_button.setEnabled(False) self._stop_training_button.setEnabled(True) self._status_label.setText("Running") self.start_training elif self._training_status == self.TrainingStatus.Stopped: self._start_training_button.setEnabled(True) self._stop_training_button.setEnabled(False) self._status_label.setText("Stopped") elif self._training_status == self.TrainingStatus.Not_Compiled: self._start_training_button.setEnabled(True) self._stop_training_button.setEnabled(False) self._status_label.setText("Not Compiled") def set_ttv(self, ttv: "TTVSets"): """Sets the Train/Test/Validation models used for training.""" self._ttv = ttv def set_pretrained_model(self, pretrained_model: "Model"): """Sets a new pretrained model for training.""" self._pretrained_model = pretrained_model self.training_status = self.TrainingStatus.Not_Compiled def set_hyperparameters(self, hyperparameters: Dict): """Sets new hyperparameters for training.""" self._hyperparameters = hyperparameters self.training_status = self.TrainingStatus.Not_Compiled def set_callbacks(self, callbacks: List[Callback]): self._callbacks = callbacks def get_trained_model(self): """Returns the model after it has been trained.""" return self._trained_model def compile_model(self): """Compile the model with the passed hyperparameters. The dataset is needed for the input shape.""" LOGGER.info("Starting to compile the model...") if not self._is_input_ready(): return False # Create a new model based on the pretrained one, but with a new InputLayer # compatible with the dataset if self._pretrained_model.layers[0].__class__.__name__ != "InputLayer": input_layer = Input(self._ttv.train.input_shape) output = self._pretrained_model(input_layer) self._trained_model = Model(input_layer, output) else: self._trained_model = self._pretrained_model try: self._trained_model.compile( optimizer=self._hyperparameters["optimizer"], loss=self._hyperparameters["loss_function"], metrics=["accuracy"], ) self._trained_model.summary() LOGGER.info("Model compiled successfully!!") self.training_status = self.TrainingStatus.Stopped return True except Exception as err: LOGGER.exception("Model Compiling error: ", err) self.training_output_textbox.setPlainText( "> Error while compiling the model:\n", str(err)) return False def start_training(self): """Starts the training on a new thread.""" if self.training_status == self.TrainingStatus.Not_Compiled: successfully_compiled = self.compile_model() if not successfully_compiled: LOGGER.info("Couldn't compile model. Training not started.") return total_train_batches = len(self._ttv.train) total_train_epochs = self._hyperparameters["epochs"] self._batch_progress_bar.setMaximum(total_train_batches) self._epoch_progress_bar.setMaximum(total_train_epochs) self._epoch_progress_bar.setValue(0) self.training_output_textbox.clear() def epoch_begin_update(epoch: int, logs): message = f"==== Epoch {epoch + 1}/{total_train_epochs} ====" LOGGER.info(message) self.training_output_textbox.appendPlainText(message) self._epoch_progress_bar.setValue(epoch) def batch_end_update(batch: int, logs): # Update progress self._batch_progress_bar.setValue(batch) # Log metrics on console message = f"{batch}/{total_train_batches}" for (k, v) in list(logs.items()): message += f" - {k}: {v:.4f}" LOGGER.info(message) self.training_output_textbox.appendPlainText(message) def train_end_update(logs): # Put the progress bar at 100% when the training ends self._batch_progress_bar.setValue( self._batch_progress_bar.maximum()) self._epoch_progress_bar.setValue( self._epoch_progress_bar.maximum()) # Stop the training self.stop_training() # Connect callbacks signals_callback = SignalsCallback() signals_callback.epoch_begin.connect(epoch_begin_update) signals_callback.train_batch_end.connect(batch_end_update) signals_callback.train_end.connect(train_end_update) self.training_stopped.connect(signals_callback.stop_model) print(self._callbacks) # log_dir = "logs/fit/" + datetime.datetime.now().strftime("%Y_%m_%d-%H_%M_%S") # tb = program.TensorBoard() # tb.configure(argv=[None, "--logdir", log_dir]) # url = tb.launch() # print("Launched Tensorboard instance in:", url) # tf_callback=tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1) # tf_callback.set_model(self._trained_model) # Start training self._fit_worker = FitWorker( self._trained_model, self._ttv.train, self._hyperparameters, callbacks=[signals_callback] + self._callbacks, ) self.training_status = self.TrainingStatus.Running self._fit_worker.start() self.training_started.emit() def stop_training(self): """Stops the training.""" self.training_status = self.TrainingStatus.Stopped self.training_stopped.emit() def _is_input_ready(self) -> bool: """Checks if the input values used for training (model, dataset, hyperparameters...) are valid.""" message = "" if not self._ttv.train: message += "> Training dataset not specified\n" if not self._pretrained_model: message += "> Model not specified.\n" if not self._hyperparameters: message += "> Hyperparameters not specified.\n" if message: self.training_output_textbox.setPlainText(message) LOGGER.info(message) return False return True def sizeHint(self) -> "QSize": """Returns the expected size of the widget.""" return QSize(500, 300) def __reduce__(self): return (TrainingConsoleWidget, ())
class Widget(QWidget): cinema_url_label_text = "粘贴独播库的连续剧/综艺/动漫 URL: " movie_url_label_text = "粘贴独播库的电影 URL: " cinema_dest_label_text = "输入连续剧/综艺/动漫名 (用来命名下载的目录): " movie_dest_label_text = "输入电影名 (用来命名下载的文件): " def __init__(self): QWidget.__init__(self) self.q = None self.pool = None self.top = QHBoxLayout() self.top.setMargin(10) self.radioButtonMov = QRadioButton("电影") self.radioButtonCinema = QRadioButton("连续剧/综艺/动漫") self.top.addWidget(self.radioButtonMov) self.top.addWidget(self.radioButtonCinema) self.middle = QVBoxLayout() self.middle.setMargin(10) self.url_label = QLabel() self.url = QLineEdit() self.url_label.setBuddy(self.url) self.middle.addWidget(self.url_label) self.middle.addWidget(self.url) self.browse_folder_label = QLabel("下载到:") self.browseFolder = QPushButton("选择目录") self.browse_folder_label.setBuddy(self.browseFolder) self.middle.addWidget(self.browse_folder_label) self.middle.addWidget(self.browseFolder) self.browse_folder_value = "" self.dest_file_label = QLabel() # "输入电影/电视剧名 (用来命名下载的文件/目录): " title set by choose_movie_widgets() later self.folder_or_filename = QLineEdit() self.dest_file_label.setBuddy(self.folder_or_filename) self.middle.addWidget(self.dest_file_label) self.middle.addWidget(self.folder_or_filename) self.bk_cinemae_spin_from = 1 self.bk_cinemae_spin_to = 1 self.fromEpSpinBox = QSpinBox() self.fromEpSpinBox.setMinimum(1) self.fromEpSpinBox.setMaximum(2147483647) self.fromEpLabel = QLabel("&从第几集开始下载:") self.fromEpLabel.setBuddy(self.fromEpSpinBox) self.toEpSpinBox = QSpinBox() self.toEpSpinBox.setMinimum(1) self.toEpSpinBox.setMaximum(2147483647) self.toEpLabel = QLabel("&到第几集停止下载:") self.toEpLabel.setBuddy(self.toEpSpinBox) self.cinema_ly = QHBoxLayout() #self.cinema_ly.setMargin(10) self.cinema_ly.addWidget(self.fromEpLabel) self.cinema_ly.addWidget(self.fromEpSpinBox) self.cinema_ly.addWidget(self.toEpLabel) self.cinema_ly.addWidget(self.toEpSpinBox) self.middle.addLayout(self.cinema_ly) self.proxy_label = QLabel("(如有)代理:") self.proxy = QLineEdit() self.proxy_label.setBuddy(self.proxy) self.middle.addWidget(self.proxy_label) self.middle.addWidget(self.proxy) self.add = QPushButton("开始下载") self.add.setEnabled(False) self.middle.addWidget(self.add) self.stop_me = QPushButton("停止下载") self.stop_me.setEnabled(False) self.middle.addWidget(self.stop_me) self.log_area = QPlainTextEdit() self.log_area.setReadOnly(True) self.log_area.setMaximumBlockCount(1000) self.middle.addWidget(self.log_area) #self.table_view.setSizePolicy(size) #self.layout.addWidget(self.table) self.layout = QVBoxLayout() self.layout.addLayout(self.top) self.layout.addLayout(self.middle) self.setLayout(self.layout) self.radioButtonMov.toggled.connect(self.choose_movie_widgets) self.radioButtonCinema.toggled.connect(self.choose_cinema_widgets) self.url.textChanged[str].connect(self.check_disable_download) self.browseFolder.clicked.connect(self.add_folder) self.folder_or_filename.textChanged[str].connect( self.check_disable_download) self.add.clicked.connect(self.start_download) self.stop_me.clicked.connect(self.stop_download) self.radioButtonMov.setChecked( True) #set default only after .connect above # TESTING PURPOSE ''' self.radioButtonMov.setChecked(False) self.url.setText('https://www.duboku.net/voddetail-969.html') self.browse_folder_value = 'C:/Users/Administrator/Documents/duboku' self.folder_or_filename.setText('初恋') ''' #set current process (not queue that one) log handler: logger = logging.getLogger(__name__) handler2 = LoggerWriter() logger.addHandler(handler2) logger.setLevel(logging.INFO) #DEBUG handler2.emitter.sigLog.connect(self.log_area.appendPlainText) sys.stdout = handler2 #LoggerWriter() #sys.stderr = handler2 #Seems no difference #handler2.emitter.sigLog.emit('hihi') @Slot() def choose_movie_widgets(self): if self.radioButtonMov.isChecked(): self.url_label.setText(self.movie_url_label_text) self.dest_file_label.setText(self.movie_dest_label_text) self.fromEpLabel.setDisabled(True) self.toEpLabel.setDisabled(True) self.fromEpSpinBox.setDisabled(True) self.toEpSpinBox.setDisabled(True) self.bk_cinemae_spin_from = self.fromEpSpinBox.value() self.bk_cinemae_spin_to = self.toEpSpinBox.value() self.fromEpSpinBox.setValue(1) self.toEpSpinBox.setValue(1) @Slot() def choose_cinema_widgets(self): if self.radioButtonCinema.isChecked(): self.url_label.setText(self.cinema_url_label_text) self.dest_file_label.setText(self.cinema_dest_label_text) self.fromEpLabel.setEnabled(True) self.toEpLabel.setEnabled(True) self.fromEpSpinBox.setEnabled(True) self.toEpSpinBox.setEnabled(True) self.fromEpSpinBox.setValue(self.bk_cinemae_spin_from) self.toEpSpinBox.setValue(self.bk_cinemae_spin_to) @Slot() def add_folder(self, s): #fname = QFileDialog.getOpenFileName(self, 'Open file', "c:\'", "Image files (*.jpg *.gif)") #fname = QFileDialog.getOpenFileName(self, 'Open file', '', QFileDialog.ShowDirsOnly) fname = QFileDialog.getExistingDirectory(self, '选择下载至什么目录', '', QFileDialog.ShowDirsOnly) #print('repr: ' + repr(fname)) if fname and fname.strip(): fname = fname.strip() self.browse_folder_value = fname #if getOpenFileName, will return ('/home/xiaobai/Pictures/disco.jpg', 'Image files (*.jpg *.gif)') #, while if getExistingDirectory, will return single path string only self.browseFolder.setText(fname) self.check_disable_download(fname) #else: # print('User cancel') @Slot() def check_disable_download(self, s): if self.url.text().strip( ) and self.browse_folder_value and self.folder_or_filename.text(): self.add.setEnabled(True) else: self.add.setEnabled(False) def task_done(self, retVal): self.add.setEnabled(True) self.stop_me.setEnabled(False) @Slot() def stop_download(self): if self.q: self.q.close() if self.pool: self.pool.terminate() self.add.setEnabled(True) self.stop_me.setEnabled(False) print('下载停止。') @Slot() def start_download(self): if self.fromEpSpinBox.value() > self.toEpSpinBox.value(): self.log_area.setPlainText('[!] 从第几集必须小于或等于到第几集。') return #No need worry click twice too fast, it seems already handle by PySide2 self.add.setEnabled(False) self.stop_me.setEnabled(True) self.log_area.clear() dest_full_path = os.path.join(self.browse_folder_value, self.folder_or_filename.text()) ''' print('dest_full_path: ' + repr(dest_full_path)) print('self.url.text(): ' + repr(self.url.text())) print('self.fromEpSpinBox.value(): ' + repr(self.fromEpSpinBox.value())) print('self.toEpSpinBox.value(): ' + repr(self.toEpSpinBox.value())) ''' import duboku_console #Windows can't set like that bcoz not update for args.url, must put explicitly #duboku_console.redirect_stdout_to_custom_stdout(arg_url, ...etc, LoggerWriter()) #failed other process handler = LogHandlerOtherProcess() handler.emitter.sigLog.connect(self.log_area.appendPlainText) ''' #ref current process: logger = logging.getLogger(__name__) handler2 = LoggerWriter() logger.addHandler(handler2) logger.setLevel(logging.DEBUG) handler2.emitter.sigLog.connect(self.log_area.appendPlainText) sys.stdout = handler2 #LoggerWriter() #handler2.emitter.sigLog.emit('hihi') ''' #handler = LoggerWriter() #handler.emitter.sigLog.connect(self.log_area.appendPlainText) self.q = multiprocessing.Queue() self.ql = QueueListener(self.q, handler) self.ql.start() self.pool = multiprocessing.Pool(1, worker_init, [self.q]) if self.radioButtonMov.isChecked(): self.pool.apply_async(duboku_console.main, args=(None, dest_full_path, self.fromEpSpinBox.value(), self.toEpSpinBox.value(), self.url.text().strip(), LoggerWriterOtherProcess(), False, self.proxy.text()), callback=self.task_done) else: self.pool.apply_async(duboku_console.main, args=(dest_full_path, None, self.fromEpSpinBox.value(), self.toEpSpinBox.value(), self.url.text().strip(), LoggerWriterOtherProcess(), False, self.proxy.text()), callback=self.task_done)
class MainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.textEdit = QPlainTextEdit() self.curFile = "" self.centralWidget = qtw.QWidget() self.mainWindow = self.createMainWindow() self.centralWidget.setLayout(self.mainWindow) self.setCentralWidget(self.centralWidget) #self.setFixedSize(1000, 550) self.createActions() self.createMenus() #self.createToolBars() self.createStatusBar() self.readSettings() self.textEdit.document().contentsChanged.connect(self.documentWasModified) #self.setCurrentFile("") self.setUnifiedTitleAndToolBarOnMac(True) #self.menuBar = qtw.QLayout.menuBar() #self.statusBar = qtw.QMainWindow.statusBar() self.Qsettings = QSettings() #self.connectlist() def createMainWindow(self): self.graphics = GraphicsWindow() self.spreadsheet = self.graphics.scene_class.spreadsheet self.control = self.spreadsheet.control self.controlobj = self.control.controlBox self.control.nextButton.clicked.connect(self.spreadsheet.update_table) self.control.nextButton.clicked.connect(self.graphics.update_view) self.control.prevButton.clicked.connect(self.spreadsheet.prev_table) self.control.prevButton.clicked.connect(self.graphics.prev_view) self.righttop = QVBoxLayout() self.righttop.addLayout(self.controlobj) self.righttop.addWidget(self.spreadsheet.table) self.mainWin = QHBoxLayout() self.mainWin.addWidget(self.graphics) self.mainWin.addLayout(self.righttop) return self.mainWin def closeEvent(self, event): if self.maybeSave: self.writeSettings event.accept() else: event.ignore() def newFile(self): if self.maybeSave: self.textEdit.clear() self.setCurrentFile("") def open(self): if self.maybeSave: fileName = QFileDialog.getOpenFileName(self) if not fileName.isEmpty(): self.loadFile(fileName) def save(self): if self.curFile == "": return self.saveAs else: return self.saveFile(self.curFile) def saveAs(self): fileName = QFileDialog.getSaveFileName(self) if fileName.isEmpty(): return False return self.saveFile(fileName) def about(self): QMessageBox.about(self, "About Application\n", "The <b>Application</b> example demonstrates how to\n" "write modern GUI applications using Qt, With a menu bar\n" "toolbars, and a status bar.") def documentWasModified(self): self.setWindowModified(self.textEdit.document().isModified()) def createActions(self): self.Act = QAction(QIcon(":/images/new.png"), "&New", self) self.Act.setShortcuts(QKeySequence.New) self.Act.setStatusTip("Create a new file") self.Act.triggered.connect(self.newFile) self.openAct = QAction(QIcon(":/images/new.png"), "&Open", self) self.openAct.setShortcuts(QKeySequence.Open) self.openAct.setStatusTip("Open an exsting file") self.openAct.triggered.connect(self.open) self.saveAct = QAction("&Save", self) self.saveAct.setShortcuts(QKeySequence.Save) self.saveAct.setStatusTip("Save a file") self.saveAct.triggered.connect(self.save) self.saveasAct = QAction("&Save as", self) self.saveasAct.setShortcuts(QKeySequence.SaveAs) self.saveasAct.setStatusTip("Save as a file") self.saveasAct.triggered.connect(self.saveAs) self.aboutQtAct = QAction("About &Qt", self) self.aboutQtAct.setStatusTip("Show the Qt library's About box") self.aboutQtAct.triggered.connect(qApp.aboutQt) self.exitAct = QAction("&Exit", self) self.exitAct.setStatusTip("Exit") self.exitAct.triggered.connect(self.exit) #self.cutAct.setEnabled(False) #self.copyAct.setEnabled(False) #self.textEdit.copyAvailable[bool].connect(self.cutAct.setEnabled) #self.textEdit.copyAvailable[bool].connect(self.copyAct.setEnabled) self.findAct = QAction("&Find", self) self.findAct.triggered.connect(self.find) self.gotocellAct = QAction("&GoToCell", self) self.gotocellAct.triggered.connect(self.gotocell) self.sortAct = QAction("&Sort", self) self.sortAct.triggered.connect(self.sort) self.undoAct = QAction("&Undo", self) self.redoAct = QAction("&Redo", self) self.cutAct = QAction("&Cut", self) self.copyAct = QAction("&Copy", self) self.pasteAct = QAction("&Paste", self) self.aboutAct = QAction("&About", self) self.boldAct = QAction("&Bold", self) self.italicAct = QAction("&Italic", self) self.leftAlignAct = QAction("&LeftAlign", self) self.rightAlignAct = QAction("&Alignment", self) self.justifyAct = QAction("&Justify", self) self.centerAct = QAction("&Center", self) self.setLineSpacingAct = QAction("&setLine", self) self.setParagrahSpacingAct = QAction("&setPAragrah", self) def createStatusBar(self): self.statusBar().showMessage("Ready") def createMenus(self): self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.Act) self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.saveAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.exitAct) self.editMenu = self.menuBar().addMenu("&Edit") self.editMenu.addAction(self.undoAct) self.editMenu.addAction(self.redoAct) self.editMenu.addSeparator() self.editMenu.addAction(self.cutAct) self.editMenu.addAction(self.copyAct) self.editMenu.addAction(self.pasteAct) self.editMenu.addSeparator() self.dataMenu = self.menuBar().addMenu("&Data") self.dataMenu.addAction(self.findAct) self.dataMenu.addAction(self.gotocellAct) self.dataMenu.addAction(self.sortAct) self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAct) self.helpMenu.addAction(self.aboutQtAct) self.formatMenu = self.editMenu.addMenu("&Fornat") self.formatMenu.addAction(self.boldAct) self.formatMenu.addAction(self.italicAct) self.formatMenu.addSeparator().setText("Alignment") self.formatMenu.addAction(self.leftAlignAct) self.formatMenu.addAction(self.rightAlignAct) self.formatMenu.addAction(self.justifyAct) self.formatMenu.addAction(self.centerAct) self.formatMenu.addSeparator() self.formatMenu.addAction(self.setLineSpacingAct) self.formatMenu.addAction(self.setParagrahSpacingAct) def readSettings(self): self.settings = QSettings("Trolltrch", "Application Example") self.pos = self.settings.value("pos", QPoint(200, 200))#.toPoint() self.size = self.settings.value("size", QSize(400, 400))#.toSize() self.resize(self.size) self.move(self.pos) def writeSettings(self): self.settings = QSettings("Trolltech", "Application Example") self.settings.setValue("pos", self.pos) self.setting.setValue("size", self.size) def maybeSave(self): if self.textEdit.document().isModified(): ret = QMessageBox.warning(self, "Application", "The document has been modified.\n" "Do you want to save your changes?", QMessageBox.Save | QMessageBox.Discard | QMessageBox.cancel) if ret == QMessageBox.Save: return self.save elif ret == QMessageBox.Cancel: return False return True def loadFile(self, fileName): file = QFile(fileName) if not file.open(QFile.ReadOnly | QFile.Text): QMessageBox.warning(self, "Application", "Cannot read file" "{}:\n{}".format(fileName, file.errorString())) return False in_ = QTextStream(file) QApplication.setOverrideCursor(Qt.WaitCursor) self.textEdit.setPlainText(in_.readAll()) QApplication.restoreOverrideCursor() self.setCurrentFile(fileName) self.statusBar().showMessage("File loaded", 2000) def saveFile(self, fileName): file = QFile(fileName) if not file.open(QFile.WriteOnly | QFile.Text): QMessageBox.warning(self, "Application", "Cannot write file %1:\n%2.".arg(fileName) .arg(file.errorString())) return False self.out = QTextStream(file) QApplication.setOverrideCursor(Qt.WaitCursor) self.out = self.textEdit.toPlainText() QApplication.restoreOverrideCursor() self.setCurrentFile(fileName) self.statusBar().showMessage("File saved", 2000) return True def setCurrentFile(self, fileName): self.curFile = fileName self.textEdit.document().setModified(False) self.setWindowModified(False) if self.curFile.isEmpty(): shownName = "untitled.txt" else: shownName = strippedName(curFile) setWindowTitle(tr("%1[*] - %2").arg(shownName).arg("Application")) def strippedName(self, fullFileName): return QFileInfo(fullFileName).fileName() def exit(self): pass ''' def connectlist(self): self.copyAct.triggered.connect(self.spreadsheet.copy()) self.pasteAct.triggered.connect(self.spreadsheet.paste()) # self. ''' def find(self): print("findをクリックしました") self.fw = FindDialog() self.fw.show() self.fw.activateWindow() def gotocell(self): self.gw = GoToCell() self.gw.show() self.gw.activateWindow() def sort(self): self.sw = Sort() self.sw.show() self.sw.activateWindow()
class MyWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.thread = SerialMonitorThread() self.thread.dataReady.connect(self.get_data, Qt.QueuedConnection) self.thread.setTerminationEnabled(True) #Menu self.setPalette(get_verifone_color()) collapsible = CollapsibleWidget() self.init_logging(collapsible) self.init_download(collapsible) self.init_analyser(collapsible) collapsible.add_sections() # Scroll Area self.createLoggingDisplayLabel() self.scrollArea = QScrollArea() self.scrollArea.setBackgroundRole(QPalette.Dark) self.scrollArea.setWidget(self.text) self.scrollArea.setWidgetResizable(True) hLayout = QHBoxLayout() #hLayout.addLayout(vLayout) hLayout.addWidget(collapsible) hLayout.addWidget(self.scrollArea) self.setLayout(hLayout) def init_logging(self, collapsible): self.logger = QPushButton("Start Logging", self) self.logger.setFont(QFont("Times", 14, QFont.Bold)) self.logger.clicked.connect(lambda: self.display_log_data()) self.logger.setStyleSheet("background-color: white") #self.filterLayout = QtWidgets.QHBoxLayout() self.logFilterLabel = QLabel('Filter', self) self.logFilterLabel.setFont(QFont("Times", 14, QFont.Bold)) self.logFilterLabel.setPalette(get_white_color_text()) self.logFilterLabel.setFixedWidth(60) self.logFilter = QLineEdit(self) self.logFilter.setPalette(get_white_color()) self.logFilter.setStyleSheet("background-color: white") self.logFilter.setFixedWidth(200) #self.filterLayout.addWidget(self.logFilterLabel, QtCore.Qt.AlignLeft) #self.filterLayout.addWidget(self.logFilter, QtCore.Qt.AlignLeft) self.serialList = QComboBox() ports = get_available_serial_ports() if (len(ports) == 1): self.serialList.addItem(ports[0]) self.thread.set_comport(self.serialList.currentText()) else: self.serialList.addItem("Select") for port in ports: self.serialList.addItem(port) self.serialList.currentIndexChanged.connect( lambda: self.set_serial_port()) self.serialList.setStyleSheet("background-color: white") self.clear = QPushButton("Clear Log File", self) self.clear.setStyleSheet("background-color: white") self.clear.setFont(QFont("Times", 14, QFont.Bold)) self.clear.clicked.connect(lambda: self.clear_data()) widget = QFrame(collapsible.get_tree()) widget.setPalette(get_verifone_color()) title = "Logging" self.loggerGrid = QGridLayout(widget) self.loggerGrid.addWidget(self.logger, 0, 0, 1, 2) self.loggerGrid.addWidget(self.logFilterLabel, 1, 0, 1, 1) self.loggerGrid.addWidget(self.logFilter, 1, 1, 1, 1) self.loggerGrid.addWidget(self.serialList, 2, 0, 1, 2) self.loggerGrid.addWidget(self.clear, 3, 0, 1, 2) collapsible.include_section(title, widget) def init_download(self, collapsible): self.download = QPushButton("Download Package", self) self.download.setFont(QFont("Times", 14, QFont.Bold)) self.download.clicked.connect(lambda: self.send_file()) self.download.setStyleSheet("background-color: white") self.loadDownloadFile = QPushButton("Load File", self) self.loadDownloadFile.setFont(QFont("Times", 14, QFont.Bold)) self.loadDownloadFile.clicked.connect(self.loadFromFile) self.loadDownloadFile.setStyleSheet("background-color: white") self.downloadFileName = QLineEdit("File name", self) self.downloadFileName.setStyleSheet("background-color: white") self.downloadFileName.setFixedWidth(300) self.downloadAddress = QLineEdit("IP Address", self) self.downloadAddress.setStyleSheet("background-color: white") self.downloadAddress.setFixedWidth(300) self.downloadStatus = QLabel("Download Status", self) self.downloadStatus.setStyleSheet( "background-color: rgba(3, 169, 229, 0); color : white") self.downloadStatus.setFixedWidth(300) widget = QFrame(collapsible.get_tree()) title = "Download" self.downloadGrid = QGridLayout(widget) self.downloadGrid.addWidget(self.download, 0, 0, 1, 2) self.downloadGrid.addWidget(self.loadDownloadFile, 1, 0, 1, 2) self.downloadGrid.addWidget(self.downloadFileName, 2, 0, 1, 2) self.downloadGrid.addWidget(self.downloadAddress, 3, 0, 1, 2) self.downloadGrid.addWidget(self.downloadStatus, 4, 0, 1, 2) collapsible.include_section(title, widget) def init_analyser(self, collapsible): self.performanceData = QPushButton("View Performance Data", self) self.performanceData.setFont(QFont("Times", 14, QFont.Bold)) self.performanceData.clicked.connect( lambda: self.display_performance_data()) self.performanceData.setStyleSheet("background-color: white") self.performanceChart = QPushButton("View Performance Chart", self) self.performanceChart.setFont(QFont("Times", 14, QFont.Bold)) self.performanceChart.clicked.connect( lambda: self.display_performance_chart()) self.performanceChart.setStyleSheet("background-color: white") widget = QFrame(collapsible.get_tree()) title = "Analyser" self.analyserGrid = QGridLayout(widget) self.analyserGrid.addWidget(self.performanceData, 0, 0, 1, 2) self.analyserGrid.addWidget(self.performanceChart, 1, 0, 1, 2) collapsible.include_section(title, widget) def loadFromFile(self): fileName, _ = QFileDialog.getOpenFileName( self, "Load Package", '', "Download Files (*.tgz);;All Files (*)") if not fileName: return try: in_file = open(str(fileName), 'rb') except IOError: QMessageBox.information( self, "Unable to open file", "There was an error opening \"%s\"" % fileName) return in_file.close() self.downloadFileName.setText(fileName) def createLoggingDisplayLabel(self): # Display Area self.text = QPlainTextEdit(self) self.text.setReadOnly(True) self.text.setFont(QFont("Times", 12, QFont.Bold)) self.text.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard) def clear_data(self): self.text.clear() os.remove(logfile_path, dir_fd=None) def display_log_data(self): #send_file() if ('COM' in self.serialList.currentText()): self.createLoggingDisplayLabel() self.scrollArea.setWidget(self.text) self.thread.stop() self.thread.start() data = get_data_from_file(logfile_path) if (len(data) > 0 and data != None): self.text.appendPlainText(data) self.logger.setDisabled(True) def get_data(self, data): if (len(data) > 0): logFile = open(logfile_path, "a") logFile.write(data) logFile.close() filterText = self.logFilter.text() if filterText in data.rstrip(): self.text.appendPlainText(data.rstrip()) vbar = self.scrollArea.verticalScrollBar() vbar.setValue(vbar.maximum()) def display_performance_data(self): self.thread.stop() data = get_data_from_file(logfile_path) jsonData = translate_data_to_json(data) self.performanceData = DisplayPerformanceData() self.performanceData.loadCsv( os.path.join(base_log_path, "performance_data.csv")) self.scrollArea.setWidget(self.performanceData) self.logger.setDisabled(False) def display_performance_chart(self): self.thread.stop() self.scrollArea.setWidget(get_performace_chart()) self.logger.setDisabled(False) def set_serial_port(self): self.thread.set_comport(self.serialList.currentText()) def send_file(self): base_path = os.path.join("c:/", "VFI", 'wks', 'global-payment-application', 'GPA', 'output', 'vos2', 'gpa', 'dl.gpa-1.0.0.0-000.tgz') fileName = self.downloadFileName.text() try: in_file = open(str(fileName), 'rb') except IOError: QMessageBox.information( self, "Unable to open file", "There was an error opening \"%s\"" % fileName) return in_file.close() load_vos_package_ip('192.168.0.104', fileName, self.downloadStatus)
class application(QTabWidget): bot = 0 def __init__(self, parent=None): super(application, self).__init__(parent) self.bot = None self.db = sqlite3.connect('database') # tabs self.tab1 = QWidget() self.tab2 = QWidget() self.tab3 = QWidget() self.tab4 = QWidget() self.tab5 = QWidget() self.resize(640, 400) self.addTab(self.tab1, "Tab 1") self.addTab(self.tab2, "Tab 2") self.addTab(self.tab3, "Tab 3") self.addTab(self.tab4, "Tab 4") self.addTab(self.tab5, "Tab 5") # tab set keys self.h_box_key = QHBoxLayout() self.change_key_b = QPushButton("Edit keys") self.edit_1 = QLineEdit() self.edit_2 = QLineEdit() self.edit_3 = QLineEdit() self.edit_4 = QLineEdit() self.result = QLabel() self.set_button = QPushButton("Set keys") self.handle_info = QLabel() self.follower_info = QLabel() self.ready_lab = QLabel() # tab follow self.box_label = QLabel("Link to tweet") self.combo_label = QLabel("Mode") self.spin_label = QLabel("Limit before sleep") self.prog_bar = QProgressBar() self.combo_box = QComboBox() self.h_box = QHBoxLayout() self.spin_box = QSpinBox() self.link_box = QLineEdit() self.link_result = QLabel() self.follow_button = QPushButton("Follow Retweeters") self.cancel_button = QPushButton("Cancel") self.logger = QPlainTextEdit() self.h_box2 = QHBoxLayout() self.max_box = QSpinBox() self.max_label = QLabel("Max follows before stop") # tab unfollow self.unfollow_button = QPushButton("Unfollow Auto followers") self.unf_logger = QPlainTextEdit() self.unfollow_res = QLabel() self.prog_bar_unf = QProgressBar() self.unfollow_cancel = QPushButton("Cancel") self.unf_confirm = QMessageBox() # tab help self.help_box = QPlainTextEdit() self.help_label = QLabel( "<a href='http://Optumsense.com/'>http://Optumsense.com/</a>") #tab schedule self.tweet_box = QPlainTextEdit() self.date_time = QDateTimeEdit(QDateTime.currentDateTime()) self.schedule_but = QPushButton("Schedule Tweet") self.schedule_info = QLabel() self.schedule_table = QTableView() # threads self.follow_thread = None self.unfollow_thread = None self.schedule_thread = None # tabs self.tab1UI() self.tab2UI() self.tab3UI() self.tab4UI() self.tab5UI() self.setWindowTitle("Optumize") self.setWindowIcon(QtGui.QIcon('assets/oo.png')) # db cursor = self.db.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS keys(one TEXT, two TEXT, three TEXT, four TEXT)''' ) self.db.commit() def tab1UI(self): layout = QFormLayout() layout.addRow(self.h_box) self.h_box.addWidget(self.combo_label) self.combo_label.setAlignment(Qt.AlignRight) self.h_box.addWidget(self.combo_box) self.combo_box.addItem("Follow Retweeters") self.combo_box.addItem("Follow Followers") self.combo_box.currentIndexChanged.connect(self.selection_change) self.h_box.addWidget(self.spin_label) self.spin_label.setAlignment(Qt.AlignRight) self.h_box.addWidget(self.spin_box) self.spin_box.setMinimum(1) self.spin_box.setValue(30) layout.addRow(self.box_label, self.link_box) self.link_result.setAlignment(Qt.AlignCenter) layout.addRow(self.link_result) layout.addRow(self.follow_button) self.follow_button.clicked.connect(self.follow_ret) layout.addRow(self.cancel_button) self.cancel_button.clicked.connect(self.cancel_onclick) layout.addRow(self.h_box2) self.h_box2.addWidget(self.max_label) self.h_box2.addWidget(self.max_box) self.max_box.setFixedWidth(100) self.max_label.setAlignment(Qt.AlignRight) self.max_box.setMaximum(1000000) self.max_box.setValue(100) self.max_label.hide() self.max_box.hide() layout.addRow(self.logger) self.logger.setReadOnly(True) layout.addRow(self.prog_bar) self.prog_bar.setAlignment(Qt.AlignCenter) self.setTabText(0, "Follow") self.setTabIcon(0, QtGui.QIcon('assets/check_mark.png')) self.tab1.setLayout(layout) def selection_change(self, i): if i == 0: self.box_label.setText("Link to tweet") self.follow_button.setText("Follow Retweeters") self.link_result.setText("") self.max_label.hide() self.max_box.hide() self.follow_button.clicked.connect(self.follow_ret) else: self.box_label.setText("Handle of user") self.follow_button.setText("Follow Followers") self.link_result.setText("") self.max_label.show() self.max_box.show() self.max_label.setText("Max follows before stop") self.follow_button.clicked.connect(self.follow_fol) def cancel_onclick(self): if self.follow_thread is None: pass elif self.follow_thread.isRunning(): self.prog_bar.setValue(0) self.logger.appendPlainText("Cancelled script") self.follow_thread.terminate() self.follow_thread = None def follow_ret(self): self.prog_bar.setValue(0) self.follow_button.setEnabled(False) self.link_result.setText("") self.logger.clear() limit = self.spin_box.value() if self.bot is None: self.link_result.setText( "<font color='red'>Configure access keys in set keys tab</font>" ) return if self.follow_thread is not None: return link = self.link_box.text() id_tweet = link.split("/")[-1] try: tweet = self.bot.api.get_status(id_tweet) self.logger.appendPlainText( f"following retweeters from link: {link}...") self.follow_thread = FollowThread(self.bot, id_tweet, limit, 1, 0) self.follow_thread.start() self.connect(self.follow_thread, SIGNAL("finished()"), self.done) self.connect(self.follow_thread, SIGNAL("setup_prog(QString)"), self.setup_prog) self.connect(self.follow_thread, SIGNAL("post_follow(QString)"), self.post_follow) except tweepy.error.TweepError: self.follow_button.setEnabled(True) self.link_result.setText( "<font color='red'>Could not find tweet</font>") def setup_prog(self, msg): self.prog_bar.setMaximum(int(msg)) def follow_fol(self): self.prog_bar.setValue(0) self.follow_button.setEnabled(False) self.link_result.setText("") self.logger.clear() limit = self.spin_box.value() if self.bot is None: self.link_result.setText( "<font color='red'>Configure access keys in set keys tab</font>" ) return if self.follow_thread is not None: return handle = self.link_box.text() if handle == '': self.link_result.setText( "<font color='red'>Enter a handle above</font>") return elif handle[0] == '@': id_user = handle[1:] else: id_user = handle try: man = self.bot.api.get_user(id_user) self.logger.appendPlainText( f"following followers of {man.screen_name}...") self.logger.appendPlainText(f"Collecting") self.follow_thread = FollowThread(self.bot, id_user, limit, 2, self.max_box.value()) self.follow_thread.start() self.connect(self.follow_thread, SIGNAL("finished()"), self.done) self.connect(self.follow_thread, SIGNAL("setup_prog(QString)"), self.setup_prog) self.connect(self.follow_thread, SIGNAL("post_follow(QString)"), self.post_follow) except tweepy.error.TweepError: self.follow_button.setEnabled(True) self.link_result.setText( "<font color='red'>Could not find user</font>") def post_follow(self, message): if message == "bad": self.logger.appendPlainText( "Rate limit exceeded... sleeping for cooldown") else: self.logger.appendPlainText(message) self.prog_bar.setValue(self.prog_bar.value() + 1) def done(self): self.follow_thread = None self.follow_button.setEnabled(True) def tab2UI(self): layout = QFormLayout() layout.addRow(self.unfollow_button) layout.addRow(self.unfollow_cancel) layout.addRow(self.unfollow_res) self.unfollow_res.setAlignment(Qt.AlignCenter) self.unfollow_button.clicked.connect(self.unfollow_fol) self.unfollow_cancel.clicked.connect(self.unfollow_can) layout.addWidget(self.unf_logger) self.unf_logger.setReadOnly(True) layout.addRow(self.prog_bar_unf) self.prog_bar_unf.setAlignment(Qt.AlignCenter) self.setTabText(1, "Unfollow") self.setTabIcon(1, QtGui.QIcon('assets/cross.png')) self.tab2.setLayout(layout) def unfollow_fol(self): self.unfollow_button.setEnabled(False) self.unfollow_thread = UnfollowThread(self.bot) self.unfollow_thread.start() self.connect(self.unfollow_thread, SIGNAL("post_unfol(QString)"), self.post_unfol) self.connect(self.unfollow_thread, SIGNAL("finished()"), self.done_unf) def done_unf(self): self.unfollow_thread = None self.unf_logger.appendPlainText("Done") self.unfollow_button.setEnabled(True) def post_unfol(self, msg): if msg == "bad": self.unf_logger.appendPlainText( "rate limit exceeded, resting for 15 minutes") else: self.unf_logger.appendPlainText(f"Unfollowing {msg}") def unfollow_can(self): if self.unfollow_thread is None: pass elif self.unfollow_thread.isRunning(): self.unf_logger.appendPlainText("Cancelled script") self.unfollow_thread.terminate() self.unfollow_thread = None def tab3UI(self): layout = QFormLayout() layout.addWidget(self.tweet_box) self.tweet_box.setMaximumHeight(150) self.tweet_box.setPlaceholderText("Tweet contents") layout.addRow(self.date_time) self.date_time.setCalendarPopup(True) layout.addRow(self.schedule_info) layout.addRow(self.schedule_but) self.schedule_but.clicked.connect(self.schedule_tweet) layout.addWidget(self.schedule_table) self.setTabText(2, "Schedule Tweet") self.setTabIcon(2, QtGui.QIcon('assets/calendar.png')) self.tab3.setLayout(layout) def schedule_tweet(self): tweet_contents = self.tweet_box.toPlainText() if (len(tweet_contents) == 0): print("length of tweet is 0") self.schedule_info.setText("length of tweet is 0") return elif (len(tweet_contents) >= 280): self.schedule_info.setText("Tweet char limit exceeded") return if self.bot is None: self.schedule_info.setText( "<font color='red'>Configure access keys in set keys tab</font>" ) return datetime = self.date_time.text() try: print("done") self.schedule_thread.start() self.schedule_thread.add_tweet(tweet_contents, datetime) except: self.follow_button.setEnabled(True) self.link_result.setText( "<font color='red'>Could not find user</font>") def done_schedule(self): print("done scheduler") def tab4UI(self): layout = QFormLayout() layout.addRow("API key", self.edit_1) layout.addRow("API key secret", self.edit_2) layout.addRow("Auth token", self.edit_3) layout.addRow("Auth token secret", self.edit_4) self.set_button.clicked.connect(self.set_keys) l = self.read_file() if l is not None: if len(l) == 4: self.edit_1.setText(l[0]) self.edit_2.setText(l[1]) self.edit_3.setText(l[2]) self.edit_4.setText(l[3]) self.set_keys() layout.addRow(self.result) self.result.setAlignment(Qt.AlignCenter) layout.addRow(self.h_box_key) self.h_box_key.addWidget(self.change_key_b) self.h_box_key.addWidget(self.set_button) self.change_key_b.clicked.connect(self.change_keys) layout.addRow(self.handle_info) self.handle_info.setAlignment(Qt.AlignCenter) layout.addRow(self.follower_info) self.follower_info.setAlignment(Qt.AlignCenter) layout.addRow(self.ready_lab) self.ready_lab.setAlignment(Qt.AlignCenter) self.setTabText(3, "Settings") self.setTabIcon(3, QtGui.QIcon('assets/settings.png')) self.tab4.setLayout(layout) def change_keys(self): self.set_button.setEnabled(True) def set_keys(self): self.set_button.setEnabled(False) self.result.setText("") one = self.edit_1.text() two = self.edit_2.text() three = self.edit_3.text() four = self.edit_4.text() try: self.bot = apiconnector.ApiConnector(one, two, three, four) me = self.bot.add_keys(one, two, three, four) self.handle_info.setText("Handle: @" + me.screen_name) self.follower_info.setText("Followers: " + str(me.followers_count)) self.ready_lab.setText("<font color='green'>Ready!</font>") cursor = self.db.cursor() cursor.execute('DELETE FROM keys;', ) cursor.execute( '''INSERT INTO keys(one, two, three, four) VALUES(?,?,?,?)''', (one, two, three, four)) self.db.commit() self.schedule_thread = ScheduleThread(self.bot) except: print("Could not authenticate you") self.result.setText( "<font color='red'>Could not authenticate you</font>") def read_file(self): result = [] try: cursor = self.db.cursor() cursor.execute('''SELECT one, two, three, four FROM keys''') all_rows = cursor.fetchall() for row in all_rows: result.append(row[0]) result.append(row[1]) result.append(row[2]) result.append(row[3]) return result except: return None def tab5UI(self): layout = QFormLayout() layout.addRow("Website", self.help_label) self.help_label.setOpenExternalLinks(True) self.setTabText(4, "Help") self.setTabIcon(4, QtGui.QIcon('assets/help.png')) self.tab5.setLayout(layout)
class MyTextEditor(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.textEdit = QPlainTextEdit() self.setCentralWidget(self.textEdit) self.createActions() self.readSettings() self.setCurrentFile("") def createActions(self): self.toolBar = self.addToolBar("main tool bar") self.menuBar = self.menuBar() self.fileMenu = self.menuBar.addMenu('File') self.helpMenu = self.menuBar.addMenu('Help') Act = QAction(QIcon(":/icons/new.png"), self.tr("&New"), self) Act.setShortcuts(QKeySequence.New) Act.setStatusTip(self.tr("Create a new file")) Act.triggered.connect(self.newFile) self.fileMenu.addAction(Act) self.toolBar.addAction(Act) openAct = QAction(QIcon(":/icons/openconf.png"), self.tr("&Open..."), self) openAct.setShortcuts(QKeySequence.Open) openAct.setStatusTip(self.tr("Open an existing file")) self.fileMenu.addAction(openAct) self.toolBar.addAction(openAct) openAct.triggered.connect(self.open) saveAct = QAction(QIcon(":/icons/save.png"), self.tr("&Save..."), self) saveAct.setShortcuts(QKeySequence.Save) saveAct.setStatusTip(self.tr("Save a file")) self.fileMenu.addAction(saveAct) self.toolBar.addAction(saveAct) saveAct.triggered.connect(self.save) self.fileMenu.addSeparator() quitAct = QAction(QIcon(":/icons/exit.png"), self.tr("&Exit..."), self) quitAct.setShortcuts(QKeySequence.Close) quitAct.setStatusTip(self.tr("Exit")) self.fileMenu.addAction(quitAct) self.toolBar.addAction(quitAct) quitAct.triggered.connect(self.exit) aboutQtAct = QAction(self.tr("About &Qt"), self) aboutQtAct.setStatusTip(self.tr("Show the Qt library's About box")) aboutQtAct.triggered.connect(self.about) self.helpMenu.addAction(aboutQtAct) def loadFile(self, fileName): file = QFile(fileName) if not file.open(QFile.ReadOnly | QFile.Text): QMessageBox.warning(self, self.tr("Application"), self.tr("Cannot read file " "{}:\n{}.".format(fileName, file.errorString()))) return inStream = QTextStream(file) QApplication.setOverrideCursor(QtGui.Qt.WaitCursor) self.textEdit.setPlainText(inStream.readAll()) print(inStream.readAll()) QApplication.restoreOverrideCursor() self.setCurrentFile(fileName) self.statusBar().showMessage(self.tr("File loaded"), 2000) def newFile(self): if self.maybeSave(): self.fileName = QFileDialog.getOpenFileName(self) if not self.fileName == "": self.loadFile(self.fileName[0]) def open(self): if self.maybeSave(): fileName = QFileDialog.getOpenFileName(self) if not fileName == "" : self.loadFile(fileName[0]) def save(self): if self.curFile == "": return self.saveAs() else: return self.saveFile(self.curFile) def saveAs(self): fileName = QFileDialog.getSaveFileName(self) if fileName == "" : return False return self.saveFile(fileName[0]) def saveFile(self, fileName): file = QFile(fileName) if not file.open(QFile.WriteOnly | QFile.Text): QMessageBox.warning(self, self.tr("Application"), self.tr("Cannot write file %1:\n%2.") .arg(fileName) .arg(file.errorString())) return False out = QTextStream(file) QApplication.setOverrideCursor(QtGui.Qt.WaitCursor) print (self.textEdit.toPlainText()) out << self.textEdit.toPlainText() QApplication.restoreOverrideCursor() self.setCurrentFile(fileName) self.statusBar().showMessage(self.tr("File saved"), 2000) return True def documentWasModified(self): self.setWindowModified(self.textEdit.document().isModified()) def about(self): QMessageBox.about(self, self.tr("About Application"), self.tr("The <b>Application</b> example demonstrates how to " "write modern GUI applications using Qt, with a menu bar, " "toolbars, and a status bar.")) def File(self): if self.maybeSave(): self.textEdit.clear() self.setCurrentFile("") def setCurrentFile(self,fileName): self.curFile = fileName self.textEdit.document().setModified(False) self.setWindowModified(False) if self.curFile == "": shownName = "untitled.txt" else: shownName = self.strippedName(self.curFile) self.setWindowTitle(self.tr("{}[*] - {}").format(shownName,self.tr("Application"))) def strippedName(self, fullFileName): return QFileInfo(fullFileName).fileName() def readSettings(self): settings = QSettings("Trolltech", "Application Example") pos = settings.value("pos", QPoint(200, 200)) size = settings.value("size", QSize(400, 400)) self.resize(size) self.move(pos) def writeSettings(self): settings = QSettings("Trolltech", "Application Example") settings.setValue("pos", self.pos()) settings.setValue("size", self.size()) def closeEvent(self, event): if self.maybeSave(): self.writeSettings() event.accept() else: event.ignore() def exit(self): self.close() #with call to closeEvent #self.deleteLater() #Do not call closeEvent def maybeSave(self): if self.textEdit.document().isModified(): ret = QMessageBox.warning(self, self.tr("Application"), self.tr("The document has been modified.\n" "Do you want to save your changes?"), QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) if ret == QMessageBox.Save: return self.save() elif ret == QMessageBox.Cancel: return False return True
class BibTeXWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self._source = None self._table = None self._id = -1 self._inspire_id = None self._doi = None self._bibtex_string = "" self._fetcher = Fetcher() self._fetcher.BatchReady.connect(self._HandleBatchReady) self._fetcher.FetchingFinished.connect(self._HandleFetchingCompleted) self._text_edit = QPlainTextEdit() font = QFont("Monospace") self._text_edit.setFont(font) self._text_edit.setTextInteractionFlags( Qt.TextInteractionFlag.TextSelectableByKeyboard | Qt.TextInteractionFlag.TextSelectableByMouse) self._text_edit.setEnabled(False) self._dowload_inspire = QToolButton() self._dowload_inspire.setIcon(QIcon(icons.INSPIRE)) self._dowload_inspire.clicked.connect(self._FetchINSPIRE) self._dowload_inspire.setEnabled(False) self._dowload_doi = QToolButton() self._dowload_doi.setIcon(QIcon(icons.DOI)) self._dowload_doi.clicked.connect(self._FetchDOI) self._dowload_doi.setEnabled(False) self._SetupUI() def _SetupUI(self): tool_widget = QWidget() tool_layout = QHBoxLayout() tool_layout.setContentsMargins(0, 0, 0, 0) tool_layout.addWidget(self._dowload_inspire) tool_layout.addWidget(self._dowload_doi) tool_layout.addStretch(1) tool_widget.setLayout(tool_layout) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._text_edit) layout.addWidget(tool_widget) self.setLayout(layout) def SetTable(self, database_table): if self._table == database_table: return if self._table is not None: self._table.Cleared.disconnect(self.Clear) self._table = database_table self._table.Cleared.connect(self.Clear) def Clear(self): self._text_edit.clear() self._text_edit.setEnabled(False) self._dowload_inspire.setEnabled(False) self._dowload_doi.setEnabled(False) def DisplayItem(self, id_): # NOTE: What if the item gets updated? if self._id == id_: return self._id = id_ self._fetcher.Stop() self.Clear() if self._id == -1: return record = self._table.GetRow(self._id, ("inspire_id", "dois")) self._inspire_id = record["inspire_id"] self._doi = None if record["dois"] == [] else record["dois"][0] has_inspire_id = self._inspire_id is not None has_doi = self._doi is not None self._text_edit.setEnabled(has_inspire_id | has_doi) self._dowload_inspire.setEnabled(has_inspire_id) self._dowload_doi.setEnabled(has_doi) def _FetchINSPIRE(self): self._fetcher.Fetch(InspireBibTeXPlugin, "recid:" + str(self._inspire_id), batch_size=2) def _FetchDOI(self): self._fetcher.Fetch(DOIBibTeXPlugin, self._doi) def _HandleBatchReady(self, batch): self._bibtex_string = batch[0] def _HandleFetchingCompleted(self, records_number): self._text_edit.setPlainText(self._bibtex_string)