class abstract_treeview(QtWidgets.QTreeWidget): """Base class for the two treeview controls""" instanceSelected = QtCore.pyqtSignal([object]) instanceVisibilityChanged = QtCore.pyqtSignal([object, int]) instanceDisplayModeChanged = QtCore.pyqtSignal([object, int]) def __init__(self): QtWidgets.QTreeView.__init__(self) self.setColumnCount(len(self.ATTRIBUTES)) self.setHeaderLabels(self.ATTRIBUTES) self.children = defaultdict(list) def get_children(self, inst): c = [inst] i = 0 while i < len(c): c.extend(self.children[c[i]]) i += 1 return c def contextMenuEvent(self, event): menu = QtWidgets.QMenu(self) visibility = [menu.addAction("Show"), menu.addAction("Hide")] displaymode = [ menu.addAction("Solid"), menu.addAction("Wireframe") ] action = menu.exec_(self.mapToGlobal(event.pos())) index = self.selectionModel().currentIndex() inst = index.data(QtCore.Qt.UserRole) if hasattr(inst, "toPyObject"): inst = inst if action in visibility: self.instanceVisibilityChanged.emit(inst, visibility.index(action)) elif action in displaymode: self.instanceDisplayModeChanged.emit(inst, displaymode.index(action)) def clicked_(self, index): inst = index.data(QtCore.Qt.UserRole) if hasattr(inst, "toPyObject"): inst = inst if inst: self.instanceSelected.emit(inst) def select(self, product): itm = self.product_to_item.get(product) if itm is None: return self.selectionModel().setCurrentIndex( itm, QtCore.QItemSelectionModel.SelectCurrent | QtCore.QItemSelectionModel.Rows)
class window(QtWidgets.QMainWindow): TITLE = "IfcOpenShell IFC viewer" window_closed = QtCore.pyqtSignal([]) def __init__(self): QtWidgets.QMainWindow.__init__(self) self.setWindowTitle(self.TITLE) self.menu = self.menuBar() self.menus = {} def closeEvent(self, *args): self.window_closed.emit() def add_menu_item(self, menu, label, callback, icon=None, shortcut=None): m = self.menus.get(menu) if m is None: m = self.menu.addMenu(menu) self.menus[menu] = m if icon: a = QtWidgets.QAction(QtGui.QIcon(icon), label, self) else: a = QtWidgets.QAction(label, self) if shortcut: a.setShortcut(shortcut) a.triggered.connect(callback) m.addAction(a)
class CreateVirtualEnvThread(QtCore.QThread): """ Thread used to run the process that creates a new virtual env. """ #: Signal emitted when the virtual env has been created created = QtCore.pyqtSignal(str) #: Path of the environment to create path = '' #: Base interpreter, must be a system interpreter interpreter = '' #: True to enable system site-packages. Not recommended. system_site_packages = False def run(self): """ Creates the virtual env """ command = ['virtualenv', '-p', self.interpreter, self.path] if self.system_site_packages: command.insert(1, '--system-site-packages') command = ' '.join(command) if os.system(command) == 0: ext = '.exe' if WINDOWS else '' path = os.path.join(self.path, 'bin', 'python' + ext) else: path = None self.created.emit(path)
class FileDropWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(FileDropWidget, self).__init__(parent) self.setAcceptDrops(True) fileDropped = QtCore.pyqtSignal(str) def dragEnterEvent(self, event): if event.mimeData().hasUrls: event.accept() else: event.acceptProposedAction() def dragMoveEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() else: event.acceptProposedAction() def dropEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() for url in event.mimeData().urls(): self.fileDropped.emit(str(url.toLocalFile())) else: event.acceptProposedAction()
class geometry_creation_signals(QtCore.QObject): completed = QtCore.pyqtSignal("PyQt_PyObject") progress = QtCore.pyqtSignal("PyQt_PyObject")
class viewer(qtViewer3d): instanceSelected = QtCore.pyqtSignal([object]) # @staticmethod # def ais_to_key(ais_handle): # def yield_shapes(): # ais = ais_handle.GetObject() # if hasattr(ais, "Shape"): # yield ais.Shape() # return # shp = OCC.AIS.Handle_AIS_Shape.DownCast(ais_handle) # if not shp.IsNull(): # yield shp.Shape() # return # mult = ais_handle # if mult.IsNull(): # shp = OCC.AIS.Handle_AIS_Shape.DownCast(ais_handle) # if not shp.IsNull(): # yield shp # else: # li = mult.GetObject().ConnectedTo() # for i in range(li.Length()): # shp = OCC.AIS.Handle_AIS_Shape.DownCast(li.Value(i + 1)) # if not shp.IsNull(): # yield shp # return tuple(shp.HashCode(1 << 24) for shp in yield_shapes()) def __init__(self, widget): qtViewer3d.__init__(self, widget) self.ais_to_product = {} self.product_to_ais = {} self.counter = 0 self.window = widget self.thread = None def initialize(self): self.InitDriver() self._display.Select = self.HandleSelection def finished(self, file_shapes): it, f, shapes = file_shapes v = self._display t = {0: time.time()} def update(dt=None): t1 = time.time() if dt is None or t1 - t[0] > dt: v.FitAll() v.Repaint() t[0] = t1 for shape in shapes: ais = display_shape(shape, viewer_handle=v) product = f[shape.data.id] if USE_OCCT_HANDLE: ais.GetObject().SetSelectionPriority(self.counter) self.ais_to_product[self.counter] = product self.product_to_ais[product] = ais self.counter += 1 QtWidgets.QApplication.processEvents() if product.is_a() in {"IfcSpace", "IfcOpeningElement"}: v.Context.Erase(ais, True) update(1.0) update() self.thread = None def load_file(self, f, setting=None): if self.thread is not None: return if setting is None: setting = settings() setting.set(setting.INCLUDE_CURVES, True) setting.set(setting.USE_PYTHON_OPENCASCADE, True) self.signals = geometry_creation_signals() thread = self.thread = geometry_creation_thread( self.signals, setting, f) self.window.window_closed.connect(lambda *args: thread.terminate()) self.signals.completed.connect(self.finished) self.thread.start() def select(self, product): ais = self.product_to_ais.get(product) if ais is None: return v = self._display.Context v.ClearSelected(False) v.SetSelected(ais, True) def toggle(self, product_or_products, fn): if not isinstance(product_or_products, Iterable): product_or_products = [product_or_products] aiss = list( filter(None, map(self.product_to_ais.get, product_or_products))) last = len(aiss) - 1 for i, ais in enumerate(aiss): fn(ais, i == last) def toggle_visibility(self, product_or_products, flag): v = self._display.Context if flag: def visibility(ais, last): v.Erase(ais, last) else: def visibility(ais, last): v.Display(ais, last) self.toggle(product_or_products, visibility) def toggle_wireframe(self, product_or_products, flag): v = self._display.Context if flag: def wireframe(ais, last): if v.IsDisplayed(ais): v.SetDisplayMode(ais, 0, last) else: def wireframe(ais, last): if v.IsDisplayed(ais): v.SetDisplayMode(ais, 1, last) self.toggle(product_or_products, wireframe) def HandleSelection(self, X, Y): v = self._display.Context v.Select() v.InitSelected() if v.MoreSelected(): ais = v.SelectedInteractive() inst = self.ais_to_product[ais.GetObject().SelectionPriority()] self.instanceSelected.emit(inst)
class viewer(qtViewer3d): instanceSelected = QtCore.pyqtSignal([object]) @staticmethod def ais_to_key(ais_handle): def yield_shapes(): ais = ais_handle.GetObject() if hasattr(ais, 'Shape'): yield ais.Shape() return shp = OCC.AIS.Handle_AIS_Shape.DownCast(ais_handle) if not shp.IsNull(): yield shp.Shape() return mult = ais_handle if mult.IsNull(): shp = OCC.AIS.Handle_AIS_Shape.DownCast(ais_handle) if not shp.IsNull(): yield shp else: li = mult.GetObject().ConnectedTo() for i in range(li.Length()): shp = OCC.AIS.Handle_AIS_Shape.DownCast(li.Value(i + 1)) if not shp.IsNull(): yield shp return tuple(shp.HashCode(1 << 24) for shp in yield_shapes()) def __init__(self, widget): qtViewer3d.__init__(self, widget) self.ais_to_product = {} self.product_to_ais = {} self.counter = 0 self.window = widget def initialize(self): self.InitDriver() self._display.Select = self.HandleSelection def load_file(self, f, setting=None): if setting is None: setting = settings() setting.set(setting.USE_PYTHON_OPENCASCADE, True) v = self._display t = {0: time.time()} def update(dt=None): t1 = time.time() if t1 - t[0] > (dt or -1): v.FitAll() v.Repaint() t[0] = t1 terminate = [False] self.window.window_closed.connect( lambda *args: operator.setitem(terminate, 0, True)) t0 = time.time() it = iterator(setting, f) if not it.initialize(): return old_progress = -1 while True: if terminate[0]: break shape = it.get() product = f[shape.data.id] ais = display_shape(shape, viewer_handle=v) ais.GetObject().SetSelectionPriority(self.counter) self.ais_to_product[self.counter] = product self.product_to_ais[product] = ais self.counter += 1 QtWidgets.QApplication.processEvents() if product.is_a() in {'IfcSpace', 'IfcOpeningElement'}: v.Context.Erase(ais, True) progress = it.progress() // 2 if progress > old_progress: print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="") old_progress = progress if not it.next(): break update(0.2) print("\rOpened file in %.2f seconds%s" % (time.time() - t0, " " * 25)) update() def select(self, product): ais = self.product_to_ais.get(product) if ais is None: return v = self._display.Context v.ClearSelected(False) v.SetSelected(ais, True) def toggle(self, product_or_products, fn): if not isinstance(product_or_products, Iterable): product_or_products = [product_or_products] aiss = list( filter(None, map(self.product_to_ais.get, product_or_products))) last = len(aiss) - 1 for i, ais in enumerate(aiss): fn(ais, i == last) def toggle_visibility(self, product_or_products, flag): v = self._display.Context if flag: def visibility(ais, last): v.Erase(ais, last) else: def visibility(ais, last): v.Display(ais, last) self.toggle(product_or_products, visibility) def toggle_wireframe(self, product_or_products, flag): v = self._display.Context if flag: def wireframe(ais, last): if v.IsDisplayed(ais): v.SetDisplayMode(ais, 0, last) else: def wireframe(ais, last): if v.IsDisplayed(ais): v.SetDisplayMode(ais, 1, last) self.toggle(product_or_products, wireframe) def HandleSelection(self, X, Y): v = self._display.Context v.Select() v.InitSelected() if v.MoreSelected(): ais = v.SelectedInteractive() inst = self.ais_to_product[ais.GetObject().SelectionPriority()] self.instanceSelected.emit(inst)
class WindowBase(QtWidgets.QMainWindow): closed = QtCore.pyqtSignal(QtWidgets.QMainWindow) @property def app(self): """ :rtype: qidle.app.Application """ return self._app() def __init__(self, ui, app): super(WindowBase, self).__init__() self._quitting = False self._app = weakref.ref(app) self._height = None # path of the script, 'Untitled' if new file not saved to disk. self.path = None self.ui = ui self.ui.setupUi(self) # we need to set proper menu roles for OS X self.ui.actionAbout_QIdle.setMenuRole(QtWidgets.QAction.AboutRole) self.ui.actionQuit.setMenuRole(QtWidgets.QAction.QuitRole) if sys.platform == 'win32': self.ui.actionConfigure_IDLE.setText('Preferences') self.ui.actionConfigure_IDLE.setMenuRole( QtWidgets.QAction.PreferencesRole) self.ui.actionConfigureRun.setMenuRole(QtWidgets.QAction.NoRole) self._setup_mnu_file(app, ui) self._setup_windows_menu(ui) self._setup_help_menu() ui.actionConfigure_IDLE.triggered.connect(self.edit_preferences) self._setup_icons() self.dock_manager_right = DockManager(self) self.dock_manager_right.setObjectName('dockManagerRight') self.dock_manager_right.setWindowTitle('Dock manager right') self.addToolBar(QtCore.Qt.RightToolBarArea, self.dock_manager_right) self.dock_manager_bottom = DockManager(self) self.dock_manager_bottom.setObjectName('dockManagerBottom') self.dock_manager_bottom.setWindowTitle('Dock manager bottom') self.addToolBar(QtCore.Qt.BottomToolBarArea, self.dock_manager_bottom) def apply_preferences(self): raise NotImplementedError() def save(self): raise NotImplementedError() def save_as(self): raise NotImplementedError() def restore_state(self): raise NotImplementedError() def save_state(self): raise NotImplementedError() def update_recents_menu(self): self.menu_recents.update_actions() def current_interpreter(self): raise NotImplementedError() def edit_preferences(self): DlgPreferences.edit_preferences(self, callback=self.app.apply_preferences) def zoom_height(self): _logger(self).debug('zoom height') desktop = QtWidgets.QApplication.instance().desktop() if sys.platform == 'win32': difference = (self.frameGeometry().height() - self.geometry().height()) else: difference = 0 self.resize(self.width(), desktop.availableGeometry(self).height() - difference) self.move(self.pos().x(), 0) def update_windows_menu(self, open_windows): _logger(self).debug('update windows menu: %r' % open_windows) self._open_windows = open_windows self.ui.menuWindows.clear() self.ui.menuWindows.addAction(self.ui.actionZoom_height) self.ui.menuWindows.addSeparator() self.ui.menuWindows.addMenu(self.ui.menuTools) self.ui.menuWindows.addSeparator() for win in open_windows: action = QtWidgets.QAction(self) if win == self: action.setDisabled(True) action.setText(win.windowTitle()) action.setData(win) action.triggered.connect(self._show_window_from_action) self.ui.menuWindows.addAction(action) def _emit_closed(self): self.closed.emit(self) def _setup_icons(self): self.ui.actionNew_file.setIcon(icons.new_file) self.ui.actionNew_project.setIcon(icons.new_folder) self.ui.actionOpen_file.setIcon(icons.open_file) self.ui.actionOpen_directory.setIcon(icons.open_folder) self.ui.actionSave.setIcon(icons.save) self.ui.actionSave_as.setIcon(icons.save_as) self.ui.actionPython_docs.setIcon(icons.python_interpreter) self.ui.actionAbout_QIdle.setIcon(icons.help_about) self.ui.actionHelp_content.setIcon(icons.help_contents) self.ui.actionConfigure_IDLE.setIcon(icons.preferences) self.ui.actionConfigureRun.setIcon(icons.configure) self.ui.actionRun.setIcon(icons.run) self.ui.actionClose.setIcon(icons.window_close) self.ui.actionQuit.setIcon(icons.application_exit) def _setup_status_bar(self): self.lbl_cursor_pos = QtWidgets.QLabel() self.lbl_cursor_pos.setText('na') self.statusBar().addPermanentWidget(self.lbl_cursor_pos) self.lbl_encoding = QtWidgets.QLabel() self.lbl_encoding.setText('na') self.statusBar().addPermanentWidget(self.lbl_encoding) def _setup_mnu_file(self, app, ui): self.ui.menuFile.clear() self.ui.menuFile.addAction(self.ui.actionNew_file) self.ui.menuFile.addAction(self.ui.actionOpen_file) self.ui.menuFile.addSeparator() self.ui.menuFile.addAction(self.ui.actionNew_project) self.ui.menuFile.addAction(self.ui.actionOpen_directory) self.ui.menuFile.addSeparator() self.menu_recents = MenuRecentFiles(self, app.recent_files_manager, title='Recents', icon_provider=IconProvider()) self.menu_recents.open_requested.connect(self.app.open_recent) self.ui.menuFile.addMenu(self.menu_recents) self.ui.menuFile.addSeparator() self.ui.menuFile.addAction(self.ui.actionSave) self.ui.menuFile.addAction(self.ui.actionSave_as) self.ui.menuFile.addSeparator() self.ui.menuFile.addAction(self.ui.actionClose) self.ui.menuFile.addAction(self.ui.actionQuit) ui.actionNew_file.triggered.connect(self._on_new_file_triggered) ui.actionOpen_file.triggered.connect(self._on_open_file_triggered) ui.actionNew_project.triggered.connect(self._on_new_project_triggered) ui.actionOpen_directory.triggered.connect( self._on_open_project_triggered) ui.actionSave.triggered.connect(self.save) ui.actionSave_as.triggered.connect(self.save_as) self.ui.actionClose.triggered.connect(self._close) self.ui.actionQuit.triggered.connect(self._quit) def _setup_windows_menu(self, ui): ui.actionZoom_height.triggered.connect(self.zoom_height) ui.menuTools.addActions(self.createPopupMenu().actions()) action_prev_window = QtWidgets.QAction(self) action_prev_window.setShortcut('Alt+Up') action_prev_window.triggered.connect(self._show_prev_window) self.addAction(action_prev_window) action_next_window = QtWidgets.QAction(self) action_next_window.setShortcut('Alt+Down') action_next_window.triggered.connect(self._show_next_window) self.addAction(action_next_window) def _setup_help_menu(self): self.ui.actionPython_docs.triggered.connect(self._show_python_docs) def configure_shortcuts(self): self.addActions(self.ui.menuFile.actions()) # menu edit already added self.addActions(self.ui.menuRun.actions()) self.addActions(self.ui.menuOptions.actions()) self.addActions(self.ui.menuWindows.actions()) self.addActions(self.ui.menuHelp.actions()) key_bindings = Preferences().key_bindings.dict() for action in self.actions(): try: key_sequence = key_bindings[action.objectName()] except KeyError: pass else: action.setShortcut(key_sequence) def _show_window_from_action(self): window = self.sender().data() self.app.activate_window(window) def _on_new_file_triggered(self): _logger(self).debug('create new file') self.save_state() self.app.create_script_window() def _on_new_project_triggered(self): pass def _open_in_current_window(self, path, script): from qidle.windows.script import ScriptWindow if ((script and isinstance(self, ScriptWindow) or (not script and not isinstance(self, ScriptWindow)))): self.open(path) else: self._open_in_new_window(path, script) def _open_in_new_window(self, path, script): if script: self.app.create_script_window(path) else: self.app.create_project_window(path) def _on_open_file_triggered(self): path, filter = QtWidgets.QFileDialog.getOpenFileName( self, 'Open script', self.path, filter='Python files (*.py *.pyw)') if path: script = os.path.isfile(path) action = Preferences().general.open_scr_action if action == Preferences().general.OpenActions.NEW: self._open_in_new_window(path, script) elif action == Preferences().general.OpenActions.CURRENT: self._open_in_current_window(path, script) else: # ask val = DlgAskOpenScript.ask(self) if val == Preferences().general.OpenActions.NEW: self._open_in_new_window(path, script) elif val == Preferences().general.OpenActions.CURRENT: self._open_in_current_window(path, script) def _on_open_project_triggered(self): path = QtWidgets.QFileDialog.getExistingDirectory( self, 'Open project', os.path.expanduser('~')) if path: script = os.path.isfile(path) action = Preferences().general.open_project_action if action == Preferences().general.OpenActions.NEW: self._open_in_new_window(path, script) elif action == Preferences().general.OpenActions.CURRENT: self._open_in_current_window(path, script) else: # ask val = DlgAskOpenScript.ask(self) if val == Preferences().general.OpenActions.NEW: self._open_in_new_window(path, script) elif val == Preferences().general.OpenActions.CURRENT: self._open_in_current_window(path, script) def _show_prev_window(self): i = self.app.windows.index(self) i -= 1 if i < 0: i = len(self.app.windows) - 1 window = self.app.windows[i] self.app.activate_window(window) def _show_next_window(self): i = self.app.windows.index(self) i += 1 if i >= len(self.app.windows): i = 0 window = self.app.windows[i] self.app.activate_window(window) def _restart_backend(self, editor): """ Restart the backend process of `editor`. The goal is to :param editor: pyqode.core.api.CodeEdit or subclass """ editor.backend.start( editor.backend.server_script, self.current_interpreter(), [] if sys.executable == self.current_interpreter() else ['-s'] + [get_library_zip_path()]) # update checker modes for the new interpreter syntax. for mode in editor.modes: if isinstance(mode, CheckerMode): mode.request_analysis() def _show_python_docs(self): _logger(self).info('opening python docs in the default browser') QtGui.QDesktopServices.openUrl( QtCore.QUrl('https://docs.python.org/3/')) def quit_confirmation(self): if Preferences().general.confirm_application_exit and \ not self._quitting: button = QtWidgets.QMessageBox.question( self, "Confirm exit", "Are you sure you want to exit QIdle?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) res = button == QtWidgets.QMessageBox.Yes return res return True def _quit(self): if self.quit_confirmation(): self._quitting = True self.app.qapp.closeAllWindows() self._quitting = False def _close(self): # not sure why but if we don't do that using a timer we get a segfault self.close() def _apply_editor_preferences(self, editor, show_panels): _logger(self).info('applying preferences on editor: %s' % editor.file.path) prefs = Preferences() # appearance editor.font_name = prefs.editor_appearance.font editor.font_size = prefs.editor_appearance.font_size editor.show_whitespaces = prefs.editor_appearance.show_whitespaces scheme = ColorScheme(prefs.editor_appearance.color_scheme) editor.syntax_highlighter.color_scheme = scheme self.ui.textEditPgmOutput.apply_color_scheme(scheme) # editor settings editor.panels.get('FoldingPanel').highlight_caret_scope = \ prefs.editor.highlight_caret_scope editor.use_spaces_instead_of_tabs = \ prefs.editor.use_spaces_instead_of_tabs editor.modes.get('RightMarginMode').position = \ prefs.editor.margin_pos editor.tab_length = prefs.editor.tab_len editor.file.replace_tabs_by_spaces = \ prefs.editor.convert_tabs_to_spaces editor.file.clean_trailing_whitespaces = \ prefs.editor.clean_trailing editor.file.fold_imports = prefs.editor.fold_imports editor.file.fold_docstrings = prefs.editor.fold_docstrings editor.file.restore_cursor = prefs.editor.restore_cursor editor.file.safe_save = prefs.editor.safe_save mode = editor.modes.get('CodeCompletionMode') mode.trigger_length = prefs.editor.cc_trigger_len mode.show_tooltips = prefs.editor.cc_show_tooltips mode.case_sensitive = prefs.editor.cc_case_sensitive editor.setCenterOnScroll(prefs.editor.center_on_scroll) # modes for m in editor.modes: if m.name in prefs.editor.modes: m.enabled = prefs.editor.modes[m.name] else: m.enabled = True # disable unwanted panels for name, state in prefs.editor.panels.items(): try: panel = editor.panels.get(name) except KeyError: _logger().exception('failed to retrieve mode by name: %r' % name) else: if name not in commons.DYNAMIC_PANELS: if not show_panels and state is True: continue panel.setEnabled(state) panel.setVisible(state) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.path)
class QDbgConsole(QtWidgets.QTextEdit): """ A simple QTextEdit, with a few pre-set attributes and a file-like interface. """ log_signal = QtCore.pyqtSignal(str) error_signal = QtCore.pyqtSignal(str) def __init__(self, size=None, parent=None): super(QDbgConsole, self).__init__(parent) self._buffer = io.StringIO() if size: self.setMinimumSize(*size) self.setReadOnly(True) self.getting_input = False self.input_cursor_pos = None self.log_signal.connect(self.write) self.error_signal.connect( lambda msg: self.write(msg, text_color=QtGui.QColor(139, 0, 0))) def write(self, msg, text_color=QtGui.QColor(0, 0, 0)): """Add msg to the console's output, on a new line.""" self.setTextColor(text_color) self.insertPlainText(msg) # Autoscroll self.moveCursor(QtGui.QTextCursor.End) self._buffer.write(msg) def get_input(self, prompt=""): self.getting_input = True self.log_signal.emit(prompt) self.moveCursor(QtGui.QTextCursor.End) self.setReadOnly(False) self.input_cursor_pos = self.textCursor().position() + len(prompt) while self.getting_input: time.sleep(0.1) self.moveCursor(QtGui.QTextCursor.End) ret = self.toPlainText()[self.input_cursor_pos:].strip() return ret def keyPressEvent(self, event): if self.getting_input: if event.key() == QtCore.Qt.Key_Return: self.setReadOnly(True) self.write("\n") self.getting_input = False return if event.key() in [QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace]: if self.textCursor().position() <= self.input_cursor_pos: return super().keyPressEvent(event) self.moveCursor(QtGui.QTextCursor.End) def __getattr__(self, attr): """ Fall back to the buffer object if an attribute can't be found. """ return getattr(self._buffer, attr)
class Application(QtCore.QObject): """ Sets up the Qt Application, the main window and the various controllers. The application class contains references to the main window user interface and to the various controllers so that they can collaborate each others. """ _report_exception_requested = QtCore.pyqtSignal(object, str) def apply_mimetypes_preferences(self): for ext in Settings().all_extensions: mimetypes.add_type('text/x-cobol', ext) mimetypes.add_type('text/x-cobol', ext.upper()) def __init__(self, parse_args=True): super().__init__() if system.darwin: Application._osx_init() self._reported_tracebacks = [] self._old_except_hook = sys.excepthook sys.excepthook = self._except_hook self._report_exception_requested.connect(self._report_exception) if hasattr(sys, 'frozen') and sys.platform == 'win32': sys.stdout = open( os.path.join(system.get_cache_directory(), 'ocide_stdout.log'), 'w') sys.stderr = open( os.path.join(system.get_cache_directory(), 'ocide_stderr.log'), 'w') self.app = QtWidgets.QApplication(sys.argv) if parse_args and not system.darwin: args = self.parse_args() verbose = args.verbose files = args.files else: verbose = False files = [] logger.setup_logging(__version__, debug=verbose or Settings().verbose) self.name = 'OpenCobolIDE' self.version = __version__ self.title = '%s %s' % (self.name, self.version) self.apply_mimetypes_preferences() self.win = MainWindow() self.win.setWindowTitle(self.title) self.file = FileController(self) self.view = ViewController(self) self.home = HomeController(self) self.edit = EditController(self) self.cobol = CobolController(self) self.help = HelpController(self) self.view.show_perspective(Settings().perspective) self.view.show_home_page() self.update_app_style() try: check_compiler() except CompilerNotFound: msg = 'Failed to find a working GnuCOBOL compiler!\n' \ "The IDE will continue to work but you won't be able to " \ 'compile...' if system.windows: msg += '\n\nTip: Ensure that there is no additional ' \ 'installation of MinGW in %s:\MinGW' % sys.executable[0] QtWidgets.QMessageBox.warning( self.win, 'COBOL compiler not found or not working', msg) else: _logger().info('GnuCOBOL version: %s', GnuCobolCompiler.get_version()) # open specified files for f in files: self.file.open_file(f) def restart(self): """ Restarts the IDE. """ if hasattr(sys, 'frozen'): QtCore.QProcess.startDetached(sys.executable) else: QtCore.QProcess.startDetached(sys.executable, sys.argv) sys.exit(0) def close(self): self.view = None self.cobol = None self.edit = None self.file = None self.win = None self.home = None self.help = None def update_app_style(self): if Settings().dark_style: try: import qdarkstyle except ImportError: Settings().dark_style = False else: qt_api = os.environ[QT_API] if qt_api in PYQT5_API: qss = qdarkstyle.load_stylesheet_pyqt5() else: qss = qdarkstyle.load_stylesheet(qt_api in PYSIDE_API) self.app.setStyleSheet(qss) return self.app.setStyleSheet('') def run(self): """ Run the Qt main loop. """ if Settings().fullscreen: self.win.showFullScreen() else: self.win.showMaximized() return self.app.exec_() @staticmethod def _osx_init(): """ Mac OSX specific initialisation, adds missing path to the PATH environment variable (see github issue #40). """ # paths = [ '/bin', '/sbin', '/usr/bin', '/usr/sbin', '/usr/local/bin', '/usr/local/sbin', '/opt/bin', '/opt/sbin', '/opt/local/bin', '/opt/local/sbin' ] os.environ['PATH'] = ':'.join(paths) def exit(self): """ Closes all top level windows and quits the application (without prompting the user, if you need to prompt the user, use Application.file.quit()) """ self.app.closeAllWindows() self.close() def parse_args(self): parser = argparse.ArgumentParser( description='Simple and lightweight COBOL IDE.') parser.add_argument('files', type=str, nargs='*', help='List of files to open, if any') parser.add_argument('--verbose', dest='verbose', action='store_true', help='Verbose mode will enable debug and info ' 'messages to be shown in the application log') return parser.parse_args() def _except_hook(self, exc_type, exc_val, tb): tb = '\n'.join([ ''.join(traceback.format_tb(tb)), '{0}: {1}'.format(exc_type.__name__, exc_val) ]) # exception might come from another thread, use a signal # so that we can be sure we will show the bug report dialog from # the main gui thread. self._report_exception_requested.emit(exc_val, tb) def _report_exception(self, exc, tb): try: _logger().critical('unhandled exception:\n%s', tb) _tb = tb if isinstance(exc, UnicodeDecodeError): # This might be the same exception in the same file but at another position # in the stream _tb = tb.splitlines()[-4] if _tb in self._reported_tracebacks: return self._reported_tracebacks.append(_tb) title = '[Unhandled exception] %s' % exc.__class__.__name__ description = 'An unhandled exception has occured:\n\n'\ '%s\n\nWould like to send a bug report to the ' \ 'development team?' % tb answer = QtWidgets.QMessageBox.critical( self.win, title, description, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes) if answer == QtWidgets.QMessageBox.Yes: description = '## Steps to reproduce\n\nPLEASE DESCRIBE '\ 'THE CONTEXT OF THIS BUG AND THE STEPS TO REPRODUCE!\n\n'\ '## Traceback\n\n```\n%s\n```' % tb DlgReportBug.report_bug(self.win, title=title, description=description) except Exception: _logger().exception('exception in excepthook')
class FileBrowserDock(DockBase): """ FileBrowserDock(QtWidgets.QWidget parent = None, Function openFunc = None) params: parent - parent widget openFunction - function used for open file commands desc: The dockwidget containing all the elements of the of a file browser. """ pylint_finished = QtCore.pyqtSignal(WritableObject) def __init__(self, parent=None): super(FileBrowserDock, self).__init__(parent) self.initDock("File Browser") # make sure icons are loaded Icons.load() # self.openFunction = openFunc # tree view self.fileTree = widgets.FileSystemTreeView() self.fileTree.set_root_path(os.getcwd()) self.contextMenu = FileBrowserMenu() self.fileTree.set_context_menu(self.contextMenu) # enable drag and drop self.fileTree.setDragEnabled(True) self.fileTree.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop) self.fileTree.viewport().setAcceptDrops(True) self.fileTree.setDropIndicatorShown(True) # To Do -- not use a private member -- self.fileTree._fs_model_source.setReadOnly(False) self.fileTree.setEditTriggers( QtWidgets.QAbstractItemView.NoEditTriggers) self.fileTree.setDefaultDropAction(QtCore.Qt.MoveAction) # Folder button self.menuHLayout = QtWidgets.QHBoxLayout() self.menuHLayout.setContentsMargins(1, 1, 1, 1) self.menuHLayout.addStretch(1) self.menuBtn = QtWidgets.QPushButton(Icons.folder, "...", self) self.menuBtn.setFlat(True) self.menuBtn.setStyleSheet("background-color: " "rgba(255, 255, 255, 0);") self.menuHLayout.addWidget(self.menuBtn) self.menuVLayout = QtWidgets.QVBoxLayout() self.menuVLayout.setContentsMargins(1, 1, 1, 1) self.menuVLayout.addLayout(self.menuHLayout) self.menuVLayout.addStretch(1) self.fileTree.setLayout(self.menuVLayout) self.dirContextMenu = DirMenu() self.menuBtn.setMenu(self.dirContextMenu) self.dirContextMenu.parentDirAction.triggered.connect( self.onParentDirClicked) self.dirContextMenu.openDirAction.triggered.connect( self.onOpenDirClicked) # add everything to the dock self.contents = QtWidgets.QWidget() self.layout = QtWidgets.QGridLayout(self.contents) self.layout.addWidget(self.fileTree, 0, 0, 1, 1) self.setWidget(self.contents) self.fileTree.doubleClicked.connect(self.onOpenItem) self.contextMenu.OpenAction.triggered.connect(self.openFile) # self.contextMenu.pylintAction.triggered.connect(self.runPyLint) # self.hide() # global signal fileOpenRequest = QtCore.pyqtSignal(str) @QtCore.pyqtSlot() def openFile(self): """openFile() -- open selected file in editor.""" if self.fileTree is not None: urls = self.fileTree.helper.selected_urls() # Open all selected files for u in urls: if os.path.isfile(u): # make sure it is a file self.fileOpenRequest.emit(u) @QtCore.pyqtSlot(QtCore.QModelIndex) def onOpenItem(self, index): """ onOpenItem(index)""" # if self.openFunction != None: filePath = self.fileTree.filePath(index) self.fileOpenRequest.emit(filePath) # self.openFunction(filePath) @QtCore.pyqtSlot() def onParentDirClicked(self): """ Move file browser root up to parent directory.""" # There has to be a better way to do this # if not then shame on python path = utils.rectifyPath(self.fileTree.root_path) parPath = utils.getParentDir(path) if os.path.isdir(parPath): self.fileTree.set_root_path(parPath) @QtCore.pyqtSlot() def onOpenDirClicked(self): """ Open a new directory in the file browser""" # Open folder dialog -- only allow folders path = str( QtWidgets.QFileDialog.getExistingDirectory( self, "Select Directory", self.fileTree.root_path, QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog)) utils.rectifyPath(path) if os.path.isdir(path): self.fileTree.set_root_path(path)