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 MainWindow(StyledMainWindow): def __init__(self): super(MainWindow, self).__init__(Strs.Main_Window_Title) # initialize all views self.__init_views() # resize to a proper one self.resize(*dim.main_window_size) def __init_views(self): # the base h-box self.__hbox_all = StyledHBox(spacing=10) # the menu list self.__lis_menu = MenuListWidget( VocAddingPage(0, Strs.Menu_List_Item_Add_Voc), PreferencesPage(1, Strs.Menu_List_Item_Preferences)) self.__hbox_all.addWidget(self.__lis_menu, 0) # the right-side v-box self.__vbox_right = StyledVBox(spacing=10) self.__hbox_all.addLayout(self.__vbox_right, 1) # the stacked-layout for containing various pages self.__page_layout = QStackedLayout() for menu_widget in self.__lis_menu.get_all_widgets(): self.__page_layout.addWidget(menu_widget.page.wrap_as_frame()) self.__vbox_right.addLayout(self.__page_layout, 1) # the label for showing the source of used icons self.__lbl_icons_attribution = StyledLabel( cfg.icons_attribution_html_code, fixed_size=FixedSizes.TINY) self.__vbox_right.addWidget(self.__lbl_icons_attribution, 0, Qt.AlignBottom | Qt.AlignRight) # set the h-box as the layout of this window self.__frame_all = QFrame() self.__frame_all.setLayout(self.__hbox_all) self.setCentralWidget(self.__frame_all) # initially set to the first page self.set_page_by_index(0) # set the page def set_page_by_index(self, index: int): self.__page_layout.setCurrentIndex(index) self.__page_layout.currentWidget().layout().set_focus()
class MainWindow(QWidget): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.actionAuswahl = QComboBox() self.actionAuswahl.addItem("Einen Sportler bearbeiten") self.actionAuswahl.addItem("Liste aller Sportler anzeigen") self.actionAuswahl.addItem("Eine Ergebnissliste bearbeiten") self.actionAuswahl.addItem("Ergebnisse & Urkunden drucken") self.actionAuswahl.addItem("Ergebnissvorlagen drucken") self.actionAuswahl.setEditable(False) self.actionAuswahl.activated.connect(self.setAction) self.actionSportlerEdit = SportlerEdit() self.actionSportlerShow = SportlerShow() self.actionErgebnissListe = ErgebnissListe() self.actionErgebnisseDrucken = ErgebnisseDrucken() self.actionErgebnissVorlage = ErgebnissVorlage() self.actionLayout = QStackedLayout() self.actionLayout.addWidget(self.actionSportlerEdit) self.actionLayout.addWidget(self.actionSportlerShow) self.actionLayout.addWidget(self.actionErgebnissListe) self.actionLayout.addWidget(self.actionErgebnisseDrucken) self.actionLayout.addWidget(self.actionErgebnissVorlage) self.mainLayout = QVBoxLayout() self.mainLayout.addWidget(self.actionAuswahl) self.mainLayout.addStretch(0) self.mainLayout.addLayout(self.actionLayout) self.mainLayout.addStretch(0) self.setLayout(self.mainLayout) self.show() def setAction(self,index): self.actionLayout.setCurrentIndex(index) self.actionLayout.currentWidget().update()
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 Tex0WidgetGroup(QWidget): def __init__(self, parent, tex0s=None, max_rows=0, max_columns=4, brres=None): super().__init__(parent) main_layout = QVBoxLayout(self) self.stack = QStackedLayout(self) self.stack_widget = QWidget(self) self.map_box = QComboBox() self.map_box.activated.connect(self.on_map_change) self.__init_context_menu() main_layout.addWidget(self.map_box) self.stack_widget.setLayout(self.stack) main_layout.addWidget(self.stack_widget) self.subscriber = parent if tex0s is not None: self.set_tex0s(tex0s) if brres is None: self.brres = tex0s[0].parent if brres: self.brres = brres self.setLayout(main_layout) def __init_context_menu(self): self.setContextMenuPolicy(Qt.ActionsContextMenu) create_action = QAction('&Add Map', self) create_action.setToolTip('Add new map') create_action.triggered.connect(self.create_map) self.addAction(create_action) replace_action = QAction('&Replace', self) replace_action.setToolTip('Replace map') replace_action.triggered.connect(self.replace_map) self.addAction(replace_action) export_action = QAction('&Export', self) export_action.setToolTip('Export as png') export_action.triggered.connect(self.export) self.addAction(export_action) remove_action = QAction('&Delete', self) remove_action.setToolTip('Remove the map') remove_action.triggered.connect(self.remove) self.addAction(remove_action) def export(self): self.stack.currentWidget().export() def remove(self): self.remove_map_widget(self.stack.currentWidget()) def replace_map(self): self.stack.currentWidget().replace_map() def get_tex0(self, index): return self.stack.itemAt(index).tex0 def on_map_replace(self, tex): if self.subscriber is not None: self.subscriber.on_map_replace(tex, self.stack.currentIndex()) def on_map_change(self, index): self.stack.setCurrentIndex(index) if self.subscriber is not None: self.subscriber.on_map_change(self.stack.currentWidget().tex0, index) def reset(self): for i in reversed(range(self.stack.count())): widget = self.stack.itemAt(i).widget() widget.del_widget() self.map_box.removeItem(i) def set_brres(self, brres): self.brres = brres def set_tex0s(self, tex0s): self.reset() for x in tex0s: self.add_tex0(x) def add_tex0(self, x): widget = MapWidget(self, x) self.add_map_widget(widget) def add_map_widget(self, map_widget): self.stack.addWidget(map_widget) self.map_box.addItem(map_widget.name) def remove_map_widget(self, map_widget): tex0 = map_widget.tex0 index = self.stack.currentIndex() if self.subscriber is not None: self.subscriber.on_map_remove(tex0, index) def create_map(self): self.importer = MapImporter(self, self.brres) def on_import(self, tex0): index = self.stack.count() if self.subscriber: self.subscriber.on_map_add(tex0, index) self.importer = None
class ComboEditor(QDialog): closeSplit = pyqtSignal(QWidget) aboutToCloseComboEditor = pyqtSignal() allFilesClosed = pyqtSignal() splitEditor = pyqtSignal("QWidget*", "QWidget*", bool) recentTabsModified = pyqtSignal() class NAVIGATE: prev = 0 next = 1 Q_ENUMS(NAVIGATE) def __init__(self, original=False, Force_Free=False): super(ComboEditor, self).__init__(None) #, Qt.WindowStaysOnTopHint) self.__original = original self.Force_Free = Force_Free self.__undocked = [] self._single_undocked = [] self._symbols_index = [] self.__OFiles = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.bar = ActionBar(self, main_combo=original) vbox.addWidget(self.bar) self.stackedEditor = QStackedLayout() vbox.addLayout(self.stackedEditor) self._main_container = IDE.get_service('main_container') if not self.__original and not self.Force_Free: self._main_container.fileOpened.connect(self._file_opened_by_main) # QApplication.instance().focusChanged["QWidget*", "QWidget*"].connect(\ # lambda w1, w2: QTimer.singleShot(10, lambda w1=w1, w2=w2: print("\n\nQApplication::focusChanged::", w1, w2))) self.bar.combo.showComboSelector.connect( self._main_container.change_tab) self.bar.changeCurrent.connect(self._set_current) self.bar.editorSplited.connect(self.split_editor) self.bar.runFile[str].connect(self._run_file) self.bar.closeFile.connect(lambda: self.closeSplit.emit(self)) self.bar.addToProject[str].connect(self._add_to_project) self.bar.showFileInExplorer.connect(self._show_file_in_explorer) self.bar.goToSymbol.connect(self._go_to_symbol) self.bar.undockEditor.connect(self.undock_editor) self.bar.undockThisEditor.connect(self.single_undock_editor) self.bar.reopenTab[str].connect(self._main_container.open_file) self.bar.recentTabsModified.connect( self._main_container.recent_files_changed) self.bar.code_navigator.btnPrevious.clicked['bool'].connect( lambda: self._navigate_code(self.NAVIGATE.prev)) self.bar.code_navigator.btnNext.clicked['bool'].connect( lambda: self._navigate_code(self.NAVIGATE.prev)) # QTimer.singleShot(39999, lambda : print("\n\ncombo:-:", self)) # def closeEvent(self, event): # for comboeditor in self._single_undocked: # print("has undocked", comboeditor) # comboeditor.reject() # comboeditor.deleteLater() # self.bar._close_all_files() # super(ComboEditor, self).closeEvent(event) def _navigate_code(self, val): op = self.bar.code_navigator.operation self._main_container.navigate_code_history(val, op) def setFocus(self): super(ComboEditor, self).setFocus() w = self.stackedEditor.currentWidget() if w: w.setFocus() self._editor_with_focus() def _file_opened_by_main(self, path): index = self.stackedEditor.currentIndex() ninjaide = IDE.getInstance() editable = ninjaide.get_or_create_editable(path) print("_file_opened_by_main", editable) self.add_editor(editable) self.bar.set_current_by_index(index) if index == -1: self.bar.set_current_by_index(0) def add_editor(self, neditable, keep_index=False): """Add Editor Widget to the UI area.""" if neditable.editor: if self.__original or self.Force_Free: editor = neditable.editor print("\n\nadd_editor() ignora por ahora!", editor) # disconnect old Signals try: editor.cursorPositionChanged[int, int].disconnect() except TypeError: pass try: editor.editorFocusObtained.disconnect() except TypeError: pass try: editor.currentLineChanged.disconnect() except TypeError: pass try: editor.modificationChanged['bool'].disconnect() except TypeError: pass try: neditable.checkersUpdated.disconnect() except TypeError: pass try: neditable.fileSaved.disconnect() except TypeError: pass # Disonnect file system signals only in the original try: neditable.fileClosing.disconnect() except TypeError: pass if self.__original: try: neditable.askForSaveFileClosing.disconnect() except TypeError: pass try: neditable.fileChanged.disconnect() except TypeError: pass else: editor = self._main_container.create_text_editor_from_editable( neditable) index = self.stackedEditor.currentIndex() self.stackedEditor.addWidget(editor) self.bar.add_item(neditable.display_name, neditable) if keep_index: self.bar.set_current_by_index(index) # Editor Signals editor.cursorPositionChanged[int, int].connect( self._update_cursor_position) editor.editorFocusObtained.connect(self._editor_with_focus) editor.currentLineChanged.connect(self._set_current_symbol) editor.modificationChanged['bool'].connect(self._editor_modified) neditable.checkersUpdated.connect(self._show_notification_icon) neditable.fileSaved.connect(self._update_symbols) neditable.fileSaved.connect(self._update_combo_info) # Connect file system signals only in the original neditable.fileClosing.connect(self._close_file) if self.__original: neditable.askForSaveFileClosing.connect(self._ask_for_save) neditable.fileChanged.connect(self._file_has_been_modified) # Load Symbols self._load_symbols(neditable) def show_combo_file(self): print("show_combo_file") self.bar.combo.showPopup() def show_combo_symbol(self): self.bar.symbols_combo.showPopup() def getOpenedFiles(self): return self.__OFiles.copy() def addOpenedFiles(self, fil): self.__OFiles.append(fil) def unlink_editors(self): for index in range(self.stackedEditor.count()): widget = self.stackedEditor.widget(index) widget.setDocument(QsciDocument()) def split_editor(self, orientationVertical): new_widget = ComboEditor() for neditable in self.bar.get_editables(): print("\nsplit_editor", neditable, new_widget) new_widget.add_editor(neditable) self.splitEditor.emit(self, new_widget, orientationVertical) def undock_editor(self): new_combo = ComboEditor() new_combo.setWindowTitle("NINJA-IDE") self.add_Undocked(new_combo) for neditable in self.bar.get_editables(): print("undock_editor", neditable) new_combo.add_editor(neditable) new_combo.resize(500, 500) new_combo.aboutToCloseComboEditor.connect(self._remove_undock) new_combo.show() def _remove_undock(self): print("_remove_undock", self.sender()) widget = self.sender() self.sub_Undocked(widget) def add_Undocked(self, combedit): self.__undocked.append(combedit) def sub_Undocked(self, combedit): self.__undocked.remove(combedit) def add_SingleUndocked(self, combedit): self._single_undocked.append(combedit) def sub_SingleUndocked(self, combedit): self._single_undocked.remove(combedit) # aún no se ha puesto en funcionamiento!. def single_split_editor(self, orientationVertical): new_widget = ComboEditor() for neditable in self.bar.get_editables(): print("\nsingle_split_editor", neditable, new_widget) new_widget.add_editor(neditable) self.splitEditor.emit(self, new_widget, orientationVertical) def single_undock_editor(self): new_combo = ComboEditor(Force_Free=True) new_combo.setWindowTitle("NINJA-IDE") self.add_SingleUndocked(new_combo) nEdit = self.stackedEditor.takeAt( self.stackedEditor.currentIndex()).widget() ide = IDE.getInstance() ide.unload_NEditable(nEdit) neditable = self.bar.take_editable() print("\n\nsingle_undock_editor:::", neditable, neditable.editor, nEdit, neditable.nfile) if self.stackedEditor.isEmpty(): self.allFilesClosed.emit() new_combo.add_editor(neditable) new_combo.stackedEditor.setCurrentIndex( 0) # new_combo.stackedEditor.setCurrentWidget(nEdit) new_combo.resize(500, 500) new_combo.aboutToCloseComboEditor.connect(self.single__remove_undock) new_combo.show() def single__remove_undock(self): print("single__remove_undock", self.sender()) widget = self.sender() self.sub_SingleUndocked(widget) ##widget.deleteLater() def bind_Editable(self, editable): self.bar.add_item(neditable.display_name, editable) def close_current_file(self): self.bar.about_to_close_file() def _close_file(self, neditable): print("\n\n_close_file", self.__original, self.Force_Free) index = self.bar.close_file(neditable) layoutItem = self.stackedEditor.takeAt(index) #neditable.editor.completer.cc.unload_module() self._add_to_last_opened(neditable.file_path) layoutItem.widget().deleteLater( ) # @@4 -> Comentando ésta linea desaparece el mensaje if self.stackedEditor.isEmpty(): self.allFilesClosed.emit() def _add_to_last_opened(self, path): if path not in settings.LAST_OPENED_FILES: settings.LAST_OPENED_FILES.append(path) if len(settings.LAST_OPENED_FILES) > settings.MAX_REMEMBER_TABS: self.__lastOpened = self.__lastOpened[1:] self.recentTabsModified.emit() def _editor_with_focus(self): if self._main_container.current_comboEditor is not self: self._main_container.current_comboEditor = self editor = self.stackedEditor.currentWidget() self._main_container.current_editor_changed( editor.neditable.file_path) self._load_symbols(editor.neditable) editor.neditable.update_checkers_display() def _ask_for_save(self, neditable): val = QMessageBox.No fileName = neditable.nfile.file_name val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.No: neditable.nfile.close(force_close=True) elif val == QMessageBox.Yes: neditable.ignore_checkers = True self._main_container.save_file(neditable.editor) neditable.nfile.close() def _file_has_been_modified(self, neditable): val = QMessageBox.No fileName = neditable.file_path val = QMessageBox.question( self, translations.TR_FILE_HAS_BEEN_MODIFIED, "%s%s" % (fileName, translations.TR_FILE_MODIFIED_OUTSIDE), QMessageBox.Yes | QMessageBox.No) if val == QMessageBox.Yes: neditable.reload_file() def _run_file(self, path): self._main_container.run_file(path) def _add_to_project(self, path): self._main_container._add_to_project(path) def _show_file_in_explorer(self, path): '''Connected to ActionBar's showFileInExplorer(QString) signal, forwards the file path on to the main container.''' self._main_container._show_file_in_explorer(path) def set_current(self, neditable): if neditable: self.bar.set_current_file(neditable) def _set_current(self, neditable, index): if neditable: self.stackedEditor.setCurrentIndex(index) editor = self.stackedEditor.currentWidget() self._update_cursor_position(ignore_sender=True) editor.setFocus() self._main_container.current_editor_changed(neditable.file_path) self._load_symbols(neditable) self._show_file_in_explorer(neditable.file_path) neditable.update_checkers_display() def widget(self, index): return self.stackedEditor.widget(index) def countEditors(self): """Return the number of editors opened.""" return self.stackedEditor.count() def currentEditor(self): """ rtn -> Editor()# local QsciScintilla""" return self.stackedEditor.currentWidget() def _update_cursor_position(self, line=0, col=0, ignore_sender=False): obj = self.sender() editor = self.stackedEditor.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: line += 1 self.bar.update_line_col(line, col) def _set_current_symbol(self, line, ignore_sender=False): obj = self.sender() editor = self.stackedEditor.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: index = bisect.bisect(self._symbols_index, line) if (index >= len(self._symbols_index) or self._symbols_index[index] > (line + 1)): index -= 1 self.bar.set_current_symbol(index) def _editor_modified(self, value): obj = self.sender() neditable = obj.neditable if value: text = "\u2022 %s" % neditable.display_name self.bar.update_item_text(neditable, text) else: self.bar.update_item_text(neditable, neditable.display_name) def _go_to_symbol(self, index): print("_go_to_symbol in index:", index) line = self._symbols_index[index] editor = self.stackedEditor.currentWidget() editor.go_to_line(line) def _update_symbols(self, neditable): editor = self.stackedEditor.currentWidget() # Check if it's current to avoid signals from other splits. if editor == neditable.editor: self._load_symbols(neditable) def _update_combo_info(self, neditable): self.bar.update_item_text(neditable, neditable.display_name) self._main_container.current_editor_changed(neditable.file_path) def _load_symbols(self, neditable): symbols_handler = handlers.get_symbols_handler('py') source = neditable.editor.text() source = source.encode(neditable.editor.encoding) symbols, symbols_simplified = symbols_handler.obtain_symbols( source, simple=True) self._symbols_index = sorted(symbols_simplified.keys()) symbols_simplified = sorted(list(symbols_simplified.items()), key=lambda x: x[0]) self.bar.add_symbols(symbols_simplified) line, _ = neditable.editor.getCursorPosition() self._set_current_symbol(line, True) tree_symbols = IDE.get_service('symbols_explorer') tree_symbols.update_symbols_tree(symbols, neditable.file_path) def _show_notification_icon(self, neditable): checkers = neditable.sorted_checkers icon = QIcon() for items in checkers: checker, color, _ = items if checker.checks: if isinstance(checker.checker_icon, int): icon = self.style().standardIcon(checker.checker_icon) elif isinstance(checker.checker_icon, str): icon = QIcon(checker.checker_icon) break self.bar.update_item_icon(neditable, icon) def show_menu_navigation(self): self.bar.code_navigator.show_menu_navigation() def closeEvent(self, event): for comboeditor in self._single_undocked: print("has undocked", comboeditor) comboeditor.reject() comboeditor.deleteLater() # print("\n\ncloseEvent", self) if self.__original: self.bar._close_all_files() self.aboutToCloseComboEditor.emit() super(ComboEditor, self).closeEvent(event) def reject(self): if not self.__original: super(ComboEditor, self).reject()
class 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 ComboEditor(ui_tools.StyledBar): # Signals closeSplit = pyqtSignal('PyQt_PyObject') splitEditor = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject', bool) allFilesClosed = pyqtSignal() about_to_close_combo_editor = pyqtSignal() def __init__(self, original=False): super(ComboEditor, self).__init__(None) self.__original = original self.__undocked = [] self._symbols_index = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.bar = ActionBar(main_combo=original) vbox.addWidget(self.bar) # Info bar self.info_bar = InfoBar(self) self.info_bar.setVisible(False) vbox.addWidget(self.info_bar) self.stacked = QStackedLayout() vbox.addLayout(self.stacked) self._main_container = IDE.get_service('main_container') if not self.__original: self._main_container.fileOpened['QString'].connect( self._file_opened_by_main) # self.bar.combo.showComboSelector.connect( # lambda: self._main_container.change_tab()) self.bar.change_current['PyQt_PyObject', int].connect(self._set_current) self.bar.splitEditor[bool].connect(self.split_editor) self.bar.runFile['QString'].connect(self._run_file) self.bar.closeSplit.connect(lambda: self.closeSplit.emit(self)) self.bar.addToProject['QString'].connect(self._add_to_project) self.bar.showFileInExplorer['QString'].connect( self._show_file_in_explorer) self.bar.goToSymbol[int].connect(self._go_to_symbol) self.bar.undockEditor.connect(self.undock_editor) self.bar.reopenTab['QString'].connect( lambda path: self._main_container.open_file(path)) # self.connect(self.bar, SIGNAL("recentTabsModified()"), # lambda: self._main_container.recent_files_changed()) # self.connect(self.bar.code_navigator.btnPrevious, SIGNAL("clicked()"), # lambda: self._navigate_code(False)) # self.connect(self.bar.code_navigator.btnNext, SIGNAL("clicked()"), # lambda: self._navigate_code(True)) # def _navigate_code(self, val): # op = self.bar.code_navigator.operation # self._main_container.navigate_code_history(val, op) def currentWidget(self): return self.stacked.currentWidget() def setFocus(self): super(ComboEditor, self).setFocus() self.stacked.currentWidget().setFocus() self._editor_with_focus() def _file_opened_by_main(self, path): index = self.stacked.currentIndex() ninjaide = IDE.get_service('ide') editable = ninjaide.get_or_create_editable(path) self.add_editor(editable) self.bar.set_current_by_index(index) if index == -1: self.bar.set_current_by_index(0) def add_editor(self, neditable, keep_index=False): """Add Editor Widget to the UI area.""" if neditable.editor: if self.__original: editor = neditable.editor else: # editor = neditable.editor.clone() editor = self._main_container.create_editor_from_editable( neditable) current_index = self.stacked.currentIndex() new_index = self.stacked.addWidget(editor) self.stacked.setCurrentIndex(new_index) self.bar.add_item(neditable.display_name, neditable) # Bar is not visible because all the files have been closed, # so if a new file is opened, show the bar if not self.bar.isVisible(): self.bar.setVisible(True) if keep_index: self.bar.set_current_by_index(current_index) # Connections neditable.fileClosing.connect(self._close_file) editor.editorFocusObtained.connect(self._editor_with_focus) editor.modificationChanged.connect(self._editor_modified) neditable.checkersUpdated.connect(self._show_notification_icon) # Connect file system signals only in the original if self.__original: neditable.askForSaveFileClosing.connect(self._ask_for_save) neditable.fileChanged.connect(self._file_has_been_modified) # Editor Signals editor.cursor_position_changed[int, int].connect( self._update_cursor_position) editor.current_line_changed[int].connect(self._set_current_symbol) """ # self.connect(editor, SIGNAL("editorFocusObtained()"), # self._editor_with_focus) editor.editorFocusObtained.connect(self._editor_with_focus) neditable.fileSaved['PyQt_PyObject'].connect( self._update_combo_info) neditable.fileSaved['PyQt_PyObject'].connect( self._update_symbols) editor.modificationChanged[bool].connect(self._editor_modified) neditable.checkersUpdated.connect(self._show_notification_icon) # Connect file system signals only in the original neditable.fileClosing['PyQt_PyObject'].connect(self._close_file) if self.__original: neditable.askForSaveFileClosing['PyQt_PyObject'].connect( self._ask_for_save) neditable.fileChanged['PyQt_PyObject'].connect( self._file_has_been_modified) self.info_bar.reloadClicked.connect(neditable.reload_file) # Load Symbols self._load_symbols(neditable) """ def show_combo_file(self): self.bar.combo.showPopup() def show_combo_symbol(self): self.bar.symbols_combo.showPopup() def unlink_editors(self): for index in range(self.stacked.count()): widget = self.stacked.widget(index) # widget.setDocument(QsciDocument()) def split_editor(self, orientation_vertical): new_widget = ComboEditor() for neditable in self.bar.get_editables(): new_widget.add_editor(neditable) self.splitEditor.emit(self, new_widget, orientation_vertical) # for neditable in self.bar.get_editables(): # new_widget.add_editor(neditable) # self.splitEditor.emit(self, new_widget, orientationVertical) # self.emit(SIGNAL("splitEditor(PyQt_PyObject, PyQt_PyObject, bool)"), # self, new_widget, orientationVertical) def undock_editor(self): new_combo = ComboEditor() new_combo.setWindowTitle("NINJA-IDE") editor = self.currentWidget() new_combo.add_editor(editor.neditable) new_combo.show() """ for neditable in self.bar.get_editables(): new_combo.add_editor(neditable) neditor = self.currentWidget().clone() new_combo.set_current(neditor.neditable) new_combo.resize(700, 500) new_combo.about_to_close_combo_editor.connect(self._remove_undock) new_combo.show() """ def _remove_undock(self): widget = self.sender() self.__undocked.remove(widget) def close_current_file(self): self.bar.about_to_close_file() def _close_file(self, neditable): index = self.bar.close_file(neditable) print(index) layoutItem = self.stacked.takeAt(index) # neditable.editor.completer.cc.unload_module() self._add_to_last_opened(neditable.file_path) layoutItem.widget().deleteLater() if self.stacked.isEmpty(): self.bar.hide() self.allFilesClosed.emit() def _add_to_last_opened(self, path): if path not in settings.LAST_OPENED_FILES: settings.LAST_OPENED_FILES.append(path) if len(settings.LAST_OPENED_FILES) > settings.MAX_REMEMBER_TABS: self.__lastOpened = self.__lastOpened[1:] print("RecentTabsModified") # self.emit(SIGNAL("recentTabsModified()")) def _editor_with_focus(self): if self._main_container.current_widget is not self: self._main_container.current_widget = self editor = self.stacked.currentWidget() self._main_container.current_editor_changed( editor.neditable.file_path) self._load_symbols(editor.neditable) editor.neditable.update_checkers_display() def _ask_for_save(self, neditable): val = QMessageBox.No fileName = neditable.nfile.file_name val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.No: neditable.nfile.close(force_close=True) elif val == QMessageBox.Yes: neditable.ignore_checkers = True self._main_container.save_file(neditable.editor) neditable.nfile.close() def _file_has_been_modified(self, neditable): self.info_bar.show_message(translations.TR_FILE_MODIFIED_OUTSIDE) # val = QMessageBox.No # fileName = neditable.file_path # val = QMessageBox.question( # self, translations.TR_FILE_HAS_BEEN_MODIFIED, # "%s%s" % (fileName, translations.TR_FILE_MODIFIED_OUTSIDE), # QMessageBox.Yes | QMessageBox.No) # if val == QMessageBox.Yes: # neditable.reload_file() def _run_file(self, path): self._main_container.run_file(path) def _add_to_project(self, path): self._main_container._add_to_project(path) def _show_file_in_explorer(self, path): '''Connected to ActionBar's showFileInExplorer(QString) signal, forwards the file path on to the main container.''' self._main_container._show_file_in_explorer(path) def set_current(self, neditable): if neditable: self.bar.set_current_file(neditable) def _set_current(self, neditable, index): if neditable: self.stacked.setCurrentIndex(index) editor = self.stacked.currentWidget() self._update_cursor_position(ignore_sender=True) editor.setFocus() self._main_container.current_editor_changed(neditable.file_path) self._load_symbols(neditable) # self._show_file_in_explorer(neditable.file_path) neditable.update_checkers_display() def widget(self, index): return self.stacked.widget(index) def count(self): """Return the number of editors opened.""" return self.stacked.count() def _update_cursor_position(self, line=0, col=0, ignore_sender=False): obj = self.sender() editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: line += 1 self.bar.update_line_col(line, col) def _set_current_symbol(self, line, ignore_sender=False): obj = self.sender() editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: index = bisect.bisect(self._symbols_index, line) if (index >= len(self._symbols_index) or self._symbols_index[index] > (line + 1)): index -= 1 self.bar.set_current_symbol(index) def _editor_modified(self, value): obj = self.sender() neditable = obj.neditable if value: text = "\u2022 %s" % neditable.display_name self.bar.update_item_text(neditable, text) else: self.bar.update_item_text(neditable, neditable.display_name) def _go_to_symbol(self, index): # FIXME: index 0 invalid line = self._symbols_index[index] editor = self.stacked.currentWidget() editor.go_to_line(line) def _update_symbols(self, neditable): editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if editor == neditable.editor: self._load_symbols(neditable) def _update_combo_info(self, neditable): self.bar.update_item_text(neditable, neditable.display_name) self._main_container.current_editor_changed(neditable.file_path) def _load_symbols(self, neditable): symbols_handler = handlers.get_symbols_handler('py') source = neditable.editor.text source = source.encode(neditable.editor.encoding) symbols, symbols_simplified = symbols_handler.obtain_symbols( source, simple=True) self._symbols_index = sorted(symbols_simplified.keys()) symbols_simplified = sorted(list(symbols_simplified.items()), key=lambda x: x[0]) self.bar.add_symbols(symbols_simplified) line, _ = neditable.editor.cursor_position self._set_current_symbol(line, True) tree_symbols = IDE.get_service('symbols_explorer') tree_symbols.update_symbols_tree(symbols, neditable.file_path) def _show_notification_icon(self, neditable): checkers = neditable.sorted_checkers icon = QIcon() for items in checkers: checker, color, _ = items if checker.checks: if isinstance(checker.checker_icon, int): icon = self.style().standardIcon(checker.checker_icon) elif isinstance(checker.checker_icon, str): icon = QIcon(checker.checker_icon) # FIXME: sucks else: icon = QIcon(checker.checker_icon) break self.bar.update_item_icon(neditable, icon) def show_menu_navigation(self): self.bar.code_navigator.show_menu_navigation() def closeEvent(self, event): self.about_to_close_combo_editor.emit() # self.emit(SIGNAL("aboutToCloseComboEditor()")) super(ComboEditor, self).closeEvent(event) def reject(self): if not self.__original: super(ComboEditor, self).reject()
class SolveEquationWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self.start = True self.resize(300, 200) self.layout = QStackedLayout() self.start_widget = QWidget() self.start_layout = QVBoxLayout() self._21equation = QPushButton('2 variables linear equation') self._21equation.clicked.connect( partial(self.create_solving_widget, 2, 3, func='line')) self._21equation.setMinimumHeight(80) change_font(self._21equation) self._31equation = QPushButton('3 variables linear equation') self._31equation.clicked.connect( partial(self.create_solving_widget, 3, 4, func='line')) self._31equation.setMinimumHeight(80) change_font(self._31equation) self._12equation = QPushButton('1 variables quadratic equation') self._12equation.clicked.connect( partial(self.create_solving_widget, 1, 3, func='quad')) self._12equation.setMinimumHeight(80) change_font(self._12equation) self.start_layout.addWidget(self._21equation) self.start_layout.addWidget(self._31equation) self.start_layout.addWidget(self._12equation) self.start_widget.setLayout(self.start_layout) self.layout.addWidget(self.start_widget) self.setLayout(self.layout) def create_solving_widget(self, row, column, func): self.func = func self.start = False self.solving_widget = QWidget() self.solving_layout = QVBoxLayout() return_button = Button('Return') return_button.clicked.connect(self.return_to_start_widget) self.solving_layout.addWidget(return_button) self.entries = [] for i in range(row): self.entries.append(QHBoxLayout()) for j in range(column): self.entries[i].addWidget(QLineEdit()) self.solving_layout.addLayout(self.entries[i]) result_hbox = QHBoxLayout() result_label = QLabel('Result') result_hbox.addWidget(result_label) self.result_num = [] if func == 'quad': row = 2 for i in range(row): self.result_num.append(QLabel()) result_hbox.addWidget(self.result_num[i]) self.solving_layout.addLayout(result_hbox) ok_button = Button('OK') ok_button.clicked.connect(self.solve) self.solving_layout.addWidget(ok_button) self.solving_widget.setLayout(self.solving_layout) self.layout.addWidget(self.solving_widget) self.layout.setCurrentIndex(1) def return_to_start_widget(self): self.start = True clear_layout(self.solving_layout) del self.solving_widget self.layout.removeWidget(self.layout.currentWidget()) self.layout.setCurrentIndex(0) def solve(self): para = [] for hbox in self.entries: for i in range(hbox.count()): para.append(evaluator.parse(hbox.itemAt(i).widget().text())) if self.func == 'line': result = linear_solver(*para) elif self.func == 'quad': result = quad_solver(*para) if result: for i in range(len(self.result_num)): self.result_num[i].setText(str(round(result[i], 11))) else: self.result_num[0].setText("No Answer") def keyPressEvent(self, event): if not self.start: if event.key() == Qt.Key_Enter: self.solve() @staticmethod def run(parent=None, position=None): w = SolveEquationWidget(parent) if position is not None: w.move(position) w.show()
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() # Call the inherited classes __init__ method self.core = Core.Core.get_core() def loadGui(self): ui_file = self.core.rsrc_dir + "mainWindow.ui" uic.loadUi(ui_file, self) # Load the .ui file self.optionsBox = self.findChild(QHBoxLayout, 'optionsBox') self.installersButton = self.findChild(QPushButton, 'installersButton') icn = QIcon.fromTheme( os.path.join(settings.ICONS_THEME, "view-list-details.svg")) self.installersButton.setIcon(icn) self.installersButton.setText(_("Installers")) self.installersButton.clicked.connect(lambda: self.changePanel("I")) self.configurationButton = self.findChild(QPushButton, "configurationButton") icn = QIcon.fromTheme( os.path.join(settings.ICONS_THEME, "configure.svg")) self.configurationButton.setIcon(icn) #self.configurationButton.setIconSize(QSize(16,16)) self.configurationButton.setText(_("Configuration Options")) self.configurationButton.clicked.connect(lambda: self.changePanel("C")) self.mainBox = self.findChild(QVBoxLayout, 'mainBox') self.bannerBox = self.findChild(QLabel, 'bannerLabel') self.bannerBox.setStyleSheet("background-color: #7f0907") self.messageBox = self.findChild(QVBoxLayout, 'messageBox') self.messageLabel = self.findChild(QLabel, 'messageLabel') self.controlsBox = self.findChild(QVBoxLayout, 'controlsBox') self.applyButton = self.findChild(QPushButton, 'applyButton') icn = QIcon.fromTheme(os.path.join(settings.ICONS_THEME, "gtk-ok.svg")) self.applyButton.setIcon(icn) self.applyButton.setText(_("Install")) self.applyButton.clicked.connect(self.applyButtonClicked) self.helpButton = self.findChild(QPushButton, 'helpButton') icn = QIcon.fromTheme( os.path.join(settings.ICONS_THEME, "help-whatsthis.svg")) self.helpButton.setIcon(icn) self.helpButton.setText(_("Help")) self.helpButton.clicked.connect(self.helpButtonClicked) self.loadingBox = self.core.loadingBox self.installersBox = self.core.installersBox self.configurationBox = self.core.configurationBox self.QtStack = QStackedLayout() self.QtStack.addWidget(self.loadingBox) self.QtStack.addWidget(self.installersBox) self.QtStack.addWidget(self.configurationBox) self.mainBox.addLayout(self.QtStack) self.gatherInfo = gatherInfo() self.installersButton.hide() self.configurationButton.hide() self.messageLabel.hide() self.applyButton.hide() qtRectangle = self.frameGeometry() centerPoint = QDesktopWidget().availableGeometry().center() qtRectangle.moveCenter(centerPoint) self.move(qtRectangle.topLeft()) centerPoint = QDesktopWidget().availableGeometry().center() qtRectangle.moveCenter(centerPoint) self.move(qtRectangle.topLeft()) self.gatherInfo.start() self.gatherInfo.finished.connect(self._finishProcess) #def loadGui def _finishProcess(self): self.loadingBox.spinner.stop() if len(self.core.javaPanelManager.java_list) > 0: self.installersBox.drawInstallerList() self.fader_widget = FaderWidget(self.QtStack.currentWidget(), self.QtStack.widget(1)) self.QtStack.setCurrentIndex(1) self.configurationButton.show() self.messageLabel.show() self.applyButton.show() else: self.messageLabel.show() self.loadingBox.spinner.hide() self.messageLabel.setText( _("No Java version(s) availables detected")) #def _finishProcess def applyButtonClicked(self): self.othersBox = [] self.javasToInstall = self.installersBox.javas_selected self.boxSelected = self.installersBox.box_selected #self.installersBox.scrollArea.setEnabled(False) self.messageLabel.setText("") if len(self.javasToInstall) > 0: self.applyButton.setEnabled(False) self.messageLabel.setText( _("Installing selected Java version(s). Wait a moment...")) for item in self.boxSelected: item.itemAt(0).widget().setEnabled(False) item.itemAt(3).widget().hide() item.itemAt(4).widget().show() item.itemAt(4).widget().start() for item in self.installersBox.boxInstallers.children(): if item.itemAt(0).widget().isEnabled(): item.itemAt(0).widget().setEnabled(False) self.othersBox.append(item) self.install = installProcess(self.javasToInstall) self.install.start() self.install.finished.connect(self._finishInstall) else: self.messageLabel.setText( _("You must select a Java version to install")) #def applyButtonClicked def _finishInstall(self): result = self.core.javaPanelManager.result_install error = False for item in result: if result[item]: for element in self.boxSelected: if element.itemAt(4).widget().item == item: element.itemAt(4).widget().stop() element.itemAt(4).widget().hide() pixmap = QPixmap(self.core.rsrc_dir + "check.png") element.itemAt(3).widget().setPixmap(pixmap) element.itemAt(0).widget().setChecked(False) element.itemAt(3).widget().show() else: for element in self.boxSelected: if element.itemAt(4).widget().item == item: element.itemAt(4).widget().stop() element.itemAt(4).widget().hide() pixmap = QPixmap(self.core.rsrc_dir + "error.png") element.itemAt(3).widget().setPixmap(pixmap) element.itemAt(0).widget().setChecked(False) element.itemAt(0).widget().setEnabled(True) element.itemAt(3).widget().show() error = True self.applyButton.setEnabled(True) for item in self.othersBox: item.itemAt(0).widget().setEnabled(True) if error: self.messageLabel.setText( _("Installing process has ending with errors")) else: self.messageLabel.setText( _("Installing process has ending successfully")) #def _finishInstall def changePanel(self, panel): if panel == "C": self.configurationButton.hide() self.messageLabel.setText("") self.configurationBox.drawConfigurationList() self.fader_widget = FaderWidget(self.QtStack.currentWidget(), self.QtStack.widget(2)) self.QtStack.setCurrentIndex(2) self.installersButton.show() self.applyButton.setEnabled(False) elif panel == "I": self.installersButton.hide() self.messageLabel.setText("") self.fader_widget = FaderWidget(self.QtStack.currentWidget(), self.QtStack.widget(2)) self.QtStack.setCurrentIndex(1) self.configurationButton.show() self.applyButton.setEnabled(True) self.configurationBox.boxDelete() #def changePanel def helpButtonClicked(self): lang = os.environ["LANG"] run_pkexec = False if "PKEXEC_UID" in os.environ: run_pkexec = True if 'ca_ES' in lang: cmd = 'xdg-open https://wiki.edu.gva.es/lliurex/tiki-index.php?page=LliureX-Java-Panel.' else: cmd = 'xdg-open https://wiki.edu.gva.es/lliurex/tiki-index.php?page=LliureX-Java-Panel' if not run_pkexec: self.fcmd = "su -c '%s' $USER" % cmd else: user = pwd.getpwuid(int(os.environ["PKEXEC_UID"])).pw_name self.fcmd = "su -c '" + cmd + "' " + user os.system(self.fcmd)
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() """ ############################################################################### 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 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 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 ComboEditor(QWidget): # Signals closeSplit = pyqtSignal('PyQt_PyObject') splitEditor = pyqtSignal( 'PyQt_PyObject', 'PyQt_PyObject', Qt.Orientation) allFilesClosed = pyqtSignal() about_to_close_combo_editor = pyqtSignal() fileClosed = pyqtSignal("PyQt_PyObject") def __init__(self, original=False): super(ComboEditor, self).__init__(None) self.__original = original self.__undocked = [] self._symbols_index = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.bar = ActionBar(main_combo=original) vbox.addWidget(self.bar) # Info bar # self.info_bar = InfoBar(self) # self.info_bar.setVisible(False) # vbox.addWidget(self.info_bar) self.stacked = QStackedLayout() vbox.addLayout(self.stacked) self._main_container = IDE.get_service('main_container') if not self.__original: self._main_container.fileOpened['QString'].connect( self._file_opened_by_main) self.bar.combo_files.showComboSelector.connect( self._main_container.show_files_handler) self.bar.combo_files.hideComboSelector.connect( self._main_container.hide_files_handler) self.bar.change_current['PyQt_PyObject', int].connect(self._set_current) self.bar.splitEditor[bool].connect(self.split_editor) self.bar.runFile['QString'].connect(self._run_file) self.bar.closeSplit.connect(lambda: self.closeSplit.emit(self)) self.bar.addToProject['QString'].connect(self._add_to_project) self.bar.showFileInExplorer['QString'].connect( self._show_file_in_explorer) self.bar.goToSymbol[int].connect(self._go_to_symbol) self.bar.undockEditor.connect(self.undock_editor) self.bar.reopenTab['QString'].connect( lambda path: self._main_container.open_file(path)) self.bar.closeImageViewer.connect(self._close_image) self.bar.code_navigator.previousPressed.connect(self._navigate_code) self.bar.code_navigator.nextPressed.connect(self._navigate_code) # self.connect(self.bar, SIGNAL("recentTabsModified()"), # lambda: self._main_container.recent_files_changed()) # self.connect(self.bar.code_navigator.btnPrevious, # SIGNAL("clicked()"), # lambda: self._navigate_code(False)) # self.connect(self.bar.code_navigator.btnNext, SIGNAL("clicked()"), # lambda: self._navigate_code(True)) def _navigate_code(self, operation, forward=True): self._main_container.navigate_code_history(operation, forward) # op = self.bar.code_navigator.operation # self._main_container.navigate_code_history(val, op) def current_editor(self): return self.stacked.currentWidget() def setFocus(self): super(ComboEditor, self).setFocus() self.current_editor().setFocus() self._editor_with_focus() def _file_opened_by_main(self, path): index = self.stacked.currentIndex() ninjaide = IDE.get_service('ide') editable = ninjaide.get_or_create_editable(path) self.add_editor(editable) self.bar.set_current_by_index(index) if index == -1: self.bar.set_current_by_index(0) def add_image_viewer(self, viewer): """Add Image Viewer widget to the UI area""" self.stacked.addWidget(viewer) viewer.scaleFactorChanged.connect( self.bar.image_viewer_controls.update_scale_label) viewer.imageSizeChanged.connect( self.bar.image_viewer_controls.update_size_label) self.bar.add_item(viewer.display_name(), None) viewer.create_scene() if not self.bar.isVisible(): self.bar.setVisible(True) def add_editor(self, neditable, keep_index=False): """Add Editor Widget to the UI area.""" if neditable.editor: if self.__original: editor = neditable.editor else: # editor = neditable.editor.clone() editor = self._main_container.create_editor_from_editable( neditable) neditable.editor.link(editor) current_index = self.stacked.currentIndex() new_index = self.stacked.addWidget(editor) self.stacked.setCurrentIndex(new_index) self.bar.add_item(neditable.display_name, neditable) # Bar is not visible because all the files have been closed, # so if a new file is opened, show the bar if not self.bar.isVisible(): self.bar.setVisible(True) if keep_index: self.bar.set_current_by_index(current_index) # Connections neditable.fileClosing.connect(self._close_file) neditable.fileSaved.connect(self._update_symbols) editor.editorFocusObtained.connect(self._editor_with_focus) editor.modificationChanged.connect(self._editor_modified) editor.cursor_position_changed[int, int].connect( self._update_cursor_position) editor.current_line_changed[int].connect(self._set_current_symbol) if neditable._swap_file.dirty: self._editor_modified(True, sender=editor) neditable.checkersUpdated.connect(self._show_notification_icon) # Connect file system signals only in the original if self.__original: neditable.askForSaveFileClosing.connect(self._ask_for_save) neditable.fileChanged.connect(self._file_has_been_modified) # Load Symbols self._load_symbols(neditable) def show_combo_file(self): self.bar.combo.showPopup() def show_combo_symbol(self): self.bar.symbols_combo.showPopup() def show_combo_set_language(self): self.bar.set_language_combo.showPopup() def unlink_editors(self): for index in range(self.stacked.count()): widget = self.stacked.widget(index) # widget.setDocument(QsciDocument()) def clone(self): combo = ComboEditor() for neditable in self.bar.get_editables(): combo.add_editor(neditable) return combo def split_editor(self, orientation): new_combo = self.clone() self.splitEditor.emit(self, new_combo, orientation) def undock_editor(self): new_combo = ComboEditor() for neditable in self.bar.get_editables(): new_combo.add_editor(neditable) self.__undocked.append(new_combo) new_combo.setWindowTitle("NINJA-IDE") editor = self.current_editor() new_combo.set_current(editor.neditable) new_combo.resize(700, 500) new_combo.about_to_close_combo_editor.connect(self._remove_undock) new_combo.show() def _remove_undock(self): widget = self.sender() self.__undocked.remove(widget) def close_current_file(self): self.bar.about_to_close_file() def _close_image(self, index): layout_item = self.stacked.takeAt(index) layout_item.widget().deleteLater() if self.stacked.isEmpty(): self.bar.hide() self.allFilesClosed.emit() def _close_file(self, neditable): index = self.bar.close_file(neditable) layoutItem = self.stacked.takeAt(index) # neditable.editor.completer.cc.unload_module() self.fileClosed.emit(neditable.nfile) layoutItem.widget().deleteLater() if self.stacked.isEmpty(): self.bar.hide() self.allFilesClosed.emit() tree_symbols = IDE.get_service("symbols_explorer") if tree_symbols is not None: tree_symbols.clear() def _editor_with_focus(self): self._main_container.combo_area = self editor = self.current_editor() if editor is not None: self._main_container.current_editor_changed( editor.neditable.file_path) self._load_symbols(editor.neditable) editor.neditable.update_checkers_display() def _ask_for_save(self, neditable): val = QMessageBox.No fileName = neditable.nfile.file_name val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.No: neditable.nfile.close(force_close=True) elif val == QMessageBox.Yes: neditable.ignore_checkers = True self._main_container.save_file(neditable.editor) neditable.nfile.close() @pyqtSlot("PyQt_PyObject") def _recovery(self, neditable): print("lalalal") def _file_has_been_modified(self, neditable): index = self.bar.combo_files.findData(neditable) self.stacked.setCurrentIndex(index) self.bar.combo_files.setCurrentIndex(index) msg_box = QMessageBox(self) msg_box.setIcon(QMessageBox.Information) msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box.setDefaultButton(QMessageBox.Yes) msg_box.setWindowTitle(translations.TR_FILE_HAS_BEEN_MODIFIED) msg_box.setText( translations.TR_FILE_MODIFIED_OUTSIDE % neditable.display_name) result = msg_box.exec_() if result == QMessageBox.Yes: neditable.reload_file() return def _run_file(self, path): self._main_container.run_file(path) def _add_to_project(self, path): self._main_container._add_to_project(path) def _show_file_in_explorer(self, path): '''Connected to ActionBar's showFileInExplorer(QString) signal, forwards the file path on to the main container.''' self._main_container._show_file_in_explorer(path) def set_current(self, neditable): if neditable: self.bar.set_current_file(neditable) def _set_current(self, neditable, index): self.stacked.setCurrentIndex(index) if neditable: self.bar.image_viewer_controls.setVisible(False) self.bar.code_navigator.setVisible(True) self.bar.symbols_combo.setVisible(True) self.bar.lbl_position.setVisible(True) editor = self.current_editor() self._update_cursor_position(ignore_sender=True) editor.setFocus() self._main_container.current_editor_changed( neditable.file_path) self._load_symbols(neditable) # self._show_file_in_explorer(neditable.file_path) neditable.update_checkers_display() else: self.bar.combo_files.setCurrentIndex(index) viewer_widget = self.stacked.widget(index) self._main_container.current_editor_changed( viewer_widget.image_filename) self.bar.image_viewer_controls.setVisible(True) self.bar.code_navigator.setVisible(False) self.bar.symbols_combo.setVisible(False) self.bar.lbl_position.setVisible(False) def widget(self, index): return self.stacked.widget(index) def count(self): """Return the number of editors opened.""" return self.stacked.count() def _update_cursor_position(self, line=0, col=0, ignore_sender=False): obj = self.sender() editor = self.current_editor() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: line += 1 self.bar.update_line_col(line, col) def _set_current_symbol(self, line, ignore_sender=False): obj = self.sender() editor = self.current_editor() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: index = bisect.bisect(self._symbols_index, line) if (index >= len(self._symbols_index) or self._symbols_index[index] > (line + 1)): index -= 1 self.bar.set_current_symbol(index) def _editor_modified(self, value, sender=None): if sender is None: sender = self.sender() neditable = sender.neditable if value: text = "\u2022 %s" % neditable.display_name self.bar.update_item_text(neditable, text) else: self.bar.update_item_text(neditable, neditable.display_name) def _go_to_symbol(self, index): line = self._symbols_index[index] editor = self.current_editor() editor.go_to_line(line, center=True) editor.setFocus() def _update_symbols(self, neditable): editor = self.current_editor() # Check if it's current to avoid signals from other splits. if editor.neditable == neditable: self._load_symbols(neditable) def _update_combo_info(self, neditable): self.bar.update_item_text(neditable, neditable.display_name) self._main_container.current_editor_changed(neditable.file_path) def _load_symbols(self, neditable): # Get symbols handler by language symbols_handler = handlers.get_symbols_handler(neditable.language()) if symbols_handler is None: return source = neditable.editor.text source = source.encode(neditable.editor.encoding) symbols, symbols_simplified = symbols_handler.obtain_symbols( source, simple=True) self._symbols_index = sorted(symbols_simplified.keys()) symbols_simplified = sorted( list(symbols_simplified.items()), key=lambda x: x[0]) self.bar.add_symbols(symbols_simplified) line, _ = neditable.editor.cursor_position self._set_current_symbol(line, True) tree_symbols = IDE.get_service('symbols_explorer') if tree_symbols is not None: tree_symbols.update_symbols_tree(symbols, neditable.file_path) def _show_notification_icon(self, neditable): checkers = neditable.sorted_checkers icon = QIcon() for items in checkers: checker, color, _ = items if checker.checks: if isinstance(checker.checker_icon, int): icon = self.style().standardIcon(checker.checker_icon) elif isinstance(checker.checker_icon, str): icon = QIcon(checker.checker_icon) # FIXME: sucks else: icon = QIcon(checker.checker_icon) break self.bar.update_item_icon(neditable, icon) def show_menu_navigation(self): self.bar.code_navigator.show_menu_navigation() def closeEvent(self, event): self.about_to_close_combo_editor.emit() # self.emit(SIGNAL("aboutToCloseComboEditor()")) super(ComboEditor, self).closeEvent(event) def reject(self): if not self.__original: super(ComboEditor, self).reject()
class 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 MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() # Call the inherited classes __init__ method self.core = Core.Core.get_core() def loadGui(self): ui_file = self.core.rsrc_dir + "mainWindow.ui" uic.loadUi(ui_file, self) # Load the .ui file self.optionsBox = self.findChild(QHBoxLayout, 'optionsBox') self.installersButton = self.findChild(QPushButton, 'installersButton') icn = QIcon.fromTheme( os.path.join(settings.ICONS_THEME, "view-list-details.svg")) self.installersButton.setIcon(icn) self.installersButton.setText(_("Installers")) self.installersButton.clicked.connect(lambda: self.changePanel("I")) self.configurationButton = self.findChild(QPushButton, "configurationButton") icn = QIcon.fromTheme( os.path.join(settings.ICONS_THEME, "configure.svg")) self.configurationButton.setIcon(icn) #self.configurationButton.setIconSize(QSize(16,16)) self.configurationButton.setText(_("Configuration Options")) self.configurationButton.clicked.connect(lambda: self.changePanel("C")) self.mainBox = self.findChild(QVBoxLayout, 'mainBox') self.bannerBox = self.findChild(QLabel, 'bannerLabel') self.bannerBox.setStyleSheet("background-color: #7f0907") self.messageBox = self.findChild(QVBoxLayout, 'messageBox') self.messageImg = self.findChild(QLabel, 'messageImg') self.messageLabel = self.findChild(QLabel, 'messageLabel') self.progressBar = self.findChild(QProgressBar, 'progressBar') self.controlsBox = self.findChild(QVBoxLayout, 'controlsBox') self.applyButton = self.findChild(QPushButton, 'applyButton') icn = QIcon.fromTheme(os.path.join(settings.ICONS_THEME, "gtk-ok.svg")) self.applyButton.setIcon(icn) self.applyButton.setText(_("Install")) self.applyButton.clicked.connect(self.applyButtonClicked) self.helpButton = self.findChild(QPushButton, 'helpButton') icn = QIcon.fromTheme( os.path.join(settings.ICONS_THEME, "help-whatsthis.svg")) self.helpButton.setIcon(icn) self.helpButton.setText(_("Help")) self.helpButton.clicked.connect(self.helpButtonClicked) self.loadingBox = self.core.loadingBox self.installersBox = self.core.installersBox self.configurationBox = self.core.configurationBox self.QtStack = QStackedLayout() self.QtStack.addWidget(self.loadingBox) self.QtStack.addWidget(self.installersBox) self.QtStack.addWidget(self.configurationBox) self.mainBox.addLayout(self.QtStack) self.gatherInfo = gatherInfo() self.installersButton.hide() self.configurationButton.hide() #self.messageLabel.hide() self._manageMsgBox(True, False) self.applyButton.hide() self.helpButton.hide() qtRectangle = self.frameGeometry() centerPoint = QDesktopWidget().availableGeometry().center() qtRectangle.moveCenter(centerPoint) self.move(qtRectangle.topLeft()) centerPoint = QDesktopWidget().availableGeometry().center() qtRectangle.moveCenter(centerPoint) self.move(qtRectangle.topLeft()) self.exitLocked = True self.gatherInfo.start() self.gatherInfo.finished.connect(self._finishProcess) #def loadGui def _finishProcess(self): self.loadingBox.spinner.stop() if len(self.core.javaPanelManager.java_list) > 0: self.installersBox.drawInstallerList() self.fader_widget = FaderWidget(self.QtStack.currentWidget(), self.QtStack.widget(1)) self.QtStack.setCurrentIndex(1) self.configurationButton.show() self._manageMsgBox(True, False) #self.messageLabel.show() self.applyButton.show() self.helpButton.show() else: #self.messageLabel.show() self._manageMsgBox(False, True) self.loadingBox.spinner.hide() self.messageLabel.setText( _("No Java version(s) availables detected")) self.exitLocked = False #def _finishProcess def applyButtonClicked(self): self.othersBox = [] self.javasToInstall = self.installersBox.javas_selected self.boxSelected = self.installersBox.box_selected #self.installersBox.scrollArea.setEnabled(False) self._manageMsgBox(True, False) self.messageLabel.setText("") if len(self.javasToInstall) > 0: self.applyButton.setEnabled(False) self.configurationButton.setEnabled(False) self.helpButton.setEnabled(False) #self.messageLabel.setText(_("Installing selected Java version(s). Wait a moment...")) for item in self.boxSelected: item.itemAt(0).widget().setEnabled(False) item.itemAt(3).widget().hide() item.itemAt(4).widget().show() item.itemAt(4).widget().start() for item in self.installersBox.boxInstallers.children(): if item.itemAt(0).widget().isEnabled(): item.itemAt(0).widget().setEnabled(False) self.othersBox.append(item) self.exitLocked = True ''' self.install=installProcess(self.javasToInstall) self.install.start() self.install.finished.connect(self._finishInstall) ''' self.getPackages = getPackages(self.javasToInstall) self._manageMsgBox(True, False) self.messageLabel.setText( _("1 of 5: Obtaining information about Java(s) to install...")) self.progressBar.show() self.getPackages.start() self.getPackages.finished.connect(self._finishGetPackages) else: self._manageMsgBox(False, True) self.messageLabel.setText( _("You must select a Java version to install")) #def applyButtonClicked def _finishGetPackages(self): self.messageLabel.setText(_("2 of 5: Downloading packages...")) self.progressBar.setValue(100) self.checkProgress = QThread() self.worker = Worker() self.worker.moveToThread(self.checkProgress) self.checkProgress.started.connect(self.worker.run) self.worker._finished.connect(self.checkProgress.quit) self.worker._progress.connect(self._updateMessage) self.install = installProcess(self.javasToInstall) self.install.start() self.install.finished.connect(self._finishInstall) self.worker.running = True self.checkProgress.start() #def _finishGetPackages def _updateMessage(self, step): if step == "unpack": self.messageLabel.setText( _("3 of 5: Unpacking packages: %s of %s packages") % (str(self.core.javaPanelManager.progressUnpacked), len(self.core.javaPanelManager.initialNumberPackages))) elif step == "install": self.messageLabel.setText( _("4 of 5: Configuring packages: %s of %s packages") % (str(self.core.javaPanelManager.progressInstallation), len(self.core.javaPanelManager.initialNumberPackages))) elif step == "end": self.messageLabel.setText( _("5 of 5: Finishing the installation...")) self._updateProgressBar(step) #def _updateMessage def _updateProgressBar(self, step): if step == "unpack": if self.core.javaPanelManager.progressUnpackedPercentage == 0.00: self.progressBar.setValue(200) else: p_value = 2 + float( self.core.javaPanelManager.progressUnpackedPercentage) self.progressBar.setValue(p_value * 100) elif step == "install": if self.core.javaPanelManager.progressInstallationPercentage == 0.00: self.progressBar.setValue(300) else: p_value = 3 + float( self.core.javaPanelManager.progressInstallationPercentage) self.progressBar.setValue(p_value * 100) elif step == "end": self.progressBar.setValue(400) #def _updateProgressBar def _finishInstall(self): self.progressBar.hide() self.worker.running = False result = self.core.javaPanelManager.result_install error = False for item in result: if result[item]: for element in self.boxSelected: if element.itemAt(4).widget().item == item: element.itemAt(4).widget().stop() element.itemAt(4).widget().hide() pixmap = QPixmap(self.core.rsrc_dir + "check.png") element.itemAt(3).widget().setPixmap(pixmap) element.itemAt(0).widget().setChecked(False) element.itemAt(3).widget().show() else: for element in self.boxSelected: if element.itemAt(4).widget().item == item: element.itemAt(4).widget().stop() element.itemAt(4).widget().hide() pixmap = QPixmap(self.core.rsrc_dir + "error.png") element.itemAt(3).widget().setPixmap(pixmap) element.itemAt(0).widget().setChecked(False) element.itemAt(0).widget().setEnabled(True) element.itemAt(3).widget().show() error = True self.applyButton.setEnabled(True) self.configurationButton.setEnabled(True) self.helpButton.setEnabled(True) for item in self.othersBox: item.itemAt(0).widget().setEnabled(True) if error: self._manageMsgBox(False, True) self.messageLabel.setText( _("Installing process has ending with errors")) else: self._manageMsgBox(False, False) self.messageLabel.setText( _("Installing process has ending successfully")) self.exitLocked = False #def _finishInstall def changePanel(self, panel): if panel == "C": self.configurationButton.hide() self.progressBar.hide() self._manageMsgBox(True, False) self.messageLabel.setText("") self.configurationBox.drawConfigurationList() self.fader_widget = FaderWidget(self.QtStack.currentWidget(), self.QtStack.widget(2)) self.QtStack.setCurrentIndex(2) self.installersButton.show() self.applyButton.setEnabled(False) elif panel == "I": self.installersButton.hide() self._manageMsgBox(True, False) self.messageLabel.setText("") self.fader_widget = FaderWidget(self.QtStack.currentWidget(), self.QtStack.widget(2)) self.QtStack.setCurrentIndex(1) self.configurationButton.show() self.applyButton.setEnabled(True) self.configurationBox.boxDelete() #def changePanel def helpButtonClicked(self): lang = os.environ["LANG"] language = os.environ["LANGUAGE"] run_pkexec = False if "PKEXEC_UID" in os.environ: run_pkexec = True exec_lang = "" app_lang = "" if language == "": app_lang = lang else: language = language.split(":")[0] app_lang = language if 'valencia' in app_lang: exec_lang = "LANG=ca_ES.UTF-8@valencia" cmd = exec_lang + ' xdg-open https://wiki.edu.gva.es/lliurex/tiki-index.php?page=LliureX+Java+Panel.' else: exec_lang = "LANG=es_ES.UTF-8" cmd = exec_lang + ' xdg-open https://wiki.edu.gva.es/lliurex/tiki-index.php?page=LliureX-Java-Panel' if not run_pkexec: self.fcmd = "su -c '%s &' $USER" % cmd else: user = pwd.getpwuid(int(os.environ["PKEXEC_UID"])).pw_name self.fcmd = "su -c '" + cmd + " &' " + user os.system(self.fcmd) #def helpButtonClicked def closeEvent(self, event): if self.exitLocked: event.ignore() else: event.accept() #def closeEvent def _manageMsgBox(self, hide, error): self.progressBar.hide() if hide: self.messageImg.setStyleSheet("background-color: transparent") self.messageLabel.setStyleSheet("background-color: transparent") self.messageImg.hide() self.messageLabel.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) else: if error: self.messageImg.setStyleSheet( "border-bottom: 1px solid #da4453;border-left: 1px solid #da4453;border-top: 1px solid #da4453;background-color: #ebced2" ) self.messageLabel.setStyleSheet( "border-bottom: 1px solid #da4453;border-right: 1px solid #da4453;border-top: 1px solid #da4453;background-color: #ebced2" ) pixmap = QPixmap(self.core.rsrc_dir + "dialog-error.png") self.messageImg.setPixmap(pixmap) self.messageImg.show() self.messageLabel.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.messageLabel.show() else: self.messageImg.setStyleSheet( "border-bottom: 1px solid #27ae60;border-left: 1px solid #27ae60;border-top: 1px solid #27ae60;background-color: #c7e3d4" ) self.messageLabel.setStyleSheet( "border-bottom: 1px solid #27ae60;border-right: 1px solid #27ae60;border-top: 1px solid #27ae60;background-color: #c7e3d4" ) pixmap = QPixmap(self.core.rsrc_dir + "dialog-positive.png") self.messageImg.setPixmap(pixmap) self.messageImg.show() self.messageLabel.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.messageLabel.show()
class DialogWindow(QWidget): DIALOG_MAINMENU = 'mainmenu' DIALOG_TALK = 'talk' DIALOG_INPUT = 'input' def __init__(self, soul, win_id): QWidget.__init__(self) self.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setAttribute(Qt.WA_TranslucentBackground) # self.setAttribute(Qt.WA_DeleteOnClose) # self.setMouseTracking(True) # self.setAcceptDrops(True) self.ID = win_id self._soul = soul self._ghost = self._soul.getGhost() self._shellwindow = self._soul.getShellWindow() self._framelessWindowHint = True self.isFlip = False self.setWindowTitle(self._ghost.name) self.setContentsMargins(0, 0, 0, 0) self._bgImage = None self._bgPixmap = None self._bgMask = None self._rect = None self._curPage = 0 self._widgets = {} self._talkLabel = None self._inputLineEdit = None self._callback = None self.init() def init(self): # rect rect = self._soul.memoryRead('DialogRect', []) if len(rect) > 0: self._rect = QRect(rect[0], rect[1], rect[2], rect[3]) self.resize(self._rect.size()) else: p_pos = self._shellwindow.pos() p_size = self._shellwindow.size() self.resize(kikka.const.WindowConst.DialogWindowDefaultSize) x = int(p_pos.x() - self.size().width()) y = int(p_pos.y() + p_size.height() / 2 - self.size().height()) self._rect = QRect(QPoint(x, y), self.size()) rectdata = [ x - p_pos.x(), y - p_pos.y(), self.size().width(), self.size().height() ] self._soul.memoryWrite('DialogRect', rectdata) # default control self._talkLabel = QLabel() self._talkLabel.setAlignment(Qt.AlignLeft | Qt.AlignTop) self._talkLabel.setWordWrap(True) self._inputLabel = QLabel() self._inputLineEdit = QLineEdit() self._inputOk = QPushButton("OK") self._inputOk.clicked.connect(self.onInputOk) self._inputCancel = QPushButton("Cancel") self._inputCancel.clicked.connect(self.onInputCancel) # default UI talkLayout = QVBoxLayout() talkLayout.addWidget(self._talkLabel) talkWidget = QWidget(self) talkWidget.setContentsMargins(0, 0, 0, 0) talkWidget.setLayout(talkLayout) menuWidget = QWidget(self) menuWidget.setContentsMargins(0, 0, 0, 0) l = QHBoxLayout() l.addStretch() l.addWidget(self._inputOk) l.addWidget(self._inputCancel) inputLayout = QVBoxLayout() inputLayout.addWidget(self._inputLabel) inputLayout.addWidget(self._inputLineEdit) inputLayout.addLayout(l) inputLayout.addStretch() inputWidget = QWidget(self) inputWidget.setContentsMargins(0, 0, 0, 0) inputWidget.setLayout(inputLayout) self.setPage(self.DIALOG_MAINMENU, menuWidget) self.setPage(self.DIALOG_TALK, talkWidget) self.setPage(self.DIALOG_INPUT, inputWidget) self._mainLayout = QStackedLayout() self._mainLayout.addWidget(menuWidget) self.setLayout(self._mainLayout) def setFramelessWindowHint(self, boolean): self._framelessWindowHint = boolean if self._framelessWindowHint is False: self.clearMask() self.setWindowFlag(Qt.FramelessWindowHint, False) self.setWindowOpacity(0.8) self.setEnabled(False) self.show() self.move(2 * self.pos().x() - self.geometry().x(), 2 * self.pos().y() - self.geometry().y()) self.update() self.activateWindow() else: self._rect.setX(self.geometry().x() - self._shellwindow.pos().x()) self._rect.setY(self.geometry().y() - self._shellwindow.pos().y()) self._rect.setSize(self.geometry().size()) rectdata = [ self._rect.x(), self._rect.y(), self._rect.width(), self._rect.height() ] self._soul.memoryWrite('DialogRect', rectdata) pos = QPoint(self.pos().x(), self.pos().y()) self.setWindowFlag(Qt.FramelessWindowHint, True) self.setWindowOpacity(1) self.setEnabled(True) self.setMask(self._bgMask) self.show() self.move(pos.x(), pos.y()) def updatePosition(self): if self._framelessWindowHint is False: return p_pos = self._shellwindow.pos() p_size = self._shellwindow.size() new_x = self._rect.x() + p_pos.x() new_y = self._rect.y() + p_pos.y() self.resize(self._rect.size()) flip = False sw, sh = kikka.helper.getScreenResolution() if new_x + self.width() > sw or new_x < 0: flip = True new_x = int(p_pos.x() * 2 + p_size.width() - new_x - self.width()) if new_x + self.width() > sw: new_x = p_pos.x() - self.width() if new_x < 0: new_x = p_pos.x() + p_size.width() if self.isFlip != flip: self.isFlip = flip self.repaint() super().move(new_x, new_y) def setPage(self, tag, qwidget): if tag in self._widgets: self._widgets[tag].deleteLater() qwidget.hide() qwidget.setParent(self) self._widgets[tag] = qwidget def getTalkLabel(self): return self._talkLabel def showInputBox(self, title, default='', callback=None): self._inputLabel.setText(title) self._inputLineEdit.setText(default) self._callback = callback self.show(self.DIALOG_INPUT) def show(self, pageTag=None): if pageTag is None: pageTag = self.DIALOG_MAINMENU elif pageTag not in self._widgets: logging.warning("show: page[%s] not exist" % pageTag) pageTag = self.DIALOG_MAINMENU if self._widgets[pageTag] != self._mainLayout.currentWidget(): self._mainLayout.removeWidget(self._mainLayout.currentWidget()) self._mainLayout.addWidget(self._widgets[pageTag]) self._mainLayout.setCurrentIndex(0) self._curPage = pageTag param = kikka.helper.makeGhostEventParam( self._ghost.ID, self._soul.ID, kikka.const.GhostEvent.Dialog_Show, 'Show') param.data['pageTag'] = self._curPage self._ghost.emitGhostEvent(param) super().show() self.updatePosition() def closeEvent(self, event): self.setFramelessWindowHint(True) event.ignore() pass def resizeEvent(self, event): super().resizeEvent(event) self.repaint() def paintEvent(self, event): painter = QPainter(self) pixmap = QPixmap().fromImage(self._bgImage, Qt.AutoColor) painter.drawPixmap(self.rect(), pixmap) opt = QStyleOption() opt.initFrom(self) self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self) super().paintEvent(event) def setBalloon(self, balloon): self.setMinimumSize(balloon.minimumsize) self.setContentsMargins(balloon.margin[0], balloon.margin[1], balloon.margin[2], balloon.margin[3]) self.setStyleSheet(balloon.stylesheet) self.style().unpolish(self) self.style().polish(self) def repaint(self): self._bgImage = self._ghost.getBalloonImage(self.size(), self.isFlip, self.ID) self._bgPixmap = QPixmap().fromImage(self._bgImage, Qt.AutoColor) self._bgMask = self._bgPixmap.mask() super().repaint() def talkClear(self): self._talkLabel.setText('') def onTalk(self, message, speed=50): text = self._talkLabel.text() text += message self._talkLabel.setText(text) def onInputOk(self): if self._callback is not None: self._callback(self._inputLineEdit.text()) self.hide() def onInputCancel(self): self.hide()
class ProjectTreeColumn(QDialog): dockWidget = pyqtSignal("QObject*") undockWidget = pyqtSignal() changeTitle = pyqtSignal("QObject*", str) def __init__(self, parent=None): super(ProjectTreeColumn, self).__init__(parent, Qt.WindowStaysOnTopHint) vbox = QVBoxLayout(self) vbox.setSizeConstraint(QVBoxLayout.SetDefaultConstraint) vbox.setContentsMargins(0, 0, 0, 0) self._buttons = [] self._combo_project = QComboBox() self._combo_project.setMinimumHeight(30) self._combo_project.setContextMenuPolicy(Qt.CustomContextMenu) vbox.addWidget(self._combo_project) self._projects_area = QStackedLayout() logger.debug("This is the projects area") logger.debug(self._projects_area) vbox.addLayout(self._projects_area) self.projects = [] self._combo_project.currentIndexChanged[int].connect(self._change_current_project) self._combo_project.customContextMenuRequested['const QPoint &'].connect(self.context_menu_for_root) connections = ( {'target': 'main_container', 'signal_name': 'addToProject',#(QString) 'slot': self._add_file_to_project}, {'target': 'main_container', 'signal_name': 'showFileInExplorer',#(QString) 'slot': self._show_file_in_explorer}, ) IDE.register_service('projects_explorer', self) IDE.register_signals('projects_explorer', connections) ExplorerContainer.register_tab(translations.TR_TAB_PROJECTS, self) #FIXME: Should have a ninja settings object that stores tree state #FIXME: Or bettter, application data object #TODO: check this: #self.connect(ide, SIGNAL("goingDown()"), #self.tree_projects.shutdown) #def close_project_signal(): #self.emit(SIGNAL("updateLocator()")) def install_tab(self): ide = IDE.getInstance() ui_tools.install_shortcuts(self, actions.PROJECTS_TREE_ACTIONS, ide) ide.goingDown.connect(self.close) def load_session_projects(self, projects): for project in projects: if os.path.exists(project): self._open_project_folder(project) def open_project_folder(self, folderName=None): print("open_project_folder", folderName) if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") if folderName is None: folderName = QFileDialog.getExistingDirectory( self, translations.TR_OPEN_PROJECT_DIRECTORY, directory) logger.debug("Choosing Foldername") if folderName: logger.debug("Opening %s" % folderName) self._open_project_folder(folderName) def _open_project_folder(self, folderName): ninjaide = IDE.get_service("ide") project = NProject(folderName) qfsm = ninjaide.filesystem.open_project(project) if qfsm: self.add_project(project) self.save_recent_projects(folderName) main_container = IDE.get_service('main_container') if main_container: main_container.show_editor_area() if len(self.projects) > 1: title = "%s (%s)" % ( translations.TR_TAB_PROJECTS, len(self.projects)) else: title = translations.TR_TAB_PROJECTS self.changeTitle.emit(self, title) def _add_file_to_project(self, path): """Add the file for 'path' in the project the user choose here.""" if self._projects_area.count() > 0: pathProject = [self.current_project] addToProject = add_to_project.AddToProject(pathProject, self) addToProject.exec_() if not addToProject.pathSelected: return main_container = IDE.get_service('main_container') if not main_container: return editorWidget = main_container.get_current_editor() if not editorWidget.file_path: name = QInputDialog.getText(None, translations.TR_ADD_FILE_TO_PROJECT, translations.TR_FILENAME + ": ")[0] if not name: QMessageBox.information( self, translations.TR_INVALID_FILENAME, translations.TR_INVALID_FILENAME_ENTER_A_FILENAME) return else: name = file_manager.get_basename(editorWidget.file_path) new_path = file_manager.create_path(addToProject.pathSelected, name) ide_srv = IDE.get_service("ide") old_file = ide_srv.get_or_create_nfile(path) new_file = old_file.save(editorWidget.text(), new_path) #FIXME: Make this file replace the original in the open tab else: pass # Message about no project def _show_file_in_explorer(self, path): '''Iterate through the list of available projects and show the current file in the explorer view for the first project that contains it (i.e. if the same file is included in multiple open projects, the path will be expanded for the first project only). Note: This slot is connected to the main container's "showFileInExplorer(QString)" signal.''' central = IDE.get_service('central_container') if central and not central.is_lateral_panel_visible(): return for project in self.projects: index = project.model().index(path) if index.isValid(): # This highlights the index in the tree for us project.scrollTo(index, QAbstractItemView.EnsureVisible) project.setCurrentIndex(index) break def add_project(self, project): if project not in self.projects: self._combo_project.addItem(project.name) ptree = TreeProjectsWidget(project) self._projects_area.addWidget(ptree) ptree.closeProject.connect(self._close_project) print("project", project)#setRootPath pmodel = project.model ptree.setModel(pmodel) pindex = pmodel.index(pmodel.rootPath()) ptree.setRootIndex(pindex) self.projects.append(ptree) current_index = self._projects_area.count() self._projects_area.setCurrentIndex(current_index - 1) self._combo_project.setCurrentIndex(current_index - 1) def _close_project(self, widget): """Close the project related to the tree widget.""" index = self._projects_area.currentIndex() self.projects.remove(widget) self._projects_area.takeAt(index) self._combo_project.removeItem(index) index = self._combo_project.currentIndex() self._projects_area.setCurrentIndex(index) ninjaide = IDE.getInstance() ninjaide.filesystem.close_project(widget.project.path) widget.deleteLater() if len(self.projects) > 1: title = "%s (%s)" % ( translations.TR_TAB_PROJECTS, len(self.projects)) else: title = translations.TR_TAB_PROJECTS self.changeTitle.emit(self, title) def _change_current_project(self, index): self._projects_area.setCurrentIndex(index) def close_opened_projects(self): for project in reversed(self.projects): self._close_project(project) def save_project(self): """Save all the opened files that belongs to the actual project.""" if self._projects_area.count() > 0: path = self.current_project.path main_container = IDE.get_service('main_container') if path and main_container: main_container.save_project(path) def create_new_project(self): wizard = new_project_manager.NewProjectManager(self) wizard.show() @property def current_project(self): if self._projects_area.count() > 0: return self._projects_area.currentWidget().project @property def current_tree(self): obj = self._projects_area.currentWidget() return obj def save_recent_projects(self, folder): settings = IDE.data_settings() recent_project_list = settings.value('recentProjects', {}) #if already exist on the list update the date time projectProperties = json_manager.read_ninja_project(folder) name = projectProperties.get('name', '') description = projectProperties.get('description', '') if name == '': name = file_manager.get_basename(folder) if description == '': description = translations.TR_NO_DESCRIPTION if folder in recent_project_list: properties = recent_project_list[folder] properties["lastopen"] = QDateTime.currentDateTime() properties["name"] = name properties["description"] = description recent_project_list[folder] = properties else: recent_project_list[folder] = { "name": name, "description": description, "isFavorite": False, "lastopen": QDateTime.currentDateTime()} #if the length of the project list it's high that 10 then delete #the most old #TODO: add the length of available projects to setting if len(recent_project_list) > 10: del recent_project_list[self.find_most_old_open( recent_project_list)] settings.setValue('recentProjects', recent_project_list) def find_most_old_open(self, recent_project_list): listFounder = [] for recent_project_path, content in list(recent_project_list.items()): listFounder.append((recent_project_path, int( content["lastopen"].toString("yyyyMMddHHmmzzz")))) listFounder = sorted(listFounder, key=lambda date: listFounder[1], reverse=True) # sort by date last used return listFounder[0][0] def reject(self): if self.parent() is None: self.dockWidget.emit(self) def closeEvent(self, event): self.dockWidget.emit(self) event.ignore() def context_menu_for_root(self): if not self.current_tree: # en vez de un mensaje, colorear el area de projecto... # el area de projecto debería decir 'Sin Projectos'/'without Projects' # QMessageBox.information(self, translations.TR_INFO_TITLE_PROJECT_PROPERTIES, # translations.TR_INFO_MESSAGE_PROJECT_PROPERTIES) return menu = QMenu(self) path = self.current_tree.project.path action_add_file = menu.addAction(QIcon(":img/new"), translations.TR_ADD_NEW_FILE) action_add_folder = menu.addAction(QIcon( ":img/openProj"), translations.TR_ADD_NEW_FOLDER) action_create_init = menu.addAction(translations.TR_CREATE_INIT) action_add_file.triggered['bool'].connect(lambda s,_path=path: self.current_tree._add_new_file(_path)) action_add_folder.triggered['bool'].connect(lambda s,_path=path: self.current_tree._add_new_folder(_path)) action_create_init.triggered['bool'].connect(lambda s,_path=path: self.current_tree._create_init(_path)) menu.addSeparator() actionRunProject = menu.addAction(QIcon( ":img/play"), translations.TR_RUN_PROJECT) actionRunProject.triggered['bool'].connect(lambda s: self.current_tree._execute_project()) if self.current_tree._added_to_console: actionRemoveFromConsole = menu.addAction( translations.TR_REMOVE_PROJECT_FROM_PYTHON_CONSOLE) actionRemoveFromConsole.triggered['bool'].connect(lambda s: self.current_tree._remove_project_from_console()) else: actionAdd2Console = menu.addAction( translations.TR_ADD_PROJECT_TO_PYTHON_CONSOLE) actionAdd2Console.triggered['bool'].connect(lambda s: self.current_tree._add_project_to_console()) actionShowFileSizeInfo = menu.addAction(translations.TR_SHOW_FILESIZE) actionShowFileSizeInfo.triggered['bool'].connect(lambda s: self.current_tree.show_filesize_info()) actionProperties = menu.addAction(QIcon(":img/pref"), translations.TR_PROJECT_PROPERTIES) actionProperties.triggered['bool'].connect(lambda s: self.current_tree.open_project_properties()) menu.addSeparator() action_close = menu.addAction( self.style().standardIcon(QStyle.SP_DialogCloseButton), translations.TR_CLOSE_PROJECT) action_close.triggered['bool'].connect(lambda s: self.current_tree._close_project()) #menu for the project for m in self.current_tree.extra_menus_by_scope['project']: if isinstance(m, QMenu): menu.addSeparator() menu.addMenu(m) #show the menu! menu.exec_(QCursor.pos()) def open_project_properties(self): print("open_project_properties-.-.-.-.-") _prj = self.current_project if _prj: proj = project_properties_widget.ProjectProperties(_prj, self) proj.show() return QMessageBox.information(self, translations.TR_INFO_TITLE_PROJECT_PROPERTIES, translations.TR_INFO_MESSAGE_PROJECT_PROPERTIES)
class _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 ComboEditor(QDialog): closeSplit = pyqtSignal(QWidget)#closeSplit allFilesClosed = pyqtSignal() splitEditor = pyqtSignal("QWidget*", "QWidget*", bool) recentTabsModified = pyqtSignal() aboutToCloseComboEditor = pyqtSignal() class NAVIGATE: prev = 0 next = 1 Q_ENUMS(NAVIGATE) def __init__(self, original=False): super(ComboEditor, self).__init__(None, Qt.WindowStaysOnTopHint) self.__original = original self.__undocked = [] self._symbols_index = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.bar = ActionBar(main_combo=original) vbox.addWidget(self.bar) self.stacked = QStackedLayout() vbox.addLayout(self.stacked) self._main_container = IDE.get_service('main_container') if not self.__original: self._main_container.fileOpened.connect(self._file_opened_by_main) self.bar.combo.showComboSelector.connect(self._main_container.change_tab) self.bar.changeCurrent.connect(self._set_current) self.bar.editorSplited.connect(self.split_editor) self.bar.runFile[str].connect(self._run_file) self.bar.closeFile.connect(lambda: self.closeSplit.emit(self)) self.bar.addToProject[str].connect(self._add_to_project) self.bar.showFileInExplorer.connect(self._show_file_in_explorer) self.bar.goToSymbol.connect(self._go_to_symbol) self.bar.undockEditor.connect(self.undock_editor) self.bar.reopenTab[str].connect(self._main_container.open_file) self.bar.recentTabsModified.connect(self._main_container.recent_files_changed) self.bar.code_navigator.btnPrevious.clicked['bool'].connect(lambda: self._navigate_code(self.NAVIGATE.prev)) self.bar.code_navigator.btnNext.clicked['bool'].connect(lambda: self._navigate_code(self.NAVIGATE.prev)) def _navigate_code(self, val): op = self.bar.code_navigator.operation self._main_container.navigate_code_history(val, op) def currentWidget(self): return self.stacked.currentWidget() def setFocus(self): super(ComboEditor, self).setFocus() w = self.stacked.currentWidget() if w: w.setFocus() self._editor_with_focus() def _file_opened_by_main(self, path): index = self.stacked.currentIndex() ninjaide = IDE.getInstance() editable = ninjaide.get_or_create_editable(path) print("_file_opened_by_main", editable) self.add_editor(editable) self.bar.set_current_by_index(index) if index == -1: self.bar.set_current_by_index(0) def add_editor(self, neditable, keep_index=False): """Add Editor Widget to the UI area.""" if neditable.editor: if self.__original: editor = neditable.editor else: editor = self._main_container.create_editor_from_editable( neditable) index = self.stacked.currentIndex() self.stacked.addWidget(editor) self.bar.add_item(neditable.display_name, neditable) if keep_index: self.bar.set_current_by_index(index) # Editor Signals editor.cursorPositionChanged[int, int].connect(self._update_cursor_position) editor.editorFocusObtained.connect(self._editor_with_focus) editor.currentLineChanged.connect(self._set_current_symbol) editor.modificationChanged['bool'].connect(self._editor_modified) neditable.checkersUpdated.connect(self._show_notification_icon) neditable.fileSaved.connect(self._update_symbols) neditable.fileSaved.connect(self._update_combo_info) # Connect file system signals only in the original neditable.fileClosing.connect(self._close_file) if self.__original: neditable.askForSaveFileClosing.connect(self._ask_for_save) neditable.fileChanged.connect(self._file_has_been_modified) # Load Symbols self._load_symbols(neditable) def show_combo_file(self): print("show_combo_file") self.bar.combo.showPopup() def show_combo_symbol(self): self.bar.symbols_combo.showPopup() def unlink_editors(self): for index in range(self.stacked.count()): widget = self.stacked.widget(index) widget.setDocument(QsciDocument()) def split_editor(self, orientationVertical): new_widget = ComboEditor() for neditable in self.bar.get_editables(): print("\nsplit_editor", neditable, new_widget) new_widget.add_editor(neditable) self.splitEditor.emit(self, new_widget, orientationVertical) def undock_editor(self): new_combo = ComboEditor() new_combo.setWindowTitle("NINJA-IDE") self.__undocked.append(new_combo) for neditable in self.bar.get_editables(): print("undock_editor", neditable) new_combo.add_editor(neditable) new_combo.resize(500, 500) new_combo.aboutToCloseComboEditor.connect(self._remove_undock) new_combo.show() def _remove_undock(self): widget = self.sender() self.__undocked.remove(widget) def close_current_file(self): self.bar.about_to_close_file() def _close_file(self, neditable): index = self.bar.close_file(neditable) layoutItem = self.stacked.takeAt(index) #neditable.editor.completer.cc.unload_module() self._add_to_last_opened(neditable.file_path) layoutItem.widget().deleteLater() if self.stacked.isEmpty(): self.allFilesClosed.emit() def _add_to_last_opened(self, path): if path not in settings.LAST_OPENED_FILES: settings.LAST_OPENED_FILES.append(path) if len(settings.LAST_OPENED_FILES) > settings.MAX_REMEMBER_TABS: self.__lastOpened = self.__lastOpened[1:] self.recentTabsModified.emit() def _editor_with_focus(self): if self._main_container.current_widget is not self: self._main_container.current_widget = self editor = self.stacked.currentWidget() self._main_container.current_editor_changed( editor.neditable.file_path) self._load_symbols(editor.neditable) editor.neditable.update_checkers_display() def _ask_for_save(self, neditable): val = QMessageBox.No fileName = neditable.nfile.file_name val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.No: neditable.nfile.close(force_close=True) elif val == QMessageBox.Yes: neditable.ignore_checkers = True self._main_container.save_file(neditable.editor) neditable.nfile.close() def _file_has_been_modified(self, neditable): val = QMessageBox.No fileName = neditable.file_path val = QMessageBox.question( self, translations.TR_FILE_HAS_BEEN_MODIFIED, "%s%s" % (fileName, translations.TR_FILE_MODIFIED_OUTSIDE), QMessageBox.Yes | QMessageBox.No) if val == QMessageBox.Yes: neditable.reload_file() def _run_file(self, path): self._main_container.run_file(path) def _add_to_project(self, path): self._main_container._add_to_project(path) def _show_file_in_explorer(self, path): '''Connected to ActionBar's showFileInExplorer(QString) signal, forwards the file path on to the main container.''' self._main_container._show_file_in_explorer(path) def set_current(self, neditable): if neditable: self.bar.set_current_file(neditable) def _set_current(self, neditable, index): if neditable: self.stacked.setCurrentIndex(index) editor = self.stacked.currentWidget() self._update_cursor_position(ignore_sender=True) editor.setFocus() self._main_container.current_editor_changed( neditable.file_path) self._load_symbols(neditable) self._show_file_in_explorer(neditable.file_path) neditable.update_checkers_display() def widget(self, index): return self.stacked.widget(index) def count(self): """Return the number of editors opened.""" return self.stacked.count() def _update_cursor_position(self, line=0, col=0, ignore_sender=False): obj = self.sender() editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: line += 1 self.bar.update_line_col(line, col) def _set_current_symbol(self, line, ignore_sender=False): obj = self.sender() editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: index = bisect.bisect(self._symbols_index, line) if (index >= len(self._symbols_index) or self._symbols_index[index] > (line + 1)): index -= 1 self.bar.set_current_symbol(index) def _editor_modified(self, value): obj = self.sender() neditable = obj.neditable if value: text = "\u2022 %s" % neditable.display_name self.bar.update_item_text(neditable, text) else: self.bar.update_item_text(neditable, neditable.display_name) def _go_to_symbol(self, index): print("_go_to_symbol in index:", index) line = self._symbols_index[index] editor = self.stacked.currentWidget() editor.go_to_line(line) def _update_symbols(self, neditable): editor = self.stacked.currentWidget() # Check if it's current to avoid signals from other splits. if editor == neditable.editor: self._load_symbols(neditable) def _update_combo_info(self, neditable): self.bar.update_item_text(neditable, neditable.display_name) self._main_container.current_editor_changed(neditable.file_path) def _load_symbols(self, neditable): symbols_handler = handlers.get_symbols_handler('py') source = neditable.editor.text() source = source.encode(neditable.editor.encoding) symbols, symbols_simplified = symbols_handler.obtain_symbols( source, simple=True) self._symbols_index = sorted(symbols_simplified.keys()) symbols_simplified = sorted( list(symbols_simplified.items()), key=lambda x: x[0]) self.bar.add_symbols(symbols_simplified) line, _ = neditable.editor.getCursorPosition() self._set_current_symbol(line, True) tree_symbols = IDE.get_service('symbols_explorer') tree_symbols.update_symbols_tree(symbols, neditable.file_path) def _show_notification_icon(self, neditable): checkers = neditable.sorted_checkers icon = QIcon() for items in checkers: checker, color, _ = items if checker.checks: if isinstance(checker.checker_icon, int): icon = self.style().standardIcon(checker.checker_icon) elif isinstance(checker.checker_icon, str): icon = QIcon(checker.checker_icon) break self.bar.update_item_icon(neditable, icon) def show_menu_navigation(self): self.bar.code_navigator.show_menu_navigation() def closeEvent(self, event): self.aboutToCloseComboEditor.emit() super(ComboEditor, self).closeEvent(event) def reject(self): if not self.__original: super(ComboEditor, self).reject()
class ComboEditor(QWidget): # Signals closeSplit = pyqtSignal('PyQt_PyObject') splitEditor = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject', Qt.Orientation) allFilesClosed = pyqtSignal() about_to_close_combo_editor = pyqtSignal() fileClosed = pyqtSignal("PyQt_PyObject") def __init__(self, original=False): super(ComboEditor, self).__init__(None) self.__original = original self.__undocked = [] self._symbols_index = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self.bar = ActionBar(main_combo=original) vbox.addWidget(self.bar) # Info bar # self.info_bar = InfoBar(self) # self.info_bar.setVisible(False) # vbox.addWidget(self.info_bar) self.stacked = QStackedLayout() vbox.addLayout(self.stacked) self._main_container = IDE.get_service('main_container') if not self.__original: self._main_container.fileOpened['QString'].connect( self._file_opened_by_main) self.bar.combo_files.showComboSelector.connect( self._main_container.show_files_handler) self.bar.combo_files.hideComboSelector.connect( self._main_container.hide_files_handler) self.bar.change_current['PyQt_PyObject', int].connect(self._set_current) self.bar.splitEditor[bool].connect(self.split_editor) self.bar.runFile['QString'].connect(self._run_file) self.bar.closeSplit.connect(lambda: self.closeSplit.emit(self)) self.bar.addToProject['QString'].connect(self._add_to_project) self.bar.showFileInExplorer['QString'].connect( self._show_file_in_explorer) self.bar.goToSymbol[int].connect(self._go_to_symbol) self.bar.undockEditor.connect(self.undock_editor) self.bar.reopenTab['QString'].connect( lambda path: self._main_container.open_file(path)) self.bar.closeImageViewer.connect(self._close_image) self.bar.code_navigator.previousPressed.connect(self._navigate_code) self.bar.code_navigator.nextPressed.connect(self._navigate_code) # self.connect(self.bar, SIGNAL("recentTabsModified()"), # lambda: self._main_container.recent_files_changed()) # self.connect(self.bar.code_navigator.btnPrevious, # SIGNAL("clicked()"), # lambda: self._navigate_code(False)) # self.connect(self.bar.code_navigator.btnNext, SIGNAL("clicked()"), # lambda: self._navigate_code(True)) def _navigate_code(self, operation, forward=True): self._main_container.navigate_code_history(operation, forward) # op = self.bar.code_navigator.operation # self._main_container.navigate_code_history(val, op) def current_editor(self): return self.stacked.currentWidget() def setFocus(self): super(ComboEditor, self).setFocus() self.current_editor().setFocus() self._editor_with_focus() def _file_opened_by_main(self, path): index = self.stacked.currentIndex() ninjaide = IDE.get_service('ide') editable = ninjaide.get_or_create_editable(path) self.add_editor(editable) self.bar.set_current_by_index(index) if index == -1: self.bar.set_current_by_index(0) def add_image_viewer(self, viewer): """Add Image Viewer widget to the UI area""" self.stacked.addWidget(viewer) viewer.scaleFactorChanged.connect( self.bar.image_viewer_controls.update_scale_label) viewer.imageSizeChanged.connect( self.bar.image_viewer_controls.update_size_label) self.bar.add_item(viewer.display_name(), None) viewer.create_scene() if not self.bar.isVisible(): self.bar.setVisible(True) def add_editor(self, neditable, keep_index=False): """Add Editor Widget to the UI area.""" if neditable.editor: if self.__original: editor = neditable.editor else: # editor = neditable.editor.clone() editor = self._main_container.create_editor_from_editable( neditable) neditable.editor.link(editor) current_index = self.stacked.currentIndex() new_index = self.stacked.addWidget(editor) self.stacked.setCurrentIndex(new_index) self.bar.add_item(neditable.display_name, neditable) # Bar is not visible because all the files have been closed, # so if a new file is opened, show the bar if not self.bar.isVisible(): self.bar.setVisible(True) if keep_index: self.bar.set_current_by_index(current_index) # Connections neditable.fileClosing.connect(self._close_file) neditable.fileSaved.connect(self._update_symbols) editor.editorFocusObtained.connect(self._editor_with_focus) editor.modificationChanged.connect(self._editor_modified) editor.cursor_position_changed[int, int].connect( self._update_cursor_position) editor.current_line_changed[int].connect(self._set_current_symbol) if neditable._swap_file.dirty: self._editor_modified(True, sender=editor) neditable.checkersUpdated.connect(self._show_notification_icon) # Connect file system signals only in the original if self.__original: neditable.askForSaveFileClosing.connect(self._ask_for_save) neditable.fileChanged.connect(self._file_has_been_modified) # Load Symbols self._load_symbols(neditable) def show_combo_file(self): self.bar.combo.showPopup() def show_combo_symbol(self): self.bar.symbols_combo.showPopup() def show_combo_set_language(self): self.bar.set_language_combo.showPopup() def unlink_editors(self): for index in range(self.stacked.count()): widget = self.stacked.widget(index) # widget.setDocument(QsciDocument()) def clone(self): combo = ComboEditor() for neditable in self.bar.get_editables(): combo.add_editor(neditable) return combo def split_editor(self, orientation): new_combo = self.clone() self.splitEditor.emit(self, new_combo, orientation) def undock_editor(self): new_combo = ComboEditor() for neditable in self.bar.get_editables(): new_combo.add_editor(neditable) self.__undocked.append(new_combo) new_combo.setWindowTitle("NINJA-IDE") editor = self.current_editor() new_combo.set_current(editor.neditable) new_combo.resize(700, 500) new_combo.about_to_close_combo_editor.connect(self._remove_undock) new_combo.show() def _remove_undock(self): widget = self.sender() self.__undocked.remove(widget) def close_current_file(self): self.bar.about_to_close_file() def _close_image(self, index): layout_item = self.stacked.takeAt(index) layout_item.widget().deleteLater() if self.stacked.isEmpty(): self.bar.hide() self.allFilesClosed.emit() def _close_file(self, neditable): index = self.bar.close_file(neditable) layoutItem = self.stacked.takeAt(index) # neditable.editor.completer.cc.unload_module() self.fileClosed.emit(neditable.nfile) layoutItem.widget().deleteLater() if self.stacked.isEmpty(): self.bar.hide() self.allFilesClosed.emit() tree_symbols = IDE.get_service("symbols_explorer") if tree_symbols is not None: tree_symbols.clear() def _editor_with_focus(self): self._main_container.combo_area = self editor = self.current_editor() if editor is not None: self._main_container.current_editor_changed( editor.neditable.file_path) self._load_symbols(editor.neditable) editor.neditable.update_checkers_display() def _ask_for_save(self, neditable): val = QMessageBox.No fileName = neditable.nfile.file_name val = QMessageBox.question( self, (self.tr('The file %s was not saved') % fileName), self.tr("Do you want to save before closing?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if val == QMessageBox.No: neditable.nfile.close(force_close=True) elif val == QMessageBox.Yes: neditable.ignore_checkers = True self._main_container.save_file(neditable.editor) neditable.nfile.close() @pyqtSlot("PyQt_PyObject") def _recovery(self, neditable): print("lalalal") def _file_has_been_modified(self, neditable): index = self.bar.combo_files.findData(neditable) self.stacked.setCurrentIndex(index) self.bar.combo_files.setCurrentIndex(index) msg_box = QMessageBox(self) msg_box.setIcon(QMessageBox.Information) msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box.setDefaultButton(QMessageBox.Yes) msg_box.setWindowTitle(translations.TR_FILE_HAS_BEEN_MODIFIED) msg_box.setText(translations.TR_FILE_MODIFIED_OUTSIDE % neditable.display_name) result = msg_box.exec_() if result == QMessageBox.Yes: neditable.reload_file() return def _run_file(self, path): self._main_container.run_file(path) def _add_to_project(self, path): self._main_container._add_to_project(path) def _show_file_in_explorer(self, path): '''Connected to ActionBar's showFileInExplorer(QString) signal, forwards the file path on to the main container.''' self._main_container._show_file_in_explorer(path) def set_current(self, neditable): if neditable: self.bar.set_current_file(neditable) def _set_current(self, neditable, index): self.stacked.setCurrentIndex(index) if neditable: self.bar.image_viewer_controls.setVisible(False) self.bar.code_navigator.setVisible(True) self.bar.symbols_combo.setVisible(True) self.bar.lbl_position.setVisible(True) editor = self.current_editor() self._update_cursor_position(ignore_sender=True) editor.setFocus() self._main_container.current_editor_changed(neditable.file_path) self._load_symbols(neditable) # self._show_file_in_explorer(neditable.file_path) neditable.update_checkers_display() else: self.bar.combo_files.setCurrentIndex(index) viewer_widget = self.stacked.widget(index) self._main_container.current_editor_changed( viewer_widget.image_filename) self.bar.image_viewer_controls.setVisible(True) self.bar.code_navigator.setVisible(False) self.bar.symbols_combo.setVisible(False) self.bar.lbl_position.setVisible(False) def widget(self, index): return self.stacked.widget(index) def count(self): """Return the number of editors opened.""" return self.stacked.count() def _update_cursor_position(self, line=0, col=0, ignore_sender=False): obj = self.sender() editor = self.current_editor() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: line += 1 self.bar.update_line_col(line, col) def _set_current_symbol(self, line, ignore_sender=False): obj = self.sender() editor = self.current_editor() # Check if it's current to avoid signals from other splits. if ignore_sender or editor == obj: index = bisect.bisect(self._symbols_index, line) if (index >= len(self._symbols_index) or self._symbols_index[index] > (line + 1)): index -= 1 self.bar.set_current_symbol(index) def _editor_modified(self, value, sender=None): if sender is None: sender = self.sender() neditable = sender.neditable if value: text = "\u2022 %s" % neditable.display_name self.bar.update_item_text(neditable, text) else: self.bar.update_item_text(neditable, neditable.display_name) def _go_to_symbol(self, index): line = self._symbols_index[index] editor = self.current_editor() editor.go_to_line(line, center=True) editor.setFocus() def _update_symbols(self, neditable): editor = self.current_editor() # Check if it's current to avoid signals from other splits. if editor.neditable == neditable: self._load_symbols(neditable) def _update_combo_info(self, neditable): self.bar.update_item_text(neditable, neditable.display_name) self._main_container.current_editor_changed(neditable.file_path) def _load_symbols(self, neditable): # Get symbols handler by language symbols_handler = handlers.get_symbols_handler(neditable.language()) if symbols_handler is None: return source = neditable.editor.text source = source.encode(neditable.editor.encoding) symbols, symbols_simplified = symbols_handler.obtain_symbols( source, simple=True) self._symbols_index = sorted(symbols_simplified.keys()) symbols_simplified = sorted(list(symbols_simplified.items()), key=lambda x: x[0]) self.bar.add_symbols(symbols_simplified) line, _ = neditable.editor.cursor_position self._set_current_symbol(line, True) tree_symbols = IDE.get_service('symbols_explorer') if tree_symbols is not None: tree_symbols.update_symbols_tree(symbols, neditable.file_path) def _show_notification_icon(self, neditable): checkers = neditable.sorted_checkers icon = QIcon() for items in checkers: checker, color, _ = items if checker.checks: if isinstance(checker.checker_icon, int): icon = self.style().standardIcon(checker.checker_icon) elif isinstance(checker.checker_icon, str): icon = QIcon(checker.checker_icon) # FIXME: sucks else: icon = QIcon(checker.checker_icon) break self.bar.update_item_icon(neditable, icon) def show_menu_navigation(self): self.bar.code_navigator.show_menu_navigation() def closeEvent(self, event): self.about_to_close_combo_editor.emit() # self.emit(SIGNAL("aboutToCloseComboEditor()")) super(ComboEditor, self).closeEvent(event) def reject(self): if not self.__original: super(ComboEditor, self).reject()
class 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 ProjectTreeColumn(QDialog): # Signals dockWidget = pyqtSignal('PyQt_PyObject') undockWidget = pyqtSignal() changeTitle = pyqtSignal('PyQt_PyObject', 'QString') updateLocator = pyqtSignal() def __init__(self, parent=None): super(ProjectTreeColumn, self).__init__(parent) vbox = QVBoxLayout(self) # vbox.setSizeConstraint(QVBoxLayout.SetDefaultConstraint) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) self._buttons = [] combo_container = ui_tools.StyledBar() combo_container.setProperty('gradient', True) combo_layout = QVBoxLayout(combo_container) combo_layout.setContentsMargins(0, 0, 0, 0) self._combo_project = QComboBox() combo_layout.addWidget(self._combo_project) self._combo_project.setProperty("gradient", True) self._combo_project.setContextMenuPolicy(Qt.CustomContextMenu) vbox.addWidget(combo_container) self._projects_area = QStackedLayout() logger.debug("This is the projects area") vbox.addLayout(self._projects_area) self.projects = [] self._combo_project.currentIndexChanged[int].connect( self._change_current_project) self._combo_project.customContextMenuRequested[ 'const QPoint&'].connect(self.context_menu_for_root) connections = ( { 'target': 'main_container', 'signal_name': 'addToProject(QString)', 'slot': self._add_file_to_project }, { 'target': 'main_container', 'signal_name': 'showFileInExplorer(QString)', 'slot': self._show_file_in_explorer }, ) IDE.register_service('projects_explorer', self) IDE.register_signals('projects_explorer', connections) ExplorerContainer.register_tab(translations.TR_TAB_PROJECTS, self) # FIXME: Should have a ninja settings object that stores tree state # FIXME: Or bettter, application data object # TODO: check this: # self.connect(ide, SIGNAL("goingDown()"), # self.tree_projects.shutdown) # def close_project_signal(): # self.emit(SIGNAL("updateLocator()")) def install_tab(self): ide = IDE.get_service('ide') ui_tools.install_shortcuts(self, actions.PROJECTS_TREE_ACTIONS, ide) ide.goingDown.connect(self.close) def load_session_projects(self, projects): for project in projects: if os.path.exists(project): self._open_project_folder(project) def open_project_folder(self, folderName=None): if settings.WORKSPACE: directory = settings.WORKSPACE else: directory = os.path.expanduser("~") if folderName is None: folderName = QFileDialog.getExistingDirectory( self, translations.TR_OPEN_PROJECT_DIRECTORY, directory) logger.debug("Choosing Foldername") if folderName: if not file_manager.folder_exists(folderName): QMessageBox.information( self, translations.TR_PROJECT_NONEXIST_TITLE, translations.TR_PROJECT_NONEXIST % folderName) return logger.debug("Opening %s" % folderName) for p in self.projects: if p.project.path == folderName: QMessageBox.information( self, translations.TR_PROJECT_PATH_ALREADY_EXIST_TITLE, translations.TR_PROJECT_PATH_ALREADY_EXIST % folderName) return self._open_project_folder(folderName) def _open_project_folder(self, folderName): ninjaide = IDE.get_service("ide") # TODO: handle exception when .nja file is empty project = NProject(folderName) qfsm = ninjaide.filesystem.open_project(project) if qfsm: self.add_project(project) self.save_recent_projects(folderName) # FIXME: show editor area? # main_container = IDE.get_service('main_container') # if main_container: # main_container.show_editor_area() if len(self.projects) > 1: title = "%s (%s)" % (translations.TR_TAB_PROJECTS, len(self.projects)) else: title = translations.TR_TAB_PROJECTS self.changeTitle.emit(self, title) def _add_file_to_project(self, path): """Add the file for 'path' in the project the user choose here.""" if self._projects_area.count() > 0: pathProject = [self.current_project] addToProject = add_to_project.AddToProject(pathProject, self) addToProject.exec_() if not addToProject.pathSelected: return main_container = IDE.get_service('main_container') if not main_container: return editorWidget = main_container.get_current_editor() if not editorWidget.file_path: name = QInputDialog.getText( None, translations.TR_ADD_FILE_TO_PROJECT, translations.TR_FILENAME + ": ")[0] if not name: QMessageBox.information( self, translations.TR_INVALID_FILENAME, translations.TR_INVALID_FILENAME_ENTER_A_FILENAME) return else: name = file_manager.get_basename(editorWidget.file_path) new_path = file_manager.create_path(addToProject.pathSelected, name) ide_srv = IDE.get_service("ide") old_file = ide_srv.get_or_create_nfile(path) new_file = old_file.save(editorWidget.text(), new_path) # FIXME: Make this file replace the original in the open tab else: pass # Message about no project def _show_file_in_explorer(self, path): '''Iterate through the list of available projects and show the current file in the explorer view for the first project that contains it (i.e. if the same file is included in multiple open projects, the path will be expanded for the first project only). Note: This slot is connected to the main container's "showFileInExplorer(QString)" signal.''' central = IDE.get_service('central_container') if central and not central.is_lateral_panel_visible(): return for project in self.projects: index = project.model().index(path) if index.isValid(): # This highlights the index in the tree for us project.scrollTo(index, QAbstractItemView.EnsureVisible) project.setCurrentIndex(index) break def add_project(self, project): if project not in self.projects: self._combo_project.addItem(project.name) ptree = TreeProjectsWidget(project) self._projects_area.addWidget(ptree) ptree.closeProject['PyQt_PyObject'].connect(self._close_project) pmodel = project.model ptree.setModel(pmodel) pindex = pmodel.index(pmodel.rootPath()) ptree.setRootIndex(pindex) self.projects.append(ptree) current_index = self._projects_area.count() self._projects_area.setCurrentIndex(current_index - 1) self._combo_project.setCurrentIndex(current_index - 1) def _close_project(self, widget): """Close the project related to the tree widget.""" index = self._projects_area.currentIndex() self.projects.remove(widget) self._projects_area.takeAt(index) self._combo_project.removeItem(index) index = self._combo_project.currentIndex() self._projects_area.setCurrentIndex(index) ninjaide = IDE.get_service('ide') ninjaide.filesystem.close_project(widget.project.path) widget.deleteLater() if len(self.projects) > 1: title = "%s (%s)" % (translations.TR_TAB_PROJECTS, len(self.projects)) else: title = translations.TR_TAB_PROJECTS self.changeTitle.emit(self, title) self.updateLocator.emit() def _change_current_project(self, index): self._projects_area.setCurrentIndex(index) def close_opened_projects(self): for project in reversed(self.projects): self._close_project(project) def save_project(self): """Save all the opened files that belongs to the actual project.""" if self._projects_area.count() > 0: path = self.current_project.path main_container = IDE.get_service('main_container') if path and main_container: main_container.save_project(path) def create_new_project(self): wizard = new_project_manager.NewProjectManager(self) wizard.show() @property def current_project(self): if self._projects_area.count() > 0: return self._projects_area.currentWidget().project @property def current_tree(self): return self._projects_area.currentWidget() def set_current_item(self, path): if self.current_project is not None: self.current_tree.set_current_item(path) def save_recent_projects(self, folder): settings = IDE.data_settings() recent_project_list = settings.value('recentProjects', {}) # if already exist on the list update the date time projectProperties = json_manager.read_ninja_project(folder) name = projectProperties.get('name', '') description = projectProperties.get('description', '') if name == '': name = file_manager.get_basename(folder) if description == '': description = translations.TR_NO_DESCRIPTION if folder in recent_project_list: properties = recent_project_list[folder] properties["lastopen"] = QDateTime.currentDateTime() properties["name"] = name properties["description"] = description recent_project_list[folder] = properties else: recent_project_list[folder] = { "name": name, "description": description, "isFavorite": False, "lastopen": QDateTime.currentDateTime() } # if the length of the project list it's high that 10 then delete # the most old # TODO: add the length of available projects to setting if len(recent_project_list) > 10: del recent_project_list[self.find_most_old_open( recent_project_list)] settings.setValue('recentProjects', recent_project_list) def find_most_old_open(self, recent_project_list): listFounder = [] for recent_project_path, content in list(recent_project_list.items()): listFounder.append( (recent_project_path, int(content["lastopen"].toString("yyyyMMddHHmmzzz")))) listFounder = sorted(listFounder, key=lambda date: listFounder[1], reverse=True) # sort by date last used return listFounder[0][0] def reject(self): if self.parent() is None: self.dockWidget.emit(self) def closeEvent(self, event): self.dockWidget.emit(self) event.ignore() def context_menu_for_root(self): menu = QMenu(self) path = self.current_tree.project.path action_add_file = menu.addAction(QIcon(":img/new"), translations.TR_ADD_NEW_FILE) action_add_folder = menu.addAction(QIcon(":img/openProj"), translations.TR_ADD_NEW_FOLDER) action_create_init = menu.addAction(translations.TR_CREATE_INIT) # self.connect(action_add_file, SIGNAL("triggered()"), # lambda: self.current_tree._add_new_file(path)) # self.connect(action_add_folder, SIGNAL("triggered()"), # lambda: self.current_tree._add_new_folder(path)) # self.connect(action_create_init, SIGNAL("triggered()"), # lambda: self.current_tree._create_init(path)) # menu.addSeparator() # actionRunProject = menu.addAction(QIcon( # ":img/play"), translations.TR_RUN_PROJECT) # self.connect(actionRunProject, SIGNAL("triggered()"), # self.current_tree._execute_project) # if self.current_tree._added_to_console: # actionRemoveFromConsole = menu.addAction( # translations.TR_REMOVE_PROJECT_FROM_PYTHON_CONSOLE) # self.connect(actionRemoveFromConsole, SIGNAL("triggered()"), # self.current_tree._remove_project_from_console) # else: # actionAdd2Console = menu.addAction( # translations.TR_ADD_PROJECT_TO_PYTHON_CONSOLE) # self.connect(actionAdd2Console, SIGNAL("triggered()"), # self.current_tree._add_project_to_console) # actionShowFileSizeInfo = menu.addAction( # translations.TR_SHOW_FILESIZE) # self.connect(actionShowFileSizeInfo, SIGNAL("triggered()"), # self.current_tree.show_filesize_info) # actionProperties = menu.addAction(QIcon(":img/pref"), # translations.TR_PROJECT_PROPERTIES) # self.connect(actionProperties, SIGNAL("triggered()"), # self.current_tree.open_project_properties) action_properties = menu.addAction(translations.TR_PROJECT_PROPERTIES) action_properties.triggered.connect( self.current_tree.open_project_properties) # menu.addSeparator() action_close = menu.addAction(translations.TR_CLOSE_PROJECT) action_add_file.triggered.connect( lambda: self.current_tree._add_new_file(path)) action_add_folder.triggered.connect( lambda: self.current_tree._add_new_folder(path)) action_close.triggered.connect(self.current_tree._close_project) # self.connect(action_close, SIGNAL("triggered()"), # self.current_tree._close_project) # menu for the project for m in self.current_tree.extra_menus_by_scope['project']: if isinstance(m, QMenu): menu.addSeparator() menu.addMenu(m) # show the menu! menu.exec_(QCursor.pos())
class 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 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? 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? # 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())