class MainWindow(QWidget): """ The main window of mesme. It controls the transitions between screens. """ def __init__(self, *args, **kwargs): """ Create the screens. """ super().__init__(*args, **kwargs) # Create the main window layout. self.setMinimumSize(800, 600) self.setWindowTitle("mesme") self.layout = QStackedLayout() self.setLayout(self.layout) # Create the login screen. login_screen = LoginScreen() login_screen.login_successful.connect(self.on_login) self.layout.addWidget(login_screen) self.layout.setCurrentWidget(login_screen) @pyqtSlot(str, UserProfile, name="on_login") @log_exceptions def on_login(self, user_display_name, user_profile): """ Switch to the track screen. """ track_screen = TrackScreen(user_display_name, user_profile) self.layout.addWidget(track_screen) self.layout.setCurrentWidget(track_screen)
class MenuWidget(QWidget): button_clicked = pyqtSignal() def __init__(self, parent=None): super().__init__(parent=parent) self.menu: Dict[str, Menu] = {} self.current_menu: Optional[Menu] = None self.button_style: ButtonStyle = ButtonStyle() self.layout = QStackedLayout() self.setLayout(self.layout) self.active: bool = True def add_menu(self, name: str, caption: str = '') -> Menu: if not caption: caption = name menu = Menu(caption) menu.button_style = self.button_style menu.button_clicked.connect(self.button_clicked) self.menu[name] = menu self.layout.addWidget(menu) if self.current_menu is None: self.show_menu(name) return menu def show_menu(self, name: str): self.current_menu = self.menu[name] self.layout.setCurrentWidget(self.current_menu) @pyqtSlot() def on_ok_click(self): if not self.active: return if not self.isVisible(): return if self.current_menu is None: return self.current_menu.on_ok_click() @pyqtSlot() def on_back_click(self): if not self.active: return if not self.isVisible(): return if self.current_menu is None: return self.current_menu.on_back_click() @pyqtSlot() def on_up_click(self): if not self.active: return if not self.isVisible(): return if self.current_menu is None: return self.current_menu.on_up_click() @pyqtSlot() def on_down_click(self): if not self.active: return if not self.isVisible(): return if self.current_menu is None: return self.current_menu.on_down_click()
class ReliatyRadio(QMainWindow): layout = None back = pyqtSignal() def __init__(self): super().__init__() self.init_layout() # Generic UI Settings self.resize(1024, 768) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizePolicy) self.setContextMenuPolicy(QtCore.Qt.NoContextMenu) self.show_main_menu() def init_layout(self): self.layout = QStackedLayout() self.setCentralWidget(QWidget()) self.centralWidget().setSizePolicy(self.sizePolicy()) self.centralWidget().resize(1024, 768) self.centralWidget().setLayout(self.layout) def add_and_show_widget(self, widget): self.layout.addWidget(widget) self.layout.setCurrentWidget(widget) def go_back(self): count = self.layout.count() self.layout.setCurrentIndex(count - 2) self.layout.takeAt(count - 1) def show_main_menu(self): self.clearLayout() self.add_and_show_widget(MainMenu()) def clearLayout(self): while self.layout.count(): child = self.layout.takeAt(0) if child.widget(): child.widget().deleteLater()
def __init__(self, pane_selectors_layout: QLayout, panes_layout: QStackedLayout, pane: QWidget, *args, **kwargs): super().__init__(*args, **kwargs) self.setProperty('pane-selector', 'true') self.setFlat(panes_layout.currentWidget() != pane) self.setCursor(Qt.PointingHandCursor) self.released.connect(lambda: [ pane_selectors_layout.itemAt(i).widget().setFlat(True) for i in range(0, pane_selectors_layout.count()) ]) self.released.connect(lambda: self.setFlat(False)) self.released.connect(lambda: panes_layout.setCurrentWidget(pane))
class mainGUI(QWidget): def __init__(self): super().__init__() self.Horizon=QHBoxLayout() self.initialGUI() self.resize(700,400) self.setWindowIcon(QIcon('icon.jpg')) self.setWindowTitle('计网实验') def initialGUI(self): self.stkwindow=QWidget() self.choosewindow=QWidget() self.choose=QVBoxLayout() self.stk=QStackedLayout() self.HammingAct=QPushButton('哈明编码') self.HammingAct.setObjectName('Hamming') self.CRCAct=QPushButton('CRC解码') self.CRCAct.setObjectName('CRC') self.HammingContent=HammingGUI() self.CRCContent=CRCGUI() self.choose.addWidget(self.HammingAct) self.choose.addWidget(self.CRCAct) self.choosewindow.setLayout(self.choose) self.stk.addWidget(self.HammingContent) self.stk.addWidget(self.CRCContent) self.stkwindow.setLayout(self.stk) self.Horizon.addWidget(self.choosewindow) self.Horizon.addWidget(self.stkwindow) self.HammingAct.clicked.connect(self.chooseAlg) self.CRCAct.clicked.connect(self.chooseAlg) self.setLayout(self.Horizon) self.stk.setCurrentWidget(self.HammingContent) def chooseAlg(self): dic={'Hamming':0,'CRC':1} select=dic[self.sender().objectName()] if(select==0): self.stk.setCurrentWidget(self.HammingContent) else: self.stk.setCurrentWidget(self.CRCContent)
class GameWidget(QWidget): def __init__(self, parent=None): super(GameWidget, self).__init__(parent) self.game = Game() self.renderer = QSvgRenderer("theme.svg") self.boardWidget = BoardWidget(self.game, self.renderer, self) self.stackLayoutWidget = QWidget(self) self.stackLayoutWidget.setFixedWidth(150) self.initWidget = GameInitWidget(self.game, self.stackLayoutWidget) self.playWidget = GamePlayWidget(self.game, self.stackLayoutWidget) self.finishedWidget = GameFinishedWidget(self.game, self.stackLayoutWidget) self.stackLayout = QStackedLayout() self.stackLayout.addWidget(self.initWidget) self.stackLayout.addWidget(self.playWidget) self.stackLayout.addWidget(self.finishedWidget) self.stackLayoutWidget.setLayout(self.stackLayout) self.hboxLayout = QHBoxLayout() self.hboxLayout.addWidget(self.boardWidget) self.hboxLayout.addWidget(self.stackLayoutWidget) self.hboxLayout.setStretch(0, 1) self.hboxLayout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.hboxLayout) self.game.phaseChanged.connect(self.update) self.update() def update(self): print("gwUpdate phase={}".format(self.game.phase)) if self.game.phase == Phase.INIT: self.stackLayout.setCurrentWidget(self.initWidget) if self.game.phase == Phase.PLAY_HUMAN or self.game.phase == Phase.PLAY_ENGINE: self.stackLayout.setCurrentWidget(self.playWidget) if self.game.phase == Phase.FINISHED: self.stackLayout.setCurrentWidget(self.finishedWidget)
class _MainContainer(QWidget): currentEditorChanged = pyqtSignal("QString") fileOpened = pyqtSignal("QString") fileSaved = pyqtSignal("QString") def __init__(self, parent=None): super().__init__(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) # QML UI self._add_file_folder = add_file_folder.AddFileFolderWidget(self) if settings.SHOW_START_PAGE: self.show_start_page() IDE.register_service("main_container", self) # Register signals connections connections = ({ "target": "main_container", "signal_name": "updateLocator", "slot": self._explore_code }, { "target": "filesystem", "signal_name": "projectOpened", "slot": self._explore_code }, { "target": "projects_explore", "signal_name": "updateLocator", "slot": self._explore_code }) IDE.register_signals("main_container", connections) esc_sort = QShortcut(QKeySequence(Qt.Key_Escape), self) esc_sort.activated.connect(self._set_focus_to_editor) def install(self): ninjaide = IDE.get_service("ide") ninjaide.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 # Code Locator self._code_locator = locator_widget.LocatorWidget(ninjaide) ui_tools.install_shortcuts(self, actions.ACTIONS, ninjaide) def _set_focus_to_editor(self): status_bar = IDE.get_service("status_bar") tools_doock = IDE.get_service("tools_dock") editor_widget = self.get_current_editor() if status_bar.isVisible() and tools_doock.isVisible(): status_bar.hide_status_bar() elif tools_doock.isVisible(): tools_doock._hide() elif status_bar.isVisible(): status_bar.hide_status_bar() if editor_widget is not None: editor_widget.clear_extra_selections('searchs') def split_assistance(self): split_widget = split_orientation.SplitOrientation(self) split_widget.show() def show_split(self, orientation_vertical=False): self.current_widget.split_editor(orientation_vertical) 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 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 get_current_editor(self): current_widget = self.current_widget.currentWidget() if isinstance(current_widget, editor.NEditor): return current_widget return None def open_file(self, filename='', line=-1, col=0): logger.debug("Will try to open %s" % filename) if not filename: logger.debug("Has no filename") if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") editor_widget = self.get_current_editor() ninjaide = IDE.get_service("ide") current_project = ninjaide.get_current_project() # TODO: handle current project in NProject if current_project is not None: directory = current_project.full_path elif editor_widget is not None and editor_widget.file_path: directory = file_manager.get_folder( editor_widget.file_path) extensions = ";;".join([ "{}(*{})".format(e.upper()[1:], e) for e in settings.SUPPORTED_EXTENSIONS + [".*", ""] ]) filenames = QFileDialog.getOpenFileNames( self, "Open File", # FIXME: translations directory, extensions)[0] else: logger.debug("Has filename") filenames = [filename] if not filenames: return for filename in filenames: logger.debug("Will try to open: %s" % filename) self.__open_file(filename, line, col) def __open_file(self, filename, line, col): try: editor_widget = self.add_editor(filename) if line != -1: editor_widget.go_to_line(line, col) self.currentEditorChanged.emit(filename) except file_manager.NinjaIOException as reason: QMessageBox.information( self, "The file couldn't be open", # FIXME: translations str(reason)) logger.error("The file %s couldn't be open" % filename) def save_file(self, editor_widget=None): if editor_widget is None: # This may return None if there is not editor present editor_widget = self.get_current_editor() if editor_widget is None: return False # Ok, we have an editor instance # Save to file only if editor really was modified if editor_widget.is_modified: try: if editor_widget.nfile.is_new_file or \ not editor_widget.nfile.has_write_permission(): return self.save_file_as(editor_widget) # FIXME: beforeFileSaved.emit if settings.REMOVE_TRAILING_SPACES: pass # FIXME: new line at end # Save content editor_widget.neditable.save_content() # FIXME: encoding # FIXME: translations self.fileSaved.emit("File Saved: %s" % editor_widget.file_path) return True except Exception as reason: logger.error("Save file error: %s" % reason) QMessageBox.information(self, "Save Error", "The file could't be saved!") return False def save_file_as(self, editor_widget=None): force = False if editor_widget is None: # We invoque from menu editor_widget = self.get_current_editor() if editor_widget is None: # We haven't editor in main container return False force = True try: filters = "(*.py);;(*.*)" if editor_widget.file_path is not None: # Existing file extension = file_manager.get_file_extension( editor_widget.file_path) if extension != 'py': filters = "(*.%s);;(*.py);;(*.*)" % extension save_folder = self._get_save_folder(editor_widget.file_path) filename = QFileDialog.getSaveFileName(self, "Save File", save_folder, filters)[0] if not filename: return False # FIXME: remove trailing spaces extension = file_manager.get_file_extension(filename) if not extension: filename = "%s.%s" % (filename, "py") editor_widget.neditable.save_content(path=filename, force=force) self.fileSaved.emit("File Saved: {}".format(filename)) self.currentEditorChanged.emit(filename) return True except file_manager.NinjaFileExistsException as reason: QMessageBox.information( self, "File Already Exists", "Invalid Path: the file '%s' already exists." % reason.filename) except Exception as reason: logger.error("save_file_as: %s", reason) QMessageBox.information(self, "Save Error", "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 is not None: return current_project.path return os.path.expanduser("~") def close_file(self): self.current_widget.close_current_file() def add_editor(self, filename=None): ninjaide = IDE.get_service("ide") editable = ninjaide.get_or_create_editable(filename) if editable.editor: # If already open logger.debug("%s is already open" % filename) self.current_widget.set_current(editable) return self.current_widget.currentWidget() else: pass editor_widget = self.create_editor_from_editable(editable) # editor_widget.set_language() # 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) editor_widget.setFocus() return editor_widget def create_editor_from_editable(self, editable): neditor = editor.create_editor(editable) neditor.zoomChanged.connect(self._show_zoom_indicator) neditor.destroyed.connect(self.__on_editor_destroyed) # editable.fileSaved.connect(self._editor_tab) return neditor def _show_zoom_indicator(self, zoom): neditor = self.get_current_editor() indicator.Indicator.show_text(neditor, "Zoom: {} %".format(str(zoom))) @pyqtSlot() def __on_editor_destroyed(self): indicator.Indicator.instance = None def add_widget(self, widget): self.stack.addWidget(widget) def show_start_page(self): """Show Start Page widget in main container""" startp = self.stack.widget(0) if isinstance(startp, start_page.StartPage): self.stack.setCurrentIndex(0) else: startp = start_page.StartPage(parent=self) startp.newFile.connect(self.add_editor) self.stack.insertWidget(0, startp) self.stack.setCurrentIndex(0) def _files_closed(self): if settings.SHOW_START_PAGE: self.show_start_page() def add_status_bar(self, status_bar): self._vbox.addWidget(status_bar) 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 zoom_in_editor(self): """Increase the font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.zoom(1.) def zoom_out_editor(self): """Decrease the font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.zoom(-1.) def reset_zoom_editor(self): """Reset the to original font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.reset_zoom() def editor_move_up(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.move_up_down(up=True) def editor_move_down(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.move_up_down() def editor_duplicate_line(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.duplicate_line() def editor_comment(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.comment()
class ProjectTreeColumn(QDialog): # Signalsnproject = dockWidget = pyqtSignal('PyQt_PyObject') undockWidget = pyqtSignal() changeTitle = pyqtSignal('PyQt_PyObject', 'QString') updateLocator = pyqtSignal() activeProjectChanged = 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 = [] frame = QFrame() frame.setObjectName("actionbar") box = QVBoxLayout(frame) box.setContentsMargins(1, 1, 1, 1) box.setSpacing(0) self._combo_project = QComboBox() self._combo_project.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self._combo_project.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLengthWithIcon) self._combo_project.setObjectName("combo_projects") box.addWidget(self._combo_project) vbox.addWidget(frame) self._combo_project.setContextMenuPolicy(Qt.CustomContextMenu) self._projects_area = QStackedLayout() logger.debug("This is the projects area") vbox.addLayout(self._projects_area) # Empty widget self._empty_proj = QLabel(translations.TR_NO_PROJECTS) self._empty_proj.setAlignment(Qt.AlignCenter) self._empty_proj.setAutoFillBackground(True) self._empty_proj.setBackgroundRole(QPalette.Base) self._projects_area.addWidget(self._empty_proj) self._projects_area.setCurrentWidget(self._empty_proj) self.projects = [] self._combo_project.currentIndexChanged.connect( self._change_current_project) self._combo_project.customContextMenuRequested[ 'const QPoint&'].connect(self.context_menu_for_root) connections = ( { "target": "main_container", "signal_name": "addToProject", "slot": self._add_file_to_project }, { "target": "main_container", "signal_name": "showFileInExplorer", "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._on_ide_going_down) def _on_ide_going_down(self): """Save some settings before close""" if self.current_tree is None: return ds = IDE.data_settings() show_filesize = not bool(self.current_tree.isColumnHidden(1)) ds.setValue("projectsExplorer/showFileSize", show_filesize) 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: path_project = [self.current_project] _add_to_project = add_to_project.AddToProject(path_project, self) _add_to_project.exec_() if not _add_to_project.path_selected: 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(_add_to_project.path_selected, 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) tooltip = utils.path_with_tilde_homepath(project.path) self._combo_project.setToolTip(tooltip) index = self._combo_project.count() - 1 self._combo_project.setItemData(index, project) 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) self._projects_area.setCurrentWidget(ptree) # Can be empty widget self._combo_project.setCurrentIndex(index) # FIXME: improve? # if len(self.projects) == 1: # self._combo_project.currentIndexChanged[int].connect( # self._change_current_project) def _close_project(self, widget): """Close the project related to the tree widget.""" index = self._combo_project.currentIndex() self.projects.remove(widget) # index + 1 is necessary because the widget # with index 0 is the empty widget self._projects_area.takeAt(index + 1) self._combo_project.removeItem(index) index = self._combo_project.currentIndex() self._projects_area.setCurrentIndex(index + 1) 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): nproject = self._combo_project.itemData(index) ninjaide = IDE.get_service("ide") projects = ninjaide.get_projects() for project in projects.values(): if project == nproject: nproject.is_current = True else: project.is_current = False self._projects_area.setCurrentIndex(index + 1) self.activeProjectChanged.emit() 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 and self.current_tree is not None: return self.current_tree.project @property def current_tree(self): tree = None widget = self._projects_area.currentWidget() if isinstance(widget, TreeProjectsWidget): tree = widget return tree 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 not name: name = file_manager.get_basename(folder) if not 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) > MAX_RECENT_PROJECTS: 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) if self.current_tree is None: # No projects return path = self.current_tree.project.path # Reset index self.current_tree.setCurrentIndex(QModelIndex()) 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) menu.addSeparator() action_run_project = menu.addAction(translations.TR_RUN_PROJECT) action_properties = menu.addAction(translations.TR_PROJECT_PROPERTIES) action_show_file_size = menu.addAction(translations.TR_SHOW_FILESIZE) menu.addSeparator() action_close = menu.addAction(translations.TR_CLOSE_PROJECT) # Connections 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_create_init.triggered.connect(self.current_tree._create_init) action_run_project.triggered.connect( self.current_tree._execute_project) action_properties.triggered.connect( self.current_tree.open_project_properties) action_close.triggered.connect(self.current_tree._close_project) action_show_file_size.triggered.connect( self.current_tree.show_filesize_info) # 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 StatusBar(QWidget): """The statusbar at the bottom of the mainwindow. Attributes: txt: The Text widget in the statusbar. keystring: The KeyString widget in the statusbar. percentage: The Percentage widget in the statusbar. url: The UrlText widget in the statusbar. prog: The Progress widget in the statusbar. cmd: The Command widget in the statusbar. _hbox: The main QHBoxLayout. _stack: The QStackedLayout with cmd/txt widgets. _win_id: The window ID the statusbar is associated with. Class attributes: _prompt_active: If we're currently in prompt-mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _insert_active: If we're currently in insert mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _command_active: If we're currently in command mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _caret_mode: The current caret mode (off/on/selection). For some reason we need to have this as class attribute so pyqtProperty works correctly. Signals: resized: Emitted when the statusbar has resized, so the completion widget can adjust its size to it. arg: The new size. moved: Emitted when the statusbar has moved, so the completion widget can move to the right position. arg: The new position. """ resized = pyqtSignal('QRect') moved = pyqtSignal('QPoint') _severity = None _prompt_active = False _insert_active = False _command_active = False _caret_mode = CaretMode.off STYLESHEET = """ QWidget#StatusBar, QWidget#StatusBar QLabel, QWidget#StatusBar QLineEdit { font: {{ font['statusbar'] }}; background-color: {{ color['statusbar.bg'] }}; color: {{ color['statusbar.fg'] }}; } QWidget#StatusBar[caret_mode="on"], QWidget#StatusBar[caret_mode="on"] QLabel, QWidget#StatusBar[caret_mode="on"] QLineEdit { color: {{ color['statusbar.fg.caret'] }}; background-color: {{ color['statusbar.bg.caret'] }}; } QWidget#StatusBar[caret_mode="selection"], QWidget#StatusBar[caret_mode="selection"] QLabel, QWidget#StatusBar[caret_mode="selection"] QLineEdit { color: {{ color['statusbar.fg.caret-selection'] }}; background-color: {{ color['statusbar.bg.caret-selection'] }}; } QWidget#StatusBar[prompt_active="true"], QWidget#StatusBar[prompt_active="true"] QLabel, QWidget#StatusBar[prompt_active="true"] QLineEdit { color: {{ color['statusbar.fg.prompt'] }}; background-color: {{ color['statusbar.bg.prompt'] }}; } QWidget#StatusBar[insert_active="true"], QWidget#StatusBar[insert_active="true"] QLabel, QWidget#StatusBar[insert_active="true"] QLineEdit { color: {{ color['statusbar.fg.insert'] }}; background-color: {{ color['statusbar.bg.insert'] }}; } QWidget#StatusBar[command_active="true"], QWidget#StatusBar[command_active="true"] QLabel, QWidget#StatusBar[command_active="true"] QLineEdit { color: {{ color['statusbar.fg.command'] }}; background-color: {{ color['statusbar.bg.command'] }}; } """ def __init__(self, win_id, parent=None): super().__init__(parent) objreg.register('statusbar', self, scope='window', window=win_id) self.setObjectName(self.__class__.__name__) self.setAttribute(Qt.WA_StyledBackground) style.set_register_stylesheet(self) self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) self._win_id = win_id self._option = None self._hbox = QHBoxLayout(self) self.set_hbox_padding() objreg.get('config').changed.connect(self.set_hbox_padding) self._hbox.setSpacing(5) self._stack = QStackedLayout() self._hbox.addLayout(self._stack) self._stack.setContentsMargins(0, 0, 0, 0) self.cmd = command.Command(win_id) self._stack.addWidget(self.cmd) objreg.register('status-command', self.cmd, scope='window', window=win_id) self.txt = textwidget.Text() self._stack.addWidget(self.txt) self.prompt = prompt.Prompt(win_id) self._stack.addWidget(self.prompt) self.cmd.show_cmd.connect(self._show_cmd_widget) self.cmd.hide_cmd.connect(self._hide_cmd_widget) self._hide_cmd_widget() prompter = objreg.get('prompter', scope='window', window=self._win_id) prompter.show_prompt.connect(self._show_prompt_widget) prompter.hide_prompt.connect(self._hide_prompt_widget) self._hide_prompt_widget() self.keystring = keystring.KeyString() self._hbox.addWidget(self.keystring) self.url = url.UrlText() self._hbox.addWidget(self.url) self.percentage = percentage.Percentage() self._hbox.addWidget(self.percentage) self.tabindex = tabindex.TabIndex() self._hbox.addWidget(self.tabindex) # We add a parent to Progress here because it calls self.show() based # on some signals, and if that happens before it's added to the layout, # it will quickly blink up as independent window. self.prog = progress.Progress(self) self._hbox.addWidget(self.prog) objreg.get('config').changed.connect(self.maybe_hide) QTimer.singleShot(0, self.maybe_hide) def __repr__(self): return utils.get_repr(self) @config.change_filter('ui', 'hide-statusbar') def maybe_hide(self): """Hide the statusbar if it's configured to do so.""" hide = config.get('ui', 'hide-statusbar') if hide: self.hide() else: self.show() @config.change_filter('ui', 'statusbar-padding') def set_hbox_padding(self): padding = config.get('ui', 'statusbar-padding') self._hbox.setContentsMargins(padding.left, 0, padding.right, 0) @pyqtProperty(bool) def prompt_active(self): """Getter for self.prompt_active, so it can be used as Qt property.""" return self._prompt_active def _set_prompt_active(self, val): """Setter for self.prompt_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ log.statusbar.debug("Setting prompt_active to {}".format(val)) self._prompt_active = val self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) @pyqtProperty(bool) def command_active(self): """Getter for self.command_active, so it can be used as Qt property.""" return self._command_active @pyqtProperty(bool) def insert_active(self): """Getter for self.insert_active, so it can be used as Qt property.""" return self._insert_active @pyqtProperty(str) def caret_mode(self): """Getter for self._caret_mode, so it can be used as Qt property.""" return self._caret_mode.name def set_mode_active(self, mode, val): """Setter for self.{insert,command,caret}_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ if mode == usertypes.KeyMode.insert: log.statusbar.debug("Setting insert_active to {}".format(val)) self._insert_active = val if mode == usertypes.KeyMode.command: log.statusbar.debug("Setting command_active to {}".format(val)) self._command_active = val elif mode == usertypes.KeyMode.caret: tab = objreg.get('tabbed-browser', scope='window', window=self._win_id).currentWidget() log.statusbar.debug("Setting caret_mode - val {}, selection " "{}".format(val, tab.caret.selection_enabled)) if val: if tab.caret.selection_enabled: self._set_mode_text("{} selection".format(mode.name)) self._caret_mode = CaretMode.selection else: self._set_mode_text(mode.name) self._caret_mode = CaretMode.on else: self._caret_mode = CaretMode.off self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) def _set_mode_text(self, mode): """Set the mode text.""" text = "-- {} MODE --".format(mode.upper()) self.txt.set_text(self.txt.Text.normal, text) def _show_cmd_widget(self): """Show command widget instead of temporary text.""" self._stack.setCurrentWidget(self.cmd) self.show() def _hide_cmd_widget(self): """Show temporary text instead of command widget.""" log.statusbar.debug("Hiding cmd widget") self._stack.setCurrentWidget(self.txt) self.maybe_hide() def _show_prompt_widget(self): """Show prompt widget instead of temporary text.""" if self._stack.currentWidget() is self.prompt: return self._set_prompt_active(True) self._stack.setCurrentWidget(self.prompt) self.show() def _hide_prompt_widget(self): """Show temporary text instead of prompt widget.""" self._set_prompt_active(False) log.statusbar.debug("Hiding prompt widget") self._stack.setCurrentWidget(self.txt) self.maybe_hide() @pyqtSlot(str) def set_text(self, val): """Set a normal (persistent) text in the status bar.""" self.txt.set_text(self.txt.Text.normal, val) @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): """Mark certain modes in the commandline.""" keyparsers = objreg.get('keyparsers', scope='window', window=self._win_id) if keyparsers[mode].passthrough: self._set_mode_text(mode.name) if mode in [usertypes.KeyMode.insert, usertypes.KeyMode.command, usertypes.KeyMode.caret]: self.set_mode_active(mode, True) @pyqtSlot(usertypes.KeyMode, usertypes.KeyMode) def on_mode_left(self, old_mode, new_mode): """Clear marked mode.""" keyparsers = objreg.get('keyparsers', scope='window', window=self._win_id) if keyparsers[old_mode].passthrough: if keyparsers[new_mode].passthrough: self._set_mode_text(new_mode.name) else: self.txt.set_text(self.txt.Text.normal, '') if old_mode in [usertypes.KeyMode.insert, usertypes.KeyMode.command, usertypes.KeyMode.caret]: self.set_mode_active(old_mode, False) def resizeEvent(self, e): """Extend resizeEvent of QWidget to emit a resized signal afterwards. Args: e: The QResizeEvent. """ super().resizeEvent(e) self.resized.emit(self.geometry()) def moveEvent(self, e): """Extend moveEvent of QWidget to emit a moved signal afterwards. Args: e: The QMoveEvent. """ super().moveEvent(e) self.moved.emit(e.pos()) def minimumSizeHint(self): """Set the minimum height to the text height plus some padding.""" padding = config.get('ui', 'statusbar-padding') width = super().minimumSizeHint().width() height = self.fontMetrics().height() + padding.top + padding.bottom return QSize(width, height)
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 MainWidget(QMainWindow): __instance = None @staticmethod def getinstance(): if (MainWidget.__instance == None): MainWidget() return MainWidget.__instance def __init__(self, user:User): super().__init__() if (MainWidget.__instance != None): raise Exception ("Singleton") else: self.initUI(user) MainWidget.__instance = self def initUI(self, user:User): self.mainWidget = QWidget() self.setMinimumSize(500,500) self.user = user self.controller = MainWidgetController(self.user) self.setStyleSheet(open('main.css').read()) self.dashboard_button = QPushButton("Dashboard") self.dashboard_button.setFixedWidth(150) self.dashboard_button.clicked.connect(lambda success:self.LoadDashboardLayout(success)) self.dashboard_button.setObjectName("dashboard_button") self.home_button = QPushButton("Home") self.home_button.setFixedWidth(150) self.home_button.clicked.connect(lambda:self.LoadHomeLayout()) self.home_button.setObjectName("home_button") self.profile_button = QPushButton("Profile") self.profile_button.setFixedWidth(150) self.profile_button.clicked.connect(lambda success:self.LoadProfileLayout(success)) self.profile_button.setObjectName("profile_button") self.grid = QGridLayout() self.upperLayout = QHBoxLayout() self.upperLayout.addWidget(self.dashboard_button) self.upperLayout.addWidget(self.home_button) self.upperLayout.addWidget(self.profile_button) self.upperExternal = QFrame() self.upperExternal.setObjectName("upperExternal") self.upperExternal.setLayout(self.upperLayout) #print(self.user.courses) self.home_layout = None self.dashboard_layout = None self.profile_layout = None self.course_layout = None self.new_course_layout = None self.movie_layout = None self.addmovie_layout = None self.CourseMovies_layout = None self.changepass_layout = None self.stack = QStackedLayout() self.grid.addWidget(self.upperExternal,1,1) self.grid.addLayout(self.stack,2,1) self.mainWidget.setLayout(self.grid) self.setCentralWidget(self.mainWidget) self.LoadHomeLayout() # self.Recommender=RecommendationEngine() # self.Recommender.LoadRecommendations(self.user) exit = QAction("&Exit", self) exit.triggered.connect(self.closeEvent) self.show() def closeEvent(self, event): self.silence() close = QMessageBox() close.setText("You sure?") close.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel) close = close.exec() if close == QMessageBox.Yes: event.accept() else: event.ignore() #HomeLayoout---------------------------------------------------------------------------------- #Sets homelayout as default layout def LoadHomeLayout(self): self.setWindowTitle("Home") if self.home_layout == None: self.home_layout = HomeLayout(self.user) self.home_layout._WatchMovie_.connect(lambda url, M_ID:self.LoadMovieLayout(url, M_ID)) self.stack.addWidget(self.home_layout) self.stack.setCurrentWidget(self.home_layout) self.silence() #sets movielayout as default layout def LoadMovieLayout(self, url, movieID): self.setWindowTitle("Movie") if self.movie_layout!=None: self.stack.removeWidget(self.movie_layout) del self.movie_layout self.movie_layout = MovieLayout(url, movieID, self.user) self.stack.addWidget(self.movie_layout) self.stack.setCurrentWidget(self.movie_layout) #DashBoardLayout---------------------------------------------------------------------------------- #sets dashboard layout as default layout def LoadDashboardLayout(self, success): if self.dashboard_layout!=None: self.stack.removeWidget(self.dashboard_layout) del self.dashboard_layout self.setWindowTitle("Dashboard") self.dashboard_layout = DashboardLayout(self.user) self.dashboard_layout.new_course_request.connect(self.LoadCourseLayout) self.dashboard_layout.openCourse_request.connect(lambda x: self.LoadCourseMovies(x)) self.stack.addWidget(self.dashboard_layout) self.stack.setCurrentWidget(self.dashboard_layout) self.silence() if(success == 1): self.dashboard_layout.message.setText("Successfully Submitted Course") self.course_layout = None elif(success == 2): self.dashboard_layout.message.setText("Successfully Submitted Movie") self.addmovie_layout = None elif(success == 3): pass elif(success == 4): self.dashboard_layout.message.setText("Successfully Enrolled in Course") self.course_layout = None #sets course layout as default layout (upload courses) def LoadCourseLayout(self): self.setWindowTitle("Add Course") if self.course_layout == None and isinstance(self.user, Professor) == True: self.course_layout = CourseInputDialog(self.user) self.course_layout.loaddashlayout.connect(lambda success:self.LoadDashboardLayout(success)) self.stack.addWidget(self.course_layout) elif self.course_layout == None and isinstance(self.user, Student) == True: self.course_layout = CourseInputDialog(self.user) self.course_layout.loaddashlayout.connect(lambda success:self.LoadDashboardLayout(success)) self.stack.addWidget(self.course_layout) # self.course_layout.refreshDash.connect(self.) self.course_layout.refreshCourses_request.connect(self.RefreshProfile) self.stack.setCurrentWidget(self.course_layout) #sets dash movie layout as default layout (upload movie) def LoadDashMovieLayout(self, courseID): self.setWindowTitle("Add Movie") if self.addmovie_layout!=None: self.stack.removeWidget(self.addmovie_layout) del self.addmovie_layout self.addmovie_layout = MovieInputDialog(self.user, courseID) self.addmovie_layout.goback_request.connect(lambda C_ID:self.LoadCourseMovies(C_ID)) self.stack.addWidget(self.addmovie_layout) self.stack.setCurrentWidget(self.addmovie_layout) def LoadCourseMovies(self, courseID): self.setWindowTitle("Course Movies") if self.CourseMovies_layout!=None: self.stack.removeWidget(self.CourseMovies_layout) del self.CourseMovies_layout self.CourseMovies_layout = CourseMovieLayout(self.user, courseID) self.CourseMovies_layout.new_movie_request.connect(lambda C_ID: self.LoadDashMovieLayout(C_ID)) self.CourseMovies_layout.goback_request.connect(lambda success:self.LoadDashboardLayout(success)) self.CourseMovies_layout._WatchMovie_.connect(lambda url, M_ID:self.LoadMovieLayout(url, M_ID)) self.stack.addWidget(self.CourseMovies_layout) self.stack.setCurrentWidget(self.CourseMovies_layout) #ProfileLayout--------------------------------------------------------------------------------------------- #sets profile layout as default layout def LoadProfileLayout(self, success): self.setWindowTitle("Profile") if self.profile_layout != None: self.stack.removeWidget(self.profile_layout) del self.profile_layout self.profile_layout = ProfileLayout(self.user) self.profile_layout.changePass_request.connect(lambda : self.LoadChangePassLayout()) self.profile_layout.changeImage_request.connect(lambda image: self.ChangeImage(image)) if(success == 1): self.profile_layout.errorMessage.setText("Password Changed Successfully") self.stack.addWidget(self.profile_layout) self.stack.setCurrentWidget(self.profile_layout) self.silence() def LoadChangePassLayout(self): self.setWindowTitle("Change Password") if self.changepass_layout == None: self.changepass_layout = ChangePasswordLayout(self.user) self.changepass_layout.goback_request.connect(lambda success:self.LoadProfileLayout(success)) self.stack.addWidget(self.changepass_layout) self.stack.setCurrentWidget(self.changepass_layout) #----------------------------------------------------------------------------------------------------------- #stops movie from running in background (movie cleanup) def silence(self): if(self.movie_layout != None): self.movie_layout.Stop() def RefreshProfile(self): self.user.courses = self.controller.refresh() def ChangeImage(self, image): self.user.image = image
class MAIN_WINDOW(QWidget): def __init__(self): super().__init__() self.move(350, 150) self.setMinimumSize(640, 480) self.win_title = "FACE RECOGNITION" self.setWindowTitle(self.win_title) self.setStyleSheet(open("./assets/css/main.css").read()) # Main Layout of Widget self.super_layout = QStackedLayout() self.setLayout(self.super_layout) self._view() self._other_view() def _view(self): initial_layout = QGridLayout() register = QPushButton("Register") register.setIcon(QIcon("./assets/icons/add_users.png")) register.setIconSize(QSize(50, 50)) stud_det = QPushButton("Student") stud_det.setIcon(QIcon("./assets/icons/student.png")) stud_det.setIconSize(QSize(50, 50)) staf_det = QPushButton("Staff") staf_det.setIcon(QIcon("./assets/icons/staff.png")) staf_det.setIconSize(QSize(50, 50)) register.setObjectName("main_btn") stud_det.setObjectName("main_btn") staf_det.setObjectName("main_btn") initial_layout.addWidget(register, 0, 0, 1, 0) initial_layout.addWidget(stud_det, 1, 0) initial_layout.addWidget(staf_det, 1, 1) self.main_widget = QWidget() self.main_widget.setLayout(initial_layout) self.super_layout.addWidget(self.main_widget) register.clicked.connect( lambda: self._handle_open_register_view(self.super_layout)) stud_det.clicked.connect(self._handle_open_student_details_view) staf_det.clicked.connect(self._handle_open_staff_details_view) def _other_view(self): # For other screens aside Login Screen And Main View Screen self.stacked = QStackedWidget() self.main_grid_widget = QWidget() self.main_grid = QGridLayout() back_btn = QCommandLinkButton() back_btn.setIcon(QIcon("./assets/icons/back.png")) back_btn.setIconSize(QSize(30, 30)) self.main_grid_widget.setLayout(self.main_grid) self.main_grid.addWidget(back_btn, 0, 0) self.main_grid.addWidget(self.stacked, 1, 0, 1, 0) back_btn.clicked.connect(self._handle_go_back) back_btn.setMaximumWidth(45) def _handle_open_register_view(self, main_layout): self.setWindowTitle("REGISTER") self.reg_view = REGISTER_MAIN(main_layout) self.stacked.addWidget(self.reg_view.main_widget) self.stacked.setCurrentWidget(self.reg_view.main_widget) self.super_layout.addWidget(self.main_grid_widget) self.super_layout.setCurrentWidget(self.main_grid_widget) def _handle_open_student_details_view(self): self.ver_view = Student_Verify( self, self.super_layout, self.main_grid_widget, self.stacked, self._handle_go_back, ) def _handle_open_staff_details_view(self): self.ver_view = Staff_Verify( self, self.super_layout, self.main_grid_widget, self.stacked, self._handle_go_back, ) def _handle_go_back(self): self.super_layout.setCurrentIndex(0) self.setWindowTitle(self.win_title)
class StatusBar(QWidget): """The statusbar at the bottom of the mainwindow. Attributes: txt: The Text widget in the statusbar. keystring: The KeyString widget in the statusbar. percentage: The Percentage widget in the statusbar. url: The UrlText widget in the statusbar. prog: The Progress widget in the statusbar. cmd: The Command widget in the statusbar. _hbox: The main QHBoxLayout. _stack: The QStackedLayout with cmd/txt widgets. _text_queue: A deque of (error, text) tuples to be displayed. error: True if message is an error, False otherwise _text_pop_timer: A Timer displaying the error messages. _stopwatch: A QTime for the last displayed message. _timer_was_active: Whether the _text_pop_timer was active before hiding the command widget. _previous_widget: A PreviousWidget member - the widget which was displayed when an error interrupted it. _win_id: The window ID the statusbar is associated with. Class attributes: _error: If there currently is an error, accessed through the error property. For some reason we need to have this as class attribute so pyqtProperty works correctly. _prompt_active: If we're currently in prompt-mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _insert_active: If we're currently in insert mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. Signals: resized: Emitted when the statusbar has resized, so the completion widget can adjust its size to it. arg: The new size. moved: Emitted when the statusbar has moved, so the completion widget can move the the right position. arg: The new position. """ resized = pyqtSignal('QRect') moved = pyqtSignal('QPoint') _error = False _prompt_active = False _insert_active = False STYLESHEET = """ QWidget#StatusBar { {{ color['statusbar.bg'] }} } QWidget#StatusBar[insert_active="true"] { {{ color['statusbar.bg.insert'] }} } QWidget#StatusBar[prompt_active="true"] { {{ color['statusbar.bg.prompt'] }} } QWidget#StatusBar[error="true"] { {{ color['statusbar.bg.error'] }} } QLabel, QLineEdit { {{ color['statusbar.fg'] }} {{ font['statusbar'] }} } """ def __init__(self, win_id, parent=None): super().__init__(parent) objreg.register('statusbar', self, scope='window', window=win_id) self.setObjectName(self.__class__.__name__) self.setAttribute(Qt.WA_StyledBackground) style.set_register_stylesheet(self) self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) self._win_id = win_id self._option = None self._stopwatch = QTime() self._hbox = QHBoxLayout(self) self._hbox.setContentsMargins(0, 0, 0, 0) self._hbox.setSpacing(5) self._stack = QStackedLayout() self._hbox.addLayout(self._stack) self._stack.setContentsMargins(0, 0, 0, 0) self.cmd = command.Command(win_id) self._stack.addWidget(self.cmd) objreg.register('status-command', self.cmd, scope='window', window=win_id) self.txt = textwidget.Text() self._stack.addWidget(self.txt) self._timer_was_active = False self._text_queue = collections.deque() self._text_pop_timer = usertypes.Timer(self, 'statusbar_text_pop') self._text_pop_timer.timeout.connect(self._pop_text) self.set_pop_timer_interval() objreg.get('config').changed.connect(self.set_pop_timer_interval) self.prompt = prompt.Prompt(win_id) self._stack.addWidget(self.prompt) self._previous_widget = PreviousWidget.none self.cmd.show_cmd.connect(self._show_cmd_widget) self.cmd.hide_cmd.connect(self._hide_cmd_widget) self._hide_cmd_widget() prompter = objreg.get('prompter', scope='window', window=self._win_id) prompter.show_prompt.connect(self._show_prompt_widget) prompter.hide_prompt.connect(self._hide_prompt_widget) self._hide_prompt_widget() self.keystring = keystring.KeyString() self._hbox.addWidget(self.keystring) self.url = url.UrlText() self._hbox.addWidget(self.url) self.percentage = percentage.Percentage() self._hbox.addWidget(self.percentage) # We add a parent to Progress here because it calls self.show() based # on some signals, and if that happens before it's added to the layout, # it will quickly blink up as independent window. self.prog = progress.Progress(self) self._hbox.addWidget(self.prog) def __repr__(self): return utils.get_repr(self) @pyqtProperty(bool) def error(self): """Getter for self.error, so it can be used as Qt property.""" # pylint: disable=method-hidden return self._error def _set_error(self, val): """Setter for self.error, so it can be used as Qt property. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ if self._error == val: # This gets called a lot (e.g. if the completion selection was # changed), and setStyleSheet is relatively expensive, so we ignore # this if there's nothing to change. return log.statusbar.debug("Setting error to {}".format(val)) self._error = val self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) if val: # If we got an error while command/prompt was shown, raise the text # widget. self._stack.setCurrentWidget(self.txt) @pyqtProperty(bool) def prompt_active(self): """Getter for self.prompt_active, so it can be used as Qt property.""" # pylint: disable=method-hidden return self._prompt_active def _set_prompt_active(self, val): """Setter for self.prompt_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ log.statusbar.debug("Setting prompt_active to {}".format(val)) self._prompt_active = val self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) @pyqtProperty(bool) def insert_active(self): """Getter for self.insert_active, so it can be used as Qt property.""" # pylint: disable=method-hidden return self._insert_active def _set_insert_active(self, val): """Setter for self.insert_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ log.statusbar.debug("Setting insert_active to {}".format(val)) self._insert_active = val self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) def _pop_text(self): """Display a text in the statusbar and pop it from _text_queue.""" try: error, text = self._text_queue.popleft() except IndexError: self._set_error(False) self.txt.set_text(self.txt.Text.temp, '') self._text_pop_timer.stop() # If a previous widget was interrupted by an error, restore it. if self._previous_widget == PreviousWidget.prompt: self._stack.setCurrentWidget(self.prompt) elif self._previous_widget == PreviousWidget.command: self._stack.setCurrentWidget(self.command) elif self._previous_widget == PreviousWidget.none: pass else: raise AssertionError("Unknown _previous_widget!") return log.statusbar.debug("Displaying {} message: {}".format( 'error' if error else 'text', text)) log.statusbar.debug("Remaining: {}".format(self._text_queue)) self._set_error(error) self.txt.set_text(self.txt.Text.temp, text) def _show_cmd_widget(self): """Show command widget instead of temporary text.""" self._set_error(False) self._previous_widget = PreviousWidget.prompt if self._text_pop_timer.isActive(): self._timer_was_active = True self._text_pop_timer.stop() self._stack.setCurrentWidget(self.cmd) def _hide_cmd_widget(self): """Show temporary text instead of command widget.""" log.statusbar.debug("Hiding cmd widget, queue: {}".format( self._text_queue)) self._previous_widget = PreviousWidget.none if self._timer_was_active: # Restart the text pop timer if it was active before hiding. self._pop_text() self._text_pop_timer.start() self._timer_was_active = False self._stack.setCurrentWidget(self.txt) def _show_prompt_widget(self): """Show prompt widget instead of temporary text.""" if self._stack.currentWidget() is self.prompt: return self._set_error(False) self._set_prompt_active(True) self._previous_widget = PreviousWidget.prompt if self._text_pop_timer.isActive(): self._timer_was_active = True self._text_pop_timer.stop() self._stack.setCurrentWidget(self.prompt) def _hide_prompt_widget(self): """Show temporary text instead of prompt widget.""" self._set_prompt_active(False) self._previous_widget = PreviousWidget.none log.statusbar.debug("Hiding prompt widget, queue: {}".format( self._text_queue)) if self._timer_was_active: # Restart the text pop timer if it was active before hiding. self._pop_text() self._text_pop_timer.start() self._timer_was_active = False self._stack.setCurrentWidget(self.txt) def _disp_text(self, text, error, immediately=False): """Inner logic for disp_error and disp_temp_text. Args: text: The message to display. error: Whether it's an error message (True) or normal text (False) immediately: If set, message gets displayed immediately instead of queued. """ log.statusbar.debug("Displaying text: {} (error={})".format( text, error)) mindelta = config.get('ui', 'message-timeout') if self._stopwatch.isNull(): delta = None self._stopwatch.start() else: delta = self._stopwatch.restart() log.statusbar.debug("queue: {} / delta: {}".format( self._text_queue, delta)) if not self._text_queue and (delta is None or delta > mindelta): # If the queue is empty and we didn't print messages for long # enough, we can take the short route and display the message # immediately. We then start the pop_timer only to restore the # normal state in 2 seconds. log.statusbar.debug("Displaying immediately") self._set_error(error) self.txt.set_text(self.txt.Text.temp, text) self._text_pop_timer.start() elif self._text_queue and self._text_queue[-1] == (error, text): # If we get the same message multiple times in a row and we're # still displaying it *anyways* we ignore the new one log.statusbar.debug("ignoring") elif immediately: # This message is a reaction to a keypress and should be displayed # immediately, temporarily interrupting the message queue. # We display this immediately and restart the timer.to clear it and # display the rest of the queue later. log.statusbar.debug("Moving to beginning of queue") self._set_error(error) self.txt.set_text(self.txt.Text.temp, text) self._text_pop_timer.start() else: # There are still some messages to be displayed, so we queue this # up. log.statusbar.debug("queueing") self._text_queue.append((error, text)) self._text_pop_timer.start() @pyqtSlot(str, bool) def disp_error(self, text, immediately=False): """Display an error in the statusbar. Args: text: The message to display. immediately: If set, message gets displayed immediately instead of queued. """ self._disp_text(text, True, immediately) @pyqtSlot(str, bool) def disp_temp_text(self, text, immediately): """Display a temporary text in the statusbar. Args: text: The message to display. immediately: If set, message gets displayed immediately instead of queued. """ self._disp_text(text, False, immediately) @pyqtSlot(str) def set_text(self, val): """Set a normal (persistent) text in the status bar.""" self.txt.set_text(self.txt.Text.normal, val) @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): """Mark certain modes in the commandline.""" mode_manager = objreg.get('mode-manager', scope='window', window=self._win_id) if mode in mode_manager.passthrough: text = "-- {} MODE --".format(mode.name.upper()) self.txt.set_text(self.txt.Text.normal, text) if mode == usertypes.KeyMode.insert: self._set_insert_active(True) @pyqtSlot(usertypes.KeyMode) def on_mode_left(self, mode): """Clear marked mode.""" mode_manager = objreg.get('mode-manager', scope='window', window=self._win_id) if mode in mode_manager.passthrough: self.txt.set_text(self.txt.Text.normal, '') if mode == usertypes.KeyMode.insert: self._set_insert_active(False) @config.change_filter('ui', 'message-timeout') def set_pop_timer_interval(self): """Update message timeout when config changed.""" self._text_pop_timer.setInterval(config.get('ui', 'message-timeout')) def resizeEvent(self, e): """Extend resizeEvent of QWidget to emit a resized signal afterwards. Args: e: The QResizeEvent. """ super().resizeEvent(e) self.resized.emit(self.geometry()) def moveEvent(self, e): """Extend moveEvent of QWidget to emit a moved signal afterwards. Args: e: The QMoveEvent. """ super().moveEvent(e) self.moved.emit(e.pos())
class _MainContainer(QWidget): currentEditorChanged = pyqtSignal(str) fileOpened = pyqtSignal(str) beforeFileSaved = pyqtSignal(str) fileSaved = pyqtSignal(str) runFile = pyqtSignal(str) showFileInExplorer = pyqtSignal(str) addToProject = pyqtSignal(str) allFilesClosed = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.setAcceptDrops(True) 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._files_handler = files_handler.FilesHandler(self) # Code Navigation self.__code_back = [] self.__code_forward = [] self.__operations = { 0: self._navigate_code_jumps, 1: self._navigate_bookmarks } # Recent files list self.__last_opened_files = [] # QML UI self._add_file_folder = add_file_folder.AddFileFolderWidget(self) if settings.SHOW_START_PAGE: self.show_start_page() IDE.register_service("main_container", self) # Register signals connections connections = ( # { # "target": "main_container", # "signal_name": "updateLocator", # "slot": self._explore_code # }, { "target": "filesystem", "signal_name": "projectOpened", "slot": self._explore_code }, # { # "target": "projects_explore", # "signal_name": "updateLocator", # "slot": self._explore_code # } { "target": "filesystem", "signal_name": "projectClosed", "slot": self._explore_code } ) IDE.register_signals("main_container", connections) fhandler_short = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Tab), self) fhandler_short.activated.connect(self.show_files_handler) # Added for set language # self._setter_language = set_language.SetLanguageFile() def install(self): ninjaide = IDE.get_service("ide") ninjaide.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( lambda: self.allFilesClosed.emit()) self.combo_area.fileClosed.connect(self._add_to_last_opened) 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(ninjaide) data_settings = IDE.data_settings() recent_files = data_settings.value("lastSession/recentFiles") if recent_files is not None: self.__last_opened_files = recent_files ui_tools.install_shortcuts(self, actions.ACTIONS, ninjaide) # self.fileSaved.connect(self._show_message_about_saved) def run_file(self, filepath): self.runFile.emit(filepath) def _show_file_in_explorer(self, filepath): self.showFileInExplorer.emit(filepath) def _add_to_project(self, filepath): self.addToProject.emit(filepath) def show_files_handler(self): self._files_handler.next_item() def hide_files_handler(self): self._files_handler.hide() 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 navigate_code_history(self, operation, forward): self.__operations[operation](forward) def _navigate_code_jumps(self, forward=False): """Navigate between the jump points""" node = None if not forward and self.__code_back: if len(self.__code_back) == 1: return node = self.__code_back.pop() self.__code_forward.append(node) node = self.__code_back[-1] elif forward and self.__code_forward: node = self.__code_forward.pop() self.__code_back.append(node) if node is not None: filename = node[0] line, col = node[1] self.open_file(filename, line, col) def _navigate_bookmarks(self, forward=True): """Navigate between the bookmarks""" current_editor = self.get_current_editor() current_editor.navigate_bookmarks(forward=forward) def _set_focus_to_editor(self): status_bar = IDE.get_service("status_bar") tools_doock = IDE.get_service("tools_dock") editor_widget = self.get_current_editor() if status_bar.isVisible() and tools_doock.isVisible(): status_bar.hide_status_bar() elif tools_doock.isVisible(): tools_doock._hide() elif status_bar.isVisible(): status_bar.hide_status_bar() if editor_widget is not None: editor_widget.extra_selections.remove("find") editor_widget.scrollbar().remove_marker("find") def split_assistance(self): editor_widget = self.get_current_editor() if editor_widget is not None: split_widget = split_orientation.SplitOrientation(self) split_widget.show() def show_dialog(self, widget): self.add_widget(widget) self.stack.setCurrentWidget(widget) def show_split(self, orientation_vertical=False): orientation = Qt.Horizontal if orientation_vertical: orientation = Qt.Vertical self.combo_area.split_editor(orientation) 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 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 get_current_editor(self): current_widget = self.combo_area.current_editor() if isinstance(current_widget, editor.NEditor): return current_widget return None @property def last_opened_files(self): return self.__last_opened_files def _add_to_last_opened(self, nfile): MAX_RECENT_FILES = 10 # FIXME: configuration if nfile.is_new_file: return file_path = nfile.file_path if file_path in self.__last_opened_files: self.__last_opened_files.remove(file_path) self.__last_opened_files.insert(0, file_path) if len(self.__last_opened_files) > MAX_RECENT_FILES: self.__last_opened_files.pop(-1) def clear_last_opened_files(self): self.__last_opened_files.clear() def open_file(self, filename='', line=-1, col=0, ignore_checkers=False): if not filename: logger.debug("Has no filename") if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") editor_widget = self.get_current_editor() ninjaide = IDE.get_service("ide") current_project = ninjaide.get_current_project() # TODO: handle current project in NProject if current_project is not None: directory = current_project.full_path elif editor_widget is not None and editor_widget.file_path: directory = file_manager.get_folder( editor_widget.file_path) filenames = QFileDialog.getOpenFileNames( self, translations.TR_OPEN_A_FILE, directory, settings.get_supported_extensions_filter(), initialFilter="Python files (*.py *.pyw)" )[0] else: logger.debug("Has filename") filenames = [filename] if not filenames: return for filename in filenames: image_extensions = ("png", "jpg", "jpeg", "bmp", "gif") if file_manager.get_file_extension(filename) in image_extensions: logger.debug("Will open as image") self.open_image(filename) else: logger.debug("Will try to open: %s" % filename) self.__open_file( filename, line, col, ignore_checkers=ignore_checkers) def __open_file(self, filename, line, col, ignore_checkers=False): try: self.add_editor(filename) if line != -1: self.editor_go_to_line(line, col) self.currentEditorChanged.emit(filename) except file_manager.NinjaIOException as reason: QMessageBox.information( self, translations.TR_OPEN_FILE_ERROR, str(reason)) logger.error("The file %s couldn't be open" % filename) def open_image(self, filename): for index in range(self.combo_area.stacked.count()): widget = self.combo_area.stacked.widget(index) if isinstance(widget, image_viewer.ImageViewer): if widget.image_filename == filename: logger.debug("Image already open") self.combo_area._set_current(neditable=None, index=index) return viewer = image_viewer.ImageViewer(filename) self.combo_area.add_image_viewer(viewer) self.stack.setCurrentWidget(self.splitter) def autosave_file(self): for neditable in self.combo_area.bar.get_editables(): neditable.autosave_file() def save_file(self, editor_widget=None): if editor_widget is None: # This may return None if there is not editor present editor_widget = self.get_current_editor() if editor_widget is None: return False # Ok, we have an editor instance # Save to file only if editor really was modified if editor_widget.is_modified: try: if editor_widget.nfile.is_new_file or \ not editor_widget.nfile.has_write_permission(): return self.save_file_as(editor_widget) file_path = editor_widget.file_path # Emit signal before save self.beforeFileSaved.emit(file_path) if settings.REMOVE_TRAILING_SPACES: editor_widget.remove_trailing_spaces() if settings.ADD_NEW_LINE_AT_EOF: editor_widget.insert_block_at_end() # Save content editor_widget.neditable.save_content() # FIXME: encoding message = translations.TR_FILE_SAVED.format(file_path) self.fileSaved.emit(message) return True except Exception as reason: logger.error("Save file error: %s" % reason) QMessageBox.information( self, translations.TR_SAVE_FILE_ERROR_TITLE, translations.TR_SAVE_FILE_ERROR_BODY ) return False def save_file_as(self, editor_widget=None): force = False if editor_widget is None: # We invoque from menu editor_widget = self.get_current_editor() if editor_widget is None: # We haven't editor in main container return False force = True try: filters = "(*.py);;(*.*)" if editor_widget.file_path is not None: # Existing file extension = file_manager.get_file_extension( editor_widget.file_path) if extension != 'py': filters = "(*.%s);;(*.py);;(*.*)" % extension save_folder = self._get_save_folder(editor_widget.file_path) else: save_folder = settings.WORKSPACE filename = QFileDialog.getSaveFileName( self, translations.TR_SAVE_FILE_DIALOG, save_folder, filters )[0] if not filename: return False # FIXME: remove trailing spaces extension = file_manager.get_file_extension(filename) if not extension: filename = "%s.%s" % (filename, "py") editor_widget.neditable.save_content(path=filename, force=force) # self._setter_language.set_language_from_extension(extension) self.fileSaved.emit(translations.TR_FILE_SAVED.format(filename)) self.currentEditorChanged.emit(filename) return True except file_manager.NinjaFileExistsException as reason: QMessageBox.information( self, translations.TR_FILE_ALREADY_EXISTS_TITLE, translations.TR_FILE_ALREADY_EXISTS_BODY.format( reason.filename) ) except Exception as reason: logger.error("Save file as: %s", reason) QMessageBox.information( self, translations.TR_SAVE_FILE_ERROR_TITLE, translations.TR_SAVE_FILE_ERROR_BODY ) return False def save_project(self, project_path): """Save all files in the project path""" for neditable in self.combo_area.bar.get_editables(): file_path = neditable.file_path if file_path is None: # FIXME: New edited files will not be saved, its ok? continue if file_manager.belongs_to_folder(project_path, file_path): neditable.save_content() 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 is not None: return current_project.path return os.path.expanduser("~") def close_file(self): self.combo_area.close_current_file() def add_editor(self, filename=None): ninjaide = IDE.get_service("ide") editable = ninjaide.get_or_create_editable(filename) if editable.editor: # If already open logger.debug("%s is already open" % filename) self.combo_area.set_current(editable) return self.combo_area.current_editor() else: pass editor_widget = self.create_editor_from_editable(editable) # editor_widget.set_language() # 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.combo_area.set_current(editable) self.stack.setCurrentWidget(self.splitter) editor_widget.setFocus() return editor_widget def create_editor_from_editable(self, editable): neditor = editor.create_editor(editable) neditor.zoomChanged.connect(self._on_zoom_changed) neditor.addBackItemNavigation.connect(self.add_back_item_navigation) editable.fileSaved.connect( lambda neditable: self._explore_file_code(neditable.file_path)) return neditor def add_back_item_navigation(self): editor_widget = self.get_current_editor() if editor_widget is not None: item = (editor_widget.file_path, editor_widget.cursor_position) if item not in self.__code_back: self.__code_back.append(item) # self.__code_forward.clear() def _on_zoom_changed(self, zoom): text = "Zoom: {}%".format(str(zoom)) ide = IDE.get_service("ide") ide.show_message(text) def add_widget(self, widget): self.stack.addWidget(widget) def show_start_page(self): """Show Start Page widget in main container""" startp = self.stack.widget(0) if isinstance(startp, start_page.StartPage): self.stack.setCurrentIndex(0) else: startp = start_page.StartPage(parent=self) startp.newFile.connect(self.add_editor) self.stack.insertWidget(0, startp) self.stack.setCurrentIndex(0) def _files_closed(self): if settings.SHOW_START_PAGE: self.show_start_page() def add_status_bar(self, status_bar): self._vbox.addWidget(status_bar) 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 restyle_editor(self): neditables = self.combo_area.bar.get_editables() for neditable in neditables: neditable.editor.restyle() def zoom_in_editor(self): """Increase the font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.zoom(1.) def zoom_out_editor(self): """Decrease the font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.zoom(-1.) def reset_zoom_editor(self): """Reset the to original font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.reset_zoom() def editor_move_up(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.move_up_down(up=True) def editor_move_down(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.move_up_down() def editor_duplicate_line(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.duplicate_line() def editor_toggle_comment(self): """Mark the current line or selection as a comment.""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.comment_or_uncomment() def editor_go_to_line(self, line, column=0, center=True): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.go_to_line(line, column, center) editor_widget.setFocus() def _editor_settings_changed(self, key, value): key = key.split("/")[-1] editor_widget = self.get_current_editor() if editor_widget is not None: callback = getattr(editor.NEditor, key, False) if callback: callback = callback if not hasattr(callback, "__call__"): # Property! callback = callback.fset callback(editor_widget, value) 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) neditor = self.get_current_editor() if neditor is not None: neditor.show_whitespaces = settings.SHOW_TABS_AND_SPACES def __navigate_with_keyboard(self, forward): """Navigate between the positions in the jump history stack.""" operation = self.combo_area.bar.code_navigator.operation self.navigate_code_history(operation, forward) def navigate_back(self): self.__navigate_with_keyboard(forward=False) def navigate_forward(self): self.__navigate_with_keyboard(forward=True)
class ProjectTreeColumn(QDialog): # Signalsnproject = dockWidget = pyqtSignal('PyQt_PyObject') undockWidget = pyqtSignal() changeTitle = pyqtSignal('PyQt_PyObject', 'QString') updateLocator = pyqtSignal() activeProjectChanged = 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 = [] frame = QFrame() frame.setObjectName("actionbar") box = QVBoxLayout(frame) box.setContentsMargins(1, 1, 1, 1) box.setSpacing(0) self._combo_project = QComboBox() self._combo_project.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed) self._combo_project.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLengthWithIcon) self._combo_project.setObjectName("combo_projects") box.addWidget(self._combo_project) vbox.addWidget(frame) self._combo_project.setContextMenuPolicy(Qt.CustomContextMenu) self._projects_area = QStackedLayout() logger.debug("This is the projects area") vbox.addLayout(self._projects_area) # Empty widget self._empty_proj = QLabel(translations.TR_NO_PROJECTS) self._empty_proj.setAlignment(Qt.AlignCenter) self._empty_proj.setAutoFillBackground(True) self._empty_proj.setBackgroundRole(QPalette.Base) self._projects_area.addWidget(self._empty_proj) self._projects_area.setCurrentWidget(self._empty_proj) self.projects = [] self._combo_project.activated.connect( self._change_current_project) self._combo_project.customContextMenuRequested[ 'const QPoint&'].connect(self.context_menu_for_root) connections = ( { "target": "main_container", "signal_name": "addToProject", "slot": self._add_file_to_project }, { "target": "main_container", "signal_name": "showFileInExplorer", "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._on_ide_going_down) def _on_ide_going_down(self): """Save some settings before close""" if self.current_tree is None: return ds = IDE.data_settings() show_filesize = not bool(self.current_tree.isColumnHidden(1)) ds.setValue("projectsExplorer/showFileSize", show_filesize) 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: path_project = [self.current_project] _add_to_project = add_to_project.AddToProject(path_project, self) _add_to_project.exec_() if not _add_to_project.path_selected: 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( _add_to_project.path_selected, 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) tooltip = utils.path_with_tilde_homepath(project.path) self._combo_project.setToolTip(tooltip) index = self._combo_project.count() - 1 self._combo_project.setItemData(index, project) 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) self._projects_area.setCurrentWidget(ptree) # Can be empty widget self._combo_project.setCurrentIndex(index) # FIXME: improve? # if len(self.projects) == 1: # self._combo_project.currentIndexChanged[int].connect( # self._change_current_project) def _close_project(self, widget): """Close the project related to the tree widget.""" index = self._combo_project.currentIndex() self.projects.remove(widget) # index + 1 is necessary because the widget # with index 0 is the empty widget self._projects_area.takeAt(index + 1) self._combo_project.removeItem(index) index = self._combo_project.currentIndex() self._projects_area.setCurrentIndex(index + 1) 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): nproject = self._combo_project.itemData(index) ninjaide = IDE.get_service("ide") projects = ninjaide.get_projects() for project in projects.values(): if project == nproject: nproject.is_current = True else: project.is_current = False self._projects_area.setCurrentIndex(index + 1) self.activeProjectChanged.emit() 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.current_project is not None: 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): project = None if self._projects_area.count() > 0 and self.current_tree is not None: project = self.current_tree.project return project @property def current_tree(self): tree = None widget = self._projects_area.currentWidget() if isinstance(widget, TreeProjectsWidget): tree = widget return tree 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 not name: name = file_manager.get_basename(folder) if not 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) > MAX_RECENT_PROJECTS: 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) if self.current_tree is None: # No projects return path = self.current_tree.project.path # Reset index self.current_tree.setCurrentIndex(QModelIndex()) 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) menu.addSeparator() action_run_project = menu.addAction(translations.TR_RUN_PROJECT) action_properties = menu.addAction(translations.TR_PROJECT_PROPERTIES) action_show_file_size = menu.addAction(translations.TR_SHOW_FILESIZE) menu.addSeparator() action_close = menu.addAction(translations.TR_CLOSE_PROJECT) # Connections 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_create_init.triggered.connect(self.current_tree._create_init) action_run_project.triggered.connect( self.current_tree._execute_project) action_properties.triggered.connect( self.current_tree.open_project_properties) action_close.triggered.connect(self.current_tree._close_project) action_show_file_size.triggered.connect( self.current_tree.show_filesize_info) # 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 RightPanel(QFrame): def __init__(self, app, parent=None): super().__init__(parent) self._app = app self._pixmap = None self._layout = QVBoxLayout(self) self._stacked_layout = QStackedLayout() self.scrollarea = ScrollArea(self._app, self) self.table_container = self.scrollarea.t # TODO: collection container will be removed self.collection_container = CollectionContainer(self._app, self) self.bottom_panel = BottomPanel(app, self) self._setup_ui() def _setup_ui(self): self.scrollarea.setMinimumHeight(100) self.collection_container.hide() self._layout.addWidget(self.bottom_panel) self._layout.addLayout(self._stacked_layout) self._stacked_layout.addWidget(self.scrollarea) self._stacked_layout.addWidget(self.collection_container) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.setSpacing(0) def show_model(self, model): self.set_body(self.scrollarea) # TODO: use PreemptiveTask aio.create_task(self.table_container.show_model(model)) def show_songs(self, songs): self.collection_container.hide() self.set_body(self.scrollarea) self.table_container.show_songs(songs) def set_body(self, widget): self._stacked_layout.setCurrentWidget(widget) def show_collection(self, coll): def _show_pure_albums_coll(coll): self.set_body(self.scrollarea) reader = wrap(coll.models) self.table_container.show_albums_coll(reader) def _show_pure_songs_coll(coll): self.set_body(self.scrollarea) self.table_container.show_collection(coll) def _show_mixed_coll(coll): self.set_body(self.collection_container) self.collection_container.show_collection(coll) def _show_pure_videos_coll(coll): from feeluown.gui.page_containers.table import VideosRenderer self.set_body(self.scrollarea) reader = wrap(coll.models) renderer = VideosRenderer(reader) aio.create_task(self.table_container.set_renderer(renderer)) if coll.type is CollectionType.sys_library: self._app.browser.goto(page='/colls/library') return types = set() for model in coll.models: types.add(model.meta.model_type) if len(types) >= 2: break if len(types) == 1: type_ = types.pop() if type_ == ModelType.song: _show_pure_songs_coll(coll) elif type_ == ModelType.album: _show_pure_albums_coll(coll) elif type_ == ModelType.video: _show_pure_videos_coll(coll) else: _show_mixed_coll(coll) else: _show_mixed_coll(coll) def show_background_image(self, pixmap): self._pixmap = pixmap if pixmap is None: self.table_container.meta_widget.setMinimumHeight(0) else: height = (self._app.height() - self.bottom_panel.height() - self.table_container.toolbar.height()) // 2 self.table_container.meta_widget.setMinimumHeight(height) self.update() def paintEvent(self, e): """ draw pixmap as a the background with a dark overlay HELP: currently, this cost much CPU """ painter = QPainter(self) painter.setPen(Qt.NoPen) painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.SmoothPixmapTransform) # calculate available size draw_width = self.width() draw_height = 10 # spacing defined in table container draw_height += self.bottom_panel.height() if self.table_container.meta_widget.isVisible(): draw_height += self.table_container.meta_widget.height() extra = self.table_container.current_extra if extra is not None and extra.isVisible(): draw_height += extra.height() if self.table_container.toolbar.isVisible(): draw_height += self.table_container.toolbar.height() scrolled = self.scrollarea.verticalScrollBar().value() max_scroll_height = draw_height - self.bottom_panel.height() if scrolled >= max_scroll_height: painter.save() painter.setBrush(self.palette().brush(QPalette.Window)) painter.drawRect(self.bottom_panel.rect()) painter.restore() return if self._pixmap is not None: self._draw_pixmap(painter, draw_width, draw_height, scrolled) self._draw_pixmap_overlay(painter, draw_width, draw_height, scrolled) else: # draw gradient for widgets(bottom panel + meta_widget + ...) above table self._draw_overlay(painter, draw_width, draw_height, scrolled) # if scrolled height > 30, draw background to seperate bottom_panel and body if scrolled >= 30: painter.save() painter.setBrush(self.palette().brush(QPalette.Window)) painter.drawRect(self.bottom_panel.rect()) painter.restore() return # since the body's background color is palette(base), we use # the color to draw background for remain empty area painter.save() painter.setBrush(self.palette().brush(QPalette.Base)) painter.drawRect(0, draw_height, draw_width, self.height() - draw_height) painter.restore() painter.end() def _draw_pixmap_overlay(self, painter, draw_width, draw_height, scrolled): painter.save() rect = QRect(0, 0, draw_width, draw_height) painter.translate(0, -scrolled) gradient = QLinearGradient(rect.topLeft(), rect.bottomLeft()) color = self.palette().color(QPalette.Base) if draw_height == self.height(): gradient.setColorAt(0, add_alpha(color, 180)) gradient.setColorAt(1, add_alpha(color, 230)) else: if self._app.theme_mgr.theme == Light: gradient.setColorAt(0, add_alpha(color, 220)) gradient.setColorAt(0.1, add_alpha(color, 180)) gradient.setColorAt(0.2, add_alpha(color, 140)) gradient.setColorAt(0.6, add_alpha(color, 140)) gradient.setColorAt(0.8, add_alpha(color, 200)) gradient.setColorAt(0.9, add_alpha(color, 240)) gradient.setColorAt(1, color) else: gradient.setColorAt(0, add_alpha(color, 50)) gradient.setColorAt(0.6, add_alpha(color, 100)) gradient.setColorAt(0.8, add_alpha(color, 200)) gradient.setColorAt(0.9, add_alpha(color, 240)) gradient.setColorAt(1, color) painter.setBrush(gradient) painter.drawRect(rect) painter.restore() def _draw_overlay(self, painter, draw_width, draw_height, scrolled): painter.save() rect = QRect(0, 0, draw_width, draw_height) painter.translate(0, -scrolled) gradient = QLinearGradient(rect.topLeft(), rect.bottomLeft()) gradient.setColorAt(0, self.palette().color(QPalette.Window)) gradient.setColorAt(1, self.palette().color(QPalette.Base)) painter.setBrush(gradient) painter.drawRect(rect) painter.restore() def _draw_pixmap(self, painter, draw_width, draw_height, scrolled): # scale pixmap scaled_pixmap = self._pixmap.scaledToWidth( draw_width, mode=Qt.SmoothTransformation) pixmap_size = scaled_pixmap.size() # draw the center part of the pixmap on available rect painter.save() brush = QBrush(scaled_pixmap) painter.setBrush(brush) # note: in practice, most of the time, we can't show the # whole artist pixmap, as a result, the artist head will be cut, # which causes bad visual effect. So we render the top-center part # of the pixmap here. y = (pixmap_size.height() - draw_height) // 3 painter.translate(0, - y - scrolled) rect = QRect(0, y, draw_width, draw_height) painter.drawRect(rect) painter.restore() def sizeHint(self): size = super().sizeHint() return QSize(760, size.height())
class StatusBar(QWidget): """The statusbar at the bottom of the mainwindow. Attributes: txt: The Text widget in the statusbar. keystring: The KeyString widget in the statusbar. percentage: The Percentage widget in the statusbar. url: The UrlText widget in the statusbar. prog: The Progress widget in the statusbar. cmd: The Command widget in the statusbar. _hbox: The main QHBoxLayout. _stack: The QStackedLayout with cmd/txt widgets. _text_queue: A deque of (error, text) tuples to be displayed. error: True if message is an error, False otherwise _text_pop_timer: A Timer displaying the error messages. _last_text_time: The timestamp where a message was last displayed. _timer_was_active: Whether the _text_pop_timer was active before hiding the command widget. _previous_widget: A PreviousWidget member - the widget which was displayed when an error interrupted it. _win_id: The window ID the statusbar is associated with. Class attributes: _error: If there currently is an error, accessed through the error property. For some reason we need to have this as class attribute so pyqtProperty works correctly. _prompt_active: If we're currently in prompt-mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _insert_active: If we're currently in insert mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. Signals: resized: Emitted when the statusbar has resized, so the completion widget can adjust its size to it. arg: The new size. moved: Emitted when the statusbar has moved, so the completion widget can move the the right position. arg: The new position. """ resized = pyqtSignal('QRect') moved = pyqtSignal('QPoint') _error = False _prompt_active = False _insert_active = False STYLESHEET = """ QWidget#StatusBar { {{ color['statusbar.bg'] }} } QWidget#StatusBar[insert_active="true"] { {{ color['statusbar.bg.insert'] }} } QWidget#StatusBar[prompt_active="true"] { {{ color['statusbar.bg.prompt'] }} } QWidget#StatusBar[error="true"] { {{ color['statusbar.bg.error'] }} } QLabel, QLineEdit { {{ color['statusbar.fg'] }} {{ font['statusbar'] }} } """ def __init__(self, win_id, parent=None): super().__init__(parent) objreg.register('statusbar', self, scope='window', window=win_id) self.setObjectName(self.__class__.__name__) self.setAttribute(Qt.WA_StyledBackground) style.set_register_stylesheet(self) self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) self._win_id = win_id self._option = None self._last_text_time = None self._hbox = QHBoxLayout(self) self._hbox.setContentsMargins(0, 0, 0, 0) self._hbox.setSpacing(5) self._stack = QStackedLayout() self._hbox.addLayout(self._stack) self._stack.setContentsMargins(0, 0, 0, 0) self.cmd = command.Command(win_id) self._stack.addWidget(self.cmd) objreg.register('status-command', self.cmd, scope='window', window=win_id) self.txt = textwidget.Text() self._stack.addWidget(self.txt) self._timer_was_active = False self._text_queue = collections.deque() self._text_pop_timer = usertypes.Timer(self, 'statusbar_text_pop') self._text_pop_timer.timeout.connect(self._pop_text) self.set_pop_timer_interval() objreg.get('config').changed.connect(self.set_pop_timer_interval) self.prompt = prompt.Prompt(win_id) self._stack.addWidget(self.prompt) self._previous_widget = PreviousWidget.none self.cmd.show_cmd.connect(self._show_cmd_widget) self.cmd.hide_cmd.connect(self._hide_cmd_widget) self._hide_cmd_widget() prompter = objreg.get('prompter', scope='window', window=self._win_id) prompter.show_prompt.connect(self._show_prompt_widget) prompter.hide_prompt.connect(self._hide_prompt_widget) self._hide_prompt_widget() self.keystring = keystring.KeyString() self._hbox.addWidget(self.keystring) self.url = url.UrlText() self._hbox.addWidget(self.url) self.percentage = percentage.Percentage() self._hbox.addWidget(self.percentage) # We add a parent to Progress here because it calls self.show() based # on some signals, and if that happens before it's added to the layout, # it will quickly blink up as independent window. self.prog = progress.Progress(self) self._hbox.addWidget(self.prog) def __repr__(self): return utils.get_repr(self) @pyqtProperty(bool) def error(self): """Getter for self.error, so it can be used as Qt property.""" # pylint: disable=method-hidden return self._error def _set_error(self, val): """Setter for self.error, so it can be used as Qt property. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ if self._error == val: # This gets called a lot (e.g. if the completion selection was # changed), and setStyleSheet is relatively expensive, so we ignore # this if there's nothing to change. return log.statusbar.debug("Setting error to {}".format(val)) self._error = val self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) if val: # If we got an error while command/prompt was shown, raise the text # widget. self._stack.setCurrentWidget(self.txt) @pyqtProperty(bool) def prompt_active(self): """Getter for self.prompt_active, so it can be used as Qt property.""" # pylint: disable=method-hidden return self._prompt_active def _set_prompt_active(self, val): """Setter for self.prompt_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ log.statusbar.debug("Setting prompt_active to {}".format(val)) self._prompt_active = val self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) @pyqtProperty(bool) def insert_active(self): """Getter for self.insert_active, so it can be used as Qt property.""" # pylint: disable=method-hidden return self._insert_active def _set_insert_active(self, val): """Setter for self.insert_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ log.statusbar.debug("Setting insert_active to {}".format(val)) self._insert_active = val self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) def _pop_text(self): """Display a text in the statusbar and pop it from _text_queue.""" try: error, text = self._text_queue.popleft() except IndexError: self._set_error(False) self.txt.set_text(self.txt.Text.temp, '') self._text_pop_timer.stop() # If a previous widget was interrupted by an error, restore it. if self._previous_widget == PreviousWidget.prompt: self._stack.setCurrentWidget(self.prompt) elif self._previous_widget == PreviousWidget.command: self._stack.setCurrentWidget(self.command) elif self._previous_widget == PreviousWidget.none: pass else: raise AssertionError("Unknown _previous_widget!") return log.statusbar.debug("Displaying {} message: {}".format( 'error' if error else 'text', text)) log.statusbar.debug("Remaining: {}".format(self._text_queue)) self._set_error(error) self.txt.set_text(self.txt.Text.temp, text) def _show_cmd_widget(self): """Show command widget instead of temporary text.""" self._set_error(False) self._previous_widget = PreviousWidget.prompt if self._text_pop_timer.isActive(): self._timer_was_active = True self._text_pop_timer.stop() self._stack.setCurrentWidget(self.cmd) def _hide_cmd_widget(self): """Show temporary text instead of command widget.""" log.statusbar.debug("Hiding cmd widget, queue: {}".format( self._text_queue)) self._previous_widget = PreviousWidget.none if self._timer_was_active: # Restart the text pop timer if it was active before hiding. self._pop_text() self._text_pop_timer.start() self._timer_was_active = False self._stack.setCurrentWidget(self.txt) def _show_prompt_widget(self): """Show prompt widget instead of temporary text.""" if self._stack.currentWidget() is self.prompt: return self._set_error(False) self._set_prompt_active(True) self._previous_widget = PreviousWidget.prompt if self._text_pop_timer.isActive(): self._timer_was_active = True self._text_pop_timer.stop() self._stack.setCurrentWidget(self.prompt) def _hide_prompt_widget(self): """Show temporary text instead of prompt widget.""" self._set_prompt_active(False) self._previous_widget = PreviousWidget.none log.statusbar.debug("Hiding prompt widget, queue: {}".format( self._text_queue)) if self._timer_was_active: # Restart the text pop timer if it was active before hiding. self._pop_text() self._text_pop_timer.start() self._timer_was_active = False self._stack.setCurrentWidget(self.txt) def _disp_text(self, text, error, immediately=False): """Inner logic for disp_error and disp_temp_text. Args: text: The message to display. error: Whether it's an error message (True) or normal text (False) immediately: If set, message gets displayed immediately instead of queued. """ # FIXME probably using a QTime here would be easier. # https://github.com/The-Compiler/qutebrowser/issues/124 log.statusbar.debug("Displaying text: {} (error={})".format( text, error)) now = datetime.datetime.now() mindelta = config.get('ui', 'message-timeout') delta = (None if self._last_text_time is None else now - self._last_text_time) self._last_text_time = now log.statusbar.debug("queue: {} / delta: {}".format( self._text_queue, delta)) if not self._text_queue and (delta is None or delta.total_seconds() * 1000.0 > mindelta): # If the queue is empty and we didn't print messages for long # enough, we can take the short route and display the message # immediately. We then start the pop_timer only to restore the # normal state in 2 seconds. log.statusbar.debug("Displaying immediately") self._set_error(error) self.txt.set_text(self.txt.Text.temp, text) self._text_pop_timer.start() elif self._text_queue and self._text_queue[-1] == (error, text): # If we get the same message multiple times in a row and we're # still displaying it *anyways* we ignore the new one log.statusbar.debug("ignoring") elif immediately: # This message is a reaction to a keypress and should be displayed # immediately, temporarily interrupting the message queue. # We display this immediately and restart the timer.to clear it and # display the rest of the queue later. log.statusbar.debug("Moving to beginning of queue") self._set_error(error) self.txt.set_text(self.txt.Text.temp, text) self._text_pop_timer.start() else: # There are still some messages to be displayed, so we queue this # up. log.statusbar.debug("queueing") self._text_queue.append((error, text)) self._text_pop_timer.start() @pyqtSlot(str, bool) def disp_error(self, text, immediately=False): """Display an error in the statusbar. Args: text: The message to display. immediately: If set, message gets displayed immediately instead of queued. """ self._disp_text(text, True, immediately) @pyqtSlot(str, bool) def disp_temp_text(self, text, immediately): """Display a temporary text in the statusbar. Args: text: The message to display. immediately: If set, message gets displayed immediately instead of queued. """ self._disp_text(text, False, immediately) @pyqtSlot(str) def set_text(self, val): """Set a normal (persistent) text in the status bar.""" self.txt.set_text(self.txt.Text.normal, val) @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): """Mark certain modes in the commandline.""" mode_manager = objreg.get('mode-manager', scope='window', window=self._win_id) if mode in mode_manager.passthrough: text = "-- {} MODE --".format(mode.name.upper()) self.txt.set_text(self.txt.Text.normal, text) if mode == usertypes.KeyMode.insert: self._set_insert_active(True) @pyqtSlot(usertypes.KeyMode) def on_mode_left(self, mode): """Clear marked mode.""" mode_manager = objreg.get('mode-manager', scope='window', window=self._win_id) if mode in mode_manager.passthrough: self.txt.set_text(self.txt.Text.normal, '') if mode == usertypes.KeyMode.insert: self._set_insert_active(False) @config.change_filter('ui', 'message-timeout') def set_pop_timer_interval(self): """Update message timeout when config changed.""" self._text_pop_timer.setInterval(config.get('ui', 'message-timeout')) def resizeEvent(self, e): """Extend resizeEvent of QWidget to emit a resized signal afterwards. Args: e: The QResizeEvent. """ super().resizeEvent(e) self.resized.emit(self.geometry()) def moveEvent(self, e): """Extend moveEvent of QWidget to emit a moved signal afterwards. Args: e: The QMoveEvent. """ super().moveEvent(e) self.moved.emit(e.pos())
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 ChordChanges(QWidget): """ Facilitates 60 second chord changes Pick a pair of chord changes and play them alternating for one minute. Enter the number of times you played each chord combined to submit your score. """ def __init__(self, data, *args, **kwargs): super(ChordChanges, self).__init__(*args, **kwargs) self.setWindowTitle("60 Second Chord Changes") self.setProperty("id", "main") self.data = data self.__key = None #self.hbox = QHBoxLayout(self) #self.control_container = QWidget() self.vbox = QVBoxLayout(self) self.vbox.setAlignment(Qt.AlignCenter) self.vbox.setSpacing(4) self.scoreboard = Scoreboard() self.scoreboard.setProperty("id", "scoreboard") self.vbox.addWidget(self.scoreboard) self.start_button = QPushButton("Start Timer") self.start_button.setEnabled(False) self.start_button.setProperty("id", "start") self.start_button.clicked.connect(self.start_session) self.vbox.addWidget(self.start_button) self.vbox.setAlignment(self.start_button, Qt.AlignCenter) self.cancel = QPushButton("Cancel") self.cancel.setEnabled(False) self.cancel.setProperty("id", "cancel") self.cancel.clicked.connect(self.cancel_session) self.vbox.addWidget(self.cancel) self.vbox.setAlignment(self.cancel, Qt.AlignCenter) self.content = QWidget() self.content.setProperty("id", "content") self.vbox.addWidget(self.content) self.content_stack = QStackedLayout(self.content) # Subwidgets of self.content -- Chord Selection, Timer, Score Input self.chord_select = ChordSelect(data) self.chord_select.setProperty("id", "chord_select") self.content_stack.addWidget(self.chord_select) self.chord_select.pair_selected.connect(self.set_key) self.timer = PlayTimer() self.timer.done.connect(self.session_finished) self.content_stack.addWidget(self.timer) self.score_input = ScoreInput() self.content_stack.addWidget(self.score_input) self.score_input.submit.connect(self.enter_score) self.results = Results() self.content_stack.addWidget(self.results) self.results.ok.clicked.connect(self.raise_chordselect) with open("chordchange_styles.qss") as styles: self.setStyleSheet(styles.read()) @property def key(self): return self.__key @key.setter def key(self, key): self.__key = key def set_key(self, key, score): self.start_button.setEnabled(True) self.key = key self.scoreboard.update_key(key, score) def start_session(self): if self.key: self.content_stack.setCurrentWidget(self.timer) self.cancel.setEnabled(True) self.timer.start_timer() def cancel_session(self): self.content_stack.setCurrentWidget(self.chord_select) self.timer.clock.stop() self.cancel.setEnabled(False) def session_finished(self): self.content_stack.setCurrentWidget(self.score_input) def enter_score(self, score): old_score = self.data.highscore(self.key) self.data.add_score(self.key, score, time.time()) if score > old_score: self.chord_select.chord_pair_grid.button_dict[ self.key].score = score self.scoreboard.update_score(score) self.results.generate_results(True, old_score, score) else: self.results.generate_results(False, old_score, score) self.content_stack.setCurrentWidget(self.results) def raise_chordselect(self): self.cancel.setEnabled(False) self.content_stack.setCurrentWidget(self.chord_select) def refresh(self): self.raise_chordselect() self.chord_select.refresh()
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 _MainContainer(QWidget): currentEditorChanged = pyqtSignal(str) fileOpened = pyqtSignal(str) beforeFileSaved = pyqtSignal(str) fileSaved = pyqtSignal(str) runFile = pyqtSignal(str) showFileInExplorer = pyqtSignal(str) addToProject = pyqtSignal(str) allFilesClosed = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.setAcceptDrops(True) 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._files_handler = files_handler.FilesHandler(self) # Code Navigation self.__code_back = [] self.__code_forward = [] self.__operations = { 0: self._navigate_code_jumps, 1: self._navigate_bookmarks } # Recent files list self.__last_opened_files = [] # QML UI self._add_file_folder = add_file_folder.AddFileFolderWidget(self) if settings.SHOW_START_PAGE: self.show_start_page() IDE.register_service("main_container", self) # Register signals connections connections = ( # "slot": self._explore_code { "target": "filesystem", "signal_name": "projectOpened", "slot": self._explore_code }, # "slot": self._explore_code { "target": "filesystem", "signal_name": "projectClosed", "slot": self._explore_code }) IDE.register_signals("main_container", connections) fhandler_short = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Tab), self) fhandler_short.activated.connect(self.show_files_handler) # Added for set language def install(self): ninjaide = IDE.get_service("ide") ninjaide.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( lambda: self.allFilesClosed.emit()) self.combo_area.fileClosed.connect(self._add_to_last_opened) self.splitter.add_widget(self.combo_area) self.add_widget(self.splitter) # Code Locator self._code_locator = locator_widget.LocatorWidget(ninjaide) data_settings = IDE.data_settings() recent_files = data_settings.value("lastSession/recentFiles") if recent_files is not None: self.__last_opened_files = recent_files ui_tools.install_shortcuts(self, actions.ACTIONS, ninjaide) def run_file(self, filepath): self.runFile.emit(filepath) def _show_file_in_explorer(self, filepath): self.showFileInExplorer.emit(filepath) def _add_to_project(self, filepath): self.addToProject.emit(filepath) def show_files_handler(self): self._files_handler.next_item() def hide_files_handler(self): self._files_handler.hide() 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 navigate_code_history(self, operation, forward): self.__operations[operation](forward) def _navigate_code_jumps(self, forward=False): """Navigate between the jump points""" node = None if not forward and self.__code_back: if len(self.__code_back) == 1: return node = self.__code_back.pop() self.__code_forward.append(node) node = self.__code_back[-1] elif forward and self.__code_forward: node = self.__code_forward.pop() self.__code_back.append(node) if node is not None: filename = node[0] line, col = node[1] self.open_file(filename, line, col) def _navigate_bookmarks(self, forward=True): """Navigate between the bookmarks""" current_editor = self.get_current_editor() current_editor.navigate_bookmarks(forward=forward) def _set_focus_to_editor(self): status_bar = IDE.get_service("status_bar") tools_doock = IDE.get_service("tools_dock") editor_widget = self.get_current_editor() if status_bar.isVisible() and tools_doock.isVisible(): status_bar.hide_status_bar() elif tools_doock.isVisible(): tools_doock._hide() elif status_bar.isVisible(): status_bar.hide_status_bar() if editor_widget is not None: editor_widget.extra_selections.remove("find") editor_widget.scrollbar().remove_marker("find") def split_assistance(self): editor_widget = self.get_current_editor() if editor_widget is not None: split_widget = split_orientation.SplitOrientation(self) split_widget.show() def show_dialog(self, widget): self.add_widget(widget) self.stack.setCurrentWidget(widget) def show_split(self, orientation_vertical=False): orientation = Qt.Horizontal if orientation_vertical: orientation = Qt.Vertical self.combo_area.split_editor(orientation) 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 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 get_current_editor(self): current_widget = self.combo_area.current_editor() if isinstance(current_widget, editor.NEditor): return current_widget return None @property def last_opened_files(self): return self.__last_opened_files def _add_to_last_opened(self, nfile): MAX_RECENT_FILES = 10 # FIXME: configuration if nfile.is_new_file: return file_path = nfile.file_path if file_path in self.__last_opened_files: self.__last_opened_files.remove(file_path) self.__last_opened_files.insert(0, file_path) if len(self.__last_opened_files) > MAX_RECENT_FILES: self.__last_opened_files.pop(-1) def clear_last_opened_files(self): self.__last_opened_files.clear() def open_file(self, filename='', line=-1, col=0, ignore_checkers=False): if not filename: logger.debug("Has no filename") if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") editor_widget = self.get_current_editor() ninjaide = IDE.get_service("ide") current_project = ninjaide.get_current_project() # TODO: handle current project in NProject if current_project is not None: directory = current_project.full_path elif editor_widget is not None and editor_widget.file_path: directory = file_manager.get_folder( editor_widget.file_path) filenames = QFileDialog.getOpenFileNames( self, translations.TR_OPEN_A_FILE, directory, settings.get_supported_extensions_filter(), initialFilter="Python files (*.py *.pyw)")[0] else: logger.debug("Has filename") filenames = [filename] if not filenames: return for filename in filenames: image_extensions = ("png", "jpg", "jpeg", "bmp", "gif") if file_manager.get_file_extension(filename) in image_extensions: logger.debug("Will open as image") self.open_image(filename) else: logger.debug("Will try to open: %s" % filename) self.__open_file(filename, line, col, ignore_checkers=ignore_checkers) def __open_file(self, filename, line, col, ignore_checkers=False): try: self.add_editor(filename) if line != -1: self.editor_go_to_line(line, col) self.currentEditorChanged.emit(filename) except file_manager.NinjaIOException as reason: QMessageBox.information(self, translations.TR_OPEN_FILE_ERROR, str(reason)) logger.error("The file %s couldn't be open" % filename) def open_image(self, filename): for index in range(self.combo_area.stacked.count()): widget = self.combo_area.stacked.widget(index) if isinstance(widget, image_viewer.ImageViewer): if widget.image_filename == filename: logger.debug("Image already open") self.combo_area._set_current(neditable=None, index=index) return viewer = image_viewer.ImageViewer(filename) self.combo_area.add_image_viewer(viewer) self.stack.setCurrentWidget(self.splitter) def autosave_file(self): for neditable in self.combo_area.bar.get_editables(): neditable.autosave_file() def save_file(self, editor_widget=None): if editor_widget is None: # This may return None if there is not editor present editor_widget = self.get_current_editor() if editor_widget is None: return False # Ok, we have an editor instance # Save to file only if editor really was modified if editor_widget.is_modified: try: if editor_widget.nfile.is_new_file or \ not editor_widget.nfile.has_write_permission(): return self.save_file_as(editor_widget) file_path = editor_widget.file_path # Emit signal before save self.beforeFileSaved.emit(file_path) if settings.REMOVE_TRAILING_SPACES: editor_widget.remove_trailing_spaces() if settings.ADD_NEW_LINE_AT_EOF: editor_widget.insert_block_at_end() # Save content editor_widget.neditable.save_content() # FIXME: encoding message = translations.TR_FILE_SAVED.format(file_path) self.fileSaved.emit(message) return True except Exception as reason: logger.error("Save file error: %s" % reason) QMessageBox.information(self, translations.TR_SAVE_FILE_ERROR_TITLE, translations.TR_SAVE_FILE_ERROR_BODY) return False def save_file_as(self, editor_widget=None): force = False if editor_widget is None: # We invoque from menu editor_widget = self.get_current_editor() if editor_widget is None: # We haven't editor in main container return False force = True try: filters = "(*.py);;(*.*)" if editor_widget.file_path is not None: # Existing file extension = file_manager.get_file_extension( editor_widget.file_path) if extension != 'py': filters = "(*.%s);;(*.py);;(*.*)" % extension save_folder = self._get_save_folder(editor_widget.file_path) else: save_folder = settings.WORKSPACE filename = QFileDialog.getSaveFileName( self, translations.TR_SAVE_FILE_DIALOG, save_folder, filters)[0] if not filename: return False # FIXME: remove trailing spaces extension = file_manager.get_file_extension(filename) if not extension: filename = "%s.%s" % (filename, "py") editor_widget.neditable.save_content(path=filename, force=force) self.fileSaved.emit(translations.TR_FILE_SAVED.format(filename)) self.currentEditorChanged.emit(filename) return True except file_manager.NinjaFileExistsException as reason: QMessageBox.information( self, translations.TR_FILE_ALREADY_EXISTS_TITLE, translations.TR_FILE_ALREADY_EXISTS_BODY.format( reason.filename)) except Exception as reason: logger.error("Save file as: %s", reason) QMessageBox.information(self, translations.TR_SAVE_FILE_ERROR_TITLE, translations.TR_SAVE_FILE_ERROR_BODY) return False def save_project(self, project_path): """Save all files in the project path""" for neditable in self.combo_area.bar.get_editables(): file_path = neditable.file_path if file_path is None: # FIXME: New edited files will not be saved, its ok? continue if file_manager.belongs_to_folder(project_path, file_path): neditable.save_content() 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 is not None: return current_project.path return os.path.expanduser("~") def close_file(self): self.combo_area.close_current_file() def add_editor(self, filename=None): ninjaide = IDE.get_service("ide") editable = ninjaide.get_or_create_editable(filename) if editable.editor: # If already open logger.debug("%s is already open" % filename) self.combo_area.set_current(editable) return self.combo_area.current_editor() else: pass editor_widget = 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.combo_area.set_current(editable) self.stack.setCurrentWidget(self.splitter) editor_widget.setFocus() return editor_widget def create_editor_from_editable(self, editable): neditor = editor.create_editor(editable) neditor.zoomChanged.connect(self._on_zoom_changed) neditor.addBackItemNavigation.connect(self.add_back_item_navigation) editable.fileSaved.connect( lambda neditable: self._explore_file_code(neditable.file_path)) return neditor def add_back_item_navigation(self): editor_widget = self.get_current_editor() if editor_widget is not None: item = (editor_widget.file_path, editor_widget.cursor_position) if item not in self.__code_back: self.__code_back.append(item) def _on_zoom_changed(self, zoom): text = "Zoom: {}%".format(str(zoom)) ide = IDE.get_service("ide") ide.show_message(text) def add_widget(self, widget): self.stack.addWidget(widget) def show_start_page(self): """Show Start Page widget in main container""" startp = self.stack.widget(0) if isinstance(startp, start_page.StartPage): self.stack.setCurrentIndex(0) else: startp = start_page.StartPage(parent=self) startp.newFile.connect(self.add_editor) self.stack.insertWidget(0, startp) self.stack.setCurrentIndex(0) def _files_closed(self): if settings.SHOW_START_PAGE: self.show_start_page() def add_status_bar(self, status_bar): self._vbox.addWidget(status_bar) 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 restyle_editor(self): neditables = self.combo_area.bar.get_editables() for neditable in neditables: neditable.editor.restyle() def zoom_in_editor(self): """Increase the font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.zoom(1.) def zoom_out_editor(self): """Decrease the font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.zoom(-1.) def reset_zoom_editor(self): """Reset the to original font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.reset_zoom() def editor_move_up(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.move_up_down(up=True) def editor_move_down(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.move_up_down() def editor_duplicate_line(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.duplicate_line() def editor_toggle_comment(self): """Mark the current line or selection as a comment.""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.comment_or_uncomment() def editor_go_to_line(self, line, column=0, center=True): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.go_to_line(line, column, center) editor_widget.setFocus() def _editor_settings_changed(self, key, value): key = key.split("/")[-1] editor_widget = self.get_current_editor() if editor_widget is not None: callback = getattr(editor.NEditor, key, False) if callback: callback = callback if not hasattr(callback, "__call__"): # Property! callback = callback.fset callback(editor_widget, value) 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) neditor = self.get_current_editor() if neditor is not None: neditor.show_whitespaces = settings.SHOW_TABS_AND_SPACES def __navigate_with_keyboard(self, forward): """Navigate between the positions in the jump history stack.""" operation = self.combo_area.bar.code_navigator.operation self.navigate_code_history(operation, forward) def navigate_back(self): self.__navigate_with_keyboard(forward=False) def navigate_forward(self): self.__navigate_with_keyboard(forward=True)
class StatusBar(QWidget): """The statusbar at the bottom of the mainwindow. Attributes: txt: The Text widget in the statusbar. keystring: The KeyString widget in the statusbar. percentage: The Percentage widget in the statusbar. url: The UrlText widget in the statusbar. prog: The Progress widget in the statusbar. cmd: The Command widget in the statusbar. _hbox: The main QHBoxLayout. _stack: The QStackedLayout with cmd/txt widgets. _text_queue: A deque of (error, text) tuples to be displayed. error: True if message is an error, False otherwise _text_pop_timer: A Timer displaying the error messages. _stopwatch: A QTime for the last displayed message. _timer_was_active: Whether the _text_pop_timer was active before hiding the command widget. _previous_widget: A PreviousWidget member - the widget which was displayed when an error interrupted it. _win_id: The window ID the statusbar is associated with. Class attributes: _severity: The severity of the current message, a Severity member. For some reason we need to have this as class attribute so pyqtProperty works correctly. _prompt_active: If we're currently in prompt-mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _insert_active: If we're currently in insert mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _command_active: If we're currently in command mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _caret_mode: The current caret mode (off/on/selection). For some reason we need to have this as class attribute so pyqtProperty works correctly. Signals: resized: Emitted when the statusbar has resized, so the completion widget can adjust its size to it. arg: The new size. moved: Emitted when the statusbar has moved, so the completion widget can move to the right position. arg: The new position. """ resized = pyqtSignal('QRect') moved = pyqtSignal('QPoint') _severity = None _prompt_active = False _insert_active = False _command_active = False _caret_mode = CaretMode.off STYLESHEET = """ QWidget#StatusBar, QWidget#StatusBar QLabel, QWidget#StatusBar QLineEdit { font: {{ font['statusbar'] }}; background-color: {{ color['statusbar.bg'] }}; color: {{ color['statusbar.fg'] }}; } QWidget#StatusBar[caret_mode="on"], QWidget#StatusBar[caret_mode="on"] QLabel, QWidget#StatusBar[caret_mode="on"] QLineEdit { color: {{ color['statusbar.fg.caret'] }}; background-color: {{ color['statusbar.bg.caret'] }}; } QWidget#StatusBar[caret_mode="selection"], QWidget#StatusBar[caret_mode="selection"] QLabel, QWidget#StatusBar[caret_mode="selection"] QLineEdit { color: {{ color['statusbar.fg.caret-selection'] }}; background-color: {{ color['statusbar.bg.caret-selection'] }}; } QWidget#StatusBar[severity="error"], QWidget#StatusBar[severity="error"] QLabel, QWidget#StatusBar[severity="error"] QLineEdit { color: {{ color['statusbar.fg.error'] }}; background-color: {{ color['statusbar.bg.error'] }}; } QWidget#StatusBar[severity="warning"], QWidget#StatusBar[severity="warning"] QLabel, QWidget#StatusBar[severity="warning"] QLineEdit { color: {{ color['statusbar.fg.warning'] }}; background-color: {{ color['statusbar.bg.warning'] }}; } QWidget#StatusBar[prompt_active="true"], QWidget#StatusBar[prompt_active="true"] QLabel, QWidget#StatusBar[prompt_active="true"] QLineEdit { color: {{ color['statusbar.fg.prompt'] }}; background-color: {{ color['statusbar.bg.prompt'] }}; } QWidget#StatusBar[insert_active="true"], QWidget#StatusBar[insert_active="true"] QLabel, QWidget#StatusBar[insert_active="true"] QLineEdit { color: {{ color['statusbar.fg.insert'] }}; background-color: {{ color['statusbar.bg.insert'] }}; } QWidget#StatusBar[command_active="true"], QWidget#StatusBar[command_active="true"] QLabel, QWidget#StatusBar[command_active="true"] QLineEdit { color: {{ color['statusbar.fg.command'] }}; background-color: {{ color['statusbar.bg.command'] }}; } """ def __init__(self, win_id, parent=None): super().__init__(parent) objreg.register('statusbar', self, scope='window', window=win_id) self.setObjectName(self.__class__.__name__) self.setAttribute(Qt.WA_StyledBackground) style.set_register_stylesheet(self) self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) self._win_id = win_id self._option = None self._stopwatch = QTime() self._hbox = QHBoxLayout(self) self.set_hbox_padding() objreg.get('config').changed.connect(self.set_hbox_padding) self._hbox.setSpacing(5) self._stack = QStackedLayout() self._hbox.addLayout(self._stack) self._stack.setContentsMargins(0, 0, 0, 0) self.cmd = command.Command(win_id) self._stack.addWidget(self.cmd) objreg.register('status-command', self.cmd, scope='window', window=win_id) self.txt = textwidget.Text() self._stack.addWidget(self.txt) self._timer_was_active = False self._text_queue = collections.deque() self._text_pop_timer = usertypes.Timer(self, 'statusbar_text_pop') self._text_pop_timer.timeout.connect(self._pop_text) self.set_pop_timer_interval() objreg.get('config').changed.connect(self.set_pop_timer_interval) self.prompt = prompt.Prompt(win_id) self._stack.addWidget(self.prompt) self._previous_widget = PreviousWidget.none self.cmd.show_cmd.connect(self._show_cmd_widget) self.cmd.hide_cmd.connect(self._hide_cmd_widget) self._hide_cmd_widget() prompter = objreg.get('prompter', scope='window', window=self._win_id) prompter.show_prompt.connect(self._show_prompt_widget) prompter.hide_prompt.connect(self._hide_prompt_widget) self._hide_prompt_widget() self.keystring = keystring.KeyString() self._hbox.addWidget(self.keystring) self.url = url.UrlText() self._hbox.addWidget(self.url) self.percentage = percentage.Percentage() self._hbox.addWidget(self.percentage) self.tabindex = tabindex.TabIndex() self._hbox.addWidget(self.tabindex) # We add a parent to Progress here because it calls self.show() based # on some signals, and if that happens before it's added to the layout, # it will quickly blink up as independent window. self.prog = progress.Progress(self) self._hbox.addWidget(self.prog) objreg.get('config').changed.connect(self.maybe_hide) QTimer.singleShot(0, self.maybe_hide) def __repr__(self): return utils.get_repr(self) @config.change_filter('ui', 'hide-statusbar') def maybe_hide(self): """Hide the statusbar if it's configured to do so.""" hide = config.get('ui', 'hide-statusbar') if hide: self.hide() else: self.show() @config.change_filter('ui', 'statusbar-padding') def set_hbox_padding(self): padding = config.get('ui', 'statusbar-padding') self._hbox.setContentsMargins(padding.left, 0, padding.right, 0) @pyqtProperty(str) def severity(self): """Getter for self.severity, so it can be used as Qt property. Return: The severity as a string (!) """ if self._severity is None: return "" else: return self._severity.name def _set_severity(self, severity): """Set the severity for the current message. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. Args: severity: A Severity member. """ if self._severity == severity: # This gets called a lot (e.g. if the completion selection was # changed), and setStyleSheet is relatively expensive, so we ignore # this if there's nothing to change. return log.statusbar.debug("Setting severity to {}".format(severity)) self._severity = severity self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) if severity != Severity.normal: # If we got an error while command/prompt was shown, raise the text # widget. self._stack.setCurrentWidget(self.txt) @pyqtProperty(bool) def prompt_active(self): """Getter for self.prompt_active, so it can be used as Qt property.""" return self._prompt_active def _set_prompt_active(self, val): """Setter for self.prompt_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ log.statusbar.debug("Setting prompt_active to {}".format(val)) self._prompt_active = val self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) @pyqtProperty(bool) def command_active(self): """Getter for self.command_active, so it can be used as Qt property.""" return self._command_active @pyqtProperty(bool) def insert_active(self): """Getter for self.insert_active, so it can be used as Qt property.""" return self._insert_active @pyqtProperty(str) def caret_mode(self): """Getter for self._caret_mode, so it can be used as Qt property.""" return self._caret_mode.name def set_mode_active(self, mode, val): """Setter for self.{insert,command,caret}_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ if mode == usertypes.KeyMode.insert: log.statusbar.debug("Setting insert_active to {}".format(val)) self._insert_active = val if mode == usertypes.KeyMode.command: log.statusbar.debug("Setting command_active to {}".format(val)) self._command_active = val elif mode == usertypes.KeyMode.caret: webview = objreg.get('tabbed-browser', scope='window', window=self._win_id).currentWidget() log.statusbar.debug("Setting caret_mode - val {}, selection " "{}".format(val, webview.selection_enabled)) if val: if webview.selection_enabled: self._set_mode_text("{} selection".format(mode.name)) self._caret_mode = CaretMode.selection else: self._set_mode_text(mode.name) self._caret_mode = CaretMode.on else: self._caret_mode = CaretMode.off self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) def _set_mode_text(self, mode): """Set the mode text.""" text = "-- {} MODE --".format(mode.upper()) self.txt.set_text(self.txt.Text.normal, text) def _pop_text(self): """Display a text in the statusbar and pop it from _text_queue.""" try: severity, text = self._text_queue.popleft() except IndexError: self._set_severity(Severity.normal) self.txt.set_text(self.txt.Text.temp, '') self._text_pop_timer.stop() # If a previous widget was interrupted by an error, restore it. if self._previous_widget == PreviousWidget.prompt: self._stack.setCurrentWidget(self.prompt) elif self._previous_widget == PreviousWidget.command: self._stack.setCurrentWidget(self.cmd) elif self._previous_widget == PreviousWidget.none: self.maybe_hide() else: raise AssertionError("Unknown _previous_widget!") return self.show() log.statusbar.debug("Displaying message: {} (severity {})".format( text, severity)) log.statusbar.debug("Remaining: {}".format(self._text_queue)) self._set_severity(severity) self.txt.set_text(self.txt.Text.temp, text) def _show_cmd_widget(self): """Show command widget instead of temporary text.""" self._set_severity(Severity.normal) self._previous_widget = PreviousWidget.command if self._text_pop_timer.isActive(): self._timer_was_active = True self._text_pop_timer.stop() self._stack.setCurrentWidget(self.cmd) self.show() def _hide_cmd_widget(self): """Show temporary text instead of command widget.""" log.statusbar.debug("Hiding cmd widget, queue: {}".format( self._text_queue)) self._previous_widget = PreviousWidget.none if self._timer_was_active: # Restart the text pop timer if it was active before hiding. self._pop_text() self._text_pop_timer.start() self._timer_was_active = False self._stack.setCurrentWidget(self.txt) self.maybe_hide() def _show_prompt_widget(self): """Show prompt widget instead of temporary text.""" if self._stack.currentWidget() is self.prompt: return self._set_severity(Severity.normal) self._set_prompt_active(True) self._previous_widget = PreviousWidget.prompt if self._text_pop_timer.isActive(): self._timer_was_active = True self._text_pop_timer.stop() self._stack.setCurrentWidget(self.prompt) self.show() def _hide_prompt_widget(self): """Show temporary text instead of prompt widget.""" self._set_prompt_active(False) self._previous_widget = PreviousWidget.none log.statusbar.debug("Hiding prompt widget, queue: {}".format( self._text_queue)) if self._timer_was_active: # Restart the text pop timer if it was active before hiding. self._pop_text() self._text_pop_timer.start() self._timer_was_active = False self._stack.setCurrentWidget(self.txt) self.maybe_hide() def _disp_text(self, text, severity, immediately=False): """Inner logic for disp_error and disp_temp_text. Args: text: The message to display. severity: The severity of the messages. immediately: If set, message gets displayed immediately instead of queued. """ log.statusbar.debug("Displaying text: {} (severity={})".format( text, severity)) mindelta = config.get('ui', 'message-timeout') if self._stopwatch.isNull(): delta = None self._stopwatch.start() else: delta = self._stopwatch.restart() log.statusbar.debug("queue: {} / delta: {}".format( self._text_queue, delta)) if not self._text_queue and (delta is None or delta > mindelta): # If the queue is empty and we didn't print messages for long # enough, we can take the short route and display the message # immediately. We then start the pop_timer only to restore the # normal state in 2 seconds. log.statusbar.debug("Displaying immediately") self._set_severity(severity) self.show() self.txt.set_text(self.txt.Text.temp, text) self._text_pop_timer.start() elif self._text_queue and self._text_queue[-1] == (severity, text): # If we get the same message multiple times in a row and we're # still displaying it *anyways* we ignore the new one log.statusbar.debug("ignoring") elif immediately: # This message is a reaction to a keypress and should be displayed # immediately, temporarily interrupting the message queue. # We display this immediately and restart the timer.to clear it and # display the rest of the queue later. log.statusbar.debug("Moving to beginning of queue") self._set_severity(severity) self.show() self.txt.set_text(self.txt.Text.temp, text) self._text_pop_timer.start() else: # There are still some messages to be displayed, so we queue this # up. log.statusbar.debug("queueing") self._text_queue.append((severity, text)) self._text_pop_timer.start() @pyqtSlot(str, bool) def disp_error(self, text, immediately=False): """Display an error in the statusbar. Args: text: The message to display. immediately: If set, message gets displayed immediately instead of queued. """ self._disp_text(text, Severity.error, immediately) @pyqtSlot(str, bool) def disp_warning(self, text, immediately=False): """Display a warning in the statusbar. Args: text: The message to display. immediately: If set, message gets displayed immediately instead of queued. """ self._disp_text(text, Severity.warning, immediately) @pyqtSlot(str, bool) def disp_temp_text(self, text, immediately): """Display a temporary text in the statusbar. Args: text: The message to display. immediately: If set, message gets displayed immediately instead of queued. """ self._disp_text(text, Severity.normal, immediately) @pyqtSlot(str) def set_text(self, val): """Set a normal (persistent) text in the status bar.""" self.txt.set_text(self.txt.Text.normal, val) @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): """Mark certain modes in the commandline.""" keyparsers = objreg.get('keyparsers', scope='window', window=self._win_id) if keyparsers[mode].passthrough: self._set_mode_text(mode.name) if mode in (usertypes.KeyMode.insert, usertypes.KeyMode.command, usertypes.KeyMode.caret): self.set_mode_active(mode, True) @pyqtSlot(usertypes.KeyMode, usertypes.KeyMode) def on_mode_left(self, old_mode, new_mode): """Clear marked mode.""" keyparsers = objreg.get('keyparsers', scope='window', window=self._win_id) if keyparsers[old_mode].passthrough: if keyparsers[new_mode].passthrough: self._set_mode_text(new_mode.name) else: self.txt.set_text(self.txt.Text.normal, '') if old_mode in (usertypes.KeyMode.insert, usertypes.KeyMode.command, usertypes.KeyMode.caret): self.set_mode_active(old_mode, False) @config.change_filter('ui', 'message-timeout') def set_pop_timer_interval(self): """Update message timeout when config changed.""" self._text_pop_timer.setInterval(config.get('ui', 'message-timeout')) def resizeEvent(self, e): """Extend resizeEvent of QWidget to emit a resized signal afterwards. Args: e: The QResizeEvent. """ super().resizeEvent(e) self.resized.emit(self.geometry()) def moveEvent(self, e): """Extend moveEvent of QWidget to emit a moved signal afterwards. Args: e: The QMoveEvent. """ super().moveEvent(e) self.moved.emit(e.pos()) def minimumSizeHint(self): """Set the minimum height to the text height plus some padding.""" padding = config.get('ui', 'statusbar-padding') width = super().minimumSizeHint().width() height = self.fontMetrics().height() + padding.top + padding.bottom return QSize(width, height)
class TaskSpecificStatusWidget(GroupBoxWidget): # noinspection PyArgumentList def __init__(self, parent: QWidget): super(TaskSpecificStatusWidget, self).__init__(parent, _DEFAULT_TITLE, _DEFAULT_ICON) self._placeholder_widget = PlaceholderWidget(self) self._layout = QStackedLayout() self._layout.addWidget(self._placeholder_widget) self._task_id_to_widget_mapping: typing.Dict[TaskID, StatusWidgetBase] = {} for tid in TaskID: try: w = _TASK_ID_TO_WIDGET_TYPE_MAPPING[tid](self) except KeyError: self._task_id_to_widget_mapping[tid] = self._placeholder_widget else: self._task_id_to_widget_mapping[tid] = w self._layout.addWidget(w) _logger.info("Task ID to widget mapping: %r", self._task_id_to_widget_mapping) self._layout.setCurrentWidget(self._placeholder_widget) self.setLayout(self._layout) def on_general_status_update(self, timestamp: float, s: GeneralStatusView): w = self._task_id_to_widget_mapping[s.current_task_id] self._ensure_widget_active(w) # noinspection PyBroadException try: w.on_general_status_update(timestamp, s) except Exception: _logger.exception( f"Task-specific widget update failed; " f"task ID {s.current_task_id!r}, widget {type(w)!r}") self.set_icon(get_icon_name_for_task_id(s.current_task_id)) title = ( f"{get_human_friendly_task_name(s.current_task_id)} task status information" ) if title != self.title(): self.setTitle(title) def reset(self): self._ensure_widget_active(self._placeholder_widget) self.set_icon(_DEFAULT_ICON) self.setTitle(_DEFAULT_TITLE) def _ensure_widget_active(self, new_widget: StatusWidgetBase): if self._layout.currentWidget() is not new_widget: # noinspection PyBroadException try: self._layout.currentWidget().reset() except Exception: _logger.exception( f"Task-specific widget reset failed; widget {type(new_widget)!r}" ) self._layout.setCurrentWidget(new_widget)
class FluidPropertyInterrogatorPlugin(QtWidgets.QWidget): """ Fluid property interrogator GUI """ SINGLE_PHASE = 1 TWO_PHASE = 2 TWO_PHASE_NCG = 3 def __init__(self, **kwargs): super(FluidPropertyInterrogatorPlugin, self).__init__(**kwargs) self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.layoutMain = QVBoxLayout(self) self.ctlFPType = QComboBox(self) self.layoutMain.addWidget(self.ctlFPType) self.layoutFPParams = QStackedLayout() self.ctlSinglePhaseWidget = self.setupSinglePhaseUI(self.layoutFPParams) self.ctlTwoPhaseWidget = self.setupTwoPhaseUI(self.layoutFPParams) self.layoutFPParams.addWidget(self.ctlSinglePhaseWidget) self.layoutFPParams.addWidget(self.ctlTwoPhaseWidget) self.layoutMain.addLayout(self.layoutFPParams) self.ctlFPType.currentIndexChanged.connect(self.onFluidPropertyChanged) def setupSinglePhaseUI(self, layout): widget = QTabWidget(self) widget.addTab(FluidPropertyWidget1PhasePT(self), "p, T") widget.addTab(FluidPropertyWidget1PhaseRhoE(self), "rho, e") widget.addTab(FluidPropertyWidget1PhaseRhoP(self), "rho, p") return widget def setupTwoPhaseUI(self, layout): widget = QTabWidget(self) widget.addTab(FluidPropertyWidget2PhaseP(self), "p") widget.addTab(FluidPropertyWidget2PhaseT(self), "T") return widget def fillFluidProperties(self): """ Fill the combo box with single phase fluid property classes """ fp_classes = [ { 'name': 'Single phase', 'classes': [], 'data': self.SINGLE_PHASE }, { 'name': 'Two phase', 'classes': [], 'data': self.TWO_PHASE } ] json_fps = self.exe_info.json_data.json_data["blocks"]['Modules']['subblocks']['FluidProperties']['star']['subblock_types'] for class_name, vals in json_fps.items(): params = vals['parameters'] if 'fp_type' in params: fp_type = params['fp_type']['default'] if fp_type == 'single-phase-fp': fp_classes[0]['classes'].append(class_name) elif fp_type == 'two-phase-fp': fp_classes[1]['classes'].append(class_name) for fpc in fp_classes: fpc['classes'].sort() sep_indices = [] model = QStandardItemModel() for fpc in fp_classes: parent = QStandardItem(fpc['name']) parent.setEnabled(False) model.appendRow(parent) for c in fpc['classes']: item = QStandardItem(c) item.setData(fpc['data']) model.appendRow(item) sep_indices.append(model.rowCount()) self.ctlFPType.setModel(model) for idx in sep_indices[:-1]: self.ctlFPType.insertSeparator(idx) def onFluidPropertyChanged(self, index): fp_type = self.ctlFPType.model().item(index).data() if fp_type == self.SINGLE_PHASE: self.layoutFPParams.setCurrentWidget(self.ctlSinglePhaseWidget) self.ctlSinglePhaseWidget.currentWidget().updateWidgets() elif fp_type == self.TWO_PHASE: self.layoutFPParams.setCurrentWidget(self.ctlTwoPhaseWidget) self.ctlTwoPhaseWidget.currentWidget().updateWidgets() else: raise SystemExit("Unknown fp_type") def fluidPropertyInputFileBlock(self): str = "" str += "[./fp]\n" str += "type = {}\n".format(self.ctlFPType.currentText()) str += "[../]\n" return str def setExecutablePath(self, exe_path): self.exe_path = exe_path self.exe_info = ExecutableInfo() self.exe_info.setPath(self.exe_path) self.fillFluidProperties() for tab in range(self.ctlSinglePhaseWidget.count()): self.ctlSinglePhaseWidget.widget(tab).setExecutablePath(exe_path) for tab in range(self.ctlTwoPhaseWidget.count()): self.ctlTwoPhaseWidget.widget(tab).setExecutablePath(exe_path)
class MusicPreviewWidget(QWidget): def __init__(self, parent=None): super(MusicPreviewWidget, self).__init__(parent) self._lastbuildtime = 10.0 self._running = None self._current = None self._chooserLabel = QLabel() self._chooser = QComboBox(self, activated=self.selectDocument) self._log = log.Log() self._view = popplerview.View() self._progress = widgets.progressbar.TimedProgressBar() self._stack = QStackedLayout() self._top = QWidget() layout = QVBoxLayout() self.setLayout(layout) layout.addWidget(self._top) layout.addLayout(self._stack) layout.addWidget(self._progress) top = QHBoxLayout() top.setContentsMargins(0, 0, 0, 0) top.setSpacing(2) self._top.setLayout(top) top.addWidget(self._chooserLabel) top.addWidget(self._chooser) top.addStretch(1) self._stack.addWidget(self._log) self._stack.addWidget(self._view) self._top.hide() app.aboutToQuit.connect(self.cleanup) app.translateUI(self) def translateUI(self): self._chooserLabel.setText(_("Document:")) def preview(self, text, title=None): """Runs LilyPond on the given text and shows the resulting PDF.""" j = self._running = MusicPreviewJob(text, title) j.done.connect(self._done) self._log.clear() self._log.connectJob(j) j.start() self._progress.start(self._lastbuildtime) def _done(self, success): self._progress.stop(False) pdfs = self._running.resultfiles() self.setDocuments(pdfs) if not pdfs: self._stack.setCurrentWidget(self._log) return self._lastbuildtime = self._running.elapsed_time() self._stack.setCurrentWidget(self._view) if self._current: self._current.cleanup() self._current = self._running # keep the tempdir self._running = None def setDocuments(self, pdfs): """Loads the given PDF path names in the UI.""" self._documents = [popplertools.Document(name) for name in pdfs] self._chooser.clear() self._chooser.addItems([d.name() for d in self._documents]) self._top.setVisible(len(self._documents) > 1) if pdfs: self._chooser.setCurrentIndex(0) self.selectDocument(0) else: self._view.clear() def selectDocument(self, index): doc = self._documents[index].document() if doc: self._view.load(doc) def cleanup(self): if self._running: self._running.abort() self._running.cleanup() self._running = None if self._current: self._current.cleanup() self._current = None self._stack.setCurrentWidget(self._log) self._top.hide() self._view.clear() def print_(self): """Prints the currently displayed document.""" if self._documents: doc = self._documents[self._chooser.currentIndex()] import popplerprint popplerprint.printDocument(doc, self)
class StatusBar(QWidget): """Statusbar widget to display permanent and temporary messages. Packs three labels for permanent messages ("left", "center", "right") and one for temporary ones ("message"). The three labels are grouped into one hbox. The hbox and the message label are both in a QStackedLayout to toggle between them. Attributes: timer: QTimer object to remove temporary messages after timeout. status: Widget grouping the labels for status messages. message: Label to display messages. stack: Stacked layout to switch between status and message. """ STYLESHEET = """ QWidget, QWidget QLabel { font: {statusbar.font}; background-color: {statusbar.bg}; color: {statusbar.fg}; padding-top: {statusbar.padding}; padding-bottom: {statusbar.padding}; } """ def __init__(self): super().__init__() self.timer = QTimer() self.timer.setInterval(api.settings.statusbar.message_timeout.value) self.timer.setSingleShot(True) self.status = StatusLabels() self.message = QLabel() self.stack = QStackedLayout(self) self.stack.addWidget(self.status) self.stack.addWidget(self.message) self.stack.setCurrentWidget(self.status) styles.apply(self) self.timer.timeout.connect(self.clear_message) api.status.signals.clear.connect(self.clear_message) utils.log.statusbar_loghandler.message.connect(self._on_message) api.status.signals.update.connect(self._on_update_status) api.settings.statusbar.message_timeout.changed.connect( self._on_timeout_changed) @utils.slot def _on_message(self, severity: str, message: str): """Display log message when logging was called. Args: severity: levelname of the log record. message: message of the log record. """ self._set_severity_style(severity) self.message.setText(message) self.stack.setCurrentWidget(self.message) self.timer.start() @utils.slot def _on_update_status(self): """Update the statusbar.""" mode = api.modes.current().name.lower() for position, label in self.status: text = self._get_text(position, mode) label.setText(text) @utils.slot def clear_message(self): """Remove a temporary message from the statusbar.""" if self.timer.isActive(): self.timer.stop() self._clear_severity_style() self.message.clear() self.stack.setCurrentWidget(self.status) def _on_timeout_changed(self, value: int): self.timer.setInterval(value) def _get_text(self, position, mode): """Get the text to put into one label depending on the current mode. Args: position: One of "left", "center", "right" defining the label. mode: Current mode. """ try: # Prefer mode specific setting text = api.settings.get_value(f"statusbar.{position}_{mode}") except KeyError: text = api.settings.get_value(f"statusbar.{position}") return api.status.evaluate(text.replace(" ", " ")) def _set_severity_style(self, severity): """Set the style of the statusbar for a temporary message. Adds a colored border to the top of the statusbar. The border color depends on the severity. Args: severity: One of "debug", "info", "warning", "error" """ append = f""" QLabel {{ border-top: {{statusbar.message_border}} {{statusbar.{severity}}}; }} """ styles.apply(self, append) def _clear_severity_style(self): styles.apply(self)
class StatusBar(QWidget): """The statusbar at the bottom of the mainwindow. Attributes: txt: The Text widget in the statusbar. keystring: The KeyString widget in the statusbar. percentage: The Percentage widget in the statusbar. url: The UrlText widget in the statusbar. prog: The Progress widget in the statusbar. cmd: The Command widget in the statusbar. _hbox: The main QHBoxLayout. _stack: The QStackedLayout with cmd/txt widgets. _text_queue: A deque of (error, text) tuples to be displayed. error: True if message is an error, False otherwise _text_pop_timer: A Timer displaying the error messages. _stopwatch: A QTime for the last displayed message. _timer_was_active: Whether the _text_pop_timer was active before hiding the command widget. _previous_widget: A PreviousWidget member - the widget which was displayed when an error interrupted it. _win_id: The window ID the statusbar is associated with. Class attributes: _severity: The severity of the current message, a Severity member. For some reason we need to have this as class attribute so pyqtProperty works correctly. _prompt_active: If we're currently in prompt-mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _insert_active: If we're currently in insert mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _command_active: If we're currently in command mode. For some reason we need to have this as class attribute so pyqtProperty works correctly. _caret_mode: The current caret mode (off/on/selection). For some reason we need to have this as class attribute so pyqtProperty works correctly. Signals: resized: Emitted when the statusbar has resized, so the completion widget can adjust its size to it. arg: The new size. moved: Emitted when the statusbar has moved, so the completion widget can move to the right position. arg: The new position. """ resized = pyqtSignal('QRect') moved = pyqtSignal('QPoint') _severity = None _prompt_active = False _insert_active = False _command_active = False _caret_mode = CaretMode.off STYLESHEET = """ QWidget#StatusBar, QWidget#StatusBar QLabel, QWidget#StatusBar QLineEdit { font: {{ font['statusbar'] }}; background-color: {{ color['statusbar.bg'] }}; color: {{ color['statusbar.fg'] }}; } QWidget#StatusBar[caret_mode="on"], QWidget#StatusBar[caret_mode="on"] QLabel, QWidget#StatusBar[caret_mode="on"] QLineEdit { color: {{ color['statusbar.fg.caret'] }}; background-color: {{ color['statusbar.bg.caret'] }}; } QWidget#StatusBar[caret_mode="selection"], QWidget#StatusBar[caret_mode="selection"] QLabel, QWidget#StatusBar[caret_mode="selection"] QLineEdit { color: {{ color['statusbar.fg.caret-selection'] }}; background-color: {{ color['statusbar.bg.caret-selection'] }}; } QWidget#StatusBar[severity="error"], QWidget#StatusBar[severity="error"] QLabel, QWidget#StatusBar[severity="error"] QLineEdit { color: {{ color['statusbar.fg.error'] }}; background-color: {{ color['statusbar.bg.error'] }}; } QWidget#StatusBar[severity="warning"], QWidget#StatusBar[severity="warning"] QLabel, QWidget#StatusBar[severity="warning"] QLineEdit { color: {{ color['statusbar.fg.warning'] }}; background-color: {{ color['statusbar.bg.warning'] }}; } QWidget#StatusBar[prompt_active="true"], QWidget#StatusBar[prompt_active="true"] QLabel, QWidget#StatusBar[prompt_active="true"] QLineEdit { color: {{ color['statusbar.fg.prompt'] }}; background-color: {{ color['statusbar.bg.prompt'] }}; } QWidget#StatusBar[insert_active="true"], QWidget#StatusBar[insert_active="true"] QLabel, QWidget#StatusBar[insert_active="true"] QLineEdit { color: {{ color['statusbar.fg.insert'] }}; background-color: {{ color['statusbar.bg.insert'] }}; } QWidget#StatusBar[command_active="true"], QWidget#StatusBar[command_active="true"] QLabel, QWidget#StatusBar[command_active="true"] QLineEdit { color: {{ color['statusbar.fg.command'] }}; background-color: {{ color['statusbar.bg.command'] }}; } """ def __init__(self, win_id, parent=None): super().__init__(parent) objreg.register('statusbar', self, scope='window', window=win_id) self.setObjectName(self.__class__.__name__) self.setAttribute(Qt.WA_StyledBackground) style.set_register_stylesheet(self) self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) self._win_id = win_id self._option = None self._stopwatch = QTime() self._hbox = QHBoxLayout(self) self.set_hbox_padding() objreg.get('config').changed.connect(self.set_hbox_padding) self._hbox.setSpacing(5) self._stack = QStackedLayout() self._hbox.addLayout(self._stack) self._stack.setContentsMargins(0, 0, 0, 0) self.cmd = command.Command(win_id) self._stack.addWidget(self.cmd) objreg.register('status-command', self.cmd, scope='window', window=win_id) self.txt = textwidget.Text() self._stack.addWidget(self.txt) self._timer_was_active = False self._text_queue = collections.deque() self._text_pop_timer = usertypes.Timer(self, 'statusbar_text_pop') self._text_pop_timer.timeout.connect(self._pop_text) self.set_pop_timer_interval() objreg.get('config').changed.connect(self.set_pop_timer_interval) self.prompt = prompt.Prompt(win_id) self._stack.addWidget(self.prompt) self._previous_widget = PreviousWidget.none self.cmd.show_cmd.connect(self._show_cmd_widget) self.cmd.hide_cmd.connect(self._hide_cmd_widget) self._hide_cmd_widget() prompter = objreg.get('prompter', scope='window', window=self._win_id) prompter.show_prompt.connect(self._show_prompt_widget) prompter.hide_prompt.connect(self._hide_prompt_widget) self._hide_prompt_widget() self.keystring = keystring.KeyString() self._hbox.addWidget(self.keystring) self.url = url.UrlText() self._hbox.addWidget(self.url) self.percentage = percentage.Percentage() self._hbox.addWidget(self.percentage) self.tabindex = tabindex.TabIndex() self._hbox.addWidget(self.tabindex) # We add a parent to Progress here because it calls self.show() based # on some signals, and if that happens before it's added to the layout, # it will quickly blink up as independent window. self.prog = progress.Progress(self) self._hbox.addWidget(self.prog) objreg.get('config').changed.connect(self.maybe_hide) QTimer.singleShot(0, self.maybe_hide) def __repr__(self): return utils.get_repr(self) @config.change_filter('ui', 'hide-statusbar') def maybe_hide(self): """Hide the statusbar if it's configured to do so.""" hide = config.get('ui', 'hide-statusbar') if hide: self.hide() else: self.show() @config.change_filter('ui', 'statusbar-padding') def set_hbox_padding(self): padding = config.get('ui', 'statusbar-padding') self._hbox.setContentsMargins(padding.left, 0, padding.right, 0) @pyqtProperty(str) def severity(self): """Getter for self.severity, so it can be used as Qt property. Return: The severity as a string (!) """ if self._severity is None: return "" else: return self._severity.name def _set_severity(self, severity): """Set the severity for the current message. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. Args: severity: A Severity member. """ if self._severity == severity: # This gets called a lot (e.g. if the completion selection was # changed), and setStyleSheet is relatively expensive, so we ignore # this if there's nothing to change. return log.statusbar.debug("Setting severity to {}".format(severity)) self._severity = severity self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) if severity != Severity.normal: # If we got an error while command/prompt was shown, raise the text # widget. self._stack.setCurrentWidget(self.txt) @pyqtProperty(bool) def prompt_active(self): """Getter for self.prompt_active, so it can be used as Qt property.""" return self._prompt_active def _set_prompt_active(self, val): """Setter for self.prompt_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ log.statusbar.debug("Setting prompt_active to {}".format(val)) self._prompt_active = val self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) @pyqtProperty(bool) def command_active(self): """Getter for self.command_active, so it can be used as Qt property.""" return self._command_active @pyqtProperty(bool) def insert_active(self): """Getter for self.insert_active, so it can be used as Qt property.""" return self._insert_active @pyqtProperty(str) def caret_mode(self): """Getter for self._caret_mode, so it can be used as Qt property.""" return self._caret_mode.name def set_mode_active(self, mode, val): """Setter for self.{insert,command,caret}_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ if mode == usertypes.KeyMode.insert: log.statusbar.debug("Setting insert_active to {}".format(val)) self._insert_active = val if mode == usertypes.KeyMode.command: log.statusbar.debug("Setting command_active to {}".format(val)) self._command_active = val elif mode == usertypes.KeyMode.caret: webview = objreg.get('tabbed-browser', scope='window', window=self._win_id).currentWidget() log.statusbar.debug("Setting caret_mode - val {}, selection " "{}".format(val, webview.selection_enabled)) if val: if webview.selection_enabled: self._set_mode_text("{} selection".format(mode.name)) self._caret_mode = CaretMode.selection else: self._set_mode_text(mode.name) self._caret_mode = CaretMode.on else: self._caret_mode = CaretMode.off self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) def _set_mode_text(self, mode): """Set the mode text.""" text = "-- {} MODE --".format(mode.upper()) self.txt.set_text(self.txt.Text.normal, text) def _pop_text(self): """Display a text in the statusbar and pop it from _text_queue.""" try: severity, text = self._text_queue.popleft() except IndexError: self._set_severity(Severity.normal) self.txt.set_text(self.txt.Text.temp, '') self._text_pop_timer.stop() # If a previous widget was interrupted by an error, restore it. if self._previous_widget == PreviousWidget.prompt: self._stack.setCurrentWidget(self.prompt) elif self._previous_widget == PreviousWidget.command: self._stack.setCurrentWidget(self.cmd) elif self._previous_widget == PreviousWidget.none: self.maybe_hide() else: raise AssertionError("Unknown _previous_widget!") return self.show() log.statusbar.debug("Displaying message: {} (severity {})".format( text, severity)) log.statusbar.debug("Remaining: {}".format(self._text_queue)) self._set_severity(severity) self.txt.set_text(self.txt.Text.temp, text) def _show_cmd_widget(self): """Show command widget instead of temporary text.""" self._set_severity(Severity.normal) self._previous_widget = PreviousWidget.command if self._text_pop_timer.isActive(): self._timer_was_active = True self._text_pop_timer.stop() self._stack.setCurrentWidget(self.cmd) self.show() def _hide_cmd_widget(self): """Show temporary text instead of command widget.""" log.statusbar.debug("Hiding cmd widget, queue: {}".format( self._text_queue)) self._previous_widget = PreviousWidget.none if self._timer_was_active: # Restart the text pop timer if it was active before hiding. self._pop_text() self._text_pop_timer.start() self._timer_was_active = False self._stack.setCurrentWidget(self.txt) self.maybe_hide() def _show_prompt_widget(self): """Show prompt widget instead of temporary text.""" if self._stack.currentWidget() is self.prompt: return self._set_severity(Severity.normal) self._set_prompt_active(True) self._previous_widget = PreviousWidget.prompt if self._text_pop_timer.isActive(): self._timer_was_active = True self._text_pop_timer.stop() self._stack.setCurrentWidget(self.prompt) self.show() def _hide_prompt_widget(self): """Show temporary text instead of prompt widget.""" self._set_prompt_active(False) self._previous_widget = PreviousWidget.none log.statusbar.debug("Hiding prompt widget, queue: {}".format( self._text_queue)) if self._timer_was_active: # Restart the text pop timer if it was active before hiding. self._pop_text() self._text_pop_timer.start() self._timer_was_active = False self._stack.setCurrentWidget(self.txt) self.maybe_hide() def _disp_text(self, text, severity, immediately=False): """Inner logic for disp_error and disp_temp_text. Args: text: The message to display. severity: The severity of the messages. immediately: If set, message gets displayed immediately instead of queued. """ log.statusbar.debug("Displaying text: {} (severity={})".format( text, severity)) mindelta = config.get('ui', 'message-timeout') if self._stopwatch.isNull(): delta = None self._stopwatch.start() else: delta = self._stopwatch.restart() log.statusbar.debug("queue: {} / delta: {}".format( self._text_queue, delta)) if not self._text_queue and (delta is None or delta > mindelta): # If the queue is empty and we didn't print messages for long # enough, we can take the short route and display the message # immediately. We then start the pop_timer only to restore the # normal state in 2 seconds. log.statusbar.debug("Displaying immediately") self._set_severity(severity) self.show() self.txt.set_text(self.txt.Text.temp, text) self._text_pop_timer.start() elif self._text_queue and self._text_queue[-1] == (severity, text): # If we get the same message multiple times in a row and we're # still displaying it *anyways* we ignore the new one log.statusbar.debug("ignoring") elif immediately: # This message is a reaction to a keypress and should be displayed # immediately, temporarily interrupting the message queue. # We display this immediately and restart the timer.to clear it and # display the rest of the queue later. log.statusbar.debug("Moving to beginning of queue") self._set_severity(severity) self.show() self.txt.set_text(self.txt.Text.temp, text) self._text_pop_timer.start() else: # There are still some messages to be displayed, so we queue this # up. log.statusbar.debug("queueing") self._text_queue.append((severity, text)) self._text_pop_timer.start() @pyqtSlot(str, bool) def disp_error(self, text, immediately=False): """Display an error in the statusbar. Args: text: The message to display. immediately: If set, message gets displayed immediately instead of queued. """ self._disp_text(text, Severity.error, immediately) @pyqtSlot(str, bool) def disp_warning(self, text, immediately=False): """Display a warning in the statusbar. Args: text: The message to display. immediately: If set, message gets displayed immediately instead of queued. """ self._disp_text(text, Severity.warning, immediately) @pyqtSlot(str, bool) def disp_temp_text(self, text, immediately): """Display a temporary text in the statusbar. Args: text: The message to display. immediately: If set, message gets displayed immediately instead of queued. """ self._disp_text(text, Severity.normal, immediately) @pyqtSlot(str) def set_text(self, val): """Set a normal (persistent) text in the status bar.""" self.txt.set_text(self.txt.Text.normal, val) @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): """Mark certain modes in the commandline.""" keyparsers = objreg.get('keyparsers', scope='window', window=self._win_id) if keyparsers[mode].passthrough: self._set_mode_text(mode.name) if mode in (usertypes.KeyMode.insert, usertypes.KeyMode.command, usertypes.KeyMode.caret): self.set_mode_active(mode, True) @pyqtSlot(usertypes.KeyMode, usertypes.KeyMode) def on_mode_left(self, old_mode, new_mode): """Clear marked mode.""" keyparsers = objreg.get('keyparsers', scope='window', window=self._win_id) if keyparsers[old_mode].passthrough: if keyparsers[new_mode].passthrough: self._set_mode_text(new_mode.name) else: self.txt.set_text(self.txt.Text.normal, '') if old_mode in (usertypes.KeyMode.insert, usertypes.KeyMode.command, usertypes.KeyMode.caret): self.set_mode_active(old_mode, False) @config.change_filter('ui', 'message-timeout') def set_pop_timer_interval(self): """Update message timeout when config changed.""" self._text_pop_timer.setInterval(config.get('ui', 'message-timeout')) def resizeEvent(self, e): """Extend resizeEvent of QWidget to emit a resized signal afterwards. Args: e: The QResizeEvent. """ super().resizeEvent(e) self.resized.emit(self.geometry()) def moveEvent(self, e): """Extend moveEvent of QWidget to emit a moved signal afterwards. Args: e: The QMoveEvent. """ super().moveEvent(e) self.moved.emit(e.pos()) def minimumSizeHint(self): """Set the minimum height to the text height plus some padding.""" padding = config.get('ui', 'statusbar-padding') width = super().minimumSizeHint().width() height = self.fontMetrics().height() + padding.top + padding.bottom return QSize(width, height)
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 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 App(ResizeWidget): _changed_playseed = pyqtSignal() def __init__(self, image_files, delay, rate=1.0, qsize=100, parent=None): QStackedWidget.__init__(self, parent) self.status = True self.delay = delay self.rate = rate self.image_files = image_files self.pictures = utils.AlbumReader(*image_files) self.maxqsize = qsize self.timer = QTimer(self) self.timer.setInterval(self.delay) self.timer.setSingleShot(True) self.timer.timeout.connect(self.show_slides) self._timeout = self.delay / 10 self._result = PriorityQueue(self.maxqsize) self._next = 0 self._queued = 0 self._gif_replay_times = [0, 3] self.layout = QStackedLayout(self) # slideshow self.SlideShowWidget = QLabel(self) self.SlideShowWidget.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.info_fps = StdInfo( lambda: "fps: {:.1f}".format(1000. / self.delay * self.rate), align=Qt.AlignRight | Qt.AlignBottom, parent=self) self._changed_playseed.connect(self.info_fps.display) self.info_playspeed = StdInfo(lambda: "x {:.0%}".format(self.rate), align=Qt.AlignRight | Qt.AlignTop, parent=self) self._changed_playseed.connect(self.info_playspeed.display) self._changed_playseed.emit() # video self.MediaPlayer = VLCMediaPlayer(self) if __VLC__ else QMediaPlayer( self) self.VideoWidget = QVideoWidget(self) self.MediaPlayer.setVideoOutput(self.VideoWidget) self.MediaPlayer.setPlaybackRate(self.rate) self.MediaPlayer.mediaStatusChanged.connect(self.end_of_media) self.layout.addWidget(self.SlideShowWidget) self.layout.addWidget(self.VideoWidget) self.threading(int(self.maxqsize / 10)) self.show() def threading(self, n=1): i = 0 while i < n: try: media = next(self.pictures) except StopIteration: self._queued = -1 return 1 if media.tag == 'meta': exec('self.' + media.get('command')) continue thread = ReadMediaThread(media.get('path'), self._result, self._queued, self.size(), parent=self) self._queued += 1 thread.start() i += 1 def show_slides(self): try: i, img_object = self._result.get(True, 5) if isinstance(img_object, QMediaContent): self.MediaPlayer.setMedia(img_object) except Empty: self.timer.stop() return while i != self._next: self._result.put((i, img_object), False) i, img_object = self._result.get() if isinstance(img_object, QMediaContent): self.MediaPlayer.setMedia(img_object) if isinstance(img_object, QMediaContent): # If a movie # self.MediaPlayer.setMedia(img_object) self.layout.setCurrentWidget(self.VideoWidget) self.MediaPlayer.play() elif isinstance(img_object, QMovie): # If a gif size = img_object.scaledSize() img_object = QMovie(img_object.fileName()) img_object.setCacheMode(QMovie.CacheAll) self._gif = img_object img_object.frameChanged.connect(self.gif_frame_changed) self.SlideShowWidget.setMovie(img_object) size.scale(self.SlideShowWidget.size(), Qt.KeepAspectRatio) img_object.setScaledSize(size) img_object.setSpeed(int(self.rate * 100)) self.layout.setCurrentWidget(self.SlideShowWidget) # self.change_playspeed(self.rate) img_object.start() else: # If a picture # print(img_object.size()) self.SlideShowWidget.setPixmap( img_object.scaled(self.SlideShowWidget.size(), Qt.KeepAspectRatio)) self.timer.start(self.delay / self.rate) self.layout.setCurrentWidget(self.SlideShowWidget) self._next += 1 self.threading(self.maxqsize - self._result.qsize()) def keyPressEvent(self, e): key = e.key() if key == Qt.Key_Escape: # Esc: Exit self.close() return if key == Qt.Key_Space: # Space: stop/start self.status = not self.status if self.status: if self.rate > 0: self.timer.start() else: self.timer.stop() return if key == Qt.Key_S: import Mp4Movie app = Mp4Movie.App(self.image_files, self.delay, rate=self.rate, aspect='4X3') app.show_slides(None) # self.close() return if key == 93: # ]: -10% self.change_playspeed(self.rate + 0.1) return if key == 91: # [: +10% self.change_playspeed(self.rate - 0.1) return if e.modifiers( ) == Qt.ControlModifier: # Ctrl + Enter: Toggle Full Screen if key == Qt.Key_Return: self.toggle() def toggle(self): if self.isFullScreen(): self.showNormal() else: self.showFullScreen() def end_of_media(self, status): if status == self.MediaPlayer.EndOfMedia: self.timer.start(0) def change_playspeed(self, speed): if speed < 0: speed = 0 self.rate = speed if self.rate == 0: self.timer.stop() elif self.status and not self.timer.isActive() and not hasattr( self, '_gif'): self.timer.start() if hasattr(self, '_gif'): self._gif.setSpeed(int(speed * 100)) self.MediaPlayer.setPlaybackRate(self.rate) self._changed_playseed.emit() def gif_frame_changed(self, frame): if (frame + 1) == self._gif.frameCount(): self._gif_replay_times[0] += 1 if self._gif_replay_times[0] == self._gif_replay_times[1]: self._gif.stop() self.timer.start(0) self._gif_replay_times[0] = 0 del self._gif
class Window(QWidget): def __init__(self, qu1: Queue, qu2: Queue, qu3: Queue): super().__init__() self.q = qu1 self.q2 = qu2 self.q3 = qu3 self.init_ui() self.showFullScreen() def init_ui(self): self.screen = QDesktopWidget().screenGeometry() self.half_width = (self.screen.width()) / 2 self.setFixedSize(self.screen.width(), self.screen.height() - 70) self.move(0, 0) self.game_mode = 1 # indikator da li se igra sa jednim ili dva igraca self.started = False self.paused = False self.left1 = False self.right1 = False self.left2 = False self.right2 = False self.timer = QTimer() self.timer.timeout.connect(self.tick) self.setWindowTitle('Breakout') self.main_menu = QWidget(self) self.game_widget = QWidget(self) self.game_widget.setMouseTracking(True) self.mouse_x = None self.key_notifier = KeyNotifier() self.key_notifier.key_signal.connect(self.__update_position__) self.key_notifier.start() o_image = QImage(os.path.join('images', 'space.jpg')) s_image = o_image.scaled( QSize(self.screen.width(), self.screen.height())) # resize Image to widgets size palette = QPalette() palette.setBrush(10, QBrush(s_image)) # 10 = Windowrole self.setPalette(palette) self.game = 0 self.painter = QPainter() self.stacked = QStackedLayout(self) self.stacked.addWidget(self.game_widget) self.set_main_menu() self.stacked.setCurrentWidget(self.main_menu) def start1(self): self.game = GameOnePlayer(Size(self.width(), self.height()), self.q2, self.q3) self.left1 = self.right1 = False self.change_current_widget(self.game_widget) self.started = True #self.doAction() self.timer.start(12) def start2(self): self.game = GameTwoPlayers(Size(self.width(), self.height()), self.q2, self.q3) self.left1 = self.right1 = False self.left2 = self.right2 = False self.change_current_widget(self.game_widget) self.started = True self.doAction() self.timer.start(12) def restart(self): if self.game_mode == 1: reply = QMessageBox.question( self, 'Play again', 'Your score: %s.\nDo you want to play again?' % self.game.player.score, QMessageBox.Yes | QMessageBox.No) else: reply = QMessageBox.question( self, 'Play again', 'Player 1 score: %s.\nPlayer 2 score: %s.\nDo you want to play again?' % (self.game.player1.score, self.game.player2.score), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.change_current_widget(self.main_menu) else: self.quit() def change_current_widget(self, widget): self.stacked.setCurrentWidget(widget) self.update() def listen(self): while True: num = self.q.get() print(num) self.game.get_deus(num, self.half_width) def doAction(self): self.q.put('go') t = Thread(target=self.listen) t.start() def tick(self): if self.game.game_over: self.restart() self.timer.stop() if self.game.won: self.notify_win() self.timer.stop() if self.game_mode == 1: turn_rate = 1 if self.right1 else -1 if self.left1 else 0 self.game.tick(turn_rate) else: turn_rate1 = 1 if self.right1 else -1 if self.left1 else 0 turn_rate2 = 1 if self.right2 else -1 if self.left2 else 0 self.game.tick(turn_rate1, turn_rate2) self.repaint() def notify_win(self): if self.game_mode == 1: QMessageBox.information( self, 'Win', 'You win. Your score: %s' % self.game.player.score) else: if self.game.player1.score > self.game.player2.score: QMessageBox.information( self, 'Win', 'Player 1 score: %s\nPlayer 2 score: %s\nCongratulations to player 1!' % (self.game.player1.score, self.game.player2.score)) elif self.game.player1.score < self.game.player2.score: QMessageBox.information( self, 'Win', 'Player 1 score: %s\nPlayer 2 score: %s\nCongratulations to player 2!' % (self.game.player1.score, self.game.player2.score)) else: QMessageBox.information( self, 'Win', 'Player 1 score: %s\nPlayer 2 score: %s\nCongratulations to both players!' % (self.game.player1.score, self.game.player2.score)) self.started = False self.change_current_widget(self.main_menu) def quit(self): os._exit(1) def set_main_menu(self): v_box = QVBoxLayout(self.main_menu) self.add_button('One Player Game', self.start1, v_box) self.add_button('Two Player Game', self.start2, v_box) self.add_button('Quit', self.quit, v_box) v_box.setAlignment(Qt.AlignCenter) self.stacked.addWidget(self.main_menu) #def mousePressEvent(self, event): #self.game.release_ball() def keyPressEvent(self, event): """ SPACE -> release ball ESC -> pause P -> pause/continue Q -> quit One player game -> moving on arrows Two player game -> moving on ASDW and arrows """ key = event.key() self.key_notifier.add_key(event.key()) if self.game_mode == 1: self.left1 = key == Qt.Key_Left self.right1 = key == Qt.Key_Right else: self.left1 = key == Qt.Key_A self.right1 = key == Qt.Key_D self.left2 = key == Qt.Key_Left self.right2 = key == Qt.Key_Right if key == Qt.Key_Space: self.game.release_ball() if key == Qt.Key_Escape: self.timer.stop() self.started = False self.change_current_widget(self.main_menu) if key == Qt.Key_P: self.paused = not self.paused if self.paused: self.timer.stop() else: self.timer.start() if key == Qt.Key_Q: self.quit() def keyReleaseEvent(self, event): key = event.key() self.key_notifier.rem_key(event.key()) if self.game_mode == 1: if key == Qt.Key_Left: self.left1 = False elif key == Qt.Key_Right: self.right1 = False else: if key == Qt.Key_A: self.left1 = False elif key == Qt.Key_D: self.right1 = False elif key == Qt.Key_Left: self.left2 = False elif key == Qt.Key_Right: self.right2 = False def __update_position__(self, key): self.game.update_moving_objects(key) def paintEvent(self, event): self.painter.begin(self) self.draw() self.painter.end() def draw(self): """ Function draws all the objects inside game window """ self.painter.setRenderHint(self.painter.Antialiasing) self.painter.setFont(QFont('Times New Roman', 20)) self.painter.setPen(QColor('silver')) if not self.started: return game = self.game life_img = QImage(os.path.join('images', 'lifebonus.png')).scaled( QSize(30, 30)) # resize Image to widgets size draw_x = 60 draw_y = 30 if self.game.__class__ == GameOnePlayer: self.game_mode = 1 self.painter.drawText( 0, 20, 'Player score: %s' % str(self.game.player.score)) for _ in range(self.game.player.lives): self.painter.drawImage(draw_x, draw_y, life_img) draw_x -= life_img.width() else: self.game_mode = 2 self.painter.drawText( 0, 20, 'Player 1 score: %s' % str(self.game.player1.score)) self.painter.drawText( self.screen.width() - 250, 20, 'Player 2 score: %s' % str(self.game.player2.score)) draw_x = (self.screen.width()) / 2 + 20 draw_y = 0 for _ in range(self.game.player1.lives): self.painter.drawImage(draw_x, draw_y, life_img) draw_x -= life_img.width() self.painter.drawLine(game.frame.left, game.border_line, game.frame.right, game.border_line) if self.game.game_over: return self.draw_game_objects() def draw_game_objects(self): """ Function draw all the objects in the game """ for obj in self.game.get_objects(): self.painter.drawImage( QRectF(*obj.location, obj.frame.width, obj.frame.height), QImage(obj.get_image())) @staticmethod def add_button(text, callback, layout, alignment=Qt.AlignCenter): button = QPushButton(text) button.setFixedSize(400, 50) button.pressed.connect(callback) button.setStyleSheet('QPushButton {' 'font-size: 20px;' 'background-color: transparent;' 'color: rgb(0, 0, 0);' '}' 'QPushButton:focus {' 'opacity: 0.5;' 'background-color: rgba(179, 179, 255,0.3);' '}') button.setAutoDefault(True) layout.addWidget(button, alignment=alignment) return button
class _MainContainer(QWidget): currentEditorChanged = pyqtSignal("QString") fileOpened = pyqtSignal("QString") fileSaved = pyqtSignal("QString") def __init__(self, parent=None): super().__init__(parent) self.setAcceptDrops(True) 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._files_handler = files_handler.FilesHandler(self) # Code Navigation self.__operations = { 0: self._navigate_bookmarks } # QML UI self._add_file_folder = add_file_folder.AddFileFolderWidget(self) if settings.SHOW_START_PAGE: self.show_start_page() IDE.register_service("main_container", self) # Register signals connections connections = ( { "target": "main_container", "signal_name": "updateLocator", "slot": self._explore_code }, { "target": "filesystem", "signal_name": "projectOpened", "slot": self._explore_code }, { "target": "projects_explore", "signal_name": "updateLocator", "slot": self._explore_code } ) IDE.register_signals("main_container", connections) esc_sort = QShortcut(QKeySequence(Qt.Key_Escape), self) esc_sort.activated.connect(self._set_focus_to_editor) # Added for set language self._setter_language = set_language.SetLanguageFile() def install(self): ninjaide = IDE.get_service("ide") ninjaide.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 # Code Locator self._code_locator = locator_widget.LocatorWidget(ninjaide) ui_tools.install_shortcuts(self, actions.ACTIONS, ninjaide) def show_files_handler(self): self._files_handler.next_item() def navigate_code_history(self, operation, forward): self.__operations[operation](forward) def _navigate_bookmarks(self, forward=True): current_editor = self.get_current_editor() current_editor.navigate_bookmarks(forward=forward) def _set_focus_to_editor(self): status_bar = IDE.get_service("status_bar") tools_doock = IDE.get_service("tools_dock") editor_widget = self.get_current_editor() if status_bar.isVisible() and tools_doock.isVisible(): status_bar.hide_status_bar() elif tools_doock.isVisible(): tools_doock._hide() elif status_bar.isVisible(): status_bar.hide_status_bar() if editor_widget is not None: editor_widget.clear_extra_selections('searchs') def split_assistance(self): split_widget = split_orientation.SplitOrientation(self) split_widget.show() def show_dialog(self, widget): self.add_widget(widget) self.stack.setCurrentWidget(widget) def show_split(self, orientation_vertical=False): orientation = Qt.Horizontal if orientation_vertical: orientation = Qt.Vertical self.combo_area.split_editor(orientation) 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 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 get_current_editor(self): current_widget = self.combo_area.current_editor() if isinstance(current_widget, editor.NEditor): return current_widget return None 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 no filename") if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") editor_widget = self.get_current_editor() ninjaide = IDE.get_service("ide") current_project = ninjaide.get_current_project() # TODO: handle current project in NProject if current_project is not None: directory = current_project.full_path elif editor_widget is not None and editor_widget.file_path: directory = file_manager.get_folder( editor_widget.file_path) filenames = QFileDialog.getOpenFileNames( self, "Open File", # FIXME: translations directory, settings.get_supported_extensions_filter() )[0] else: logger.debug("Has filename") filenames = [filename] if not filenames: return for filename in filenames: image_extensions = ("png", "jpg", "jpeg", "bmp", "gif") if file_manager.get_file_extension(filename) in image_extensions: logger.debug("Will open as image") self.open_image(filename) else: logger.debug("Will try to open: %s" % filename) self.__open_file( filename, line, col, ignore_checkers=ignore_checkers) def __open_file(self, filename, line, col, ignore_checkers=False): try: editor_widget = self.add_editor(filename) if line != -1: editor_widget.go_to_line(line, col) self.currentEditorChanged.emit(filename) except file_manager.NinjaIOException as reason: QMessageBox.information( self, "The file couldn't be open", # FIXME: translations str(reason)) logger.error("The file %s couldn't be open" % filename) def open_image(self, filename): for index in range(self.combo_area.stacked.count()): widget = self.combo_area.stacked.widget(index) if isinstance(widget, image_viewer.ImageViewer): if widget.image_filename == filename: logger.debug("Image already open") self.combo_area._set_current(neditable=None, index=index) return viewer = image_viewer.ImageViewer(filename) self.combo_area.add_image_viewer(viewer) self.stack.setCurrentWidget(self.splitter) def save_file(self, editor_widget=None): if editor_widget is None: # This may return None if there is not editor present editor_widget = self.get_current_editor() if editor_widget is None: return False # Ok, we have an editor instance # Save to file only if editor really was modified if editor_widget.is_modified: try: if editor_widget.nfile.is_new_file or \ not editor_widget.nfile.has_write_permission(): return self.save_file_as(editor_widget) # FIXME: beforeFileSaved.emit if settings.REMOVE_TRAILING_SPACES: helpers.remove_trailing_spaces(editor_widget) # FIXME: new line at end if settings.ADD_NEW_LINE_AT_EOF: helpers.insert_block_at_end(editor_widget) # Save content editor_widget.neditable.save_content() # FIXME: encoding # FIXME: translations self.fileSaved.emit("File Saved: %s" % editor_widget.file_path) return True except Exception as reason: logger.error("Save file error: %s" % reason) QMessageBox.information( self, "Save Error", "The file could't be saved!" ) return False def save_file_as(self, editor_widget=None): force = False if editor_widget is None: # We invoque from menu editor_widget = self.get_current_editor() if editor_widget is None: # We haven't editor in main container return False force = True try: filters = "(*.py);;(*.*)" if editor_widget.file_path is not None: # Existing file extension = file_manager.get_file_extension( editor_widget.file_path) if extension != 'py': filters = "(*.%s);;(*.py);;(*.*)" % extension save_folder = self._get_save_folder(editor_widget.file_path) filename = QFileDialog.getSaveFileName( self, "Save File", save_folder, filters )[0] if not filename: return False # FIXME: remove trailing spaces extension = file_manager.get_file_extension(filename) if not extension: filename = "%s.%s" % (filename, "py") editor_widget.neditable.save_content(path=filename, force=force) self._setter_language.set_language_from_extension(extension) self.fileSaved.emit("File Saved: {}".format(filename)) self.currentEditorChanged.emit(filename) return True except file_manager.NinjaFileExistsException as reason: QMessageBox.information( self, "File Already Exists", "Invalid Path: the file '%s' already exists." % reason.filename ) except Exception as reason: logger.error("save_file_as: %s", reason) QMessageBox.information( self, "Save Error", "The file couldn't be saved!" ) return False def save_project(self, project_path): """Save all files in the project path""" for neditable in self.combo_area.bar.get_editables(): file_path = neditable.file_path if file_manager.belongs_to_folder(project_path, file_path): neditable.save_content() 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 is not None: return current_project.path return os.path.expanduser("~") def close_file(self): self.combo_area.close_current_file() def add_editor(self, filename=None): ninjaide = IDE.get_service("ide") editable = ninjaide.get_or_create_editable(filename) if editable.editor: # If already open logger.debug("%s is already open" % filename) self.combo_area.set_current(editable) return self.combo_area.current_editor() else: pass editor_widget = self.create_editor_from_editable(editable) # editor_widget.set_language() # 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.combo_area.set_current(editable) self.stack.setCurrentWidget(self.splitter) editor_widget.setFocus() return editor_widget def create_editor_from_editable(self, editable): neditor = editor.create_editor(editable) neditor.zoomChanged.connect(self._show_zoom_indicator) neditor.destroyed.connect(self.__on_editor_destroyed) # editable.fileSaved.connect(self._editor_tab) return neditor def _show_zoom_indicator(self, zoom): neditor = self.get_current_editor() indicator.Indicator.show_text( neditor, "Zoom: {} %".format(str(zoom))) @pyqtSlot() def __on_editor_destroyed(self): indicator.Indicator.instance = None def add_widget(self, widget): self.stack.addWidget(widget) def show_start_page(self): """Show Start Page widget in main container""" startp = self.stack.widget(0) if isinstance(startp, start_page.StartPage): self.stack.setCurrentIndex(0) else: startp = start_page.StartPage(parent=self) startp.newFile.connect(self.add_editor) self.stack.insertWidget(0, startp) self.stack.setCurrentIndex(0) def _files_closed(self): if settings.SHOW_START_PAGE: self.show_start_page() def add_status_bar(self, status_bar): self._vbox.addWidget(status_bar) 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 zoom_in_editor(self): """Increase the font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.zoom(1.) def zoom_out_editor(self): """Decrease the font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.zoom(-1.) def reset_zoom_editor(self): """Reset the to original font size in the current editor""" editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.reset_zoom() def editor_move_up(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.move_up_down(up=True) def editor_move_down(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.move_up_down() def editor_duplicate_line(self): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.duplicate_line() def editor_comment(self): """Mark the current line or selection as a comment.""" editor_widget = self.get_current_editor() if editor_widget is not None: helpers.comment_or_uncomment(editor_widget) def editor_go_to_line(self, line): editor_widget = self.get_current_editor() if editor_widget is not None: editor_widget.go_to_line(line) editor_widget.setFocus() def _editor_settings_changed(self, key, value): key = key.split("/")[-1] editor_widget = self.get_current_editor() if editor_widget is not None: callback = getattr(editor.NEditor, key, False) if callback: callback = callback if not hasattr(callback, "__call__"): # Property! callback = callback.fset callback(editor_widget, value) 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) neditor = self.get_current_editor() if neditor is not None: neditor.show_whitespaces = settings.SHOW_TABS_AND_SPACES
class MainWindow(QMainWindow): def __init__(self, db, dbFound): super().__init__() self.db = db self.setWindowTitle('Password Manager') self.setStyleSheet(open("styles.qss", "r").read()) self.stacked_layout = QStackedLayout() # Holds various layouts self.create_login_layout(db, dbFound) self.stacked_layout.addWidget(self.login_screen) self.central_widget = QWidget() self.central_widget.setLayout(self.stacked_layout) self.setCentralWidget(self.central_widget) self.setGeometry(500, 500, 600, 300) self.show() def change_layout(self, page): if (page == 0): self.welcome() self.stacked_layout.addWidget(self.welcome_screen) self.stacked_layout.setCurrentWidget(self.welcome_screen) if (page == 1): self.stacked_layout.setCurrentWidget(self.welcome_screen) elif (page == 2): self.create_add_service_layout() self.stacked_layout.addWidget(self.add_service_widget) self.stacked_layout.setCurrentWidget(self.add_service_widget) elif (page == 3): self.create_get_userpass_layout() self.stacked_layout.addWidget(self.get_userpass_widget) self.stacked_layout.setCurrentWidget(self.get_userpass_widget) elif (page == 4): self.create_change_pass_layout() self.stacked_layout.addWidget(self.change_pass_widget) self.stacked_layout.setCurrentWidget(self.change_pass_widget) # elif(sender.text() == 'Generate Password'): # self.create_gen_pass_layout() # self.stacked_layout.addWidget(self.gen_pass_widget) # self.stacked_layout.setCurrentWidget(self.gen_pass_widget) def create_login_layout(self, db, dbFound): grid = QGridLayout() self.login_screen = QWidget() self.login_screen.setLayout(grid) loginLbl = QLabel('Login') loginForm = QLineEdit() loginForm.setPlaceholderText('Password') loginBtn = QPushButton('Login') grid.addWidget(loginLbl, 0, 0) grid.addWidget(loginForm, 0, 1) grid.addWidget(loginBtn, 1, 1) loginBtn.clicked.connect( lambda: login(self, db, dbFound, loginForm.text())) def welcome(self): grid = QGridLayout() self.welcome_screen = QWidget() self.welcome_screen.setLayout(grid) addServiceBtn = QPushButton('Add Service', self) getUserAndPassBtn = QPushButton('Get Username and Password', self) changePassBtn = QPushButton('Change Password', self) # genPassBtn = QPushButton('Generate Password', self) grid.addWidget(addServiceBtn, 0, 0) grid.addWidget(getUserAndPassBtn, 0, 1) grid.addWidget(changePassBtn, 0, 2) # grid.addWidget(genPassBtn, 2, 1) addServiceBtn.clicked.connect(self.change_layout(2)) getUserAndPassBtn.clicked.connect(self.change_layout(3)) changePassBtn.clicked.connect(self.change_layout(4)) # genPassBtn.clicked.connect(self.change_layout) def create_add_service_layout(self): grid = QGridLayout() self.add_service_widget = QWidget() self.add_service_widget.setLayout(grid) serviceLbl = QLabel('Service Name: ') serviceName = QLineEdit() serviceName.setPlaceholderText('Service') userLbl = QLabel('Username: '******'User name') addServiceBtn = QPushButton( 'Add Service') # ~~~~Todo Write Add Service Function~~~~~ DONE returnBtn = QPushButton('Return') grid.addWidget(serviceLbl, 1, 0) grid.addWidget(serviceName, 1, 1) grid.addWidget(userLbl, 2, 0) grid.addWidget(userNameField, 2, 1) grid.addWidget(addServiceBtn, 3, 0) grid.addWidget(returnBtn, 3, 1) addServiceBtn.clicked.connect(lambda: addService( self.db, userNameField.text(), serviceName.text())) returnBtn.clicked.connect(self.change_layout(1)) def create_get_userpass_layout(self): grid = QGridLayout() self.get_userpass_widget = QWidget() self.get_userpass_widget.setLayout(grid) serviceSelecter = QComboBox() for i in RetrieveData.retrieveServices(self.db): serviceSelecter.addItem(i[0]) userLbl = QLabel('Username: '******'Password has been copied to clipboard') getInfoBtn = QPushButton('Get Username and Password') returnBtn = QPushButton('Return') grid.addWidget(serviceSelecter, 1, 0) grid.addWidget(getInfoBtn, 1, 1) grid.addWidget(returnBtn, 2, 1) getInfoBtn.clicked.connect( lambda: getUserPassInfo(self.db, serviceSelecter.currentText())) returnBtn.clicked.connect(self.change_layout(1)) def create_change_pass_layout(self): grid = QGridLayout() self.change_pass_widget = QWidget() self.change_pass_widget.setLayout(grid) serviceLbl = QLabel('Service') serviceSelecter = QComboBox() for i in RetrieveData.retrieveServices(self.db): serviceSelecter.addItem(i[0]) oldLbl = QLabel('Old Password') oldField = QLineEdit() returnBtn = QPushButton('Return') changePassBtn = QPushButton('Confirm Change') grid.addWidget(serviceLbl, 1, 0) grid.addWidget(serviceSelecter, 1, 1) grid.addWidget(oldLbl, 2, 0) grid.addWidget(oldField, 2, 1) grid.addWidget(changePassBtn, 3, 0) grid.addWidget(returnBtn, 3, 1) changePassBtn.clicked.connect(lambda: changePass( self.db, serviceSelecter.currentText(), oldField.text())) returnBtn.clicked.connect(self.change_layout(1))
class StatusBar(QWidget): """The statusbar at the bottom of the mainwindow. Attributes: txt: The Text widget in the statusbar. keystring: The KeyString widget in the statusbar. percentage: The Percentage widget in the statusbar. url: The UrlText widget in the statusbar. prog: The Progress widget in the statusbar. cmd: The Command widget in the statusbar. _hbox: The main QHBoxLayout. _stack: The QStackedLayout with cmd/txt widgets. _win_id: The window ID the statusbar is associated with. Signals: resized: Emitted when the statusbar has resized, so the completion widget can adjust its size to it. arg: The new size. moved: Emitted when the statusbar has moved, so the completion widget can move to the right position. arg: The new position. """ resized = pyqtSignal('QRect') moved = pyqtSignal('QPoint') _severity = None _color_flags = [] STYLESHEET = _generate_stylesheet() def __init__(self, *, win_id, private, parent=None): super().__init__(parent) objreg.register('statusbar', self, scope='window', window=win_id) self.setObjectName(self.__class__.__name__) self.setAttribute(Qt.WA_StyledBackground) config.set_register_stylesheet(self) self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) self._win_id = win_id self._color_flags = ColorFlags() self._color_flags.private = private self._hbox = QHBoxLayout(self) self._set_hbox_padding() self._hbox.setSpacing(5) self._stack = QStackedLayout() self._hbox.addLayout(self._stack) self._stack.setContentsMargins(0, 0, 0, 0) self.cmd = command.Command(private=private, win_id=win_id) self._stack.addWidget(self.cmd) objreg.register('status-command', self.cmd, scope='window', window=win_id) self.txt = textwidget.Text() self._stack.addWidget(self.txt) self.cmd.show_cmd.connect(self._show_cmd_widget) self.cmd.hide_cmd.connect(self._hide_cmd_widget) self._hide_cmd_widget() self.keystring = keystring.KeyString() self._hbox.addWidget(self.keystring) self.url = url.UrlText() self._hbox.addWidget(self.url) self.percentage = percentage.Percentage() self._hbox.addWidget(self.percentage) self.backforward = backforward.Backforward() self._hbox.addWidget(self.backforward) self.tabindex = tabindex.TabIndex() self._hbox.addWidget(self.tabindex) # We add a parent to Progress here because it calls self.show() based # on some signals, and if that happens before it's added to the layout, # it will quickly blink up as independent window. self.prog = progress.Progress(self) self._hbox.addWidget(self.prog) config.instance.changed.connect(self._on_config_changed) QTimer.singleShot(0, self.maybe_hide) def __repr__(self): return utils.get_repr(self) @pyqtSlot(str) def _on_config_changed(self, option): if option == 'statusbar.hide': self.maybe_hide() elif option == 'statusbar.padding': self._set_hbox_padding() @pyqtSlot() def maybe_hide(self): """Hide the statusbar if it's configured to do so.""" tab = self._current_tab() hide = config.val.statusbar.hide if hide or (tab is not None and tab.data.fullscreen): self.hide() else: self.show() def _set_hbox_padding(self): padding = config.val.statusbar.padding self._hbox.setContentsMargins(padding.left, 0, padding.right, 0) @pyqtProperty('QStringList') def color_flags(self): """Getter for self.color_flags, so it can be used as Qt property.""" return self._color_flags.to_stringlist() def _current_tab(self): """Get the currently displayed tab.""" window = objreg.get('tabbed-browser', scope='window', window=self._win_id) return window.currentWidget() def set_mode_active(self, mode, val): """Setter for self.{insert,command,caret}_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ if mode == usertypes.KeyMode.insert: log.statusbar.debug("Setting insert flag to {}".format(val)) self._color_flags.insert = val if mode == usertypes.KeyMode.passthrough: log.statusbar.debug("Setting passthrough flag to {}".format(val)) self._color_flags.passthrough = val if mode == usertypes.KeyMode.command: log.statusbar.debug("Setting command flag to {}".format(val)) self._color_flags.command = val elif mode in [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]: log.statusbar.debug("Setting prompt flag to {}".format(val)) self._color_flags.prompt = val elif mode == usertypes.KeyMode.caret: tab = self._current_tab() log.statusbar.debug("Setting caret flag - val {}, selection " "{}".format(val, tab.caret.selection_enabled)) if val: if tab.caret.selection_enabled: self._set_mode_text("{} selection".format(mode.name)) self._color_flags.caret = ColorFlags.CaretMode.selection else: self._set_mode_text(mode.name) self._color_flags.caret = ColorFlags.CaretMode.on else: self._color_flags.caret = ColorFlags.CaretMode.off config.set_register_stylesheet(self, update=False) def _set_mode_text(self, mode): """Set the mode text.""" if mode == 'passthrough': key_instance = config.key_instance all_bindings = key_instance.get_reverse_bindings_for('passthrough') bindings = all_bindings.get('leave-mode') if bindings: suffix = ' ({} to leave)'.format(bindings[0]) else: suffix = '' else: suffix = '' text = "-- {} MODE --{}".format(mode.upper(), suffix) self.txt.set_text(self.txt.Text.normal, text) def _show_cmd_widget(self): """Show command widget instead of temporary text.""" self._stack.setCurrentWidget(self.cmd) self.show() def _hide_cmd_widget(self): """Show temporary text instead of command widget.""" log.statusbar.debug("Hiding cmd widget") self._stack.setCurrentWidget(self.txt) self.maybe_hide() @pyqtSlot(str) def set_text(self, val): """Set a normal (persistent) text in the status bar.""" self.txt.set_text(self.txt.Text.normal, val) @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): """Mark certain modes in the commandline.""" keyparsers = objreg.get('keyparsers', scope='window', window=self._win_id) if keyparsers[mode].passthrough: self._set_mode_text(mode.name) if mode in [usertypes.KeyMode.insert, usertypes.KeyMode.command, usertypes.KeyMode.caret, usertypes.KeyMode.prompt, usertypes.KeyMode.yesno, usertypes.KeyMode.passthrough]: self.set_mode_active(mode, True) @pyqtSlot(usertypes.KeyMode, usertypes.KeyMode) def on_mode_left(self, old_mode, new_mode): """Clear marked mode.""" keyparsers = objreg.get('keyparsers', scope='window', window=self._win_id) if keyparsers[old_mode].passthrough: if keyparsers[new_mode].passthrough: self._set_mode_text(new_mode.name) else: self.txt.set_text(self.txt.Text.normal, '') if old_mode in [usertypes.KeyMode.insert, usertypes.KeyMode.command, usertypes.KeyMode.caret, usertypes.KeyMode.prompt, usertypes.KeyMode.yesno, usertypes.KeyMode.passthrough]: self.set_mode_active(old_mode, False) @pyqtSlot(browsertab.AbstractTab) def on_tab_changed(self, tab): """Notify sub-widgets when the tab has been changed.""" self.url.on_tab_changed(tab) self.prog.on_tab_changed(tab) self.percentage.on_tab_changed(tab) self.backforward.on_tab_changed(tab) self.maybe_hide() assert tab.private == self._color_flags.private def resizeEvent(self, e): """Extend resizeEvent of QWidget to emit a resized signal afterwards. Args: e: The QResizeEvent. """ super().resizeEvent(e) self.resized.emit(self.geometry()) def moveEvent(self, e): """Extend moveEvent of QWidget to emit a moved signal afterwards. Args: e: The QMoveEvent. """ super().moveEvent(e) self.moved.emit(e.pos()) def minimumSizeHint(self): """Set the minimum height to the text height plus some padding.""" padding = config.val.statusbar.padding width = super().minimumSizeHint().width() height = self.fontMetrics().height() + padding.top + padding.bottom return QSize(width, height)
class StatusBar(QWidget): """The statusbar at the bottom of the mainwindow. Attributes: txt: The Text widget in the statusbar. keystring: The KeyString widget in the statusbar. percentage: The Percentage widget in the statusbar. url: The UrlText widget in the statusbar. prog: The Progress widget in the statusbar. cmd: The Command widget in the statusbar. _hbox: The main QHBoxLayout. _stack: The QStackedLayout with cmd/txt widgets. _win_id: The window ID the statusbar is associated with. Signals: resized: Emitted when the statusbar has resized, so the completion widget can adjust its size to it. arg: The new size. moved: Emitted when the statusbar has moved, so the completion widget can move to the right position. arg: The new position. """ resized = pyqtSignal('QRect') moved = pyqtSignal('QPoint') STYLESHEET = _generate_stylesheet() def __init__(self, *, win_id, private, parent=None): super().__init__(parent) self.setObjectName(self.__class__.__name__) self.setAttribute(Qt.WA_StyledBackground) stylesheet.set_register(self) self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) self._win_id = win_id self._color_flags = ColorFlags() self._color_flags.private = private self._hbox = QHBoxLayout(self) self._set_hbox_padding() self._hbox.setSpacing(5) self._stack = QStackedLayout() self._hbox.addLayout(self._stack) self._stack.setContentsMargins(0, 0, 0, 0) self.cmd = command.Command(private=private, win_id=win_id) self._stack.addWidget(self.cmd) objreg.register('status-command', self.cmd, scope='window', window=win_id) self.txt = textbase.TextBase() self._stack.addWidget(self.txt) self.cmd.show_cmd.connect(self._show_cmd_widget) self.cmd.hide_cmd.connect(self._hide_cmd_widget) self._hide_cmd_widget() self.url = url.UrlText() self.percentage = percentage.Percentage() self.backforward = backforward.Backforward() self.tabindex = tabindex.TabIndex() self.keystring = keystring.KeyString() self.prog = progress.Progress(self) self._text_widgets = [] self._draw_widgets() config.instance.changed.connect(self._on_config_changed) QTimer.singleShot(0, self.maybe_hide) def __repr__(self): return utils.get_repr(self) @pyqtSlot(str) def _on_config_changed(self, option): if option == 'statusbar.show': self.maybe_hide() elif option == 'statusbar.padding': self._set_hbox_padding() elif option == 'statusbar.widgets': self._draw_widgets() def _draw_widgets(self): """Draw statusbar widgets.""" self._clear_widgets() tab = self._current_tab() # Read the list and set widgets accordingly for segment in config.val.statusbar.widgets: if segment == 'url': self._hbox.addWidget(self.url) self.url.show() elif segment == 'scroll': self._hbox.addWidget(self.percentage) self.percentage.show() elif segment == 'scroll_raw': self._hbox.addWidget(self.percentage) self.percentage.set_raw() self.percentage.show() elif segment == 'history': self._hbox.addWidget(self.backforward) self.backforward.enabled = True if tab: self.backforward.on_tab_changed(tab) elif segment == 'tabs': self._hbox.addWidget(self.tabindex) self.tabindex.show() elif segment == 'keypress': self._hbox.addWidget(self.keystring) self.keystring.show() elif segment == 'progress': self._hbox.addWidget(self.prog) self.prog.enabled = True if tab: self.prog.on_tab_changed(tab) elif segment.startswith('text:'): cur_widget = textbase.TextBase() self._text_widgets.append(cur_widget) cur_widget.setText(segment.split(':', maxsplit=1)[1]) self._hbox.addWidget(cur_widget) cur_widget.show() else: raise utils.Unreachable(segment) def _clear_widgets(self): """Clear widgets before redrawing them.""" # Start with widgets hidden and show them when needed for widget in [ self.url, self.percentage, self.backforward, self.tabindex, self.keystring, self.prog, *self._text_widgets ]: assert isinstance(widget, QWidget) widget.hide() self._hbox.removeWidget(widget) self._text_widgets.clear() @pyqtSlot() def maybe_hide(self): """Hide the statusbar if it's configured to do so.""" strategy = config.val.statusbar.show tab = self._current_tab() if tab is not None and tab.data.fullscreen: self.hide() elif strategy == 'never': self.hide() elif strategy == 'in-mode': try: mode_manager = modeman.instance(self._win_id) except modeman.UnavailableError: self.hide() else: if mode_manager.mode == usertypes.KeyMode.normal: self.hide() else: self.show() elif strategy == 'always': self.show() else: raise utils.Unreachable def _set_hbox_padding(self): padding = config.val.statusbar.padding self._hbox.setContentsMargins(padding.left, 0, padding.right, 0) @pyqtProperty('QStringList') # type: ignore[type-var] def color_flags(self): """Getter for self.color_flags, so it can be used as Qt property.""" return self._color_flags.to_stringlist() def _current_tab(self): """Get the currently displayed tab.""" window = objreg.get('tabbed-browser', scope='window', window=self._win_id) return window.widget.currentWidget() def set_mode_active(self, mode, val): """Setter for self.{insert,command,caret}_active. Re-set the stylesheet after setting the value, so everything gets updated by Qt properly. """ if mode == usertypes.KeyMode.insert: log.statusbar.debug("Setting insert flag to {}".format(val)) self._color_flags.insert = val if mode == usertypes.KeyMode.passthrough: log.statusbar.debug("Setting passthrough flag to {}".format(val)) self._color_flags.passthrough = val if mode == usertypes.KeyMode.command: log.statusbar.debug("Setting command flag to {}".format(val)) self._color_flags.command = val elif mode in [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]: log.statusbar.debug("Setting prompt flag to {}".format(val)) self._color_flags.prompt = val elif mode == usertypes.KeyMode.caret: if not val: # Turning on is handled in on_current_caret_selection_toggled log.statusbar.debug("Setting caret mode off") self._color_flags.caret = ColorFlags.CaretMode.off stylesheet.set_register(self, update=False) def _set_mode_text(self, mode): """Set the mode text.""" if mode == 'passthrough': key_instance = config.key_instance all_bindings = key_instance.get_reverse_bindings_for('passthrough') bindings = all_bindings.get('mode-leave') if bindings: suffix = ' ({} to leave)'.format(' or '.join(bindings)) else: suffix = '' else: suffix = '' text = "-- {} MODE --{}".format(mode.upper(), suffix) self.txt.setText(text) def _show_cmd_widget(self): """Show command widget instead of temporary text.""" self._stack.setCurrentWidget(self.cmd) self.show() def _hide_cmd_widget(self): """Show temporary text instead of command widget.""" log.statusbar.debug("Hiding cmd widget") self._stack.setCurrentWidget(self.txt) self.maybe_hide() @pyqtSlot(str) def set_text(self, text): """Set a normal (persistent) text in the status bar.""" log.message.debug(text) self.txt.setText(text) @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): """Mark certain modes in the commandline.""" mode_manager = modeman.instance(self._win_id) if config.val.statusbar.show == 'in-mode': self.show() if mode_manager.parsers[mode].passthrough: self._set_mode_text(mode.name) if mode in [ usertypes.KeyMode.insert, usertypes.KeyMode.command, usertypes.KeyMode.caret, usertypes.KeyMode.prompt, usertypes.KeyMode.yesno, usertypes.KeyMode.passthrough ]: self.set_mode_active(mode, True) @pyqtSlot(usertypes.KeyMode, usertypes.KeyMode) def on_mode_left(self, old_mode, new_mode): """Clear marked mode.""" mode_manager = modeman.instance(self._win_id) if config.val.statusbar.show == 'in-mode': self.hide() if mode_manager.parsers[old_mode].passthrough: if mode_manager.parsers[new_mode].passthrough: self._set_mode_text(new_mode.name) else: self.txt.setText('') if old_mode in [ usertypes.KeyMode.insert, usertypes.KeyMode.command, usertypes.KeyMode.caret, usertypes.KeyMode.prompt, usertypes.KeyMode.yesno, usertypes.KeyMode.passthrough ]: self.set_mode_active(old_mode, False) @pyqtSlot(browsertab.AbstractTab) def on_tab_changed(self, tab): """Notify sub-widgets when the tab has been changed.""" self.url.on_tab_changed(tab) self.prog.on_tab_changed(tab) self.percentage.on_tab_changed(tab) self.backforward.on_tab_changed(tab) self.maybe_hide() assert tab.is_private == self._color_flags.private @pyqtSlot(browsertab.SelectionState) def on_caret_selection_toggled(self, selection_state): """Update the statusbar when entering/leaving caret selection mode.""" log.statusbar.debug( "Setting caret selection {}".format(selection_state)) if selection_state is browsertab.SelectionState.normal: self._set_mode_text("caret selection") self._color_flags.caret = ColorFlags.CaretMode.selection elif selection_state is browsertab.SelectionState.line: self._set_mode_text("caret line selection") self._color_flags.caret = ColorFlags.CaretMode.selection else: self._set_mode_text("caret") self._color_flags.caret = ColorFlags.CaretMode.on stylesheet.set_register(self, update=False) def resizeEvent(self, e): """Extend resizeEvent of QWidget to emit a resized signal afterwards. Args: e: The QResizeEvent. """ super().resizeEvent(e) self.resized.emit(self.geometry()) def moveEvent(self, e): """Extend moveEvent of QWidget to emit a moved signal afterwards. Args: e: The QMoveEvent. """ super().moveEvent(e) self.moved.emit(e.pos()) def minimumSizeHint(self): """Set the minimum height to the text height plus some padding.""" padding = config.cache['statusbar.padding'] width = super().minimumSizeHint().width() height = self.fontMetrics().height() + padding.top + padding.bottom return QSize(width, height)
class MusicPreviewWidget(QWidget): def __init__(self, parent=None): super(MusicPreviewWidget, self).__init__(parent) self._lastbuildtime = 10.0 self._running = None self._current = None self._chooserLabel = QLabel() self._chooser = QComboBox(self, activated=self.selectDocument) self._log = log.Log() self._view = popplerview.View() self._progress = widgets.progressbar.TimedProgressBar() self._stack = QStackedLayout() self._top = QWidget() layout = QVBoxLayout() self.setLayout(layout) layout.addWidget(self._top) layout.addLayout(self._stack) layout.addWidget(self._progress) top = QHBoxLayout() top.setContentsMargins(0, 0, 0, 0) top.setSpacing(2) self._top.setLayout(top) top.addWidget(self._chooserLabel) top.addWidget(self._chooser) top.addStretch(1) self._stack.addWidget(self._log) self._stack.addWidget(self._view) self._top.hide() app.aboutToQuit.connect(self.cleanup) app.translateUI(self) def translateUI(self): self._chooserLabel.setText(_("Document:")) def preview(self, text, title=None): """Runs LilyPond on the given text and shows the resulting PDF.""" j = self._running = MusicPreviewJob(text, title) j.done.connect(self._done) self._log.clear() self._log.connectJob(j) j.start() self._progress.start(self._lastbuildtime) def _done(self, success): self._progress.stop(False) pdfs = self._running.resultfiles() self.setDocuments(pdfs) if not pdfs: self._stack.setCurrentWidget(self._log) return self._lastbuildtime = self._running.elapsed_time() self._stack.setCurrentWidget(self._view) if self._current: self._current.cleanup() self._current = self._running # keep the tempdir self._running = None def setDocuments(self, pdfs): """Loads the given PDF path names in the UI.""" self._documents = [popplertools.Document(name) for name in pdfs] self._chooser.clear() self._chooser.addItems([d.name() for d in self._documents]) self._top.setVisible(len(self._documents) > 1) if pdfs: self._chooser.setCurrentIndex(0) self.selectDocument(0) else: self._view.clear() def selectDocument(self, index): doc = self._documents[index].document() if doc: self._view.load(doc) def cleanup(self): if self._running: self._running.abort() self._running.cleanup() self._running = None if self._current: self._current.cleanup() self._current = None self._stack.setCurrentWidget(self._log) self._top.hide() self._view.clear() def print_(self): """Prints the currently displayed document.""" if self._documents: doc = self._documents[self._chooser.currentIndex()] import popplerprint popplerprint.printDocument(doc, self)
def __init__(self, parent=None): super().__init__() self.cards = {} # type: Dict[GuiCards, QWidget] """ Python's GC will clean up QPropertyAnimations as soon as it leaves the button handler, therefore they will appear not to work. Use members to store the animations. see http://stackoverflow.com/a/6953965 """ self.slide_in_animation = None self.slide_out_animation = None self.animation_group = None self.setObjectName("coffeeFundWindow") """ Store the position and size of visible/hidden cards for the animation sequences """ self.hidden_geometry = None self.visible_geometry = None layout = QStackedLayout() layout.setStackingMode(QStackedLayout.StackAll) card_remove = RemoveCard() self.cards[GuiCards.RemoveCard] = card_remove layout.addWidget(card_remove) card_choose_action = ChooseActionCard() self.cards[GuiCards.ChooseAction] = card_choose_action layout.addWidget(card_choose_action) card_account = AccountCard() self.cards[GuiCards.AccountInfo] = card_account layout.addWidget(card_account) # keep this as last initialized card, the last card will be shown on startup! card_start = StartCard() self.cards[GuiCards.Start] = card_start layout.addWidget(card_start) self.setLayout(layout) self.setWindowTitle("Kaffeekasse") layout.setCurrentWidget(card_start) self.active_card = None card_choose_action.button_account.clicked.connect(self.signal_account) card_choose_action.button_coffee.clicked.connect(self.signal_coffee) card_account.button_back.clicked.connect(self.signal_back)