class AppDemo(QWidget): ppt = PPTController() def __init__(self): app = QApplication(sys.argv) # 화면 전환용 widget 설정 self.widget = QStackedLayout() # 레이아웃 생성 self.introLayout = IntroLayout() self.guideLayout = GuideLayout() self.fileUploadLayout = FileUploadLayout(self) self.webcamLayout = WebcamLayout() # widget 추가 self.widget.addWidget(self.introLayout) self.widget.addWidget(self.guideLayout) self.widget.addWidget(self.fileUploadLayout) self.widget.addWidget(self.webcamLayout) self.introLayout.nextButton.clicked.connect(lambda: self.nextPage()) self.guideLayout.nextButton.clicked.connect(lambda: self.nextPage()) self.guideLayout.backButton.clicked.connect(lambda: self.backPage()) self.fileUploadLayout.backButton.clicked.connect( lambda: self.backPage()) self.fileUploadLayout.camTestButton.clicked.connect( lambda: self.nextPage()) self.webcamLayout.backButton.clicked.connect(lambda: self.backPage()) app.exec_() def start(self): self.webcamLayout.start() def nextPage(self): # max page : 4 if self.widget.currentIndex() < 3: self.widget.setCurrentIndex(self.widget.currentIndex() + 1) def backPage(self): if self.widget.currentIndex() > 0: self.widget.setCurrentIndex(self.widget.currentIndex() - 1)
class Window(BaseWindow): def __init__(self): super(Window, self).__init__() self.last_index = 0 self.setFixedSize(WINDOW_WIDTH, WINDOW_HEIGHT) # 要想使 move_center 起作用,必须设置 resize self.resize(WINDOW_WIDTH, WINDOW_HEIGHT) self.move_center() self.setWindowIcon(QIcon(abs_path('static/images/icon/logo.png'))) self.setWindowTitle('江科大课表导出 v1.0') self.stack_layout = QStackedLayout() self.stack_layout.addWidget(StepHello(self.stack_layout)) self.stack_layout.addWidget(StepAccount(self.stack_layout)) self.stack_layout.addWidget(StepDate(self.stack_layout)) self.stack_layout.addWidget(StepSuccess(self.stack_layout)) self.stack_layout.addWidget(StepMistake(self.stack_layout)) self.stack_layout.setCurrentIndex(self.last_index) self.stack_layout.currentChanged.connect(self.interface_changed) self.setLayout(self.stack_layout) self.show() def interface_changed(self): if self.last_index < self.stack_layout.currentIndex(): self.last_index = self.stack_layout.currentIndex() elif self.last_index == 4 and self.stack_layout.currentIndex() == 0: for i in range(3)[::-1]: self.stack_layout.widget(i).step_ani(True) self.last_index = 0 elif self.last_index in [3, 4 ] and self.stack_layout.currentIndex() == 2: self.stack_layout.widget(2).step_ani(True) self.last_index = 2 elif self.last_index == 2 and self.stack_layout.currentIndex() == 1: self.stack_layout.widget(1).step_ani(True) self.last_index = 1 elif self.last_index == 1 and self.stack_layout.currentIndex() == 0: self.stack_layout.widget(0).step_ani(True) self.last_index = 0
class AbstractTemplateWidget(QFrame): """ TemplateWidget is used in reports and options tab. So it needs several common methods and common layout. But behavior is different. it should be defined in children classes. """ def __init__(self, main_window, items): super().__init__() self.items = items self.visible_items = [] self.layout = QStackedLayout() self.menu_layout = QVBoxLayout() self.templates_layout = QStackedLayout() self.showEvent = self._get_show_event(main_window) self.menu_wrapper = QVBoxLayout() try: self.ACTION_BTN_ICON except AttributeError: self.ACTION_BTN_ICON = '' self.setLayout(self.layout) self.layout.addWidget(self._get_static_widgets()) def _get_static_widgets(self): """ Create layout that does not depend on content. """ hbox = QHBoxLayout() self.menu_wrapper.addWidget(utils.get_scrollable(self.menu_layout)) hbox.addLayout(self.menu_wrapper, stretch=30) hbox.addLayout(self.templates_layout, stretch=70) widget = QWidget() widget.setLayout(hbox) widget.setGraphicsEffect(utils.get_shadow()) return widget def _iterate_items(self): """ Filter items if they has no values. """ pass def hideEvent(self, event): """ Clear menu and templates. """ utils.clear_layout(self.menu_layout) utils.clear_layout(self.templates_layout) def _get_show_event(self, main_window): """ Update templates list and re-select them. """ def show_event(event): utils.clear_layout(self.menu_layout) utils.clear_layout(self.templates_layout) self.visible_items = self._iterate_items() self._show_menu() self._show_templates() if not self.layout.currentIndex(): main_window.communication.action_button_toggle.emit( bool(self.visible_items), self.ACTION_BTN_ICON, self.action_btn_function) return show_event def _show_menu(self): """ Update menu on showEvent. """ for i, item in enumerate(self.visible_items): b = QRadioButton(self._get_button_name(item)) b.setChecked(i == 0) b.clicked.connect( functools.partial(self.templates_layout.setCurrentIndex, i)) b.setObjectName('menu_button') self.menu_layout.addWidget(b) if not self.visible_items: self.menu_layout.addStretch() # l = QLabel('Чтобы создать отчет\nначните заполнять данные') l = QLabel( 'Para criar um relatório,\ninicie os dados de preenchimento') l.setAlignment(Qt.AlignCenter) self.menu_layout.addWidget(l) self.menu_layout.addStretch() def _show_templates(self): """ Update templates on shoeEvent. """ cols = 3 templates = template_module.Template.get_all() check_templates = [] for j, item in enumerate(self.visible_items): if not templates[item.id]: # l = QLabel('Нет шаблонов для данного объекта\nУправлять шаблонами можно на вкладке настроек') l = QLabel( 'Não há modelos para este objeto\nOs modelos de gerenciamento podem estar na guia de configurações' ) l.setAlignment(Qt.AlignCenter) self.templates_layout.addWidget(l) continue layouts = [QVBoxLayout() for _ in range(cols)] for i, each in enumerate(templates[item.id]): b = QRadioButton(each.name) b.setChecked(item.template == each) b.clicked.connect( functools.partial(self._template_clicked, j, each)) if len(templates[item.id]) == 1: check_templates.append((b, j, each)) b.mouseDoubleClickEvent = functools.partial( self.open_template_edit_widget, j, each) layouts[i % cols].addWidget(b) wrapper = QHBoxLayout() for each in layouts: each.addStretch() wrapper.addLayout(each, stretch=int(100 / cols)) self.templates_layout.addWidget(utils.get_scrollable(wrapper)) for i, each in enumerate(check_templates): b, j, template = each self._template_clicked(j, template, i == 0) b.setChecked(True) def _template_selected(self, index, template): """ Change menu item name. Add template for the item. """ self.visible_items[index].template = template buttons = self.findChildren(QRadioButton, name='menu_button') buttons[index].setText(self._get_button_name( self.visible_items[index])) for i in range(len(self.visible_items)): ind = (i + index) % len(self.visible_items) if not self.visible_items[ind].template: self.templates_layout.setCurrentIndex(ind) buttons[ind].setChecked(True) buttons[index].setChecked(False) return def _get_button_name(self, item): pass def _double_click(self, index, template, event): pass def _template_clicked(self, index, template, show_next=False): pass def action_btn_function(self, event): pass def open_template_edit_widget(self, index, template, event): pass
class TimerWidget(QWidget, ClockAssets): # widget containing the timer and all methods associated with it def __init__(self): super().__init__() self.timer_duration = QTime(0, 5, 0) self.zero_time = QTime(0, 0, 0) self.timer_is_running = False self.timer = QTimer() self.timer.timeout.connect(self.update_timer) self.init_window() def init_window(self): self.stack = QStackedLayout() self.stack.addWidget(self.create_timer_window()) self.stack.addWidget(self.create_change_time_window()) self.setLayout(self.stack) def change_window(self, index): self.stack.setCurrentIndex(index) if self.stack.currentIndex() == 0: self.display_timer_time() self.change_timer_button_logo("play") self.toggle_timer_button.setDisabled(False) elif self.stack.currentIndex() == 1: # stops timer if user goes to change_time window self.stop_timer() # WINDOW def create_timer_window(self): # shows timer window self.timer_time = self.timer_duration self.timer_time_label = QLabel() self.timer_time_label.setAlignment(Qt.AlignCenter) self.timer_time_label.setFont(QFont("Arial", 20)) self.display_timer_time() self.change_time_button = QPushButton("Change Time") self.change_time_button.setFixedWidth(200) self.change_time_button.clicked.connect(lambda: self.change_window(1)) self.reset_timer_button = QPushButton() self.reset_timer_button.setIcon(QIcon(self.reset_icon)) self.reset_timer_button.setFixedWidth(200) self.reset_timer_button.clicked.connect(self.reset_timer) self.toggle_timer_button = QPushButton() self.toggle_timer_button.setIcon(QIcon(self.play_icon)) self.toggle_timer_button.setFixedWidth(200) self.toggle_timer_button.clicked.connect(self.toggle_timer) if self.timer_time == self.zero_time: self.toggle_timer_button.setDisabled(True) else: self.toggle_timer_button.setDisabled(False) self.vbox = QVBoxLayout() self.vbox.addWidget(self.timer_time_label) self.vbox.addWidget(self.change_time_button, alignment=Qt.AlignHCenter) self.vbox.addWidget(self.reset_timer_button, alignment=Qt.AlignHCenter, stretch=1) self.vbox.addWidget(self.toggle_timer_button, alignment=Qt.AlignHCenter, stretch=2) self.timer_widget = QWidget() self.timer_widget.setLayout(self.vbox) return self.timer_widget def display_timer_time(self): self.timer_time_label.setText(self.timer_time.toString("hh:mm:ss")) # EFFECT def change_timer_button_logo(self, action): if action == "play": self.toggle_timer_button.setIcon(QIcon(self.play_icon)) elif action == "pause": self.toggle_timer_button.setIcon(QIcon(self.pause_icon)) # EFFECT def play_alarm_sound_effect(self): # plays sound effect self.sound_effect = QSound(self.alarm_sound) self.sound_effect.play() def toggle_timer(self): # starts timer if it's not running or # stops timer if it is running if self.timer_is_running: self.timer_is_running = False self.change_timer_button_logo("play") self.stop_timer() else: # resume playing timer if self.timer_time != self.zero_time: self.timer_is_running = True self.change_timer_button_logo("pause") self.start_timer() def start_timer(self): # starts timer countdown self.timer.start(1000) def stop_timer(self): self.timer.stop() def update_timer(self): # updates timer's time every 1 second # decrements one second from time self.timer_time = self.timer_time.addSecs(-1) if self.timer_time == self.zero_time: self.stop_timer() self.timer_is_running = False self.change_timer_button_logo("play") self.toggle_timer_button.setDisabled(True) self.play_alarm_sound_effect() self.display_timer_time() def reset_timer(self): # changes timer countdown back to normal self.timer_time = self.timer_duration self.display_timer_time() if self.timer_time == self.zero_time: self.toggle_timer_button.setDisabled(True) else: self.toggle_timer_button.setDisabled(False) # WINDOW def create_change_time_window(self): # shows change time window self.stop_timer() self.hour_input = QLineEdit() self.hour_input.setAlignment(Qt.AlignHCenter) self.hour_input.setValidator(QIntValidator()) self.hour_input.setText(self.timer_duration.toString("hh")) self.hour_label = QLabel("HH") self.minute_input = QLineEdit() self.minute_input.setAlignment(Qt.AlignHCenter) self.minute_input.setValidator(QIntValidator()) self.minute_input.setText(self.timer_duration.toString("mm")) self.minute_label = QLabel("MM") self.second_input = QLineEdit() self.second_input.setAlignment(Qt.AlignHCenter) self.second_input.setValidator(QIntValidator()) self.second_input.setText(self.timer_duration.toString("ss")) self.second_label = QLabel("SS") self.grid_layout = QGridLayout() self.grid_layout.addWidget(self.hour_input, 0, 0) self.grid_layout.addWidget(self.hour_label, 1, 0, alignment=Qt.AlignHCenter) self.grid_layout.addWidget(self.minute_input, 0, 1) self.grid_layout.addWidget(self.minute_label, 1, 1, alignment=Qt.AlignHCenter) self.grid_layout.addWidget(self.second_input, 0, 2) self.grid_layout.addWidget(self.second_label, 1, 2, alignment=Qt.AlignHCenter) self.set_time_button = QPushButton("Set Time") self.set_time_button.clicked.connect(self.check_time_input) self.vbox = QVBoxLayout() self.vbox.addStretch() self.vbox.addLayout(self.grid_layout) self.vbox.addStretch() self.vbox.addWidget(self.set_time_button) self.change_time_widget = QWidget() self.change_time_widget.setLayout(self.vbox) return self.change_time_widget def check_time_input(self): # checks if user's HH, MM, SS in Change Time Window # are valid inputs hours = self.hour_input.text() minutes = self.minute_input.text() seconds = self.second_input.text() if hours == "": hours = 0 if minutes == "": minutes = 0 if seconds == "": seconds = 0 hours = int(hours) minutes = int(minutes) seconds = int(seconds) if hours >= 0 and minutes >= 0 and seconds >= 0: if hours < 24 and minutes < 60 and seconds < 60: if hours == 0 and minutes == 0 and seconds == 0: self.show_button_effect() else: self.timer_duration = QTime(hours, minutes, seconds) self.change_window(0) self.timer_time = self.timer_duration self.display_timer_time() else: # button turns red to give user feedback self.show_button_effect() else: self.show_button_effect() # EFFECT def show_button_effect(self): # changes button's color to red and returns back to normal after half a second self.set_time_button.setStyleSheet("background-color: red") QTimer.singleShot(500, self.reset_button_color) # EFFECT def reset_button_color(self): # resets buttons color back to light grey self.set_time_button.setStyleSheet("background-color: light grey")
class ComboEditor(QDialog): closeSplit = pyqtSignal(QWidget) aboutToCloseComboEditor = pyqtSignal() allFilesClosed = pyqtSignal() splitEditor = pyqtSignal("QWidget*", "QWidget*", bool) recentTabsModified = pyqtSignal() class NAVIGATE: prev = 0 next = 1 Q_ENUMS(NAVIGATE) def __init__(self, original=False, Force_Free=False): super(ComboEditor, self).__init__(None) #, Qt.WindowStaysOnTopHint) self.__original = original self.Force_Free = Force_Free self.__undocked = [] self._single_undocked = [] self._symbols_index = [] self.__OFiles = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.bar = ActionBar(self, main_combo=original) vbox.addWidget(self.bar) self.stackedEditor = QStackedLayout() vbox.addLayout(self.stackedEditor) self._main_container = IDE.get_service('main_container') if not self.__original and not self.Force_Free: self._main_container.fileOpened.connect(self._file_opened_by_main) # QApplication.instance().focusChanged["QWidget*", "QWidget*"].connect(\ # lambda w1, w2: QTimer.singleShot(10, lambda w1=w1, w2=w2: print("\n\nQApplication::focusChanged::", w1, w2))) self.bar.combo.showComboSelector.connect( self._main_container.change_tab) self.bar.changeCurrent.connect(self._set_current) self.bar.editorSplited.connect(self.split_editor) self.bar.runFile[str].connect(self._run_file) self.bar.closeFile.connect(lambda: self.closeSplit.emit(self)) self.bar.addToProject[str].connect(self._add_to_project) self.bar.showFileInExplorer.connect(self._show_file_in_explorer) self.bar.goToSymbol.connect(self._go_to_symbol) self.bar.undockEditor.connect(self.undock_editor) self.bar.undockThisEditor.connect(self.single_undock_editor) self.bar.reopenTab[str].connect(self._main_container.open_file) self.bar.recentTabsModified.connect( self._main_container.recent_files_changed) self.bar.code_navigator.btnPrevious.clicked['bool'].connect( lambda: self._navigate_code(self.NAVIGATE.prev)) self.bar.code_navigator.btnNext.clicked['bool'].connect( lambda: self._navigate_code(self.NAVIGATE.prev)) # QTimer.singleShot(39999, lambda : print("\n\ncombo:-:", self)) # def closeEvent(self, event): # for comboeditor in self._single_undocked: # print("has undocked", comboeditor) # comboeditor.reject() # comboeditor.deleteLater() # self.bar._close_all_files() # super(ComboEditor, self).closeEvent(event) def _navigate_code(self, val): op = self.bar.code_navigator.operation self._main_container.navigate_code_history(val, op) def setFocus(self): super(ComboEditor, self).setFocus() w = self.stackedEditor.currentWidget() if w: w.setFocus() self._editor_with_focus() def _file_opened_by_main(self, path): index = self.stackedEditor.currentIndex() ninjaide = IDE.getInstance() editable = ninjaide.get_or_create_editable(path) print("_file_opened_by_main", editable) self.add_editor(editable) self.bar.set_current_by_index(index) if index == -1: self.bar.set_current_by_index(0) def add_editor(self, neditable, keep_index=False): """Add Editor Widget to the UI area.""" if neditable.editor: if self.__original or self.Force_Free: editor = neditable.editor print("\n\nadd_editor() ignora por ahora!", editor) # disconnect old Signals try: editor.cursorPositionChanged[int, int].disconnect() except TypeError: pass try: editor.editorFocusObtained.disconnect() except TypeError: pass try: editor.currentLineChanged.disconnect() except TypeError: pass try: editor.modificationChanged['bool'].disconnect() except TypeError: pass try: neditable.checkersUpdated.disconnect() except TypeError: pass try: neditable.fileSaved.disconnect() except TypeError: pass # Disonnect file system signals only in the original try: neditable.fileClosing.disconnect() except TypeError: pass if self.__original: try: neditable.askForSaveFileClosing.disconnect() except TypeError: pass try: neditable.fileChanged.disconnect() except TypeError: pass else: editor = self._main_container.create_text_editor_from_editable( neditable) index = self.stackedEditor.currentIndex() self.stackedEditor.addWidget(editor) self.bar.add_item(neditable.display_name, neditable) if keep_index: self.bar.set_current_by_index(index) # Editor Signals editor.cursorPositionChanged[int, int].connect( self._update_cursor_position) editor.editorFocusObtained.connect(self._editor_with_focus) editor.currentLineChanged.connect(self._set_current_symbol) editor.modificationChanged['bool'].connect(self._editor_modified) neditable.checkersUpdated.connect(self._show_notification_icon) neditable.fileSaved.connect(self._update_symbols) neditable.fileSaved.connect(self._update_combo_info) # Connect file system signals only in the original neditable.fileClosing.connect(self._close_file) if self.__original: neditable.askForSaveFileClosing.connect(self._ask_for_save) neditable.fileChanged.connect(self._file_has_been_modified) # Load Symbols self._load_symbols(neditable) def show_combo_file(self): print("show_combo_file") self.bar.combo.showPopup() def show_combo_symbol(self): self.bar.symbols_combo.showPopup() def getOpenedFiles(self): return self.__OFiles.copy() def addOpenedFiles(self, fil): self.__OFiles.append(fil) def unlink_editors(self): for index in range(self.stackedEditor.count()): widget = self.stackedEditor.widget(index) widget.setDocument(QsciDocument()) def split_editor(self, orientationVertical): new_widget = ComboEditor() for neditable in self.bar.get_editables(): print("\nsplit_editor", neditable, new_widget) new_widget.add_editor(neditable) self.splitEditor.emit(self, new_widget, orientationVertical) def undock_editor(self): new_combo = ComboEditor() new_combo.setWindowTitle("NINJA-IDE") self.add_Undocked(new_combo) for neditable in self.bar.get_editables(): print("undock_editor", neditable) new_combo.add_editor(neditable) new_combo.resize(500, 500) new_combo.aboutToCloseComboEditor.connect(self._remove_undock) new_combo.show() def _remove_undock(self): print("_remove_undock", self.sender()) widget = self.sender() self.sub_Undocked(widget) def add_Undocked(self, combedit): self.__undocked.append(combedit) def sub_Undocked(self, combedit): self.__undocked.remove(combedit) def add_SingleUndocked(self, combedit): self._single_undocked.append(combedit) def sub_SingleUndocked(self, combedit): self._single_undocked.remove(combedit) # aún no se ha puesto en funcionamiento!. def single_split_editor(self, orientationVertical): new_widget = ComboEditor() for neditable in self.bar.get_editables(): print("\nsingle_split_editor", neditable, new_widget) new_widget.add_editor(neditable) self.splitEditor.emit(self, new_widget, orientationVertical) def single_undock_editor(self): new_combo = ComboEditor(Force_Free=True) new_combo.setWindowTitle("NINJA-IDE") self.add_SingleUndocked(new_combo) nEdit = self.stackedEditor.takeAt( self.stackedEditor.currentIndex()).widget() ide = IDE.getInstance() ide.unload_NEditable(nEdit) neditable = self.bar.take_editable() print("\n\nsingle_undock_editor:::", neditable, neditable.editor, nEdit, neditable.nfile) if self.stackedEditor.isEmpty(): self.allFilesClosed.emit() new_combo.add_editor(neditable) new_combo.stackedEditor.setCurrentIndex( 0) # new_combo.stackedEditor.setCurrentWidget(nEdit) new_combo.resize(500, 500) new_combo.aboutToCloseComboEditor.connect(self.single__remove_undock) new_combo.show() def single__remove_undock(self): print("single__remove_undock", self.sender()) widget = self.sender() self.sub_SingleUndocked(widget) ##widget.deleteLater() def bind_Editable(self, editable): self.bar.add_item(neditable.display_name, editable) def close_current_file(self): self.bar.about_to_close_file() def _close_file(self, neditable): print("\n\n_close_file", self.__original, self.Force_Free) index = self.bar.close_file(neditable) layoutItem = self.stackedEditor.takeAt(index) #neditable.editor.completer.cc.unload_module() self._add_to_last_opened(neditable.file_path) layoutItem.widget().deleteLater( ) # @@4 -> Comentando ésta linea desaparece el mensaje if self.stackedEditor.isEmpty(): self.allFilesClosed.emit() def _add_to_last_opened(self, path): if path not in settings.LAST_OPENED_FILES: settings.LAST_OPENED_FILES.append(path) if len(settings.LAST_OPENED_FILES) > settings.MAX_REMEMBER_TABS: self.__lastOpened = self.__lastOpened[1:] self.recentTabsModified.emit() def _editor_with_focus(self): if self._main_container.current_comboEditor is not self: self._main_container.current_comboEditor = self editor = self.stackedEditor.currentWidget() self._main_container.current_editor_changed( editor.neditable.file_path) self._load_symbols(editor.neditable) editor.neditable.update_checkers_display() def _ask_for_save(self, neditable): val = QMessageBox.No fileName = neditable.nfile.file_name val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.No: neditable.nfile.close(force_close=True) elif val == QMessageBox.Yes: neditable.ignore_checkers = True self._main_container.save_file(neditable.editor) neditable.nfile.close() def _file_has_been_modified(self, neditable): val = QMessageBox.No fileName = neditable.file_path val = QMessageBox.question( self, translations.TR_FILE_HAS_BEEN_MODIFIED, "%s%s" % (fileName, translations.TR_FILE_MODIFIED_OUTSIDE), QMessageBox.Yes | QMessageBox.No) if val == QMessageBox.Yes: neditable.reload_file() def _run_file(self, path): self._main_container.run_file(path) def _add_to_project(self, path): self._main_container._add_to_project(path) def _show_file_in_explorer(self, path): '''Connected to ActionBar's showFileInExplorer(QString) signal, forwards the file path on to the main container.''' self._main_container._show_file_in_explorer(path) def set_current(self, neditable): if neditable: self.bar.set_current_file(neditable) def _set_current(self, neditable, index): if neditable: self.stackedEditor.setCurrentIndex(index) editor = self.stackedEditor.currentWidget() self._update_cursor_position(ignore_sender=True) editor.setFocus() self._main_container.current_editor_changed(neditable.file_path) self._load_symbols(neditable) self._show_file_in_explorer(neditable.file_path) neditable.update_checkers_display() def widget(self, index): return self.stackedEditor.widget(index) def countEditors(self): """Return the number of editors opened.""" return self.stackedEditor.count() def currentEditor(self): """ rtn -> Editor()# local QsciScintilla""" return self.stackedEditor.currentWidget() def _update_cursor_position(self, line=0, col=0, ignore_sender=False): obj = self.sender() editor = self.stackedEditor.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: line += 1 self.bar.update_line_col(line, col) def _set_current_symbol(self, line, ignore_sender=False): obj = self.sender() editor = self.stackedEditor.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: index = bisect.bisect(self._symbols_index, line) if (index >= len(self._symbols_index) or self._symbols_index[index] > (line + 1)): index -= 1 self.bar.set_current_symbol(index) def _editor_modified(self, value): obj = self.sender() neditable = obj.neditable if value: text = "\u2022 %s" % neditable.display_name self.bar.update_item_text(neditable, text) else: self.bar.update_item_text(neditable, neditable.display_name) def _go_to_symbol(self, index): print("_go_to_symbol in index:", index) line = self._symbols_index[index] editor = self.stackedEditor.currentWidget() editor.go_to_line(line) def _update_symbols(self, neditable): editor = self.stackedEditor.currentWidget() # Check if it's current to avoid signals from other splits. if editor == neditable.editor: self._load_symbols(neditable) def _update_combo_info(self, neditable): self.bar.update_item_text(neditable, neditable.display_name) self._main_container.current_editor_changed(neditable.file_path) def _load_symbols(self, neditable): symbols_handler = handlers.get_symbols_handler('py') source = neditable.editor.text() source = source.encode(neditable.editor.encoding) symbols, symbols_simplified = symbols_handler.obtain_symbols( source, simple=True) self._symbols_index = sorted(symbols_simplified.keys()) symbols_simplified = sorted(list(symbols_simplified.items()), key=lambda x: x[0]) self.bar.add_symbols(symbols_simplified) line, _ = neditable.editor.getCursorPosition() self._set_current_symbol(line, True) tree_symbols = IDE.get_service('symbols_explorer') tree_symbols.update_symbols_tree(symbols, neditable.file_path) def _show_notification_icon(self, neditable): checkers = neditable.sorted_checkers icon = QIcon() for items in checkers: checker, color, _ = items if checker.checks: if isinstance(checker.checker_icon, int): icon = self.style().standardIcon(checker.checker_icon) elif isinstance(checker.checker_icon, str): icon = QIcon(checker.checker_icon) break self.bar.update_item_icon(neditable, icon) def show_menu_navigation(self): self.bar.code_navigator.show_menu_navigation() def closeEvent(self, event): for comboeditor in self._single_undocked: print("has undocked", comboeditor) comboeditor.reject() comboeditor.deleteLater() # print("\n\ncloseEvent", self) if self.__original: self.bar._close_all_files() self.aboutToCloseComboEditor.emit() super(ComboEditor, self).closeEvent(event) def reject(self): if not self.__original: super(ComboEditor, self).reject()
class MyApplication(QMainWindow): def __init__(self, parent=None): super().__init__() self.serverpack = {"equation": 0, "coeffs": []} self.initWidgets() self.initUI() def initWidgets(self): self.greetingWidget() self.mainWidget() self.centralWidget = QWidget() self.setCentralWidget(self.centralWidget) self.lay = QStackedLayout() self.lay.addWidget(self.greeting_widget) self.lay.addWidget(self.main_widget) self.core_lay = QVBoxLayout() self.core_lay.addLayout(self.lay) self.centralWidget.setLayout(self.core_lay) def greetingWidget(self): self.greeting_widget = QWidget() btn = QPushButton("Continue") btn.setStyleSheet( "color: #4d4d4f; border: 1px solid #4d4d4f; padding: 10px; border-radius: 5px; font-size: 20px; outline: none; background: none;" ) btn.setCursor(Qt.OpenHandCursor) btn.clicked.connect(self.change) lbl = QLabel( "This is equation solver.\nChoose equation, input coefficients, get answer\nand have fun!", self) lbl.setAlignment(Qt.AlignHCenter) lbl.setStyleSheet( "font-size: 20px; margin-top: 30px; margin-bottom: 50px;") grid = QVBoxLayout() grid.setAlignment(Qt.AlignTop) grid.addWidget(lbl) grid.addWidget(btn) self.greeting_widget.setLayout(grid) def mainWidget(self): self.main_widget = QWidget() self.equation_lbl = QLabel('Equation type', self) equations = [ 'Ax + B = 0', 'Ax^2 + Bx + C = 0', 'Ax + By + C = 0; Cx + Dy + E = 0', 'Ax^2 + By^2 + C = 0; Cx^2 + Dy^2 + E = 0' ] self.equation = QComboBox(self) self.equation.setMaximumWidth(300) for eq in equations: self.equation.addItem(eq) self.coef_lbl = QLabel('Coefficients') self.coeffs = QLineEdit() style = self.stylify('input') self.coeffs.setStyleSheet(style) self.coeffs.setPlaceholderText('Example: A=1;B=-1;') self.coeffs.setMaximumWidth(300) answerBtn = QPushButton('Get Answer') style = self.stylify('btn--small') answerBtn.setStyleSheet(style) answerBtn.setCursor(Qt.OpenHandCursor) answerBtn.setMaximumWidth(300) answerBtn.clicked.connect(self.sendToServer) greetBtn = QPushButton('Go to start page') style = self.stylify('btn--large') greetBtn.setStyleSheet(style) greetBtn.setCursor(Qt.OpenHandCursor) greetBtn.clicked.connect(self.change) self.resultLabel = QLabel(self) self.resultLabel.setText("Here Will Be An Answer") self.resultLabel.setMinimumWidth(165) grid = QGridLayout() grid.addWidget(self.equation_lbl, 0, 0) grid.addWidget(self.equation, 0, 1) grid.addWidget(self.coef_lbl, 1, 0) grid.addWidget(self.coeffs, 1, 1) grid.addWidget(answerBtn, 2, 1) grid.addWidget(self.resultLabel, 2, 0) grid.addWidget(greetBtn, 3, 0, 1, 2) self.main_widget.setLayout(grid) def initUI(self): self.setGeometry(400, 50, 500, 400) self.setWindowTitle('MathSelf') self.show() # STYLES def stylify(self, target): if target == 'btn--large': return "color: #4d4d4f; border: 1px solid #4d4d4f; padding: 10px; border-radius: 5px; font-size: 20px; outline: none; background: none;" elif target == 'btn--small': return "color: #4d4d4f; border: 1px solid #4d4d4f; padding: 4px; border-radius: 5px; font-size: 14px; outline: none; background: none;" elif target == 'input': return "color: #4d4d4f; border: 1px solid #4d4d4f; padding: 4px; border-radius: 5px; font-size: 14px; outline: none; background: none;" # EVENTS HANDLER def change(self): if self.lay.currentIndex() == 0: self.lay.setCurrentWidget(self.main_widget) elif self.lay.currentIndex() == 1: self.lay.setCurrentWidget(self.greeting_widget) self.resultLabel.setText("Here Will Be Your Answer") self.equation.setCurrentIndex(0) self.coeffs.setText("") # COMMUNICATES WITH SERVER def initSocketConnection(self): self.clientSocket = socket.socket() self.clientSocket.connect(('localhost', 9090)) self.serverpack = {"equation": 0, "coeffs": []} def sendToServer(self): success = self.parse() if not success: self.setErrorLabel() return else: self.initSocketConnection() self.serverpack["equation"] = self.equation.currentIndex() self.serverpack["coeffs"] = self.parsed_coeffs data = json.dumps(self.serverpack) self.clientSocket.send(data.encode()) self.receiveData() def receiveData(self): serialized_result = self.clientSocket.recv(4048) self.clientSocket.close() result = json.loads(serialized_result.decode()) print(result) if result != 'noresult': self.setResultLabel(result) else: self.setNoResultLabel() # DEAL WITH INFORMATION FOR/FROM SERVER def parse(self): self.parsed_coeffs = [] # determine which regex we need to use for a particular equation current_eq = self.equation.currentIndex() if current_eq == 0: reg_ex = r'A=.?\d+;\s*B=.?\d+;' self.serverpack["equation"] = current_eq elif current_eq == 1: reg_ex = r'A=.?\d+;\s*B=.?\d+;\s*C=.?\d+;\s*' self.serverpack["equation"] = current_eq elif current_eq == 2: reg_ex = r'A=.?\d+;\s*B=.?\d+;\s*C=.?\d+;\s*D=.?\d+;\s*E=.?\d+;\s*' self.serverpack["equation"] = current_eq elif current_eq == 3: reg_ex = r'A=.?\d+;\s*B=.?\d+;\s*C=.?\d+;\s*D=.?\d+;\s*E=.?\d+;\s*' self.serverpack["equation"] = current_eq # check regex success_reg = re.findall(reg_ex, self.coeffs.text()) if success_reg: for coef in re.findall(r'.?\d+', self.coeffs.text()): if coef[0] == '-': self.parsed_coeffs.append(int(coef)) else: self.parsed_coeffs.append(int(coef[1:len(coef)])) return True else: return False def setErrorLabel(self): self.resultLabel.setText("Wrong coefficients input") def setResultLabel(self, result): self.resultLabel.setText("%s" % result) def setNoResultLabel(self): self.resultLabel.setText("No Real Result For This Coefficients")
class ProjectTreeColumn(QDialog): dockWidget = pyqtSignal("QObject*") undockWidget = pyqtSignal() changeTitle = pyqtSignal("QObject*", str) def __init__(self, parent=None): super(ProjectTreeColumn, self).__init__(parent, Qt.WindowStaysOnTopHint) vbox = QVBoxLayout(self) vbox.setSizeConstraint(QVBoxLayout.SetDefaultConstraint) vbox.setContentsMargins(0, 0, 0, 0) self._buttons = [] self._combo_project = QComboBox() self._combo_project.setMinimumHeight(30) self._combo_project.setContextMenuPolicy(Qt.CustomContextMenu) vbox.addWidget(self._combo_project) self._projects_area = QStackedLayout() logger.debug("This is the projects area") logger.debug(self._projects_area) vbox.addLayout(self._projects_area) self.projects = [] self._combo_project.currentIndexChanged[int].connect(self._change_current_project) self._combo_project.customContextMenuRequested['const QPoint &'].connect(self.context_menu_for_root) connections = ( {'target': 'main_container', 'signal_name': 'addToProject',#(QString) 'slot': self._add_file_to_project}, {'target': 'main_container', 'signal_name': 'showFileInExplorer',#(QString) 'slot': self._show_file_in_explorer}, ) IDE.register_service('projects_explorer', self) IDE.register_signals('projects_explorer', connections) ExplorerContainer.register_tab(translations.TR_TAB_PROJECTS, self) #FIXME: Should have a ninja settings object that stores tree state #FIXME: Or bettter, application data object #TODO: check this: #self.connect(ide, SIGNAL("goingDown()"), #self.tree_projects.shutdown) #def close_project_signal(): #self.emit(SIGNAL("updateLocator()")) def install_tab(self): ide = IDE.getInstance() ui_tools.install_shortcuts(self, actions.PROJECTS_TREE_ACTIONS, ide) ide.goingDown.connect(self.close) def load_session_projects(self, projects): for project in projects: if os.path.exists(project): self._open_project_folder(project) def open_project_folder(self, folderName=None): print("open_project_folder", folderName) if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") if folderName is None: folderName = QFileDialog.getExistingDirectory( self, translations.TR_OPEN_PROJECT_DIRECTORY, directory) logger.debug("Choosing Foldername") if folderName: logger.debug("Opening %s" % folderName) self._open_project_folder(folderName) def _open_project_folder(self, folderName): ninjaide = IDE.get_service("ide") project = NProject(folderName) qfsm = ninjaide.filesystem.open_project(project) if qfsm: self.add_project(project) self.save_recent_projects(folderName) main_container = IDE.get_service('main_container') if main_container: main_container.show_editor_area() if len(self.projects) > 1: title = "%s (%s)" % ( translations.TR_TAB_PROJECTS, len(self.projects)) else: title = translations.TR_TAB_PROJECTS self.changeTitle.emit(self, title) def _add_file_to_project(self, path): """Add the file for 'path' in the project the user choose here.""" if self._projects_area.count() > 0: pathProject = [self.current_project] addToProject = add_to_project.AddToProject(pathProject, self) addToProject.exec_() if not addToProject.pathSelected: return main_container = IDE.get_service('main_container') if not main_container: return editorWidget = main_container.get_current_editor() if not editorWidget.file_path: name = QInputDialog.getText(None, translations.TR_ADD_FILE_TO_PROJECT, translations.TR_FILENAME + ": ")[0] if not name: QMessageBox.information( self, translations.TR_INVALID_FILENAME, translations.TR_INVALID_FILENAME_ENTER_A_FILENAME) return else: name = file_manager.get_basename(editorWidget.file_path) new_path = file_manager.create_path(addToProject.pathSelected, name) ide_srv = IDE.get_service("ide") old_file = ide_srv.get_or_create_nfile(path) new_file = old_file.save(editorWidget.text(), new_path) #FIXME: Make this file replace the original in the open tab else: pass # Message about no project def _show_file_in_explorer(self, path): '''Iterate through the list of available projects and show the current file in the explorer view for the first project that contains it (i.e. if the same file is included in multiple open projects, the path will be expanded for the first project only). Note: This slot is connected to the main container's "showFileInExplorer(QString)" signal.''' central = IDE.get_service('central_container') if central and not central.is_lateral_panel_visible(): return for project in self.projects: index = project.model().index(path) if index.isValid(): # This highlights the index in the tree for us project.scrollTo(index, QAbstractItemView.EnsureVisible) project.setCurrentIndex(index) break def add_project(self, project): if project not in self.projects: self._combo_project.addItem(project.name) ptree = TreeProjectsWidget(project) self._projects_area.addWidget(ptree) ptree.closeProject.connect(self._close_project) print("project", project)#setRootPath pmodel = project.model ptree.setModel(pmodel) pindex = pmodel.index(pmodel.rootPath()) ptree.setRootIndex(pindex) self.projects.append(ptree) current_index = self._projects_area.count() self._projects_area.setCurrentIndex(current_index - 1) self._combo_project.setCurrentIndex(current_index - 1) def _close_project(self, widget): """Close the project related to the tree widget.""" index = self._projects_area.currentIndex() self.projects.remove(widget) self._projects_area.takeAt(index) self._combo_project.removeItem(index) index = self._combo_project.currentIndex() self._projects_area.setCurrentIndex(index) ninjaide = IDE.getInstance() ninjaide.filesystem.close_project(widget.project.path) widget.deleteLater() if len(self.projects) > 1: title = "%s (%s)" % ( translations.TR_TAB_PROJECTS, len(self.projects)) else: title = translations.TR_TAB_PROJECTS self.changeTitle.emit(self, title) def _change_current_project(self, index): self._projects_area.setCurrentIndex(index) def close_opened_projects(self): for project in reversed(self.projects): self._close_project(project) def save_project(self): """Save all the opened files that belongs to the actual project.""" if self._projects_area.count() > 0: path = self.current_project.path main_container = IDE.get_service('main_container') if path and main_container: main_container.save_project(path) def create_new_project(self): wizard = new_project_manager.NewProjectManager(self) wizard.show() @property def current_project(self): if self._projects_area.count() > 0: return self._projects_area.currentWidget().project @property def current_tree(self): obj = self._projects_area.currentWidget() return obj def save_recent_projects(self, folder): settings = IDE.data_settings() recent_project_list = settings.value('recentProjects', {}) #if already exist on the list update the date time projectProperties = json_manager.read_ninja_project(folder) name = projectProperties.get('name', '') description = projectProperties.get('description', '') if name == '': name = file_manager.get_basename(folder) if description == '': description = translations.TR_NO_DESCRIPTION if folder in recent_project_list: properties = recent_project_list[folder] properties["lastopen"] = QDateTime.currentDateTime() properties["name"] = name properties["description"] = description recent_project_list[folder] = properties else: recent_project_list[folder] = { "name": name, "description": description, "isFavorite": False, "lastopen": QDateTime.currentDateTime()} #if the length of the project list it's high that 10 then delete #the most old #TODO: add the length of available projects to setting if len(recent_project_list) > 10: del recent_project_list[self.find_most_old_open( recent_project_list)] settings.setValue('recentProjects', recent_project_list) def find_most_old_open(self, recent_project_list): listFounder = [] for recent_project_path, content in list(recent_project_list.items()): listFounder.append((recent_project_path, int( content["lastopen"].toString("yyyyMMddHHmmzzz")))) listFounder = sorted(listFounder, key=lambda date: listFounder[1], reverse=True) # sort by date last used return listFounder[0][0] def reject(self): if self.parent() is None: self.dockWidget.emit(self) def closeEvent(self, event): self.dockWidget.emit(self) event.ignore() def context_menu_for_root(self): if not self.current_tree: # en vez de un mensaje, colorear el area de projecto... # el area de projecto debería decir 'Sin Projectos'/'without Projects' # QMessageBox.information(self, translations.TR_INFO_TITLE_PROJECT_PROPERTIES, # translations.TR_INFO_MESSAGE_PROJECT_PROPERTIES) return menu = QMenu(self) path = self.current_tree.project.path action_add_file = menu.addAction(QIcon(":img/new"), translations.TR_ADD_NEW_FILE) action_add_folder = menu.addAction(QIcon( ":img/openProj"), translations.TR_ADD_NEW_FOLDER) action_create_init = menu.addAction(translations.TR_CREATE_INIT) action_add_file.triggered['bool'].connect(lambda s,_path=path: self.current_tree._add_new_file(_path)) action_add_folder.triggered['bool'].connect(lambda s,_path=path: self.current_tree._add_new_folder(_path)) action_create_init.triggered['bool'].connect(lambda s,_path=path: self.current_tree._create_init(_path)) menu.addSeparator() actionRunProject = menu.addAction(QIcon( ":img/play"), translations.TR_RUN_PROJECT) actionRunProject.triggered['bool'].connect(lambda s: self.current_tree._execute_project()) if self.current_tree._added_to_console: actionRemoveFromConsole = menu.addAction( translations.TR_REMOVE_PROJECT_FROM_PYTHON_CONSOLE) actionRemoveFromConsole.triggered['bool'].connect(lambda s: self.current_tree._remove_project_from_console()) else: actionAdd2Console = menu.addAction( translations.TR_ADD_PROJECT_TO_PYTHON_CONSOLE) actionAdd2Console.triggered['bool'].connect(lambda s: self.current_tree._add_project_to_console()) actionShowFileSizeInfo = menu.addAction(translations.TR_SHOW_FILESIZE) actionShowFileSizeInfo.triggered['bool'].connect(lambda s: self.current_tree.show_filesize_info()) actionProperties = menu.addAction(QIcon(":img/pref"), translations.TR_PROJECT_PROPERTIES) actionProperties.triggered['bool'].connect(lambda s: self.current_tree.open_project_properties()) menu.addSeparator() action_close = menu.addAction( self.style().standardIcon(QStyle.SP_DialogCloseButton), translations.TR_CLOSE_PROJECT) action_close.triggered['bool'].connect(lambda s: self.current_tree._close_project()) #menu for the project for m in self.current_tree.extra_menus_by_scope['project']: if isinstance(m, QMenu): menu.addSeparator() menu.addMenu(m) #show the menu! menu.exec_(QCursor.pos()) def open_project_properties(self): print("open_project_properties-.-.-.-.-") _prj = self.current_project if _prj: proj = project_properties_widget.ProjectProperties(_prj, self) proj.show() return QMessageBox.information(self, translations.TR_INFO_TITLE_PROJECT_PROPERTIES, translations.TR_INFO_MESSAGE_PROJECT_PROPERTIES)
class OptionsWidget(QWidget): """ Widget holds menu with all options. """ def __init__(self, main_window, items): super().__init__() self.items = items self.layout = QStackedLayout() self._switch_user = self._get_switch_user_func(main_window) self.setLayout(self.layout) self._hide_action_button = lambda: main_window.communication.action_button_toggle.emit( False, '', None) self._create_layout(main_window) def set_current_index(self, index): self.layout.setCurrentIndex(index) if not index: self._hide_action_button() def showEvent(self, event): if not self.layout.currentIndex(): self._hide_action_button() def _get_switch_user_func(self, main_window): def _switch_user(): main_window.menu_btn_clicked(main_window.user_frame_index) self.layout.setCurrentIndex(0) return _switch_user def _get_template_import_func(self, main_window): func = self._wrap_template_func(template.Template.import_, main_window) def _alert_callback(path, value): if value: func(path[0]) def _f(): # path = QFileDialog.getOpenFileName(main_window, 'Выберите файл', options.DATABASE_DIR) path = QFileDialog.getOpenFileName(main_window, 'Selecione o arquivo', options.DATABASE_DIR) if path: # main_window.create_alert('Шаблоны с одинаковыми именами будут перезаписаны.' # '\nПродолжить?', functools.partial(_alert_callback, path)) main_window.create_alert( 'Padrões com o mesmo nome serão sobrescritos.' '\nContinua?', functools.partial(_alert_callback, path)) return _f def _get_template_export_func(self, main_window): func = self._wrap_template_func(template.Template.export, main_window) def _f(): # path = QFileDialog.getExistingDirectory(main_window, 'Выберите путь', options.DATABASE_DIR) path = QFileDialog.getExistingDirectory(main_window, 'Escolha um caminho', options.DATABASE_DIR) if path: return func(path) return _f @staticmethod def _wrap_template_func(func, main_window): def _f(path): ok, result = func(path) if ok: # main_window.show_message('Готово') main_window.show_message('Feito') else: # main_window.create_alert('Произошла ошибка\n{}'.format(result.get('error'))) main_window.create_alert('Ocorreu um erro\n{}'.format( result.get('error'))) return ok, result return _f def _create_layout(self, main_window): wrapper = QHBoxLayout() self.layout.addWidget(utils.get_scrollable(wrapper)) rows = 8 cols = 3 vboxes = [QVBoxLayout() for _ in range(cols)] # widgets = ((TemplateWidgetInOptions(main_window, self.items, self), 'Шаблоны'), # (UsersAndGroupsWidget(main_window, self), 'Пользователи и группы'), # (self._switch_user, 'Сменить пользователя'), # (self._get_template_export_func(main_window), 'Экспортировать шаблоны'), # (self._get_template_import_func(main_window), 'Импортировать шаблоны')) widgets = ((TemplateWidgetInOptions(main_window, self.items, self), 'Templates'), (UsersAndGroupsWidget(main_window, self), 'Usuários e Grupos'), (self._switch_user, 'Alterar usuário'), (self._get_template_export_func(main_window), 'Modelos de exportação'), (self._get_template_import_func(main_window), 'Importar modelos')) for i, widget in enumerate(widgets): b = QPushButton(widget[1]) if callable(widget[0]): b.clicked.connect(widget[0]) else: b.clicked.connect( functools.partial(self.layout.setCurrentIndex, i + 1)) self.layout.addWidget(widget[0]) b.setGraphicsEffect(utils.get_shadow()) vboxes[(i // rows) % cols].addWidget(b) for each in vboxes: each.addStretch() wrapper.addLayout(each, stretch=int(100 / cols))
class GUI(MainUi): def __init__(self): super(GUI, self).__init__() # 左边面板按钮触发函数,可以是import进来的,也可以是类内的 self.left_button_1.clicked.connect(self.click_password_book) self.left_button_2.clicked.connect(self.click_PrivateFiles) self.left_button_3.clicked.connect(self.click_browser_history) self.left_button_4.clicked.connect(self.click_visual_crypt) self.left_button_5.clicked.connect(self.click_mar_word) self.left_button_8.clicked.connect(self.click_find_device_location) self.left_button_6.clicked.connect(self.click_face_collect) self.left_button_7.clicked.connect(self.click_build_model) self.left_button_9.clicked.connect(self.click_set_user_password) self.left_close.clicked.connect(self.close_and_enc) # 多个窗口切换 self.stacked_layout = QStackedLayout(self.right_widget) self.main_frame1 = QWidget() self.right_layout = QtWidgets.QGridLayout() self.main_frame1.setLayout(self.right_layout) self.main_frame2 = QWidget() self.right_layout2 = QtWidgets.QGridLayout() self.main_frame2.setLayout(self.right_layout2) self.main_frame3 = QWidget() self.right_layout3 = QtWidgets.QGridLayout() self.main_frame3.setLayout(self.right_layout3) self.main_frame4 = QWidget() self.right_layout4 = QtWidgets.QGridLayout() self.main_frame4.setLayout(self.right_layout4) self.FileRecorder = [] self.main_frame5 = QWidget() self.right_layout5 = QtWidgets.QGridLayout() self.main_frame5.setLayout(self.right_layout5) self.main_frame6 = QWidget() self.right_layout6 = QtWidgets.QGridLayout() self.main_frame6.setLayout(self.right_layout6) self.stacked_layout.addWidget(self.main_frame1) self.stacked_layout.addWidget(self.main_frame2) self.stacked_layout.addWidget(self.main_frame3) self.stacked_layout.addWidget(self.main_frame4) self.stacked_layout.addWidget(self.main_frame5) self.stacked_layout.addWidget(self.main_frame6) def click_find_device_location(self): # 4. visual crypto try: from track import track_gui if self.stacked_layout.currentIndex() != 5: self.stacked_layout.setCurrentIndex(5) track_gui.find_device_location(self) except Exception as err: print(err) def click_visual_crypt(self): # 4. visual crypto from vc import vc_gui if self.stacked_layout.currentIndex() != 1: self.stacked_layout.setCurrentIndex(1) vc_gui.show_visual_crypt(self) def click_browser_history(self): # 3. browser_history from browser import browser_gui if self.stacked_layout.currentIndex() != 0: self.stacked_layout.setCurrentIndex(0) browser_gui.show_browser_history(self) def click_mar_word(self): # 5. mar_word_talk from mar import mar_gui if self.stacked_layout.currentIndex() != 2: self.stacked_layout.setCurrentIndex(2) mar_gui.show_mar_gui(self) def click_PrivateFiles(self): # 2. private_files try: self.AES = AES_128() from file_encrypt import file_gui if self.stacked_layout.currentIndex() != 3: self.stacked_layout.setCurrentIndex(3) file_gui.file_encrypt_gui(self) except Exception as err: print(err) def close_and_enc(self): try: sno = identity.global_vari.login_sno key = identity.global_vari.login_password base_dir = r"./identity/datasets" img_dir = base_dir + "\\" + "stu_" + sno lists = os.listdir(img_dir) for img in lists: path = img_dir + "\\" + img enc_pic(key, path) self.close() except Exception as err: print(err) def click_face_collect(self): try: logging.config.fileConfig('./identity/config/logging.cfg') window = DataRecordUI() window.show() except Exception as err: print(err) def click_build_model(self): try: logging.config.fileConfig('./identity/config/logging.cfg') window = DataManageUI() window.show() except Exception as err: print(err) def click_password_book(self): self.controler = Controller() self.controler.show_login() def click_set_user_password(self): if self.stacked_layout.currentIndex() != 4: self.stacked_layout.setCurrentIndex(4) self.right_min_info = QtWidgets.QLabel("学号:") self.right_layout5.addWidget(self.right_min_info, 1, 1, 1, 1) self.lineedit = QLineEdit(self) self.right_layout5.addWidget(self.lineedit, 1, 2, 1, 1) self.lineedit.setPlaceholderText("输入新用户的学号") self.right_min_info2 = QtWidgets.QLabel("密码:") self.right_layout5.addWidget(self.right_min_info2, 2, 1, 1, 1) self.lineedit2 = QLineEdit(self) self.right_layout5.addWidget(self.lineedit2, 2, 2, 1, 1) self.lineedit2.setPlaceholderText("输入新用户的密码") self.bt = QPushButton('插入', self) self.right_layout5.addWidget(self.bt, 3, 1, 1, 1) self.bt.clicked.connect(self.insert) def insert(self): sno = self.lineedit.text() password = self.lineedit2.text() mysql_insert_data(sno, password) QMessageBox.about(self, "通知", "新用户密码插入数据库成功!")
class mainWindow(QMainWindow): end_game = False def __init__(self): super().__init__() self.setWindowTitle('Oczko 21') self.setGeometry(400, 40, 700, 640) self.create_welcome_layout() self.stacked_layout = QStackedLayout() self.stacked_layout.addWidget(self.welcome_widget) self.central_widget = QWidget() self.central_widget.setLayout(self.stacked_layout) self.setCentralWidget(self.central_widget) #====================Create New Game Layout===================== self.create_new_game_layout() self.stacked_layout.addWidget(self.new_game_widget) #==========================MAIN MENU==================================== self.statusBar() newGame = QAction('&New Game', self) newGame.setStatusTip('Starts a new game...') newGame.triggered.connect(self.newGame_opt) help = QAction('&Help', self) help.setStatusTip('Help...') help.triggered.connect(self.help_opt) quitGame = QAction('&Quit', self) quitGame.setStatusTip('Quits the game...') quitGame.triggered.connect(self.quit_opt) mainMenu = self.menuBar() optionsMenu = mainMenu.addMenu('&Options') optionsMenu.addAction(newGame) optionsMenu.addAction(help) optionsMenu.addAction(quitGame) #=======================END MAIN MENU=========================================== def newGame_opt(self): #options from menu bar if self.stacked_layout.currentIndex() != 0: choice = QMessageBox.question(self, 'Start a new game', 'Do you want to start a new game?', QMessageBox.Yes | QMessageBox.No) if choice == QMessageBox.Yes: self.new_game_radio_button.check_first() self.stacked_layout.setCurrentIndex(1) self.player1_line_edit.setText('Player 1') self.player2_line_edit.setText('Player 2') else: pass else: self.new_game_radio_button.check_first() self.stacked_layout.setCurrentIndex(1) self.player1_line_edit.setText('Player 1') self.player2_line_edit.setText('Player 2') def quit_opt(self): #options from menu bar choice = QMessageBox.question(self, 'Exit', 'Do you really want to exit the game?', QMessageBox.Yes | QMessageBox.No) if choice == QMessageBox.Yes: sys.exit() else: pass def help_opt(self): # options from menu bar self.dialog = HelpWindow() self.dialog.show() @classmethod def end_game_change_value(cls, value): #method used to verify if game has ended cls.end_game = value def create_welcome_layout(self): #first layout self.welcome_layout = QVBoxLayout() self.welcome_widget = QWidget() self.welcome_widget.setLayout(self.welcome_layout) def create_new_game_layout(self): #start new game layout (new game menu) self.player1_label = QLabel('First player name:') self.player2_label = QLabel('Second player name:') self.player1_line_edit = QLineEdit() self.player2_line_edit = QLineEdit() self.cancel_button = QPushButton('Cancel') self.cancel_button.setMaximumWidth(330) self.cancel_button.setMinimumWidth(330) self.cancel_button.setMinimumHeight(60) self.start_button = QPushButton('Start New Game') self.start_button.setMaximumWidth(330) self.start_button.setMinimumWidth(330) self.start_button.setMinimumHeight(60) self.start_button.clicked.connect(self.start_new_game) self.cancel_button.clicked.connect(self.cancel) self.player_grid = QGridLayout() self.new_game_grid = QGridLayout() #player grid self.player_grid.addWidget(self.player1_label, 0, 0) self.player_grid.addWidget(self.player2_label, 1, 0) self.player_grid.addWidget(self.player1_line_edit, 0, 1) self.player_grid.addWidget(self.player2_line_edit, 1, 1) #new game grid self.new_game_radio_button = RadioButton('Play with', ('NPC', 'Friend')) self.new_game_grid.addWidget(self.new_game_radio_button, 0, 0) self.new_game_grid.addLayout(self.player_grid, 0, 1) self.new_game_grid.addWidget(self.cancel_button, 1, 0) self.new_game_grid.addWidget(self.start_button, 1, 1) self.new_game_widget = QWidget() self.new_game_widget.setLayout(self.new_game_grid) def create_game_layout(self, player_type): self.player1_points = QLabel('0') self.player1_points.setAlignment(Qt.AlignTop) self.player2_points = QLabel('0') self.player2_points.setAlignment(Qt.AlignTop) self.player1_name = QLabel(self.player1_line_edit.text()) if player_type == 1: self.player2_name = QLabel(self.player2_line_edit.text() + ' (NPC)') else: self.player2_name = QLabel(self.player2_line_edit.text()) self.points1_label = QLabel('Points:') self.points1_label.setAlignment(Qt.AlignBottom) self.points2_label = QLabel('Points:') self.points2_label.setAlignment(Qt.AlignBottom) self.player1_card = QLabel() self.player2_card = QLabel() self.stop_button_player1 = QPushButton('Stop') self.pull_button_player1 = QPushButton('Pull') self.stop_button_player2 = QPushButton('Stop') self.pull_button_player2 = QPushButton('Pull') self.new_deal_button = QPushButton('New Deal') self.information_field = QPlainTextEdit('Log here baby') self.information_field.setMaximumHeight(243) self.information_field.setMaximumWidth(200) #Buttons connection self.stop_button_player1.clicked.connect( lambda: self.stop_card_player1(player_type)) self.stop_button_player1.setMinimumHeight(30) self.pull_button_player1.clicked.connect( lambda: self.pull_card_player1(player_type)) self.pull_button_player1.setMinimumHeight(30) self.stop_button_player2.clicked.connect(self.stop_card_player2) self.stop_button_player2.setMinimumHeight(30) self.pull_button_player2.clicked.connect(self.pull_card_player2) self.pull_button_player2.setMinimumHeight(30) self.new_deal_button.clicked.connect(self.new_deal) self.new_deal_button.setMaximumWidth(90) self.new_deal_button.setMinimumHeight(80) #Table self.table = QTableWidget() self.table.setRowCount(11) self.table.setColumnCount(2) self.table.setHorizontalHeaderLabels( ("{0};{1};").format(self.player1_name.text(), self.player2_name.text()).split(";")) self.table.setVerticalHeaderLabels( ("1;2;3;4;5;6;7;8;9;10;SUM").split(";")) self.table.setMaximumHeight(355) self.table.setMaximumWidth(237) #Grids definitions self.main_game_grid = QGridLayout() self.board_grid = QGridLayout() self.table_grid = QGridLayout() self.below_table_grid = QGridLayout() self.player1_grid = QGridLayout() self.player2_grid = QGridLayout() self.player1_board_grid = QGridLayout() self.player2_board_grid = QGridLayout() self.player1_buttons_grid = QGridLayout() self.player2_buttons_grid = QGridLayout() self.player1_points_grid = QGridLayout() self.player2_points_grid = QGridLayout() #player points grids self.player1_points_grid.addWidget(self.points1_label, 0, 0) self.player1_points_grid.addWidget(self.player1_points, 1, 0) self.player2_points_grid.addWidget(self.points2_label, 0, 0) self.player2_points_grid.addWidget(self.player2_points, 1, 0) #player buttons grids self.player1_buttons_grid.addWidget(self.stop_button_player1, 0, 0) self.player1_buttons_grid.addWidget(self.pull_button_player1, 0, 1) self.player2_buttons_grid.addWidget(self.stop_button_player2, 0, 0) self.player2_buttons_grid.addWidget(self.pull_button_player2, 0, 1) #player board grids self.player1_board_grid.addWidget(self.player1_name, 0, 0) self.player1_board_grid.addWidget(self.player1_card, 0, 1) self.player1_board_grid.addLayout(self.player1_points_grid, 0, 3) self.player2_board_grid.addWidget(self.player2_name, 0, 0) self.player2_board_grid.addWidget(self.player2_card, 0, 1) self.player2_board_grid.addLayout(self.player2_points_grid, 0, 3) #player grids self.player1_grid.addLayout(self.player1_buttons_grid, 0, 0) self.player1_grid.addLayout(self.player1_board_grid, 1, 0) self.player2_grid.addLayout(self.player2_buttons_grid, 1, 0) self.player2_grid.addLayout(self.player2_board_grid, 0, 0) #below table grid self.below_table_grid.addWidget(self.information_field, 0, 0) self.below_table_grid.addWidget(self.new_deal_button, 0, 1) #three main grids self.board_grid.addLayout(self.player1_grid, 0, 0) self.board_grid.addLayout(self.player2_grid, 1, 0) self.table_grid.addWidget(self.table, 0, 0) self.table_grid.addLayout(self.below_table_grid, 1, 0) self.main_game_grid.addLayout(self.board_grid, 0, 0) self.main_game_grid.addLayout(self.table_grid, 0, 1) # layout self.game_widget = QWidget() self.game_widget.setLayout(self.main_game_grid) def cancel(self): self.stacked_layout.setCurrentIndex(0) def start_new_game(self): player_type = self.new_game_radio_button.selected_button() if len(self.player1_line_edit.text()) > 10 or len( self.player2_line_edit.text()) > 10: QMessageBox.information( self, "Too long name", "At least one of player's name is too long. It has to be shorter than 10 characters." ) elif self.player1_line_edit.text( ) == '' or self.player2_line_edit.text() == '': QMessageBox.information( self, "Empty name", "At least one of player's name is empty. Please provide correct one." ) else: if self.stacked_layout.count() > 2: self.stacked_layout.removeWidget(self.game_widget) self.create_game_layout(player_type) self.stacked_layout.addWidget(self.game_widget) self.stacked_layout.setCurrentIndex(2) self.player1_score = 0 self.player2_score = 0 self.match = -1 self.new_match() def new_match(self): self.end_game_change_value(False) self.new_deal_button.setDisabled(True) self.match += 1 self.deck = Deck() self.turn = 0 self.disable_player2_buttons() self.stop_counter = 0 self.player1_allcards = [] self.player2_allcards = [] self.pixmap = QPixmap('karty\empty.png') self.pixmap = self.pixmap.scaledToWidth(180) self.player1_card.setPixmap(self.pixmap) self.player2_card.setPixmap(self.pixmap) self.information_field.setPlainText('New deal! {0} starts'.format( self.player1_name.text())) def pull_card_player1(self, player_type): #card pixmap self.pixmap1 = QPixmap( os.path.join('karty', '{0}.png').format(self.deck.pack[self.turn])) self.pixmap1 = self.pixmap1.scaledToWidth(180) self.player1_card.setPixmap(self.pixmap1) self.player1_points.setText( str( int(self.player1_points.text()) + self.points(self.deck.pack[self.turn][:2]))) #log info self.information_field.setPlainText( self.information_field.toPlainText() + '\n{0} pulls and gains {1} points'.format( self.player1_name.text(), str(self.points(self.deck.pack[self.turn][:2])))) self.player1_allcards.append(self.deck.pack[self.turn]) self.turn += 1 self.stop_counter = 0 self.disable_player1_buttons() if player_type == 1: self.game_function() if self.end_game == False: self.npc_turn() elif player_type == 2 and self.end_game == False: self.enable_player2_buttons() self.game_function() else: self.disable_player1_buttons() self.disable_player2_buttons() def stop_card_player1(self, player_type): self.stop_counter += 1 #log info self.information_field.setPlainText( self.information_field.toPlainText() + '\n{0} stops the card'.format(self.player1_name.text())) self.disable_player1_buttons() if player_type == 1: self.game_function() if self.end_game == False: self.npc_turn() elif player_type == 2 and self.end_game == False: self.enable_player2_buttons() self.game_function() else: self.disable_player1_buttons() self.disable_player2_buttons() def pull_card_player2(self): #card pixmap self.pixmap2 = QPixmap( os.path.join('karty', '{0}.png').format(self.deck.pack[self.turn])) self.pixmap2 = self.pixmap2.scaledToWidth(180) self.player2_card.setPixmap(self.pixmap2) self.player2_points.setText( str( int(self.player2_points.text()) + self.points(self.deck.pack[self.turn][:2]))) #log info self.information_field.setPlainText( self.information_field.toPlainText() + '\n{0} pulls and gains {1} points'.format( self.player2_name.text(), str(self.points(self.deck.pack[self.turn][:2])))) self.player2_allcards.append(self.deck.pack[self.turn]) self.turn += 1 self.stop_counter = 0 self.disable_player2_buttons() if self.end_game == False: self.enable_player1_buttons() self.game_function() else: self.disable_player1_buttons() def stop_card_player2(self): self.disable_player2_buttons() self.stop_counter += 1 #log info self.information_field.setPlainText( self.information_field.toPlainText() + '\n{0} stops the card'.format(self.player2_name.text())) if self.end_game == False: self.enable_player1_buttons() self.game_function() def game_function(self): #check whether there is a winning result if int(self.player1_points.text()) == 21 and int( self.player2_points.text()) == 21: self.draw() if int(self.player1_points.text()) == 21 and self.stop_counter == 1: self.player1_wins() if int(self.player2_points.text()) == 21 and self.stop_counter == 1: self.player2_wins() # =====================Two Aces======================== if int(self.player1_points.text() ) == 22 and 'AA' in self.player1_allcards[ 0] and 'AA' in self.player1_allcards[1]: self.player1_wins() self.information_field.setPlainText( self.information_field.toPlainText() + '\n{0} has two Aces!'.format(self.player1_name.text())) if int(self.player2_points.text() ) == 22 and 'AA' in self.player2_allcards[ 0] and 'AA' in self.player2_allcards[1]: self.player2_wins() self.information_field.setPlainText( self.information_field.toPlainText() + '\n{0} has two Aces!'.format(self.player1_name.text())) # ================================================================== #============================Two times Stop button in row============================ if self.stop_counter == 2: if int(self.player1_points.text()) > int( self.player2_points.text()): self.player1_wins() elif int(self.player1_points.text()) < int( self.player2_points.text()): self.player2_wins() else: self.draw() if int(self.player1_points.text()) > 21: # player 1 exceeds 21 points self.information_field.setPlainText( self.information_field.toPlainText() + '. 21 is exceeded!') self.player2_wins() if int(self.player2_points.text()) > 21: # player 2 exceeds 21 points self.disable_player1_buttons() self.information_field.setPlainText( self.information_field.toPlainText() + '. 21 is exceeded!') self.player1_wins() def npc_turn(self): #determines when npc pulls or stops the card time.sleep(0.5) if int(self.player1_points.text()) == 21 and int( self.player2_points.text()) in [18, 19]: self.pull_card_player2() elif int(self.player1_points.text()) == 21 and int( self.player2_points.text()) == 20: self.stop_card_player2() elif int(self.player1_points.text()) >= 21: self.stop_card_player2() elif int(self.player1_points.text()) >= int( self.player2_points.text()) or int( self.player2_points.text()) <= 11: self.pull_card_player2() elif int(self.player2_points.text()) == 12: if random.randint(0, 100) < 85: self.pull_card_player2() else: self.stop_card_player2() elif int(self.player2_points.text()) == 13: if random.randint(0, 100) < 80: self.pull_card_player2() else: self.stop_card_player2() elif int(self.player2_points.text()) == 14: if random.randint(0, 100) < 70: self.pull_card_player2() else: self.stop_card_player2() elif int(self.player2_points.text()) == 15: if random.randint(0, 100) < 50: self.pull_card_player2() else: self.stop_card_player2() elif int(self.player2_points.text()) == 16: if random.randint(0, 100) < 35: self.pull_card_player2() else: self.stop_card_player2() elif int(self.player2_points.text()) == 17: if random.randint(0, 100) < 20: self.pull_card_player2() else: self.stop_card_player2() elif int(self.player2_points.text()) >= 18: if random.randint(0, 100) < 5: self.pull_card_player2() else: self.stop_card_player2() else: self.stop_card_player2() def player1_wins(self): #action when player 1 wins self.player1_score += 1 self.sum_rows() self.disable_player1_buttons() self.disable_player2_buttons() self.table.setItem(self.match, 0, QTableWidgetItem('1')) self.table.setItem(self.match, 1, QTableWidgetItem('0')) self.end_game_change_value(True) #log self.information_field.setPlainText( self.information_field.toPlainText() + '\n{0} wins the deal with {1} points!'.format( self.player1_name.text(), self.player1_points.text())) if self.match < 9: self.new_deal_button.setDisabled(False) else: self.result_message(self.player1_score, self.player2_score) def player2_wins(self): #action when player 2 wins self.player2_score += 1 self.sum_rows() self.disable_player1_buttons() self.disable_player2_buttons() self.table.setItem(self.match, 0, QTableWidgetItem('0')) self.table.setItem(self.match, 1, QTableWidgetItem('1')) self.end_game_change_value(True) #log self.information_field.setPlainText( self.information_field.toPlainText() + '\n{0} wins the deal with {1} points!'.format( self.player2_name.text(), self.player2_points.text())) if self.match < 9: self.new_deal_button.setDisabled(False) else: self.result_message(self.player1_score, self.player2_score) def draw(self): #action when there is a draw self.disable_player1_buttons() self.disable_player2_buttons() self.table.setItem(self.match, 0, QTableWidgetItem('0')) self.table.setItem(self.match, 1, QTableWidgetItem('0')) self.end_game_change_value(True) #log self.information_field.setPlainText( self.information_field.toPlainText() + '\nDraw!') if self.match < 9: self.new_deal_button.setDisabled(False) else: self.result_message(self.player1_score, self.player2_score) def result_message(self, player1_score, player2_score): if player1_score > player2_score: QMessageBox.information( self, "{0} won!".format(self.player1_name.text()), "The score is {0}:{1}. Good luck next time {2}!".format( player1_score, player2_score, self.player2_name.text())) elif player1_score < player2_score: QMessageBox.information( self, "{0} won!".format(self.player2_name.text()), "The score is {0}:{1}. Good luck next time {2}!".format( player2_score, player1_score, self.player1_name.text())) else: QMessageBox.information( self, "Draw!", "Both players scored {0} points".format(player1_score)) def disable_player1_buttons(self): self.pull_button_player1.setDisabled(True) self.stop_button_player1.setDisabled(True) def enable_player1_buttons(self): self.pull_button_player1.setDisabled(False) self.stop_button_player1.setDisabled(False) def disable_player2_buttons(self): self.pull_button_player2.setDisabled(True) self.stop_button_player2.setDisabled(True) def enable_player2_buttons(self): self.pull_button_player2.setDisabled(False) self.stop_button_player2.setDisabled(False) def points(self, value): #method to match card's name with its points value score = { '11': 1, '22': 2, '33': 3, '44': 4, '55': 5, '66': 6, '77': 7, '88': 8, '99': 9, '10': 10, 'WW': 2, 'DD': 3, 'KK': 4, 'AA': 11 } return score[value] def sum_rows(self): #sums rows from the table self.table.setItem(10, 0, QTableWidgetItem(str(self.player1_score))) self.table.setItem(10, 1, QTableWidgetItem(str(self.player2_score))) def new_deal(self): #start new deal self.enable_player1_buttons() self.player1_card.setText('') self.player2_card.setText('') self.player1_points.setText('0') self.player2_points.setText('0') self.new_match()
class ComboEditor(QWidget): # Signals closeSplit = pyqtSignal('PyQt_PyObject') splitEditor = pyqtSignal( 'PyQt_PyObject', 'PyQt_PyObject', Qt.Orientation) allFilesClosed = pyqtSignal() about_to_close_combo_editor = pyqtSignal() fileClosed = pyqtSignal("PyQt_PyObject") def __init__(self, original=False): super(ComboEditor, self).__init__(None) self.__original = original self.__undocked = [] self._symbols_index = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.bar = ActionBar(main_combo=original) vbox.addWidget(self.bar) # Info bar # self.info_bar = InfoBar(self) # self.info_bar.setVisible(False) # vbox.addWidget(self.info_bar) self.stacked = QStackedLayout() vbox.addLayout(self.stacked) self._main_container = IDE.get_service('main_container') if not self.__original: self._main_container.fileOpened['QString'].connect( self._file_opened_by_main) self.bar.combo_files.showComboSelector.connect( self._main_container.show_files_handler) self.bar.combo_files.hideComboSelector.connect( self._main_container.hide_files_handler) self.bar.change_current['PyQt_PyObject', int].connect(self._set_current) self.bar.splitEditor[bool].connect(self.split_editor) self.bar.runFile['QString'].connect(self._run_file) self.bar.closeSplit.connect(lambda: self.closeSplit.emit(self)) self.bar.addToProject['QString'].connect(self._add_to_project) self.bar.showFileInExplorer['QString'].connect( self._show_file_in_explorer) self.bar.goToSymbol[int].connect(self._go_to_symbol) self.bar.undockEditor.connect(self.undock_editor) self.bar.reopenTab['QString'].connect( lambda path: self._main_container.open_file(path)) self.bar.closeImageViewer.connect(self._close_image) self.bar.code_navigator.previousPressed.connect(self._navigate_code) self.bar.code_navigator.nextPressed.connect(self._navigate_code) # self.connect(self.bar, SIGNAL("recentTabsModified()"), # lambda: self._main_container.recent_files_changed()) # self.connect(self.bar.code_navigator.btnPrevious, # SIGNAL("clicked()"), # lambda: self._navigate_code(False)) # self.connect(self.bar.code_navigator.btnNext, SIGNAL("clicked()"), # lambda: self._navigate_code(True)) def _navigate_code(self, operation, forward=True): self._main_container.navigate_code_history(operation, forward) # op = self.bar.code_navigator.operation # self._main_container.navigate_code_history(val, op) def current_editor(self): return self.stacked.currentWidget() def setFocus(self): super(ComboEditor, self).setFocus() self.current_editor().setFocus() self._editor_with_focus() def _file_opened_by_main(self, path): index = self.stacked.currentIndex() ninjaide = IDE.get_service('ide') editable = ninjaide.get_or_create_editable(path) self.add_editor(editable) self.bar.set_current_by_index(index) if index == -1: self.bar.set_current_by_index(0) def add_image_viewer(self, viewer): """Add Image Viewer widget to the UI area""" self.stacked.addWidget(viewer) viewer.scaleFactorChanged.connect( self.bar.image_viewer_controls.update_scale_label) viewer.imageSizeChanged.connect( self.bar.image_viewer_controls.update_size_label) self.bar.add_item(viewer.display_name(), None) viewer.create_scene() if not self.bar.isVisible(): self.bar.setVisible(True) def add_editor(self, neditable, keep_index=False): """Add Editor Widget to the UI area.""" if neditable.editor: if self.__original: editor = neditable.editor else: # editor = neditable.editor.clone() editor = self._main_container.create_editor_from_editable( neditable) neditable.editor.link(editor) current_index = self.stacked.currentIndex() new_index = self.stacked.addWidget(editor) self.stacked.setCurrentIndex(new_index) self.bar.add_item(neditable.display_name, neditable) # Bar is not visible because all the files have been closed, # so if a new file is opened, show the bar if not self.bar.isVisible(): self.bar.setVisible(True) if keep_index: self.bar.set_current_by_index(current_index) # Connections neditable.fileClosing.connect(self._close_file) neditable.fileSaved.connect(self._update_symbols) editor.editorFocusObtained.connect(self._editor_with_focus) editor.modificationChanged.connect(self._editor_modified) editor.cursor_position_changed[int, int].connect( self._update_cursor_position) editor.current_line_changed[int].connect(self._set_current_symbol) if neditable._swap_file.dirty: self._editor_modified(True, sender=editor) neditable.checkersUpdated.connect(self._show_notification_icon) # Connect file system signals only in the original if self.__original: neditable.askForSaveFileClosing.connect(self._ask_for_save) neditable.fileChanged.connect(self._file_has_been_modified) # Load Symbols self._load_symbols(neditable) def show_combo_file(self): self.bar.combo.showPopup() def show_combo_symbol(self): self.bar.symbols_combo.showPopup() def show_combo_set_language(self): self.bar.set_language_combo.showPopup() def unlink_editors(self): for index in range(self.stacked.count()): widget = self.stacked.widget(index) # widget.setDocument(QsciDocument()) def clone(self): combo = ComboEditor() for neditable in self.bar.get_editables(): combo.add_editor(neditable) return combo def split_editor(self, orientation): new_combo = self.clone() self.splitEditor.emit(self, new_combo, orientation) def undock_editor(self): new_combo = ComboEditor() for neditable in self.bar.get_editables(): new_combo.add_editor(neditable) self.__undocked.append(new_combo) new_combo.setWindowTitle("NINJA-IDE") editor = self.current_editor() new_combo.set_current(editor.neditable) new_combo.resize(700, 500) new_combo.about_to_close_combo_editor.connect(self._remove_undock) new_combo.show() def _remove_undock(self): widget = self.sender() self.__undocked.remove(widget) def close_current_file(self): self.bar.about_to_close_file() def _close_image(self, index): layout_item = self.stacked.takeAt(index) layout_item.widget().deleteLater() if self.stacked.isEmpty(): self.bar.hide() self.allFilesClosed.emit() def _close_file(self, neditable): index = self.bar.close_file(neditable) layoutItem = self.stacked.takeAt(index) # neditable.editor.completer.cc.unload_module() self.fileClosed.emit(neditable.nfile) layoutItem.widget().deleteLater() if self.stacked.isEmpty(): self.bar.hide() self.allFilesClosed.emit() tree_symbols = IDE.get_service("symbols_explorer") if tree_symbols is not None: tree_symbols.clear() def _editor_with_focus(self): self._main_container.combo_area = self editor = self.current_editor() if editor is not None: self._main_container.current_editor_changed( editor.neditable.file_path) self._load_symbols(editor.neditable) editor.neditable.update_checkers_display() def _ask_for_save(self, neditable): val = QMessageBox.No fileName = neditable.nfile.file_name val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.No: neditable.nfile.close(force_close=True) elif val == QMessageBox.Yes: neditable.ignore_checkers = True self._main_container.save_file(neditable.editor) neditable.nfile.close() @pyqtSlot("PyQt_PyObject") def _recovery(self, neditable): print("lalalal") def _file_has_been_modified(self, neditable): index = self.bar.combo_files.findData(neditable) self.stacked.setCurrentIndex(index) self.bar.combo_files.setCurrentIndex(index) msg_box = QMessageBox(self) msg_box.setIcon(QMessageBox.Information) msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box.setDefaultButton(QMessageBox.Yes) msg_box.setWindowTitle(translations.TR_FILE_HAS_BEEN_MODIFIED) msg_box.setText( translations.TR_FILE_MODIFIED_OUTSIDE % neditable.display_name) result = msg_box.exec_() if result == QMessageBox.Yes: neditable.reload_file() return def _run_file(self, path): self._main_container.run_file(path) def _add_to_project(self, path): self._main_container._add_to_project(path) def _show_file_in_explorer(self, path): '''Connected to ActionBar's showFileInExplorer(QString) signal, forwards the file path on to the main container.''' self._main_container._show_file_in_explorer(path) def set_current(self, neditable): if neditable: self.bar.set_current_file(neditable) def _set_current(self, neditable, index): self.stacked.setCurrentIndex(index) if neditable: self.bar.image_viewer_controls.setVisible(False) self.bar.code_navigator.setVisible(True) self.bar.symbols_combo.setVisible(True) self.bar.lbl_position.setVisible(True) editor = self.current_editor() self._update_cursor_position(ignore_sender=True) editor.setFocus() self._main_container.current_editor_changed( neditable.file_path) self._load_symbols(neditable) # self._show_file_in_explorer(neditable.file_path) neditable.update_checkers_display() else: self.bar.combo_files.setCurrentIndex(index) viewer_widget = self.stacked.widget(index) self._main_container.current_editor_changed( viewer_widget.image_filename) self.bar.image_viewer_controls.setVisible(True) self.bar.code_navigator.setVisible(False) self.bar.symbols_combo.setVisible(False) self.bar.lbl_position.setVisible(False) def widget(self, index): return self.stacked.widget(index) def count(self): """Return the number of editors opened.""" return self.stacked.count() def _update_cursor_position(self, line=0, col=0, ignore_sender=False): obj = self.sender() editor = self.current_editor() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: line += 1 self.bar.update_line_col(line, col) def _set_current_symbol(self, line, ignore_sender=False): obj = self.sender() editor = self.current_editor() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: index = bisect.bisect(self._symbols_index, line) if (index >= len(self._symbols_index) or self._symbols_index[index] > (line + 1)): index -= 1 self.bar.set_current_symbol(index) def _editor_modified(self, value, sender=None): if sender is None: sender = self.sender() neditable = sender.neditable if value: text = "\u2022 %s" % neditable.display_name self.bar.update_item_text(neditable, text) else: self.bar.update_item_text(neditable, neditable.display_name) def _go_to_symbol(self, index): line = self._symbols_index[index] editor = self.current_editor() editor.go_to_line(line, center=True) editor.setFocus() def _update_symbols(self, neditable): editor = self.current_editor() # Check if it's current to avoid signals from other splits. if editor.neditable == neditable: self._load_symbols(neditable) def _update_combo_info(self, neditable): self.bar.update_item_text(neditable, neditable.display_name) self._main_container.current_editor_changed(neditable.file_path) def _load_symbols(self, neditable): # Get symbols handler by language symbols_handler = handlers.get_symbols_handler(neditable.language()) if symbols_handler is None: return source = neditable.editor.text source = source.encode(neditable.editor.encoding) symbols, symbols_simplified = symbols_handler.obtain_symbols( source, simple=True) self._symbols_index = sorted(symbols_simplified.keys()) symbols_simplified = sorted( list(symbols_simplified.items()), key=lambda x: x[0]) self.bar.add_symbols(symbols_simplified) line, _ = neditable.editor.cursor_position self._set_current_symbol(line, True) tree_symbols = IDE.get_service('symbols_explorer') if tree_symbols is not None: tree_symbols.update_symbols_tree(symbols, neditable.file_path) def _show_notification_icon(self, neditable): checkers = neditable.sorted_checkers icon = QIcon() for items in checkers: checker, color, _ = items if checker.checks: if isinstance(checker.checker_icon, int): icon = self.style().standardIcon(checker.checker_icon) elif isinstance(checker.checker_icon, str): icon = QIcon(checker.checker_icon) # FIXME: sucks else: icon = QIcon(checker.checker_icon) break self.bar.update_item_icon(neditable, icon) def show_menu_navigation(self): self.bar.code_navigator.show_menu_navigation() def closeEvent(self, event): self.about_to_close_combo_editor.emit() # self.emit(SIGNAL("aboutToCloseComboEditor()")) super(ComboEditor, self).closeEvent(event) def reject(self): if not self.__original: super(ComboEditor, self).reject()
class Demo(QWidget): def __init__(self): super().__init__() self.setWindowTitle("测试") # 窗口大小 self.resize(1400, 800) # self.setFixedSize(1500, 600) # 设置窗口为固定尺寸, 此时窗口不可调整大小 # self.setMinimumSize(1800, 1000) # 设置窗口最大尺寸 # self.setMaximumSize(900, 300) # 设置窗口最小尺寸 # self.setWindowFlag(Qt.WindowStaysOnTopHint) # 设置窗口顶层显示 # self.setWindowFlags(Qt.FramelessWindowHint) # 设置无边框窗口样式,不显示最上面的标题栏 self.content_font = QFont("微软雅黑", 12, QFont.Medium) # 定义字体样式 self.center() self.__setup_ui__() # 控制窗口显示在屏幕中心的方法 def center(self): # 获得窗口 qr = self.frameGeometry() # 获得屏幕中心点 cp = QDesktopWidget().availableGeometry().center() # 显示到屏幕中心 qr.moveCenter(cp) self.move(qr.topLeft()) # 关闭窗口的时候,触发了QCloseEvent,需要重写closeEvent()事件处理程序,这样就可以弹出是否退出的确认窗口 def closeEvent(self, event): reply = QMessageBox.question( self, "退出程序", # 提示框标题 "确定退出xxxx程序吗?", # 消息对话框中显示的文本 QMessageBox.Yes | QMessageBox.No, # 指定按钮的组合 Yes和No QMessageBox.No # 默认的按钮焦点,这里默认是No按钮 ) # 判断按钮的选择 if reply == QMessageBox.Yes: event.accept() else: event.ignore() def __setup_ui__(self): # 工具栏 self.frame_tool = QFrame(self) self.frame_tool.setObjectName("frame_tool") self.frame_tool.setGeometry(0, 0, self.width(), 25) self.frame_tool.setStyleSheet("border-color: rgb(0, 0, 0);") self.frame_tool.setFrameShape(QFrame.Panel) self.frame_tool.setFrameShadow(QFrame.Raised) # 1.1 界面1按钮 self.window1_btn = QToolButton(self.frame_tool) self.window1_btn.setCheckable(True) self.window1_btn.setText("window1") self.window1_btn.setObjectName("menu_btn") self.window1_btn.resize(100, 25) self.window1_btn.clicked.connect(self.click_window1) self.window1_btn.setAutoRaise( True) # 去掉工具按钮的边框线如果是QPushButton按钮的话,就是用setFlat(True)这个方法,用法相同 # 1.2 界面2按钮 self.window2_btn = QToolButton(self.frame_tool) self.window2_btn.setCheckable(True) self.window2_btn.setText("window2") self.window2_btn.setObjectName("menu_btn") self.window2_btn.resize(100, 25) self.window2_btn.move(self.window1_btn.width(), 0) self.window2_btn.clicked.connect(self.click_window2) self.window2_btn.setAutoRaise(True) self.btn_group = QButtonGroup(self.frame_tool) self.btn_group.addButton(self.window1_btn, 1) self.btn_group.addButton(self.window2_btn, 2) # 1.3 帮助下拉菜单栏 # 创建帮助工具按钮 help_btn = QToolButton(self.frame_tool) help_btn.setText("帮助") help_btn.setObjectName("menu_btn") help_btn.resize(100, 25) help_btn.move(self.window2_btn.x() + self.window2_btn.width(), 0) help_btn.setAutoRaise(True) help_btn.setPopupMode(QToolButton.InstantPopup) # 创建关于菜单 help_menu = QMenu("帮助", self.frame_tool) feedback_action = QAction(QIcon("xxx.png"), "反馈", help_menu) feedback_action.triggered.connect(self.click_feedback) about_action = QAction(QIcon("xxx.png"), "关于", help_menu) about_action.triggered.connect(self.click_about) # 把两个QAction放入help_menu help_menu.addAction(feedback_action) help_menu.addAction(about_action) # 把help_menu放入help_btn help_btn.setMenu(help_menu) # 2. 工作区域 self.main_frame = QFrame(self) self.main_frame.setGeometry(0, 25, self.width(), self.height() - self.frame_tool.height()) # self.main_frame.setStyleSheet("background-color: rgb(65, 95, 255)") # 创建堆叠布局 self.stacked_layout = QStackedLayout(self.main_frame) # 第一个布局 self.main_frame1 = QMainWindow() self.frame1_bar = QStatusBar() self.frame1_bar.setObjectName("frame1_bar") self.main_frame1.setStatusBar(self.frame1_bar) self.frame1_bar.showMessage("欢迎进入frame1") rom_frame = QFrame(self.main_frame1) rom_frame.setGeometry(0, 0, self.width(), self.main_frame.height() - 25) rom_frame.setFrameShape(QFrame.Panel) rom_frame.setFrameShadow(QFrame.Raised) # 超链接 self.super_link = QLabel(rom_frame) self.super_link.setText(""" 超链接: <a href="https://blog.csdn.net/s_daqing">点击打开查看</a> """) self.super_link.setGeometry(20, 30, 300, 25) self.super_link.setFont(self.content_font) # 使用字体样式 self.super_link.setOpenExternalLinks(True) # 使其成为超链接 self.super_link.setTextInteractionFlags( Qt.TextBrowserInteraction) # 双击可以复制文本 self.start_btn = QPushButton("开 始", rom_frame) self.start_btn.setGeometry(self.width() * 0.7, self.height() * 0.8, 100, 40) # self.start_btn.clicked.connect(self.start_btn_click) self.quit_btn = QPushButton("退 出", rom_frame) self.quit_btn.setGeometry(self.width() * 0.85, self.height() * 0.8, 100, 40) self.quit_btn.setStatusTip("点击关闭程序") # self.quit_btn.clicked.connect(QCoreApplication.instance().quit) # 点击退出可以直接退出 self.quit_btn.clicked.connect(self.close) # 点击退出按钮的退出槽函数 #rom_frame1 = QFrame() #rom_frame1.setFrameShape(QFrame.Panel) #rom_frame1.setFrameShadow(QFrame.Raised) #rom_frame2 = QFrame() #rom_frame2.setFrameShape(QFrame.Panel) #rom_frame2.setFrameShadow(QFrame.Raised) # 创建布局管理器 self.layout1 = QBoxLayout(QBoxLayout.TopToBottom) # 给管理器对象设置父控件 rom_frame.setLayout(self.layout1) self.main_frame1.setCentralWidget(rom_frame) # 把子控件添加到布局管理器中 #self.layout1.addWidget(rom_frame1, 1) #self.layout1.addWidget(rom_frame2, 1) self.layout1.setContentsMargins(0, 0, 0, 0) # 设置布局的左上右下外边距 self.layout1.setSpacing(0) # 设置子控件的内边距 frame1_bar_frame = QFrame(self.main_frame1) frame1_bar_frame.setGeometry(0, self.main_frame.height(), self.width(), 25) # 第二个布局 self.main_frame2 = QMainWindow() self.frame2_bar = QStatusBar() self.frame2_bar.setObjectName("frame2_bar") self.main_frame2.setStatusBar(self.frame2_bar) self.frame2_bar.showMessage("欢迎进入frame2") custom_frame = QFrame(self.main_frame2) custom_frame.setGeometry(0, 0, self.width(), self.main_frame.height() - 25) custom_frame.setFrameShape(QFrame.Panel) custom_frame.setFrameShadow(QFrame.Raised) custom_frame1 = QFrame() custom_frame1.setFrameShape(QFrame.Panel) custom_frame1.setFrameShadow(QFrame.Raised) custom_frame2 = QFrame() custom_frame2.setFrameShape(QFrame.Panel) custom_frame2.setFrameShadow(QFrame.Raised) custom_frame3 = QFrame() custom_frame3.setFrameShape(QFrame.Panel) custom_frame3.setFrameShadow(QFrame.Raised) # 创建布局管理器 self.layout2 = QBoxLayout(QBoxLayout.TopToBottom) # 给管理器对象设置父控件 custom_frame.setLayout(self.layout2) """ 使用了父类为QMainWindow的话,在里面使用布局类,QGridLayout, QHBoxLayout ,QVBoxLayout 等等时,发现不好用, 加上下面这句代码就可以了,QMainWindow对象.setCentralWidget(这里填布局管理器的父控件对象) """ self.main_frame2.setCentralWidget(custom_frame) # 把子控件添加到布局管理器中 self.layout2.addWidget(custom_frame1, 1) self.layout2.addWidget(custom_frame2, 1) self.layout2.addWidget(custom_frame3, 1) self.layout2.setContentsMargins(0, 0, 0, 0) # 设置布局的左上右下外边距 self.layout2.setSpacing(0) # 设置子控件的内边距 frame2_bar_frame = QFrame(self.main_frame2) frame2_bar_frame.setGeometry(0, self.main_frame.height(), self.width(), 25) # 把两个布局放进去 self.stacked_layout.addWidget(self.main_frame1) self.stacked_layout.addWidget(self.main_frame2) def click_window1(self): if self.stacked_layout.currentIndex() != 0: self.stacked_layout.setCurrentIndex(0) self.frame1_bar.showMessage("欢迎进入frame1") def click_window2(self): if self.stacked_layout.currentIndex() != 1: self.stacked_layout.setCurrentIndex(1) self.frame2_bar.showMessage("欢迎进入frame2") QDesktopServices.openUrl(QUrl("https://www.csdn.net/") ) # 点击window2按钮后,执行这个槽函数的时候,会在浏览器自动打开这个网址 def click_feedback(self, event): QMessageBox.about(self, "反馈", "使用过程中如有疑问,请联系:xxxx.163.com\r\n\r\n版本:V1.0.1") def click_about(self, event): QMessageBox.about(self, "关于", "使用文档,请参考:xxxxxx")
class ComboEditor(QDialog): closeSplit = pyqtSignal(QWidget)#closeSplit allFilesClosed = pyqtSignal() splitEditor = pyqtSignal("QWidget*", "QWidget*", bool) recentTabsModified = pyqtSignal() aboutToCloseComboEditor = pyqtSignal() class NAVIGATE: prev = 0 next = 1 Q_ENUMS(NAVIGATE) def __init__(self, original=False): super(ComboEditor, self).__init__(None, Qt.WindowStaysOnTopHint) self.__original = original self.__undocked = [] self._symbols_index = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.bar = ActionBar(main_combo=original) vbox.addWidget(self.bar) self.stacked = QStackedLayout() vbox.addLayout(self.stacked) self._main_container = IDE.get_service('main_container') if not self.__original: self._main_container.fileOpened.connect(self._file_opened_by_main) self.bar.combo.showComboSelector.connect(self._main_container.change_tab) self.bar.changeCurrent.connect(self._set_current) self.bar.editorSplited.connect(self.split_editor) self.bar.runFile[str].connect(self._run_file) self.bar.closeFile.connect(lambda: self.closeSplit.emit(self)) self.bar.addToProject[str].connect(self._add_to_project) self.bar.showFileInExplorer.connect(self._show_file_in_explorer) self.bar.goToSymbol.connect(self._go_to_symbol) self.bar.undockEditor.connect(self.undock_editor) self.bar.reopenTab[str].connect(self._main_container.open_file) self.bar.recentTabsModified.connect(self._main_container.recent_files_changed) self.bar.code_navigator.btnPrevious.clicked['bool'].connect(lambda: self._navigate_code(self.NAVIGATE.prev)) self.bar.code_navigator.btnNext.clicked['bool'].connect(lambda: self._navigate_code(self.NAVIGATE.prev)) def _navigate_code(self, val): op = self.bar.code_navigator.operation self._main_container.navigate_code_history(val, op) def currentWidget(self): return self.stacked.currentWidget() def setFocus(self): super(ComboEditor, self).setFocus() w = self.stacked.currentWidget() if w: w.setFocus() self._editor_with_focus() def _file_opened_by_main(self, path): index = self.stacked.currentIndex() ninjaide = IDE.getInstance() editable = ninjaide.get_or_create_editable(path) print("_file_opened_by_main", editable) self.add_editor(editable) self.bar.set_current_by_index(index) if index == -1: self.bar.set_current_by_index(0) def add_editor(self, neditable, keep_index=False): """Add Editor Widget to the UI area.""" if neditable.editor: if self.__original: editor = neditable.editor else: editor = self._main_container.create_editor_from_editable( neditable) index = self.stacked.currentIndex() self.stacked.addWidget(editor) self.bar.add_item(neditable.display_name, neditable) if keep_index: self.bar.set_current_by_index(index) # Editor Signals editor.cursorPositionChanged[int, int].connect(self._update_cursor_position) editor.editorFocusObtained.connect(self._editor_with_focus) editor.currentLineChanged.connect(self._set_current_symbol) editor.modificationChanged['bool'].connect(self._editor_modified) neditable.checkersUpdated.connect(self._show_notification_icon) neditable.fileSaved.connect(self._update_symbols) neditable.fileSaved.connect(self._update_combo_info) # Connect file system signals only in the original neditable.fileClosing.connect(self._close_file) if self.__original: neditable.askForSaveFileClosing.connect(self._ask_for_save) neditable.fileChanged.connect(self._file_has_been_modified) # Load Symbols self._load_symbols(neditable) def show_combo_file(self): print("show_combo_file") self.bar.combo.showPopup() def show_combo_symbol(self): self.bar.symbols_combo.showPopup() def unlink_editors(self): for index in range(self.stacked.count()): widget = self.stacked.widget(index) widget.setDocument(QsciDocument()) def split_editor(self, orientationVertical): new_widget = ComboEditor() for neditable in self.bar.get_editables(): print("\nsplit_editor", neditable, new_widget) new_widget.add_editor(neditable) self.splitEditor.emit(self, new_widget, orientationVertical) def undock_editor(self): new_combo = ComboEditor() new_combo.setWindowTitle("NINJA-IDE") self.__undocked.append(new_combo) for neditable in self.bar.get_editables(): print("undock_editor", neditable) new_combo.add_editor(neditable) new_combo.resize(500, 500) new_combo.aboutToCloseComboEditor.connect(self._remove_undock) new_combo.show() def _remove_undock(self): widget = self.sender() self.__undocked.remove(widget) def close_current_file(self): self.bar.about_to_close_file() def _close_file(self, neditable): index = self.bar.close_file(neditable) layoutItem = self.stacked.takeAt(index) #neditable.editor.completer.cc.unload_module() self._add_to_last_opened(neditable.file_path) layoutItem.widget().deleteLater() if self.stacked.isEmpty(): self.allFilesClosed.emit() def _add_to_last_opened(self, path): if path not in settings.LAST_OPENED_FILES: settings.LAST_OPENED_FILES.append(path) if len(settings.LAST_OPENED_FILES) > settings.MAX_REMEMBER_TABS: self.__lastOpened = self.__lastOpened[1:] self.recentTabsModified.emit() def _editor_with_focus(self): if self._main_container.current_widget is not self: self._main_container.current_widget = self editor = self.stacked.currentWidget() self._main_container.current_editor_changed( editor.neditable.file_path) self._load_symbols(editor.neditable) editor.neditable.update_checkers_display() def _ask_for_save(self, neditable): val = QMessageBox.No fileName = neditable.nfile.file_name val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.No: neditable.nfile.close(force_close=True) elif val == QMessageBox.Yes: neditable.ignore_checkers = True self._main_container.save_file(neditable.editor) neditable.nfile.close() def _file_has_been_modified(self, neditable): val = QMessageBox.No fileName = neditable.file_path val = QMessageBox.question( self, translations.TR_FILE_HAS_BEEN_MODIFIED, "%s%s" % (fileName, translations.TR_FILE_MODIFIED_OUTSIDE), QMessageBox.Yes | QMessageBox.No) if val == QMessageBox.Yes: neditable.reload_file() def _run_file(self, path): self._main_container.run_file(path) def _add_to_project(self, path): self._main_container._add_to_project(path) def _show_file_in_explorer(self, path): '''Connected to ActionBar's showFileInExplorer(QString) signal, forwards the file path on to the main container.''' self._main_container._show_file_in_explorer(path) def set_current(self, neditable): if neditable: self.bar.set_current_file(neditable) def _set_current(self, neditable, index): if neditable: self.stacked.setCurrentIndex(index) editor = self.stacked.currentWidget() self._update_cursor_position(ignore_sender=True) editor.setFocus() self._main_container.current_editor_changed( neditable.file_path) self._load_symbols(neditable) self._show_file_in_explorer(neditable.file_path) neditable.update_checkers_display() def widget(self, index): return self.stacked.widget(index) def count(self): """Return the number of editors opened.""" return self.stacked.count() def _update_cursor_position(self, line=0, col=0, ignore_sender=False): obj = self.sender() editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: line += 1 self.bar.update_line_col(line, col) def _set_current_symbol(self, line, ignore_sender=False): obj = self.sender() editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: index = bisect.bisect(self._symbols_index, line) if (index >= len(self._symbols_index) or self._symbols_index[index] > (line + 1)): index -= 1 self.bar.set_current_symbol(index) def _editor_modified(self, value): obj = self.sender() neditable = obj.neditable if value: text = "\u2022 %s" % neditable.display_name self.bar.update_item_text(neditable, text) else: self.bar.update_item_text(neditable, neditable.display_name) def _go_to_symbol(self, index): print("_go_to_symbol in index:", index) line = self._symbols_index[index] editor = self.stacked.currentWidget() editor.go_to_line(line) def _update_symbols(self, neditable): editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if editor == neditable.editor: self._load_symbols(neditable) def _update_combo_info(self, neditable): self.bar.update_item_text(neditable, neditable.display_name) self._main_container.current_editor_changed(neditable.file_path) def _load_symbols(self, neditable): symbols_handler = handlers.get_symbols_handler('py') source = neditable.editor.text() source = source.encode(neditable.editor.encoding) symbols, symbols_simplified = symbols_handler.obtain_symbols( source, simple=True) self._symbols_index = sorted(symbols_simplified.keys()) symbols_simplified = sorted( list(symbols_simplified.items()), key=lambda x: x[0]) self.bar.add_symbols(symbols_simplified) line, _ = neditable.editor.getCursorPosition() self._set_current_symbol(line, True) tree_symbols = IDE.get_service('symbols_explorer') tree_symbols.update_symbols_tree(symbols, neditable.file_path) def _show_notification_icon(self, neditable): checkers = neditable.sorted_checkers icon = QIcon() for items in checkers: checker, color, _ = items if checker.checks: if isinstance(checker.checker_icon, int): icon = self.style().standardIcon(checker.checker_icon) elif isinstance(checker.checker_icon, str): icon = QIcon(checker.checker_icon) break self.bar.update_item_icon(neditable, icon) def show_menu_navigation(self): self.bar.code_navigator.show_menu_navigation() def closeEvent(self, event): self.aboutToCloseComboEditor.emit() super(ComboEditor, self).closeEvent(event) def reject(self): if not self.__original: super(ComboEditor, self).reject()
class MainWindow(QWidget): """ Root widget for application. Handles signals. """ def __init__(self, items): """ Init gui. Connect signals. Create layout. """ super().__init__() self.items = items self.user = None self.communication = Communication() self.frames_layout = QStackedLayout() self.data_frame_index = None self.top_system_frame_height = 0 self._init_gui() self._set_shortcuts() def _init_gui(self): self._set_sys_attributes() self._create_layout() self.communication.menu_btn_clicked.connect(self.menu_btn_clicked) self.show() self._first_start_check() def _first_start_check(self): def _import_templates(value): if value: from app.model import template template.Template.import_(options.INIT_TEMPLATES_PATH) self.show_message('Готово!') db.KeyValue(key=options.FIRST_START_KEY, value='').save() try: first_time = db.KeyValue.get(key=options.FIRST_START_KEY).value except (exc.NoResultFound, AttributeError): first_time = True if first_time: self.create_alert(options.FIRST_START_WELCOME_TEXT, _import_templates) def _create_layout(self): """ Add TopFrame and main frames. """ MessageWidget(self) vbox = QVBoxLayout() vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.setLayout(vbox) ActionButton(self) vbox.addWidget(TopFrame(self, self.items), stretch=15) vbox.addLayout(self.frames_layout, stretch=40) self._add_frames() self.show() def _add_frames(self): """ Add frames to stacked layout. """ frames = [ DataWidget(self, self.items), TemplateWidget(self, self.items), DBWidget(self), OptionsWidget(self, self.items), UsersWidget(self), ] for i, frame in enumerate(frames): if isinstance(frame, DataWidget): self.data_frame_index = i if isinstance(frame, UsersWidget): self.user_frame_index = i self.frames_layout.addWidget(frame) self.frames_layout.setCurrentIndex(len(frames) - 1) def _set_sys_attributes(self): """ Set sys attributes like window titile. Disable OS-specific buttons. Remove borders. """ self.setWindowFlags(Qt.FramelessWindowHint) self.setWindowTitle('Hospital Helper') dw = QDesktopWidget() w = min(1300, dw.geometry().size().width() * 0.75) self.setFixedSize(w, w * 0.6) qr = self.frameGeometry() cp = dw.availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def menu_btn_clicked(self, index): """ Callback to switch between main frames. """ if self.frames_layout.currentIndex() == index == self.data_frame_index: self.communication.toggle_select_item.emit() return else: self.communication.set_select_item_visibility.emit(False) self.frames_layout.setCurrentIndex(index) def input_changed(self, item): """ Change TopFrame label when client name is changed. Emit signal for TopFrame. """ # Well it doesnt look good, but i was never good with UI. if self.items.index(item) == 0: text = [] for i, key in enumerate(item.keys()): if item[key]: text.append(str(item[key])) if i == 2: break self.communication.input_changed_signal.emit(' '.join(text)) def _set_shortcuts(self): """ Set shortcuts for fast items switch on DataWidget. """ def _shortcut_callback(key): if self.frames_layout.currentIndex() != self.data_frame_index: return self.communication.shortcut_pressed.emit(key) QShortcut(QKeySequence('Esc'), self).activated.connect(self.close) keys = [str(i) for i in range(0, 11)] + [chr(c) for c in range(ord('A'), ord('Z') + 1)] for key in keys: QShortcut(QKeySequence('Ctrl+{}'.format(key)), self).activated.connect( functools.partial(_shortcut_callback, key)) def create_crud_widget(self, model, callback, db_object=None): """ Add CrudWidget with self as parent """ CrudWidget(self, model, callback, db_object) def create_alert(self, text, callback=None): AlertWidget(self, text, callback) def show_message(self, text): self.communication.set_message_text.emit(text) def user_selected(self, user, go_to_data_frame=False): """ Callback when user is selected. Sets user, emits signal. """ self.user = user self.communication.user_selected.emit(user) if go_to_data_frame: self.communication.menu_btn_clicked.emit(self.data_frame_index) def create_report(self): """ Render and save report. Open report in default OS program. """ r = report.Report(self.user, self.items) db_report = r.render_and_save() r.open(db_report.path) self.show_message('Отчет создан') def clean_input(self): def _clean_input(value): if not value: return self.communication.clean_items.emit() self.communication.input_changed_signal.emit('') for item in self.items: item.clean() self.show_message('Ok') self.create_alert('Очистить все поля?', _clean_input) def resized(self, top_frame, top_sys_btns, event): """ Called when window is resized. Calculates Y position of the border between TopFrame and DataFrame """ waterline = top_frame.y() + top_frame.height() self.top_system_frame_height = top_sys_btns.height() self.communication.resized.emit(self.width(), waterline, self.top_system_frame_height) def keyPressEvent(self, event): """ If key is Ctrl - toggle SelectItemMenu visibility. If key is Ctrl+Return - opens ReportFrame Emits signal. """ mods = event.modifiers() if mods & Qt.ControlModifier and self.frames_layout.currentIndex() == self.data_frame_index: if event.text() is '': self.communication.ctrl_hotkey.emit(True) return elif event.key() == Qt.Key_Return: self.communication.menu_btn_clicked.emit(self.data_frame_index + 1) def keyReleaseEvent(self, event): """ If key is Ctrl - toggle SelectItemMenu visibility. """ if event.text() is '': self.communication.ctrl_hotkey.emit(False) def close(self, event=None): """ Close the application """ QCoreApplication.instance().quit() def minimize(self, event=None): """ Minimize the application """ self.setWindowState(Qt.WindowMinimized)
class MainWindow(QWidget): """ Root widget for application. Handles signals. """ def __init__(self, items): """ Init gui. Connect signals. Create layout. """ super().__init__() self.items = items self.user = None self.communication = Communication() self.frames_layout = QStackedLayout() self.data_frame_index = None self.top_system_frame_height = 0 self._init_gui() self._set_shortcuts() def _init_gui(self): self._set_sys_attributes() self._create_layout() self.communication.menu_btn_clicked.connect(self.menu_btn_clicked) self.show() self._first_start_check() def _first_start_check(self): def _import_templates(value): if value: from app.model import template template.Template.import_(options.INIT_TEMPLATES_PATH) # self.show_message('Готово!') self.show_message('Feito!') db.KeyValue(key=options.FIRST_START_KEY, value='').save() # try: # first_time = db.KeyValue.get(key=options.FIRST_START_KEY).value # except (exc.NoResultFound, AttributeError): # first_time = True # if first_time: # self.create_alert(options.FIRST_START_WELCOME_TEXT, _import_templates) def _create_layout(self): """ Add TopFrame and main frames. """ vbox = QVBoxLayout() vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.setLayout(vbox) vbox.addWidget(TopFrame(self, self.items), stretch=15) vbox.addLayout(self.frames_layout, stretch=40) self._add_frames() self.show() def _add_frames(self): """ Add frames to stacked layout. """ # frames = [ # DataWidget(self, self.items), # TemplateWidget(self, self.items), # DBWidget(self), # OptionsWidget(self, self.items), # UsersWidget(self), # ] # for i, frame in enumerate(frames): # if isinstance(frame, DataWidget): # self.data_frame_index = i # if isinstance(frame, UsersWidget): # self.user_frame_index = i # self.frames_layout.addWidget(frame) # self.frames_layout.setCurrentIndex(len(frames) - 1) def _set_sys_attributes(self): """ Set sys attributes like window title. Disable OS-specific buttons. Remove borders. """ self.setWindowFlags(Qt.FramelessWindowHint) self.setWindowTitle('Hospital Helper') dw = QDesktopWidget() w = min(1300, dw.geometry().size().width() * 0.75) self.setFixedSize(w, w * 0.65) qr = self.frameGeometry() cp = dw.availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def menu_btn_clicked(self, index): """ Callback to switch between main frames. """ if self.frames_layout.currentIndex() == index == self.data_frame_index: self.communication.toggle_select_item.emit() return else: self.communication.set_select_item_visibility.emit(False) self.frames_layout.setCurrentIndex(index) def input_changed(self, item): """ Change TopFrame label when client name is changed. Emit signal for TopFrame. """ # Well it doesnt look good, but i was never good with UI. if self.items.index(item) == 0: text = [] for i, key in enumerate(item.keys()): if item[key]: text.append(str(item[key])) if i == 2: break self.communication.input_changed_signal.emit(' '.join(text)) def _set_shortcuts(self): """ Set shortcuts for fast items switch on DataWidget. """ def _shortcut_callback(key): if self.frames_layout.currentIndex() != self.data_frame_index: return self.communication.shortcut_pressed.emit(key) # QShortcut(QKeySequence('Esc'), self).activated.connect(self.close) keys = [str(i) for i in range(0, 11) ] + [chr(c) for c in range(ord('A'), ord('Z') + 1)] for key in keys: QShortcut(QKeySequence('Ctrl+{}'.format(key)), self).activated.connect( functools.partial(_shortcut_callback, key)) def create_crud_widget(self, model, callback, db_object=None): """ Add CrudWidget with self as parent """ CrudWidget(self, model, callback, db_object) def create_alert(self, text, callback=None): AlertWidget(self, text, callback) def show_message(self, text): self.communication.set_message_text.emit(text) def user_selected(self, user, go_to_data_frame=False): """ Callback when user is selected. Sets user, emits signal. """ self.user = user self.communication.user_selected.emit(user) if go_to_data_frame: self.communication.menu_btn_clicked.emit(self.data_frame_index) def create_report(self, event=None): """ Render and save report. Open report in default OS program. """ r = report.Report(self.user, self.items) db_report = r.render_and_save() r.open(db_report.path) # self.show_message('Отчет создан') self.show_message('Relatório criado') def clean_input(self): def _clean_input(value): if not value: return self.communication.clean_items.emit() self.communication.input_changed_signal.emit('') for item in self.items: item.clean() self.show_message('Ok') # self.create_alert('Очистить все поля?', _clean_input) self.create_alert('Limpar todos os campos?', _clean_input) def resized(self, top_frame, top_sys_btns, event): """ Called when window is resized. Calculates Y position of the border between TopFrame and DataFrame """ waterline = top_frame.y() + top_frame.height() self.top_system_frame_height = top_sys_btns.height() self.communication.resized.emit(self.width(), waterline, self.top_system_frame_height) def keyPressEvent(self, event): """ If key is Ctrl - toggle SelectItemMenu visibility. If key is Ctrl+Return - opens ReportFrame Emits signal. """ mods = event.modifiers() if mods & Qt.ControlModifier and self.frames_layout.currentIndex( ) == self.data_frame_index: if event.text() is '': self.communication.ctrl_hotkey.emit(True) return elif event.key() == Qt.Key_Return: self.communication.menu_btn_clicked.emit( self.data_frame_index + 1) def keyReleaseEvent(self, event): """ If key is Ctrl - toggle SelectItemMenu visibility. """ if event.text() is '': self.communication.ctrl_hotkey.emit(False) def close(self, event=None): """ Close the application """ QCoreApplication.instance().quit() def minimize(self, event=None): """ Minimize the application """ self.setWindowState(Qt.WindowMinimized) def excepthook(self, exctype, value, traceback): with open(options.LOG_FILE, 'a') as f: f.write('\n{}\n{}'.format( datetime.datetime.now(), '\n'.join(trb.format_exception(exctype, value, traceback)))) # self.create_alert('Произошла непредвиденная ошибка.\n' # 'Попробуйте повторить действие, хотя это вряд ли поможет') self.create_alert( 'Um erro inesperado ocorreu.\n' 'Tente repetir a ação, embora seja improvável que isso ajude.')
class TopForm(QMainWindow): def __init__(self, parent=None, client_list=None): #super(TopForm, self).__init__() #super().__init__() QMainWindow.__init__(self) self.client_list = client_list self.initUI(client_list) def initUI(self, client=None): # Hide default system buttons self.setWindowFlags(Qt.Window | Qt.WindowTitleHint | Qt.WindowCloseButtonHint) #self.screen = QDesktopWidget().screenGeometry() #self.setGeometry(0,0,50,40) #self.resize(self.screen.width(),self.screen.height()) self.cwid = QWidget(self) self.setCentralWidget(self.cwid) self.layout = QGridLayout() self.cwid.setLayout(self.layout) self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 20, 0) self.mainForm = MainForm(self, client_list=client) self.loginForm = LoginForm(self) self.loginForm.loginBtn.clicked.connect(self.loginClicked) self.firststack = QStackedLayout() self.firststack.addWidget(self.loginForm) self.firststack.addWidget(self.mainForm) self.layout.addLayout(self.firststack, 1, 1) #self.layout.addLayout(self.firststack) #self.layout.insertStretch(1, 1) self.showFullScreen() def loginClicked(self): # Check username and password, if matched, then # close login form and show main form username = self.loginForm.userInput.text() password = self.loginForm.pwdInput.text() #check whether username/password stored #in the userdata module checks ok #userdata module can be extended to include mysql database #if ( 1== 0 ): if (not checkPass(username, password)): #user pass do not match #print an error message in the mainform self.loginForm.showErrorLogin() else: if self.firststack.currentIndex() == 0: self.firststack.setCurrentIndex(1) if username == password or username == 'admin': pass change_location(0, self.client_list) for i in range(len(check_list)): file_changed(check_list[i]) # Function for place center def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) # Default function for close event def closeEvent(self, event): # Show confirm prompt for exit reply = QMessageBox.question(self, 'Message', "Are you sure to quit?", QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore()
class excitation_input( QWidget, ): """ class to take input of excited states for CCFULL if type = 0: inert. no input required. if type = 1: vibrational case omega, beta, lambda, nphonon if nuclei='target', additional input omega_2, beta_2, lambda_2, nphonon_2 if type =2 : rotational case e2, beta2, beta4, nrot """ def __init__(self, nuclei='target', type_index=0): super().__init__() self.layout = QStackedLayout() self.setLayout(self.layout) self.nuclei = nuclei #---first page self.widget_first_page = QLabel('Inert ({})'.format(nuclei)) self.layout.addWidget(self.widget_first_page) #--second page self.widget_second_page = QWidget() self.layout.addWidget(self.widget_second_page) self.second_layout = QGridLayout() self.widget_second_page.setLayout(self.second_layout) self.second_layout.addWidget(QLabel('Vibrational ({})'.format(nuclei)), 0, 0) self.vib_lambda = QLineEdit('3') self.vib_omega = QLineEdit('1.81') self.vib_beta = QLineEdit('0.205') self.vib_nphonon = QLineEdit('1') self.vib_add_mod = QCheckBox('add. mod.') self.vib_lambda2 = QLineEdit('0') self.vib_omega2 = QLineEdit('0.0') self.vib_beta2 = QLineEdit('0.0') self.vib_nphonon2 = QLineEdit('0') self.vib_main_widget = combined_Widgets_grid( [[ QLabel('lambda'), self.vib_lambda, QLabel('hbar omega'), self.vib_omega ], [ QLabel('beta_lambda'), self.vib_beta, QLabel('number of phonons'), self.vib_nphonon ]]) self.second_layout.addWidget(self.vib_main_widget) if nuclei == 'target': self.vib_2nd_widget = combined_Widgets_grid( [[QCheckBox('additional mode')], [ QLabel('lambda'), self.vib_lambda2, QLabel('hbar omega'), self.vib_omega2 ], [ QLabel('beta_lambda'), self.vib_beta2, QLabel('number of phonons'), self.vib_nphonon2 ]]) self.second_layout.addWidget(self.vib_2nd_widget) #--third page self.widget_third_page = QWidget() self.layout.addWidget(self.widget_third_page) self.third_layout = QGridLayout() self.widget_third_page.setLayout(self.third_layout) self.linEdit_E2 = QLineEdit() self.linEdit_beta2 = QLineEdit() self.linEdit_beta4 = QLineEdit() self.linEdit_nrot = QLineEdit() self.rot_widget = combined_Widgets_grid( [[QLabel('Rotational ({})'.format(nuclei))], [QLabel('energy(2+)'), self.linEdit_E2], [QLabel('beta_2'), self.linEdit_beta2], [QLabel('beta_4'), self.linEdit_beta4], [QLabel('number of levels'), self.linEdit_nrot]]) self.third_layout.addWidget(self.rot_widget) #---initial page self.layout.setCurrentIndex(type_index) def change_type(self, type_index): self.layout.setCurrentIndex(type_index) def read_input(self, ): output = {} type_index = self.layout.currentIndex() output['type'] = type_index output['nuclei'] = self.nuclei if type_index == 0: # inert pass elif type_index == 1: # vibrational output['vib'] = self.vib_main_widget.get_values() if self.nuclei == 'target': output['vib2'] = self.vib_2nd_widget.get_values() elif type_index == 2: #rotational output['rot'] = self.rot_widget.get_values() return output def set_values(self, input_dict): #--opposite of read_input self.layout.setCurrentIndex(input_dict['type']) type_index = input_dict['type'] self.nuclei = input_dict['nuclei'] if type_index == 1: self.vib_main_widget.put_values(input_dict['vib']) if self.nuclei == 'target': self.vib_2nd_widget.put_values(input_dict['vib2']) elif type_index == 2: self.rot_widget.put_values(input_dict['rot']) return
class AppWindow(QMainWindow): "The application's main window" move_listener = pyqtSignal() db_activity_checker = pyqtSignal() graphics_blur = QGraphicsBlurEffect() def __init__(self): super().__init__() app_constants.GENERAL_THREAD = QThread(self) app_constants.GENERAL_THREAD.finished.connect(app_constants.GENERAL_THREAD.deleteLater) app_constants.GENERAL_THREAD.start() self.setAcceptDrops(True) self.initUI() self.start_up() QTimer.singleShot(3000, self._check_update) self.setFocusPolicy(Qt.NoFocus) self.set_shortcuts() self.graphics_blur.setParent(self) #ex = settings.ExProperties() #d = pewnet.ExHenManager(ex.ipb_id, ex.ipb_pass) #item = d.from_gallery_url('http://exhentai.org/g/861957/02741dc584/') #def a(): print(item.file) #if not item.file: # item.file_rdy.connect(a) #else: # a() def set_shortcuts(self): quit = QShortcut(QKeySequence('Ctrl+Q'), self, self.close) def init_watchers(self): def remove_gallery(g): index = self.manga_list_view.find_index(g.id, True) if index: self.manga_list_view.remove_gallery([index]) def create_gallery(path): g_dia = gallerydialog.GalleryDialog(self, path) g_dia.SERIES.connect(self.manga_list_view.gallery_model.addRows) g_dia.show() def update_gallery(g): index = self.manga_list_view.find_index(g.id) if index: self.manga_list_view.replace_edit_gallery([g], index.row()) else: log_e('Could not find gallery to update from watcher') def created(path): c_popup = io_misc.CreatedPopup(path, self) c_popup.ADD_SIGNAL.connect(create_gallery) def modified(path, gallery): mod_popup = io_misc.ModifiedPopup(path, gallery, self) def deleted(path, gallery): d_popup = io_misc.DeletedPopup(path, gallery, self) d_popup.UPDATE_SIGNAL.connect(update_gallery) d_popup.REMOVE_SIGNAL.connect(remove_gallery) def moved(new_path, gallery): mov_popup = io_misc.MovedPopup(new_path, gallery, self) mov_popup.UPDATE_SIGNAL.connect(update_gallery) self.watchers = io_misc.Watchers() self.watchers.gallery_handler.CREATE_SIGNAL.connect(created) self.watchers.gallery_handler.MODIFIED_SIGNAL.connect(modified) self.watchers.gallery_handler.MOVED_SIGNAL.connect(moved) self.watchers.gallery_handler.DELETED_SIGNAL.connect(deleted) admin_db_method_invoker = pyqtSignal(str) def start_up(self): hello = ["Hello!", "Hi!", "Onii-chan!", "Senpai!", "Hisashiburi!", "Welcome!", "Okaerinasai!", "Welcome back!", "Hajimemashite!"] self.notification_bar.add_text("{} Please don't hesitate to report any bugs you find.".format(hello[random.randint(0, len(hello)-1)])+ " Go to Settings -> About -> Bug Reporting for more info!") level = 5 def normalize_first_time(): settings.set(level, 'Application', 'first time level') settings.save() def done(status=True): gallerydb.DatabaseEmitter.RUN = True if app_constants.FIRST_TIME_LEVEL != level: normalize_first_time() if app_constants.ENABLE_MONITOR and\ app_constants.MONITOR_PATHS and all(app_constants.MONITOR_PATHS): self.init_watchers() if app_constants.LOOK_NEW_GALLERY_STARTUP: if self.manga_list_view.gallery_model.db_emitter.count == app_constants.GALLERY_DATA: self.scan_for_new_galleries() else: self.manga_list_view.gallery_model.db_emitter.DONE.connect(self.scan_for_new_galleries) self.download_manager = pewnet.Downloader() app_constants.DOWNLOAD_MANAGER = self.download_manager self.download_manager.start_manager(4) if app_constants.FIRST_TIME_LEVEL < 4: log_i('Invoking first time level {}'.format(4)) settings.set([], 'Application', 'monitor paths') settings.set([], 'Application', 'ignore paths') app_constants.MONITOR_PATHS = [] app_constants.IGNORE_PATHS = [] settings.save() done() elif app_constants.FIRST_TIME_LEVEL < 5: log_i('Invoking first time level {}'.format(5)) app_widget = misc.ApplicationPopup(self) app_widget.note_info.setText("<font color='red'>IMPORTANT:</font> Application restart is required when done") app_widget.restart_info.hide() self.admin_db = gallerydb.AdminDB() self.admin_db.moveToThread(app_constants.GENERAL_THREAD) self.admin_db.DONE.connect(done) self.admin_db.DONE.connect(lambda: app_constants.NOTIF_BAR.add_text("Application requires a restart")) self.admin_db.DONE.connect(self.admin_db.deleteLater) self.admin_db.DATA_COUNT.connect(app_widget.prog.setMaximum) self.admin_db.PROGRESS.connect(app_widget.prog.setValue) self.admin_db_method_invoker.connect(self.admin_db.rebuild_db) self.admin_db_method_invoker.connect(app_widget.show) app_widget.adjustSize() db_p = os.path.join(os.path.split(database.db_constants.DB_PATH)[0], 'sadpanda.db') self.admin_db_method_invoker.emit(db_p) else: done() def initUI(self): self.center = QWidget() self.display = QStackedLayout() self._main_layout = QVBoxLayout() self._main_layout.setSpacing(0) self._main_layout.setContentsMargins(0,0,0,0) self._main_layout.addLayout(self.display) self.center.setLayout(self._main_layout) # init the manga view variables self.manga_display() log_d('Create manga display: OK') # init the chapter view variables #self.chapter_display() self.m_l_view_index = self.display.addWidget(self.manga_list_view) self.m_t_view_index = self.display.addWidget(self.manga_table_view) self.download_window = io_misc.GalleryDownloader(self) self.download_window.hide() # init toolbar self.init_toolbar() log_d('Create toolbar: OK') # init status bar self.init_stat_bar() log_d('Create statusbar: OK') self.tags_treeview = None if app_constants.TAGS_TREEVIEW_ON_START: def tags_tree_none(): self.tags_treeview = None self.tags_treeview = misc_db.DBOverview(self, True) self.tags_treeview.about_to_close.connect(tags_tree_none) self.tags_treeview.show() self.system_tray = misc.SystemTray(QIcon(app_constants.APP_ICO_PATH), self) app_constants.SYSTEM_TRAY = self.system_tray tray_menu = QMenu(self) self.system_tray.setContextMenu(tray_menu) self.system_tray.setToolTip('Happypanda {}'.format(app_constants.vs)) tray_quit = QAction('Quit', tray_menu) tray_update = tray_menu.addAction('Check for update') tray_update.triggered.connect(self._check_update) tray_menu.addAction(tray_quit) tray_quit.triggered.connect(self.close) self.system_tray.show() def tray_activate(r=None): if not r or r == QSystemTrayIcon.Trigger: self.showNormal() self.activateWindow() self.system_tray.messageClicked.connect(tray_activate) self.system_tray.activated.connect(tray_activate) log_d('Create system tray: OK') #self.display.addWidget(self.chapter_main) self.setCentralWidget(self.center) self.setWindowIcon(QIcon(app_constants.APP_ICO_PATH)) props = settings.win_read(self, 'AppWindow') if props.resize: x, y = props.resize self.resize(x, y) else: self.resize(app_constants.MAIN_W, app_constants.MAIN_H) posx, posy = props.pos self.move(posx, posy) self.init_spinners() self.show() log_d('Show window: OK') self.notification_bar = misc.NotificationOverlay(self) p = self.toolbar.pos() self.notification_bar.move(p.x(), p.y()+self.toolbar.height()) self.notification_bar.resize(self.width()) app_constants.NOTIF_BAR = self.notification_bar log_d('Create notificationbar: OK') log_d('Window Create: OK') def _check_update(self): class upd_chk(QObject): UPDATE_CHECK = pyqtSignal(str) def __init__(self, **kwargs): super().__init__(**kwargs) def fetch_vs(self): import requests import time log_d('Checking Update') time.sleep(1.5) try: if os.path.exists('cacert.pem'): r = requests.get("https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt", verify='cacert.pem') else: r = requests.get("https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt") a = r.text vs = a.strip() self.UPDATE_CHECK.emit(vs) except: log.exception('Checking Update: FAIL') self.UPDATE_CHECK.emit('this is a very long text which is sure to be over limit') def check_update(vs): log_i('Received version: {}\nCurrent version: {}'.format(vs, app_constants.vs)) if vs != app_constants.vs: if len(vs) < 10: self.notification_bar.begin_show() self.notification_bar.add_text("Version {} of Happypanda is".format(vs)+ " available. Click here to update!", False) self.notification_bar.clicked.connect(lambda: utils.open_web_link( 'https://github.com/Pewpews/happypanda/releases')) self.notification_bar.set_clickable(True) else: self.notification_bar.add_text("An error occurred while checking for new version") self.update_instance = upd_chk() thread = QThread(self) self.update_instance.moveToThread(thread) thread.started.connect(self.update_instance.fetch_vs) self.update_instance.UPDATE_CHECK.connect(check_update) self.update_instance.UPDATE_CHECK.connect(self.update_instance.deleteLater) thread.finished.connect(thread.deleteLater) thread.start() def _web_metadata_picker(self, gallery, title_url_list, queue, parent=None): if not parent: parent = self text = "Which gallery do you want to extract metadata from?" s_gallery_popup = misc.SingleGalleryChoices(gallery, title_url_list, text, parent) s_gallery_popup.USER_CHOICE.connect(queue.put) def get_metadata(self, gal=None): metadata_spinner = misc.Spinner(self) def move_md_spinner(): metadata_spinner.update_move( QPoint( self.pos().x()+self.width()-65, self.pos().y()+self.toolbar.height()+55)) metadata_spinner.set_text("Metadata") metadata_spinner.set_size(55) metadata_spinner.move(QPoint(self.pos().x()+self.width()-65, self.pos().y()+self.toolbar.height()+55)) self.move_listener.connect(move_md_spinner) thread = QThread(self) thread.setObjectName('App.get_metadata') fetch_instance = fetch.Fetch() if gal: if not isinstance(gal, list): galleries = [gal] else: galleries = gal else: if app_constants.CONTINUE_AUTO_METADATA_FETCHER: galleries = [g for g in self.manga_list_view.gallery_model._data if not g.exed] else: galleries = self.manga_list_view.gallery_model._data if not galleries: self.notification_bar.add_text('Looks like we\'ve already gone through all galleries!') return None fetch_instance.galleries = galleries self.notification_bar.begin_show() fetch_instance.moveToThread(thread) def done(status): self.notification_bar.end_show() fetch_instance.deleteLater() if not isinstance(status, bool): galleries = [] for tup in status: galleries.append(tup[0]) class GalleryContextMenu(QMenu): app_instance = self def __init__(self, parent=None): super().__init__(parent) show_in_library_act = self.addAction('Show in library') show_in_library_act.triggered.connect(self.show_in_library) def show_in_library(self): viewer = self.app_instance.manga_list_view index = viewer.find_index(self.gallery_widget.gallery.id, True) if index: self.app_instance.manga_table_view.scroll_to_index(index) self.app_instance.manga_list_view.scroll_to_index(index) g_popup = io_misc.GalleryPopup(('Fecthing metadata for these galleries failed.'+ ' Check happypanda.log for details.', galleries), self, menu=GalleryContextMenu()) #errors = {g[0].id: g[1] for g in status} #for g_item in g_popup.get_all_items(): # g_item.setToolTip(errors[g_item.gallery.id]) g_popup.graphics_blur.setEnabled(False) close_button = g_popup.add_buttons('Close')[0] close_button.clicked.connect(g_popup.close) fetch_instance.GALLERY_PICKER.connect(self._web_metadata_picker) fetch_instance.GALLERY_EMITTER.connect(self.manga_list_view.replace_edit_gallery) fetch_instance.AUTO_METADATA_PROGRESS.connect(self.notification_bar.add_text) thread.started.connect(fetch_instance.auto_web_metadata) fetch_instance.FINISHED.connect(done) fetch_instance.FINISHED.connect(metadata_spinner.close) fetch_instance.FINISHED.connect(lambda: self.move_listener.disconnect(move_md_spinner)) thread.finished.connect(thread.deleteLater) thread.start() metadata_spinner.show() def init_stat_bar(self): self.status_bar = self.statusBar() self.status_bar.setMaximumHeight(20) self.status_bar.setSizeGripEnabled(False) self.stat_info = QLabel() self.stat_info.setIndent(5) self.sort_main = QAction("Asc", self) sort_menu = QMenu() self.sort_main.setMenu(sort_menu) s_by_title = QAction("Title", sort_menu) s_by_artist = QAction("Artist", sort_menu) sort_menu.addAction(s_by_title) sort_menu.addAction(s_by_artist) self.status_bar.addPermanentWidget(self.stat_info) #self.status_bar.addAction(self.sort_main) self.temp_msg = QLabel() self.temp_timer = QTimer() self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.connect(self.stat_row_info) self.manga_list_view.gallery_model.db_emitter.COUNT_CHANGE.connect(self.stat_row_info) self.manga_list_view.gallery_model.STATUSBAR_MSG.connect(self.stat_temp_msg) self.manga_list_view.STATUS_BAR_MSG.connect(self.stat_temp_msg) self.manga_table_view.STATUS_BAR_MSG.connect(self.stat_temp_msg) self.stat_row_info() app_constants.STAT_MSG_METHOD = self.stat_temp_msg def stat_temp_msg(self, msg): self.temp_timer.stop() self.temp_msg.setText(msg) self.status_bar.addWidget(self.temp_msg) self.temp_timer.timeout.connect(self.temp_msg.clear) self.temp_timer.setSingleShot(True) self.temp_timer.start(5000) def stat_row_info(self): r = self.manga_list_view.model().rowCount() t = self.manga_list_view.gallery_model.db_emitter.count self.stat_info.setText("Loaded {} of {} ".format(r, t)) def manga_display(self): "initiates the manga view and related things" #list view self.manga_list_view = gallery.MangaView(self) #table view self.manga_table_view = gallery.MangaTableView(self) self.manga_table_view.gallery_model = self.manga_list_view.gallery_model self.manga_table_view.sort_model = self.manga_list_view.sort_model self.manga_table_view.setModel(self.manga_table_view.sort_model) self.manga_table_view.sort_model.change_model(self.manga_table_view.gallery_model) self.manga_table_view.setColumnWidth(app_constants.FAV, 20) self.manga_table_view.setColumnWidth(app_constants.ARTIST, 200) self.manga_table_view.setColumnWidth(app_constants.TITLE, 400) self.manga_table_view.setColumnWidth(app_constants.TAGS, 300) self.manga_table_view.setColumnWidth(app_constants.TYPE, 60) self.manga_table_view.setColumnWidth(app_constants.CHAPTERS, 60) self.manga_table_view.setColumnWidth(app_constants.LANGUAGE, 100) self.manga_table_view.setColumnWidth(app_constants.LINK, 400) def init_spinners(self): # fetching spinner self.data_fetch_spinner = misc.Spinner(self) self.data_fetch_spinner.set_size(60) self.move_listener.connect( lambda: self.data_fetch_spinner.update_move( QPoint(self.pos().x()+self.width()//2, self.pos().y()+self.height()//2))) self.manga_list_view.gallery_model.ADD_MORE.connect(self.data_fetch_spinner.show) self.manga_list_view.gallery_model.db_emitter.START.connect(self.data_fetch_spinner.show) self.manga_list_view.gallery_model.ADDED_ROWS.connect(self.data_fetch_spinner.before_hide) self.manga_list_view.gallery_model.db_emitter.CANNOT_FETCH_MORE.connect(self.data_fetch_spinner.before_hide) ## deleting spinner #self.gallery_delete_spinner = misc.Spinner(self) #self.gallery_delete_spinner.set_size(40,40) ##self.gallery_delete_spinner.set_text('Removing...') #self.manga_list_view.gallery_model.rowsAboutToBeRemoved.connect(self.gallery_delete_spinner.show) #self.manga_list_view.gallery_model.rowsRemoved.connect(self.gallery_delete_spinner.before_hide) def search(self, srch_string): self.search_bar.setText(srch_string) self.search_backward.setVisible(True) self.manga_list_view.sort_model.init_search(srch_string) old_cursor_pos = self._search_cursor_pos[0] self.search_bar.end(False) if self.search_bar.cursorPosition() != old_cursor_pos+1: self.search_bar.setCursorPosition(old_cursor_pos) def favourite_display(self): "Switches to favourite display" self.manga_table_view.sort_model.fav_view() self.favourite_btn.selected = True self.library_btn.selected = False def catalog_display(self): "Switches to catalog display" self.manga_table_view.sort_model.catalog_view() self.library_btn.selected = True self.favourite_btn.selected = False def settings(self): sett = settingsdialog.SettingsDialog(self) sett.scroll_speed_changed.connect(self.manga_list_view.updateGeometries) #sett.show() def init_toolbar(self): self.toolbar = QToolBar() self.toolbar.setFixedHeight(25) self.toolbar.setWindowTitle("Show") # text for the contextmenu #self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined? self.toolbar.setMovable(False) self.toolbar.setFloatable(False) #self.toolbar.setIconSize(QSize(20,20)) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.toolbar.setIconSize(QSize(20,20)) spacer_start = QWidget() # aligns the first actions properly spacer_start.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_start) self.favourite_btn = misc.ToolbarButton(self.toolbar, 'Favorites') self.toolbar.addWidget(self.favourite_btn) self.favourite_btn.clicked.connect(self.favourite_display) #need lambda to pass extra args self.library_btn = misc.ToolbarButton(self.toolbar, 'Library') self.toolbar.addWidget(self.library_btn) self.library_btn.clicked.connect(self.catalog_display) #need lambda to pass extra args self.library_btn.selected = True self.toolbar.addSeparator() gallery_menu = QMenu() gallery_action = QToolButton() gallery_action.setText('Gallery ') gallery_action.setPopupMode(QToolButton.InstantPopup) gallery_action.setToolTip('Contains various gallery related features') gallery_action.setMenu(gallery_menu) add_gallery_icon = QIcon(app_constants.PLUS_PATH) gallery_action_add = QAction(add_gallery_icon, "Add single gallery...", self) gallery_action_add.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit) gallery_action_add.setToolTip('Add a single gallery thoroughly') gallery_menu.addAction(gallery_action_add) add_more_action = QAction(add_gallery_icon, "Add galleries...", self) add_more_action.setStatusTip('Add galleries from different folders') add_more_action.triggered.connect(lambda: self.populate(True)) gallery_menu.addAction(add_more_action) populate_action = QAction(add_gallery_icon, "Populate from directory/archive...", self) populate_action.setStatusTip('Populates the DB with galleries from a single folder or archive') populate_action.triggered.connect(self.populate) gallery_menu.addAction(populate_action) gallery_menu.addSeparator() metadata_action = QAction('Get metadata for all galleries', self) metadata_action.triggered.connect(self.get_metadata) gallery_menu.addAction(metadata_action) scan_galleries_action = QAction('Scan for new galleries', self) scan_galleries_action.triggered.connect(self.scan_for_new_galleries) scan_galleries_action.setStatusTip('Scan monitored folders for new galleries') gallery_menu.addAction(scan_galleries_action) gallery_action_random = gallery_menu.addAction("Open random gallery") gallery_action_random.triggered.connect(self.manga_list_view.open_random_gallery) self.toolbar.addWidget(gallery_action) misc_action = QToolButton() misc_action.setText('Tools ') misc_action_menu = QMenu() misc_action.setMenu(misc_action_menu) misc_action.setPopupMode(QToolButton.InstantPopup) misc_action.setToolTip("Contains misc. features") gallery_downloader = QAction("Gallery Downloader", misc_action_menu) gallery_downloader.triggered.connect(self.download_window.show) misc_action_menu.addAction(gallery_downloader) duplicate_check_simple = QAction("Simple Duplicate Finder", misc_action_menu) duplicate_check_simple.triggered.connect(lambda: self.manga_list_view.duplicate_check()) misc_action_menu.addAction(duplicate_check_simple) self.toolbar.addWidget(misc_action) spacer_middle = QWidget() # aligns buttons to the right spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolbar.addWidget(spacer_middle) sort_action = QToolButton() sort_action.setIcon(QIcon(app_constants.SORT_PATH)) sort_action.setMenu(misc.SortMenu(self.toolbar, self.manga_list_view)) sort_action.setPopupMode(QToolButton.InstantPopup) self.toolbar.addWidget(sort_action) self.grid_toggle_g_icon = QIcon(app_constants.GRID_PATH) self.grid_toggle_l_icon = QIcon(app_constants.LIST_PATH) self.grid_toggle = QToolButton() if self.display.currentIndex() == self.m_l_view_index: self.grid_toggle.setIcon(self.grid_toggle_l_icon) else: self.grid_toggle.setIcon(self.grid_toggle_g_icon) self.grid_toggle.setObjectName('gridtoggle') self.grid_toggle.clicked.connect(self.toggle_view) self.toolbar.addWidget(self.grid_toggle) spacer_mid2 = QWidget() spacer_mid2.setFixedSize(QSize(5, 1)) self.toolbar.addWidget(spacer_mid2) def set_search_case(b): app_constants.GALLERY_SEARCH_CASE = b settings.set(b, 'Application', 'gallery search case') settings.save() def set_search_strict(b): app_constants.GALLERY_SEARCH_STRICT = b settings.set(b, 'Application', 'gallery search strict') settings.save() self.search_bar = misc.LineEdit() search_options = self.search_bar.addAction(QIcon(app_constants.SEARCH_OPTIONS_PATH), QLineEdit.TrailingPosition) search_options_menu = QMenu(self) search_options.triggered.connect(lambda: search_options_menu.popup(QCursor.pos())) search_options.setMenu(search_options_menu) case_search_option = search_options_menu.addAction('Case Sensitive') case_search_option.setCheckable(True) case_search_option.setChecked(app_constants.GALLERY_SEARCH_CASE) case_search_option.toggled.connect(set_search_case) strict_search_option = search_options_menu.addAction('Match whole terms') strict_search_option.setCheckable(True) strict_search_option.setChecked(app_constants.GALLERY_SEARCH_STRICT) strict_search_option.toggled.connect(set_search_strict) self.search_bar.setObjectName('search_bar') self.search_timer = QTimer(self) self.search_timer.setSingleShot(True) self.search_timer.timeout.connect(lambda: self.search(self.search_bar.text())) self._search_cursor_pos = [0, 0] def set_cursor_pos(old, new): self._search_cursor_pos[0] = old self._search_cursor_pos[1] = new self.search_bar.cursorPositionChanged.connect(set_cursor_pos) if app_constants.SEARCH_AUTOCOMPLETE: completer = QCompleter(self) completer_view = misc.CompleterPopupView() completer.setPopup(completer_view) completer_view._setup() completer.setModel(self.manga_list_view.gallery_model) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setCompletionMode(QCompleter.PopupCompletion) completer.setCompletionRole(Qt.DisplayRole) completer.setCompletionColumn(app_constants.TITLE) completer.setFilterMode(Qt.MatchContains) self.search_bar.setCompleter(completer) self.search_bar.returnPressed.connect(lambda: self.search(self.search_bar.text())) if not app_constants.SEARCH_ON_ENTER: self.search_bar.textEdited.connect(lambda: self.search_timer.start(800)) self.search_bar.setPlaceholderText("Search title, artist, namespace & tags") self.search_bar.setMinimumWidth(150) self.search_bar.setMaximumWidth(500) self.search_bar.setFixedHeight(19) self.manga_list_view.sort_model.HISTORY_SEARCH_TERM.connect(lambda a: self.search_bar.setText(a)) self.toolbar.addWidget(self.search_bar) def search_history(_, back=True): # clicked signal passes a bool sort_model = self.manga_list_view.sort_model nav = sort_model.PREV if back else sort_model.NEXT history_term = sort_model.navigate_history(nav) if back: self.search_forward.setVisible(True) back = QShortcut(QKeySequence(QKeySequence.Back), self, lambda: search_history(None)) forward = QShortcut(QKeySequence(QKeySequence.Forward), self, lambda: search_history(None, False)) search_backbutton = QToolButton(self.toolbar) search_backbutton.setText(u'\u25C0') search_backbutton.setFixedWidth(15) search_backbutton.clicked.connect(search_history) self.search_backward = self.toolbar.addWidget(search_backbutton) self.search_backward.setVisible(False) search_forwardbutton = QToolButton(self.toolbar) search_forwardbutton.setText(u'\u25B6') search_forwardbutton.setFixedWidth(15) search_forwardbutton.clicked.connect(lambda: search_history(None, False)) self.search_forward = self.toolbar.addWidget(search_forwardbutton) self.search_forward.setVisible(False) spacer_end = QWidget() # aligns settings action properly spacer_end.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_end) settings_act = QToolButton(self.toolbar) settings_act.setIcon(QIcon(app_constants.SETTINGS_PATH)) settings_act.clicked.connect(self.settings) self.toolbar.addWidget(settings_act) spacer_end2 = QWidget() # aligns About action properly spacer_end2.setFixedSize(QSize(5, 1)) self.toolbar.addWidget(spacer_end2) self.addToolBar(self.toolbar) def toggle_view(self): """ Toggles the current display view """ if self.display.currentIndex() == self.m_l_view_index: self.display.setCurrentIndex(self.m_t_view_index) self.grid_toggle.setIcon(self.grid_toggle_g_icon) else: self.display.setCurrentIndex(self.m_l_view_index) self.grid_toggle.setIcon(self.grid_toggle_l_icon) # TODO: Improve this so that it adds to the gallery dialog, # so user can edit data before inserting (make it a choice) def populate(self, mixed=None): "Populates the database with gallery from local drive'" if mixed: gallery_view = misc.GalleryListView(self, True) gallery_view.SERIES.connect(self.gallery_populate) gallery_view.show() else: msg_box = misc.BasePopup(self) l = QVBoxLayout() msg_box.main_widget.setLayout(l) l.addWidget(QLabel('Directory or Archive?')) l.addLayout(msg_box.buttons_layout) def from_dir(): path = QFileDialog.getExistingDirectory(self, "Choose a directory containing your galleries") if not path: return msg_box.close() app_constants.OVERRIDE_SUBFOLDER_AS_GALLERY = True self.gallery_populate(path, True) def from_arch(): path = QFileDialog.getOpenFileName(self, 'Choose an archive containing your galleries', filter=utils.FILE_FILTER) path = [path[0]] if not all(path) or not path: return msg_box.close() app_constants.OVERRIDE_SUBFOLDER_AS_GALLERY = True self.gallery_populate(path, True) buttons = msg_box.add_buttons('Directory', 'Archive', 'Close') buttons[2].clicked.connect(msg_box.close) buttons[0].clicked.connect(from_dir) buttons[1].clicked.connect(from_arch) msg_box.adjustSize() msg_box.show() def gallery_populate(self, path, validate=False): "Scans the given path for gallery to add into the DB" if len(path) is not 0: data_thread = QThread(self) data_thread.setObjectName('General gallery populate') loading = misc.Loading(self) self.g_populate_inst = fetch.Fetch() self.g_populate_inst.series_path = path loading.show() def finished(status): def hide_loading(): loading.hide() if status: if len(status) != 0: def add_gallery(gallery_list): def append_to_model(x): self.manga_list_view.sort_model.insertRows(x, None, len(x)) self.manga_list_view.sort_model.init_search( self.manga_list_view.sort_model.current_term) class A(QObject): done = pyqtSignal() prog = pyqtSignal(int) def __init__(self, obj, parent=None): super().__init__(parent) self.obj = obj self.galleries = [] def add_to_db(self): for y, x in enumerate(self.obj): gallerydb.add_method_queue( gallerydb.GalleryDB.add_gallery_return, False, x) self.galleries.append(x) y += 1 self.prog.emit(y) append_to_model(self.galleries) self.done.emit() loading.progress.setMaximum(len(gallery_list)) self.a_instance = A(gallery_list) thread = QThread(self) thread.setObjectName('Database populate') def loading_show(numb): if loading.isHidden(): loading.show() loading.setText('Populating database ({}/{})\nPlease wait...'.format( numb, loading.progress.maximum())) loading.progress.setValue(numb) loading.show() def loading_hide(): loading.close() self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.emit() self.a_instance.moveToThread(thread) self.a_instance.prog.connect(loading_show) thread.started.connect(self.a_instance.add_to_db) self.a_instance.done.connect(loading_hide) self.a_instance.done.connect(self.a_instance.deleteLater) #a_instance.add_to_db() thread.finished.connect(thread.deleteLater) thread.start() #data_thread.quit hide_loading() log_i('Populating DB from gallery folder: OK') if validate: gallery_list = misc.GalleryListView(self) gallery_list.SERIES.connect(add_gallery) for ser in status: if ser.is_archive and app_constants.SUBFOLDER_AS_GALLERY: p = os.path.split(ser.path)[1] if ser.chapters[0].path: pt_in_arch = os.path.split(ser.path_in_archive) pt_in_arch = pt_in_arch[1] or pt_in_arch[0] text = '{}: {}'.format(p, pt_in_arch) else: text = p gallery_list.add_gallery(ser, text) else: gallery_list.add_gallery(ser, os.path.split(ser.path)[1]) #self.manga_list_view.gallery_model.populate_data() gallery_list.update_count() gallery_list.show() else: add_gallery(status) else: log_d('No new gallery was found') loading.setText("No new gallery found") #data_thread.quit else: log_e('Populating DB from gallery folder: Nothing was added!') loading.setText("<font color=red>Nothing was added. Check happypanda_log for details..</font>") loading.progress.setStyleSheet("background-color:red;") data_thread.quit QTimer.singleShot(8000, loading.close) def skipped_gs(s_list): "Skipped galleries" msg_box = QMessageBox(self) msg_box.setIcon(QMessageBox.Question) msg_box.setText('Do you want to view skipped paths?') msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box.setDefaultButton(QMessageBox.No) if msg_box.exec() == QMessageBox.Yes: list_wid = QTableWidget(self) list_wid.setAttribute(Qt.WA_DeleteOnClose) list_wid.setRowCount(len(s_list)) list_wid.setColumnCount(2) list_wid.setAlternatingRowColors(True) list_wid.setEditTriggers(list_wid.NoEditTriggers) list_wid.setHorizontalHeaderLabels(['Reason', 'Path']) list_wid.setSelectionBehavior(list_wid.SelectRows) list_wid.setSelectionMode(list_wid.SingleSelection) list_wid.setSortingEnabled(True) list_wid.verticalHeader().hide() list_wid.setAutoScroll(False) for x, g in enumerate(s_list): list_wid.setItem(x, 0, QTableWidgetItem(g[1])) list_wid.setItem(x, 1, QTableWidgetItem(g[0])) list_wid.resizeColumnsToContents() list_wid.setWindowTitle('{} skipped paths'.format(len(s_list))) list_wid.setWindowFlags(Qt.Window) list_wid.resize(900,400) list_wid.doubleClicked.connect(lambda i: utils.open_path( list_wid.item(i.row(), 1).text(), list_wid.item(i.row(), 1).text())) list_wid.show() def a_progress(prog): loading.progress.setValue(prog) loading.setText("Preparing galleries...") self.g_populate_inst.moveToThread(data_thread) self.g_populate_inst.DATA_COUNT.connect(loading.progress.setMaximum) self.g_populate_inst.PROGRESS.connect(a_progress) self.g_populate_inst.FINISHED.connect(finished) self.g_populate_inst.FINISHED.connect(self.g_populate_inst.deleteLater) self.g_populate_inst.SKIPPED.connect(skipped_gs) data_thread.finished.connect(data_thread.deleteLater) data_thread.started.connect(self.g_populate_inst.local) data_thread.start() #.g_populate_inst.local() log_i('Populating DB from directory/archive') def scan_for_new_galleries(self): available_folders = app_constants.ENABLE_MONITOR and\ app_constants.MONITOR_PATHS and all(app_constants.MONITOR_PATHS) if available_folders and not app_constants.SCANNING_FOR_GALLERIES: app_constants.SCANNING_FOR_GALLERIES = True self.notification_bar.add_text("Scanning for new galleries...") log_i('Scanning for new galleries...') try: class ScanDir(QObject): final_paths_and_galleries = pyqtSignal(list, list) finished = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.scanned_data = [] def scan_dirs(self): paths = [] for p in app_constants.MONITOR_PATHS: if os.path.exists(p): dir_content = scandir.scandir(p) for d in dir_content: paths.append(d.path) else: log_e("Monitored path does not exists: {}".format(p.encode(errors='ignore'))) fetch_inst = fetch.Fetch(self) fetch_inst.series_path = paths def set_scanned_d(d): self.scanned_data = d fetch_inst.FINISHED.connect(set_scanned_d) fetch_inst.local() #contents = [] #for g in self.scanned_data: # contents.append(g) #paths = sorted(paths) #new_galleries = [] #for x in contents: # y = utils.b_search(paths, os.path.normcase(x.path)) # if not y: # new_galleries.append(x) galleries = [] final_paths = [] if self.scanned_data: for g in self.scanned_data: try: if g.is_archive: g.profile = utils.get_gallery_img(g.chapters[0].path, g.path) else: g.profile = utils.get_gallery_img(g.chapters[0].path) if not g.profile: raise Exception except: g.profile = app_constants.NO_IMAGE_PATH galleries.append(g) final_paths.append(g.path) self.final_paths_and_galleries.emit(final_paths, galleries) self.finished.emit() self.deleteLater() #if app_constants.LOOK_NEW_GALLERY_AUTOADD: # QTimer.singleShot(10000, self.gallery_populate(final_paths)) # return def show_new_galleries(final_paths, galleries): if final_paths and galleries: app_constants.OVERRIDE_MOVE_IMPORTED_IN_FETCH = True if app_constants.LOOK_NEW_GALLERY_AUTOADD: self.gallery_populate(final_paths) else: class NewGalleryMenu(QMenu): def __init__(self, parent=None): super().__init__(parent) ignore_act = self.addAction('Add to ignore list') ignore_act.triggered.connect(self.add_to_ignore) def add_to_ignore(self): gallery = self.gallery_widget.gallery app_constants.IGNORE_PATHS.append(gallery.path) settings.set(app_constants.IGNORE_PATHS, 'Application', 'ignore paths') if self.gallery_widget.parent_widget.gallery_layout.count() == 1: self.gallery_widget.parent_widget.close() else: self.gallery_widget.close() if len(galleries) == 1: self.notification_bar.add_text("{} new gallery was discovered in one of your monitored directories".format(len(galleries))) else: self.notification_bar.add_text("{} new galleries were discovered in one of your monitored directories".format(len(galleries))) text = "These new galleries were discovered! Do you want to add them?"\ if len(galleries) > 1 else "This new gallery was discovered! Do you want to add it?" g_popup = io_misc.GalleryPopup((text, galleries), self, NewGalleryMenu()) buttons = g_popup.add_buttons('Add', 'Close') def populate_n_close(): g_popup.close() self.gallery_populate(final_paths) buttons[0].clicked.connect(populate_n_close) buttons[1].clicked.connect(g_popup.close) def finished(): app_constants.SCANNING_FOR_GALLERIES = False thread = QThread(self) self.scan_inst = ScanDir() self.scan_inst.moveToThread(thread) self.scan_inst.final_paths_and_galleries.connect(show_new_galleries) self.scan_inst.finished.connect(finished) thread.started.connect(self.scan_inst.scan_dirs) #self.scan_inst.scan_dirs() thread.finished.connect(thread.deleteLater) thread.start() except: self.notification_bar.add_text('An error occured while attempting to scan for new galleries. Check happypanda.log.') log.exception('An error occured while attempting to scan for new galleries.') app_constants.SCANNING_FOR_GALLERIES = False def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() else: self.notification_bar.add_text('File is not supported') def dropEvent(self, event): acceptable = [] unaccept = [] for u in event.mimeData().urls(): path = u.toLocalFile() if os.path.isdir(path) or path.endswith(utils.ARCHIVE_FILES): acceptable.append(path) else: unaccept.append(path) log_i('Acceptable dropped items: {}'.format(len(acceptable))) log_i('Unacceptable dropped items: {}'.format(len(unaccept))) log_d('Dropped items: {}\n{}'.format(acceptable, unaccept).encode(errors='ignore')) if acceptable: self.notification_bar.add_text('Adding dropped items...') log_i('Adding dropped items') l = len(acceptable) == 1 f_item = acceptable[0] if f_item.endswith(utils.ARCHIVE_FILES): f_item = utils.check_archive(f_item) else: f_item = utils.recursive_gallery_check(f_item) f_item_l = len(f_item) < 2 subfolder_as_c = not app_constants.SUBFOLDER_AS_GALLERY if l and subfolder_as_c or l and f_item_l: g_d = gallerydialog.GalleryDialog(self, acceptable[0]) g_d.SERIES.connect(self.manga_list_view.gallery_model.addRows) g_d.show() else: self.gallery_populate(acceptable, True) else: text = 'File not supported' if len(unaccept) < 2 else 'Files not supported' self.notification_bar.add_text(text) if unaccept: self.notification_bar.add_text('Some unsupported files did not get added') def resizeEvent(self, event): try: self.notification_bar.resize(event.size().width()) except AttributeError: pass self.move_listener.emit() return super().resizeEvent(event) def moveEvent(self, event): self.move_listener.emit() return super().moveEvent(event) def showEvent(self, event): return super().showEvent(event) def cleanup_exit(self): self.system_tray.hide() # watchers try: self.watchers.stop_all() except AttributeError: pass # settings settings.set(self.manga_list_view.current_sort, 'General', 'current sort') settings.set(app_constants.IGNORE_PATHS, 'Application', 'ignore paths') settings.win_save(self, 'AppWindow') # temp dir try: for root, dirs, files in scandir.walk('temp', topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) log_d('Flush temp on exit: OK') except: log.exception('Flush temp on exit: FAIL') if self.tags_treeview: self.tags_treeview.close() self.download_window.close() # check if there is db activity if not gallerydb.method_queue.empty(): class DBActivityChecker(QObject): FINISHED = pyqtSignal() def __init__(self, **kwargs): super().__init__(**kwargs) def check(self): gallerydb.method_queue.join() self.FINISHED.emit() self.deleteLater() db_activity = DBActivityChecker() db_spinner = misc.Spinner(self) self.db_activity_checker.connect(db_activity.check) db_activity.moveToThread(app_constants.GENERAL_THREAD) db_activity.FINISHED.connect(db_spinner.close) db_spinner.set_size(50) db_spinner.set_text('Activity') db_spinner.move(QPoint(self.pos().x()+self.width()-70, self.pos().y()+self.height()-70)) self.move_listener.connect(lambda: db_spinner.update_move(QPoint(self.pos().x()+self.width()-70, self.pos().y()+self.height()-70))) db_spinner.show() self.db_activity_checker.emit() msg_box = QMessageBox(self) msg_box.setText('Database activity detected!') msg_box.setInformativeText("Closing now might result in data loss." + " Do you still want to close?\n(Wait for the activity spinner to hide before closing)") msg_box.setIcon(QMessageBox.Critical) msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box.setDefaultButton(QMessageBox.No) if msg_box.exec() == QMessageBox.Yes: return 1 else: return 2 else: return 0 def closeEvent(self, event): r_code = self.cleanup_exit() if r_code == 1: log_d('Force Exit App: OK') super().closeEvent(event) elif r_code == 2: log_d('Ignore Exit App') event.ignore() else: log_d('Normal Exit App: OK') super().closeEvent(event)
class InsertRecord(QWidget): """Main widget for inserting new records in the database This class creates a stacked layout containing four insert form widgets (Books, Authors, Publishers, Series) selected with a drop down menu. It implements the function to insert the record in the database. """ def __init__(self, *args, **kwargs): super(InsertRecord, self).__init__(*args, **kwargs) # Define main layout layout = QVBoxLayout() # Create menu with the database tables menu = QComboBox() menu.addItem('Books') menu.addItem('Authors') menu.addItem('Publishers') menu.addItem('Series') menu.currentIndexChanged[str].connect(self.change_table) layout.addWidget(menu) # Define stacked layout for the different tables in database self.layout_insert = QStackedLayout() # Create insert form for each table in database self.book_insert = InsertBookForm() self.author_insert = InsertAuthorForm() self.publisher_insert = InsertPublisherForm() self.series_insert = InsertSeriesForm() # Add search forms to layout self.layout_insert.addWidget(self.book_insert) self.layout_insert.addWidget(self.author_insert) self.layout_insert.addWidget(self.publisher_insert) self.layout_insert.addWidget(self.series_insert) layout.addLayout(self.layout_insert) # Define layout for buttons layout_button = QHBoxLayout() # Create buttons insert_button = QPushButton('Insert') clear_button = QPushButton('Clear') insert_button.setMinimumSize(400, 30) insert_button.setMaximumSize(500, 30) clear_button.setMinimumSize(400, 30) clear_button.setMaximumSize(500, 30) # Define buttons behavior insert_button.clicked.connect(self.insert_record) clear_button.clicked.connect(self.clear) # Add buttons to layout layout_button.addWidget(insert_button) layout_button.addWidget(clear_button) layout_button.addWidget(QWidget()) layout_button.addWidget(QWidget()) layout_button.setAlignment(insert_button, Qt.AlignLeft) layout_button.setAlignment(clear_button, Qt.AlignLeft) layout.addLayout(layout_button) # Set main layout self.setLayout(layout) # Function to clear all insert fields def clear(self): """Clear the text in the insert form.""" self.book_insert.isbn.clear() self.book_insert.title.clear() self.book_insert.author.clear() self.book_insert.otherauthors.clear() self.book_insert.publisher.clear() self.book_insert.series.clear() self.book_insert.subseries.clear() self.book_insert.category.clear() self.book_insert.language.clear() self.book_insert.year.clear() self.book_insert.pages.clear() self.book_insert.owner.setCurrentIndex(0) self.book_insert.booktype.setCurrentIndex(0) self.author_insert.name.clear() self.author_insert.gender.setCurrentIndex(0) self.author_insert.nationality.clear() self.author_insert.birthyear.clear() self.author_insert.deathyear.clear() self.publisher_insert.name.clear() self.series_insert.name.clear() self.series_insert.author.clear() # Function to set insert form according to database table selected def change_table(self, table_name): """Select the insert form (Books, Authors, Publishers or Series).""" # Clear all fields self.clear() # Books if table_name == 'Books': self.layout_insert.setCurrentIndex(0) # Authors elif table_name == 'Authors': self.layout_insert.setCurrentIndex(1) # Publishers elif table_name == 'Publishers': self.layout_insert.setCurrentIndex(2) # Series else: self.layout_insert.setCurrentIndex(3) # Function to insert record in database def insert_record(self): """Insert record in the database The method reads the informations in the active insert form (Books, Authors, Publishers or Series) and uses them to insert a new record in the database. """ sql_query = QSqlQuery() if self.layout_insert.currentIndex() == 0: # Get text from insert form isbn = QVariant(self.book_insert.isbn.text()) title = QVariant(self.book_insert.title.text()) author = QVariant(self.book_insert.author.text()) otherauthors = QVariant(self.book_insert.otherauthors.text()) publisher = QVariant(self.book_insert.publisher.text()) series = QVariant(self.book_insert.series.text()) subseries = QVariant(self.book_insert.subseries.text()) category = QVariant(self.book_insert.category.text()) language = QVariant(self.book_insert.language.text()) year = QVariant(self.book_insert.year.text()) pages = QVariant(self.book_insert.pages.text()) owner = QVariant(self.book_insert.owner.currentText()) booktype = QVariant(self.book_insert.booktype.currentText()) # Set values to None where strings are empty if isbn.value() == '': isbn = QVariant() else: isbn = isbn.value() # Check is the ISBN is valid if ISBN_CHECK: if '-' in isbn: isbn = canonical(isbn) if len(isbn) == 10: if not is_isbn10(isbn): # Show an error if the ISBN is invalid error = ErrorDialog( 'The ISBN inserted is invalid. Operation failed.' ) error.show() return elif len(isbn) == 13: if not is_isbn13(isbn): # Show an error if the ISBN is invalid error = ErrorDialog( 'The ISBN inserted is invalid. Operation failed.' ) error.show() return else: # Show an error if the ISBN is invalid error = ErrorDialog( 'The ISBN inserted is invalid. Operation failed.') error.show() return isbn = QVariant(isbn) if title.value() == '': title = QVariant() if author.value() == '': author = QVariant() if otherauthors.value() == '': otherauthors = QVariant() if publisher.value() == '': publisher = QVariant() if series.value() == '': series = QVariant() if subseries.value() == '': subseries = QVariant() if category.value() == '': category = QVariant() if language.value() == '': language = QVariant() if year.value() == '': year = QVariant() if pages.value() == '': pages = QVariant() if owner.value() == '': owner = QVariant() if booktype.value() == '': booktype = QVariant() # Get Author Id from Name if not author.isNull(): value = author.value().replace("'", "\\'") sql_query.prepare( f'SELECT Id FROM Authors WHERE Name LIKE \'%{value}%\'') if sql_query.exec_(): if sql_query.size() == 0: # Author cannot be NULL, show error error = ErrorDialog( 'Author not found. Operation failed') error.show() return if sql_query.size() == 1: sql_query.next() author = sql_query.value(0) else: # Show warning if string matches multiple authors warning = WarningDialog('String matches multiple authors. ' +\ 'Using exact match') warning.show() # Get Author Id from Name using exact match sql_query.prepare( f'SELECT Id FROM Authors WHERE Name=\'{author.value()}\'' ) if sql_query.exec_(): if sql_query.size() == 0: # Author cannot be NULL, show error error = ErrorDialog( 'Author not found. Operation failed') error.show() return if sql_query.size() == 1: sql_query.next() author = sql_query.value(0) else: #Show error if query failed error = ErrorDialog( str(sql_query.lastError().databaseText())) error.show() return else: #Show error if query failed error = ErrorDialog( str(sql_query.lastError().databaseText())) error.show() return # Get Publisher Id from Name if not publisher.isNull(): value = publisher.value().replace("'", "\\'") sql_query.prepare('SELECT Id FROM Publishers WHERE ' +\ f'Name LIKE \'%{value}%\'') if sql_query.exec_(): if sql_query.size() == 0: publisher = QVariant() # Show warning if string doesn't match any Publisher warning = WarningDialog( 'Publisher not found, set to \'NULL\'') warning.show() elif sql_query.size() == 1: sql_query.next() publisher = sql_query.value(0) else: # Show warning if string matches multiple publishers warning = WarningDialog('String matches multiple publishers.' +\ 'Using exact match') warning.show() # Get Publisher Id from Name using exact match sql_query.prepare('SELECT Id FROM Publishers WHERE ' +\ f'Name=\'{publisher.value()}\'') if sql_query.exec_(): if sql_query.size() == 0: publisher = QVariant() # Show warning if exact match is not found warning = WarningDialog( 'Publisher not found, set to \'NULL\'') warning.show() elif sql_query.size() == 1: sql_query.next() publisher = sql_query.value(0) else: #Show error if query failed error = ErrorDialog( str(sql_query.lastError().databaseText())) error.show() return else: #Show error if query failed error = ErrorDialog( str(sql_query.lastError().databaseText())) error.show() return # Get Series Id from Name if not series.isNull(): value = series.value().replace("'", "\\'") sql_query.prepare( f'SELECT Id FROM Series WHERE Name LIKE \'%{value}%\'') if sql_query.exec_(): if sql_query.size() == 0: series = QVariant() # Show warning if string doesn't match any Series warning = WarningDialog( 'Series not found, set to \'NULL\'') warning.show() elif sql_query.size() == 1: sql_query.next() series = sql_query.value(0) else: # Show warning is string matches multiple Series warning = WarningDialog( 'String matches multiple series. Using exact match' ) warning.show() # Get Series Id from Name using exact match sql_query.prepare( f'SELECT Id FROM Series WHERE Name=\'{series.value()}\'' ) if sql_query.exec_(): if sql_query.size() == 0: series = QVariant() # Show warning if exact match is not found warning = WarningDialog( 'Series not found, set to \'NULL\'') warning.show() elif sql_query.size() == 1: sql_query.next() series = sql_query.value(0) else: #Show error if query failed error = ErrorDialog( str(sql_query.lastError().databaseText())) error.show() return else: #Show error if query failed error = ErrorDialog( str(sql_query.lastError().databaseText())) error.show() return sql_query.prepare('INSERT INTO Books(ISBN, Title, Author, OtherAuthors, Publisher, ' +\ 'Series, Subseries, Category, Language, Year, Pages, Owner, Type) ' +\ 'Values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)') sql_query.bindValue(0, isbn) sql_query.bindValue(1, title) sql_query.bindValue(2, author) sql_query.bindValue(3, otherauthors) sql_query.bindValue(4, publisher) sql_query.bindValue(5, series) sql_query.bindValue(6, subseries) sql_query.bindValue(7, category) sql_query.bindValue(8, language) sql_query.bindValue(9, year) sql_query.bindValue(10, pages) sql_query.bindValue(11, owner) sql_query.bindValue(12, booktype) # Execute the query if sql_query.exec_(): # Show message if insertion succeeded info = InfoDialog('Record inserted successfully into Books') info.show() else: # Create error message box error = ErrorDialog(str(sql_query.lastError().databaseText())) error.show() elif self.layout_insert.currentIndex() == 1: # Get text from insert form name = QVariant(self.author_insert.name.text()) gender = QVariant(self.author_insert.gender.currentText()) nationality = QVariant(self.author_insert.nationality.text()) birthyear = QVariant(self.author_insert.birthyear.text()) deathyear = QVariant(self.author_insert.deathyear.text()) # Set values to None where strings are empty if name.value() == '': name = QVariant() if gender.value() == '': gender = QVariant() if nationality.value() == '': nationality = QVariant() if birthyear.value() == '': birthyear = QVariant() if deathyear.value() == '': deathyear = QVariant() sql_query.prepare('INSERT INTO Authors(' +\ 'Name, Gender, Nationality, BirthYear, DeathYear) ' +\ 'Values(?, ?, ?, ?, ?)') sql_query.bindValue(0, name) sql_query.bindValue(1, gender) sql_query.bindValue(2, nationality) sql_query.bindValue(3, birthyear) sql_query.bindValue(4, deathyear) # Execute the query if sql_query.exec_(): # Show message if insertion succeeded info = InfoDialog('Record inserted successfully into Authors') info.show() else: # Create error message box error = ErrorDialog(str(sql_query.lastError().databaseText())) error.show() elif self.layout_insert.currentIndex() == 2: # Get text from insert form name = QVariant(self.publisher_insert.name.text()) # Set value to None if string is empty if name.value() == '': name = QVariant() sql_query.prepare('INSERT INTO Publishers(Name) Values(?)') sql_query.bindValue(0, name) # Execute the query if sql_query.exec_(): # Show message if insertion succeeded info = InfoDialog( 'Record inserted successfully into Publishers') info.show() else: # Create error message box error = ErrorDialog(str(sql_query.lastError().databaseText())) error.show() elif self.layout_insert.currentIndex() == 3: # Get text from insert form name = QVariant(self.series_insert.name.text()) author = QVariant(self.series_insert.author.text()) # Set values to None where strings are empty if name.value() == '': name = QVariant() if author.value() == '': author = QVariant() if not author.isNull(): # Get Author Id from Name value = author.value().replace("'", "\\'") sql_query.prepare( f'SELECT Id FROM Authors WHERE Name LIKE \'%{value}%\'') if sql_query.exec_(): if sql_query.size() == 0: # Author cannot be NULL, show error error = ErrorDialog( 'Author not found. Operation failed') error.show() return if sql_query.size() == 1: sql_query.next() author = sql_query.value(0) else: # Show warning if string matches multiple authors warning = WarningDialog('String matches multiple authors. ' +\ 'Using exact match') warning.show() # Get Author Id from Name using exact match sql_query.prepare(f'SELECT Id FROM Authors WHERE ' +\ 'Name = \'{author.value()}\'') if sql_query.exec_(): if sql_query.size() == 0: # Author cannot be NULL, show error error = ErrorDialog( 'Author not found. Operation failed') error.show() return if sql_query.size() == 1: sql_query.next() author = sql_query.value(0) else: #Show error if query failed error = ErrorDialog( str(sql_query.lastError().databaseText())) error.show() else: #Show error if query failed error = ErrorDialog( str(sql_query.lastError().databaseText())) error.show() sql_query.prepare('INSERT INTO Series(Name, Author) Values(?, ?)') sql_query.bindValue(0, name) sql_query.bindValue(1, author) # Execute the query if sql_query.exec_(): # Show message if insertion succeeded info = InfoDialog('Record inserted successfully into Series') info.show() else: # Create error message box error = ErrorDialog(str(sql_query.lastError().databaseText())) error.show()
class Game(QWidget): """ Define the Window and all the elements that makes the graphic interface """ def __init__(self, h, w): QWidget.__init__(self) self.h = h self.w = w self.initUI() def initUI(self): # Set the Window self.setWindowTitle("The Game") self.pal = self.palette() self.pal.setColor(self.backgroundRole(), Qt.white) self.setPalette(self.pal) self.autoFillBackground = True # Set the QStackeLayout, where we put all the layouts self.layouts = QStackedLayout() self.counter = 0 # Count the number of layout add self.setLayout(self.layouts) # Define a button self.start = basic_widgets.Button("startover") self.start.clicked.connect(self.display_grid) # The table_layouts is usefull for the choice of layout to print self.table_layouts = [] # Set the first layout : the rules self.rules = rules.Show_rules() self.table_layouts.append(self.rules) self.layouts.addWidget(self.rules) self.counter += 1 self.rules.next.clicked.connect(self.layout_up) # Set the second layout : the choice of the map self.map_chosen = choose_map.Choose_map() self.table_layouts.append(self.map_chosen) self.layouts.addWidget(self.map_chosen) self.counter += 1 # Set the third layout : the choice of the number of players self.player_chosen = choose_player.Choose_player() self.table_layouts.append(self.player_chosen) self.layouts.addWidget(self.player_chosen) self.counter += 1 # Set the buttons for the selection of the map self.map_chosen.button[0].clicked.connect(self.map_choice_1) self.map_chosen.button[1].clicked.connect(self.map_choice_2) self.map_chosen.button[2].clicked.connect(self.map_choice_3) self.map_chosen.button[3].clicked.connect(self.map_choice_4) self.map_chosen.back.clicked.connect(self.go_back) # Set the buttons for the selection of the players self.player_chosen.button[0].clicked.connect(self.player_choice_1) self.player_chosen.button[1].clicked.connect(self.player_choice_2) self.player_chosen.button[2].clicked.connect(self.player_choice_3) self.player_chosen.button[3].clicked.connect(self.player_choice_4) self.player_chosen.back.clicked.connect(self.go_back) # We chose the size (w, h) for our Window self.resize(self.w, self.h) def go_back(self): """ Go back into the Layouts """ index = self.layouts.currentIndex() self.layouts.setCurrentWidget(self.table_layouts[index - 1]) # Functions that select the name of the map def map_choice_1(self): self.name = "TheForest" self.layout_up() def map_choice_2(self): self.name = "TheTunnel" self.layout_up() def map_choice_3(self): self.name = "ThePrison" self.layout_up() def map_choice_4(self): self.name = "PracticeField" self.layout_up() # Functions that select the number of player of the map def player_choice_1(self): self.nb = 1 self.display_grid() def player_choice_2(self): self.nb = 2 self.display_grid() def player_choice_3(self): self.nb = 3 self.display_grid() def player_choice_4(self): self.nb = 4 self.display_grid() def display_grid(self): """ Create the Layout where the Grid is printed and where you can play """ self.game = layout_game.Controls(self.name, self.nb, self.h, self.w) self.game.grid_button.addWidget(self.start, 3, 1) self.table_layouts.append(self.game) self.layouts.addWidget(self.game) self.counter += 1 self.layouts.setCurrentWidget(self.table_layouts[self.counter - 1]) for i in range(4): self.game.button_move[i].clicked.connect(self.end) def layout_up(self): """ Go forward into the Layouts """ index = self.layouts.currentIndex() self.layouts.setCurrentWidget(self.table_layouts[index + 1]) def end(self): """ If we reach the dooor, we need to print the final Layout """ if self.game.state == 2: self.the_end = the_end.The_End() self.table_layouts.append(self.the_end) self.layouts.addWidget(self.the_end) self.counter += 1 self.the_end.quit.clicked.connect(self.close) self.the_end.button.clicked.connect(self.start_over) self.layouts.setCurrentWidget(self.the_end) def start_over(self): """ If we want to play a new game """ self.layouts.setCurrentWidget(self.rules)
class ComboEditor(ui_tools.StyledBar): # Signals closeSplit = pyqtSignal('PyQt_PyObject') splitEditor = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject', bool) allFilesClosed = pyqtSignal() about_to_close_combo_editor = pyqtSignal() def __init__(self, original=False): super(ComboEditor, self).__init__(None) self.__original = original self.__undocked = [] self._symbols_index = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.bar = ActionBar(main_combo=original) vbox.addWidget(self.bar) # Info bar self.info_bar = InfoBar(self) self.info_bar.setVisible(False) vbox.addWidget(self.info_bar) self.stacked = QStackedLayout() vbox.addLayout(self.stacked) self._main_container = IDE.get_service('main_container') if not self.__original: self._main_container.fileOpened['QString'].connect( self._file_opened_by_main) # self.bar.combo.showComboSelector.connect( # lambda: self._main_container.change_tab()) self.bar.change_current['PyQt_PyObject', int].connect(self._set_current) self.bar.splitEditor[bool].connect(self.split_editor) self.bar.runFile['QString'].connect(self._run_file) self.bar.closeSplit.connect(lambda: self.closeSplit.emit(self)) self.bar.addToProject['QString'].connect(self._add_to_project) self.bar.showFileInExplorer['QString'].connect( self._show_file_in_explorer) self.bar.goToSymbol[int].connect(self._go_to_symbol) self.bar.undockEditor.connect(self.undock_editor) self.bar.reopenTab['QString'].connect( lambda path: self._main_container.open_file(path)) # self.connect(self.bar, SIGNAL("recentTabsModified()"), # lambda: self._main_container.recent_files_changed()) # self.connect(self.bar.code_navigator.btnPrevious, SIGNAL("clicked()"), # lambda: self._navigate_code(False)) # self.connect(self.bar.code_navigator.btnNext, SIGNAL("clicked()"), # lambda: self._navigate_code(True)) # def _navigate_code(self, val): # op = self.bar.code_navigator.operation # self._main_container.navigate_code_history(val, op) def currentWidget(self): return self.stacked.currentWidget() def setFocus(self): super(ComboEditor, self).setFocus() self.stacked.currentWidget().setFocus() self._editor_with_focus() def _file_opened_by_main(self, path): index = self.stacked.currentIndex() ninjaide = IDE.get_service('ide') editable = ninjaide.get_or_create_editable(path) self.add_editor(editable) self.bar.set_current_by_index(index) if index == -1: self.bar.set_current_by_index(0) def add_editor(self, neditable, keep_index=False): """Add Editor Widget to the UI area.""" if neditable.editor: if self.__original: editor = neditable.editor else: # editor = neditable.editor.clone() editor = self._main_container.create_editor_from_editable( neditable) current_index = self.stacked.currentIndex() new_index = self.stacked.addWidget(editor) self.stacked.setCurrentIndex(new_index) self.bar.add_item(neditable.display_name, neditable) # Bar is not visible because all the files have been closed, # so if a new file is opened, show the bar if not self.bar.isVisible(): self.bar.setVisible(True) if keep_index: self.bar.set_current_by_index(current_index) # Connections neditable.fileClosing.connect(self._close_file) editor.editorFocusObtained.connect(self._editor_with_focus) editor.modificationChanged.connect(self._editor_modified) neditable.checkersUpdated.connect(self._show_notification_icon) # Connect file system signals only in the original if self.__original: neditable.askForSaveFileClosing.connect(self._ask_for_save) neditable.fileChanged.connect(self._file_has_been_modified) # Editor Signals editor.cursor_position_changed[int, int].connect( self._update_cursor_position) editor.current_line_changed[int].connect(self._set_current_symbol) """ # self.connect(editor, SIGNAL("editorFocusObtained()"), # self._editor_with_focus) editor.editorFocusObtained.connect(self._editor_with_focus) neditable.fileSaved['PyQt_PyObject'].connect( self._update_combo_info) neditable.fileSaved['PyQt_PyObject'].connect( self._update_symbols) editor.modificationChanged[bool].connect(self._editor_modified) neditable.checkersUpdated.connect(self._show_notification_icon) # Connect file system signals only in the original neditable.fileClosing['PyQt_PyObject'].connect(self._close_file) if self.__original: neditable.askForSaveFileClosing['PyQt_PyObject'].connect( self._ask_for_save) neditable.fileChanged['PyQt_PyObject'].connect( self._file_has_been_modified) self.info_bar.reloadClicked.connect(neditable.reload_file) # Load Symbols self._load_symbols(neditable) """ def show_combo_file(self): self.bar.combo.showPopup() def show_combo_symbol(self): self.bar.symbols_combo.showPopup() def unlink_editors(self): for index in range(self.stacked.count()): widget = self.stacked.widget(index) # widget.setDocument(QsciDocument()) def split_editor(self, orientation_vertical): new_widget = ComboEditor() for neditable in self.bar.get_editables(): new_widget.add_editor(neditable) self.splitEditor.emit(self, new_widget, orientation_vertical) # for neditable in self.bar.get_editables(): # new_widget.add_editor(neditable) # self.splitEditor.emit(self, new_widget, orientationVertical) # self.emit(SIGNAL("splitEditor(PyQt_PyObject, PyQt_PyObject, bool)"), # self, new_widget, orientationVertical) def undock_editor(self): new_combo = ComboEditor() new_combo.setWindowTitle("NINJA-IDE") editor = self.currentWidget() new_combo.add_editor(editor.neditable) new_combo.show() """ for neditable in self.bar.get_editables(): new_combo.add_editor(neditable) neditor = self.currentWidget().clone() new_combo.set_current(neditor.neditable) new_combo.resize(700, 500) new_combo.about_to_close_combo_editor.connect(self._remove_undock) new_combo.show() """ def _remove_undock(self): widget = self.sender() self.__undocked.remove(widget) def close_current_file(self): self.bar.about_to_close_file() def _close_file(self, neditable): index = self.bar.close_file(neditable) print(index) layoutItem = self.stacked.takeAt(index) # neditable.editor.completer.cc.unload_module() self._add_to_last_opened(neditable.file_path) layoutItem.widget().deleteLater() if self.stacked.isEmpty(): self.bar.hide() self.allFilesClosed.emit() def _add_to_last_opened(self, path): if path not in settings.LAST_OPENED_FILES: settings.LAST_OPENED_FILES.append(path) if len(settings.LAST_OPENED_FILES) > settings.MAX_REMEMBER_TABS: self.__lastOpened = self.__lastOpened[1:] print("RecentTabsModified") # self.emit(SIGNAL("recentTabsModified()")) def _editor_with_focus(self): if self._main_container.current_widget is not self: self._main_container.current_widget = self editor = self.stacked.currentWidget() self._main_container.current_editor_changed( editor.neditable.file_path) self._load_symbols(editor.neditable) editor.neditable.update_checkers_display() def _ask_for_save(self, neditable): val = QMessageBox.No fileName = neditable.nfile.file_name val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.No: neditable.nfile.close(force_close=True) elif val == QMessageBox.Yes: neditable.ignore_checkers = True self._main_container.save_file(neditable.editor) neditable.nfile.close() def _file_has_been_modified(self, neditable): self.info_bar.show_message(translations.TR_FILE_MODIFIED_OUTSIDE) # val = QMessageBox.No # fileName = neditable.file_path # val = QMessageBox.question( # self, translations.TR_FILE_HAS_BEEN_MODIFIED, # "%s%s" % (fileName, translations.TR_FILE_MODIFIED_OUTSIDE), # QMessageBox.Yes | QMessageBox.No) # if val == QMessageBox.Yes: # neditable.reload_file() def _run_file(self, path): self._main_container.run_file(path) def _add_to_project(self, path): self._main_container._add_to_project(path) def _show_file_in_explorer(self, path): '''Connected to ActionBar's showFileInExplorer(QString) signal, forwards the file path on to the main container.''' self._main_container._show_file_in_explorer(path) def set_current(self, neditable): if neditable: self.bar.set_current_file(neditable) def _set_current(self, neditable, index): if neditable: self.stacked.setCurrentIndex(index) editor = self.stacked.currentWidget() self._update_cursor_position(ignore_sender=True) editor.setFocus() self._main_container.current_editor_changed(neditable.file_path) self._load_symbols(neditable) # self._show_file_in_explorer(neditable.file_path) neditable.update_checkers_display() def widget(self, index): return self.stacked.widget(index) def count(self): """Return the number of editors opened.""" return self.stacked.count() def _update_cursor_position(self, line=0, col=0, ignore_sender=False): obj = self.sender() editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: line += 1 self.bar.update_line_col(line, col) def _set_current_symbol(self, line, ignore_sender=False): obj = self.sender() editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: index = bisect.bisect(self._symbols_index, line) if (index >= len(self._symbols_index) or self._symbols_index[index] > (line + 1)): index -= 1 self.bar.set_current_symbol(index) def _editor_modified(self, value): obj = self.sender() neditable = obj.neditable if value: text = "\u2022 %s" % neditable.display_name self.bar.update_item_text(neditable, text) else: self.bar.update_item_text(neditable, neditable.display_name) def _go_to_symbol(self, index): # FIXME: index 0 invalid line = self._symbols_index[index] editor = self.stacked.currentWidget() editor.go_to_line(line) def _update_symbols(self, neditable): editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if editor == neditable.editor: self._load_symbols(neditable) def _update_combo_info(self, neditable): self.bar.update_item_text(neditable, neditable.display_name) self._main_container.current_editor_changed(neditable.file_path) def _load_symbols(self, neditable): symbols_handler = handlers.get_symbols_handler('py') source = neditable.editor.text source = source.encode(neditable.editor.encoding) symbols, symbols_simplified = symbols_handler.obtain_symbols( source, simple=True) self._symbols_index = sorted(symbols_simplified.keys()) symbols_simplified = sorted(list(symbols_simplified.items()), key=lambda x: x[0]) self.bar.add_symbols(symbols_simplified) line, _ = neditable.editor.cursor_position self._set_current_symbol(line, True) tree_symbols = IDE.get_service('symbols_explorer') tree_symbols.update_symbols_tree(symbols, neditable.file_path) def _show_notification_icon(self, neditable): checkers = neditable.sorted_checkers icon = QIcon() for items in checkers: checker, color, _ = items if checker.checks: if isinstance(checker.checker_icon, int): icon = self.style().standardIcon(checker.checker_icon) elif isinstance(checker.checker_icon, str): icon = QIcon(checker.checker_icon) # FIXME: sucks else: icon = QIcon(checker.checker_icon) break self.bar.update_item_icon(neditable, icon) def show_menu_navigation(self): self.bar.code_navigator.show_menu_navigation() def closeEvent(self, event): self.about_to_close_combo_editor.emit() # self.emit(SIGNAL("aboutToCloseComboEditor()")) super(ComboEditor, self).closeEvent(event) def reject(self): if not self.__original: super(ComboEditor, self).reject()
class ExpressionDialog(QDialog): def __init__(self, expr_pool): super().__init__() self.pool = expr_pool self.stack = QStackedLayout() self.stack.addWidget(self.build_first_page()) self.stack.addWidget(self.build_second_page()) self.stack.addWidget(self.build_third_page()) self.stack.addWidget(self.build_fourth_page()) self.stack.addWidget(self.build_fifth_page()) self.setLayout(self.stack) self.setWindowTitle('Add expression') def build_first_page(self): first_page = QWidget() expression_type_box = QGroupBox('Select expression type') self.simple_button = QRadioButton( 'Simple expression\nUse variables, operators, numbers and existing' 'expressions to create a new expression. Example: B+H+(V^2)/(2*9.81)' ) self.simple_button.setChecked(True) self.condition_button = QRadioButton( 'Conditional expression\nUse existing conditions and expressions' 'to create a conditional expression. ' 'Example: IF (B > 0) THEN (B) ELSE (0)') self.max_min_button = QRadioButton( 'Max/Min between two expressions\nUse two existing expressions and' 'MAX or MIN to create a new expression. Example: MAX(B, RB+0.5)') self.masked_button = QRadioButton( 'Masked expression\nUse an expression containing polygonal values ' 'and a non-polygonal expression\nto create a masked expression. ' 'Example: IF (POLY1) THEN (B+POLY1) ELSE (B)') self.condition_button.setEnabled( self.pool.ready_for_conditional_expression()) self.max_min_button.setEnabled( self.pool.ready_for_max_min_expression()) self.masked_button.setEnabled(self.pool.ready_for_masked_expression()) vlayout = QVBoxLayout() vlayout.addWidget(self.simple_button) vlayout.addWidget(self.condition_button) vlayout.addWidget(self.max_min_button) vlayout.addWidget(self.masked_button) expression_type_box.setLayout(vlayout) next_button = QPushButton('Next') cancel_button = QPushButton('Cancel') for bt in (next_button, cancel_button): bt.setMaximumWidth(200) bt.setFixedHeight(30) hlayout = QHBoxLayout() hlayout.addStretch() hlayout.addWidget(next_button) hlayout.addWidget(cancel_button) vlayout = QVBoxLayout() vlayout.addWidget(expression_type_box) vlayout.addStretch() vlayout.addLayout(hlayout, Qt.AlignRight) first_page.setLayout(vlayout) next_button.clicked.connect(self.turn_page) cancel_button.clicked.connect(self.reject) return first_page def build_second_page(self): second_page = QWidget() self.expression_text = QTextEdit() var_list = VariableList(self.pool, self.expression_text) ok_button = QPushButton('OK') cancel_button = QPushButton('Cancel') for bt in (ok_button, cancel_button): bt.setMaximumWidth(200) bt.setFixedHeight(30) hlayout = QHBoxLayout() vlayout = QVBoxLayout() lb = QLabel('Available variables and expressions') vlayout.addWidget(lb) vlayout.addWidget(var_list) vlayout.setAlignment(lb, Qt.AlignHCenter) hlayout.addLayout(vlayout) vlayout = QVBoxLayout() vlayout.addWidget( QLabel( '<p style="font-size:10pt">' '<b>Help</b>: double click on the list to add variables or existing expressions.<br>' 'You can also enter operators, parentheses and numbers.<br>' 'Supported operators: <tt>+ - * / ^ sqrt sin cos atan</tt>.</p>' )) vlayout.addItem(QSpacerItem(10, 10)) vlayout.addWidget(QLabel('Expression Editor')) vlayout.addWidget(self.expression_text) hlayout.addLayout(vlayout) hlayout.setSpacing(10) vlayout = QVBoxLayout() vlayout.addLayout(hlayout) hlayout = QHBoxLayout() hlayout.addStretch() hlayout.addWidget(ok_button) hlayout.addWidget(cancel_button) vlayout.addLayout(hlayout) second_page.setLayout(vlayout) ok_button.clicked.connect(self.check) cancel_button.clicked.connect(self.reject) return second_page def build_third_page(self): third_page = QWidget() if not self.condition_button.isEnabled(): return third_page self.condition_box = QComboBox() self.true_box = QComboBox() self.false_box = QComboBox() ok_button = QPushButton('OK') cancel_button = QPushButton('Cancel') for bt in (ok_button, cancel_button): bt.setMaximumWidth(200) bt.setFixedHeight(30) for box in (self.condition_box, self.true_box, self.false_box): box.setFixedHeight(30) box.setMaximumWidth(250) for i in range(1, self.pool.nb_expressions() + 1): expr = self.pool.expressions()[i] if expr.masked: continue self.true_box.addItem(str(expr)) self.false_box.addItem(str(expr)) for i in range(1, self.pool.nb_conditions() + 1): self.condition_box.addItem(str(self.pool.conditions()[i])) vlayout = QVBoxLayout() glayout = QGridLayout() glayout.addWidget(QLabel('Condition'), 1, 1, Qt.AlignHCenter) glayout.addWidget(QLabel('True'), 1, 2, Qt.AlignHCenter) glayout.addWidget(QLabel('False'), 1, 3, Qt.AlignHCenter) glayout.addWidget(self.condition_box, 2, 1) glayout.addWidget(self.true_box, 2, 2) glayout.addWidget(self.false_box, 2, 3) glayout.setVerticalSpacing(12) glayout.setRowStretch(0, 1) vlayout.addLayout(glayout) vlayout.addStretch() hlayout = QHBoxLayout() hlayout.addStretch() hlayout.addWidget(ok_button) hlayout.addWidget(cancel_button) vlayout.addLayout(hlayout) third_page.setLayout(vlayout) ok_button.clicked.connect(self.check) cancel_button.clicked.connect(self.reject) return third_page def build_fourth_page(self): fourth_page = QWidget() if not self.max_min_button.isEnabled(): return fourth_page self.max_min_box = QComboBox() self.max_min_box.addItem('MAX') self.max_min_box.addItem('MIN') self.first_box = QComboBox() self.second_box = QComboBox() ok_button = QPushButton('OK') cancel_button = QPushButton('Cancel') for bt in (ok_button, cancel_button): bt.setMaximumWidth(200) bt.setFixedHeight(30) for box in (self.first_box, self.second_box): box.setFixedHeight(30) box.setMaximumWidth(250) self.max_min_box.setFixedSize(100, 30) for i in range(1, self.pool.nb_expressions() + 1): expr = self.pool.expressions()[i] if expr.masked: continue self.first_box.addItem(str(expr)) self.second_box.addItem(str(expr)) vlayout = QVBoxLayout() glayout = QGridLayout() glayout.addWidget(QLabel('Condition'), 1, 1, Qt.AlignHCenter) glayout.addWidget(QLabel('True'), 1, 2, Qt.AlignHCenter) glayout.addWidget(QLabel('False'), 1, 3, Qt.AlignHCenter) glayout.addWidget(self.max_min_box, 2, 1) glayout.addWidget(self.first_box, 2, 2) glayout.addWidget(self.second_box, 2, 3) glayout.setVerticalSpacing(12) glayout.setRowStretch(0, 1) vlayout.addLayout(glayout) vlayout.addStretch() hlayout = QHBoxLayout() hlayout.addStretch() hlayout.addWidget(ok_button) hlayout.addWidget(cancel_button) vlayout.addLayout(hlayout) fourth_page.setLayout(vlayout) ok_button.clicked.connect(self.check) cancel_button.clicked.connect(self.reject) return fourth_page def build_fifth_page(self): fifth_page = QWidget() if not self.masked_button.isEnabled(): return fifth_page self.poly_box = QComboBox() self.inside_box = QComboBox() self.outside_box = QComboBox() ok_button = QPushButton('OK') cancel_button = QPushButton('Cancel') for bt in (ok_button, cancel_button): bt.setMaximumWidth(200) bt.setFixedHeight(30) for box in (self.poly_box, self.inside_box, self.outside_box): box.setFixedHeight(30) box.setMaximumWidth(250) for i in range(1, self.pool.nb_masks() + 1): mask = self.pool.masks()[i] if mask.nb_children > 0: self.poly_box.addItem(mask.code()) for i in range(1, self.pool.nb_expressions() + 1): expr = self.pool.expressions()[i] if not expr.polygonal: self.outside_box.addItem(str(expr)) self.update_inside_mask(self.poly_box.currentText()) self.poly_box.currentTextChanged.connect(self.update_inside_mask) vlayout = QVBoxLayout() glayout = QGridLayout() glayout.addWidget(QLabel('Mask'), 1, 1, Qt.AlignHCenter) glayout.addWidget(QLabel('Inside'), 1, 2, Qt.AlignHCenter) glayout.addWidget(QLabel('Outside'), 1, 3, Qt.AlignHCenter) glayout.addWidget(self.poly_box, 2, 1) glayout.addWidget(self.inside_box, 2, 2) glayout.addWidget(self.outside_box, 2, 3) glayout.setVerticalSpacing(12) glayout.setRowStretch(0, 1) vlayout.addLayout(glayout) vlayout.addStretch() hlayout = QHBoxLayout() hlayout.addStretch() hlayout.addWidget(ok_button) hlayout.addWidget(cancel_button) vlayout.addLayout(hlayout) fifth_page.setLayout(vlayout) ok_button.clicked.connect(self.check) cancel_button.clicked.connect(self.reject) return fifth_page def update_inside_mask(self, current_mask): mask = self.pool.get_mask(current_mask) self.inside_box.clear() for child_code in mask.children: expr = self.pool.get_expression(child_code) self.inside_box.addItem(str(expr)) def turn_page(self): if self.simple_button.isChecked(): self.stack.setCurrentIndex(1) elif self.condition_button.isChecked(): self.stack.setCurrentIndex(2) elif self.max_min_button.isChecked(): self.stack.setCurrentIndex(3) else: self.stack.setCurrentIndex(4) def polygonal_success_message(self): QMessageBox.information( None, 'Polygonal expression created', 'You just created an expression containing polygon values.\n' 'To use it, click "Add Expression" then choose "Masked expression"\n' '(You will also need at least one non-polygonal expression).', QMessageBox.Ok) def polygonal_fail_message(self): QMessageBox.critical( None, 'Error', 'One expression can only use only one polygonal mask!', QMessageBox.Ok) def check(self): current_page = self.stack.currentIndex() if current_page == 1: literal_expression = self.expression_text.toPlainText() success_code = self.pool.add_simple_expression(literal_expression) if success_code == -1: QMessageBox.critical(None, 'Error', 'Invalid expression.', QMessageBox.Ok) return elif success_code == -2: self.polygonal_fail_message() return elif success_code == 1: self.polygonal_success_message() elif current_page == 2: str_true, str_false = self.true_box.currentText( ), self.false_box.currentText() if str_true == str_false: QMessageBox.critical( None, 'Error', 'The True/False expressions cannot be identical!', QMessageBox.Ok) return str_cond = self.condition_box.currentText() success_code = self.pool.add_conditional_expression( self.pool.get_condition(str_cond), self.pool.get_expression(str_true), self.pool.get_expression(str_false)) if success_code == -2: self.polygonal_fail_message() return elif success_code == 1: self.polygonal_success_message() elif current_page == 3: is_max = self.max_min_box.currentText() == 'MAX' str_first, str_second = self.first_box.currentText( ), self.second_box.currentText() if str_first == str_second: QMessageBox.critical( None, 'Error', 'The two expressions cannot be identical!', QMessageBox.Ok) return success_code = self.pool.add_max_min_expression( self.pool.get_expression(str_first), self.pool.get_expression(str_second), is_max) if success_code == -2: self.polygonal_fail_message() return elif success_code == 1: self.polygonal_success_message() else: str_inside, str_outside = self.inside_box.currentText( ), self.outside_box.currentText() self.pool.add_masked_expression( self.pool.get_expression(str_inside), self.pool.get_expression(str_outside)) self.accept()
class Tex0WidgetGroup(QWidget): def __init__(self, parent, tex0s=None, max_rows=0, max_columns=4, brres=None): super().__init__(parent) main_layout = QVBoxLayout(self) self.stack = QStackedLayout(self) self.stack_widget = QWidget(self) self.map_box = QComboBox() self.map_box.activated.connect(self.on_map_change) self.__init_context_menu() main_layout.addWidget(self.map_box) self.stack_widget.setLayout(self.stack) main_layout.addWidget(self.stack_widget) self.subscriber = parent if tex0s is not None: self.set_tex0s(tex0s) if brres is None: self.brres = tex0s[0].parent if brres: self.brres = brres self.setLayout(main_layout) def __init_context_menu(self): self.setContextMenuPolicy(Qt.ActionsContextMenu) create_action = QAction('&Add Map', self) create_action.setToolTip('Add new map') create_action.triggered.connect(self.create_map) self.addAction(create_action) replace_action = QAction('&Replace', self) replace_action.setToolTip('Replace map') replace_action.triggered.connect(self.replace_map) self.addAction(replace_action) export_action = QAction('&Export', self) export_action.setToolTip('Export as png') export_action.triggered.connect(self.export) self.addAction(export_action) remove_action = QAction('&Delete', self) remove_action.setToolTip('Remove the map') remove_action.triggered.connect(self.remove) self.addAction(remove_action) def export(self): self.stack.currentWidget().export() def remove(self): self.remove_map_widget(self.stack.currentWidget()) def replace_map(self): self.stack.currentWidget().replace_map() def get_tex0(self, index): return self.stack.itemAt(index).tex0 def on_map_replace(self, tex): if self.subscriber is not None: self.subscriber.on_map_replace(tex, self.stack.currentIndex()) def on_map_change(self, index): self.stack.setCurrentIndex(index) if self.subscriber is not None: self.subscriber.on_map_change(self.stack.currentWidget().tex0, index) def reset(self): for i in reversed(range(self.stack.count())): widget = self.stack.itemAt(i).widget() widget.del_widget() self.map_box.removeItem(i) def set_brres(self, brres): self.brres = brres def set_tex0s(self, tex0s): self.reset() for x in tex0s: self.add_tex0(x) def add_tex0(self, x): widget = MapWidget(self, x) self.add_map_widget(widget) def add_map_widget(self, map_widget): self.stack.addWidget(map_widget) self.map_box.addItem(map_widget.name) def remove_map_widget(self, map_widget): tex0 = map_widget.tex0 index = self.stack.currentIndex() if self.subscriber is not None: self.subscriber.on_map_remove(tex0, index) def create_map(self): self.importer = MapImporter(self, self.brres) def on_import(self, tex0): index = self.stack.count() if self.subscriber: self.subscriber.on_map_add(tex0, index) self.importer = None
class ConditionDialog(QDialog): def __init__(self, expr_pool): super().__init__() self.expr_pool = expr_pool self.stack = QStackedLayout() self.stack.addWidget(self.build_first_page()) self.stack.addWidget(self.build_second_page()) self.stack.addWidget(self.build_third_page()) self.setLayout(self.stack) self.setWindowTitle('Add condition') def build_first_page(self): first_page = QWidget() expression_type_box = QGroupBox('Select condition type') self.simple_button = QRadioButton( 'Simple condition\nUse an expression, a comparator and a threshold value' 'to create a new condition. Example: B > 0') self.simple_button.setChecked(True) self.and_or_button = QRadioButton( 'AND/OR condition\nUse two existing conditions and AND/OR operators' 'to create a new condition. Example: (B > 0) AND (B < 100)') self.and_or_button.setEnabled(self.expr_pool.nb_conditions() > 1) vlayout = QVBoxLayout() vlayout.addWidget(self.simple_button) vlayout.addWidget(self.and_or_button) expression_type_box.setLayout(vlayout) next_button = QPushButton('Next') cancel_button = QPushButton('Cancel') for bt in (next_button, cancel_button): bt.setMaximumWidth(200) bt.setFixedHeight(30) hlayout = QHBoxLayout() hlayout.addStretch() hlayout.addWidget(next_button) hlayout.addWidget(cancel_button) vlayout = QVBoxLayout() vlayout.addWidget(expression_type_box) vlayout.addStretch() vlayout.addLayout(hlayout, Qt.AlignRight) first_page.setLayout(vlayout) next_button.clicked.connect(self.turn_page) cancel_button.clicked.connect(self.reject) return first_page def build_second_page(self): second_page = QWidget() buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) buttons.accepted.connect(self.check) buttons.rejected.connect(self.reject) self.expression_box = QComboBox() self.expression_box.setFixedHeight(30) self.expression_box.setMinimumWidth(150) self.expression_box.setMaximumWidth(250) for i in range(1, self.expr_pool.nb_expressions() + 1): expr = self.expr_pool.expressions()[i] if expr.masked: continue self.expression_box.addItem(str(expr)) self.comparator_box = QComboBox() for comparator in ['>', '<', '>=', '<=']: self.comparator_box.addItem(comparator) self.comparator_box.setFixedSize(50, 30) self.threshold_box = QLineEdit() self.threshold_box.setFixedSize(150, 30) vlayout = QVBoxLayout() glayout = QGridLayout() glayout.addWidget(QLabel('Expression'), 1, 1, Qt.AlignHCenter) glayout.addWidget(QLabel('Comparator'), 1, 2, Qt.AlignHCenter) glayout.addWidget(QLabel('Threshold'), 1, 3, Qt.AlignHCenter) glayout.addWidget(self.expression_box, 2, 1) glayout.addWidget(self.comparator_box, 2, 2) glayout.addWidget(self.threshold_box, 2, 3) glayout.setVerticalSpacing(12) glayout.setRowStretch(0, 1) vlayout.addLayout(glayout) vlayout.addStretch() vlayout.addWidget(buttons) second_page.setLayout(vlayout) return second_page def build_third_page(self): third_page = QWidget() if not self.and_or_button.isEnabled(): return third_page self.and_or_box = QComboBox() self.and_or_box.addItem('AND') self.and_or_box.addItem('OR') self.first_box = QComboBox() self.second_box = QComboBox() ok_button = QPushButton('OK') cancel_button = QPushButton('Cancel') for bt in (ok_button, cancel_button): bt.setMaximumWidth(200) bt.setFixedHeight(30) for box in (self.first_box, self.second_box): box.setFixedHeight(30) box.setMaximumWidth(250) self.and_or_box.setFixedSize(100, 30) for i in range(1, self.expr_pool.nb_conditions() + 1): condition = str(self.expr_pool.conditions()[i]) self.first_box.addItem(condition) self.second_box.addItem(condition) vlayout = QVBoxLayout() glayout = QGridLayout() glayout.addWidget(QLabel('Condition 1'), 1, 1, Qt.AlignHCenter) glayout.addWidget(QLabel('Operator'), 1, 2, Qt.AlignHCenter) glayout.addWidget(QLabel('Condition 2'), 1, 3, Qt.AlignHCenter) glayout.addWidget(self.first_box, 2, 1) glayout.addWidget(self.and_or_box, 2, 2) glayout.addWidget(self.second_box, 2, 3) glayout.setVerticalSpacing(12) glayout.setRowStretch(0, 1) vlayout.addLayout(glayout) vlayout.addStretch() hlayout = QHBoxLayout() hlayout.addStretch() hlayout.addWidget(ok_button) hlayout.addWidget(cancel_button) vlayout.addLayout(hlayout) third_page.setLayout(vlayout) ok_button.clicked.connect(self.check) cancel_button.clicked.connect(self.reject) return third_page def turn_page(self): if self.simple_button.isChecked(): self.stack.setCurrentIndex(1) else: self.stack.setCurrentIndex(2) def check(self): current_page = self.stack.currentIndex() if current_page == 1: threshold = self.threshold_box.text() try: threshold = float(threshold) except ValueError: QMessageBox.critical(None, 'Error', 'The threshold is not a number!', QMessageBox.Ok) return expr_text = self.expression_box.currentText() self.expr_pool.add_condition( self.expr_pool.get_expression(expr_text), self.comparator_box.currentText(), threshold) else: is_and = self.and_or_box.currentText() == 'AND' first_text, second_text = self.first_box.currentText( ), self.second_box.currentText() if first_text == second_text: QMessageBox.critical( None, 'Error', 'The two conditions cannot be identical!', QMessageBox.Ok) return success_code = self.expr_pool.add_and_or_condition( self.expr_pool.get_condition(first_text), self.expr_pool.get_condition(second_text), is_and) if success_code == -2: QMessageBox.critical( None, 'Error', 'One condition can only use only one polygonal mask!', QMessageBox.Ok) return self.accept()
class MainView(QLabel): DirPath = 'Images' def __init__(self, *args, **kwargs): super(MainView, self).__init__(*args, **kwargs) self.setScaledContents(True) rect = QApplication.instance().desktop().availableGeometry() # 桌面大小的2/3 self.resize(int(rect.width() * 2 / 3), int(rect.height() * 2 / 3)) # 布局 layout = QHBoxLayout(self, spacing=0) layout.setContentsMargins(0, 0, 0, 0) Signals.setParent(self) NetWork.setParent(self) # 获取上一页图片的按钮 layout.addWidget( QPushButton(self, objectName='previousButton', clicked=self.onPreviousPage)) # 故事控件 layout.addWidget(StoryView(self)) # 层叠布局 self.stackLayout = QStackedLayout(spacing=0) self.stackLayout.setContentsMargins(0, 0, 0, 0) layout.addLayout(self.stackLayout) # 获取下一页图片的按钮 layout.addWidget( QPushButton(self, objectName='nextButton', clicked=self.onNextPage)) # 当前日期的图片被下载 Signals.currentImageAdded.connect(self.loadCurrentImage) # 单个item鼠标悬停离开信号 Signals.imageHovered.connect(self.onImageHovered) def closeEvent(self, event): """关闭窗口时保存窗口的截图文件""" os.makedirs(self.DirPath, exist_ok=True) self.grab().save(os.path.join(self.DirPath, 'tmp.jpg')) super(MainView, self).closeEvent(event) def onImageHovered(self, hovered, image): self.setPixmap(QPixmap.fromImage(image) if hovered else self.oldImage) def onPreviousPage(self): """上一页""" index = self.stackLayout.currentIndex() if index > 0: index -= 1 self.stackLayout.setCurrentIndex(index) def onNextPage(self): """下一页""" index = self.stackLayout.currentIndex() count = self.stackLayout.count() - 1 if index < count: index += 1 self.stackLayout.setCurrentIndex(index) def splist(self, src, length): # 等分列表 return (src[i:i + length] for i in range(len(src)) if i % length == 0) def addImages(self, _, datas): """解析json数据并生成层叠2x2的网格页面""" try: imagesGroup = self.splist( json.loads(datas.decode()).get('images', []), 4) # 每组4个 except Exception as e: print(e) imagesGroup = [] for images in imagesGroup: pageView = PageView(self) pageView.addImages(images) self.stackLayout.addWidget(pageView) # 设置当前索引 self.stackLayout.setCurrentIndex(0) def loadCurrentImage(self, path): """加载当前日期的图片作为背景""" self.oldImage = QPixmap(path) self.setPixmap(self.oldImage) # 延迟1秒后显示 QTimer.singleShot(1000, self.onShow) def onShow(self): self.setCursor(Qt.ArrowCursor) self.setVisible(True) Signals.splashClosed.emit(self) # 通知关闭启动界面 def initData(self): """加载api接口数据""" # 获取最近几天的 url = 'https://cn.bing.com/HPImageArchive.aspx?format=js&idx=-1&n=8' NetWork.get(self.createRequest(url, self.addImages)) # 获取之前的 url = 'https://cn.bing.com/HPImageArchive.aspx?format=js&idx=7&n=8' NetWork.get(self.createRequest(url, self.addImages)) # 获取每日故事 url = 'http://cn.bing.com/cnhp/coverstory/' NetWork.get(self.createRequest(url, Signals.storyAdded.emit)) def createRequest(self, url, callback): """创建网络请求""" req = QNetworkRequest(QUrl(url)) # 回调函数用于加载图片显示 req.setAttribute(QNetworkRequest.User + 3, callback) return req
class AppWindow(QMainWindow): "The application's main window" def __init__(self): super().__init__() self.initUI() self.start_up() QTimer.singleShot(3000, self._check_update) def init_watchers(self): def remove_gallery(g): index = self.manga_list_view.find_index(g.id) if index: self.manga_list_view.remove_gallery([index]) def create_gallery(path): g_dia = gallerydialog.GalleryDialog(self, path) g_dia.SERIES.connect(self.manga_list_view.gallery_model.addRows) g_dia.show() def update_gallery(g): index = self.manga_list_view.find_index(g.id) if index: self.manga_list_view.replace_edit_gallery([g], index.row()) else: log_e("Could not find gallery to update from Watcher") def created(path): c_popup = file_misc.CreatedPopup(path, self) c_popup.ADD_SIGNAL.connect(create_gallery) def modified(path, gallery): mod_popup = file_misc.ModifiedPopup(path, gallery, self) def deleted(path, gallery): d_popup = file_misc.DeletedPopup(path, gallery, self) d_popup.UPDATE_SIGNAL.connect(update_gallery) d_popup.REMOVE_SIGNAL.connect(remove_gallery) def moved(new_path, gallery): mov_popup = file_misc.MovedPopup(new_path, gallery, self) mov_popup.UPDATE_SIGNAL.connect(update_gallery) self.watchers = file_misc.Watchers() self.watchers.gallery_handler.CREATE_SIGNAL.connect(created) self.watchers.gallery_handler.MODIFIED_SIGNAL.connect(modified) self.watchers.gallery_handler.MOVED_SIGNAL.connect(moved) self.watchers.gallery_handler.DELETED_SIGNAL.connect(deleted) if gui_constants.LOOK_NEW_GALLERY_STARTUP: self.notification_bar.add_text("Looking for new galleries...") try: class ScanDir(QObject): final_paths_and_galleries = pyqtSignal(list, list) def __init__(self, model_data, parent=None): super().__init__(parent) self.model_data = model_data def scan_dirs(self): db_data = self.model_data paths = [] for g in range(len(db_data)): paths.append(os.path.normcase(db_data[g].path)) contents = [] case_path = [] # needed for tile and artist parsing... e.g to avoid lowercase for m_path in gui_constants.MONITOR_PATHS: for p in os.listdir(m_path): abs_p = os.path.join(m_path, p) if os.path.isdir(abs_p) or p.endswith(utils.ARCHIVE_FILES): case_path.append(abs_p) contents.append(os.path.normcase(abs_p)) paths = sorted(paths) new_galleries = [] for c, x in enumerate(contents): y = utils.b_search(paths, x) if not y: # (path, number for case_path) new_galleries.append((x, c)) galleries = [] final_paths = [] if new_galleries: for g in new_galleries: gallery = gallerydb.Gallery() try: gallery.profile = utils.get_gallery_img(g[0]) except: gallery.profile = gui_constants.NO_IMAGE_PATH parser_dict = utils.title_parser(os.path.split(case_path[g[1]])[1]) gallery.title = parser_dict["title"] gallery.artist = parser_dict["artist"] galleries.append(gallery) final_paths.append(case_path[g[1]]) self.final_paths_and_galleries.emit(final_paths, galleries) # if gui_constants.LOOK_NEW_GALLERY_AUTOADD: # QTimer.singleShot(10000, self.gallery_populate(final_paths)) # return def show_new_galleries(final_paths, galleries): if final_paths and galleries: if gui_constants.LOOK_NEW_GALLERY_AUTOADD: self.gallery_populate(final_paths) else: if len(galleries) == 1: self.notification_bar.add_text( "{} new gallery was discovered in one of your monitored directories".format( len(galleries) ) ) else: self.notification_bar.add_text( "{} new galleries were discovered in one of your monitored directories".format( len(galleries) ) ) text = ( "These new galleries were discovered! Do you want to add them?" if len(galleries) > 1 else "This new gallery was discovered! Do you want to add it?" ) g_popup = file_misc.GalleryPopup((text, galleries), self) buttons = g_popup.add_buttons("Add", "Close") def populate_n_close(): self.gallery_populate(final_paths) g_popup.close() buttons[0].clicked.connect(populate_n_close) buttons[1].clicked.connect(g_popup.close) thread = QThread(self) self.scan_inst = ScanDir(self.manga_list_view.gallery_model._data) self.scan_inst.moveToThread(thread) self.scan_inst.final_paths_and_galleries.connect(show_new_galleries) self.scan_inst.final_paths_and_galleries.connect(lambda a: self.scan_inst.deleteLater()) thread.started.connect(self.scan_inst.scan_dirs) thread.finished.connect(thread.deleteLater) thread.start() except: self.notification_bar.add_text( "An error occured while attempting to scan for new galleries. Check happypanda.log." ) log.exception("An error occured while attempting to scan for new galleries.") def start_up(self): def normalize_first_time(): settings.set(2, "Application", "first time level") def done(): self.manga_list_view.gallery_model.init_data() if gui_constants.ENABLE_MONITOR and gui_constants.MONITOR_PATHS and all(gui_constants.MONITOR_PATHS): self.init_watchers() if gui_constants.FIRST_TIME_LEVEL != 2: normalize_first_time() if gui_constants.FIRST_TIME_LEVEL < 2: class FirstTime(file_misc.BasePopup): def __init__(self, parent=None): super().__init__(parent) main_layout = QVBoxLayout() info_lbl = QLabel( "Hi there! Some big changes are about to occur!\n" + "Please wait.. This will take at most a few minutes.\n" + "If not then try restarting the application." ) info_lbl.setAlignment(Qt.AlignCenter) main_layout.addWidget(info_lbl) prog = QProgressBar(self) prog.setMinimum(0) prog.setMaximum(0) prog.setTextVisible(False) main_layout.addWidget(prog) main_layout.addWidget(QLabel("Note: This popup will close itself when everything is ready")) self.main_widget.setLayout(main_layout) ft_widget = FirstTime(self) log_i("Invoking first time level 2") bridge = gallerydb.Bridge() thread = QThread(self) thread.setObjectName("Startup") bridge.moveToThread(thread) thread.started.connect(bridge.rebuild_galleries) bridge.DONE.connect(ft_widget.close) bridge.DONE.connect(self.setEnabled) bridge.DONE.connect(done) bridge.DONE.connect(bridge.deleteLater) thread.finished.connect(thread.deleteLater) thread.start() ft_widget.adjustSize() ft_widget.show() self.setEnabled(False) else: done() def initUI(self): self.center = QWidget() self.display = QStackedLayout() self.center.setLayout(self.display) # init the manga view variables self.manga_display() log_d("Create manga display: OK") # init the chapter view variables # self.chapter_display() self.m_l_view_index = self.display.addWidget(self.manga_list_main) self.m_t_view_index = self.display.addWidget(self.manga_table_view) # init toolbar self.init_toolbar() log_d("Create toolbar: OK") # init status bar self.init_stat_bar() log_d("Create statusbar: OK") self.system_tray = misc.SystemTray(QIcon(gui_constants.APP_ICO_PATH), self) gui_constants.SYSTEM_TRAY = self.system_tray tray_menu = QMenu(self) self.system_tray.setContextMenu(tray_menu) self.system_tray.setToolTip("Happypanda {}".format(gui_constants.vs)) tray_quit = QAction("Quit", tray_menu) tray_menu.addAction(tray_quit) tray_quit.triggered.connect(self.close) self.system_tray.show() log_d("Create system tray: OK") # self.display.addWidget(self.chapter_main) self.setCentralWidget(self.center) self.setWindowTitle("Happypanda") self.setWindowIcon(QIcon(gui_constants.APP_ICO_PATH)) props = settings.win_read(self, "AppWindow") if props.resize: x, y = props.resize self.resize(x, y) else: self.resize(gui_constants.MAIN_W, gui_constants.MAIN_H) posx, posy = props.pos self.move(posx, posy) self.show() log_d("Show window: OK") self.notification_bar = misc.NotificationOverlay(self) p = self.toolbar.pos() self.notification_bar.move(p.x(), p.y() + self.toolbar.height()) self.notification_bar.resize(self.width()) gui_constants.NOTIF_BAR = self.notification_bar log_d("Create notificationbar: OK") log_d("Window Create: OK") def _check_update(self): class upd_chk(QObject): UPDATE_CHECK = pyqtSignal(str) def __init__(self, **kwargs): super().__init__(**kwargs) def fetch_vs(self): import requests import time try: log_d("Checking Update") time.sleep(1.5) r = requests.get( "https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt", verify="cacert.pem" ) a = r.text vs = a.strip() self.UPDATE_CHECK.emit(vs) except: log.exception("Checking Update: FAIL") self.UPDATE_CHECK.emit("this is a very long text which is is sure to be over limit") def check_update(vs): log_i("Received version: {}\nCurrent version: {}".format(vs, gui_constants.vs)) if vs != gui_constants.vs: if len(vs) < 10: self.notification_bar.add_text( "Version {} of Happypanda is".format(vs) + " available. Click here to update!", False ) self.notification_bar.clicked.connect( lambda: utils.open_web_link("https://github.com/Pewpews/happypanda/releases") ) self.notification_bar.set_clickable(True) else: self.notification_bar.add_text("An error occurred while checking for new version") self.update_instance = upd_chk() thread = QThread(self) self.update_instance.moveToThread(thread) thread.started.connect(self.update_instance.fetch_vs) self.update_instance.UPDATE_CHECK.connect(check_update) self.update_instance.UPDATE_CHECK.connect(self.update_instance.deleteLater) thread.finished.connect(thread.deleteLater) thread.start() def _web_metadata_picker(self, gallery, title_url_list, queue, parent=None): if not parent: parent = self text = "Which gallery do you want to extract metadata from?" s_gallery_popup = misc.SingleGalleryChoices(gallery, title_url_list, text, parent) s_gallery_popup.USER_CHOICE.connect(queue.put) def get_metadata(self, gal=None): thread = QThread(self) thread.setObjectName("App.get_metadata") fetch_instance = fetch.Fetch() if gal: galleries = [gal] else: if gui_constants.CONTINUE_AUTO_METADATA_FETCHER: galleries = [g for g in self.manga_list_view.gallery_model._data if not g.exed] else: galleries = self.manga_list_view.gallery_model._data if not galleries: self.notification_bar.add_text("Looks like we've already gone through all galleries!") return None fetch_instance.galleries = galleries self.notification_bar.begin_show() fetch_instance.moveToThread(thread) def done(status): self.notification_bar.end_show() fetch_instance.deleteLater() fetch_instance.GALLERY_PICKER.connect(self._web_metadata_picker) fetch_instance.GALLERY_EMITTER.connect(self.manga_list_view.replace_edit_gallery) fetch_instance.AUTO_METADATA_PROGRESS.connect(self.notification_bar.add_text) thread.started.connect(fetch_instance.auto_web_metadata) fetch_instance.FINISHED.connect(done) thread.finished.connect(thread.deleteLater) thread.start() # def style_tooltip(self): # palette = QToolTip.palette() # palette.setColor() def init_stat_bar(self): self.status_bar = self.statusBar() self.status_bar.setMaximumHeight(20) self.status_bar.setSizeGripEnabled(False) self.stat_info = QLabel() self.stat_info.setIndent(5) self.sort_main = QAction("Asc", self) sort_menu = QMenu() self.sort_main.setMenu(sort_menu) s_by_title = QAction("Title", sort_menu) s_by_artist = QAction("Artist", sort_menu) sort_menu.addAction(s_by_title) sort_menu.addAction(s_by_artist) self.status_bar.addPermanentWidget(self.stat_info) # self.status_bar.addAction(self.sort_main) self.temp_msg = QLabel() self.temp_timer = QTimer() self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.connect(self.stat_row_info) self.manga_list_view.gallery_model.STATUSBAR_MSG.connect(self.stat_temp_msg) self.manga_list_view.STATUS_BAR_MSG.connect(self.stat_temp_msg) self.manga_table_view.STATUS_BAR_MSG.connect(self.stat_temp_msg) self.stat_row_info() def stat_temp_msg(self, msg): self.temp_timer.stop() self.temp_msg.setText(msg) self.status_bar.addWidget(self.temp_msg) self.temp_timer.timeout.connect(self.temp_msg.clear) self.temp_timer.setSingleShot(True) self.temp_timer.start(5000) def stat_row_info(self): r = self.manga_list_view.model().rowCount() t = self.manga_list_view.gallery_model._data_count self.stat_info.setText("Loaded {} of {} ".format(r, t)) def manga_display(self): "initiates the manga view" # list view self.manga_list_main = QWidget() # self.manga_list_main.setContentsMargins(-10, -12, -10, -10) self.manga_list_main.setContentsMargins(10, -9, -10, -10) # x, y, inverted_width, inverted_height self.manga_list_layout = QHBoxLayout() self.manga_list_main.setLayout(self.manga_list_layout) self.manga_list_view = gallery.MangaView(self) self.manga_list_view.clicked.connect(self.popup) self.manga_list_view.manga_delegate.POPUP.connect(self.popup) self.popup_window = self.manga_list_view.manga_delegate.popup_window self.manga_list_layout.addWidget(self.manga_list_view) # table view self.manga_table_main = QWidget() self.manga_table_layout = QVBoxLayout() self.manga_table_main.setLayout(self.manga_table_layout) self.manga_table_view = gallery.MangaTableView(self) self.manga_table_view.gallery_model = self.manga_list_view.gallery_model self.manga_table_view.sort_model = self.manga_list_view.sort_model self.manga_table_view.setModel(self.manga_table_view.sort_model) self.manga_table_view.sort_model.change_model(self.manga_table_view.gallery_model) self.manga_table_view.setColumnWidth(gui_constants.FAV, 20) self.manga_table_view.setColumnWidth(gui_constants.ARTIST, 200) self.manga_table_view.setColumnWidth(gui_constants.TITLE, 400) self.manga_table_view.setColumnWidth(gui_constants.TAGS, 300) self.manga_table_view.setColumnWidth(gui_constants.TYPE, 60) self.manga_table_view.setColumnWidth(gui_constants.CHAPTERS, 60) self.manga_table_view.setColumnWidth(gui_constants.LANGUAGE, 100) self.manga_table_view.setColumnWidth(gui_constants.LINK, 400) self.manga_table_layout.addWidget(self.manga_table_view) def search(self, srch_string): case_ins = srch_string.lower() if not gui_constants.ALLOW_SEARCH_REGEX: remove = "^$*+?{}\\|()[]" for x in remove: if x == "[" or x == "]": continue else: case_ins = case_ins.replace(x, ".") else: try: re.compile(case_ins) except re.error: return self.manga_list_view.sort_model.search(case_ins) def popup(self, index): if not self.popup_window.isVisible(): m_x = QCursor.pos().x() m_y = QCursor.pos().y() d_w = QDesktopWidget().width() d_h = QDesktopWidget().height() p_w = gui_constants.POPUP_WIDTH p_h = gui_constants.POPUP_HEIGHT index_rect = self.manga_list_view.visualRect(index) index_point = self.manga_list_view.mapToGlobal(index_rect.topRight()) # adjust so it doesn't go offscreen if d_w - m_x < p_w and d_h - m_y < p_h: # bottom self.popup_window.move(m_x - p_w + 5, m_y - p_h) elif d_w - m_x > p_w and d_h - m_y < p_h: self.popup_window.move(m_x + 5, m_y - p_h) elif d_w - m_x < p_w: self.popup_window.move(m_x - p_w + 5, m_y + 5) else: self.popup_window.move(index_point) self.popup_window.set_gallery(index.data(Qt.UserRole + 1)) self.popup_window.show() def favourite_display(self): "Switches to favourite display" if self.display.currentIndex() == self.m_l_view_index: self.manga_list_view.sort_model.fav_view() else: self.manga_table_view.sort_model.fav_view() def catalog_display(self): "Switches to catalog display" if self.display.currentIndex() == self.m_l_view_index: self.manga_list_view.sort_model.catalog_view() else: self.manga_table_view.sort_model.catalog_view() def settings(self): sett = settingsdialog.SettingsDialog(self) sett.scroll_speed_changed.connect(self.manga_list_view.updateGeometries) # sett.show() def init_toolbar(self): self.toolbar = QToolBar() self.toolbar.setFixedHeight(25) self.toolbar.setWindowTitle("Show") # text for the contextmenu # self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined? self.toolbar.setMovable(False) self.toolbar.setFloatable(False) # self.toolbar.setIconSize(QSize(20,20)) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) spacer_start = QWidget() # aligns the first actions properly spacer_start.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_start) favourite_view_icon = QIcon(gui_constants.STAR_BTN_PATH) favourite_view_action = QAction(favourite_view_icon, "Favorites", self) favourite_view_action.setToolTip("Show only favourite galleries") favourite_view_action.triggered.connect(self.favourite_display) # need lambda to pass extra args self.toolbar.addAction(favourite_view_action) catalog_view_icon = QIcon(gui_constants.HOME_BTN_PATH) catalog_view_action = QAction(catalog_view_icon, "Library", self) catalog_view_action.setToolTip("Show all your galleries") # catalog_view_action.setText("Catalog") catalog_view_action.triggered.connect(self.catalog_display) # need lambda to pass extra args self.toolbar.addAction(catalog_view_action) self.toolbar.addSeparator() gallery_menu = QMenu() gallery_action = QToolButton() gallery_action.setText("Gallery ") gallery_action.setPopupMode(QToolButton.InstantPopup) gallery_action.setToolTip("Contains various gallery related features") gallery_action.setMenu(gallery_menu) add_gallery_icon = QIcon(gui_constants.PLUS_PATH) gallery_action_add = QAction(add_gallery_icon, "Add gallery", self) gallery_action_add.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit) gallery_action_add.setToolTip("Add a single gallery thoroughly") gallery_menu.addAction(gallery_action_add) add_more_action = QAction(add_gallery_icon, "Add galleries...", self) add_more_action.setStatusTip("Add galleries from different folders") add_more_action.triggered.connect(lambda: self.populate(True)) gallery_menu.addAction(add_more_action) populate_action = QAction(add_gallery_icon, "Populate from folder...", self) populate_action.setStatusTip("Populates the DB with galleries from a single folder") populate_action.triggered.connect(self.populate) gallery_menu.addAction(populate_action) gallery_menu.addSeparator() metadata_action = QAction("Get metadata for all galleries", self) metadata_action.triggered.connect(self.get_metadata) gallery_menu.addAction(metadata_action) self.toolbar.addWidget(gallery_action) self.toolbar.addSeparator() misc_action = QToolButton() misc_action.setText("Misc ") misc_action_menu = QMenu() misc_action.setMenu(misc_action_menu) misc_action.setPopupMode(QToolButton.InstantPopup) misc_action.setToolTip("Contains misc. features") misc_action_random = QAction("Open random gallery", misc_action_menu) misc_action_random.triggered.connect(self.manga_list_view.open_random_gallery) misc_action_menu.addAction(misc_action_random) duplicate_check_simple = QAction("Simple duplicate finder", misc_action_menu) duplicate_check_simple.triggered.connect(lambda: self.manga_list_view.duplicate_check()) misc_action_menu.addAction(duplicate_check_simple) self.toolbar.addWidget(misc_action) spacer_middle = QWidget() # aligns buttons to the right spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolbar.addWidget(spacer_middle) self.grid_toggle_g_icon = QIcon(gui_constants.GRID_PATH) self.grid_toggle_l_icon = QIcon(gui_constants.LIST_PATH) self.grid_toggle = QToolButton() if self.display.currentIndex() == self.m_l_view_index: self.grid_toggle.setIcon(self.grid_toggle_l_icon) else: self.grid_toggle.setIcon(self.grid_toggle_g_icon) self.grid_toggle.setObjectName("gridtoggle") self.grid_toggle.clicked.connect(self.toggle_view) self.toolbar.addWidget(self.grid_toggle) self.search_bar = misc.LineEdit() if gui_constants.SEARCH_AUTOCOMPLETE: completer = QCompleter(self) completer.setModel(self.manga_list_view.gallery_model) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setCompletionMode(QCompleter.PopupCompletion) completer.setCompletionRole(Qt.DisplayRole) completer.setCompletionColumn(gui_constants.TITLE) completer.setFilterMode(Qt.MatchContains) self.search_bar.setCompleter(completer) if gui_constants.SEARCH_ON_ENTER: self.search_bar.returnPressed.connect(lambda: self.search(self.search_bar.text())) else: self.search_bar.textChanged[str].connect(self.search) self.search_bar.setPlaceholderText("Search title, artist, namespace & tags") self.search_bar.setMinimumWidth(150) self.search_bar.setMaximumWidth(500) self.toolbar.addWidget(self.search_bar) self.toolbar.addSeparator() settings_icon = QIcon(gui_constants.SETTINGS_PATH) settings_action = QAction("Set&tings", self) settings_action.triggered.connect(self.settings) self.toolbar.addAction(settings_action) spacer_end = QWidget() # aligns About action properly spacer_end.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_end) self.addToolBar(self.toolbar) def toggle_view(self): """ Toggles the current display view """ if self.display.currentIndex() == self.m_l_view_index: self.display.setCurrentIndex(self.m_t_view_index) self.grid_toggle.setIcon(self.grid_toggle_g_icon) else: self.display.setCurrentIndex(self.m_l_view_index) self.grid_toggle.setIcon(self.grid_toggle_l_icon) # TODO: Improve this so that it adds to the gallery dialog, # so user can edit data before inserting (make it a choice) def populate(self, mixed=None): "Populates the database with gallery from local drive'" if mixed: gallery_view = misc.GalleryListView(self, True) gallery_view.SERIES.connect(self.gallery_populate) gallery_view.show() else: path = QFileDialog.getExistingDirectory(None, "Choose a folder containing your galleries") self.gallery_populate(path, True) def gallery_populate(self, path, validate=False): "Scans the given path for gallery to add into the DB" if len(path) is not 0: data_thread = QThread(self) data_thread.setObjectName("General gallery populate") loading = misc.Loading(self) if not loading.ON: misc.Loading.ON = True fetch_instance = fetch.Fetch() fetch_instance.series_path = path loading.show() def finished(status): def hide_loading(): loading.hide() if status: if len(status) != 0: def add_gallery(gallery_list): def append_to_model(x): self.manga_list_view.gallery_model.insertRows(x, None, len(x)) class A(QObject): done = pyqtSignal() prog = pyqtSignal(int) def __init__(self, obj, parent=None): super().__init__(parent) self.obj = obj self.galleries = [] def add_to_db(self): gui_constants.NOTIF_BAR.begin_show() gui_constants.NOTIF_BAR.add_text("Populating database...") for y, x in enumerate(self.obj): gui_constants.NOTIF_BAR.add_text( "Populating database {}/{}".format(y + 1, len(self.obj)) ) gallerydb.add_method_queue(gallerydb.GalleryDB.add_gallery_return, False, x) self.galleries.append(x) y += 1 self.prog.emit(y) append_to_model(self.galleries) gui_constants.NOTIF_BAR.end_show() self.done.emit() loading.progress.setMaximum(len(gallery_list)) a_instance = A(gallery_list) thread = QThread(self) thread.setObjectName("Database populate") def loading_show(): loading.setText("Populating database.\nPlease wait...") loading.show() def loading_hide(): loading.close() self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.emit() def del_later(): try: a_instance.deleteLater() except NameError: pass a_instance.moveToThread(thread) a_instance.prog.connect(loading.progress.setValue) thread.started.connect(loading_show) thread.started.connect(a_instance.add_to_db) a_instance.done.connect(loading_hide) a_instance.done.connect(del_later) thread.finished.connect(thread.deleteLater) thread.start() data_thread.quit hide_loading() log_i("Populating DB from gallery folder: OK") if validate: gallery_list = misc.GalleryListView(self) gallery_list.SERIES.connect(add_gallery) for ser in status: gallery_list.add_gallery(ser, os.path.split(ser.path)[1]) # self.manga_list_view.gallery_model.populate_data() gallery_list.show() else: add_gallery(status) misc.Loading.ON = False else: log_d("No new gallery was found") loading.setText("No new gallery found") data_thread.quit misc.Loading.ON = False else: log_e("Populating DB from gallery folder: Nothing was added!") loading.setText("<font color=red>Nothing was added. Check happypanda_log for details..</font>") loading.progress.setStyleSheet("background-color:red;") data_thread.quit QTimer.singleShot(10000, loading.close) def fetch_deleteLater(): try: fetch_instance.deleteLater except NameError: pass def a_progress(prog): loading.progress.setValue(prog) loading.setText("Searching for galleries...") fetch_instance.moveToThread(data_thread) fetch_instance.DATA_COUNT.connect(loading.progress.setMaximum) fetch_instance.PROGRESS.connect(a_progress) data_thread.started.connect(fetch_instance.local) fetch_instance.FINISHED.connect(finished) fetch_instance.FINISHED.connect(fetch_deleteLater) data_thread.finished.connect(data_thread.deleteLater) data_thread.start() log_i("Populating DB from gallery folder") def resizeEvent(self, event): try: self.notification_bar.resize(event.size().width()) except AttributeError: pass return super().resizeEvent(event) def closeEvent(self, event): # watchers try: self.watchers.stop_all() except AttributeError: pass # settings settings.set(self.manga_list_view.current_sort, "General", "current sort") settings.win_save(self, "AppWindow") # temp dir try: for root, dirs, files in os.walk("temp", topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) log_d("Empty temp on exit: OK") except: log_d("Empty temp on exit: FAIL") # error err = sys.exc_info() if all(err): log_c("Last error before exit:\n{}\n{}\n{}".format(err[0], err[1], err[2])) else: log_d("Normal Exit App: OK") super().closeEvent(event) app = QApplication.instance() app.quit() sys.exit()
class _MainContainer(QWidget): ############################################################################### # MainContainer SIGNALS ############################################################################### """ newFileOpened(QString) allTabClosed() runFile(QString) addToProject(QString) showFileInExplorer(QString) recentTabsModified() currentEditorChanged(QString) fileOpened(QString) ---------migrationAnalyzed() findOcurrences(QString) ---------updateFileMetadata() editorKeyPressEvent(QEvent) locateFunction(QString, QString, bool) [functionName, filePath, isVariable] updateLocator(QString) beforeFileSaved(QString) fileSaved(QString) openPreferences() --------openProject(QString) ---------dontOpenStartPage() """ newFileOpened = pyqtSignal(str) allTabClosed = pyqtSignal() runFile = pyqtSignal(str) addToProject = pyqtSignal(str) showFileInExplorer = pyqtSignal(str) recentTabsModified = pyqtSignal() currentEditorChanged = pyqtSignal(str) fileOpened = pyqtSignal(str) migrationAnalyzed = pyqtSignal()#----------- findOcurrences = pyqtSignal(str) updateFileMetadata = pyqtSignal()#----------- editorKeyPressEvent = pyqtSignal('QEvent*') locateFunction = pyqtSignal(str, str, bool) updateLocator = pyqtSignal(str) beforeFileSaved = pyqtSignal(str) fileSaved = pyqtSignal(str) openPreferences = pyqtSignal() openProject = pyqtSignal(str)#----------- dontOpenStartPage = pyqtSignal()#----------- closeDialog = pyqtSignal('QObject*') allTabsClosed = pyqtSignal() splitEditor = pyqtSignal('QWidget*', 'QWidget*', bool) closeSplit = pyqtSignal(QWidget) toRemovePreview = pyqtSignal() ############################################################################### def __init__(self, parent=None): super(_MainContainer, self).__init__(parent) self._parent = parent self._vbox = QVBoxLayout(self) self._vbox.setContentsMargins(0, 0, 0, 0) self._vbox.setSpacing(0) self.stack = QStackedLayout() self.stack.setStackingMode(QStackedLayout.StackAll) self._vbox.addLayout(self.stack) self.splitter = dynamic_splitter.DynamicSplitter() self.setAcceptDrops(True) # self._files_handler = files_handler.FilesHandler(self) self._add_file_folder = add_file_folder.AddFileFolderWidget(self) self.tdir = None #documentation browser self.docPage = None #Code Navigation self._locator = locator.GoToDefinition() self.__codeBack = [] self.__codeForward = [] self.__bookmarksFile = '' self.__bookmarksPos = -1 self.__breakpointsFile = '' self.__breakpointsPos = -1 self.__operations = { 0: self._navigate_code_jumps, 1: self._navigate_bookmarks, 2: self._navigate_breakpoints} self.locateFunction.connect(self.locate_function) IDE.register_service('main_container', self) #Register signals connections connections = ( {'target': 'menu_file', 'signal_name': 'openFile',#(QString) 'slot': self.open_file}, {'target': 'explorer_container', 'signal_name': 'goToDefinition',#(int) 'slot': self.editor_go_to_line}, {'target': 'explorer_container', 'signal_name': 'pep8Activated',#(bool) 'slot': self.reset_pep8_warnings}, {'target': 'explorer_container', 'signal_name': 'lintActivated',#(bool) 'slot': self.reset_lint_warnings}, ) IDE.register_signals('main_container', connections) self.selector = main_selector.MainSelector(self) self._opening_dialog = False self.add_widget(self.selector) if settings.SHOW_START_PAGE: self.show_start_page() self.selector.changeCurrent[int].connect(self._change_current_stack) self.selector.removeWidget[int].connect(self._remove_item_from_stack) self.selector.ready.connect(self._selector_ready) self.selector.closePreviewer.connect(self._selector_Close) self.selector.animationCompleted.connect(self._selector_animation_completed) self.closeDialog.connect(self.remove_widget) self.stack.widgetRemoved[int].connect(lambda i:print("widgetRemoved._-", i)) def install(self): ide = IDE.getInstance() ide.place_me_on("main_container", self, "central", top=True) self.combo_area = combo_editor.ComboEditor(original=True) self.combo_area.allFilesClosed.connect(self._files_closed) self.splitter.add_widget(self.combo_area) self.add_widget(self.splitter) self.current_widget = self.combo_area ui_tools.install_shortcuts(self, actions.ACTIONS, ide) def add_status_bar(self, status): self._vbox.addWidget(status) @property def combo_header_size(self): return self.combo_area.bar.height() def add_widget(self, widget): i = self.stack.addWidget(widget) #if not isinstance(widget, start_page.StartPage): self.tryMakeImagePreview(i) def remove_widget(self, widget): #self.toRemovePreview.emit(self.stack.widget(widget)) self.stack.removeWidget(widget) def _close_dialog(self, widget): self.closeDialog.emit(widget) widget.finished[int].disconnect()#lambda i: self._close_dialog(widget)) def show_dialog(self, widget): print("\n\nshow_dialog", self.isVisible()) self._opening_dialog = True widget.finished[int].connect(lambda i: self._close_dialog(widget)) widget.setVisible(True) self.show_selector() def show_selector(self): print("\n\nshow_selector::", self.selector, self.stack.currentWidget()) if self.selector != self.stack.currentWidget(): _dir = self.Successful_Tmp() if not _dir: print("failed!") return # temp_dir = os.path.join(QDir.tempPath(), "ninja-ide") # if not os.path.exists(temp_dir): # os.mkdir(temp_dir) collected_data = [] current = self.stack.currentIndex() for index in range(self.stack.count()): widget = self.stack.widget(index) if widget == self.selector: continue closable = True if widget == self.splitter: closable = False # path = os.path.join(temp_dir, "screen%s.png" % index) #ff = QFile(_dir, "screen%s.png" % index) path = _dir.absolutePath()+"/screen%s.png" % index pixmap = widget.grab()#widget.rect()) pixmap.save(path) #path = path.replace("\\", '/') #print("path::", path, QFileInfo(path).exists()) path = "file:///"+path if index == current: self.selector.set_preview(index, path)#QUrl(path) collected_data.insert(0, (index, path, closable)) else: collected_data.append((index, path, closable)) self.selector.set_model(collected_data) print("self.selector.set_model()", collected_data) self.stack.setCurrentWidget(self.selector) else: print("\n\n_selector_Close()") self._selector_Close() def Successful_Tmp(self):# CheckTmpDir, StateTmpDir failed = lambda: not self.tdir or not self.tdir.isValid() if failed():# not successfully self.tdir = QTemporaryDir() if failed(): QMessageBox.critical(self, "Unexpected Failurer", "The application has detected a problem trying to\nCreate or Access a Temporary File!.") return None else: self.tdir.setAutoRemove(True) d = QDir(self.tdir.path()) if not d.exists("ninja-ide"): if not d.mkdir("ninja-ide"): self.tdir = None d.cd("ninja-ide") return d def tryMakeImagePreview(self, index): return d = self.Successful_Tmp() if d: self.makeImagePreview(d, index) def makeImagePreview(self, _dir, index): return path = _dir.absolutePath()+"/screen%s.png" % index widget = self.stack.widget(index) pixmap = widget.grab()#widget.rect() pixmap.save(path) def _selector_ready(self): self.stack.setCurrentWidget(self.selector) self.selector.GoTo_GridPreviews() def _selector_Close(self): self.stack.setCurrentWidget(self.splitter) def _selector_animation_completed(self): if self._opening_dialog: # We choose the last one with -2, -1 (for last one) + # -1 for the hidden selector widget which is in the stacked too. self.selector.open_item(self.stack.count() - 2) self._opening_dialog = False def _change_current_stack(self, index): self.stack.setCurrentIndex(index) def _remove_item_from_stack(self, index): #self.toRemovePreview.emit(index) widget = self.stack.takeAt(index) del widget def show_editor_area(self): self.stack.setCurrentWidget(self.splitter) def _files_closed(self): if settings.SHOW_START_PAGE: self.show_start_page() def change_visibility(self): """Show/Hide the Main Container area.""" print("change_visibility11") if self.isVisible(): self.hide() else: self.show() def expand_symbol_combo(self): self.stack.setCurrentWidget(self.splitter) self.current_widget.show_combo_symbol() def expand_file_combo(self): print("expand_file_combo") self.stack.setCurrentWidget(self.splitter) self.current_widget.show_combo_file() def locate_function(self, function, filePath, isVariable): """Move the cursor to the proper position in the navigate stack.""" editorWidget = self.get_current_editor() if editorWidget: self.__codeBack.append((editorWidget.file_path, editorWidget.getCursorPosition())) self.__codeForward = [] self._locator.navigate_to(function, filePath, isVariable) def run_file(self, path): self.runFile.emit(path) def _add_to_project(self, path): self.addToProject.emit(path) def _show_file_in_explorer(self, path): self.showFileInExplorer.emit(path) def paste_history(self): """Paste the text from the copy/paste history.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): line, index = editorWidget.getCursorPosition() central = IDE.get_service('central_container') if central: editorWidget.insertAt(central.get_paste(), line, index) def copy_history(self): """Copy the selected text into the copy/paste history.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): copy = editorWidget.selectedText() central = IDE.get_service('central_container') if central: central.add_copy(copy) def import_from_everywhere(self): """Insert an import line from any place in the editor.""" editorWidget = self.get_current_editor() if editorWidget: dialog = from_import_dialog.FromImportDialog(editorWidget, self) dialog.show() def add_back_item_navigation(self): """Add an item to the back stack and reset the forward stack.""" editorWidget = self.get_current_editor() if editorWidget: self.__codeBack.append((editorWidget.file_path, editorWidget.getCursorPosition())) self.__codeForward = [] def preview_in_browser(self): """Load the current html file in the default browser.""" editorWidget = self.get_current_editor() if editorWidget: if not editorWidget.file_path: self.save_file() ext = file_manager.get_file_extension(editorWidget.file_path) if ext in ('html', 'shpaml', 'handlebars', 'tpl'): webbrowser.open_new_tab(editorWidget.file_path) def add_bookmark_breakpoint(self): """Add a bookmark or breakpoint to the current file in the editor.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): if self.current_widget.bar.code_navigator.operation == 1: editorWidget.handle_bookmarks_breakpoints( editorWidget.getCursorPosition()[0], Qt.ControlModifier) elif self.current_widget.bar.code_navigator.operation == 2: editorWidget.handle_bookmarks_breakpoints( editorWidget.getCursorPosition()[0], Qt.NoModifier) def __navigate_with_keyboard(self, val): """Navigate between the positions in the jump history stack.""" op = self.current_widget.bar.code_navigator.operation self.navigate_code_history(val, op) def navigate_code_history(self, val, op): """Navigate the code history.""" self.__operations[op](val) def _navigate_code_jumps(self, val): """Navigate between the jump points.""" node = None if not val and self.__codeBack: node = self.__codeBack.pop() editorWidget = self.get_current_editor() if editorWidget: self.__codeForward.append((editorWidget.file_path, editorWidget.getCursorPosition())) elif val and self.__codeForward: node = self.__codeForward.pop() editorWidget = self.get_current_editor() if editorWidget: self.__codeBack.append((editorWidget.file_path, editorWidget.getCursorPosition())) if node: filename = node[0] line, col = node[1] self.open_file(filename, line, col) def _navigate_breakpoints(self, val): """Navigate between the breakpoints.""" #FIXME: put navigate breakpoints and bookmarks as one method. breakList = list(settings.BREAKPOINTS.keys()) breakList.sort() if not breakList: return if self.__breakpointsFile not in breakList: self.__breakpointsFile = breakList[0] index = breakList.index(self.__breakpointsFile) breaks = settings.BREAKPOINTS.get(self.__breakpointsFile, []) lineNumber = 0 #val == True: forward if val: if (len(breaks) - 1) > self.__breakpointsPos: self.__breakpointsPos += 1 lineNumber = breaks[self.__breakpointsPos] elif len(breaks) > 0: if index < (len(breakList) - 1): self.__breakpointsFile = breakList[index + 1] else: self.__breakpointsFile = breakList[0] self.__breakpointsPos = 0 lineNumber = settings.BREAKPOINTS[self.__breakpointsFile][0] else: if self.__breakpointsPos > 0: self.__breakpointsPos -= 1 lineNumber = breaks[self.__breakpointsPos] elif len(breaks) > 0: self.__breakpointsFile = breakList[index - 1] breaks = settings.BREAKPOINTS[self.__breakpointsFile] self.__breakpointsPos = len(breaks) - 1 lineNumber = breaks[self.__breakpointsPos] if file_manager.file_exists(self.__breakpointsFile): self.open_file(self.__breakpointsFile, lineNumber, None, True) else: settings.BREAKPOINTS.pop(self.__breakpointsFile) if settings.BREAKPOINTS: self._navigate_breakpoints(val) def _navigate_bookmarks(self, val): """Navigate between the bookmarks.""" bookList = list(settings.BOOKMARKS.keys()) bookList.sort() if not bookList: return if self.__bookmarksFile not in bookList: self.__bookmarksFile = bookList[0] index = bookList.index(self.__bookmarksFile) bookms = settings.BOOKMARKS.get(self.__bookmarksFile, []) lineNumber = 0 #val == True: forward if val: if (len(bookms) - 1) > self.__bookmarksPos: self.__bookmarksPos += 1 lineNumber = bookms[self.__bookmarksPos] elif len(bookms) > 0: if index < (len(bookList) - 1): self.__bookmarksFile = bookList[index + 1] else: self.__bookmarksFile = bookList[0] self.__bookmarksPos = 0 lineNumber = settings.BOOKMARKS[self.__bookmarksFile][0] else: if self.__bookmarksPos > 0: self.__bookmarksPos -= 1 lineNumber = bookms[self.__bookmarksPos] elif len(bookms) > 0: self.__bookmarksFile = bookList[index - 1] bookms = settings.BOOKMARKS[self.__bookmarksFile] self.__bookmarksPos = len(bookms) - 1 lineNumber = bookms[self.__bookmarksPos] if file_manager.file_exists(self.__bookmarksFile): self.open_file(self.__bookmarksFile, lineNumber, None, True) else: settings.BOOKMARKS.pop(self.__bookmarksFile) if settings.BOOKMARKS: self._navigate_bookmarks(val) def count_file_code_lines(self): """Count the lines of code in the current file.""" editorWidget = self.get_current_editor() if editorWidget: block_count = editorWidget.lines() blanks = re.findall('(^\n)|(^(\s+)?#)|(^( +)?($|\n))', editorWidget.text(), re.M) blanks_count = len(blanks) resume = self.tr("Lines code: %s\n") % (block_count - blanks_count) resume += (self.tr("Blanks and commented lines: %s\n\n") % blanks_count) resume += self.tr("Total lines: %s") % block_count msgBox = QMessageBox(QMessageBox.Information, self.tr("Summary of lines"), resume, QMessageBox.Ok, editorWidget) msgBox.exec_() def editor_cut(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.cut() def editor_copy(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.copy() def editor_paste(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.paste() def editor_upper(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.to_upper() def editor_lower(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.to_lower() def editor_title(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.to_title() def editor_go_to_definition(self): """Search the definition of the method or variable under the cursor. If more than one method or variable is found with the same name, shows a table with the results and let the user decide where to go.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.go_to_definition() def editor_redo(self): """Execute the redo action in the current editor.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.redo() def editor_undo(self): editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.undo() def editor_indent_less(self): """Indent 1 position to the left for the current line or selection.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.indent_less() def editor_indent_more(self): """Indent 1 position to the right for the current line or selection.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.indent_more() def editor_insert_debugging_prints(self): """Insert a print statement in each selected line.""" editorWidget = self.get_current_editor() if editorWidget: helpers.insert_debugging_prints(editorWidget) def editor_insert_pdb(self): """Insert a pdb.set_trace() statement in tjhe current line.""" editorWidget = self.get_current_editor() if editorWidget: helpers.insert_pdb(editorWidget) def editor_comment(self): """Mark the current line or selection as a comment.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.comment(editorWidget) def editor_uncomment(self): """Uncomment the current line or selection.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.uncomment(editorWidget) def editor_insert_horizontal_line(self): """Insert an horizontal lines of comment symbols.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.insert_horizontal_line(editorWidget) def editor_insert_title_comment(self): """Insert a Title surrounded by comment symbols.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.insert_title_comment(editorWidget) def editor_remove_trailing_spaces(self): """Remove the trailing spaces in the current editor.""" editorWidget = self.get_current_editor() if editorWidget: helpers.remove_trailing_spaces(editorWidget) def editor_replace_tabs_with_spaces(self): """Replace the Tabs with Spaces in the current editor.""" editorWidget = self.get_current_editor() if editorWidget: helpers.replace_tabs_with_spaces(editorWidget) def editor_move_up(self): """Move the current line or selection one position up.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.move_up(editorWidget) def editor_move_down(self): """Move the current line or selection one position down.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.move_down(editorWidget) def editor_remove_line(self): """Remove the current line or selection.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.remove_line(editorWidget) def editor_duplicate(self): """Duplicate the current line or selection.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.duplicate(editorWidget) def editor_highlight_word(self): """Highlight the occurrences of the current word in the editor.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.highlight_selected_word() def editor_complete_declaration(self): """Do the opposite action that Complete Declaration expect.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.complete_declaration() def editor_go_to_line(self, line, select=False):#def editor_go_to_line(self, line): """Jump to the specified line in the current editor.""" editorWidget = self.get_current_editor() print("\nluego en segundo lugar por aca") if editorWidget: editorWidget.jump_to_line(line)#select def editor_go_to_symbol_line(self, line, sym= "", select=False): """Jump to the specified line in the current editor.""" editorWidget = self.get_current_editor() print("\nluego en segundo lugar por aca") if editorWidget: editorWidget.go_to_symbol(line, sym, select)#select def zoom_in_editor(self): """Increase the font size in the current editor.""" editorWidget = self.get_current_editor() if editorWidget: editorWidget.zoom_in() def zoom_out_editor(self): """Decrease the font size in the current editor.""" editorWidget = self.get_current_editor() if editorWidget: editorWidget.zoom_out() def recent_files_changed(self): self.recentTabsModified.emit() def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() else: event.ignore() def dropEvent(self, event): # file_path = event.mimeData().urls()[0].toLocalFile() # paths = [item.toLocalFile() for item in event.mimeData().urls()] self.open_files_fromUrlList(event.mimeData().urls()) # print("\n\n dropEvent", paths) # self.open_file(file_path) def setFocus(self): widget = self.get_current_widget() if widget: widget.setFocus() def current_editor_changed(self, filename): """Notify the new filename of the current editor.""" if filename is None: filename = translations.TR_NEW_DOCUMENT self.currentEditorChanged.emit(filename) def show_split(self, orientation_vertical=False): #IDE.select_current(self.current_widget.currentWidget()) self.current_widget.split_editor(orientation_vertical) def add_editor(self, fileName=None, ignore_checkers=False): print("filename::", fileName) ninjaide = IDE.getInstance() editable = ninjaide.get_or_create_editable(fileName) if editable.editor: self.current_widget.set_current(editable) print("\n\nreturn") return self.current_widget.currentWidget() else: editable.ignore_checkers = ignore_checkers editorWidget = self.create_editor_from_editable(editable) #add the tab keep_index = (self.splitter.count() > 1 and self.combo_area.stacked.count() > 0) self.combo_area.add_editor(editable, keep_index) #emit a signal about the file open self.fileOpened.emit(fileName) if keep_index: self.current_widget.set_current(editable) self.stack.setCurrentWidget(self.splitter) return editorWidget def create_editor_from_editable(self, editable): editorWidget = editor.create_editor(editable) #Connect signals editable.fileSaved.connect(self._editor_tab_was_saved) editorWidget.openDropFile.connect(self.open_file) editorWidget.addBackItemNavigation.connect(self.add_back_item_navigation) editorWidget.locateFunction.connect(self._editor_locate_function) editorWidget.findOcurrences.connect(self._find_occurrences) #keyPressEventSignal for plugins editorWidget.keyPressSignal.connect(self._editor_keyPressEvent) return editorWidget def reset_pep8_warnings(self, value): pass #FIXME: check how we handle this #for i in range(self._tabMain.count()): #widget = self._tabMain.widget(i) #if type(widget) is editor.Editor: #if value: #widget.syncDocErrorsSignal = True #widget.pep8.check_style() #else: #widget.hide_pep8_errors() def reset_lint_warnings(self, value): pass #FIXME: check how we handle this #for i in range(self._tabMain.count()): #widget = self._tabMain.widget(i) #if type(widget) is editor.Editor: #if value: #widget.syncDocErrorsSignal = True #widget.errors.check_errors() #else: #widget.hide_lint_errors() def _find_occurrences(self, word): self.findOcurrences.emit(word) def _editor_keyPressEvent(self, event): self.editorKeyPressEvent.emit(event) def _editor_locate_function(self, function, filePath, isVariable): self.locateFunction.emit(function, filePath, isVariable) def _editor_tab_was_saved(self, editable=None): self.updateLocator.emit(editable.file_path) def get_current_widget(self): return self.current_widget.currentWidget() def get_current_editor(self): """Return the Actual Editor or None Return an instance of Editor if the Current Tab contains an Editor or None if it is not an instance of Editor""" widget = self.current_widget.currentWidget() if isinstance(widget, editor.Editor): return widget return None def reload_file(self, editorWidget=None): if editorWidget is None: editorWidget = self.get_current_editor() if editorWidget is not None: editorWidget.neditable.reload_file() def add_tab(self, widget, tabName, tabIndex=None): pass #return self.tabs.add_tab(widget, tabName, index=tabIndex) def open_image(self, fileName): try: if not self.is_open(fileName): viewer = image_viewer.ImageViewer(fileName) self.add_tab(viewer, file_manager.get_basename(fileName)) viewer.ID = fileName else: self.move_to_open(fileName) except Exception as reason: logger.error('open_image: %s', reason) QMessageBox.information(self, self.tr("Incorrect File"), self.tr("The image couldn\'t be open")) def open_files_fromList(self, lst): for f in lst: self.open_file(f) def open_files_fromUrlList(self, lst): for f in lst: self.open_file(f.toLocalFile()) def open_file(self, filename='', line=-1, col=0, ignore_checkers=False): logger.debug("will try to open %s" % filename) if not filename: logger.debug("has nofilename") if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") editorWidget = self.get_current_editor() ninjaide = IDE.getInstance() if ninjaide: current_project = ninjaide.get_current_project() if current_project is not None: directory = current_project elif editorWidget is not None and editorWidget.file_path: directory = file_manager.get_folder( editorWidget.file_path) extensions = ';;'.join( ['{}(*{})'.format(e.upper()[1:], e) for e in settings.SUPPORTED_EXTENSIONS + ['.*', '']]) fileNames = QFileDialog.getOpenFileNames(self, self.tr("Open File"), directory, extensions)[0]#list() else: logger.debug("has filename") fileNames = [filename] if not fileNames: return print("\n\nopen_file") othersFileNames = [] image_extensions = ('bmp', 'gif', 'jpeg', 'jpg', 'png') for filename in fileNames: print("nombre", filename) if QFileInfo(filename).isDir(): othersFileNames.extend( QFileDialog.getOpenFileNames(None, "Select files", filename, "Files (*.*)")[0] ) elif file_manager.get_file_extension(filename) in image_extensions: logger.debug("will open as image") self.open_image(filename) elif file_manager.get_file_extension(filename).endswith('ui'): logger.debug("will load in ui editor") self.w = uic.loadUi(filename) self.w.show() else: logger.debug("will try to open: " + filename) self.__open_file(filename, line, col, ignore_checkers) for filename in othersFileNames: print("nombre", filename) if QFileInfo(filename).isDir(): continue elif file_manager.get_file_extension(filename) in image_extensions: logger.debug("will open as image") self.open_image(filename) elif file_manager.get_file_extension(filename).endswith('ui'): logger.debug("will load in ui editor") self.w = uic.loadUi(filename) self.w.show() else: logger.debug("will try to open: " + filename) self.__open_file(filename, line, col, ignore_checkers) def __open_file(self, fileName='', line=-1, col=0, ignore_checkers=False): print("unio", fileName) try: editorWidget = self.add_editor(fileName, ignore_checkers=ignore_checkers) if line != -1: editorWidget.set_cursor_position(line, col) self.currentEditorChanged.emit(fileName) except file_manager.NinjaIOException as reason: QMessageBox.information(self, self.tr("The file couldn't be open"), str(reason)) def is_open(self, filename): pass #return self.tabs.is_open(filename) != -1 def move_to_open(self, filename): pass #FIXME: add in the current split? #if self.tabs.is_open(filename) != -1: #self.tabs.move_to_open(filename) #self.tabs.currentWidget().setFocus() #self.emit(SIGNAL("currentEditorChanged(QString)"), filename) def get_widget_for_id(self, filename): pass #widget = None #index = self.tabs.is_open(filename) #if index != -1: #widget = self.tabs.widget(index) #return widget def change_open_tab_id(self, idname, newId): """Search for the Tab with idname, and set the newId to that Tab.""" pass #index = self.tabs.is_open(idname) #if index != -1: #widget = self.tabs.widget(index) #tabName = file_manager.get_basename(newId) #self.tabs.change_open_tab_name(index, tabName) #widget.ID = newId def close_deleted_file(self, idname): """Search for the Tab with id, and ask the user if should be closed.""" pass #index = self.tabs.is_open(idname) #if index != -1: #result = QMessageBox.question(self, self.tr("Close Deleted File"), #self.tr("Are you sure you want to close the deleted file?\n" #"The content will be completely deleted."), #buttons=QMessageBox.Yes | QMessageBox.No) #if result == QMessageBox.Yes: #self.tabs.removeTab(index) def save_file(self, editorWidget=None): #FIXME: check how we handle this if not editorWidget: editorWidget = self.get_current_editor() if not editorWidget: return False try: #editorWidget.just_saved = True if (editorWidget.nfile.is_new_file or not editorWidget.nfile.has_write_permission()): return self.save_file_as() self.beforeFileSaved.emit(editorWidget.file_path) if settings.REMOVE_TRAILING_SPACES: helpers.remove_trailing_spaces(editorWidget) editorWidget.neditable.save_content() #file_manager.store_file_content( #fileName, content, addExtension=False) encoding = file_manager.get_file_encoding(editorWidget.text()) editorWidget.encoding = encoding self.fileSaved.emit((self.tr("File Saved: %s") % editorWidget.file_path)) return True except Exception as reason: logger.error('save_file: %s', reason) QMessageBox.information(self, self.tr("Save Error"), self.tr("The file couldn't be saved!")) return False def save_file_as(self): editorWidget = self.get_current_editor() if not editorWidget: return False try: filters = '(*.py);;(*.*)' if editorWidget.file_path: ext = file_manager.get_file_extension(editorWidget.file_path) if ext != 'py': filters = '(*.%s);;(*.py);;(*.*)' % ext save_folder = self._get_save_folder(editorWidget.file_path) fileName = QFileDialog.getSaveFileName( self._parent, self.tr("Save File"), save_folder, filters) if not fileName: return False if settings.REMOVE_TRAILING_SPACES: helpers.remove_trailing_spaces(editorWidget) editorWidget.neditable.save_content(path=fileName) editorWidget.register_syntax( file_manager.get_file_extension(fileName)) self.fileSaved.emit((self.tr("File Saved: %s") % fileName)) self.currentEditorChanged.emit(fileName) return True except file_manager.NinjaFileExistsException as ex: QMessageBox.information(self, self.tr("File Already Exists"), (self.tr("Invalid Path: the file '%s' " " already exists.") % ex.filename)) except Exception as reason: logger.error('save_file_as: %s', reason) QMessageBox.information(self, self.tr("Save Error"), self.tr("The file couldn't be saved!")) return False def _get_save_folder(self, fileName): """ Returns the root directory of the 'Main Project' or the home folder """ ninjaide = IDE.getInstance() current_project = ninjaide.get_current_project() if current_project: return current_project.path return os.path.expanduser("~") def save_project(self, projectFolder): pass #FIXME: check how we handle this #for i in range(self._tabMain.count()): #editorWidget = self._tabMain.widget(i) #if type(editorWidget) is editor.Editor and \ #file_manager.belongs_to_folder(projectFolder, #editorWidget.file_path): #reloaded = self._tabMain.check_for_external_modifications( #editorWidget) #if not reloaded: #self.save_file(editorWidget) #for i in range(self.tabsecondary.count()): #editorWidget = self.tabsecondary.widget(i) #if type(editorWidget) is editor.Editor and \ #file_manager.belongs_to_folder(projectFolder, #editorWidget.file_path): #reloaded = self.tabsecondary.check_for_external_modifications( #editorWidget) #if not reloaded: #self.save_file(editorWidget) def save_all(self): pass #FIXME: check how we handle this #for i in range(self._tabMain.count()): #editorWidget = self._tabMain.widget(i) #if type(editorWidget) is editor.Editor: #reloaded = self._tabMain.check_for_external_modifications( #editorWidget) #if not reloaded: #self.save_file(editorWidget) #for i in range(self.tabsecondary.count()): #editorWidget = self.tabsecondary.widget(i) #self.tabsecondary.check_for_external_modifications(editorWidget) #if type(editorWidget) is editor.Editor: #reloaded = self.tabsecondary.check_for_external_modifications( #editorWidget) #if not reloaded: #self.save_file(editorWidget) def call_editors_function(self, call_function, *arguments): pass #args = arguments[0] #kwargs = arguments[1] #for i in range(self.tabs.count()): #editorWidget = self.tabs.widget(i) #if isinstance(editorWidget, editor.Editor): #function = getattr(editorWidget, call_function) #function(*args, **kwargs) #TODO: add other splits def show_start_page(self): start = self.stack.widget(0) if isinstance(start, start_page.StartPage): self.stack.setCurrentIndex(0) else: startPage = start_page.StartPage(parent=self) startPage.openProject.connect(self.open_project) startPage.openPreferences.connect(self.openPreferences.emit) startPage.newFile.connect(self.add_editor) startPage.openFiles.connect(self.open_files_fromList) self.stack.insertWidget(0, startPage) self.stack.setCurrentIndex(0) self.tryMakeImagePreview(0) #"screen0.png" def show_python_doc(self): if sys.platform == 'win32': self.docPage = browser_widget.BrowserWidget( 'http://docs.python.org/') else: process = runner.start_pydoc() self.docPage = browser_widget.BrowserWidget(process[1], process[0]) self.add_tab(self.docPage, translations.TR_PYTHON_DOC) def show_report_bugs(self): webbrowser.open(resources.BUGS_PAGE) def show_plugins_doc(self): bugsPage = browser_widget.BrowserWidget(resources.PLUGINS_DOC, self) self.add_tab(bugsPage, translations.TR_HOW_TO_WRITE_PLUGINS) def editor_jump_to_line(self, lineno=None): """Jump to line *lineno* if it is not None otherwise ask to the user the line number to jump """ editorWidget = self.get_current_editor() if editorWidget: editorWidget.jump_to_line(lineno=lineno) def get_opened_documents(self): #return self.tabs.get_documents_data() return [] def check_for_unsaved_files(self): pass #return self.tabs._check_unsaved_tabs() def get_unsaved_files(self): pass #return self.tabs.get_unsaved_files() def reset_editor_flags(self): pass #for i in range(self.tabs.count()): #widget = self.tabs.widget(i) #if isinstance(widget, editor.Editor): #widget.set_flags() def _specify_syntax(self, widget, syntaxLang): if isinstance(widget, editor.Editor): widget.restyle(syntaxLang) def apply_editor_theme(self, family, size): pass #for i in range(self.tabs.count()): #widget = self.tabs.widget(i) #if isinstance(widget, editor.Editor): #widget.restyle() #widget.set_font(family, size) def update_editor_margin_line(self): pass #for i in range(self.tabs.count()): #widget = self.tabs.widget(i) #if isinstance(widget, editor.Editor): #widget._update_margin_line() def open_project(self, path): print("open_project") self.openProject.emit(path) def close_python_doc(self): pass #close the python document server (if running) #if self.docPage: #index = self.tabs.indexOf(self.docPage) #self.tabs.removeTab(index) ##assign None to the browser #self.docPage = None def close_file(self): """Close the current tab in the current TabWidget.""" self.current_widget.close_current_file() def create_file(self, base_path, project_path): self._add_file_folder.create_file(base_path, project_path) def create_folder(self, base_path, project_path): self._add_file_folder.create_folder(base_path, project_path) def change_tab(self): """Change the tab in the current TabWidget.""" print("\nchange_tab") self.stack.setCurrentWidget(self.splitter) # self._files_handler.next_item() pass def change_tab_reverse(self): """Change the tab in the current TabWidget backwards.""" print("\nchange_tab_reverse") self.stack.setCurrentWidget(self.splitter) # self._files_handler.previous_item() def toggle_tabs_and_spaces(self): """ Toggle Show/Hide Tabs and Spaces """ settings.SHOW_TABS_AND_SPACES = not settings.SHOW_TABS_AND_SPACES qsettings = IDE.ninja_settings() qsettings.setValue('preferences/editor/showTabsAndSpaces', settings.SHOW_TABS_AND_SPACES) def show_navigation_buttons(self): """Show Navigation menu.""" self.stack.setCurrentWidget(self.splitter) self.combo_area.show_menu_navigation() def change_split_focus(self): pass #FIXME: check how we handle this #if self.actualTab == self._tabMain and self.tabsecondary.isVisible(): #self.actualTab = self.tabsecondary #else: #self.actualTab = self._tabMain #widget = self.actualTab.currentWidget() #if widget is not None: #widget.setFocus() def shortcut_index(self, index): pass #self.tabs.setCurrentIndex(index) def print_file(self): """Call the print of ui_tool Call print of ui_tool depending on the focus of the application""" #TODO: Add funtionality for proyect tab and methods tab editorWidget = self.get_current_editor() if editorWidget is not None: fileName = "newDocument.pdf" if editorWidget.file_path: fileName = file_manager.get_basename( editorWidget.file_path) fileName = fileName[:fileName.rfind('.')] + '.pdf' ui_tools.print_file(fileName, editorWidget.print_) def split_assistance(self): dialog = split_orientation.SplitOrientation(self) dialog.show() def close_split(self): if self.current_widget != self.combo_area: self.current_widget.bar.close_split() def split_vertically(self): self.show_split(False) def split_horizontally(self): self.show_split(True) def navigate_back(self): self.__navigate_with_keyboard(False) def navigate_forward(self): self.__navigate_with_keyboard(True)
class SettingsDialog(QWidget): "A settings dialog" scroll_speed_changed = pyqtSignal() def __init__(self, parent=None): super().__init__(parent, flags=Qt.Window) self.resize(700, 500) self.show() self.restore_values() self.initUI() def initUI(self): main_layout = QVBoxLayout() sub_layout = QHBoxLayout() # Left Panel left_panel = QListWidget() left_panel.setViewMode(left_panel.ListMode) #left_panel.setIconSize(QSize(40,40)) left_panel.setTextElideMode(Qt.ElideRight) left_panel.setMaximumWidth(200) left_panel.itemClicked.connect(self.change) #web.setText('Web') self.application = QListWidgetItem() self.application.setText('Application') self.web = QListWidgetItem() self.web.setText('Web') self.visual = QListWidgetItem() self.visual.setText('Visual') self.advanced = QListWidgetItem() self.advanced.setText('Advanced') self.about = QListWidgetItem() self.about.setText('About') #main.setIcon(QIcon(os.path.join(gui_constants.static_dir, 'plus2.png'))) left_panel.addItem(self.application) left_panel.addItem(self.web) left_panel.addItem(self.visual) left_panel.addItem(self.advanced) left_panel.addItem(self.about) left_panel.setMaximumWidth(100) # right panel self.right_panel = QStackedLayout() self.init_right_panel() # bottom bottom_layout = QHBoxLayout() ok_btn = QPushButton('Ok') ok_btn.clicked.connect(self.accept) cancel_btn = QPushButton('Cancel') cancel_btn.clicked.connect(self.close) info_lbl = QLabel() info_lbl.setText('<a href="https://github.com/Pewpews/happypanda">'+ 'Visit GitHub Repo</a> | Options marked with * requires application restart.') info_lbl.setTextFormat(Qt.RichText) info_lbl.setTextInteractionFlags(Qt.TextBrowserInteraction) info_lbl.setOpenExternalLinks(True) self.spacer = QWidget() self.spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) bottom_layout.addWidget(info_lbl, 0, Qt.AlignLeft) bottom_layout.addWidget(self.spacer) bottom_layout.addWidget(ok_btn, 0, Qt.AlignRight) bottom_layout.addWidget(cancel_btn, 0, Qt.AlignRight) sub_layout.addWidget(left_panel) sub_layout.addLayout(self.right_panel) main_layout.addLayout(sub_layout) main_layout.addLayout(bottom_layout) self.restore_options() self.setLayout(main_layout) self.setWindowTitle('Settings') def change(self, item): def curr_index(index): if index != self.right_panel.currentIndex(): self.right_panel.setCurrentIndex(index) if item == self.application: curr_index(self.application_index) elif item == self.web: curr_index(self.web_index) elif item == self.visual: curr_index(self.visual_index) elif item == self.advanced: curr_index(self.advanced_index) elif item == self.about: curr_index(self.about_index) def restore_values(self): #Web self.exprops = settings.ExProperties() # Visual self.high_quality_thumbs = gui_constants.HIGH_QUALITY_THUMBS self.popup_width = gui_constants.POPUP_WIDTH self.popup_height = gui_constants.POPUP_HEIGHT self.style_sheet = gui_constants.user_stylesheet_path # Advanced self.scroll_speed = gui_constants.SCROLL_SPEED self.cache_size = gui_constants.THUMBNAIL_CACHE_SIZE self.prefetch_item_amnt = gui_constants.PREFETCH_ITEM_AMOUNT def restore_options(self): # App / Monitor / Misc self.enable_monitor.setChecked(gui_constants.ENABLE_MONITOR) self.look_new_gallery_startup.setChecked(gui_constants.LOOK_NEW_GALLERY_STARTUP) self.auto_add_new_galleries.setChecked(gui_constants.LOOK_NEW_GALLERY_AUTOADD) # App / Monitor / Folders for path in gui_constants.MONITOR_PATHS: self.add_folder_monitor(path) # Web / General if 'g.e-hentai' in gui_constants.DEFAULT_EHEN_URL: self.default_ehen_url.setChecked(True) else: self.exhentai_ehen_url.setChecked(True) self.replace_metadata.setChecked(gui_constants.REPLACE_METADATA) self.always_first_hit.setChecked(gui_constants.ALWAYS_CHOOSE_FIRST_HIT) self.web_time_offset.setValue(gui_constants.GLOBAL_EHEN_TIME) self.continue_a_metadata_fetcher.setChecked(gui_constants.CONTINUE_AUTO_METADATA_FETCHER) self.use_jpn_title.setChecked(gui_constants.USE_JPN_TITLE) # Web / Exhentai self.ipbid_edit.setText(self.exprops.ipb_id) self.ipbpass_edit.setText(self.exprops.ipb_pass) # Visual / Grid View / Tooltip self.grid_tooltip_group.setChecked(gui_constants.GRID_TOOLTIP) self.visual_grid_tooltip_title.setChecked(gui_constants.TOOLTIP_TITLE) self.visual_grid_tooltip_author.setChecked(gui_constants.TOOLTIP_AUTHOR) self.visual_grid_tooltip_chapters.setChecked(gui_constants.TOOLTIP_CHAPTERS) self.visual_grid_tooltip_status.setChecked(gui_constants.TOOLTIP_STATUS) self.visual_grid_tooltip_type.setChecked(gui_constants.TOOLTIP_TYPE) self.visual_grid_tooltip_lang.setChecked(gui_constants.TOOLTIP_LANG) self.visual_grid_tooltip_descr.setChecked(gui_constants.TOOLTIP_DESCR) self.visual_grid_tooltip_tags.setChecked(gui_constants.TOOLTIP_TAGS) self.visual_grid_tooltip_last_read.setChecked(gui_constants.TOOLTIP_LAST_READ) self.visual_grid_tooltip_times_read.setChecked(gui_constants.TOOLTIP_TIMES_READ) self.visual_grid_tooltip_pub_date.setChecked(gui_constants.TOOLTIP_PUB_DATE) self.visual_grid_tooltip_date_added.setChecked(gui_constants.TOOLTIP_DATE_ADDED) # Visual / Grid View / Gallery self.external_viewer_ico.setChecked(gui_constants.USE_EXTERNAL_PROG_ICO) self.gallery_type_ico.setChecked(gui_constants.DISPLAY_GALLERY_TYPE) if gui_constants.GALLERY_FONT_ELIDE: self.gallery_text_elide.setChecked(True) else: self.gallery_text_fit.setChecked(True) self.font_lbl.setText(gui_constants.GALLERY_FONT[0]) self.font_size_lbl.setValue(gui_constants.GALLERY_FONT[1]) def re_enforce(s): if s: self.search_on_enter.setChecked(True) self.search_allow_regex.clicked.connect(re_enforce) if gui_constants.SEARCH_ON_ENTER: self.search_on_enter.setChecked(True) else: self.search_every_keystroke.setChecked(True) # Visual / Grid View / Colors self.grid_label_color.setText(gui_constants.GRID_VIEW_LABEL_COLOR) self.grid_title_color.setText(gui_constants.GRID_VIEW_TITLE_COLOR) self.grid_artist_color.setText(gui_constants.GRID_VIEW_ARTIST_COLOR) # Advanced / Misc / External Viewer self.external_viewer_path.setText(gui_constants.EXTERNAL_VIEWER_PATH) def accept(self): set = settings.set # App / Monitor / misc gui_constants.ENABLE_MONITOR = self.enable_monitor.isChecked() set(gui_constants.ENABLE_MONITOR, 'Application', 'enable monitor') gui_constants.LOOK_NEW_GALLERY_STARTUP = self.look_new_gallery_startup.isChecked() set(gui_constants.LOOK_NEW_GALLERY_STARTUP, 'Application', 'look new gallery startup') gui_constants.LOOK_NEW_GALLERY_AUTOADD = self.auto_add_new_galleries.isChecked() set(gui_constants.LOOK_NEW_GALLERY_AUTOADD, 'Application', 'look new gallery autoadd') # App / Monitor / folders n = self.folders_layout.rowCount() paths = '' for x in range(n): item = self.folders_layout.takeAt(x+1) l_edit = item.widget() p = l_edit.text() if p: if x == n-1: paths += '{}'.format(p) else: paths += '{},'.format(p) set(paths, 'Application', 'monitor paths') gui_constants.MONITOR_PATHS = paths.split(',') # Web / General if self.default_ehen_url.isChecked(): gui_constants.DEFAULT_EHEN_URL = 'http://g.e-hentai.org/' else: gui_constants.DEFAULT_EHEN_URL = 'http://exhentai.org/' set(gui_constants.DEFAULT_EHEN_URL, 'Web', 'default ehen url') gui_constants.REPLACE_METADATA = self.replace_metadata.isChecked() set(gui_constants.REPLACE_METADATA, 'Web', 'replace metadata') gui_constants.ALWAYS_CHOOSE_FIRST_HIT = self.always_first_hit.isChecked() set(gui_constants.ALWAYS_CHOOSE_FIRST_HIT, 'Web', 'always choose first hit') gui_constants.GLOBAL_EHEN_TIME = self.web_time_offset.value() set(gui_constants.GLOBAL_EHEN_TIME, 'Web', 'global ehen time offset') gui_constants.CONTINUE_AUTO_METADATA_FETCHER = self.continue_a_metadata_fetcher.isChecked() set(gui_constants.CONTINUE_AUTO_METADATA_FETCHER, 'Web', 'continue auto metadata fetcher') gui_constants.USE_JPN_TITLE = self.use_jpn_title.isChecked() set(gui_constants.USE_JPN_TITLE, 'Web', 'use jpn title') # Web / ExHentai self.exprops.ipb_id = self.ipbid_edit.text() self.exprops.ipb_pass = self.ipbpass_edit.text() # Visual / Grid View / Tooltip gui_constants.GRID_TOOLTIP = self.grid_tooltip_group.isChecked() set(gui_constants.GRID_TOOLTIP, 'Visual', 'grid tooltip') gui_constants.TOOLTIP_TITLE = self.visual_grid_tooltip_title.isChecked() set(gui_constants.TOOLTIP_TITLE, 'Visual', 'tooltip title') gui_constants.TOOLTIP_AUTHOR = self.visual_grid_tooltip_author.isChecked() set(gui_constants.TOOLTIP_AUTHOR, 'Visual', 'tooltip author') gui_constants.TOOLTIP_CHAPTERS = self.visual_grid_tooltip_chapters.isChecked() set(gui_constants.TOOLTIP_CHAPTERS, 'Visual', 'tooltip chapters') gui_constants.TOOLTIP_STATUS = self.visual_grid_tooltip_status.isChecked() set(gui_constants.TOOLTIP_STATUS, 'Visual', 'tooltip status') gui_constants.TOOLTIP_TYPE = self.visual_grid_tooltip_type.isChecked() set(gui_constants.TOOLTIP_TYPE, 'Visual', 'tooltip type') gui_constants.TOOLTIP_LANG = self.visual_grid_tooltip_lang.isChecked() set(gui_constants.TOOLTIP_LANG, 'Visual', 'tooltip lang') gui_constants.TOOLTIP_DESCR = self.visual_grid_tooltip_descr.isChecked() set(gui_constants.TOOLTIP_DESCR, 'Visual', 'tooltip descr') gui_constants.TOOLTIP_TAGS = self.visual_grid_tooltip_tags.isChecked() set(gui_constants.TOOLTIP_TAGS, 'Visual', 'tooltip tags') gui_constants.TOOLTIP_LAST_READ = self.visual_grid_tooltip_last_read.isChecked() set(gui_constants.TOOLTIP_LAST_READ, 'Visual', 'tooltip last read') gui_constants.TOOLTIP_TIMES_READ = self.visual_grid_tooltip_times_read.isChecked() set(gui_constants.TOOLTIP_TIMES_READ, 'Visual', 'tooltip times read') gui_constants.TOOLTIP_PUB_DATE = self.visual_grid_tooltip_pub_date.isChecked() set(gui_constants.TOOLTIP_PUB_DATE, 'Visual', 'tooltip pub date') gui_constants.TOOLTIP_DATE_ADDED = self.visual_grid_tooltip_date_added.isChecked() set(gui_constants.TOOLTIP_DATE_ADDED, 'Visual', 'tooltip date added') # Visual / Grid View / Gallery gui_constants.USE_EXTERNAL_PROG_ICO = self.external_viewer_ico.isChecked() set(gui_constants.USE_EXTERNAL_PROG_ICO, 'Visual', 'use external prog ico') gui_constants.DISPLAY_GALLERY_TYPE = self.gallery_type_ico.isChecked() set(gui_constants.DISPLAY_GALLERY_TYPE, 'Visual', 'display gallery type') if self.gallery_text_elide.isChecked(): gui_constants.GALLERY_FONT_ELIDE = True else: gui_constants.GALLERY_FONT_ELIDE = False set(gui_constants.GALLERY_FONT_ELIDE, 'Visual', 'gallery font elide') gui_constants.GALLERY_FONT = (self.font_lbl.text(), self.font_size_lbl.value()) set(gui_constants.GALLERY_FONT[0], 'Visual', 'gallery font family') set(gui_constants.GALLERY_FONT[1], 'Visual', 'gallery font size') # Visual / Grid View / Colors if self.color_checker(self.grid_title_color.text()): gui_constants.GRID_VIEW_TITLE_COLOR = self.grid_title_color.text() set(gui_constants.GRID_VIEW_TITLE_COLOR, 'Visual', 'grid view title color') if self.color_checker(self.grid_artist_color.text()): gui_constants.GRID_VIEW_ARTIST_COLOR = self.grid_artist_color.text() set(gui_constants.GRID_VIEW_ARTIST_COLOR, 'Visual', 'grid view artist color') if self.color_checker(self.grid_label_color.text()): gui_constants.GRID_VIEW_LABEL_COLOR = self.grid_label_color.text() set(gui_constants.GRID_VIEW_LABEL_COLOR, 'Visual', 'grid view label color') # Advanced / Misc # Advanced / Misc / Grid View gui_constants.SCROLL_SPEED = self.scroll_speed set(self.scroll_speed, 'Advanced', 'scroll speed') self.scroll_speed_changed.emit() gui_constants.THUMBNAIL_CACHE_SIZE = self.cache_size set(self.cache_size[1], 'Advanced', 'cache size') QPixmapCache.setCacheLimit(self.cache_size[0]* self.cache_size[1]) # Advanced / Misc / Search gui_constants.ALLOW_SEARCH_REGEX = self.search_allow_regex.isChecked() set(gui_constants.ALLOW_SEARCH_REGEX, 'Advanced', 'allow search regex') gui_constants.SEARCH_AUTOCOMPLETE = self.search_autocomplete.isChecked() set(gui_constants.SEARCH_AUTOCOMPLETE, 'Advanced', 'search autocomplete') if self.search_on_enter.isChecked(): gui_constants.SEARCH_ON_ENTER = True else: gui_constants.SEARCH_ON_ENTER = False set(gui_constants.SEARCH_ON_ENTER, 'Advanced', 'search on enter') # Advanced / Misc / External Viewer if not self.external_viewer_path.text(): gui_constants.USE_EXTERNAL_VIEWER = False set(False, 'Advanced', 'use external viewer') else: gui_constants.USE_EXTERNAL_VIEWER = True set(True, 'Advanced', 'use external viewer') gui_constants._REFRESH_EXTERNAL_VIEWER = True gui_constants.EXTERNAL_VIEWER_PATH = self.external_viewer_path.text() set(gui_constants.EXTERNAL_VIEWER_PATH,'Advanced', 'external viewer path') settings.save() self.close() def init_right_panel(self): #def title_def(title): # title_lbl = QLabel(title) # f = QFont() # f.setPixelSize(16) # title_lbl.setFont(f) # return title_lbl # App application = QTabWidget() self.application_index = self.right_panel.addWidget(application) application_general = QWidget() application.addTab(application_general, 'General') application.setTabEnabled(0, False) # App / Monitor app_monitor_page = QScrollArea() app_monitor_page.setBackgroundRole(QPalette.Base) app_monitor_dummy = QWidget() app_monitor_page.setWidgetResizable(True) app_monitor_page.setWidget(app_monitor_dummy) application.addTab(app_monitor_page, 'Monitoring') application.setCurrentIndex(1) app_monitor_m_l = QVBoxLayout(app_monitor_dummy) # App / Monitor / misc app_monitor_misc_group = QGroupBox('General *', self) app_monitor_m_l.addWidget(app_monitor_misc_group) app_monitor_misc_m_l = QFormLayout(app_monitor_misc_group) monitor_info = QLabel('Directory monitoring will monitor the specified directories for any'+ ' gallery events. For example if you delete a gallery source in one of your'+ ' monitored directories the application will inform you about it, and ask if'+ ' you want to delete the gallery from the application as well.') monitor_info.setWordWrap(True) app_monitor_misc_m_l.addRow(monitor_info) self.enable_monitor = QCheckBox('Enable directory monitoring') app_monitor_misc_m_l.addRow(self.enable_monitor) self.look_new_gallery_startup = QGroupBox('Scan for new galleries on startup', self) app_monitor_misc_m_l.addRow(self.look_new_gallery_startup) self.look_new_gallery_startup.setCheckable(True) look_new_gallery_startup_m_l = QVBoxLayout(self.look_new_gallery_startup) self.auto_add_new_galleries = QCheckBox('Automatically add found galleries') look_new_gallery_startup_m_l.addWidget(self.auto_add_new_galleries) # App / Monitor / folders app_monitor_group = QGroupBox('Directories *', self) app_monitor_m_l.addWidget(app_monitor_group, 1) app_monitor_folders_m_l = QVBoxLayout(app_monitor_group) app_monitor_folders_add = QPushButton('+') app_monitor_folders_add.clicked.connect(self.add_folder_monitor) app_monitor_folders_add.setMaximumWidth(20) app_monitor_folders_add.setMaximumHeight(20) app_monitor_folders_m_l.addWidget(app_monitor_folders_add, 0, Qt.AlignRight) self.folders_layout = QFormLayout() app_monitor_folders_m_l.addLayout(self.folders_layout) # Web web = QTabWidget() self.web_index = self.right_panel.addWidget(web) web_general_page = QScrollArea() web_general_page.setBackgroundRole(QPalette.Base) web_general_page.setWidgetResizable(True) web.addTab(web_general_page, 'General') web_general_dummy = QWidget() web_general_page.setWidget(web_general_dummy) web_general_m_l = QVBoxLayout(web_general_dummy) metadata_fetcher_group = QGroupBox('Metadata', self) web_general_m_l.addWidget(metadata_fetcher_group) metadata_fetcher_m_l = QFormLayout(metadata_fetcher_group) self.default_ehen_url = QRadioButton('g.e-hentai.org', metadata_fetcher_group) self.exhentai_ehen_url = QRadioButton('exhentai.org', metadata_fetcher_group) ehen_url_l = QHBoxLayout() ehen_url_l.addWidget(self.default_ehen_url) ehen_url_l.addWidget(self.exhentai_ehen_url, 1) metadata_fetcher_m_l.addRow('Default URL:', ehen_url_l) self.continue_a_metadata_fetcher = QCheckBox('Continue from where auto metadata fetcher left off') metadata_fetcher_m_l.addRow(self.continue_a_metadata_fetcher) self.use_jpn_title = QCheckBox('Use japanese title') metadata_fetcher_m_l.addRow(self.use_jpn_title) time_offset_info = QLabel('To avoid getting banned, we need to impose a delay between our requests.'+ ' I have made it so you cannot set the delay lower than the recommended (I don\'t'+ ' want you to get banned, anon).\nSpecify the delay between requests in seconds.') time_offset_info.setWordWrap(True) self.web_time_offset = QSpinBox() self.web_time_offset.setMaximumWidth(40) self.web_time_offset.setMinimum(4) self.web_time_offset.setMaximum(99) metadata_fetcher_m_l.addRow(time_offset_info) metadata_fetcher_m_l.addRow('Requests delay in', self.web_time_offset) replace_metadata_info = QLabel('When fetching for metadata the new metadata will be appended'+ ' to the gallery by default. This means that new data will only be set if'+ ' the field was empty. There is however a special case for namespace & tags.'+ ' We go through all the new namespace & tags to only add those that'+ ' do not already exists.\n\nEnabling this option makes it so that a gallery\'s old data'+ ' are deleted and replaced with the new data.') replace_metadata_info.setWordWrap(True) self.replace_metadata = QCheckBox('Replace old metadata with new metadata') metadata_fetcher_m_l.addRow(replace_metadata_info) metadata_fetcher_m_l.addRow(self.replace_metadata) first_hit_info = QLabel('By default, you get to choose which gallery to extract metadata from when'+ ' there is more than one gallery found when searching.\n'+ 'Enabling this option makes it choose the first hit, saving you from moving your mouse.') first_hit_info.setWordWrap(True) self.always_first_hit = QCheckBox('Always choose first hit') metadata_fetcher_m_l.addRow(first_hit_info) metadata_fetcher_m_l.addRow(self.always_first_hit) # Web / Exhentai exhentai_page = QWidget() web.addTab(exhentai_page, 'ExHentai') ipb_layout = QFormLayout() exhentai_page.setLayout(ipb_layout) self.ipbid_edit = QLineEdit() self.ipbpass_edit = QLineEdit() exh_tutorial = QLabel(gui_constants.EXHEN_COOKIE_TUTORIAL) exh_tutorial.setTextFormat(Qt.RichText) ipb_layout.addRow('IPB Member ID:', self.ipbid_edit) ipb_layout.addRow('IPB Pass Hash:', self.ipbpass_edit) ipb_layout.addRow(exh_tutorial) # Visual visual = QTabWidget() self.visual_index = self.right_panel.addWidget(visual) visual_general_page = QWidget() visual.addTab(visual_general_page, 'General') grid_view_general_page = QWidget() visual.addTab(grid_view_general_page, 'Grid View') grid_view_layout = QVBoxLayout() grid_view_layout.addWidget(QLabel('Options marked with * requires application restart'), 0, Qt.AlignTop) grid_view_general_page.setLayout(grid_view_layout) # grid view # grid view / tooltip self.grid_tooltip_group = QGroupBox('Tooltip', grid_view_general_page) self.grid_tooltip_group.setCheckable(True) grid_view_layout.addWidget(self.grid_tooltip_group, 0, Qt.AlignTop) grid_tooltip_layout = QFormLayout() self.grid_tooltip_group.setLayout(grid_tooltip_layout) grid_tooltip_layout.addRow(QLabel('Control what is'+ ' displayed in the tooltip')) grid_tooltips_hlayout = FlowLayout() grid_tooltip_layout.addRow(grid_tooltips_hlayout) self.visual_grid_tooltip_title = QCheckBox('Title') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_title) self.visual_grid_tooltip_author = QCheckBox('Author') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_author) self.visual_grid_tooltip_chapters = QCheckBox('Chapters') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_chapters) self.visual_grid_tooltip_status = QCheckBox('Status') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_status) self.visual_grid_tooltip_type = QCheckBox('Type') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_type) self.visual_grid_tooltip_lang = QCheckBox('Language') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_lang) self.visual_grid_tooltip_descr = QCheckBox('Description') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_descr) self.visual_grid_tooltip_tags = QCheckBox('Tags') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_tags) self.visual_grid_tooltip_last_read = QCheckBox('Last read') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_last_read) self.visual_grid_tooltip_times_read = QCheckBox('Times read') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_times_read) self.visual_grid_tooltip_pub_date = QCheckBox('Publication Date') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_pub_date) self.visual_grid_tooltip_date_added = QCheckBox('Date added') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_date_added) # grid view / gallery grid_gallery_group = QGroupBox('Gallery', grid_view_general_page) grid_view_layout.addWidget(grid_gallery_group, 0, Qt.AlignTop) grid_gallery_main_l = QFormLayout() grid_gallery_main_l.setFormAlignment(Qt.AlignLeft) grid_gallery_group.setLayout(grid_gallery_main_l) grid_gallery_display = FlowLayout() grid_gallery_main_l.addRow('Display on gallery:', grid_gallery_display) self.external_viewer_ico = QCheckBox('External Viewer') self.external_viewer_ico.setDisabled(True) grid_gallery_display.addWidget(self.external_viewer_ico) self.gallery_type_ico = QCheckBox('File Type') self.gallery_type_ico.setDisabled(True) grid_gallery_display.addWidget(self.gallery_type_ico) gallery_text_mode = QWidget() grid_gallery_main_l.addRow('Text Mode:', gallery_text_mode) gallery_text_mode_l = QHBoxLayout() gallery_text_mode.setLayout(gallery_text_mode_l) self.gallery_text_elide = QRadioButton('Elide text', gallery_text_mode) self.gallery_text_fit = QRadioButton('Fit text', gallery_text_mode) gallery_text_mode_l.addWidget(self.gallery_text_elide, 0, Qt.AlignLeft) gallery_text_mode_l.addWidget(self.gallery_text_fit, 0, Qt.AlignLeft) gallery_text_mode_l.addWidget(Spacer('h'), 1, Qt.AlignLeft) gallery_font = QHBoxLayout() grid_gallery_main_l.addRow('Font:*', gallery_font) self.font_lbl = QLabel() self.font_size_lbl = QSpinBox() self.font_size_lbl.setMaximum(100) self.font_size_lbl.setMinimum(1) self.font_size_lbl.setToolTip('Font size in pixels') choose_font = QPushButton('Choose font') choose_font.clicked.connect(self.choose_font) gallery_font.addWidget(self.font_lbl, 0, Qt.AlignLeft) gallery_font.addWidget(self.font_size_lbl, 0, Qt.AlignLeft) gallery_font.addWidget(choose_font, 0, Qt.AlignLeft) gallery_font.addWidget(Spacer('h'), 1, Qt.AlignLeft) # grid view / colors grid_colors_group = QGroupBox('Colors', grid_view_general_page) grid_view_layout.addWidget(grid_colors_group, 1, Qt.AlignTop) grid_colors_l = QFormLayout() grid_colors_group.setLayout(grid_colors_l) def color_lineedit(): l = QLineEdit() l.setPlaceholderText('Hex colors. Eg.: #323232') l.setMaximumWidth(200) return l self.grid_label_color = color_lineedit() self.grid_title_color = color_lineedit() self.grid_artist_color = color_lineedit() grid_colors_l.addRow('Label color:', self.grid_label_color) grid_colors_l.addRow('Title color:', self.grid_title_color) grid_colors_l.addRow('Artist color:', self.grid_artist_color) style_page = QWidget() visual.addTab(style_page, 'Style') visual.setTabEnabled(0, False) visual.setTabEnabled(2, False) visual.setCurrentIndex(1) # Advanced advanced = QTabWidget() self.advanced_index = self.right_panel.addWidget(advanced) advanced_misc = QWidget() advanced.addTab(advanced_misc, 'Misc') advanced_misc_main_layout = QVBoxLayout() advanced_misc.setLayout(advanced_misc_main_layout) misc_controls_layout = QFormLayout() misc_controls_layout.addWidget(QLabel('Options marked with * requires application restart')) advanced_misc_main_layout.addLayout(misc_controls_layout) # Advanced / Misc / Grid View misc_gridview = QGroupBox('Grid View') misc_controls_layout.addWidget(misc_gridview) misc_gridview_layout = QFormLayout() misc_gridview.setLayout(misc_gridview_layout) # Advanced / Misc / Grid View / scroll speed scroll_speed_spin_box = QSpinBox() scroll_speed_spin_box.setFixedWidth(60) scroll_speed_spin_box.setToolTip('Control the speed when scrolling in'+ ' grid view. DEFAULT: 7') scroll_speed_spin_box.setValue(self.scroll_speed) def scroll_speed(v): self.scroll_speed = v scroll_speed_spin_box.valueChanged[int].connect(scroll_speed) misc_gridview_layout.addRow('Scroll speed:', scroll_speed_spin_box) # Advanced / Misc / Grid View / cache size cache_size_spin_box = QSpinBox() cache_size_spin_box.setFixedWidth(120) cache_size_spin_box.setMaximum(999999999) cache_size_spin_box.setToolTip('This will greatly improve the grid view.' + ' Increase the value if you experience lag when scrolling'+ ' through galleries. DEFAULT: 200 MiB') def cache_size(c): self.cache_size = (self.cache_size[0], c) cache_size_spin_box.setValue(self.cache_size[1]) cache_size_spin_box.valueChanged[int].connect(cache_size) misc_gridview_layout.addRow('Cache Size (MiB):', cache_size_spin_box) # Advanced / Misc / Regex misc_search = QGroupBox('Search') misc_controls_layout.addWidget(misc_search) misc_search_layout = QFormLayout() misc_search.setLayout(misc_search_layout) search_allow_regex_l = QHBoxLayout() self.search_allow_regex = QCheckBox() self.search_allow_regex.setChecked(gui_constants.ALLOW_SEARCH_REGEX) self.search_allow_regex.adjustSize() self.search_allow_regex.setToolTip('A regex cheatsheet is located at About->Regex Cheatsheet') search_allow_regex_l.addWidget(self.search_allow_regex) search_allow_regex_l.addWidget(QLabel('A regex cheatsheet is located at About->Regex Cheatsheet')) search_allow_regex_l.addWidget(Spacer('h')) misc_search_layout.addRow('Regex:', search_allow_regex_l) # Advanced / Misc / Regex / autocomplete self.search_autocomplete = QCheckBox('*') self.search_autocomplete.setChecked(gui_constants.SEARCH_AUTOCOMPLETE) self.search_autocomplete.setToolTip('Turn autocomplete on/off') misc_search_layout.addRow('Autocomplete', self.search_autocomplete) # Advanced / Misc / Regex / search behaviour self.search_every_keystroke = QRadioButton('Search on every keystroke *', misc_search) misc_search_layout.addRow(self.search_every_keystroke) self.search_on_enter = QRadioButton('Search on enter-key *', misc_search) misc_search_layout.addRow(self.search_on_enter) # Advanced / Misc / External Viewer misc_external_viewer = QGroupBox('External Viewer') misc_controls_layout.addWidget(misc_external_viewer) misc_external_viewer_l = QFormLayout() misc_external_viewer.setLayout(misc_external_viewer_l) misc_external_viewer_l.addRow(QLabel(gui_constants.SUPPORTED_EXTERNAL_VIEWER_LBL)) self.external_viewer_path = PathLineEdit(misc_external_viewer, False) self.external_viewer_path.setPlaceholderText('Right/Left-click to open folder explorer.'+ ' Leave empty to use default viewer') self.external_viewer_path.setToolTip('Right/Left-click to open folder explorer.'+ ' Leave empty to use default viewer') self.external_viewer_path.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) misc_external_viewer_l.addRow('Path:', self.external_viewer_path) # Advanced / Database advanced_db_page = QWidget() advanced.addTab(advanced_db_page, 'Database') advanced.setTabEnabled(1, False) # About about = QTabWidget() self.about_index = self.right_panel.addWidget(about) about_happypanda_page = QWidget() about_troubleshoot_page = QWidget() about.addTab(about_happypanda_page, 'About Happypanda') about_layout = QVBoxLayout() about_happypanda_page.setLayout(about_layout) info_lbl = QLabel('<b>Author:</b> <a href=\'https://github.com/Pewpews\'>'+ 'Pewpews</a><br/>'+ 'Chat: <a href=\'https://gitter.im/Pewpews/happypanda\'>'+ 'Gitter chat</a><br/>'+ 'Email: [email protected]<br/>'+ '<b>Current version {}</b><br/>'.format(gui_constants.vs)+ 'Happypanda was created using:<br/>'+ '- Python 3.4<br/>'+ '- The Qt5 Framework') info_lbl.setOpenExternalLinks(True) about_layout.addWidget(info_lbl, 0, Qt.AlignTop) gpl_lbl = QLabel(gui_constants.GPL) gpl_lbl.setOpenExternalLinks(True) gpl_lbl.setWordWrap(True) about_layout.addWidget(gpl_lbl, 0, Qt.AlignTop) about_layout.addWidget(Spacer('v')) # About / Tags about_tags_page = QWidget() about.addTab(about_tags_page, 'Tags') about.setTabEnabled(1, False) # list of tags/namespaces here # About / Troubleshooting about.addTab(about_troubleshoot_page, 'Troubleshooting Guide') troubleshoot_layout = QVBoxLayout() about_troubleshoot_page.setLayout(troubleshoot_layout) guide_lbl = QLabel(gui_constants.TROUBLE_GUIDE) guide_lbl.setTextFormat(Qt.RichText) guide_lbl.setOpenExternalLinks(True) troubleshoot_layout.addWidget(guide_lbl, 0, Qt.AlignTop) troubleshoot_layout.addWidget(Spacer('v')) # About / Regex Cheatsheet about_s_regex = QGroupBox('Regex') about.addTab(about_s_regex, 'Regex Cheatsheet') about_s_regex_l = QFormLayout() about_s_regex.setLayout(about_s_regex_l) about_s_regex_l.addRow('\\\\\\\\', QLabel('Match literally \\')) about_s_regex_l.addRow('.', QLabel('Match any single character')) about_s_regex_l.addRow('^', QLabel('Start of string')) about_s_regex_l.addRow('$', QLabel('End of string')) about_s_regex_l.addRow('\\d', QLabel('Match any decimal digit')) about_s_regex_l.addRow('\\D', QLabel('Match any non-digit character')) about_s_regex_l.addRow('\\s', QLabel('Match any whitespace character')) about_s_regex_l.addRow('\\S', QLabel('Match any non-whitespace character')) about_s_regex_l.addRow('\\w', QLabel('Match any alphanumeric character')) about_s_regex_l.addRow('\\W', QLabel('Match any non-alphanumeric character')) about_s_regex_l.addRow('*', QLabel('Repeat previous character zero or more times')) about_s_regex_l.addRow('+', QLabel('Repeat previous character one or more times')) about_s_regex_l.addRow('?', QLabel('Repeat previous character one or zero times')) about_s_regex_l.addRow('{m, n}', QLabel('Repeat previous character atleast <i>m</i> times but no more than <i>n</i> times')) about_s_regex_l.addRow('(...)', QLabel('Match everything enclosed')) about_s_regex_l.addRow('(a|b)', QLabel('Match either a or b')) about_s_regex_l.addRow('[abc]', QLabel('Match a single character of: a, b or c')) about_s_regex_l.addRow('[^abc]', QLabel('Match a character except: a, b or c')) about_s_regex_l.addRow('[a-z]', QLabel('Match a character in the range')) about_s_regex_l.addRow('[^a-z]', QLabel('Match a character not in the range')) # About / Search tutorial about_search_scroll = QScrollArea() about_search_scroll.setBackgroundRole(QPalette.Base) about_search_scroll.setWidgetResizable(True) about_search_tut = QWidget() about.addTab(about_search_scroll, 'Search Guide') about_search_tut_l = QVBoxLayout() about_search_tut.setLayout(about_search_tut_l) # General about_search_general = QGroupBox('General') about_search_tut_l.addWidget(about_search_general) about_search_general_l = QFormLayout() about_search_general.setLayout(about_search_general_l) about_search_general_l.addRow(QLabel(gui_constants.SEARCH_TUTORIAL_GENERAL)) # Title & Author about_search_tit_aut = QGroupBox('Title and Author') about_search_tut_l.addWidget(about_search_tit_aut) about_search_tit_l = QFormLayout() about_search_tit_aut.setLayout(about_search_tit_l) about_search_tit_l.addRow(QLabel(gui_constants.SEARCH_TUTORIAL_TIT_AUT)) # Namespace & Tags about_search_tags = QGroupBox('Namespace and Tags') about_search_tut_l.addWidget(about_search_tags) about_search_tags_l = QFormLayout() about_search_tags.setLayout(about_search_tags_l) about_search_tags_l.addRow(QLabel(gui_constants.SEARCH_TUTORIAL_TAGS)) about_search_scroll.setWidget(about_search_tut) def add_folder_monitor(self, path=''): if not isinstance(path, str): path = '' l_edit = PathLineEdit() l_edit.setText(path) n = self.folders_layout.rowCount() + 1 self.folders_layout.addRow('Dir {}'.format(n), l_edit) def color_checker(self, txt): allow = False if len(txt) == 7: if txt[0] == '#': allow = True return allow def choose_font(self): tup = QFontDialog.getFont(self) font = tup[0] if tup[1]: self.font_lbl.setText(font.family()) self.font_size_lbl.setValue(font.pointSize()) def reject(self): self.close()
class DataStatistics(QDialog): def __init__(self, parent=None): super(DataStatistics, self).__init__(parent) self.initData() self.initModule() self.setModule() self.funcLink() self.drawGraph() self.analyzeSingleMemo() def initModule(self): self.setWindowTitle(u'数据统计') self.setWindowIcon(QIcon(css.dataBtnPath)) self.resize(500, 500) pg.setConfigOptions(foreground=QColor(113, 148, 116), antialias=True) self.btnframe = QFrame(self) self.mainframe = QFrame(self) self.btngroup = QButtonGroup(self.btnframe) self.stacklayout = QStackedLayout(self.mainframe) self.btn1 = QToolButton(self.btnframe) self.btn2 = QToolButton(self.btnframe) self.btngroup.addButton(self.btn1, 1) self.btngroup.addButton(self.btn2, 2) self.frame1 = QMainWindow() self.frame1_bar = QStatusBar() self.frame1.setStatusBar(self.frame1_bar) self.frame1_bar.showMessage(self.date + ': ' + str(readFinishRate(self.date)[2] * 100) + '%') self.frame2 = QMainWindow() self.frame2_bar = QStatusBar() self.frame2.setStatusBar(self.frame2_bar) self.frame2_bar.showMessage("坚持,就是每一天很难,可一年一年越来越容易。") self.stacklayout.addWidget(self.frame1) self.stacklayout.addWidget(self.frame2) def setModule(self): self.btnframe.setGeometry(0, 0, self.width(), 35) self.btnframe.setStyleSheet("border-color: rgb(0, 0, 0);") self.btnframe.setFrameShape(QFrame.Panel) self.btnframe.setFrameShadow(QFrame.Raised) self.mainframe.setGeometry(0, 35, self.width(), self.height() - self.btnframe.height()) self.btn1.setCheckable(True) self.btn1.setText("整体完成率") self.btn1.resize(100, 35) self.btn2.setCheckable(True) self.btn2.setText("单条完成情况") self.btn2.resize(100, 35) self.btn2.move(self.btn1.width(), 0) def funcLink(self): self.btn1.clicked.connect(self.showFrame1) self.btn2.clicked.connect(self.showFrame2) def initData(self): date = QDate.currentDate() self.date = date.toString(Qt.ISODate) self.statistics = readStatistics(css.statistics, self.date) def drawGraph(self): self.myplot = pg.PlotWidget(self.frame1, title='每日任务完成率') self.frame1.setCentralWidget(self.myplot) x = [] for key in self.statistics.keys(): x.append(key) points = [] for key in x: points.append(readFinishRate(key)[2]) tick_b = [list(zip(range(len(x)), x))] bottom = self.myplot.getAxis('bottom') bottom.setTicks(tick_b) self.myplot.setBackground((210, 240, 240)) # 背景色 self.myplot.showGrid(y=True) pen = pg.mkPen({'color': (155, 200, 160), 'width': 4}) # 画笔设置 self.myplot.plot(points[0:], clear=True, pen=pen, symbol='o', symbolBrush=QColor(113, 148, 116)) def analyzeSingleMemo(self): #data extract data = read(css.userdata) self.content = [] self.set_date = [] self.if_done = [] if data['memo_data']: for memo in data['memo_data']: self.content.append(memo['content']) self.set_date.append(memo['set_date']) self.if_done.append(memo['if_done']) #UI init self.ui = QWidget(self.frame2) self.option = QComboBox(self.ui) self.label0 = QLabel(self.ui) self.label1 = QLabel(self.ui) self.label2 = QLabel(self.ui) self.label3 = QLabel(self.ui) self.frame2.setCentralWidget(self.ui) #UI set self.option.setGeometry(10, 95, self.width() - 100, 40) self.option.setStyleSheet(css.combobox_style) self.option.addItems(self.content) self.option.currentIndexChanged.connect(self.getMemoMessage) self.label0.setGeometry(10, 5, self.width() - 100, 80) self.label1.setGeometry(10, 145, self.width() - 100, 80) self.label2.setGeometry(10, 235, self.width() - 100, 80) self.label3.setGeometry(10, 325, self.width() - 100, 80) self.label0.setStyleSheet(css.label_style) self.label1.setStyleSheet(css.label_style) self.label2.setStyleSheet(css.label_style) self.label3.setStyleSheet(css.label_style) self.label0.setText('选择需要查看的memo:') self.label1.setText('设立已 ' + str(getDateDiffer(self.set_date[0], self.date)) + ' 天') self.label2.setText('已完成 ' + str(len(self.if_done[0])) + ' 天') self.label3.setText('最大连续完成 ' + str(len(getLongest(self.if_done[0]))) + ' 天') def getMemoMessage(self): self.label1.setText('设立已 ' + str( getDateDiffer(self.set_date[self.option.currentIndex()], self.date)) + ' 天') self.label2.setText( '已完成 ' + str(len(self.if_done[self.option.currentIndex()])) + ' 天') self.label3.setText( '最大连续完成 ' + str(len(getLongest(self.if_done[self.option.currentIndex()]))) + ' 天') def showFrame1(self): if self.stacklayout.currentIndex() != 0: self.stacklayout.setCurrentIndex(0) def showFrame2(self): if self.stacklayout.currentIndex() != 1: self.stacklayout.setCurrentIndex(1)
class SettingsDialog(QWidget): "A settings dialog" scroll_speed_changed = pyqtSignal() init_gallery_rebuild = pyqtSignal(bool) init_gallery_eximport = pyqtSignal(object) def __init__(self, parent=None): super().__init__(parent, flags=Qt.Window) self.init_gallery_rebuild.connect(self.accept) self.parent_widget = parent self.setAttribute(Qt.WA_DeleteOnClose) self.resize(700, 500) self.restore_values() self.initUI() self.setWindowTitle('Settings') self.show() def initUI(self): main_layout = QVBoxLayout(self) sub_layout = QHBoxLayout() # Left Panel left_panel = QListWidget() left_panel.setViewMode(left_panel.ListMode) #left_panel.setIconSize(QSize(40,40)) left_panel.setTextElideMode(Qt.ElideRight) left_panel.setMaximumWidth(200) left_panel.itemClicked.connect(self.change) #web.setText('Web') self.application = QListWidgetItem() self.application.setText('Application') self.web = QListWidgetItem() self.web.setText('Web') self.visual = QListWidgetItem() self.visual.setText('Visual') self.advanced = QListWidgetItem() self.advanced.setText('Advanced') self.about = QListWidgetItem() self.about.setText('About') #main.setIcon(QIcon(os.path.join(app_constants.static_dir, 'plus2.png'))) left_panel.addItem(self.application) left_panel.addItem(self.web) left_panel.addItem(self.visual) left_panel.addItem(self.advanced) left_panel.addItem(self.about) left_panel.setMaximumWidth(100) # right panel self.right_panel = QStackedLayout() self.init_right_panel() # bottom bottom_layout = QHBoxLayout() ok_btn = QPushButton('Ok') ok_btn.clicked.connect(self.accept) cancel_btn = QPushButton('Cancel') cancel_btn.clicked.connect(self.close) info_lbl = QLabel() info_lbl.setText('<a href="https://github.com/Pewpews/happypanda">'+ 'Visit GitHub Repo</a> | Options marked with * requires application restart.') info_lbl.setTextFormat(Qt.RichText) info_lbl.setTextInteractionFlags(Qt.TextBrowserInteraction) info_lbl.setOpenExternalLinks(True) self.spacer = QWidget() self.spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) bottom_layout.addWidget(info_lbl, 0, Qt.AlignLeft) bottom_layout.addWidget(self.spacer) bottom_layout.addWidget(ok_btn, 0, Qt.AlignRight) bottom_layout.addWidget(cancel_btn, 0, Qt.AlignRight) sub_layout.addWidget(left_panel) sub_layout.addLayout(self.right_panel) main_layout.addLayout(sub_layout) main_layout.addLayout(bottom_layout) self.restore_options() def change(self, item): def curr_index(index): if index != self.right_panel.currentIndex(): self.right_panel.setCurrentIndex(index) if item == self.application: curr_index(self.application_index) elif item == self.web: curr_index(self.web_index) elif item == self.visual: curr_index(self.visual_index) elif item == self.advanced: curr_index(self.advanced_index) elif item == self.about: curr_index(self.about_index) def restore_values(self): #Web self.exprops = settings.ExProperties() # Visual self.high_quality_thumbs = app_constants.HIGH_QUALITY_THUMBS self.popup_width = app_constants.POPUP_WIDTH self.popup_height = app_constants.POPUP_HEIGHT self.style_sheet = app_constants.user_stylesheet_path # Advanced self.scroll_speed = app_constants.SCROLL_SPEED self.cache_size = app_constants.THUMBNAIL_CACHE_SIZE self.prefetch_item_amnt = app_constants.PREFETCH_ITEM_AMOUNT def restore_options(self): # App / General self.subfolder_as_chapters.setChecked(app_constants.SUBFOLDER_AS_GALLERY) self.extract_gallery_before_opening.setChecked(app_constants.EXTRACT_CHAPTER_BEFORE_OPENING) self.open_galleries_sequentially.setChecked(app_constants.OPEN_GALLERIES_SEQUENTIALLY) self.scroll_to_new_gallery.setChecked(app_constants.SCROLL_TO_NEW_GALLERIES) self.move_imported_gs.setChecked(app_constants.MOVE_IMPORTED_GALLERIES) self.move_imported_def_path.setText(app_constants.IMPORTED_GALLERY_DEF_PATH) self.open_random_g_chapters.setChecked(app_constants.OPEN_RANDOM_GALLERY_CHAPTERS) self.rename_g_source_group.setChecked(app_constants.RENAME_GALLERY_SOURCE) self.path_to_unrar.setText(app_constants.unrar_tool_path) # App / General / External Viewer self.external_viewer_path.setText(app_constants.EXTERNAL_VIEWER_PATH) # App / Monitor / Misc self.enable_monitor.setChecked(app_constants.ENABLE_MONITOR) self.look_new_gallery_startup.setChecked(app_constants.LOOK_NEW_GALLERY_STARTUP) self.auto_add_new_galleries.setChecked(app_constants.LOOK_NEW_GALLERY_AUTOADD) # App / Monitor / Folders for path in app_constants.MONITOR_PATHS: self.add_folder_monitor(path) # App / Monitor / Ignore list for path in app_constants.IGNORE_PATHS: self.add_ignore_path(path) # Web / General if 'g.e-hentai' in app_constants.DEFAULT_EHEN_URL: self.default_ehen_url.setChecked(True) else: self.exhentai_ehen_url.setChecked(True) self.replace_metadata.setChecked(app_constants.REPLACE_METADATA) self.always_first_hit.setChecked(app_constants.ALWAYS_CHOOSE_FIRST_HIT) self.web_time_offset.setValue(app_constants.GLOBAL_EHEN_TIME) self.continue_a_metadata_fetcher.setChecked(app_constants.CONTINUE_AUTO_METADATA_FETCHER) self.use_jpn_title.setChecked(app_constants.USE_JPN_TITLE) self.use_gallery_link.setChecked(app_constants.USE_GALLERY_LINK) # Web / Download if app_constants.HEN_DOWNLOAD_TYPE == 0: self.archive_download.setChecked(True) else: self.torrent_download.setChecked(True) self.download_directory.setText(app_constants.DOWNLOAD_DIRECTORY) self.torrent_client.setText(app_constants.TORRENT_CLIENT) # Web / Exhentai self.ipbid_edit.setText(self.exprops.ipb_id) self.ipbpass_edit.setText(self.exprops.ipb_pass) # Visual / Grid View / Tooltip self.grid_tooltip_group.setChecked(app_constants.GRID_TOOLTIP) self.visual_grid_tooltip_title.setChecked(app_constants.TOOLTIP_TITLE) self.visual_grid_tooltip_author.setChecked(app_constants.TOOLTIP_AUTHOR) self.visual_grid_tooltip_chapters.setChecked(app_constants.TOOLTIP_CHAPTERS) self.visual_grid_tooltip_status.setChecked(app_constants.TOOLTIP_STATUS) self.visual_grid_tooltip_type.setChecked(app_constants.TOOLTIP_TYPE) self.visual_grid_tooltip_lang.setChecked(app_constants.TOOLTIP_LANG) self.visual_grid_tooltip_descr.setChecked(app_constants.TOOLTIP_DESCR) self.visual_grid_tooltip_tags.setChecked(app_constants.TOOLTIP_TAGS) self.visual_grid_tooltip_last_read.setChecked(app_constants.TOOLTIP_LAST_READ) self.visual_grid_tooltip_times_read.setChecked(app_constants.TOOLTIP_TIMES_READ) self.visual_grid_tooltip_pub_date.setChecked(app_constants.TOOLTIP_PUB_DATE) self.visual_grid_tooltip_date_added.setChecked(app_constants.TOOLTIP_DATE_ADDED) # Visual / Grid View / Gallery self.external_viewer_ico.setChecked(app_constants.USE_EXTERNAL_PROG_ICO) self.gallery_type_ico.setChecked(app_constants.DISPLAY_GALLERY_TYPE) if app_constants.GALLERY_FONT_ELIDE: self.gallery_text_elide.setChecked(True) else: self.gallery_text_fit.setChecked(True) self.font_lbl.setText(app_constants.GALLERY_FONT[0]) self.font_size_lbl.setValue(app_constants.GALLERY_FONT[1]) def re_enforce(s): if s: self.search_on_enter.setChecked(True) self.search_allow_regex.clicked.connect(re_enforce) if app_constants.SEARCH_ON_ENTER: self.search_on_enter.setChecked(True) else: self.search_every_keystroke.setChecked(True) # Visual / Grid View / Colors self.grid_label_color.setText(app_constants.GRID_VIEW_LABEL_COLOR) self.grid_title_color.setText(app_constants.GRID_VIEW_TITLE_COLOR) self.grid_artist_color.setText(app_constants.GRID_VIEW_ARTIST_COLOR) # Advanced / Gallery / Gallery Text Fixer self.g_data_regex_fix_edit.setText(app_constants.GALLERY_DATA_FIX_REGEX) self.g_data_replace_fix_edit.setText(app_constants.GALLERY_DATA_FIX_REPLACE) self.g_data_fixer_title.setChecked(app_constants.GALLERY_DATA_FIX_TITLE) self.g_data_fixer_artist.setChecked(app_constants.GALLERY_DATA_FIX_ARTIST) # About / DB Overview self.tags_treeview_on_start.setChecked(app_constants.TAGS_TREEVIEW_ON_START) def accept(self): set = settings.set # App / General / Gallery app_constants.SUBFOLDER_AS_GALLERY = self.subfolder_as_chapters.isChecked() set(app_constants.SUBFOLDER_AS_GALLERY, 'Application', 'subfolder as gallery') app_constants.EXTRACT_CHAPTER_BEFORE_OPENING = self.extract_gallery_before_opening.isChecked() set(app_constants.EXTRACT_CHAPTER_BEFORE_OPENING, 'Application', 'extract chapter before opening') app_constants.OPEN_GALLERIES_SEQUENTIALLY = self.open_galleries_sequentially.isChecked() set(app_constants.OPEN_GALLERIES_SEQUENTIALLY, 'Application', 'open galleries sequentially') app_constants.SCROLL_TO_NEW_GALLERIES = self.scroll_to_new_gallery.isChecked() set(app_constants.SCROLL_TO_NEW_GALLERIES, 'Application', 'scroll to new galleries') app_constants.MOVE_IMPORTED_GALLERIES = self.move_imported_gs.isChecked() set(app_constants.MOVE_IMPORTED_GALLERIES, 'Application', 'move imported galleries') if not self.move_imported_def_path.text() or os.path.exists(self.move_imported_def_path.text()): app_constants.IMPORTED_GALLERY_DEF_PATH = self.move_imported_def_path.text() set(app_constants.IMPORTED_GALLERY_DEF_PATH, 'Application', 'imported gallery def path') app_constants.OPEN_RANDOM_GALLERY_CHAPTERS = self.open_random_g_chapters.isChecked() set(app_constants.OPEN_RANDOM_GALLERY_CHAPTERS, 'Application', 'open random gallery chapters') app_constants.RENAME_GALLERY_SOURCE = self.rename_g_source_group.isChecked() set(app_constants.RENAME_GALLERY_SOURCE, 'Application', 'rename gallery source') app_constants.unrar_tool_path = self.path_to_unrar.text() set(app_constants.unrar_tool_path, 'Application', 'unrar tool path') # App / General / Search app_constants.ALLOW_SEARCH_REGEX = self.search_allow_regex.isChecked() set(app_constants.ALLOW_SEARCH_REGEX, 'Application', 'allow search regex') app_constants.SEARCH_AUTOCOMPLETE = self.search_autocomplete.isChecked() set(app_constants.SEARCH_AUTOCOMPLETE, 'Application', 'search autocomplete') if self.search_on_enter.isChecked(): app_constants.SEARCH_ON_ENTER = True else: app_constants.SEARCH_ON_ENTER = False set(app_constants.SEARCH_ON_ENTER, 'Application', 'search on enter') # App / General / External Viewer if not self.external_viewer_path.text(): app_constants.USE_EXTERNAL_VIEWER = False set(False, 'Application', 'use external viewer') else: app_constants.USE_EXTERNAL_VIEWER = True set(True, 'Application', 'use external viewer') app_constants._REFRESH_EXTERNAL_VIEWER = True app_constants.EXTERNAL_VIEWER_PATH = self.external_viewer_path.text() set(app_constants.EXTERNAL_VIEWER_PATH,'Application', 'external viewer path') # App / Monitor / misc app_constants.ENABLE_MONITOR = self.enable_monitor.isChecked() set(app_constants.ENABLE_MONITOR, 'Application', 'enable monitor') app_constants.LOOK_NEW_GALLERY_STARTUP = self.look_new_gallery_startup.isChecked() set(app_constants.LOOK_NEW_GALLERY_STARTUP, 'Application', 'look new gallery startup') app_constants.LOOK_NEW_GALLERY_AUTOADD = self.auto_add_new_galleries.isChecked() set(app_constants.LOOK_NEW_GALLERY_AUTOADD, 'Application', 'look new gallery autoadd') # App / Monitor / folders paths = [] folder_p_widgets = self.take_all_layout_widgets(self.folders_layout) for x, l_edit in enumerate(folder_p_widgets): p = l_edit.text() if p: paths.append(p) set(paths, 'Application', 'monitor paths') app_constants.MONITOR_PATHS = paths # App / Monitor / ignore list paths = [] ignore_p_widgets = self.take_all_layout_widgets(self.ignore_path_l) for x, l_edit in enumerate(ignore_p_widgets): p = l_edit.text() if p: paths.append(p) set(paths, 'Application', 'ignore paths') app_constants.IGNORE_PATHS = paths # Web / Downloader if self.archive_download.isChecked(): app_constants.HEN_DOWNLOAD_TYPE = 0 else: app_constants.HEN_DOWNLOAD_TYPE = 1 set(app_constants.HEN_DOWNLOAD_TYPE, 'Web', 'hen download type') app_constants.DOWNLOAD_DIRECTORY = self.download_directory.text() set(app_constants.DOWNLOAD_DIRECTORY, 'Web', 'download directory') app_constants.TORRENT_CLIENT = self.torrent_client.text() set(app_constants.TORRENT_CLIENT, 'Web', 'torrent client') # Web / Metdata if self.default_ehen_url.isChecked(): app_constants.DEFAULT_EHEN_URL = 'http://g.e-hentai.org/' else: app_constants.DEFAULT_EHEN_URL = 'http://exhentai.org/' set(app_constants.DEFAULT_EHEN_URL, 'Web', 'default ehen url') app_constants.REPLACE_METADATA = self.replace_metadata.isChecked() set(app_constants.REPLACE_METADATA, 'Web', 'replace metadata') app_constants.ALWAYS_CHOOSE_FIRST_HIT = self.always_first_hit.isChecked() set(app_constants.ALWAYS_CHOOSE_FIRST_HIT, 'Web', 'always choose first hit') app_constants.GLOBAL_EHEN_TIME = self.web_time_offset.value() set(app_constants.GLOBAL_EHEN_TIME, 'Web', 'global ehen time offset') app_constants.CONTINUE_AUTO_METADATA_FETCHER = self.continue_a_metadata_fetcher.isChecked() set(app_constants.CONTINUE_AUTO_METADATA_FETCHER, 'Web', 'continue auto metadata fetcher') app_constants.USE_JPN_TITLE = self.use_jpn_title.isChecked() set(app_constants.USE_JPN_TITLE, 'Web', 'use jpn title') app_constants.USE_GALLERY_LINK = self.use_gallery_link.isChecked() set(app_constants.USE_GALLERY_LINK, 'Web', 'use gallery link') # Web / ExHentai self.exprops.ipb_id = self.ipbid_edit.text() self.exprops.ipb_pass = self.ipbpass_edit.text() # Visual / Grid View / Tooltip app_constants.GRID_TOOLTIP = self.grid_tooltip_group.isChecked() set(app_constants.GRID_TOOLTIP, 'Visual', 'grid tooltip') app_constants.TOOLTIP_TITLE = self.visual_grid_tooltip_title.isChecked() set(app_constants.TOOLTIP_TITLE, 'Visual', 'tooltip title') app_constants.TOOLTIP_AUTHOR = self.visual_grid_tooltip_author.isChecked() set(app_constants.TOOLTIP_AUTHOR, 'Visual', 'tooltip author') app_constants.TOOLTIP_CHAPTERS = self.visual_grid_tooltip_chapters.isChecked() set(app_constants.TOOLTIP_CHAPTERS, 'Visual', 'tooltip chapters') app_constants.TOOLTIP_STATUS = self.visual_grid_tooltip_status.isChecked() set(app_constants.TOOLTIP_STATUS, 'Visual', 'tooltip status') app_constants.TOOLTIP_TYPE = self.visual_grid_tooltip_type.isChecked() set(app_constants.TOOLTIP_TYPE, 'Visual', 'tooltip type') app_constants.TOOLTIP_LANG = self.visual_grid_tooltip_lang.isChecked() set(app_constants.TOOLTIP_LANG, 'Visual', 'tooltip lang') app_constants.TOOLTIP_DESCR = self.visual_grid_tooltip_descr.isChecked() set(app_constants.TOOLTIP_DESCR, 'Visual', 'tooltip descr') app_constants.TOOLTIP_TAGS = self.visual_grid_tooltip_tags.isChecked() set(app_constants.TOOLTIP_TAGS, 'Visual', 'tooltip tags') app_constants.TOOLTIP_LAST_READ = self.visual_grid_tooltip_last_read.isChecked() set(app_constants.TOOLTIP_LAST_READ, 'Visual', 'tooltip last read') app_constants.TOOLTIP_TIMES_READ = self.visual_grid_tooltip_times_read.isChecked() set(app_constants.TOOLTIP_TIMES_READ, 'Visual', 'tooltip times read') app_constants.TOOLTIP_PUB_DATE = self.visual_grid_tooltip_pub_date.isChecked() set(app_constants.TOOLTIP_PUB_DATE, 'Visual', 'tooltip pub date') app_constants.TOOLTIP_DATE_ADDED = self.visual_grid_tooltip_date_added.isChecked() set(app_constants.TOOLTIP_DATE_ADDED, 'Visual', 'tooltip date added') # Visual / Grid View / Gallery app_constants.USE_EXTERNAL_PROG_ICO = self.external_viewer_ico.isChecked() set(app_constants.USE_EXTERNAL_PROG_ICO, 'Visual', 'use external prog ico') app_constants.DISPLAY_GALLERY_TYPE = self.gallery_type_ico.isChecked() set(app_constants.DISPLAY_GALLERY_TYPE, 'Visual', 'display gallery type') if self.gallery_text_elide.isChecked(): app_constants.GALLERY_FONT_ELIDE = True else: app_constants.GALLERY_FONT_ELIDE = False set(app_constants.GALLERY_FONT_ELIDE, 'Visual', 'gallery font elide') app_constants.GALLERY_FONT = (self.font_lbl.text(), self.font_size_lbl.value()) set(app_constants.GALLERY_FONT[0], 'Visual', 'gallery font family') set(app_constants.GALLERY_FONT[1], 'Visual', 'gallery font size') # Visual / Grid View / Colors if self.color_checker(self.grid_title_color.text()): app_constants.GRID_VIEW_TITLE_COLOR = self.grid_title_color.text() set(app_constants.GRID_VIEW_TITLE_COLOR, 'Visual', 'grid view title color') if self.color_checker(self.grid_artist_color.text()): app_constants.GRID_VIEW_ARTIST_COLOR = self.grid_artist_color.text() set(app_constants.GRID_VIEW_ARTIST_COLOR, 'Visual', 'grid view artist color') if self.color_checker(self.grid_label_color.text()): app_constants.GRID_VIEW_LABEL_COLOR = self.grid_label_color.text() set(app_constants.GRID_VIEW_LABEL_COLOR, 'Visual', 'grid view label color') # Advanced / Misc # Advanced / Misc / Grid View app_constants.SCROLL_SPEED = self.scroll_speed set(self.scroll_speed, 'Advanced', 'scroll speed') self.scroll_speed_changed.emit() app_constants.THUMBNAIL_CACHE_SIZE = self.cache_size set(self.cache_size[1], 'Advanced', 'cache size') QPixmapCache.setCacheLimit(self.cache_size[0]* self.cache_size[1]) # Advanced / General / Gallery Text Fixer app_constants.GALLERY_DATA_FIX_REGEX = self.g_data_regex_fix_edit.text() set(app_constants.GALLERY_DATA_FIX_REGEX, 'Advanced', 'gallery data fix regex') app_constants.GALLERY_DATA_FIX_TITLE = self.g_data_fixer_title.isChecked() set(app_constants.GALLERY_DATA_FIX_TITLE, 'Advanced', 'gallery data fix title') app_constants.GALLERY_DATA_FIX_ARTIST = self.g_data_fixer_artist.isChecked() set(app_constants.GALLERY_DATA_FIX_ARTIST, 'Advanced', 'gallery data fix artist') app_constants.GALLERY_DATA_FIX_REPLACE = self.g_data_replace_fix_edit.text() set(app_constants.GALLERY_DATA_FIX_REPLACE, 'Advanced', 'gallery data fix replace') # About / DB Overview app_constants.TAGS_TREEVIEW_ON_START = self.tags_treeview_on_start.isChecked() set(app_constants.TAGS_TREEVIEW_ON_START, 'Application', 'tags treeview on start') settings.save() self.close() def init_right_panel(self): #def title_def(title): # title_lbl = QLabel(title) # f = QFont() # f.setPixelSize(16) # title_lbl.setFont(f) # return title_lbl def groupbox(name, layout, parent, add_in_layout=None): """ Makes a groupbox and a layout for you Returns groupbox and layout """ g = QGroupBox(name, parent) l = layout(g) if add_in_layout: if isinstance(add_in_layout, QFormLayout): add_in_layout.addRow(g) else: add_in_layout.addWidget(g) return g, l def option_lbl_checkbox(text, optiontext, parent=None): l = QLabel(text) c = QCheckBox(text, parent) return l, c def new_tab(name, parent, scroll=False): """ Creates a new tab. Returns new tab page widget and it's layout """ new_t = QWidget(parent) new_l = QFormLayout(new_t) if scroll: scr = QScrollArea(parent) scr.setBackgroundRole(QPalette.Base) scr.setWidget(new_t) scr.setWidgetResizable(True) parent.addTab(scr, name) return new_t, new_l else: parent.addTab(new_t, name) return new_t, new_l # App application = QTabWidget(self) self.application_index = self.right_panel.addWidget(application) application_general, app_general_m_l = new_tab('General', application, True) # App / General / gallery app_gallery_page, app_gallery_l = new_tab('Gallery', application, True) self.subfolder_as_chapters = QCheckBox("Subdirectiories should be treated as standalone galleries instead of chapters (applies in archives too)") self.subfolder_as_chapters.setToolTip("This option will enable creating standalone galleries for each subdirectiories found recursively when importing."+ "\nDefault action is treating each subfolder found as chapters of a gallery.") extract_gallery_info = QLabel("Note: This option has no effect when turned off if path to viewer is not specified.") self.extract_gallery_before_opening = QCheckBox("Extract archive before opening (only turn off if your viewer supports it)") self.open_galleries_sequentially = QCheckBox("Open chapters sequentially (Note: has no effect if path to viewer is not specified)") subf_info = QLabel("Behaviour of 'Scan for new galleries on startup' option will be affected.") subf_info.setWordWrap(True) app_gallery_l.addRow('Note:', subf_info) app_gallery_l.addRow(self.subfolder_as_chapters) app_gallery_l.addRow(extract_gallery_info) app_gallery_l.addRow(self.extract_gallery_before_opening) app_gallery_l.addRow(self.open_galleries_sequentially) self.scroll_to_new_gallery = QCheckBox("Scroll to newly added gallery") self.scroll_to_new_gallery.setDisabled(True) app_gallery_l.addRow(self.scroll_to_new_gallery) self.move_imported_gs, move_imported_gs_l = groupbox('Move imported galleries', QFormLayout, app_gallery_page) self.move_imported_gs.setCheckable(True) self.move_imported_gs.setToolTip("Move imported galleries to specified folder.") self.move_imported_def_path = PathLineEdit() move_imported_gs_l.addRow('Directory:', self.move_imported_def_path) app_gallery_l.addRow(self.move_imported_gs) self.rename_g_source_group, rename_g_source_l = groupbox('Rename gallery source', QFormLayout, app_gallery_page) self.rename_g_source_group.setCheckable(True) self.rename_g_source_group.setDisabled(True) app_gallery_l.addRow(self.rename_g_source_group) rename_g_source_l.addRow(QLabel("Check what to include when renaming gallery source. (Same order)")) rename_g_source_flow_l = FlowLayout() rename_g_source_l.addRow(rename_g_source_flow_l) self.rename_artist = QCheckBox("Artist") self.rename_title = QCheckBox("Title") self.rename_lang = QCheckBox("Language") self.rename_title.setChecked(True) self.rename_title.setDisabled(True) rename_g_source_flow_l.addWidget(self.rename_artist) rename_g_source_flow_l.addWidget(self.rename_title) rename_g_source_flow_l.addWidget(self.rename_lang) random_gallery_opener, random_g_opener_l = groupbox('Random Gallery Opener', QFormLayout, app_gallery_page) app_gallery_l.addRow(random_gallery_opener) self.open_random_g_chapters = QCheckBox("Open random gallery chapters") random_g_opener_l.addRow(self.open_random_g_chapters) # App / General / Search app_search, app_search_layout = groupbox('Search', QFormLayout, application_general) app_general_m_l.addRow(app_search) search_allow_regex_l = QHBoxLayout() self.search_allow_regex = QCheckBox() self.search_allow_regex.setChecked(app_constants.ALLOW_SEARCH_REGEX) self.search_allow_regex.adjustSize() self.search_allow_regex.setToolTip('A regex cheatsheet is located at About->Regex Cheatsheet') search_allow_regex_l.addWidget(self.search_allow_regex) search_allow_regex_l.addWidget(QLabel('A regex cheatsheet is located at About->Regex Cheatsheet')) search_allow_regex_l.addWidget(Spacer('h')) app_search_layout.addRow('Regex:', search_allow_regex_l) # App / General / Search / autocomplete self.search_autocomplete = QCheckBox('*') self.search_autocomplete.setChecked(app_constants.SEARCH_AUTOCOMPLETE) self.search_autocomplete.setToolTip('Turn autocomplete on/off') app_search_layout.addRow('Autocomplete', self.search_autocomplete) # App / General / Search / search behaviour self.search_every_keystroke = QRadioButton('Search on every keystroke *', app_search) app_search_layout.addRow(self.search_every_keystroke) self.search_on_enter = QRadioButton('Search on return-key *', app_search) app_search_layout.addRow(self.search_on_enter) # App / General / External Viewer app_external_viewer, app_external_viewer_l = groupbox('External Viewer', QFormLayout, application_general, app_general_m_l) app_external_viewer_l.addRow(QLabel("Most image viewers should work. Incase it doesn't," + " hit me up on email/github/gitter-chat to add support.")) self.external_viewer_path = PathLineEdit(app_external_viewer, False, '') self.external_viewer_path.setPlaceholderText('Right/Left-click to open folder explorer.'+ ' Leave empty to use default viewer') self.external_viewer_path.setToolTip('Right/Left-click to open folder explorer.'+ ' Leave empty to use default viewer') self.external_viewer_path.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) app_external_viewer_l.addRow('Path:', self.external_viewer_path) # App / General / Rar Support app_rar_group, app_rar_layout = groupbox('RAR Support *', QFormLayout, self) app_general_m_l.addRow(app_rar_group) rar_info = QLabel('Specify the path to the unrar tool to enable rar support.\n'+ 'Windows: "unrar.exe" should be in the "bin" directory if you installed from the'+ ' self-extracting archive provided on github.\nOSX: You can install this via HomeBrew.'+ ' Path should be something like: "/usr/local/bin/unrar".\nLinux: Should already be'+ ' installed. You can just type "unrar". If it\'s not installed, use your package manager: pacman -S unrar') rar_info.setWordWrap(True) app_rar_layout.addRow(rar_info) self.path_to_unrar = PathLineEdit(self, False, filters='') app_rar_layout.addRow('UnRAR tool path:', self.path_to_unrar) # App / Monitor app_monitor_page = QScrollArea() app_monitor_page.setBackgroundRole(QPalette.Base) app_monitor_dummy = QWidget() app_monitor_page.setWidgetResizable(True) app_monitor_page.setWidget(app_monitor_dummy) application.addTab(app_monitor_page, 'Monitoring') app_monitor_m_l = QVBoxLayout(app_monitor_dummy) # App / Monitor / misc app_monitor_misc_group = QGroupBox('General *', self) app_monitor_m_l.addWidget(app_monitor_misc_group) app_monitor_misc_m_l = QFormLayout(app_monitor_misc_group) monitor_info = QLabel('Directory monitoring will monitor the specified directories for any'+ ' filesystem events. For example if you delete a gallery source in one of your'+ ' monitored directories the application will inform you and ask if'+ ' you want to delete the gallery from the application as well.') monitor_info.setWordWrap(True) app_monitor_misc_m_l.addRow(monitor_info) self.enable_monitor = QCheckBox('Enable directory monitoring') app_monitor_misc_m_l.addRow(self.enable_monitor) self.look_new_gallery_startup = QGroupBox('Scan for new galleries on startup', self) app_monitor_misc_m_l.addRow(self.look_new_gallery_startup) self.look_new_gallery_startup.setCheckable(True) look_new_gallery_startup_m_l = QVBoxLayout(self.look_new_gallery_startup) self.auto_add_new_galleries = QCheckBox('Automatically add found galleries') look_new_gallery_startup_m_l.addWidget(self.auto_add_new_galleries) # App / Monitor / folders app_monitor_group = QGroupBox('Directories *', self) app_monitor_m_l.addWidget(app_monitor_group, 1) app_monitor_folders_m_l = QVBoxLayout(app_monitor_group) app_monitor_folders_add = QPushButton('+') app_monitor_folders_add.clicked.connect(self.add_folder_monitor) app_monitor_folders_add.setMaximumWidth(20) app_monitor_folders_add.setMaximumHeight(20) app_monitor_folders_m_l.addWidget(app_monitor_folders_add, 0, Qt.AlignRight) self.folders_layout = QFormLayout() app_monitor_folders_m_l.addLayout(self.folders_layout) # App / Ignore app_ignore, app_ignore_m_l = new_tab('Ignore', application, True) app_ignore_group, app_ignore_list_l = groupbox('List', QVBoxLayout, app_monitor_dummy) app_ignore_m_l.addRow(app_ignore_group) add_buttons_l = QHBoxLayout() app_ignore_add_a = QPushButton('Add archive') app_ignore_add_a.clicked.connect(lambda: self.add_ignore_path(dir=False)) app_ignore_add_f = QPushButton('Add directory') app_ignore_add_f.clicked.connect(self.add_ignore_path) add_buttons_l.addWidget(app_ignore_add_a, 0, Qt.AlignRight) add_buttons_l.addWidget(app_ignore_add_f, 1, Qt.AlignRight) app_ignore_list_l.addLayout(add_buttons_l) self.ignore_path_l = QFormLayout() app_ignore_list_l.addLayout(self.ignore_path_l) # Web web = QTabWidget(self) self.web_index = self.right_panel.addWidget(web) # Web / Downloader web_downloader, web_downloader_l = new_tab('Downloader', web) hen_download_group, hen_download_group_l = groupbox('g.e-hentai/exhentai', QFormLayout, web_downloader) web_downloader_l.addRow(hen_download_group) self.archive_download = QRadioButton('Archive', hen_download_group) self.torrent_download = QRadioButton('Torrent', hen_download_group) download_type_l = QHBoxLayout() download_type_l.addWidget(self.archive_download) download_type_l.addWidget(self.torrent_download, 1) hen_download_group_l.addRow('Download Type:', download_type_l) self.download_directory = PathLineEdit(web_downloader) web_downloader_l.addRow('Destination:', self.download_directory) self.torrent_client = PathLineEdit(web_downloader, False, '') web_downloader_l.addRow(QLabel("Leave empty to use default torrent client."+ "\nIt is NOT recommended to import a file while it's still downloading.")) web_downloader_l.addRow('Torrent client:', self.torrent_client) # Web / Metadata web_metadata_page = QScrollArea() web_metadata_page.setBackgroundRole(QPalette.Base) web_metadata_page.setWidgetResizable(True) web.addTab(web_metadata_page, 'Metadata') web_metadata_dummy = QWidget() web_metadata_page.setWidget(web_metadata_dummy) web_metadata_m_l = QFormLayout(web_metadata_dummy) self.default_ehen_url = QRadioButton('g.e-hentai.org', web_metadata_page) self.exhentai_ehen_url = QRadioButton('exhentai.org', web_metadata_page) ehen_url_l = QHBoxLayout() ehen_url_l.addWidget(self.default_ehen_url) ehen_url_l.addWidget(self.exhentai_ehen_url, 1) web_metadata_m_l.addRow('Default URL:', ehen_url_l) self.continue_a_metadata_fetcher = QCheckBox('Continue from where auto metadata fetcher left off') web_metadata_m_l.addRow(self.continue_a_metadata_fetcher) self.use_jpn_title = QCheckBox('Use japanese title') self.use_jpn_title.setToolTip('Choose the japenese title over the english one') web_metadata_m_l.addRow(self.use_jpn_title) time_offset_info = QLabel('We need to impose a delay between our requests to avoid getting banned.'+ ' I have made it so you cannot set the delay lower than the recommended (I don\'t'+ ' want you to get banned, anon!).\nSpecify the delay between requests in seconds.') time_offset_info.setWordWrap(True) self.web_time_offset = QSpinBox() self.web_time_offset.setMaximumWidth(40) self.web_time_offset.setMinimum(4) self.web_time_offset.setMaximum(99) web_metadata_m_l.addRow(time_offset_info) web_metadata_m_l.addRow('Requests delay in seconds', self.web_time_offset) replace_metadata_info = QLabel('When fetching for metadata the new metadata will be appended'+ ' to the gallery by default. This means that new data will only be added if'+ ' the field was empty. There is however a special case for namespace & tags.'+ ' We go through all the new namespace & tags to only add those that'+ ' do not already exists.\n\nEnabling this option makes it so that a gallery\'s old data'+ ' are deleted and replaced with the new data.') replace_metadata_info.setWordWrap(True) self.replace_metadata = QCheckBox('Replace old metadata with new metadata') web_metadata_m_l.addRow(replace_metadata_info) web_metadata_m_l.addRow(self.replace_metadata) first_hit_info = QLabel('By default, you get to choose which gallery to extract metadata from when'+ ' there is more than one gallery found when searching.\n'+ 'Enabling this option makes it choose the first hit, saving you from moving your mouse.') first_hit_info.setWordWrap(True) self.always_first_hit = QCheckBox('Always choose first hit') web_metadata_m_l.addRow(first_hit_info) web_metadata_m_l.addRow(self.always_first_hit) self.use_gallery_link = QCheckBox('Use current gallery link') self.use_gallery_link.setToolTip("Metadata will be fetched from the current gallery link"+ " if it's a valid ex/g.e gallery url") web_metadata_m_l.addRow(self.use_gallery_link) # Web / Exhentai exhentai_page = QWidget(self) web.addTab(exhentai_page, 'ExHentai') ipb_layout = QFormLayout() exhentai_page.setLayout(ipb_layout) self.ipbid_edit = QLineEdit() self.ipbpass_edit = QLineEdit() exh_tutorial = QLabel(app_constants.EXHEN_COOKIE_TUTORIAL) exh_tutorial.setTextFormat(Qt.RichText) ipb_layout.addRow('IPB Member ID:', self.ipbid_edit) ipb_layout.addRow('IPB Pass Hash:', self.ipbpass_edit) ipb_layout.addRow(exh_tutorial) # Visual visual = QTabWidget(self) self.visual_index = self.right_panel.addWidget(visual) visual_general_page = QWidget() visual.addTab(visual_general_page, 'General') grid_view_general_page = QWidget() visual.addTab(grid_view_general_page, 'Grid View') grid_view_layout = QVBoxLayout() grid_view_layout.addWidget(QLabel('Options marked with * requires application restart'), 0, Qt.AlignTop) grid_view_general_page.setLayout(grid_view_layout) # grid view # grid view / tooltip self.grid_tooltip_group = QGroupBox('Tooltip', grid_view_general_page) self.grid_tooltip_group.setCheckable(True) grid_view_layout.addWidget(self.grid_tooltip_group, 0, Qt.AlignTop) grid_tooltip_layout = QFormLayout() self.grid_tooltip_group.setLayout(grid_tooltip_layout) grid_tooltip_layout.addRow(QLabel('Control what is'+ ' displayed in the tooltip when hovering a gallery')) grid_tooltips_hlayout = FlowLayout() grid_tooltip_layout.addRow(grid_tooltips_hlayout) self.visual_grid_tooltip_title = QCheckBox('Title') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_title) self.visual_grid_tooltip_author = QCheckBox('Author') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_author) self.visual_grid_tooltip_chapters = QCheckBox('Chapters') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_chapters) self.visual_grid_tooltip_status = QCheckBox('Status') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_status) self.visual_grid_tooltip_type = QCheckBox('Type') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_type) self.visual_grid_tooltip_lang = QCheckBox('Language') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_lang) self.visual_grid_tooltip_descr = QCheckBox('Description') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_descr) self.visual_grid_tooltip_tags = QCheckBox('Tags') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_tags) self.visual_grid_tooltip_last_read = QCheckBox('Last read') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_last_read) self.visual_grid_tooltip_times_read = QCheckBox('Times read') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_times_read) self.visual_grid_tooltip_pub_date = QCheckBox('Publication Date') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_pub_date) self.visual_grid_tooltip_date_added = QCheckBox('Date added') grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_date_added) # grid view / gallery grid_gallery_group = QGroupBox('Gallery', grid_view_general_page) grid_view_layout.addWidget(grid_gallery_group, 0, Qt.AlignTop) grid_gallery_main_l = QFormLayout() grid_gallery_main_l.setFormAlignment(Qt.AlignLeft) grid_gallery_group.setLayout(grid_gallery_main_l) grid_gallery_display = FlowLayout() grid_gallery_main_l.addRow('Display icon on gallery:', grid_gallery_display) self.external_viewer_ico = QCheckBox('External Viewer') grid_gallery_display.addWidget(self.external_viewer_ico) self.gallery_type_ico = QCheckBox('File Type') grid_gallery_display.addWidget(self.gallery_type_ico) if sys.platform.startswith('darwin'): grid_gallery_group.setEnabled(False) gallery_text_mode = QWidget() grid_gallery_main_l.addRow('Text Mode:', gallery_text_mode) gallery_text_mode_l = QHBoxLayout() gallery_text_mode.setLayout(gallery_text_mode_l) self.gallery_text_elide = QRadioButton('Elide text', gallery_text_mode) self.gallery_text_fit = QRadioButton('Fit text', gallery_text_mode) gallery_text_mode_l.addWidget(self.gallery_text_elide, 0, Qt.AlignLeft) gallery_text_mode_l.addWidget(self.gallery_text_fit, 0, Qt.AlignLeft) gallery_text_mode_l.addWidget(Spacer('h'), 1, Qt.AlignLeft) gallery_font = QHBoxLayout() grid_gallery_main_l.addRow('Font:*', gallery_font) self.font_lbl = QLabel() self.font_size_lbl = QSpinBox() self.font_size_lbl.setMaximum(100) self.font_size_lbl.setMinimum(1) self.font_size_lbl.setToolTip('Font size in pixels') choose_font = QPushButton('Choose font') choose_font.clicked.connect(self.choose_font) gallery_font.addWidget(self.font_lbl, 0, Qt.AlignLeft) gallery_font.addWidget(self.font_size_lbl, 0, Qt.AlignLeft) gallery_font.addWidget(choose_font, 0, Qt.AlignLeft) gallery_font.addWidget(Spacer('h'), 1, Qt.AlignLeft) # grid view / colors grid_colors_group = QGroupBox('Colors', grid_view_general_page) grid_view_layout.addWidget(grid_colors_group, 1, Qt.AlignTop) grid_colors_l = QFormLayout() grid_colors_group.setLayout(grid_colors_l) def color_lineedit(): l = QLineEdit() l.setPlaceholderText('Hex colors. Eg.: #323232') l.setMaximumWidth(200) return l self.grid_label_color = color_lineedit() self.grid_title_color = color_lineedit() self.grid_artist_color = color_lineedit() grid_colors_l.addRow('Label color:', self.grid_label_color) grid_colors_l.addRow('Title color:', self.grid_title_color) grid_colors_l.addRow('Artist color:', self.grid_artist_color) style_page = QWidget(self) visual.addTab(style_page, 'Style') visual.setTabEnabled(0, False) visual.setTabEnabled(2, False) visual.setCurrentIndex(1) # Advanced advanced = QTabWidget(self) self.advanced_index = self.right_panel.addWidget(advanced) advanced_misc_scroll = QScrollArea(self) advanced_misc_scroll.setBackgroundRole(QPalette.Base) advanced_misc_scroll.setWidgetResizable(True) advanced_misc = QWidget() advanced_misc_scroll.setWidget(advanced_misc) advanced.addTab(advanced_misc_scroll, 'Misc') advanced_misc_main_layout = QVBoxLayout() advanced_misc.setLayout(advanced_misc_main_layout) misc_controls_layout = QFormLayout() advanced_misc_main_layout.addLayout(misc_controls_layout) # Advanced / Misc / Grid View misc_gridview = QGroupBox('Grid View') misc_controls_layout.addWidget(misc_gridview) misc_gridview_layout = QFormLayout() misc_gridview.setLayout(misc_gridview_layout) # Advanced / Misc / Grid View / scroll speed scroll_speed_spin_box = QSpinBox() scroll_speed_spin_box.setFixedWidth(60) scroll_speed_spin_box.setToolTip('Control the speed when scrolling in'+ ' grid view. DEFAULT: 7') scroll_speed_spin_box.setValue(self.scroll_speed) def scroll_speed(v): self.scroll_speed = v scroll_speed_spin_box.valueChanged[int].connect(scroll_speed) misc_gridview_layout.addRow('Scroll speed:', scroll_speed_spin_box) # Advanced / Misc / Grid View / cache size cache_size_spin_box = QSpinBox() cache_size_spin_box.setFixedWidth(120) cache_size_spin_box.setMaximum(999999999) cache_size_spin_box.setToolTip('This can greatly reduce lags/freezes in the grid view.' + ' Increase the value if you experience lag when scrolling'+ ' through galleries. DEFAULT: 200 MiB') def cache_size(c): self.cache_size = (self.cache_size[0], c) cache_size_spin_box.setValue(self.cache_size[1]) cache_size_spin_box.valueChanged[int].connect(cache_size) misc_gridview_layout.addRow('Cache Size (MiB):', cache_size_spin_box) # Advanced / Gallery advanced_gallery, advanced_gallery_m_l = new_tab('Gallery', advanced) def rebuild_thumbs(): confirm_msg = QMessageBox(QMessageBox.Question, '', 'Are you sure you want to regenerate your thumbnails.', QMessageBox.Yes | QMessageBox.No, self) if confirm_msg.exec() == QMessageBox.Yes: clear_cache_confirm = QMessageBox(QMessageBox.Question, '', 'Do you want to delete all old thumbnails before regenerating?', QMessageBox.Yes | QMessageBox.No, self) clear_cache = False if clear_cache_confirm.exec() == QMessageBox.Yes: clear_cache = True gallerydb.DatabaseEmitter.RUN = False def start_db_activity(): gallerydb.DatabaseEmitter.RUN = True app_popup = ApplicationPopup(self.parent_widget) app_popup.info_lbl.setText("Regenerating thumbnails...") app_popup.admin_db = gallerydb.AdminDB() app_popup.admin_db.moveToThread(app_constants.GENERAL_THREAD) app_popup.admin_db.DONE.connect(app_popup.admin_db.deleteLater) app_popup.admin_db.DONE.connect(start_db_activity) app_popup.admin_db.DATA_COUNT.connect(app_popup.prog.setMaximum) app_popup.admin_db.PROGRESS.connect(app_popup.prog.setValue) self.init_gallery_rebuild.connect(app_popup.admin_db.rebuild_thumbs) app_popup.adjustSize() self.init_gallery_rebuild.emit(clear_cache) app_popup.show() rebuild_thumbs_info = QLabel("Clears thumbnail cache and rebuilds it, which can take a while. Tip: Useful when changing thumbnail size.") rebuild_thumbs_btn = QPushButton('Regenerate Thumbnails') rebuild_thumbs_btn.adjustSize() rebuild_thumbs_btn.setFixedWidth(rebuild_thumbs_btn.width()) rebuild_thumbs_btn.clicked.connect(rebuild_thumbs) advanced_gallery_m_l.addRow(rebuild_thumbs_info) advanced_gallery_m_l.addRow(rebuild_thumbs_btn) g_data_fixer_group, g_data_fixer_l = groupbox('Gallery Renamer', QFormLayout, advanced_gallery) g_data_fixer_group.setEnabled(False) advanced_gallery_m_l.addRow(g_data_fixer_group) g_data_regex_fix_lbl = QLabel("Rename a gallery through regular expression."+ " A regex cheatsheet is located at About -> Regex Cheatsheet.") g_data_regex_fix_lbl.setWordWrap(True) g_data_fixer_l.addRow(g_data_regex_fix_lbl) self.g_data_regex_fix_edit = QLineEdit() self.g_data_regex_fix_edit.setPlaceholderText("Valid regex") g_data_fixer_l.addRow('Regex:', self.g_data_regex_fix_edit) self.g_data_replace_fix_edit = QLineEdit() self.g_data_replace_fix_edit.setPlaceholderText("Leave empty to delete matches") g_data_fixer_l.addRow('Replace with:', self.g_data_replace_fix_edit) g_data_fixer_options = FlowLayout() g_data_fixer_l.addRow(g_data_fixer_options) self.g_data_fixer_title = QCheckBox("Title", g_data_fixer_group) self.g_data_fixer_artist = QCheckBox("Artist", g_data_fixer_group) g_data_fixer_options.addWidget(self.g_data_fixer_title) g_data_fixer_options.addWidget(self.g_data_fixer_artist) # Advanced / Database advanced_db_page, advanced_db_page_l = new_tab('Database', advanced) # Advanced / Database / Import/Export def init_export(): confirm_msg = QMessageBox(QMessageBox.Question, '', 'Are you sure you want to export your database? This might take a long time.', QMessageBox.Yes | QMessageBox.No, self) if confirm_msg.exec() == QMessageBox.Yes: app_popup = ApplicationPopup(self.parent_widget) app_popup.info_lbl.setText("Exporting database...") app_popup.export_instance = io_misc.ImportExport() app_popup.export_instance.moveToThread(app_constants.GENERAL_THREAD) app_popup.export_instance.finished.connect(app_popup.export_instance.deleteLater) app_popup.export_instance.finished.connect(app_popup.close) app_popup.export_instance.amount.connect(app_popup.prog.setMaximum) app_popup.export_instance.progress.connect(app_popup.prog.setValue) self.init_gallery_eximport.connect(app_popup.export_instance.export_data) self.init_gallery_eximport.emit(None) app_popup.adjustSize() app_popup.show() self.close() def init_import(): path = QFileDialog.getOpenFileName(self, 'Choose happypanda database file', filter='*.hpdb') path = path[0] if len(path) != 0: app_popup = ApplicationPopup(self.parent_widget) app_popup.restart_info.hide() app_popup.info_lbl.setText("Importing database file...") app_popup.note_info.setText("Application requires a restart after importing") app_popup.import_instance = io_misc.ImportExport() app_popup.import_instance.moveToThread(app_constants.GENERAL_THREAD) app_popup.import_instance.finished.connect(app_popup.import_instance.deleteLater) app_popup.import_instance.finished.connect(app_popup.init_restart) app_popup.import_instance.amount.connect(app_popup.prog.setMaximum) app_popup.import_instance.imported_g.connect(app_popup.info_lbl.setText) app_popup.import_instance.progress.connect(app_popup.prog.setValue) self.init_gallery_eximport.connect(app_popup.import_instance.import_data) self.init_gallery_eximport.emit(path) app_popup.adjustSize() app_popup.show() self.close() advanced_impexp, advanced_impexp_l = groupbox('Import/Export', QFormLayout, advanced_db_page) advanced_db_page_l.addRow(advanced_impexp) self.export_format = QComboBox(advanced_db_page) #self.export_format.addItem('Text File', 0) self.export_format.addItem('HPDB', 1) self.export_format.adjustSize() self.export_format.setFixedWidth(self.export_format.width()) advanced_impexp_l.addRow('Export Format:', self.export_format) self.export_path = PathLineEdit(advanced_impexp, filters='') advanced_impexp_l.addRow('Export Path:', self.export_path) import_btn = QPushButton('Import database') import_btn.clicked.connect(init_import) export_btn = QPushButton('Export database') export_btn.clicked.connect(init_export) ex_imp_btn_l = QHBoxLayout() ex_imp_btn_l.addWidget(import_btn) ex_imp_btn_l.addWidget(export_btn) advanced_impexp_l.addRow(ex_imp_btn_l) # About about = QTabWidget(self) self.about_index = self.right_panel.addWidget(about) about_happypanda_page, about_layout = new_tab("About Happypanda", about, False) info_lbl = QLabel(app_constants.ABOUT) info_lbl.setWordWrap(True) info_lbl.setOpenExternalLinks(True) about_layout.addWidget(info_lbl) about_layout.addWidget(Spacer('v')) open_hp_folder = QPushButton('Open Happypanda Directory') open_hp_folder.clicked.connect(self.open_hp_folder) open_hp_folder.adjustSize() open_hp_folder.setFixedWidth(open_hp_folder.width()) about_layout.addWidget(open_hp_folder) # About / DB Overview about_db_overview, about_db_overview_m_l = new_tab('DB Overview', about) about_stats_tab_widget = misc_db.DBOverview(self.parent_widget) about_db_overview_options = QHBoxLayout() self.tags_treeview_on_start = QCheckBox('Start with application', about_db_overview) make_window_btn = QPushButton('Open in window', about_db_overview) make_window_btn.adjustSize() make_window_btn.setFixedWidth(make_window_btn.width()) about_db_overview_options.addWidget(self.tags_treeview_on_start) about_db_overview_options.addWidget(make_window_btn) def mk_btn_false(): try: make_window_btn.setDisabled(False) except RuntimeError: pass def make_tags_treeview_window(): self.parent_widget.tags_treeview = misc_db.DBOverview(self.parent_widget, True) self.parent_widget.tags_treeview.about_to_close.connect(mk_btn_false) make_window_btn.setDisabled(True) self.parent_widget.tags_treeview.show() if self.parent_widget.tags_treeview: self.parent_widget.tags_treeview.about_to_close.connect(mk_btn_false) make_window_btn.setDisabled(True) make_window_btn.clicked.connect(make_tags_treeview_window) about_db_overview_m_l.addRow(about_db_overview_options) about_db_overview_m_l.addRow(about_stats_tab_widget) # About / Troubleshooting about_troubleshoot_page = QWidget() about.addTab(about_troubleshoot_page, 'Bug Reporting') troubleshoot_layout = QVBoxLayout() about_troubleshoot_page.setLayout(troubleshoot_layout) guide_lbl = QLabel(app_constants.TROUBLE_GUIDE) guide_lbl.setTextFormat(Qt.RichText) guide_lbl.setOpenExternalLinks(True) guide_lbl.setWordWrap(True) troubleshoot_layout.addWidget(guide_lbl, 0, Qt.AlignTop) troubleshoot_layout.addWidget(Spacer('v')) # About / Search tutorial about_search_tut, about_search_tut_l = new_tab("Search Guide", about, True) g_search_lbl = QLabel(app_constants.SEARCH_TUTORIAL_TAGS) g_search_lbl.setWordWrap(True) about_search_tut_l.addRow(g_search_lbl) # About / Regex Cheatsheet about_s_regex, about_s_regex_l = new_tab("Regex Cheatsheet", about, True) reg_info = QLabel(app_constants.REGEXCHEAT) reg_info.setWordWrap(True) about_s_regex_l.addRow(reg_info) def add_folder_monitor(self, path=''): if not isinstance(path, str): path = '' l_edit = PathLineEdit() l_edit.setText(path) n = self.folders_layout.rowCount() + 1 self.folders_layout.addRow('{}'.format(n), l_edit) def add_ignore_path(self, path='', dir=True): if not isinstance(path, str): path = '' l_edit = PathLineEdit(dir=dir) l_edit.setText(path) n = self.ignore_path_l.rowCount() + 1 self.ignore_path_l.addRow('{}'.format(n), l_edit) def color_checker(self, txt): allow = False if len(txt) == 7: if txt[0] == '#': allow = True return allow def take_all_layout_widgets(self, l): n = l.rowCount() items = [] for x in range(n): item = l.takeAt(x+1) items.append(item.widget()) return items def choose_font(self): tup = QFontDialog.getFont(self) font = tup[0] if tup[1]: self.font_lbl.setText(font.family()) self.font_size_lbl.setValue(font.pointSize()) def open_hp_folder(self): if os.name == 'posix': utils.open_path(app_constants.posix_program_dir) else: utils.open_path(os.getcwd()) def reject(self): self.close()
class AbstractTemplateWidget(QFrame): """ TemplateWidget is used in reports and options tab. So it needs several common methods and common layout. But behavior is different. it should be defined in children classes. """ def __init__(self, main_window, items): super().__init__() self.items = items self.visible_items = [] self.layout = QStackedLayout() self.menu_layout = QVBoxLayout() self.templates_layout = QStackedLayout() self.showEvent = self._get_show_event(main_window) self.menu_wrapper = QVBoxLayout() try: self.ACTION_BTN_ICON except AttributeError: self.ACTION_BTN_ICON = "" self.setLayout(self.layout) self.layout.addWidget(self._get_static_widgets()) def _get_static_widgets(self): """ Create layout that does not depend on content. """ hbox = QHBoxLayout() self.menu_wrapper.addWidget(utils.get_scrollable(self.menu_layout)) hbox.addLayout(self.menu_wrapper, stretch=30) hbox.addLayout(self.templates_layout, stretch=70) widget = QWidget() widget.setLayout(hbox) widget.setGraphicsEffect(utils.get_shadow()) return widget def _iterate_items(self): """ Filter items if they has no values. """ pass def hideEvent(self, event): """ Clear menu and templates. """ utils.clear_layout(self.menu_layout) utils.clear_layout(self.templates_layout) def _get_show_event(self, main_window): """ Update templates list and re-select them. """ def show_event(event): utils.clear_layout(self.menu_layout) utils.clear_layout(self.templates_layout) self.visible_items = self._iterate_items() self._show_menu() self._show_templates() if not self.layout.currentIndex(): main_window.communication.action_button_toggle.emit( bool(self.visible_items), self.ACTION_BTN_ICON, self.action_btn_function ) return show_event def _show_menu(self): """ Update menu on showEvent. """ for i, item in enumerate(self.visible_items): b = QRadioButton(self._get_button_name(item)) b.setChecked(i == 0) b.clicked.connect(functools.partial(self.templates_layout.setCurrentIndex, i)) b.setObjectName("menu_button") self.menu_layout.addWidget(b) if not self.visible_items: self.menu_layout.addStretch() l = QLabel("Чтобы создать отчет\nначните заполнять данные") l.setAlignment(Qt.AlignCenter) self.menu_layout.addWidget(l) self.menu_layout.addStretch() def _show_templates(self): """ Update templates on shoeEvent. """ cols = 3 templates = template_module.Template.get_all() for j, item in enumerate(self.visible_items): if not templates[item.id]: l = QLabel("Нет шаблонов для данного объекта\nУправлять шаблонами можно на вкладке настроек") l.setAlignment(Qt.AlignCenter) self.templates_layout.addWidget(l) continue layouts = [QVBoxLayout() for _ in range(cols)] for i, each in enumerate(templates[item.id]): b = QRadioButton(each.name) b.setChecked(item.template == each) b.clicked.connect(functools.partial(self._template_clicked, j, each)) b.mouseDoubleClickEvent = functools.partial(self.open_template_edit_widget, j, each) layouts[i % cols].addWidget(b) wrapper = QHBoxLayout() for each in layouts: each.addStretch() wrapper.addLayout(each, stretch=int(100 / cols)) self.templates_layout.addWidget(utils.get_scrollable(wrapper)) def _template_selected(self, index, template): """ Change menu item name. Add template for the item. """ self.visible_items[index].template = template buttons = self.findChildren(QRadioButton, name="menu_button") buttons[index].setText(self._get_button_name(self.visible_items[index])) for i in range(len(self.visible_items)): ind = (i + index) % len(self.visible_items) if not self.visible_items[ind].template: self.templates_layout.setCurrentIndex(ind) buttons[ind].setChecked(True) buttons[index].setChecked(False) return def _get_button_name(self, item): pass def _double_click(self, index, template, event): pass def _template_clicked(self, index, template): pass def action_btn_function(self, event): pass def open_template_edit_widget(self, index, template, event): pass
class Ui_MainWindow(object): def setupUi(self, MainWindow, username=None, userid=1, host='192.168.2.171', splash=None): self.username = username self.userid = userid self.host = host MainWindow.setObjectName("MainWindow") MainWindow.resize(1280, 768) MainWindow.setStyleSheet("background-color: rgb(255, 255, 255);\n") self.popupEnabled = False self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QHBoxLayout(self.centralwidget) self.horizontalLayout.setContentsMargins(0, 0, 0, 0) self.horizontalLayout.setSpacing(0) self.horizontalLayout.setObjectName("horizontalLayout") self.sideBar = QFrame(self.centralwidget) sizePolicy = QSizePolicy( QSizePolicy.Fixed, QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(100) sizePolicy.setHeightForWidth( self.sideBar.sizePolicy().hasHeightForWidth()) self.sideBar.setSizePolicy(sizePolicy) self.sideBar.setMinimumSize(QSize(300, 768)) self.sideBar.setStyleSheet( "background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, " "y2:1, stop:0 rgb(0, 115, 119), " "stop:1 rgb(4, 147, 131));" ) self.sideBar.setFrameShape(QFrame.StyledPanel) self.sideBar.setFrameShadow(QFrame.Raised) self.sideBar.setLineWidth(0) self.sideBar.setObjectName("sideBar") self.verticalLayout = QVBoxLayout(self.sideBar) self.verticalLayout.setContentsMargins(0, -1, 0, -1) self.verticalLayout.setObjectName("verticalLayout") self.label = QLabel(self.sideBar) self.label.setStyleSheet("background-color: rgba(255, 255, 255, 0);") self.label.setText("") self.label.setPixmap(QPixmap("images/logo.png")) self.label.setObjectName("label") self.verticalLayout.addWidget(self.label) self.buttonsLayout = QVBoxLayout() self.buttonsLayout.setContentsMargins(-1, 20, -1, 10) self.buttonsLayout.setSpacing(0) self.buttonsLayout.setObjectName("buttonsLayout") self.SimulationButton = HoverButton(self.sideBar) self.SimulationButton.setSelected(True) self.SimulationButton.setMinimumSize(QSize(0, 50)) font = QFont() font.setFamily("Tahoma") font.setPointSize(16) self.SimulationButton.setFont(font) self.SimulationButton.setCursor( QCursor(Qt.PointingHandCursor)) self.SimulationButton.setObjectName("SimulationButton") self.buttonsLayout.addWidget(self.SimulationButton) self.HistoryButton = HoverButton(self.sideBar) self.HistoryButton.setMinimumSize(QSize(0, 50)) font = QFont() font.setFamily("Tahoma") font.setPointSize(16) self.HistoryButton.setFont(font) self.HistoryButton.setCursor( QCursor(Qt.PointingHandCursor)) self.HistoryButton.setObjectName("HistoryButton") self.buttonsLayout.addWidget(self.HistoryButton) # self.SettingsButton = HoverButton(self.sideBar) # self.SettingsButton.setMinimumSize(QSize(0, 50)) # font = QFont() # font.setFamily("Tahoma") # font.setPointSize(16) # self.SettingsButton.setFont(font) # self.SettingsButton.setCursor( # QCursor(Qt.PointingHandCursor)) # self.SettingsButton.setObjectName("SettingsButton") # self.buttonsLayout.addWidget(self.SettingsButton) self.verticalLayout.addLayout(self.buttonsLayout) spacerItem = QSpacerItem( 20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.verticalLayout.addItem(spacerItem) self.horizontalLayout.addWidget(self.sideBar) self.VScrollArea = QScrollArea(self.centralwidget) self.VScrollArea.setWidgetResizable(True) self.VScrollArea.setObjectName("VScrollArea") sizePolicy = QSizePolicy( QSizePolicy.Expanding, QSizePolicy.MinimumExpanding) self.VScrollArea.setSizePolicy(sizePolicy) self.VScrollArea.setMinimumSize(978, 0) self.MainWidget = QWidget(self.VScrollArea) # self.MainWidget.setGeometry(QRect(0, 0, 976, 766)) self.MainWidget.setObjectName("MainWidget") self.StackedLayout = QStackedLayout(self.MainWidget) self.SimulationWidget = QWidget(self.MainWidget) self.SimulationUI = SimulationPage() self.SimulationUI.setupUi( self.SimulationWidget, username, userid, host) self.StackedLayout.addWidget(self.SimulationWidget) if splash is not None: splash.showMessage('Adding final touches...', alignment=Qt.AlignHCenter, color=Qt.white) self.HistoryWidget = QWidget(self.MainWidget) self.HistoryUI = HistoryPage() self.HistoryUI.setupUi(self.HistoryWidget, username, userid, host) self.StackedLayout.addWidget(self.HistoryWidget) self.PopupWidget = QWidget(self.MainWidget) self.PopupUI = SimulationPage() self.PopupUI.setupUi(self.PopupWidget, simid=0) self.StackedLayout.addWidget(self.PopupWidget) self.VScrollArea.setWidget(self.MainWidget) self.horizontalLayout.addWidget(self.VScrollArea) MainWindow.setCentralWidget(self.centralwidget) # EVENTS self.SimulationButton.clicked.connect(self.onSimulationButton) self.HistoryButton.clicked.connect(self.onHistoryButton) self.HistoryUI.table.cellClicked.connect(self.onHistoryPageLoad) self.PopupUI.closeButton.clicked.connect(self.onHistoryPageClose) self.SimulationUI.CreateNewButton.clicked.connect(self.onNewSimulation) # self.SettingsButton.clicked.connect(self.onSettingsButton) self.retranslateUi(MainWindow) QMetaObject.connectSlotsByName(MainWindow) def onSimulationButton(self): if self.StackedLayout.currentIndex() != 0: QApplication.setOverrideCursor( QCursor(Qt.WaitCursor)) # if self.StackedLayout.currentIndex() == 1: # self.HistoryUI.thread.terminate() # self.HistoryUI.thread.wait() self.SimulationButton.setSelected(True) self.HistoryButton.setSelected(False) self.StackedLayout.setCurrentIndex(0) QApplication.restoreOverrideCursor() # self.SettingsButton.setSelected(False) def onNewSimulation(self): username = self.SimulationUI.username userid = self.SimulationUI.userid host = self.SimulationUI.host QApplication.setOverrideCursor( QCursor(Qt.WaitCursor)) self.SimulationUI.thread.quit() self.SimulationUI.thread.wait() self.SimulationUI.Cutplans.thread.quit() self.SimulationUI.Cutplans.thread.wait() self.SimulationUI = SimulationPage() self.SimulationUI.setupUi( self.SimulationWidget, username, userid, host) self.SimulationUI.CreateNewButton.clicked.connect(self.onNewSimulation) QApplication.restoreOverrideCursor() def onHistoryButton(self): self.SimulationButton.setSelected(False) self.HistoryButton.setSelected(True) if self.StackedLayout.currentIndex() == 0 and \ self.SimulationUI.simrunning: msgBox = QMessageBox() msgBox.setWindowTitle("Simulation running...") msgBox.setText("Do you still want to leave the page?") msgBox.setStandardButtons( QMessageBox.Yes | QMessageBox.No) msgBox.setDefaultButton(QMessageBox.No) msgBox.setIcon(QMessageBox.Warning) self.HistoryButton.setSelected(True) r = msgBox.exec_() if r == QMessageBox.No: self.HistoryButton.setSelected(False) if self.StackedLayout.currentIndex() == 0: self.SimulationButton.setSelected(True) return QApplication.setOverrideCursor( QCursor(Qt.WaitCursor)) # if self.StackedLayout.currentIndex() == 1: # self.HistoryUI.thread.terminate() # self.HistoryUI.thread.wait() if self.popupEnabled: self.StackedLayout.setCurrentIndex(2) else: self.StackedLayout.setCurrentIndex(1) QApplication.restoreOverrideCursor() # self.SettingsButton.setSelected(False) def setUserID(self, name): self.username = name def LoadConfig(self): config = ConfigParser() config.read('config.ini') self.MainWidget.username = config['GENERAL']['username'] self.MainWidget.userid = config['GENERAL']['userid'] self.MainWidget.host = config['CONNECTION']['host'] def retranslateUi(self, MainWindow): _translate = QCoreApplication.translate MainWindow.setWindowTitle(_translate( "MainWindow", "SeqSim — Sequal Simulator")) self.SimulationButton.setText(_translate("MainWindow", "Simulation")) self.HistoryButton.setText(_translate("MainWindow", "History")) # self.SettingsButton.setText(_translate("MainWindow", "Settings")) def onHistoryPageLoad(self, r, c): self.PopupUI.simid = self.HistoryUI.loader.data['SimID'][r] self.PopupUI.setupReadOnly() self.popupEnabled = True self.StackedLayout.setCurrentIndex(2) def onHistoryPageClose(self): self.popupEnabled = False self.PopupUI.Cutplans.thread.quit() self.PopupUI.Cutplans.thread.wait() self.PopupUI.Cutplans.thread.started.disconnect() self.PopupUI.Cutplans.threadconnected = False self.StackedLayout.setCurrentIndex(1)
class ComboEditor(QWidget): # Signals closeSplit = pyqtSignal('PyQt_PyObject') splitEditor = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject', Qt.Orientation) allFilesClosed = pyqtSignal() about_to_close_combo_editor = pyqtSignal() fileClosed = pyqtSignal("PyQt_PyObject") def __init__(self, original=False): super(ComboEditor, self).__init__(None) self.__original = original self.__undocked = [] self._symbols_index = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.bar = ActionBar(main_combo=original) vbox.addWidget(self.bar) # Info bar # self.info_bar = InfoBar(self) # self.info_bar.setVisible(False) # vbox.addWidget(self.info_bar) self.stacked = QStackedLayout() vbox.addLayout(self.stacked) self._main_container = IDE.get_service('main_container') if not self.__original: self._main_container.fileOpened['QString'].connect( self._file_opened_by_main) self.bar.combo_files.showComboSelector.connect( self._main_container.show_files_handler) self.bar.combo_files.hideComboSelector.connect( self._main_container.hide_files_handler) self.bar.change_current['PyQt_PyObject', int].connect(self._set_current) self.bar.splitEditor[bool].connect(self.split_editor) self.bar.runFile['QString'].connect(self._run_file) self.bar.closeSplit.connect(lambda: self.closeSplit.emit(self)) self.bar.addToProject['QString'].connect(self._add_to_project) self.bar.showFileInExplorer['QString'].connect( self._show_file_in_explorer) self.bar.goToSymbol[int].connect(self._go_to_symbol) self.bar.undockEditor.connect(self.undock_editor) self.bar.reopenTab['QString'].connect( lambda path: self._main_container.open_file(path)) self.bar.closeImageViewer.connect(self._close_image) self.bar.code_navigator.previousPressed.connect(self._navigate_code) self.bar.code_navigator.nextPressed.connect(self._navigate_code) # self.connect(self.bar, SIGNAL("recentTabsModified()"), # lambda: self._main_container.recent_files_changed()) # self.connect(self.bar.code_navigator.btnPrevious, # SIGNAL("clicked()"), # lambda: self._navigate_code(False)) # self.connect(self.bar.code_navigator.btnNext, SIGNAL("clicked()"), # lambda: self._navigate_code(True)) def _navigate_code(self, operation, forward=True): self._main_container.navigate_code_history(operation, forward) # op = self.bar.code_navigator.operation # self._main_container.navigate_code_history(val, op) def current_editor(self): return self.stacked.currentWidget() def setFocus(self): super(ComboEditor, self).setFocus() self.current_editor().setFocus() self._editor_with_focus() def _file_opened_by_main(self, path): index = self.stacked.currentIndex() ninjaide = IDE.get_service('ide') editable = ninjaide.get_or_create_editable(path) self.add_editor(editable) self.bar.set_current_by_index(index) if index == -1: self.bar.set_current_by_index(0) def add_image_viewer(self, viewer): """Add Image Viewer widget to the UI area""" self.stacked.addWidget(viewer) viewer.scaleFactorChanged.connect( self.bar.image_viewer_controls.update_scale_label) viewer.imageSizeChanged.connect( self.bar.image_viewer_controls.update_size_label) self.bar.add_item(viewer.display_name(), None) viewer.create_scene() if not self.bar.isVisible(): self.bar.setVisible(True) def add_editor(self, neditable, keep_index=False): """Add Editor Widget to the UI area.""" if neditable.editor: if self.__original: editor = neditable.editor else: # editor = neditable.editor.clone() editor = self._main_container.create_editor_from_editable( neditable) neditable.editor.link(editor) current_index = self.stacked.currentIndex() new_index = self.stacked.addWidget(editor) self.stacked.setCurrentIndex(new_index) self.bar.add_item(neditable.display_name, neditable) # Bar is not visible because all the files have been closed, # so if a new file is opened, show the bar if not self.bar.isVisible(): self.bar.setVisible(True) if keep_index: self.bar.set_current_by_index(current_index) # Connections neditable.fileClosing.connect(self._close_file) neditable.fileSaved.connect(self._update_symbols) editor.editorFocusObtained.connect(self._editor_with_focus) editor.modificationChanged.connect(self._editor_modified) editor.cursor_position_changed[int, int].connect( self._update_cursor_position) editor.current_line_changed[int].connect(self._set_current_symbol) if neditable._swap_file.dirty: self._editor_modified(True, sender=editor) neditable.checkersUpdated.connect(self._show_notification_icon) # Connect file system signals only in the original if self.__original: neditable.askForSaveFileClosing.connect(self._ask_for_save) neditable.fileChanged.connect(self._file_has_been_modified) # Load Symbols self._load_symbols(neditable) def show_combo_file(self): self.bar.combo.showPopup() def show_combo_symbol(self): self.bar.symbols_combo.showPopup() def show_combo_set_language(self): self.bar.set_language_combo.showPopup() def unlink_editors(self): for index in range(self.stacked.count()): widget = self.stacked.widget(index) # widget.setDocument(QsciDocument()) def clone(self): combo = ComboEditor() for neditable in self.bar.get_editables(): combo.add_editor(neditable) return combo def split_editor(self, orientation): new_combo = self.clone() self.splitEditor.emit(self, new_combo, orientation) def undock_editor(self): new_combo = ComboEditor() for neditable in self.bar.get_editables(): new_combo.add_editor(neditable) self.__undocked.append(new_combo) new_combo.setWindowTitle("NINJA-IDE") editor = self.current_editor() new_combo.set_current(editor.neditable) new_combo.resize(700, 500) new_combo.about_to_close_combo_editor.connect(self._remove_undock) new_combo.show() def _remove_undock(self): widget = self.sender() self.__undocked.remove(widget) def close_current_file(self): self.bar.about_to_close_file() def _close_image(self, index): layout_item = self.stacked.takeAt(index) layout_item.widget().deleteLater() if self.stacked.isEmpty(): self.bar.hide() self.allFilesClosed.emit() def _close_file(self, neditable): index = self.bar.close_file(neditable) layoutItem = self.stacked.takeAt(index) # neditable.editor.completer.cc.unload_module() self.fileClosed.emit(neditable.nfile) layoutItem.widget().deleteLater() if self.stacked.isEmpty(): self.bar.hide() self.allFilesClosed.emit() tree_symbols = IDE.get_service("symbols_explorer") if tree_symbols is not None: tree_symbols.clear() def _editor_with_focus(self): self._main_container.combo_area = self editor = self.current_editor() if editor is not None: self._main_container.current_editor_changed( editor.neditable.file_path) self._load_symbols(editor.neditable) editor.neditable.update_checkers_display() def _ask_for_save(self, neditable): val = QMessageBox.No fileName = neditable.nfile.file_name val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.No: neditable.nfile.close(force_close=True) elif val == QMessageBox.Yes: neditable.ignore_checkers = True self._main_container.save_file(neditable.editor) neditable.nfile.close() @pyqtSlot("PyQt_PyObject") def _recovery(self, neditable): print("lalalal") def _file_has_been_modified(self, neditable): index = self.bar.combo_files.findData(neditable) self.stacked.setCurrentIndex(index) self.bar.combo_files.setCurrentIndex(index) msg_box = QMessageBox(self) msg_box.setIcon(QMessageBox.Information) msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box.setDefaultButton(QMessageBox.Yes) msg_box.setWindowTitle(translations.TR_FILE_HAS_BEEN_MODIFIED) msg_box.setText(translations.TR_FILE_MODIFIED_OUTSIDE % neditable.display_name) result = msg_box.exec_() if result == QMessageBox.Yes: neditable.reload_file() return def _run_file(self, path): self._main_container.run_file(path) def _add_to_project(self, path): self._main_container._add_to_project(path) def _show_file_in_explorer(self, path): '''Connected to ActionBar's showFileInExplorer(QString) signal, forwards the file path on to the main container.''' self._main_container._show_file_in_explorer(path) def set_current(self, neditable): if neditable: self.bar.set_current_file(neditable) def _set_current(self, neditable, index): self.stacked.setCurrentIndex(index) if neditable: self.bar.image_viewer_controls.setVisible(False) self.bar.code_navigator.setVisible(True) self.bar.symbols_combo.setVisible(True) self.bar.lbl_position.setVisible(True) editor = self.current_editor() self._update_cursor_position(ignore_sender=True) editor.setFocus() self._main_container.current_editor_changed(neditable.file_path) self._load_symbols(neditable) # self._show_file_in_explorer(neditable.file_path) neditable.update_checkers_display() else: self.bar.combo_files.setCurrentIndex(index) viewer_widget = self.stacked.widget(index) self._main_container.current_editor_changed( viewer_widget.image_filename) self.bar.image_viewer_controls.setVisible(True) self.bar.code_navigator.setVisible(False) self.bar.symbols_combo.setVisible(False) self.bar.lbl_position.setVisible(False) def widget(self, index): return self.stacked.widget(index) def count(self): """Return the number of editors opened.""" return self.stacked.count() def _update_cursor_position(self, line=0, col=0, ignore_sender=False): obj = self.sender() editor = self.current_editor() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: line += 1 self.bar.update_line_col(line, col) def _set_current_symbol(self, line, ignore_sender=False): obj = self.sender() editor = self.current_editor() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: index = bisect.bisect(self._symbols_index, line) if (index >= len(self._symbols_index) or self._symbols_index[index] > (line + 1)): index -= 1 self.bar.set_current_symbol(index) def _editor_modified(self, value, sender=None): if sender is None: sender = self.sender() neditable = sender.neditable if value: text = "\u2022 %s" % neditable.display_name self.bar.update_item_text(neditable, text) else: self.bar.update_item_text(neditable, neditable.display_name) def _go_to_symbol(self, index): line = self._symbols_index[index] editor = self.current_editor() editor.go_to_line(line, center=True) editor.setFocus() def _update_symbols(self, neditable): editor = self.current_editor() # Check if it's current to avoid signals from other splits. if editor.neditable == neditable: self._load_symbols(neditable) def _update_combo_info(self, neditable): self.bar.update_item_text(neditable, neditable.display_name) self._main_container.current_editor_changed(neditable.file_path) def _load_symbols(self, neditable): # Get symbols handler by language symbols_handler = handlers.get_symbols_handler(neditable.language()) if symbols_handler is None: return source = neditable.editor.text source = source.encode(neditable.editor.encoding) symbols, symbols_simplified = symbols_handler.obtain_symbols( source, simple=True) self._symbols_index = sorted(symbols_simplified.keys()) symbols_simplified = sorted(list(symbols_simplified.items()), key=lambda x: x[0]) self.bar.add_symbols(symbols_simplified) line, _ = neditable.editor.cursor_position self._set_current_symbol(line, True) tree_symbols = IDE.get_service('symbols_explorer') if tree_symbols is not None: tree_symbols.update_symbols_tree(symbols, neditable.file_path) def _show_notification_icon(self, neditable): checkers = neditable.sorted_checkers icon = QIcon() for items in checkers: checker, color, _ = items if checker.checks: if isinstance(checker.checker_icon, int): icon = self.style().standardIcon(checker.checker_icon) elif isinstance(checker.checker_icon, str): icon = QIcon(checker.checker_icon) # FIXME: sucks else: icon = QIcon(checker.checker_icon) break self.bar.update_item_icon(neditable, icon) def show_menu_navigation(self): self.bar.code_navigator.show_menu_navigation() def closeEvent(self, event): self.about_to_close_combo_editor.emit() # self.emit(SIGNAL("aboutToCloseComboEditor()")) super(ComboEditor, self).closeEvent(event) def reject(self): if not self.__original: super(ComboEditor, self).reject()
class DeviceManagementWidget(WidgetBase): # noinspection PyArgumentList,PyUnresolvedReferences def __init__( self, parent: typing.Optional[QWidget], on_connection_request: ConnectionRequestCallback, on_disconnection_request: DisconnectionRequestCallback, ): super(DeviceManagementWidget, self).__init__(parent) self.setAttribute( Qt.WA_DeleteOnClose) # This is required to stop background timers! self._port_discoverer = PortDiscoverer() self._port_mapping: typing.Dict[str:str] = {} self._port_combo = QComboBox(self) self._port_combo.setEditable(True) self._port_combo.setInsertPolicy(QComboBox.NoInsert) self._port_combo.setSizeAdjustPolicy(QComboBox.AdjustToContents) self._port_combo.setFont(get_monospace_font()) self._port_combo.lineEdit().returnPressed.connect( self._on_confirmation) self._connect_button = make_button(self, "Connect", "disconnected", on_clicked=self._on_confirmation) self._connect_button.setEnabled( False) # Empty by default, therefore disabled self._port_combo.currentTextChanged.connect( lambda: self._connect_button.setEnabled( bool(self._port_combo.currentText().strip()))) self._status_text = QLabel(self) self._status_text.setText(_STATUS_WHEN_NOT_CONNECTED) self._status_text.setWordWrap(True) self._device_info_widget = LittleBobbyTablesWidget(self) combo_completer = QCompleter() combo_completer.setCaseSensitivity(Qt.CaseInsensitive) combo_completer.setModel(self._port_combo.model()) self._port_combo.setCompleter(combo_completer) self._update_timer = QTimer(self) self._update_timer.timeout.connect(self._update_ports) self._update_timer.start(2000) self._connection_progress_bar = QProgressBar(self) self._connection_progress_bar.setMinimum(0) self._connection_progress_bar.setMaximum(100) self._connection_established = False self._last_task: typing.Optional[asyncio.Task] = None self._connection_request_callback: ConnectionRequestCallback = ( on_connection_request) self._disconnection_request_callback: DisconnectionRequestCallback = ( on_disconnection_request) # Layout self._overlay = QStackedLayout(self) self._init_overlay_widgets() self.setLayout(self._overlay) # Initialization self._update_ports() def on_connection_loss(self, reason: str): """ This method should be invoked when the connection becomes lost. It will cause the widget to change its state accordingly. :param reason: Human-readable description of the reason in one line. """ if self._connection_established: self._switch_state_disconnected() self._status_text.setText( f'Connection lost: {reason.strip() or "Unknown reason"}') def on_connection_initialization_progress_report(self, stage_description: str, progress: float): """ This method should be periodically invoked while connection is being initialized. :param stage_description: Human-readable short string displaying what is currently being done. E.g. "Opening port" :param progress: A float in [0, 1] that displays how much of the work has been completed so far, where 0 - nothing, 1 - all done. """ if self._overlay.currentIndex() != 1: raise RuntimeError( "Invalid usage: this method can only be invoked when connection initialization is " "in progress. Currently it is not.") # noinspection PyTypeChecker if not (0.0 <= progress <= 1.0): _logger.error( f"Connection progress estimate falls outside of [0, 1]: {progress}" ) stage_description = stage_description.strip() if stage_description[-1] in string.ascii_letters: stage_description += "..." self._connection_progress_bar.setValue(int(progress * 100)) self._connection_progress_bar.setFormat(stage_description) self._connection_progress_bar.setAlignment(Qt.AlignCenter) # noinspection PyArgumentList,PyUnresolvedReferences def _init_overlay_widgets(self): # Main widget operational = WidgetBase(self) operational_layout_top = QHBoxLayout() operational_layout_top.addWidget(QLabel("Port:")) operational_layout_top.addWidget(self._port_combo, stretch=1) operational_layout_top.addWidget(self._connect_button) operational_layout_bottom = QHBoxLayout() operational_layout_bottom.addWidget(self._status_text) operational_layout = QVBoxLayout() operational_layout.addLayout(operational_layout_top) operational_layout.addLayout(operational_layout_bottom) operational_layout.addWidget(self._device_info_widget, 1) operational.setLayout(operational_layout) self._overlay.addWidget(operational) # Progress widget - shown while connecting/disconnecting progress = WidgetBase(self) progress_layout = QVBoxLayout() progress_layout.addStretch(1) progress_layout.addWidget(self._connection_progress_bar) progress_layout.addStretch(1) progress.setLayout(progress_layout) self._overlay.addWidget(progress) def _update_ports(self): if self._connection_established: return # noinspection PyBroadException try: ports = self._port_discoverer.get_ports() except Exception as ex: _logger.exception("Could not list ports") self.flash(f"Could not list ports: {ex}", duration=10) ports = [] self._port_mapping = self._port_discoverer.display_ports( ports, self._port_combo) def _switch_state_connected(self, device_info: BasicDeviceInfo): self._connection_established = True self._overlay.setCurrentIndex(0) self._port_combo.setEnabled(False) self._connect_button.setEnabled(True) self._connect_button.setText("Disconnect") self._connect_button.setIcon(get_icon("connected")) self._status_text.setText("Connected") self._device_info_widget.set(device_info) def _switch_state_disconnected(self): self._connection_established = False self._overlay.setCurrentIndex(0) self._port_combo.setEnabled(True) self._connect_button.setEnabled(True) self._connect_button.setText("Connect") self._connect_button.setIcon(get_icon("disconnected")) self._device_info_widget.clear() self._status_text.setText(_STATUS_WHEN_NOT_CONNECTED) self._update_ports() async def _do_connect(self): _logger.info("Connection initialization task spawned") try: selected_port = self._port_mapping[str( self._port_combo.currentText()).strip()] except KeyError: selected_port = str(self._port_combo.currentText()).strip() # Activate the progress view and initialize it self._overlay.setCurrentIndex(1) self._connection_progress_bar.setValue(0) self._connection_progress_bar.setFormat("Requesting connection...") # noinspection PyBroadException try: device_info: BasicDeviceInfo = await self._connection_request_callback( selected_port) except Exception as ex: show_error( "Could not connect", f"Connection via the port {selected_port} could not be established.", f"Reason: {str(ex)}", parent=self, ) self._switch_state_disconnected() else: assert device_info is not None self._switch_state_connected(device_info) async def _do_disconnect(self): _logger.info("Connection termination task spawned") # Activate the progress view and initialize it self._overlay.setCurrentIndex(1) self._connection_progress_bar.setValue(100) self._connection_progress_bar.setFormat( "Disconnecting, please wait...") # noinspection PyBroadException try: await self._disconnection_request_callback() except Exception as ex: _logger.exception("Disconnect request failed") self.flash(f"Disconnection problem: {ex}", duration=10) self._switch_state_disconnected() def _on_confirmation(self): # Deactivate the controls in order to prevent accidental double-entry self._port_combo.setEnabled(False) self._connect_button.setEnabled(False) if (self._last_task is not None) and not self._last_task.done(): show_error( "I'm sorry Dave, I'm afraid I can't do that", "Cannot connect/disconnect while another connection/disconnection operation is still running", f"Pending future: {self._last_task}", self, ) else: if not self._connection_established: self._last_task = asyncio.get_event_loop().create_task( self._do_connect()) else: self._last_task = asyncio.get_event_loop().create_task( self._do_disconnect())
class _MainContainer(QWidget): ############################################################################### # MainContainer SIGNALS ############################################################################### """ newFileOpened(QString) allTabClosed() runFile(QString) addToProject(QString) showFileInExplorer(QString) recentTabsModified() currentEditorChanged(QString) fileOpened(QString) ---------migrationAnalyzed() findOcurrences(QString) ---------updateFileMetadata() editorKeyPressEvent(QEvent) locateFunction(QString, QString, bool) [functionName, filePath, isVariable] updateLocator(QString) beforeFileSaved(QString) fileSaved(QString) openPreferences() --------openProject(QString) ---------dontOpenStartPage() """ ############################################################################### fileOpened = pyqtSignal('QString') updateLocator = pyqtSignal('QString') currentEditorChanged = pyqtSignal('QString') beforeFileSaved = pyqtSignal('QString') fileSaved = pyqtSignal('QString') def __init__(self, parent=None): super(_MainContainer, self).__init__(parent) self.setContentsMargins(0, 0, 0, 0) self._parent = parent self._vbox = QVBoxLayout(self) self._vbox.setContentsMargins(0, 0, 0, 0) self._vbox.setSpacing(0) self.stack = QStackedLayout() self.stack.setStackingMode(QStackedLayout.StackAll) self._vbox.addLayout(self.stack) self.splitter = dynamic_splitter.DynamicSplitter() self.setAcceptDrops(True) # self._files_handler = files_handler.FilesHandler(self) self._add_file_folder = add_file_folder.AddFileFolderWidget(self) # documentation browser self.docPage = None # Code Navigation # self._locator = locator.GoToDefinition() self.__codeBack = [] self.__codeForward = [] self.__bookmarksFile = '' self.__bookmarksPos = -1 self.__breakpointsFile = '' self.__breakpointsPos = -1 self.__operations = { 0: self._navigate_code_jumps, 1: self._navigate_bookmarks, 2: self._navigate_breakpoints } # self.locateFunction['QString', # 'QString', # bool].connect(self.locate_function) IDE.register_service('main_container', self) # Register signals connections connections = ({ 'target': 'main_container', 'signal_name': 'updateLocator', 'slot': self._explore_file_code }, { 'target': 'filesystem', 'signal_name': 'projectOpened', 'slot': self._explore_code }, { 'target': 'projects_explorer', 'signal_name': 'updateLocator', 'slot': self._explore_code }) """ {'target': 'menu_file', 'signal_name': 'openFile(QString)', 'slot': self.open_file}, {'target': 'explorer_container', 'signal_name': 'goToDefinition(int)', 'slot': self.editor_go_to_line}, {'target': 'explorer_container', 'signal_name': 'pep8Activated(bool)', 'slot': self.reset_pep8_warnings}, {'target': 'explorer_container', 'signal_name': 'lintActivated(bool)', 'slot': self.reset_lint_warnings}, {'target': 'filesystem', 'signal_name': 'projectOpened', 'slot': self._explore_code}, {'target': 'main_container', 'signal_name': 'updateLocator(QString)', 'slot': self._explore_file_code}, ) """ IDE.register_signals('main_container', connections) self.selector = main_selector.MainSelector(self) self._opening_dialog = False self.add_widget(self.selector) if settings.SHOW_START_PAGE: self.show_start_page() self.selector.changeCurrent[int].connect(self._change_current_stack) # self.selector.removeWidget[int].connect(self._remove_item_from_stack) # self.selector.ready.connect(self._selector_ready) self.selector.animationCompleted.connect( self._selector_animation_completed) # self.closeDialog['PyQt_PyObject'].connect(self.remove_widget) def install(self): ide = IDE.get_service('ide') ide.place_me_on("main_container", self, "central", top=True) self.combo_area = combo_editor.ComboEditor(original=True) self.combo_area.allFilesClosed.connect(self._files_closed) # self.combo_area.allFilesClosed.connect(self._files_closed) self.splitter.add_widget(self.combo_area) self.add_widget(self.splitter) self.current_widget = self.combo_area # Code Locator self._code_locator = locator_widget.LocatorWidget(ide) ui_tools.install_shortcuts(self, actions.ACTIONS, ide) def show_locator(self): """Show the locator widget""" if not self._code_locator.isVisible(): self._code_locator.show() def _explore_code(self): """ Update locator metadata for the current projects """ self._code_locator.explore_code() def _explore_file_code(self, path): """ Update locator metadata for the file in path """ self._code_locator.explore_file_code(path) def add_status_bar(self, status): self._vbox.addWidget(status) @property def combo_header_size(self): return self.combo_area.bar.height() def add_widget(self, widget): self.stack.addWidget(widget) def remove_widget(self, widget): self.stack.removeWidget(widget) def _close_dialog(self, widget): self.emit(SIGNAL("closeDialog(PyQt_PyObject)"), widget) self.disconnect(widget, SIGNAL("finished(int)"), lambda: self._close_dialog(widget)) def show_dialog(self, widget): self._opening_dialog = True # self.connect(widget, SIGNAL("finished(int)"), # lambda: self._close_dialog(widget)) self.setVisible(True) self.stack.addWidget(widget) self.show_selector() def show_selector(self): if self.selector != self.stack.currentWidget(): temp_dir = os.path.join(QDir.tempPath(), "ninja-ide") if not os.path.exists(temp_dir): os.mkdir(temp_dir) collected_data = [] current = self.stack.currentIndex() for index in range(self.stack.count()): widget = self.stack.widget(index) if widget == self.selector: continue pixmap = QWidget.grab(widget, widget.rect()) path = os.path.join(temp_dir, "screen%s.png" % index) pixmap.save(path) collected_data.append((index, path)) self.selector.set_model(collected_data) self._selector_ready() """ if self.selector != self.stack.currentWidget(): temp_dir = os.path.join(QDir.tempPath(), "ninja-ide") if not os.path.exists(temp_dir): os.mkdir(temp_dir) collected_data = [] current = self.stack.currentIndex() for index in range(self.stack.count()): widget = self.stack.widget(index) if widget == self.selector: continue closable = True if widget == self.splitter: closable = False pixmap = QWidget.grab(widget, widget.rect()) path = os.path.join(temp_dir, "screen%s.png" % index) pixmap.save(path) if index == current: self.selector.set_preview(index, path) collected_data.insert(0, (index, path, closable)) else: collected_data.append((index, path, closable)) self.selector.set_model(collected_data) else: self.selector.close_selector() """ def _selector_ready(self): print(self.stack.currentWidget()) self.stack.setCurrentWidget(self.selector) print(self.stack.currentWidget()) self.selector.start_animation() def _selector_animation_completed(self): if self._opening_dialog: # We choose the last one with -2, -1 (for last one) + # -1 for the hidden selector widget which is in the stacked too. self.selector.open_item(self.stack.count() - 2) self._opening_dialog = False def _change_current_stack(self, index): self.stack.setCurrentIndex(index) def _remove_item_from_stack(self, index): widget = self.stack.takeAt(index) del widget def show_editor_area(self): self.stack.setCurrentWidget(self.splitter) def _files_closed(self): if settings.SHOW_START_PAGE: self.show_start_page() def change_visibility(self): """Show/Hide the Main Container area.""" if self.isVisible(): self.hide() else: self.show() def expand_symbol_combo(self): self.stack.setCurrentWidget(self.splitter) self.current_widget.show_combo_symbol() def expand_file_combo(self): self.stack.setCurrentWidget(self.splitter) self.current_widget.show_combo_file() def locate_function(self, function, filePath, isVariable): """Move the cursor to the proper position in the navigate stack.""" editorWidget = self.get_current_editor() if editorWidget: self.__codeBack.append( (editorWidget.file_path, editorWidget.getCursorPosition())) self.__codeForward = [] self._locator.navigate_to(function, filePath, isVariable) def run_file(self, path): self.emit(SIGNAL("runFile(QString)"), path) def _add_to_project(self, path): self.emit(SIGNAL("addToProject(QString)"), path) def _show_file_in_explorer(self, path): self.emit(SIGNAL("showFileInExplorer(QString)"), path) def paste_history(self): """Paste the text from the copy/paste history.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): line, index = editorWidget.getCursorPosition() central = IDE.get_service('central_container') if central: editorWidget.insertAt(central.get_paste(), line, index) def copy_history(self): """Copy the selected text into the copy/paste history.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): copy = editorWidget.selectedText() central = IDE.get_service('central_container') if central: central.add_copy(copy) def import_from_everywhere(self): """Insert an import line from any place in the editor.""" editorWidget = self.get_current_editor() if editorWidget: dialog = from_import_dialog.FromImportDialog(editorWidget, self) dialog.show() def add_back_item_navigation(self): """Add an item to the back stack and reset the forward stack.""" editorWidget = self.get_current_editor() if editorWidget: self.__codeBack.append( (editorWidget.file_path, editorWidget.cursor_position)) self.__codeForward = [] def preview_in_browser(self): """Load the current html file in the default browser.""" editorWidget = self.get_current_editor() if editorWidget: if not editorWidget.file_path: self.save_file() ext = file_manager.get_file_extension(editorWidget.file_path) if ext in ('html', 'shpaml', 'handlebars', 'tpl'): webbrowser.open_new_tab(editorWidget.file_path) def add_bookmark_breakpoint(self): """Add a bookmark or breakpoint to the current file in the editor.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): if self.current_widget.bar.code_navigator.operation == 1: editorWidget.handle_bookmarks_breakpoints( editorWidget.getCursorPosition()[0], Qt.ControlModifier) elif self.current_widget.bar.code_navigator.operation == 2: editorWidget.handle_bookmarks_breakpoints( editorWidget.getCursorPosition()[0], Qt.NoModifier) def __navigate_with_keyboard(self, val): """Navigate between the positions in the jump history stack.""" op = self.current_widget.bar.code_navigator.operation self.navigate_code_history(val, op) def navigate_code_history(self, val, op): """Navigate the code history.""" self.__operations[op](val) def _navigate_code_jumps(self, val): """Navigate between the jump points.""" node = None if not val and self.__codeBack: node = self.__codeBack.pop() editorWidget = self.get_current_editor() if editorWidget: self.__codeForward.append( (editorWidget.file_path, editorWidget.getCursorPosition())) elif val and self.__codeForward: node = self.__codeForward.pop() editorWidget = self.get_current_editor() if editorWidget: self.__codeBack.append( (editorWidget.file_path, editorWidget.getCursorPosition())) if node: filename = node[0] line, col = node[1] self.open_file(filename, line, col) def _navigate_breakpoints(self, val): """Navigate between the breakpoints.""" # FIXME: put navigate breakpoints and bookmarks as one method. breakList = list(settings.BREAKPOINTS.keys()) breakList.sort() if not breakList: return if self.__breakpointsFile not in breakList: self.__breakpointsFile = breakList[0] index = breakList.index(self.__breakpointsFile) breaks = settings.BREAKPOINTS.get(self.__breakpointsFile, []) lineNumber = 0 # val == True: forward if val: if (len(breaks) - 1) > self.__breakpointsPos: self.__breakpointsPos += 1 lineNumber = breaks[self.__breakpointsPos] elif len(breaks) > 0: if index < (len(breakList) - 1): self.__breakpointsFile = breakList[index + 1] else: self.__breakpointsFile = breakList[0] self.__breakpointsPos = 0 lineNumber = settings.BREAKPOINTS[self.__breakpointsFile][0] else: if self.__breakpointsPos > 0: self.__breakpointsPos -= 1 lineNumber = breaks[self.__breakpointsPos] elif len(breaks) > 0: self.__breakpointsFile = breakList[index - 1] breaks = settings.BREAKPOINTS[self.__breakpointsFile] self.__breakpointsPos = len(breaks) - 1 lineNumber = breaks[self.__breakpointsPos] if file_manager.file_exists(self.__breakpointsFile): self.open_file(self.__breakpointsFile, lineNumber, None, True) else: settings.BREAKPOINTS.pop(self.__breakpointsFile) if settings.BREAKPOINTS: self._navigate_breakpoints(val) def _navigate_bookmarks(self, val): """Navigate between the bookmarks.""" bookList = list(settings.BOOKMARKS.keys()) bookList.sort() if not bookList: return if self.__bookmarksFile not in bookList: self.__bookmarksFile = bookList[0] index = bookList.index(self.__bookmarksFile) bookms = settings.BOOKMARKS.get(self.__bookmarksFile, []) lineNumber = 0 # val == True: forward if val: if (len(bookms) - 1) > self.__bookmarksPos: self.__bookmarksPos += 1 lineNumber = bookms[self.__bookmarksPos] elif len(bookms) > 0: if index < (len(bookList) - 1): self.__bookmarksFile = bookList[index + 1] else: self.__bookmarksFile = bookList[0] self.__bookmarksPos = 0 lineNumber = settings.BOOKMARKS[self.__bookmarksFile][0] else: if self.__bookmarksPos > 0: self.__bookmarksPos -= 1 lineNumber = bookms[self.__bookmarksPos] elif len(bookms) > 0: self.__bookmarksFile = bookList[index - 1] bookms = settings.BOOKMARKS[self.__bookmarksFile] self.__bookmarksPos = len(bookms) - 1 lineNumber = bookms[self.__bookmarksPos] if file_manager.file_exists(self.__bookmarksFile): self.open_file(self.__bookmarksFile, lineNumber, None, True) else: # settings.BOOKMARKS.pop(self.__bookmarksFile) if settings.BOOKMARKS: self._navigate_bookmarks(val) def count_file_code_lines(self): """Count the lines of code in the current file.""" editorWidget = self.get_current_editor() if editorWidget: block_count = editorWidget.lines() blanks = re.findall('(^\n)|(^(\s+)?#)|(^( +)?($|\n))', editorWidget.text(), re.M) blanks_count = len(blanks) resume = self.tr("Lines code: %s\n") % (block_count - blanks_count) resume += (self.tr("Blanks and commented lines: %s\n\n") % blanks_count) resume += self.tr("Total lines: %s") % blockdget msgBox.exec_() msgBox = QMessageBox(QMessageBox.Information, self.tr("Summary of lines"), resume, QMessageBox.Ok, editorWidget) def editor_cut(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.cut() def editor_copy(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.copy() def editor_paste(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.paste() def editor_upper(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.to_upper() def editor_lower(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.to_lower() def editor_title(self): editorWidget = self.get_current_editor() if editorWidget: editorWidget.to_title() def editor_go_to_definition(self): """Search the definition of the method or variable under the cursor. If more than one method or variable is found with the same name, shows a table with the results and let the user decide where to go.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.go_to_definition() def editor_redo(self): """Execute the redo action in the current editor.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.redo() def editor_undo(self): editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.undo() def editor_indent_less(self): """Indent 1 position to the left for the current line or selection.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.indent_less() def editor_indent_more(self): """Indent 1 position to the right for the current line or selection.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.indent_more() def editor_insert_debugging_prints(self): """Insert a print statement in each selected line.""" editorWidget = self.get_current_editor() if editorWidget: helpers.insert_debugging_prints(editorWidget) def editor_insert_pdb(self): """Insert a pdb.set_trace() statement in tjhe current line.""" editorWidget = self.get_current_editor() if editorWidget: helpers.insert_pdb(editorWidget) def editor_comment(self): """Mark the current line or selection as a comment.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.comment_or_uncomment(editorWidget) def editor_uncomment(self): """Uncomment the current line or selection.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.uncomment(editorWidget) def editor_insert_horizontal_line(self): """Insert an horizontal lines of comment symbols.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.insert_horizontal_line(editorWidget) def editor_insert_title_comment(self): """Insert a Title surrounded by comment symbols.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.insert_title_comment(editorWidget) def editor_remove_trailing_spaces(self): """Remove the trailing spaces in the current editor.""" editorWidget = self.get_current_editor() if editorWidget: helpers.remove_trailing_spaces(editorWidget) def editor_replace_tabs_with_spaces(self): """Replace the Tabs with Spaces in the current editor.""" editorWidget = self.get_current_editor() if editorWidget: helpers.replace_tabs_with_spaces(editorWidget) def editor_move_up(self): """Move the current line or selection one position up.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.move_up(editorWidget) def editor_move_down(self): """Move the current line or selection one position down.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.move_down(editorWidget) def editor_remove_line(self): """Remove the current line or selection.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.remove_line(editorWidget) def editor_duplicate(self): """Duplicate the current line or selection.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): helpers.duplicate(editorWidget) def editor_highlight_word(self): """Highlight the occurrences of the current word in the editor.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.highlight_selected_word() def editor_complete_declaration(self): """Do the opposite action that Complete Declaration expect.""" editorWidget = self.get_current_editor() if editorWidget and editorWidget.hasFocus(): editorWidget.complete_declaration() def editor_go_to_line(self, line): """ Jump to the specified line in the current editor. """ editorWidget = self.get_current_editor() if editorWidget: editorWidget.go_to_line(line) editorWidget.setFocus() def zoom_in_editor(self): """Increase the font size in the current editor.""" editorWidget = self.get_current_editor() if editorWidget: editorWidget.zoom(1.0) def zoom_out_editor(self): """Decrease the font size in the current editor.""" editorWidget = self.get_current_editor() if editorWidget: editorWidget.zoom(-1.0) def reset_zoom(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.reset_zoom() def recent_files_changed(self): self.emit(SIGNAL("recentTabsModified()")) def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() else: event.ignore() def dropEvent(self, event): file_path = event.mimeData().urls()[0].toLocalFile() self.open_file(file_path) def setFocus(self): widget = self.get_current_widget() if widget: widget.setFocus() def current_editor_changed(self, filename): """Notify the new filename of the current editor.""" if filename is None: filename = translations.TR_NEW_DOCUMENT self.currentEditorChanged.emit(filename) def show_split(self, orientation_vertical=False): # IDE.select_current(self.current_widget.currentWidget()) self.current_widget.split_editor(orientation_vertical) def add_editor(self, fileName=None, ignore_checkers=False): ninjaide = IDE.get_service('ide') editable = ninjaide.get_or_create_editable(fileName) if editable.editor: self.current_widget.set_current(editable) return self.current_widget.currentWidget() else: editable.ignore_checkers = ignore_checkers editorWidget = self.create_editor_from_editable(editable) # add the tab keep_index = (self.splitter.count() > 1 and self.combo_area.stacked.count() > 0) self.combo_area.add_editor(editable, keep_index) # emit a signal about the file open self.fileOpened.emit(fileName) if not keep_index: self.current_widget.set_current(editable) self.stack.setCurrentWidget(self.splitter) editorWidget.setFocus() return editorWidget def create_editor_from_editable(self, editable): neditor = editor.create_editor(editable) # Connect signals neditor.zoomChanged[int].connect(self._show_zoom_indicator) neditor.destroyed.connect(self._editor_destroyed) editable.fileSaved.connect(self._editor_tab_was_saved) neditor.addBackItemNavigation.connect(self.add_back_item_navigation) # self.connect(editable, SIGNAL("fileSaved(PyQt_PyObject)"), # self._editor_tab_was_saved) # editorWidget.font_changed.connect(self.show_zoom_indicator) # self.connect(editorWidget, SIGNAL("openDropFile(QString)"), # self.open_file) # self.connect(editorWidget, SIGNAL("addBackItemNavigation()"), # self.add_back_item_navigation) # self.connect(editorWidget, # SIGNAL("locateFunction(QString, QString, bool)"), # self._editor_locate_function) # self.connect(editorWidget, SIGNAL("findOcurrences(QString)"), # self._find_occurrences) # keyPressEventSignal for plugins # self.connect(editorWidget, SIGNAL("keyPressEvent(QEvent)"), # self._editor_keyPressEvent) return neditor def _editor_destroyed(self): ui_tools.FadingIndicator.editor_destroyed() def _show_zoom_indicator(self, text): neditor = self.get_current_editor() ui_tools.FadingIndicator.show_text(neditor, "Zoom: {}%".format(str(text))) def reset_pep8_warnings(self, value): pass # FIXME: check how we handle this # for i in range(self._tabMain.count()): # widget = self._tabMain.widget(i) # if type(widget) is editor.Editor: # if value: # widget.syncDocErrorsSignal = True # widget.pep8.check_style() # else: # widget.hide_pep8_errors() def reset_lint_warnings(self, value): pass #FIXME: check how we handle this # for i in range(self._tabMain.count()): #widget = self._tabMain.widget(i) #if type(widget) is editor.Editor: #if value: #widget.syncDocErrorsSignal = True #widget.errors.check_errors() #else: #widget.hide_lint_errors() def show_zoom_indicator(self, text): ui_tools.FadingIndicator.show_text(self, "Zoom: {0}%".format(text)) def _find_occurrences(self, word): self.emit(SIGNAL("findOcurrences(QString)"), word) def _editor_keyPressEvent(self, event): self.emit(SIGNAL("editorKeyPressEvent(QEvent)"), event) def _editor_locate_function(self, function, filePath, isVariable): self.emit(SIGNAL("locateFunction(QString, QString, bool)"), function, filePath, isVariable) def _editor_tab_was_saved(self, editable=None): self.updateLocator.emit(editable.file_path) # self.emit(SIGNAL("updateLocator(QString)"), editable.file_path) def get_current_widget(self): return self.current_widget.currentWidget() def get_current_editor(self): """Return the Actual Editor or None Return an instance of Editor if the Current Tab contains an Editor or None if it is not an instance of Editor""" widget = self.current_widget.currentWidget() if isinstance(widget, editor.NEditor): return widget return None def reload_file(self, editorWidget=None): if editorWidget is None: editorWidget = self.get_current_editor() editorWidget.neditable.reload_file() def open_image(self, fileName): try: if not self.is_open(fileName): viewer = image_viewer.ImageViewer(fileName) # self.add_tab(viewer, file_manager.get_basename(fileName)) # viewer.ID = fileName else: self.move_to_open(fileName) except Exception as reason: logger.error('open_image: %s', reason) QMessageBox.information(self, self.tr("Incorrect File"), self.tr("The image couldn\'t be open")) def open_file(self, filename='', line=-1, col=0, ignore_checkers=False): logger.debug("will try to open %s" % filename) if not filename: logger.debug("has nofilename") if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") editorWidget = self.get_current_editor() ninjaide = IDE.get_service('ide') if ninjaide: current_project = ninjaide.get_current_project() if current_project is not None: directory = current_project elif editorWidget is not None and editorWidget.file_path: directory = file_manager.get_folder( editorWidget.file_path) extensions = ';;'.join([ '{}(*{})'.format(e.upper()[1:], e) for e in settings.SUPPORTED_EXTENSIONS + ['.*', ''] ]) fileNames = QFileDialog.getOpenFileNames(self, self.tr("Open File"), directory, extensions)[0] else: logger.debug("has filename") fileNames = [filename] if not fileNames: return for filename in fileNames: image_extensions = ('bmp', 'gif', 'jpeg', 'jpg', 'png') if file_manager.get_file_extension(filename) in image_extensions: logger.debug("will open as image") self.open_image(filename) elif file_manager.get_file_extension(filename).endswith('ui'): logger.debug("will load in ui editor") self.w = uic.loadUi(filename) self.w.show() else: logger.debug("will try to open: " + filename) self.__open_file(filename, line, col, ignore_checkers) def __open_file(self, fileName='', line=-1, col=0, ignore_checkers=False): try: editorWidget = self.add_editor(fileName, ignore_checkers=ignore_checkers) if line != -1: editorWidget.go_to_line(line, col) self.currentEditorChanged.emit(fileName) except file_manager.NinjaIOException as reason: QMessageBox.information(self, self.tr("The file couldn't be open"), str(reason)) def is_open(self, filename): pass #return self.tabs.is_open(filename) != -1 def move_to_open(self, filename): pass #FIXME: add in the current split? #if self.tabs.is_open(filename) != -1: #self.tabs.move_to_open(filename) #self.tabs.currentWidget().setFocus() #self.emit(SIGNAL("currentEditorChanged(QString)"), filename) def get_widget_for_id(self, filename): pass #widget = None #index = self.tabs.is_open(filename) #if index != -1: #widget = self.tabs.widget(index) #return widget def change_open_tab_id(self, idname, newId): """Search for the Tab with idname, and set the newId to that Tab.""" pass #index = self.tabs.is_open(idname) #if index != -1: #widget = self.tabs.widget(index) #tabName = file_manager.get_basename(newId) #self.tabs.change_open_tab_name(index, tabName) #widget.ID = newId def close_deleted_file(self, idname): """Search for the Tab with id, and ask the user if should be closed.""" pass #index = self.tabs.is_open(idname) #if index != -1: #result = QMessageBox.question(self, self.tr("Close Deleted File"), #self.tr("Are you sure you want to close the deleted file?\n" #"The content will be completely deleted."), #buttons=QMessageBox.Yes | QMessageBox.No) #if result == QMessageBox.Yes: #self.tabs.removeTab(index) def save_file(self, editorWidget=None): # FIXME: check how we handle this if not editorWidget: editorWidget = self.get_current_editor() if editorWidget is None: return False # Ok, we have an editor instance # Save to file only if editor really was modified if editorWidget.is_modified: try: if (editorWidget.nfile.is_new_file or not editorWidget.nfile.has_write_permission()): return self.save_file_as() self.beforeFileSaved.emit(editorWidget.file_path) if settings.REMOVE_TRAILING_SPACES: helpers.remove_trailing_spaces(editorWidget) # New line at end # FIXME: from settings helpers.insert_block_at_end(editorWidget) # Save convent editorWidget.neditable.save_content() encoding = file_manager.get_file_encoding(editorWidget.text) editorWidget.encoding = encoding self.fileSaved.emit( self.tr("File Saved: {}".format(editorWidget.file_path))) return True except Exception as reason: logger.error('save_file: %s', reason) QMessageBox.information(self, self.tr("Save Error"), self.tr("The file couldn't be saved!")) return False def save_file_as(self): editorWidget = self.get_current_editor() if not editorWidget: return False try: filters = '(*.py);;(*.*)' if editorWidget.file_path: # existing file ext = file_manager.get_file_extension(editorWidget.file_path) if ext != 'py': filters = '(*.%s);;(*.py);;(*.*)' % ext save_folder = self._get_save_folder(editorWidget.file_path) fileName = QFileDialog.getSaveFileName(self._parent, self.tr("Save File"), save_folder, filters)[0] if not fileName: return False if settings.REMOVE_TRAILING_SPACES: helpers.remove_trailing_spaces(editorWidget) ext = file_manager.get_file_extension(fileName) if not ext: fileName = '%s.%s' % ( fileName, 'py', ) editorWidget.neditable.save_content(path=fileName) # editorWidget.register_syntax( # file_manager.get_file_extension(fileName)) self.fileSaved.emit(self.tr("File Saved: {}".format(fileName))) self.currentEditorChanged.emit(fileName) return True except file_manager.NinjaFileExistsException as ex: QMessageBox.information( self, self.tr("File Already Exists"), (self.tr("Invalid Path: the file '%s' " " already exists.") % ex.filename)) except Exception as reason: logger.error('save_file_as: %s', reason) QMessageBox.information(self, self.tr("Save Error"), self.tr("The file couldn't be saved!")) return False def _get_save_folder(self, fileName): """ Returns the root directory of the 'Main Project' or the home folder """ ninjaide = IDE.get_service('ide') current_project = ninjaide.get_current_project() if current_project: return current_project.path return os.path.expanduser("~") def save_project(self, projectFolder): pass #FIXME: check how we handle this #for i in range(self._tabMain.count()): #editorWidget = self._tabMain.widget(i) #if type(editorWidget) is editor.Editor and \ #file_manager.belongs_to_folder(projectFolder, #editorWidget.file_path): #reloaded = self._tabMain.check_for_external_modifications( #editorWidget) #if not reloaded: #self.save_file(editorWidget) #for i in range(self.tabsecondary.count()): #editorWidget = self.tabsecondary.widget(i) #if type(editorWidget) is editor.Editor and \ #file_manager.belongs_to_folder(projectFolder, #editorWidget.file_path): #reloaded = self.tabsecondary.check_for_external_modifications( #editorWidget) #if not reloaded: #self.save_file(editorWidget) def save_all(self): pass #FIXME: check how we handle this #for i in range(self._tabMain.count()): #editorWidget = self._tabMain.widget(i) #if type(editorWidget) is editor.Editor: #reloaded = self._tabMain.check_for_external_modifications( #editorWidget) #if not reloaded: #self.save_file(editorWidget) #for i in range(self.tabsecondary.count()): #editorWidget = self.tabsecondary.widget(i) #self.tabsecondary.check_for_external_modifications(editorWidget) #if type(editorWidget) is editor.Editor: #reloaded = self.tabsecondary.check_for_external_modifications( #editorWidget) #if not reloaded: #self.save_file(editorWidget) def call_editors_function(self, call_function, *arguments): pass #args = arguments[0] #kwargs = arguments[1] #for i in range(self.tabs.count()): #editorWidget = self.tabs.widget(i) #if isinstance(editorWidget, editor.Editor): #function = getattr(editorWidget, call_function) #function(*args, **kwargs) #TODO: add other splits def show_start_page(self): start = self.stack.widget(0) if isinstance(start, start_page.StartPage): self.stack.setCurrentIndex(0) else: startPage = start_page.StartPage(parent=self) # self.connect(startPage, SIGNAL("openProject(QString)"), # self.open_project) # self.connect(startPage, SIGNAL("openPreferences()"), # lambda: self.emit(SIGNAL("openPreferences()"))) # Connections startPage.newFile.connect(self.add_editor) self.stack.insertWidget(0, startPage) self.stack.setCurrentIndex(0) def show_python_doc(self): if sys.platform == 'win32': self.docPage = browser_widget.BrowserWidget( 'http://docs.python.org/') else: process = runner.start_pydoc() self.docPage = browser_widget.BrowserWidget(process[1], process[0]) self.add_tab(self.docPage, translations.TR_PYTHON_DOC) def show_report_bugs(self): webbrowser.open(resources.BUGS_PAGE) def show_plugins_doc(self): bugsPage = browser_widget.BrowserWidget(resources.PLUGINS_DOC, self) self.add_tab(bugsPage, translations.TR_HOW_TO_WRITE_PLUGINS) def editor_jump_to_line(self, lineno=None): """Jump to line *lineno* if it is not None otherwise ask to the user the line number to jump """ editorWidget = self.get_current_editor() if editorWidget: editorWidget.jump_to_line(lineno=lineno) def get_opened_documents(self): #return self.tabs.get_documents_data() return [] def check_for_unsaved_files(self): pass #return self.tabs._check_unsaved_tabs() def get_unsaved_files(self): pass #return self.tabs.get_unsaved_files() def reset_editor_flags(self): pass #for i in range(self.tabs.count()): #widget = self.tabs.widget(i) #if isinstance(widget, editor.Editor): #widget.set_flags() def _specify_syntax(self, widget, syntaxLang): if isinstance(widget, editor.Editor): widget.restyle(syntaxLang) def apply_editor_theme(self, family, size): pass #for i in range(self.tabs.count()): #widget = self.tabs.widget(i) #if isinstance(widget, editor.Editor): #widget.restyle() #widget.set_font(family, size) def update_editor_margin_line(self): pass #for i in range(self.tabs.count()): #widget = self.tabs.widget(i) #if isinstance(widget, editor.Editor): #widget._update_margin_line() def open_project(self, path): self.emit(SIGNAL("openProject(QString)"), path) def close_python_doc(self): pass # close the python document server (if running) # if self.docPage: # index = self.tabs.indexOf(self.docPage) # self.tabs.removeTab(index) # assign None to the browser # self.docPage = None def close_file(self): self.current_widget.close_current_file() def create_file(self, base_path, project_path): self._add_file_folder.create_file(base_path, project_path) def create_folder(self, base_path, project_path): self._add_file_folder.create_folder(base_path, project_path) def change_tab(self): """Change the tab in the current TabWidget.""" self.stack.setCurrentWidget(self.splitter) # self._files_handler.next_item() def change_tab_reverse(self): """Change the tab in the current TabWidget backwards.""" self.stack.setCurrentWidget(self.splitter) # self._files_handler.previous_item() def toggle_tabs_and_spaces(self): """Toggle Show/Hide Tabs and Spaces""" settings.SHOW_TABS_AND_SPACES = not settings.SHOW_TABS_AND_SPACES qsettings = IDE.ninja_settings() qsettings.setValue('preferences/editor/show_tabs_and_spaces', settings.SHOW_TABS_AND_SPACES) neditor = self.get_current_editor() if neditor is not None: neditor.show_whitespaces = settings.SHOW_TABS_AND_SPACES def show_navigation_buttons(self): """Show Navigation menu.""" self.stack.setCurrentWidget(self.splitter) self.combo_area.show_menu_navigation() def change_split_focus(self): pass #FIXME: check how we handle this #if self.actualTab == self._tabMain and self.tabsecondary.isVisible(): #self.actualTab = self.tabsecondary #else: #self.actualTab = self._tabMain #widget = self.actualTab.currentWidget() #if widget is not None: #widget.setFocus() def shortcut_index(self, index): pass #self.tabs.setCurrentIndex(index) def print_file(self): """Call the print of ui_tool Call print of ui_tool depending on the focus of the application""" #TODO: Add funtionality for proyect tab and methods tab editorWidget = self.get_current_editor() if editorWidget is not None: fileName = "newDocument.pdf" if editorWidget.file_path: fileName = file_manager.get_basename(editorWidget.file_path) fileName = fileName[:fileName.rfind('.')] + '.pdf' ui_tools.print_file(fileName, editorWidget.print_) def split_assistance(self): dialog = split_orientation.SplitOrientation(self) dialog.show() # def close_split(self): # if self.current_widget != self.combo_area: # self.current_widget.bar.close_split() # def split_vertically(self): # self.show_split(False) # def split_horizontally(self): # self.show_split(True) def navigate_back(self): self.__navigate_with_keyboard(False) def navigate_forward(self): self.__navigate_with_keyboard(True)
class Blur(Plugin): def __init__(self, parent = None): super(Blur, self).__init__() self.setObjectName('blur') # should be plugin name # declare all parameter widgets below vbox1 = QVBoxLayout() vbox1.setSpacing(2) hbox1 = QHBoxLayout() hbox1.setSpacing(2) labels = ['box', 'Gaussian', 'Median', 'Bilateral'] groupBox, self.bgrpBlurMethod = radio_filler('Blur Method', labels, buttons_per_row = 2, tool_tips = None) self.bgrpBlurMethod.buttonClicked.connect(self.radio_handler) self.stackedLayout = QStackedLayout() self.boxPage = QWidget() self.gaussianPage = QWidget() self.medianPage = QWidget() self.bilateralPage = QWidget() gridLayout = QGridLayout() gridLayout.setSpacing(2) self.dblBoxKSize = QDoubleSpinBox() self.dblBoxKSize.setObjectName('dblBoxKSize') self.dblBoxKSize.setValue(5) self.dblBoxKSize.setMinimum(1) self.dblBoxKSize.setSingleStep(2) tt = '''blurring kernel size''' self.dblBoxKSize.setToolTip(tt) gridLayout.addWidget(QLabel('ksize')) gridLayout.addWidget(self.dblBoxKSize) self.boxPage.setLayout(gridLayout) gridLayout = QGridLayout() gridLayout.setSpacing(2) self.dblGaussianKSize = QDoubleSpinBox() self.dblGaussianKSize.setObjectName('dblGaussianKSize') self.dblGaussianKSize.setValue(5) self.dblGaussianKSize.setMinimum(1) self.dblGaussianKSize.setSingleStep(2) tt = '''blurring kernel size''' self.dblGaussianKSize.setToolTip(tt) self.dblSigma = QDoubleSpinBox() self.dblSigma.setObjectName('dblSigma') self.dblSigma.setValue(0) tt = '''Gaussian kernel standard deviation. If sigma is zero, it is computed from ksize (see getGaussianKernel for details);''' self.dblSigma.setToolTip(tt) self.dblSigma.setSingleStep(.1) gridLayout.addWidget(QLabel('ksize'), 0, 0) gridLayout.addWidget(self.dblGaussianKSize, 0 , 1) gridLayout.addWidget(QLabel('sigma'), 1 ,0 ) gridLayout.addWidget(self.dblSigma, 1, 1) self.gaussianPage.setLayout(gridLayout) gridLayout = QGridLayout() gridLayout.setSpacing(2) self.dblMedianKSize = QDoubleSpinBox() self.dblMedianKSize.setObjectName('dblMedianKSize') self.dblMedianKSize.setValue(5) self.dblMedianKSize.setMinimum(1) self.dblMedianKSize.setSingleStep(2) tt = '''aperture linear size; it must be odd and greater than 1, for example: 3, 5, 7 ... ''' self.dblMedianKSize.setToolTip(tt) gridLayout.addWidget(QLabel('ksize')) gridLayout.addWidget(self.dblMedianKSize) self.medianPage.setLayout(gridLayout) gridLayout = QGridLayout() gridLayout.setSpacing(2) self.dblD = QDoubleSpinBox() self.dblD.setObjectName('dblD') self.dblD.setValue(5) self.dblD.setMinimum(1) self.dblD.setSingleStep(1) tt = '''Diameter of each pixel neighborhood that is used during filtering. If it is non-positive, it is computed from sigma''' self.dblD.setToolTip(tt) self.dblBSigma = QDoubleSpinBox() self.dblBSigma.setObjectName('dblBSigma') self.dblBSigma.setValue(50) tt = '''Filter sigma in the color space. A larger value of the parameter means that farther colors within the pixel neighborhood (see sigmaSpace) will be mixed together, resulting in larger areas of semi-equal color.''' self.dblBSigma.setToolTip(tt) self.dblBSigma.setSingleStep(.1) gridLayout.addWidget(QLabel('d'), 0, 0) gridLayout.addWidget(self.dblD, 0 , 1) gridLayout.addWidget(QLabel('sigma'), 1, 0) gridLayout.addWidget(self.dblBSigma ,1 ,1) self.bilateralPage.setLayout(gridLayout) self.stackedLayout.addWidget(self.boxPage) self.stackedLayout.addWidget(self.gaussianPage) self.stackedLayout.addWidget(self.medianPage) self.stackedLayout.addWidget(self.bilateralPage) vbox1.addWidget(groupBox) vbox1.addLayout(self.stackedLayout) self.setLayout(vbox1) # ~ self.img = None # set all sizepolicy s to preferred for item in self.findChildren(QWidget): item.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) # set margins and spacing for all Layouts for item in self.findChildren(QLayout): item.setContentsMargins(0, 0, 0, 0) item.setSpacing(2) self.bgrpBlurMethod.button(0).setChecked(True) def radio_handler(self): self.stackedLayout.setCurrentIndex(self.bgrpBlurMethod.checkedId()) def mainFunc(self, playing, scriptList, row): if self.inputImg is not None: if self.stackedLayout.currentIndex() == 0: # box blur ksize = int(self.dblBoxKSize.value()) ksize = (ksize, ksize) # ~ anchor = (-1, -1) self.outputImg = cv.blur(self.inputImg, ksize) elif self.stackedLayout.currentIndex() == 1: # Gaussian blur ksize = int(self.dblGaussianKSize.value()) ksize = (ksize, ksize) sigma = self.dblSigma.value() # ~ anchor = (-1, -1) self.outputImg = cv.GaussianBlur(self.inputImg, ksize, sigma, sigma) elif self.stackedLayout.currentIndex() == 2: # median blur ksize = int(self.dblMedianKSize.value()) self.outputImg = cv.medianBlur(self.inputImg, ksize) elif self.stackedLayout.currentIndex() == 3: # bilateral d = int(self.dblD.value()) sigma = self.dblBSigma.value() self.outputImg = cv.bilateralFilter(self.inputImg, d, sigma, sigma)
class ProjectTreeColumn(QDialog): # Signals dockWidget = pyqtSignal('PyQt_PyObject') undockWidget = pyqtSignal() changeTitle = pyqtSignal('PyQt_PyObject', 'QString') updateLocator = pyqtSignal() def __init__(self, parent=None): super(ProjectTreeColumn, self).__init__(parent) vbox = QVBoxLayout(self) # vbox.setSizeConstraint(QVBoxLayout.SetDefaultConstraint) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self._buttons = [] combo_container = ui_tools.StyledBar() combo_container.setProperty('gradient', True) combo_layout = QVBoxLayout(combo_container) combo_layout.setContentsMargins(0, 0, 0, 0) self._combo_project = QComboBox() combo_layout.addWidget(self._combo_project) self._combo_project.setProperty("gradient", True) self._combo_project.setContextMenuPolicy(Qt.CustomContextMenu) vbox.addWidget(combo_container) self._projects_area = QStackedLayout() logger.debug("This is the projects area") vbox.addLayout(self._projects_area) self.projects = [] self._combo_project.currentIndexChanged[int].connect( self._change_current_project) self._combo_project.customContextMenuRequested[ 'const QPoint&'].connect(self.context_menu_for_root) connections = ( { 'target': 'main_container', 'signal_name': 'addToProject(QString)', 'slot': self._add_file_to_project }, { 'target': 'main_container', 'signal_name': 'showFileInExplorer(QString)', 'slot': self._show_file_in_explorer }, ) IDE.register_service('projects_explorer', self) IDE.register_signals('projects_explorer', connections) ExplorerContainer.register_tab(translations.TR_TAB_PROJECTS, self) # FIXME: Should have a ninja settings object that stores tree state # FIXME: Or bettter, application data object # TODO: check this: # self.connect(ide, SIGNAL("goingDown()"), # self.tree_projects.shutdown) # def close_project_signal(): # self.emit(SIGNAL("updateLocator()")) def install_tab(self): ide = IDE.get_service('ide') ui_tools.install_shortcuts(self, actions.PROJECTS_TREE_ACTIONS, ide) ide.goingDown.connect(self.close) def load_session_projects(self, projects): for project in projects: if os.path.exists(project): self._open_project_folder(project) def open_project_folder(self, folderName=None): if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") if folderName is None: folderName = QFileDialog.getExistingDirectory( self, translations.TR_OPEN_PROJECT_DIRECTORY, directory) logger.debug("Choosing Foldername") if folderName: if not file_manager.folder_exists(folderName): QMessageBox.information( self, translations.TR_PROJECT_NONEXIST_TITLE, translations.TR_PROJECT_NONEXIST % folderName) return logger.debug("Opening %s" % folderName) for p in self.projects: if p.project.path == folderName: QMessageBox.information( self, translations.TR_PROJECT_PATH_ALREADY_EXIST_TITLE, translations.TR_PROJECT_PATH_ALREADY_EXIST % folderName) return self._open_project_folder(folderName) def _open_project_folder(self, folderName): ninjaide = IDE.get_service("ide") # TODO: handle exception when .nja file is empty project = NProject(folderName) qfsm = ninjaide.filesystem.open_project(project) if qfsm: self.add_project(project) self.save_recent_projects(folderName) # FIXME: show editor area? # main_container = IDE.get_service('main_container') # if main_container: # main_container.show_editor_area() if len(self.projects) > 1: title = "%s (%s)" % (translations.TR_TAB_PROJECTS, len(self.projects)) else: title = translations.TR_TAB_PROJECTS self.changeTitle.emit(self, title) def _add_file_to_project(self, path): """Add the file for 'path' in the project the user choose here.""" if self._projects_area.count() > 0: pathProject = [self.current_project] addToProject = add_to_project.AddToProject(pathProject, self) addToProject.exec_() if not addToProject.pathSelected: return main_container = IDE.get_service('main_container') if not main_container: return editorWidget = main_container.get_current_editor() if not editorWidget.file_path: name = QInputDialog.getText( None, translations.TR_ADD_FILE_TO_PROJECT, translations.TR_FILENAME + ": ")[0] if not name: QMessageBox.information( self, translations.TR_INVALID_FILENAME, translations.TR_INVALID_FILENAME_ENTER_A_FILENAME) return else: name = file_manager.get_basename(editorWidget.file_path) new_path = file_manager.create_path(addToProject.pathSelected, name) ide_srv = IDE.get_service("ide") old_file = ide_srv.get_or_create_nfile(path) new_file = old_file.save(editorWidget.text(), new_path) # FIXME: Make this file replace the original in the open tab else: pass # Message about no project def _show_file_in_explorer(self, path): '''Iterate through the list of available projects and show the current file in the explorer view for the first project that contains it (i.e. if the same file is included in multiple open projects, the path will be expanded for the first project only). Note: This slot is connected to the main container's "showFileInExplorer(QString)" signal.''' central = IDE.get_service('central_container') if central and not central.is_lateral_panel_visible(): return for project in self.projects: index = project.model().index(path) if index.isValid(): # This highlights the index in the tree for us project.scrollTo(index, QAbstractItemView.EnsureVisible) project.setCurrentIndex(index) break def add_project(self, project): if project not in self.projects: self._combo_project.addItem(project.name) ptree = TreeProjectsWidget(project) self._projects_area.addWidget(ptree) ptree.closeProject['PyQt_PyObject'].connect(self._close_project) pmodel = project.model ptree.setModel(pmodel) pindex = pmodel.index(pmodel.rootPath()) ptree.setRootIndex(pindex) self.projects.append(ptree) current_index = self._projects_area.count() self._projects_area.setCurrentIndex(current_index - 1) self._combo_project.setCurrentIndex(current_index - 1) def _close_project(self, widget): """Close the project related to the tree widget.""" index = self._projects_area.currentIndex() self.projects.remove(widget) self._projects_area.takeAt(index) self._combo_project.removeItem(index) index = self._combo_project.currentIndex() self._projects_area.setCurrentIndex(index) ninjaide = IDE.get_service('ide') ninjaide.filesystem.close_project(widget.project.path) widget.deleteLater() if len(self.projects) > 1: title = "%s (%s)" % (translations.TR_TAB_PROJECTS, len(self.projects)) else: title = translations.TR_TAB_PROJECTS self.changeTitle.emit(self, title) self.updateLocator.emit() def _change_current_project(self, index): self._projects_area.setCurrentIndex(index) def close_opened_projects(self): for project in reversed(self.projects): self._close_project(project) def save_project(self): """Save all the opened files that belongs to the actual project.""" if self._projects_area.count() > 0: path = self.current_project.path main_container = IDE.get_service('main_container') if path and main_container: main_container.save_project(path) def create_new_project(self): wizard = new_project_manager.NewProjectManager(self) wizard.show() @property def current_project(self): if self._projects_area.count() > 0: return self._projects_area.currentWidget().project @property def current_tree(self): return self._projects_area.currentWidget() def set_current_item(self, path): if self.current_project is not None: self.current_tree.set_current_item(path) def save_recent_projects(self, folder): settings = IDE.data_settings() recent_project_list = settings.value('recentProjects', {}) # if already exist on the list update the date time projectProperties = json_manager.read_ninja_project(folder) name = projectProperties.get('name', '') description = projectProperties.get('description', '') if name == '': name = file_manager.get_basename(folder) if description == '': description = translations.TR_NO_DESCRIPTION if folder in recent_project_list: properties = recent_project_list[folder] properties["lastopen"] = QDateTime.currentDateTime() properties["name"] = name properties["description"] = description recent_project_list[folder] = properties else: recent_project_list[folder] = { "name": name, "description": description, "isFavorite": False, "lastopen": QDateTime.currentDateTime() } # if the length of the project list it's high that 10 then delete # the most old # TODO: add the length of available projects to setting if len(recent_project_list) > 10: del recent_project_list[self.find_most_old_open( recent_project_list)] settings.setValue('recentProjects', recent_project_list) def find_most_old_open(self, recent_project_list): listFounder = [] for recent_project_path, content in list(recent_project_list.items()): listFounder.append( (recent_project_path, int(content["lastopen"].toString("yyyyMMddHHmmzzz")))) listFounder = sorted(listFounder, key=lambda date: listFounder[1], reverse=True) # sort by date last used return listFounder[0][0] def reject(self): if self.parent() is None: self.dockWidget.emit(self) def closeEvent(self, event): self.dockWidget.emit(self) event.ignore() def context_menu_for_root(self): menu = QMenu(self) path = self.current_tree.project.path action_add_file = menu.addAction(QIcon(":img/new"), translations.TR_ADD_NEW_FILE) action_add_folder = menu.addAction(QIcon(":img/openProj"), translations.TR_ADD_NEW_FOLDER) action_create_init = menu.addAction(translations.TR_CREATE_INIT) # self.connect(action_add_file, SIGNAL("triggered()"), # lambda: self.current_tree._add_new_file(path)) # self.connect(action_add_folder, SIGNAL("triggered()"), # lambda: self.current_tree._add_new_folder(path)) # self.connect(action_create_init, SIGNAL("triggered()"), # lambda: self.current_tree._create_init(path)) # menu.addSeparator() # actionRunProject = menu.addAction(QIcon( # ":img/play"), translations.TR_RUN_PROJECT) # self.connect(actionRunProject, SIGNAL("triggered()"), # self.current_tree._execute_project) # if self.current_tree._added_to_console: # actionRemoveFromConsole = menu.addAction( # translations.TR_REMOVE_PROJECT_FROM_PYTHON_CONSOLE) # self.connect(actionRemoveFromConsole, SIGNAL("triggered()"), # self.current_tree._remove_project_from_console) # else: # actionAdd2Console = menu.addAction( # translations.TR_ADD_PROJECT_TO_PYTHON_CONSOLE) # self.connect(actionAdd2Console, SIGNAL("triggered()"), # self.current_tree._add_project_to_console) # actionShowFileSizeInfo = menu.addAction( # translations.TR_SHOW_FILESIZE) # self.connect(actionShowFileSizeInfo, SIGNAL("triggered()"), # self.current_tree.show_filesize_info) # actionProperties = menu.addAction(QIcon(":img/pref"), # translations.TR_PROJECT_PROPERTIES) # self.connect(actionProperties, SIGNAL("triggered()"), # self.current_tree.open_project_properties) action_properties = menu.addAction(translations.TR_PROJECT_PROPERTIES) action_properties.triggered.connect( self.current_tree.open_project_properties) # menu.addSeparator() action_close = menu.addAction(translations.TR_CLOSE_PROJECT) action_add_file.triggered.connect( lambda: self.current_tree._add_new_file(path)) action_add_folder.triggered.connect( lambda: self.current_tree._add_new_folder(path)) action_close.triggered.connect(self.current_tree._close_project) # self.connect(action_close, SIGNAL("triggered()"), # self.current_tree._close_project) # menu for the project for m in self.current_tree.extra_menus_by_scope['project']: if isinstance(m, QMenu): menu.addSeparator() menu.addMenu(m) # show the menu! menu.exec_(QCursor.pos())
class FileConverterOutputTab(QWidget): def __init__(self, inputTab): super().__init__() self.input = inputTab self.EMPTY = 0 self.BK_SHP = 1 self.Z_FROM_SHP = 2 self.M_FROM_SHP = 3 self.SHP_BK = 4 self.Z_AND_BK = 5 self.Z_AND_M = 6 self.convert_type = { 'xyz': { 'xyz': self.EMPTY, 'shp PointZ': self.BK_SHP, 'csv': self.EMPTY }, 'i2s': { 'i2s': self.EMPTY, 'shp Polyline': self.BK_SHP, 'shp Polygon': self.BK_SHP, 'csv': self.EMPTY }, 'i3s': { 'i3s': self.EMPTY, 'i2s': self.EMPTY, 'shp PolylineZ': self.BK_SHP, 'shp PolygonZ': self.BK_SHP, 'csv': self.EMPTY }, 'shp Point': { 'shp Point': self.EMPTY, 'shp PointZ': self.Z_AND_M, 'xyz': self.Z_FROM_SHP, 'csv': self.EMPTY }, 'shp PointZ': { 'shp Point': self.EMPTY, 'shp PointZ': self.Z_AND_M, 'shp PointM': self.M_FROM_SHP, 'xyz': self.Z_FROM_SHP, 'csv': self.EMPTY }, 'shp PointM': { 'shp Point': self.EMPTY, 'shp PointM': self.M_FROM_SHP, 'shp PointZ': self.Z_AND_M, 'xyz': self.Z_FROM_SHP, 'csv': self.EMPTY }, 'shp Polyline': { 'shp Polyline': self.EMPTY, 'shp PolylineZ': self.Z_AND_M, 'i2s': self.SHP_BK, 'i3s': self.Z_AND_BK, 'csv': self.EMPTY }, 'shp Polygon': { 'shp Polygon': self.EMPTY, 'shp PolygonZ': self.Z_AND_M, 'i2s': self.SHP_BK, 'i3s': self.Z_AND_BK, 'csv': self.EMPTY }, 'shp PolylineZ': { 'shp PolylineZ': self.Z_AND_M, 'shp Polyline': self.EMPTY, 'i2s': self.SHP_BK, 'i3s': self.Z_AND_BK, 'csv': self.EMPTY }, 'shp PolygonZ': { 'shp PolygonZ': self.Z_AND_M, 'shp Polygon': self.EMPTY, 'i2s': self.SHP_BK, 'i3s': self.Z_AND_BK, 'csv': self.EMPTY }, 'shp PolylineM': { 'shp Polyline': self.EMPTY, 'shp PolylineM': self.M_FROM_SHP, 'shp PolylineZ': self.Z_AND_M, 'i2s': self.SHP_BK, 'i3s': self.Z_AND_BK, 'csv': self.EMPTY }, 'shp PolygonM': { 'shp Polygon': self.EMPTY, 'shp PolygonM': self.M_FROM_SHP, 'shp PolygonZ': self.Z_AND_M, 'i2s': self.SHP_BK, 'i3s': self.Z_AND_BK, 'csv': self.EMPTY }, 'shp Multipoint': { 'shp MultiPoint': self.EMPTY, 'shp MultiPointZ': self.Z_AND_M, 'shp MultiPointM': self.M_FROM_SHP, 'shp Point': self.EMPTY, 'shp PointZ': self.Z_AND_M, 'shp PointM': self.M_FROM_SHP, 'xyz': self.Z_FROM_SHP, 'csv': self.EMPTY }, 'shp MultiPoint': { 'shp MultiPoint': self.EMPTY, 'shp MultiPointZ': self.Z_AND_M, 'shp MultiPointM': self.M_FROM_SHP, 'shp Point': self.EMPTY, 'shp PointZ': self.Z_AND_M, 'shp PointM': self.M_FROM_SHP, 'xyz': self.Z_FROM_SHP, 'csv': self.EMPTY }, 'shp MultiPointZ': { 'shp MultiPoint': self.EMPTY, 'shp MultiPointZ': self.Z_AND_M, 'shp MultiPointM': self.M_FROM_SHP, 'shp Point': self.EMPTY, 'shp PointZ': self.Z_AND_M, 'shp PointM': self.M_FROM_SHP, 'xyz': self.Z_FROM_SHP, 'csv': self.EMPTY }, 'shp MultiPointM': { 'shp MultiPoint': self.EMPTY, 'shp MultiPointZ': self.Z_AND_M, 'shp MultiPointM': self.M_FROM_SHP, 'shp Point': self.EMPTY, 'shp PointZ': self.Z_AND_M, 'shp PointM': self.M_FROM_SHP, 'xyz': self.Z_FROM_SHP, 'csv': self.EMPTY } } self._initWidgets() self._setLayout() self._bindEvents() def _initWidgets(self): # create a text for input information self.inputBox = QPlainTextEdit() self.inputBox.setFixedHeight(60) self.inputBox.setReadOnly(True) # create the widget displaying message logs self.logTextBox = QPlainTextEditLogger(self) self.logTextBox.setFormatter( logging.Formatter(settings.LOGGING_FMT_GUI)) logging.getLogger().addHandler(self.logTextBox) logging.getLogger().setLevel(self.input.parent.logging_level) # create a combo box for output file type self.outTypeBox = QComboBox() self.outTypeBox.setFixedHeight(30) # create a text box for output file name self.outNameBox = QLineEdit() self.outNameBox.setReadOnly(True) self.outNameBox.setFixedHeight(30) # create the option panel self.stack = QStackedLayout() self.empty = QWidget() self.bkshp = QWidget() hlayout = QHBoxLayout() hlayout.addWidget(QLabel('Attribute name')) self.bkshpname = QLineEdit('Value') self.bkshpname.setFixedHeight(30) hlayout.addWidget(self.bkshpname) self.bkshp.setLayout(hlayout) # Fill Z for xyz self.zfield = QWidget() hlayout = QHBoxLayout() hlayout.addWidget(QLabel('Fill Z with')) self.zfieldchoice = QComboBox() self.zfieldchoice.setFixedHeight(30) hlayout.addWidget(self.zfieldchoice, Qt.AlignLeft) self.zfield.setLayout(hlayout) self.mfield = QWidget() hlayout = QHBoxLayout() hlayout.addWidget(QLabel('Fill M with')) self.mfieldchoice = QComboBox() self.mfieldchoice.setFixedHeight(30) hlayout.addWidget(self.mfieldchoice, Qt.AlignLeft) self.mfield.setLayout(hlayout) self.shpbk = QWidget() hlayout = QHBoxLayout() hlayout.addWidget(QLabel('Fill attribute with')) self.shpbkmethod = QComboBox() self.shpbkmethod.setFixedHeight(30) hlayout.addWidget(self.shpbkmethod, Qt.AlignLeft) self.shpbk.setLayout(hlayout) self.shpbkz = QWidget() vlayout = QVBoxLayout() hlayout = QHBoxLayout() hlayout.addWidget(QLabel('Fill Z with')) self.zfieldchoicebis = QComboBox() self.zfieldchoicebis.setFixedHeight(30) hlayout.addWidget(self.zfieldchoicebis, Qt.AlignLeft) vlayout.addLayout(hlayout) hlayout = QHBoxLayout() hlayout.addWidget(QLabel('Fill attribute with')) self.shpbkmethodbis = QComboBox() self.shpbkmethodbis.setFixedHeight(30) hlayout.addWidget(self.shpbkmethodbis, Qt.AlignLeft) vlayout.addLayout(hlayout) self.shpbkz.setLayout(vlayout) self.shpzm = QWidget() vlayout = QVBoxLayout() hlayout = QHBoxLayout() hlayout.addWidget(QLabel('Fill Z with')) self.zfieldchoiceter = QComboBox() self.zfieldchoiceter.setFixedHeight(30) hlayout.addWidget(self.zfieldchoiceter, Qt.AlignLeft) vlayout.addLayout(hlayout) hlayout = QHBoxLayout() hlayout.addWidget(QLabel('Fill M with')) self.mfieldchoicebis = QComboBox() self.mfieldchoicebis.setFixedHeight(30) hlayout.addWidget(self.mfieldchoicebis, Qt.AlignLeft) vlayout.addLayout(hlayout) self.shpzm.setLayout(vlayout) for panel in [ self.empty, self.bkshp, self.zfield, self.mfield, self.shpbk, self.shpbkz, self.shpzm ]: self.stack.addWidget(panel) self.stackbis = QStackedLayout() self.emptybis = QWidget() self.resample = QGroupBox('Re-sample lines by Maximum Length') self.resample.setCheckable(True) vlayout = QVBoxLayout() self.valueButton = QRadioButton('Use constant') self.valueBox = QLineEdit('1') self.choiceButton = QRadioButton('Use attribute') self.choiceBox = QComboBox() hlayout = QHBoxLayout() hlayout.addWidget(self.valueButton) hlayout.addWidget(self.valueBox) vlayout.addLayout(hlayout) hlayout = QHBoxLayout() hlayout.addWidget(self.choiceButton) hlayout.addWidget(self.choiceBox, Qt.AlignLeft) vlayout.addLayout(hlayout) self.resample.setLayout(vlayout) self.choiceBox.setVisible(False) self.valueBox.setVisible(False) self.resample.setChecked(False) self.valueButton.toggled.connect( lambda checked: self.valueBox.setVisible(checked)) self.choiceButton.toggled.connect( lambda checked: self.choiceBox.setVisible(checked)) self.valueButton.setChecked(True) self.stackbis.addWidget(self.emptybis) self.stackbis.addWidget(self.resample) self.stackbis.setCurrentIndex(1) # create the submit button self.btnSubmit = QPushButton('Submit', self, icon=self.style().standardIcon( QStyle.SP_DialogSaveButton)) self.btnSubmit.setToolTip('<b>Submit</b>') self.btnSubmit.setFixedSize(105, 50) def _bindEvents(self): self.btnSubmit.clicked.connect(self.btnSubmitEvent) self.outTypeBox.currentIndexChanged.connect(self.changeOutType) def _setLayout(self): mainLayout = QVBoxLayout() mainLayout.addItem(QSpacerItem(50, 20)) mainLayout.addWidget(self.inputBox) mainLayout.addItem(QSpacerItem(50, 20)) hlayout = QHBoxLayout() hlayout.addWidget(QLabel('Output file type')) hlayout.addWidget(self.outTypeBox, Qt.AlignLeft) mainLayout.addLayout(hlayout) mainLayout.addItem(QSpacerItem(50, 10)) mainLayout.addLayout(self.stack) mainLayout.addItem(QSpacerItem(50, 10)) mainLayout.addLayout(self.stackbis) mainLayout.addItem(QSpacerItem(50, 10)) hlayout = QHBoxLayout() hlayout.addWidget(self.btnSubmit) hlayout.addWidget(self.outNameBox) mainLayout.addLayout(hlayout) mainLayout.addItem(QSpacerItem(30, 15)) mainLayout.addWidget(QLabel(' Message logs')) mainLayout.addWidget(self.logTextBox.widget) self.setLayout(mainLayout) def reset(self): self.inputBox.clear() self.outTypeBox.clear() self.outNameBox.clear() self.bkshpname.setText('Value') self.zfieldchoice.clear() self.zfieldchoicebis.clear() self.zfieldchoiceter.clear() self.mfieldchoice.clear() self.mfieldchoicebis.clear() self.shpbkmethod.clear() self.shpbkmethod.addItem('0') self.shpbkmethod.addItem('Iteration') self.shpbkmethodbis.clear() self.shpbkmethodbis.addItem('0') self.shpbkmethodbis.addItem('Iteration') self.choiceBox.clear() def changeOutType(self, index): if self.outTypeBox.currentText(): self.stack.setCurrentIndex(self.convert_type[self.input.from_type][ self.outTypeBox.currentText()]) def _check_options(self): current_type = self.stack.currentIndex() if current_type == self.BK_SHP: attribute_name = self.bkshpname.text() if not attribute_name: QMessageBox.critical(None, 'Error', 'The attribute name cannot be empty!', QMessageBox.Ok) return False, [] options = [attribute_name] elif current_type == self.Z_FROM_SHP: options = [self.zfieldchoice.currentText()] elif current_type == self.M_FROM_SHP: options = [self.mfieldchoice.currentText()] elif current_type == self.SHP_BK: options = [self.shpbkmethod.currentText()] elif current_type == self.Z_AND_BK: options = [ self.zfieldchoicebis.currentText(), self.shpbkmethodbis.currentText() ] elif current_type == self.Z_AND_M: options = [ self.zfieldchoiceter.currentText(), self.mfieldchoicebis.currentText() ] else: options = [] if self.stackbis.currentIndex() == 1: if self.resample.isChecked(): if self.valueButton.isChecked(): value = self.valueBox.text() try: value = float(value) except ValueError: QMessageBox.critical( None, 'Error', 'Re-sampling Maximum Length must be a number!', QMessageBox.Ok) return False, [] if value <= 0: QMessageBox.critical( None, 'Error', 'Re-sampling Maximum Length must be positive!', QMessageBox.Ok) return False, [] options.append('v|' + str(value)) else: attribute = self.choiceBox.currentText() if not attribute: QMessageBox.critical( None, 'Error', 'No numeric attribute available for re-sampling.', QMessageBox.Ok) return False, [] options.append('a|' + attribute) else: options.append('') return True, options def getInput(self): self.reset() from_type = self.input.from_type message = 'The input format is of type {}.\n'.format(from_type) is_line = False possible_types = self.convert_type[from_type].keys() if from_type == 'i2s' or from_type == 'i3s': is_line = True self.choiceBox.addItem('Attribute') nb_closed, nb_open = self.input.converter.nb_closed, self.input.converter.nb_open if nb_closed > 0 and nb_open > 0: possible_types = self.convert_type[from_type].keys() elif nb_closed == 0: if from_type == 'i2s': possible_types = ['i2s', 'shp Polyline', 'csv'] else: possible_types = ['i3s', 'i2s', 'shp PolylineZ', 'csv'] else: if from_type == 'i2s': possible_types = ['i2s', 'shp Polygon', 'csv'] else: possible_types = ['i3s', 'i2s', 'shp PolygonZ', 'csv'] message += 'It has {} polygon{} and {} open polyline{}.\n'.format( nb_closed, 's' if nb_closed > 1 else '', nb_open, 's' if nb_open > 1 else '') elif from_type == 'shp Point': numeric_fields = self.input.converter.numeric_fields if numeric_fields: self.mfieldchoicebis.addItem('0') for index, name in numeric_fields: item = '%d - %s' % (index, name) self.zfieldchoice.addItem(item) self.zfieldchoiceter.addItem(item) self.mfieldchoicebis.addItem(item) else: possible_types = [from_type, 'csv'] elif from_type == 'shp PointM': numeric_fields = self.input.converter.numeric_fields self.mfieldchoice.addItem('M') self.mfieldchoicebis.addItem('M') if numeric_fields: for index, name in numeric_fields: item = '%d - %s' % (index, name) self.zfieldchoice.addItem(item) self.zfieldchoiceter.addItem(item) self.mfieldchoice.addItem(item) self.mfieldchoicebis.addItem(item) else: possible_types = ['shp PointM', 'shp Point', 'csv'] elif from_type == 'shp PointZ': self.zfieldchoice.addItem('Z') self.zfieldchoiceter.addItem('Z') self.mfieldchoice.addItem('M') self.mfieldchoicebis.addItem('M') numeric_fields = self.input.converter.numeric_fields if numeric_fields: for index, name in numeric_fields: item = '%d - %s' % (index, name) self.zfieldchoice.addItem(item) self.zfieldchoiceter.addItem(item) self.mfieldchoice.addItem(item) self.mfieldchoicebis.addItem(item) elif from_type == 'shp Polyline' or from_type == 'shp Polygon': is_line = True numeric_fields = self.input.converter.numeric_fields if numeric_fields: self.mfieldchoicebis.addItem('0') for index, name in numeric_fields: item = '%d - %s' % (index, name) self.zfieldchoiceter.addItem(item) self.mfieldchoicebis.addItem(item) self.zfieldchoicebis.addItem(item) self.shpbkmethod.addItem(item) self.shpbkmethodbis.addItem(item) self.choiceBox.addItem(item) else: possible_types = [from_type, 'i2s', 'csv'] elif from_type == 'shp PolylineZ' or from_type == 'shp PolygonZ': is_line = True self.zfieldchoiceter.addItem('Z') self.zfieldchoicebis.addItem('Z') self.mfieldchoicebis.addItem('M') numeric_fields = self.input.converter.numeric_fields if numeric_fields: for index, name in numeric_fields: item = '%d - %s' % (index, name) self.zfieldchoiceter.addItem(item) self.zfieldchoicebis.addItem(item) self.mfieldchoicebis.addItem(item) self.shpbkmethod.addItem(item) self.shpbkmethodbis.addItem(item) self.choiceBox.addItem(item) elif from_type == 'shp PolylineM' or from_type == 'shp PolygonM': is_line = True numeric_fields = self.input.converter.numeric_fields self.mfieldchoice.addItem('M') self.mfieldchoicebis.addItem('M') if numeric_fields: for index, name in numeric_fields: item = '%d - %s' % (index, name) self.mfieldchoice.addItem(item) self.zfieldchoiceter.addItem(item) self.zfieldchoicebis.addItem(item) self.mfieldchoicebis.addItem(item) self.shpbkmethod.addItem(item) self.shpbkmethodbis.addItem(item) self.choiceBox.addItem(item) else: possible_types = [from_type, from_type[:-1], 'i2s', 'csv'] elif from_type == 'shp MultiPoint' or from_type == 'shp MultiPointZ': if from_type == 'shp MultiPointZ': self.zfieldchoice.addItem('Z') self.zfieldchoiceter.addItem('Z') self.mfieldchoice.addItem('M') self.mfieldchoicebis.addItem('M') numeric_fields = self.input.converter.numeric_fields if numeric_fields: for index, name in numeric_fields: item = '%d - %s' % (index, name) self.zfieldchoice.addItem(item) self.zfieldchoiceter.addItem(item) self.mfieldchoice.addItem(item) self.mfieldchoicebis.addItem(item) else: possible_types = [from_type, 'xyz', 'csv'] if from_type == 'shp MultiPointZ': possible_types.append('shp MultiPoint') logging.debug('Possible output types: %s' % possible_types) for to_type in possible_types: self.outTypeBox.addItem(to_type) if is_line: self.stackbis.setCurrentIndex(1) else: self.stackbis.setCurrentIndex(0) message += 'It can be converted to the following types: {}.'.format( ', '.join(list(possible_types))) self.inputBox.appendPlainText(message) def btnSubmitEvent(self): # check the transformations options valid, options = self._check_options() if not valid: return # getting the converter options right if self.input.transformation is not None: from_index, to_index = self.input.fromBox.currentIndex( ), self.input.toBox.currentIndex() trans = self.input.transformation.get_transformation( from_index, to_index) self.input.converter.set_transformations(trans) out_type = self.outTypeBox.currentText() if out_type[:3] == 'shp': out_type = 'shp' filename, _ = QFileDialog.getSaveFileName( self, 'Choose the output file name', '', '%s Files (*.%s)' % (out_type, out_type), options=QFileDialog.Options() | QFileDialog.DontUseNativeDialog) if not filename: return if len(filename) < 5 or filename[-3:] != out_type: filename += '.' + out_type if filename == self.input.converter.from_file: QMessageBox.critical(self, 'Error', 'Cannot overwrite to the input file.', QMessageBox.Ok) return try: with open(filename, 'w'): pass except PermissionError: QMessageBox.critical( self, 'Error', 'Permission denied (Is the file opened by another application?).', QMessageBox.Ok) return None self.outNameBox.setText(filename) logging.info('Start conversion from %s\nto %s' % (self.input.converter.from_file, filename)) QApplication.processEvents() try: self.input.converter.write(self.outTypeBox.currentText(), filename, options) except RuntimeError: QMessageBox.critical( self, 'Error', 'The attribute used for re-sampling contains non-positive number.', QMessageBox.Ok) logging.info('Failed.') return None logging.info('Done.') QMessageBox.information(self, 'Success', 'File conversion finished successfully!', QMessageBox.Ok)
class Controls(QWidget): def __init__(self, name, nb_player, h, w): QWidget.__init__(self) self.name = name self.nb_player = nb_player self.h = h self.w = w self.initUI() def initUI(self): # Set the window self.pal = self.palette() self.pal.setColor(self.backgroundRole(), Qt.white) self.setPalette(self.pal) self.gri = QHBoxLayout(self) # Set the Grid set_grid = grids.grid_init(self.name, self.nb_player) self.grid = set_grid[0] self.players = set_grid[1] # State of the game self.state = 0 # Set the button for movement self.button_move = [basic_widgets.Button("up"), basic_widgets.Button("down"), basic_widgets.Button("left"), basic_widgets.Button("right"), ] self.button_move[0].clicked.connect(self.go_up) self.button_move[1].clicked.connect(self.go_down) self.button_move[2].clicked.connect(self.go_left) self.button_move[3].clicked.connect(self.go_right) # Set the button which select the player self.current_player = 0 self.button_player = [basic_widgets.Button("1_button"), basic_widgets.Button("2_button"), basic_widgets.Button("3_button"), basic_widgets.Button("4_button"), ] for i in range(0, self.nb_player): self.button_player[i].clicked.connect(self.player_change) # We put it in a Stacked self.current_player = QStackedLayout() for i in range(0, self.nb_player): self.current_player.addWidget(self.button_player[i]) self.index = self.current_player.currentIndex() # Set the Layout for the button of the movement self.grid_button = QGridLayout() self.grid_button.addWidget(self.button_move[0], 1, 3) self.grid_button.addWidget(self.button_move[1], 3, 3) self.grid_button.addWidget(self.button_move[2], 2, 2) self.grid_button.addWidget(self.button_move[3], 2, 4) self.grid_button.addLayout(self.current_player, 2, 3) # Set the Layout which display the Grid self.display_grid = QStackedLayout() self.initial_grid = QWidget(self) self.initial_grid.setLayout(map.Map_Grid(self.grid, self.h, self.w)) self.display_grid.addWidget(self.initial_grid) # Set the Layout of the window self.gri.addLayout(self.display_grid) self.gri.addLayout(self.grid_button) self.setLayout(self.gri) def player_change(self): self.index = (self.index + 1) % self.nb_player self.current_player.setCurrentWidget(self.button_player[self.index]) def go_up(self): self.state = self.players[self.index].move(0, -1, self.grid) self.actualize_grid() def go_down(self): self.state = self.players[self.index].move(0, 1, self.grid) self.actualize_grid() def go_left(self): self.state = self.players[self.index].move(-1, 0, self.grid) self.actualize_grid() def go_right(self): self.state = self.players[self.index].move(1, 0, self.grid) self.actualize_grid() def actualize_grid(self): """ Create the new Layout where the Grid is printed because the grid has changed with the movement """ actual_grid = QWidget(self) actual_grid.setLayout(map.Map_Grid(self.grid, self.h, self.w)) self.display_grid.addWidget(actual_grid) self.display_grid.setCurrentWidget(actual_grid)
class OptionsWidget(QWidget): """ Widget holds menu with all options. """ def __init__(self, main_window, items): super().__init__() self.items = items self.layout = QStackedLayout() self._switch_user = self._get_switch_user_func(main_window) self.setLayout(self.layout) self._hide_action_button = lambda: main_window.communication.action_button_toggle.emit(False, '', None) self._create_layout(main_window) def set_current_index(self, index): self.layout.setCurrentIndex(index) if not index: self._hide_action_button() def showEvent(self, event): if not self.layout.currentIndex(): self._hide_action_button() def _get_switch_user_func(self, main_window): def _switch_user(): main_window.menu_btn_clicked(main_window.user_frame_index) self.layout.setCurrentIndex(0) return _switch_user def _get_template_import_func(self, main_window): func = self._wrap_template_func(template.Template.import_, main_window) def _alert_callback(path, value): if value: func(path[0]) def _f(): path = QFileDialog.getOpenFileName(main_window, 'Выберите файл', options.DATABASE_DIR) if path: main_window.create_alert('Шаблоны с одинаковыми именами будут перезаписаны.' '\nПродолжить?', functools.partial(_alert_callback, path)) return _f def _get_template_export_func(self, main_window): func = self._wrap_template_func(template.Template.export, main_window) def _f(): path = QFileDialog.getExistingDirectory(main_window, 'Выберите путь', options.DATABASE_DIR) if path: return func(path) return _f @staticmethod def _wrap_template_func(func, main_window): def _f(path): ok, result = func(path) if ok: main_window.show_message('Готово') else: main_window.create_alert('Произошла ошибка\n{}'.format(result.get('error'))) return ok, result return _f def _create_layout(self, main_window): wrapper = QHBoxLayout() self.layout.addWidget(utils.get_scrollable(wrapper)) rows = 8 cols = 3 vboxes = [QVBoxLayout() for _ in range(cols)] widgets = ((TemplateWidgetInOptions(main_window, self.items, self), 'Шаблоны'), (UsersAndGroupsWidget(main_window, self), 'Пользователи и группы'), (self._switch_user, 'Сменить пользователя'), (self._get_template_export_func(main_window), 'Экспортировать шаблоны'), (self._get_template_import_func(main_window), 'Импортировать шаблоны')) for i, widget in enumerate(widgets): b = QPushButton(widget[1]) if callable(widget[0]): b.clicked.connect(widget[0]) else: b.clicked.connect(functools.partial(self.layout.setCurrentIndex, i + 1)) self.layout.addWidget(widget[0]) b.setGraphicsEffect(utils.get_shadow()) vboxes[(i // rows) % cols].addWidget(b) for each in vboxes: each.addStretch() wrapper.addLayout(each, stretch=int(100 / cols))