class SingleApplication(QApplication): messageAvailable = pyqtSignal(type(u'')) def __init__(self, argv, key): QApplication.__init__(self, argv) self._key = key self._timeout = 1000 socket = QLocalSocket(self) socket.connectToServer(self._key) if socket.waitForConnected(self._timeout): self._isRunning = True socket.abort() return socket.abort() self._isRunning = False self._server = QLocalServer(self) self._server.newConnection.connect(self.__onNewConnection) self._server.listen(self._key) self.aboutToQuit.connect(self.__onAboutToQuit) def __onAboutToQuit(self): if self._server: self._server.close() self._server = None def __onNewConnection(self): socket = self._server.nextPendingConnection() if socket.waitForReadyRead(self._timeout): self.messageAvailable.emit(socket.readAll().data().decode('utf-8')) socket.disconnectFromServer() else: pass def isRunning(self): return self._isRunning def sendMessage(self, message): assert(self._isRunning) if self.isRunning(): socket = QLocalSocket(self) socket.connectToServer(self._key, QIODevice.WriteOnly) if not socket.waitForConnected(self._timeout): return False socket.write(message.encode('utf-8')) if not socket.waitForBytesWritten(self._timeout): return False socket.disconnectFromServer() return True return False
class SingleApplication(QApplication): messageAvailable = pyqtSignal(type(u'')) def __init__(self, argv, key): QApplication.__init__(self, argv) self._key = key self._timeout = 1000 socket = QLocalSocket(self) socket.connectToServer(self._key) if socket.waitForConnected(self._timeout): self._isRunning = True socket.abort() return socket.abort() self._isRunning = False self._server = QLocalServer(self) self._server.newConnection.connect(self.__onNewConnection) self._server.listen(self._key) self.aboutToQuit.connect(self.__onAboutToQuit) def __onAboutToQuit(self): if self._server: self._server.close() self._server = None def __onNewConnection(self): socket = self._server.nextPendingConnection() if socket.waitForReadyRead(self._timeout): self.messageAvailable.emit(socket.readAll().data().decode('utf-8')) socket.disconnectFromServer() else: pass def isRunning(self): return self._isRunning def sendMessage(self, message): assert (self._isRunning) if self.isRunning(): socket = QLocalSocket(self) socket.connectToServer(self._key, QIODevice.WriteOnly) if not socket.waitForConnected(self._timeout): return False socket.write(message.encode('utf-8')) if not socket.waitForBytesWritten(self._timeout): return False socket.disconnectFromServer() return True return False
def main(): app = QtGui.QApplication(sys.argv) androidToolsMainWin = AndroidToolsMainWindow() uiMainWidget = Ui_MainWidget() winOsArgv = WinCommandEnCoding.getOsArgv() # single QApplication solution # http://blog.csdn.net/softdzf/article/details/6704187 serverName = 'AndroidToolsServer' clientSocket = QLocalSocket() clientSocket.connectToServer(serverName) QSettingsUtil.init() # 如果连接成功, 表明server 已经存在,当前已经有实例在运行, 将参数发送给服务端 if clientSocket.waitForConnected(500): # print u'连接成功 arg = ', winOsArgv stream = QtCore.QTextStream(clientSocket) # for i in range(0, len(winOsArgv)): # stream << winOsArgv[i] # 对于打开终端来说,所携带参数为第1位(打开文件的地址),第0位为本执行程序地址 if len(winOsArgv) > 1: stream << winOsArgv[1] stream.setCodec('UTF-8') stream.flush() clientSocket.waitForBytesWritten() # close client socket clientSocket.close() return app.quit() # 如果没有实例执行,创建服务器 localServer = QLocalServer() # 一直监听端口 localServer.listen(serverName) # create db createDb() try: uiMainWidget.setupUi(androidToolsMainWin, localServer, winOsArgv) androidToolsMainWin.show() sys.exit(app.exec_()) finally: localServer.close()
def main(): app = QtGui.QApplication(sys.argv) ffstoreMainWin = FFStoreMainWindow() # set skin styleSheet # SkinHelper().setStyle(app, ':/qss/white_style.qss') SkinHelper().setStyle(app, ':/qss/dark_style.qss') # single QApplication solution # http://blog.csdn.net/softdzf/article/details/6704187 serverName = 'FFStoreManagerServer' clientSocket = QLocalSocket() clientSocket.connectToServer(serverName) # 如果连接成功, 表明server 已经存在,当前已经有实例在运行, 将参数发送给服务端 if clientSocket.waitForConnected(500): # print u'连接成功 arg = ', winOsArgv stream = QtCore.QTextStream(clientSocket) # for i in range(0, len(winOsArgv)): # stream << winOsArgv[i] # 对于打开终端来说,所携带参数为第1位(打开文件的地址),第0位为本执行程序地址 # close client socket clientSocket.close() return app.quit() # 如果没有实例执行,创建服务器 localServer = QLocalServer() # 一直监听端口 localServer.listen(serverName) # 初始化全局变量 GlobalVar.init() try: showWindowLogic = ShowWindowLogic(mainWindow=ffstoreMainWin, localServer=localServer) showWindowLogic.show() # uiMainWidget.setupUi(mainWindow=ffstoreMainWin, localServer=localServer) # ffstoreMainWin.show() sys.exit(app.exec_()) finally: localServer.close()
class __IDE(QMainWindow): ############################################################################### # SIGNALS # # goingDown() ############################################################################### def __init__(self, start_server=False): QMainWindow.__init__(self) self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') self.setMinimumSize(700, 500) #Load the size and the position of the main window self.load_window_geometry() #Start server if needed self.s_listener = None if start_server: self.s_listener = QLocalServer() self.s_listener.listen("ninja_ide") self.connect(self.s_listener, SIGNAL("newConnection()"), self._process_connection) #Profile handler self.profile = None #Opacity self.opacity = settings.MAX_OPACITY #Define Actions object before the UI self.actions = actions.Actions() #StatusBar self.status = status_bar.StatusBar(self) self.status.hide() self.setStatusBar(self.status) #Main Widget - Create first than everything else self.central = central_widget.CentralWidget(self) self.load_ui(self.central) self.setCentralWidget(self.central) #ToolBar self.toolbar = QToolBar(self) self.toolbar.setToolTip(self.tr("Press and Drag to Move")) self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) self.addToolBar(settings.TOOLBAR_AREA, self.toolbar) if settings.HIDE_TOOLBAR: self.toolbar.hide() #Install Shortcuts after the UI has been initialized self.actions.install_shortcuts(self) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.actions.update_explorer) #Menu menubar = self.menuBar() file_ = menubar.addMenu(self.tr("&File")) edit = menubar.addMenu(self.tr("&Edit")) view = menubar.addMenu(self.tr("&View")) source = menubar.addMenu(self.tr("&Source")) project = menubar.addMenu(self.tr("&Project")) self.pluginsMenu = menubar.addMenu(self.tr("&Addins")) about = menubar.addMenu(self.tr("Abou&t")) #The order of the icons in the toolbar is defined by this calls self._menuFile = menu_file.MenuFile(file_, self.toolbar, self) self._menuView = menu_view.MenuView(view, self.toolbar, self) self._menuEdit = menu_edit.MenuEdit(edit, self.toolbar) self._menuSource = menu_source.MenuSource(source) self._menuProject = menu_project.MenuProject(project, self.toolbar) self._menuPlugins = menu_plugins.MenuPlugins(self.pluginsMenu) self._menuAbout = menu_about.MenuAbout(about) self.load_toolbar() #Plugin Manager services = { 'editor': plugin_services.MainService(), 'toolbar': plugin_services.ToolbarService(self.toolbar), 'menuApp': plugin_services.MenuAppService(self.pluginsMenu), 'explorer': plugin_services.ExplorerService(), 'misc': plugin_services.MiscContainerService(self.misc) } serviceLocator = plugin_manager.ServiceLocator(services) self.plugin_manager = plugin_manager.PluginManager( resources.PLUGINS, serviceLocator) self.plugin_manager.discover() #load all plugins! self.plugin_manager.load_all() #Tray Icon self.trayIcon = updates.TrayIconUpdates(self) self.trayIcon.show() self.connect(self._menuFile, SIGNAL("openFile(QString)"), self.mainContainer.open_file) self.connect(self.mainContainer, SIGNAL("fileSaved(QString)"), self.show_status_message) self.connect(self.mainContainer, SIGNAL("recentTabsModified(QStringList)"), self._menuFile.update_recent_files) def _process_connection(self): connection = self.s_listener.nextPendingConnection() connection.waitForReadyRead() data = connection.readAll() connection.close() if data: files, projects = data.split(ipc.project_delimiter, 1) files = map(lambda x: (x.split(':')[0], int(x.split(':')[1])), files.split(ipc.file_delimiter)) projects = projects.split(ipc.project_delimiter) self.load_session_files_projects(files, [], projects, None) def load_toolbar(self): self.toolbar.clear() toolbar_items = {} toolbar_items.update(self._menuFile.toolbar_items) toolbar_items.update(self._menuView.toolbar_items) toolbar_items.update(self._menuEdit.toolbar_items) toolbar_items.update(self._menuSource.toolbar_items) toolbar_items.update(self._menuProject.toolbar_items) for item in settings.TOOLBAR_ITEMS: if item == 'separator': self.toolbar.addSeparator() else: tool_item = toolbar_items.get(item, None) if tool_item is not None: self.toolbar.addAction(tool_item) #load action added by plugins, This is a special case when reload #the toolbar after save the preferences widget for toolbar_action in settings.get_toolbar_item_for_plugins(): self.toolbar.addAction(toolbar_action) def load_external_plugins(self, paths): for path in paths: self.plugin_manager.add_plugin_dir(path) #load all plugins! self.plugin_manager.discover() self.plugin_manager.load_all() def show_status_message(self, message): self.status.showMessage(message, 2000) def load_ui(self, centralWidget): #Set Application Font for ToolTips QToolTip.setFont(QFont(settings.FONT_FAMILY, 10)) #Create Main Container to manage Tabs self.mainContainer = main_container.MainContainer(self) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.change_window_title) self.connect(self.mainContainer, SIGNAL("locateFunction(QString, QString, bool)"), self.actions.locate_function) self.connect(self.mainContainer, SIGNAL("navigateCode(bool, int)"), self.actions.navigate_code_history) self.connect(self.mainContainer, SIGNAL("addBackItemNavigation()"), self.actions.add_back_item_navigation) self.connect(self.mainContainer, SIGNAL("updateFileMetadata()"), self.actions.update_explorer) self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"), self.actions.update_explorer) self.connect(self.mainContainer, SIGNAL("openPreferences()"), self._show_preferences) self.connect(self.mainContainer, SIGNAL("dontOpenStartPage()"), self._dont_show_start_page_again) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.status.handle_tab_changed) # Update symbols self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"), self.status.explore_file_code) #Create Explorer Panel self.explorer = explorer_container.ExplorerContainer(self) self.connect(self.central, SIGNAL("splitterCentralRotated()"), self.explorer.rotate_tab_position) self.connect(self.explorer, SIGNAL("updateLocator()"), self.status.explore_code) self.connect(self.explorer, SIGNAL("goToDefinition(int)"), self.actions.editor_go_to_line) self.connect(self.explorer, SIGNAL("projectClosed(QString)"), self.actions.close_files_from_project) #Create Misc Bottom Container self.misc = misc_container.MiscContainer(self) self.connect(self.mainContainer, SIGNAL("findOcurrences(QString)"), self.misc.show_find_occurrences) centralWidget.insert_central_container(self.mainContainer) centralWidget.insert_lateral_container(self.explorer) centralWidget.insert_bottom_container(self.misc) self.connect(self.mainContainer, SIGNAL("cursorPositionChange(int, int)"), self.central.lateralPanel.update_line_col) # TODO: Change current symbol on move #self.connect(self.mainContainer, #SIGNAL("cursorPositionChange(int, int)"), #self.explorer.update_current_symbol) self.connect(self.mainContainer, SIGNAL("enabledFollowMode(bool)"), self.central.enable_follow_mode_scrollbar) if settings.SHOW_START_PAGE: self.mainContainer.show_start_page() def _show_preferences(self): pref = preferences.PreferencesWidget(self.mainContainer) pref.show() def _dont_show_start_page_again(self): settings.SHOW_START_PAGE = False qsettings = QSettings() qsettings.beginGroup('preferences') qsettings.beginGroup('general') qsettings.setValue('showStartPage', settings.SHOW_START_PAGE) qsettings.endGroup() qsettings.endGroup() self.mainContainer.actualTab.close_tab() def load_session_files_projects(self, filesTab1, filesTab2, projects, current_file, recent_files=None): self.mainContainer.open_files(filesTab1, notIDEStart=False) self.mainContainer.open_files(filesTab2, mainTab=False, notIDEStart=False) self.explorer.open_session_projects(projects, notIDEStart=False) if current_file: self.mainContainer.open_file(current_file, notStart=False) if recent_files is not None: self._menuFile.update_recent_files(recent_files) def open_file(self, filename): if filename: self.mainContainer.open_file(filename) def open_project(self, project): if project: self.actions.open_project(project) def __get_profile(self): return self.profile def __set_profile(self, profileName): self.profile = profileName if self.profile is not None: self.setWindowTitle('NINJA-IDE (PROFILE: %s)' % self.profile) else: self.setWindowTitle( 'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') Profile = property(__get_profile, __set_profile) def change_window_title(self, title): if self.profile is None: self.setWindowTitle('NINJA-IDE - %s' % title) else: self.setWindowTitle('NINJA-IDE (PROFILE: %s) - %s' % (self.profile, title)) currentEditor = self.mainContainer.get_actual_editor() if currentEditor is not None: line = currentEditor.textCursor().blockNumber() + 1 col = currentEditor.textCursor().columnNumber() self.central.lateralPanel.update_line_col(line, col) def wheelEvent(self, event): if event.modifiers() == Qt.ShiftModifier: if event.delta() == 120 and self.opacity < settings.MAX_OPACITY: self.opacity += 0.1 elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY: self.opacity -= 0.1 self.setWindowOpacity(self.opacity) event.ignore() else: QMainWindow.wheelEvent(self, event) def save_settings(self): """Save the settings before the application is closed with QSettings. Info saved: Tabs and projects opened, windows state(size and position). """ qsettings = QSettings() editor_widget = self.mainContainer.get_actual_editor() current_file = '' if editor_widget is not None: current_file = editor_widget.ID if qsettings.value('preferences/general/loadFiles', 'true') == 'true': openedFiles = self.mainContainer.get_opened_documents() projects_obj = self.explorer.get_opened_projects() projects = [p.path for p in projects_obj] qsettings.setValue('openFiles/projects', projects) if len(openedFiles) > 0: qsettings.setValue('openFiles/mainTab', openedFiles[0]) if len(openedFiles) == 2: qsettings.setValue('openFiles/secondaryTab', openedFiles[1]) qsettings.setValue('openFiles/currentFile', current_file) qsettings.setValue( 'openFiles/recentFiles', self.mainContainer._tabMain.get_recent_files_list()) qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS) qsettings.setValue('preferences/editor/breakpoints', settings.BREAKPOINTS) qsettings.setValue('preferences/general/toolbarArea', self.toolBarArea(self.toolbar)) #Save if the windows state is maximixed if (self.isMaximized()): qsettings.setValue("window/maximized", True) else: qsettings.setValue("window/maximized", False) #Save the size and position of the mainwindow qsettings.setValue("window/size", self.size()) qsettings.setValue("window/pos", self.pos()) #Save the size of de splitters qsettings.setValue("window/central/areaSize", self.central.get_area_sizes()) qsettings.setValue("window/central/mainSize", self.central.get_main_sizes()) #Save the toolbar visibility qsettings.setValue("window/hide_toolbar", not self.toolbar.isVisible()) #Save Profiles if self.profile is not None: self.actions.save_profile(self.profile) else: qsettings.setValue('ide/profiles', settings.PROFILES) def load_window_geometry(self): """Load from QSettings the window size of de Ninja IDE""" qsettings = QSettings() if qsettings.value("window/maximized", 'true') == 'true': self.setWindowState(Qt.WindowMaximized) else: print dir(QSizeF) self.resize( qsettings.value("window/size", QSizeF(800, 600)).toSize()) self.move( qsettings.value("window/pos", QPointF(100, 100)).toPoint()) def closeEvent(self, event): if self.s_listener: self.s_listener.close() if settings.CONFIRM_EXIT and \ self.mainContainer.check_for_unsaved_tabs(): unsaved_files = self.mainContainer.get_unsaved_files() txt = '\n'.join(unsaved_files) val = QMessageBox.question( self, self.tr("Some changes were not saved"), self.tr("%s\n\nDo you want to exit anyway?" % txt), QMessageBox.Yes, QMessageBox.No) if val == QMessageBox.No: event.ignore() QApplication.instance().setCursorFlashTime(cursor_flash_time) self.emit(SIGNAL("goingDown()")) self.save_settings() completion_daemon.shutdown_daemon() #close python documentation server (if running) self.mainContainer.close_python_doc() #Shutdown PluginManager self.plugin_manager.shutdown() def notify_plugin_errors(self): errors = self.plugin_manager.errors if errors: plugin_error_dialog = traceback_widget.PluginErrorDialog() for err_tuple in errors: plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1]) #show the dialog plugin_error_dialog.exec_()
class IDE(QMainWindow): """This class is like the Sauron's Ring: One ring to rule them all, One ring to find them, One ring to bring them all and in the darkness bind them. This Class knows all the containers, and its know by all the containers, but the containers don't need to know between each other, in this way we can keep a better api without the need to tie the behaviour between the widgets, and let them just consume the 'actions' they need.""" ############################################################################### # SIGNALS # # goingDown() ############################################################################### __IDESERVICES = {} __IDECONNECTIONS = {} __IDESHORTCUTS = {} __IDEBARCATEGORIES = {} __IDEMENUS = {} __IDETOOLBAR = {} # CONNECTIONS structure: # ({'target': service_name, 'signal_name': string, 'slot': function_obj},) # On modify add: {connected: True} __instance = None __created = False def __init__(self, start_server=False): QMainWindow.__init__(self) self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') self.setMinimumSize(750, 500) QToolTip.setFont(QFont(settings.FONT.family(), 10)) #Load the size and the position of the main window self.load_window_geometry() self.__project_to_open = 0 #Editables self.__neditables = {} #Filesystem self.filesystem = nfilesystem.NVirtualFileSystem() #Start server if needed self.s_listener = None if start_server: self.s_listener = QLocalServer() self.s_listener.listen("ninja_ide") self.connect(self.s_listener, SIGNAL("newConnection()"), self._process_connection) #Sessions handler self._session = None #Opacity self.opacity = settings.MAX_OPACITY #ToolBar self.toolbar = QToolBar(self) if settings.IS_MAC_OS: self.toolbar.setIconSize(QSize(36, 36)) else: self.toolbar.setIconSize(QSize(24, 24)) self.toolbar.setToolTip(translations.TR_IDE_TOOLBAR_TOOLTIP) self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) # Set toggleViewAction text and tooltip self.toggleView = self.toolbar.toggleViewAction() self.toggleView.setText(translations.TR_TOOLBAR_VISIBILITY) self.toggleView.setToolTip(translations.TR_TOOLBAR_VISIBILITY) self.addToolBar(settings.TOOLBAR_AREA, self.toolbar) if settings.HIDE_TOOLBAR: self.toolbar.hide() #Notificator self.notification = notification.Notification(self) #Plugin Manager # CHECK ACTIVATE PLUGINS SETTING #services = { #'editor': plugin_services.MainService(), #'toolbar': plugin_services.ToolbarService(self.toolbar), ##'menuApp': plugin_services.MenuAppService(self.pluginsMenu), #'menuApp': plugin_services.MenuAppService(None), #'explorer': plugin_services.ExplorerService(), #'misc': plugin_services.MiscContainerService(self.misc)} #serviceLocator = plugin_manager.ServiceLocator(services) serviceLocator = plugin_manager.ServiceLocator(None) self.plugin_manager = plugin_manager.PluginManager(resources.PLUGINS, serviceLocator) self.plugin_manager.discover() #load all plugins! self.plugin_manager.load_all() #Tray Icon self.trayIcon = updates.TrayIconUpdates(self) self.connect(self.trayIcon, SIGNAL("closeTrayIcon()"), self._close_tray_icon) self.trayIcon.show() key = Qt.Key_1 for i in range(10): if settings.IS_MAC_OS: short = ui_tools.TabShortcuts( QKeySequence(Qt.CTRL + Qt.ALT + key), self, i) else: short = ui_tools.TabShortcuts( QKeySequence(Qt.ALT + key), self, i) key += 1 self.connect(short, SIGNAL("activated()"), self._change_tab_index) short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self, 10) self.connect(short, SIGNAL("activated()"), self._change_tab_index) # Register menu categories IDE.register_bar_category(translations.TR_MENU_FILE, 100) IDE.register_bar_category(translations.TR_MENU_EDIT, 110) IDE.register_bar_category(translations.TR_MENU_VIEW, 120) IDE.register_bar_category(translations.TR_MENU_SOURCE, 130) IDE.register_bar_category(translations.TR_MENU_PROJECT, 140) IDE.register_bar_category(translations.TR_MENU_EXTENSIONS, 150) IDE.register_bar_category(translations.TR_MENU_ABOUT, 160) # Register General Menu Items ui_tools.install_shortcuts(self, actions.ACTIONS_GENERAL, self) self.register_service('ide', self) self.register_service('toolbar', self.toolbar) #Register signals connections connections = ( {'target': 'main_container', 'signal_name': 'fileSaved(QString)', 'slot': self.show_message}, {'target': 'main_container', 'signal_name': 'currentEditorChanged(QString)', 'slot': self.change_window_title}, {'target': 'main_container', 'signal_name': 'openPreferences()', 'slot': self.show_preferences}, {'target': 'main_container', 'signal_name': 'allTabsClosed()', 'slot': self._last_tab_closed}, {'target': 'explorer_container', 'signal_name': 'changeWindowTitle(QString)', 'slot': self.change_window_title}, {'target': 'explorer_container', 'signal_name': 'projectClosed(QString)', 'slot': self.close_project}, ) self.register_signals('ide', connections) # Central Widget MUST always exists self.central = IDE.get_service('central_container') self.setCentralWidget(self.central) # Install Services for service_name in self.__IDESERVICES: self.install_service(service_name) IDE.__created = True menu_bar = IDE.get_service('menu_bar') if menu_bar: menu_bar.load_menu(self) #These two are the same service, I think that's ok menu_bar.load_toolbar(self) IDE.__instance = self @classmethod def get_service(cls, service_name): """Return the instance of a registered service.""" return cls.__IDESERVICES.get(service_name, None) def get_menuitems(self): """Return a dictionary with the registered menu items.""" return IDE.__IDEMENUS def get_bar_categories(self): """Get the registered Categories for the Application menus.""" return IDE.__IDEBARCATEGORIES def get_toolbaritems(self): """Return a dictionary with the registered menu items.""" return IDE.__IDETOOLBAR @classmethod def register_service(cls, service_name, obj): """Register a service providing the service name and the instance.""" cls.__IDESERVICES[service_name] = obj if cls.__created: cls.__instance.install_service(service_name) def install_service(self, service_name): """Activate the registered service.""" obj = IDE.__IDESERVICES.get(service_name, None) func = getattr(obj, 'install', None) if isinstance(func, collections.Callable): func() self._connect_signals() def place_me_on(self, name, obj, region="central", top=False): """Place a widget in some of the areas in the IDE. @name: id to access to that widget later if needed. @obj: the instance of the widget to be placed. @region: the area where to put the widget [central, lateral] @top: place the widget as the first item in the split.""" self.central.add_to_region(name, obj, region, top) @classmethod def register_signals(cls, service_name, connections): """Register all the signals that a particular service wants to be attached of. @service_name: id of the service @connections: list of dictionaries for the connection with: - 'target': 'the_other_service_name', - 'signal_name': 'name of the signal in the other service', - 'slot': function object in this service""" cls.__IDECONNECTIONS[service_name] = connections if cls.__created: cls.__instance._connect_signals() def _connect_signals(self): """Connect the signals between the different services.""" for service_name in IDE.__IDECONNECTIONS: connections = IDE.__IDECONNECTIONS[service_name] for connection in connections: if connection.get('connected', False): continue target = IDE.__IDESERVICES.get( connection['target'], None) slot = connection['slot'] signal_name = connection['signal_name'] if target and isinstance(slot, collections.Callable): self.connect(target, SIGNAL(signal_name), slot) connection['connected'] = True @classmethod def register_shortcut(cls, shortcut_name, shortcut, action=None): """Register a shortcut and action.""" cls.__IDESHORTCUTS[shortcut_name] = (shortcut, action) @classmethod def register_menuitem(cls, menu_action, section, weight): """Register a QAction or QMenu in the IDE to be loaded later in the menubar using the section(string) to define where is going to be contained, and the weight define the order where is going to be placed. @menu_action: QAction or QMenu @section: String (name) @weight: int""" cls.__IDEMENUS[menu_action] = (section, weight) @classmethod def register_toolbar(cls, action, section, weight): """Register a QAction in the IDE to be loaded later in the toolbar using the section(string) to define where is going to be contained, and the weight define the order where is going to be placed. @action: QAction @section: String (name) @weight: int""" cls.__IDETOOLBAR[action] = (section, weight) @classmethod def register_bar_category(cls, category_name, weight): """Register a Menu Category to be created with the proper weight. @category_name: string @weight: int""" cls.__IDEBARCATEGORIES[category_name] = weight @classmethod def update_shortcut(cls, shortcut_name): """Update all the shortcuts of the application.""" short = resources.get_shortcut shortcut, action = cls.__IDESHORTCUTS.get(shortcut_name) if shortcut: shortcut.setKey(short(shortcut_name)) if action: action.setShortcut(short(shortcut_name)) def get_or_create_nfile(self, filename): """For convenience access to files from ide""" return self.filesystem.get_file(nfile_path=filename) def get_or_create_editable(self, filename="", nfile=None): if nfile is None: nfile = self.filesystem.get_file(nfile_path=filename) editable = self.__neditables.get(nfile) if editable is None: editable = neditable.NEditable(nfile) self.connect(editable, SIGNAL("fileClosing(PyQt_PyObject)"), self._unload_neditable) self.__neditables[nfile] = editable return editable def _unload_neditable(self, editable): self.__neditables.pop(editable.nfile) editable.nfile.deleteLater() editable.editor.deleteLater() editable.deleteLater() @property def opened_files(self): return tuple(self.__neditables.keys()) def get_project_for_file(self, filename): project = None if filename: project = self.filesystem.get_project_for_file(filename) return project def create_project(self, path): nproj = nproject.NProject(path) self.filesystem.open_project(nproj) return nproj def close_project(self, project_path): self.filesystem.close_project(project_path) def get_projects(self): return self.filesystem.get_projects() def get_current_project(self): current_project = None projects = self.filesystem.get_projects() for project in projects: if projects[project].is_current: current_project = projects[project] break return current_project @classmethod def select_current(cls, widget): """Show the widget with a 4px lightblue border line.""" widget.setProperty("highlight", True) widget.style().unpolish(widget) widget.style().polish(widget) @classmethod def unselect_current(cls, widget): """Remove the 4px lightblue border line from the widget.""" widget.setProperty("highlight", False) widget.style().unpolish(widget) widget.style().polish(widget) def _close_tray_icon(self): """Close the System Tray Icon.""" self.trayIcon.hide() self.trayIcon.deleteLater() def _change_tab_index(self): """Change the tabs of the current TabWidget using alt+numbers.""" widget = QApplication.focusWidget() shortcut_index = getattr(widget, 'shortcut_index', None) if shortcut_index: obj = self.sender() shortcut_index(obj.index) def _process_connection(self): """Read the ipc input from another instance of ninja.""" connection = self.s_listener.nextPendingConnection() connection.waitForReadyRead() data = connection.readAll() connection.close() if data: files, projects = str(data).split(ipc.project_delimiter, 1) files = [(x.split(':')[0], int(x.split(':')[1])) for x in files.split(ipc.file_delimiter)] projects = projects.split(ipc.project_delimiter) self.load_session_files_projects(files, [], projects, None) def fullscreen_mode(self): """Change to fullscreen mode.""" if self.isFullScreen(): self.showMaximized() else: self.showFullScreen() def change_toolbar_visibility(self): """Switch the toolbar visibility""" if self.toolbar.isVisible(): self.toolbar.hide() else: self.toolbar.show() def load_external_plugins(self, paths): """Load external plugins, the ones added to ninja throw the cmd.""" for path in paths: self.plugin_manager.add_plugin_dir(path) #load all plugins! self.plugin_manager.discover() self.plugin_manager.load_all() def _last_tab_closed(self): """ Called when the last tasb is closed """ self.explorer.cleanup_tabs() def show_preferences(self): """Open the Preferences Dialog.""" pref = preferences.Preferences(self) pref.show() def load_session_files_projects(self, files, projects, current_file, recent_files=None): """Load the files and projects from previous session.""" main_container = IDE.get_service('main_container') projects_explorer = IDE.get_service('projects_explorer') if main_container and files: for fileData in files: if file_manager.file_exists(fileData[0]): mtime = os.stat(fileData[0]).st_mtime ignore_checkers = (mtime == fileData[2]) main_container.open_file(fileData[0], fileData[1], ignore_checkers=ignore_checkers) if current_file: main_container.open_file(current_file) if projects_explorer and projects: projects_explorer.load_session_projects(projects) #if recent_files is not None: #menu_file = IDE.get_service('menu_file') #menu_file.update_recent_files(recent_files) #def _set_editors_project_data(self): #self.__project_to_open -= 1 #if self.__project_to_open == 0: #self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"), #self._set_editors_project_data) #self.mainContainer.update_editor_project() #def open_file(self, filename): #if filename: #self.mainContainer.open_file(filename) #def open_project(self, project): #if project: #self.actions.open_project(project) def __get_session(self): return self._session def __set_session(self, sessionName): self._session = sessionName if self._session is not None: self.setWindowTitle(translations.TR_SESSION_IDE_HEADER % {'session': self._session}) else: self.setWindowTitle( 'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') Session = property(__get_session, __set_session) def change_window_title(self, title): """Change the title of the Application.""" if self._session is None: self.setWindowTitle('NINJA-IDE - %s' % title) else: self.setWindowTitle((translations.TR_SESSION_IDE_HEADER % {'session': self._session}) + ' - %s' % title) def wheelEvent(self, event): """Change the opacity of the application.""" if event.modifiers() == Qt.ShiftModifier: if event.delta() == 120 and self.opacity < settings.MAX_OPACITY: self.opacity += 0.1 elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY: self.opacity -= 0.1 self.setWindowOpacity(self.opacity) event.ignore() else: QMainWindow.wheelEvent(self, event) @classmethod def ninja_settings(cls): qsettings = nsettings.NSettings(resources.SETTINGS_PATH, prefix="ns") if cls.__created: cls.__instance.connect( qsettings, SIGNAL("valueChanged(QString, PyQt_PyObject)"), cls.__instance._settings_value_changed) return qsettings @classmethod def data_settings(cls): qsettings = nsettings.NSettings(resources.DATA_SETTINGS_PATH, prefix="ds") if cls.__created: cls.__instance.connect( qsettings, SIGNAL("valueChanged(QString, PyQt_PyObject)"), cls.__instance._settings_value_changed) return qsettings def _settings_value_changed(self, key, value): signal_name = "%s(PyQt_PyObject)" % key.replace("/", "_") self.emit(SIGNAL(signal_name), value) def save_settings(self): """Save the settings before the application is closed with QSettings. Info saved: Tabs and projects opened, windows state(size and position). """ qsettings = IDE.ninja_settings() data_qsettings = IDE.data_settings() main_container = self.get_service("main_container") editor_widget = None if main_container: editor_widget = main_container.get_current_editor() current_file = '' if editor_widget is not None: current_file = editor_widget.file_path if qsettings.value('preferences/general/loadFiles', True, type=bool): openedFiles = self.filesystem.get_files() projects_obj = self.filesystem.get_projects() projects = [projects_obj[proj].path for proj in projects_obj] data_qsettings.setValue('lastSession/projects', projects) files_info = [] for path in openedFiles: editable = self.__neditables.get(openedFiles[path]) if editable is not None and editable.is_dirty: stat_value = 0 else: stat_value = os.stat(path).st_mtime files_info.append([path, editable.editor.get_cursor_position(), stat_value]) data_qsettings.setValue('lastSession/openedFiles', files_info) if current_file is not None: data_qsettings.setValue('lastSession/currentFile', current_file) data_qsettings.setValue('lastSession/recentFiles', settings.LAST_OPENED_FILES) data_qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS) data_qsettings.setValue('preferences/editor/breakpoints', settings.BREAKPOINTS) # Session if self._session is not None: val = QMessageBox.question( self, translations.TR_SESSION_ACTIVE_IDE_CLOSING_TITLE, (translations.TR_SESSION_ACTIVE_IDE_CLOSING_BODY % {'session': self.Session}), QMessageBox.Yes, QMessageBox.No) if val == QMessageBox.Yes: session_manager.SessionsManager.save_session_data( self.Session, self) #qsettings.setValue('preferences/general/toolbarArea', #self.toolBarArea(self.toolbar)) #Save if the windows state is maximixed if(self.isMaximized()): qsettings.setValue("window/maximized", True) else: qsettings.setValue("window/maximized", False) #Save the size and position of the mainwindow qsettings.setValue("window/size", self.size()) qsettings.setValue("window/pos", self.pos()) self.central.save_configuration() #Save the toolbar visibility #if not self.toolbar.isVisible() and self.menuBar().isVisible(): #qsettings.setValue("window/hide_toolbar", True) #else: #qsettings.setValue("window/hide_toolbar", False) #Save Misc state #qsettings.setValue("window/show_region1", self.misc.isVisible()) #Save Profiles #if self.profile is not None: #self.actions.save_profile(self.profile) #else: #qsettings.setValue('ide/profiles', settings.PROFILES) def activate_profile(self): """Show the Session Manager dialog.""" profilesLoader = session_manager.SessionsManager(self) profilesLoader.show() def deactivate_profile(self): """Close the Session Session.""" self.Session = None def load_window_geometry(self): """Load from QSettings the window size of de Ninja IDE""" qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) if qsettings.value("window/maximized", True, type=bool): self.setWindowState(Qt.WindowMaximized) else: self.resize(qsettings.value( "window/size", QSizeF(800, 600).toSize(), type='QSize')) self.move(qsettings.value( "window/pos", QPointF(100, 100).toPoint(), type='QPoint')) def _get_unsaved_files(self): """Return an array with the path of the unsaved files.""" unsaved = [] files = self.filesystem.get_files() for f in files: editable = self.__neditables.get(files[f]) if editable is not None: if editable.editor.is_modified: unsaved.append(f) return unsaved def _save_unsaved_files(self, files): """Save the files from the paths in the array.""" for f in files: editable = self.get_or_create_editable(f) editable.ignore_checkers = True editable.save_content() def closeEvent(self, event): """Saves some global settings before closing.""" if self.s_listener: self.s_listener.close() main_container = self.get_service("main_container") unsaved_files = self._get_unsaved_files() if (settings.CONFIRM_EXIT and unsaved_files): txt = '\n'.join(unsaved_files) val = QMessageBox.question( self, translations.TR_IDE_CONFIRM_EXIT_TITLE, (translations.TR_IDE_CONFIRM_EXIT_BODY % {'files': txt}), QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel) if val == QMessageBox.Yes: #Saves all open files self._save_unsaved_files(unsaved_files) if val == QMessageBox.Cancel: event.ignore() return self.save_settings() self.emit(SIGNAL("goingDown()")) #close python documentation server (if running) main_container.close_python_doc() #Shutdown PluginManager self.plugin_manager.shutdown() #completion_daemon.shutdown_daemon() super(IDE, self).closeEvent(event) def notify_plugin_errors(self): #TODO: Check if the Plugin Error dialog can be improved errors = self.plugin_manager.errors if errors: plugin_error_dialog = traceback_widget.PluginErrorDialog() for err_tuple in errors: plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1]) #show the dialog plugin_error_dialog.exec_() def show_message(self, message, duration=3000): """Show status message.""" self.notification.set_message(message, duration) self.notification.show() def show_plugins_store(self): """Open the Plugins Manager to install/uninstall plugins.""" store = plugins_store.PluginsStore(self) store.show() #manager = plugins_manager.PluginsManagerWidget(self) #manager.show() #if manager._requirements: #dependencyDialog = plugins_manager.DependenciesHelpDialog( #manager._requirements) #dependencyDialog.show() def show_languages(self): """Open the Language Manager to install/uninstall languages.""" manager = language_manager.LanguagesManagerWidget(self) manager.show() def show_schemes(self): """Open the Schemes Manager to install/uninstall schemes.""" manager = schemes_manager.SchemesManagerWidget(self) manager.show() def show_about_qt(self): """Show About Qt Dialog.""" QMessageBox.aboutQt(self, translations.TR_ABOUT_QT) def show_about_ninja(self): """Show About NINJA-IDE Dialog.""" about = about_ninja.AboutNinja(self) about.show() def show_python_detection(self): """Show Python detection dialog for windows.""" #TODO: Notify the user when no python version could be found suggested = settings.detect_python_path() if suggested: dialog = python_detect_dialog.PythonDetectDialog(suggested, self) dialog.show()
class IDE(QMainWindow): """This class is like the Sauron's Ring: One ring to rule them all, One ring to find them, One ring to bring them all and in the darkness bind them. This Class knows all the containers, and its know by all the containers, but the containers don't need to know between each other, in this way we can keep a better api without the need to tie the behaviour between the widgets, and let them just consume the 'actions' they need.""" ############################################################################### # SIGNALS # # goingDown() ############################################################################### __IDESERVICES = {} __IDECONNECTIONS = {} __IDESHORTCUTS = {} __IDEBARCATEGORIES = {} __IDEMENUS = {} __IDETOOLBAR = {} # CONNECTIONS structure: # ({'target': service_name, 'signal_name': string, 'slot': function_obj},) # On modify add: {connected: True} __instance = None __created = False def __init__(self, start_server=False): QMainWindow.__init__(self) self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') self.setMinimumSize(750, 500) QToolTip.setFont(QFont(settings.FONT.family(), 10)) #Load the size and the position of the main window self.load_window_geometry() self.__project_to_open = 0 #Editables self.__neditables = {} #Filesystem self.filesystem = nfilesystem.NVirtualFileSystem() #Sessions handler self._session = None #Opacity self.opacity = settings.MAX_OPACITY #ToolBar self.toolbar = QToolBar(self) if settings.IS_MAC_OS: self.toolbar.setIconSize(QSize(36, 36)) else: self.toolbar.setIconSize(QSize(24, 24)) self.toolbar.setToolTip(translations.TR_IDE_TOOLBAR_TOOLTIP) self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) # Set toggleViewAction text and tooltip self.toggleView = self.toolbar.toggleViewAction() self.toggleView.setText(translations.TR_TOOLBAR_VISIBILITY) self.toggleView.setToolTip(translations.TR_TOOLBAR_VISIBILITY) self.addToolBar(settings.TOOLBAR_AREA, self.toolbar) if settings.HIDE_TOOLBAR: self.toolbar.hide() #Notificator self.notification = notification.Notification(self) #Plugin Manager # CHECK ACTIVATE PLUGINS SETTING #services = { #'editor': plugin_services.MainService(), #'toolbar': plugin_services.ToolbarService(self.toolbar), ##'menuApp': plugin_services.MenuAppService(self.pluginsMenu), #'menuApp': plugin_services.MenuAppService(None), #'explorer': plugin_services.ExplorerService(), #'misc': plugin_services.MiscContainerService(self.misc)} #serviceLocator = plugin_manager.ServiceLocator(services) serviceLocator = plugin_manager.ServiceLocator(None) self.plugin_manager = plugin_manager.PluginManager( resources.PLUGINS, serviceLocator) self.plugin_manager.discover() #load all plugins! self.plugin_manager.load_all() #Tray Icon self.trayIcon = updates.TrayIconUpdates(self) self.connect(self.trayIcon, SIGNAL("closeTrayIcon()"), self._close_tray_icon) self.trayIcon.show() key = Qt.Key_1 for i in range(10): if settings.IS_MAC_OS: short = ui_tools.TabShortcuts( QKeySequence(Qt.CTRL + Qt.ALT + key), self, i) else: short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + key), self, i) key += 1 self.connect(short, SIGNAL("activated()"), self._change_tab_index) short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self, 10) self.connect(short, SIGNAL("activated()"), self._change_tab_index) # Register menu categories IDE.register_bar_category(translations.TR_MENU_FILE, 100) IDE.register_bar_category(translations.TR_MENU_EDIT, 110) IDE.register_bar_category(translations.TR_MENU_VIEW, 120) IDE.register_bar_category(translations.TR_MENU_SOURCE, 130) IDE.register_bar_category(translations.TR_MENU_PROJECT, 140) IDE.register_bar_category(translations.TR_MENU_EXTENSIONS, 150) IDE.register_bar_category(translations.TR_MENU_ABOUT, 160) # Register General Menu Items ui_tools.install_shortcuts(self, actions.ACTIONS_GENERAL, self) self.register_service('ide', self) self.register_service('toolbar', self.toolbar) self.register_service('filesystem', self.filesystem) #Register signals connections connections = ( { 'target': 'main_container', 'signal_name': 'fileSaved(QString)', 'slot': self.show_message }, { 'target': 'main_container', 'signal_name': 'currentEditorChanged(QString)', 'slot': self.change_window_title }, { 'target': 'main_container', 'signal_name': 'openPreferences()', 'slot': self.show_preferences }, { 'target': 'main_container', 'signal_name': 'allTabsClosed()', 'slot': self._last_tab_closed }, { 'target': 'explorer_container', 'signal_name': 'changeWindowTitle(QString)', 'slot': self.change_window_title }, { 'target': 'explorer_container', 'signal_name': 'projectClosed(QString)', 'slot': self.close_project }, ) self.register_signals('ide', connections) # Central Widget MUST always exists self.central = IDE.get_service('central_container') self.setCentralWidget(self.central) # Install Services for service_name in self.__IDESERVICES: self.install_service(service_name) IDE.__created = True # Place Status Bar main_container = IDE.get_service('main_container') status_bar = IDE.get_service('status_bar') main_container.add_status_bar(status_bar) # Load Menu Bar menu_bar = IDE.get_service('menu_bar') if menu_bar: menu_bar.load_menu(self) #These two are the same service, I think that's ok menu_bar.load_toolbar(self) #Start server if needed self.s_listener = None if start_server: self.s_listener = QLocalServer() self.s_listener.listen("ninja_ide") self.connect(self.s_listener, SIGNAL("newConnection()"), self._process_connection) IDE.__instance = self @classmethod def get_service(cls, service_name): """Return the instance of a registered service.""" return cls.__IDESERVICES.get(service_name, None) def get_menuitems(self): """Return a dictionary with the registered menu items.""" return IDE.__IDEMENUS def get_bar_categories(self): """Get the registered Categories for the Application menus.""" return IDE.__IDEBARCATEGORIES def get_toolbaritems(self): """Return a dictionary with the registered menu items.""" return IDE.__IDETOOLBAR @classmethod def register_service(cls, service_name, obj): """Register a service providing the service name and the instance.""" cls.__IDESERVICES[service_name] = obj if cls.__created: cls.__instance.install_service(service_name) def install_service(self, service_name): """Activate the registered service.""" obj = IDE.__IDESERVICES.get(service_name, None) func = getattr(obj, 'install', None) if isinstance(func, collections.Callable): func() self._connect_signals() def place_me_on(self, name, obj, region="central", top=False): """Place a widget in some of the areas in the IDE. @name: id to access to that widget later if needed. @obj: the instance of the widget to be placed. @region: the area where to put the widget [central, lateral] @top: place the widget as the first item in the split.""" self.central.add_to_region(name, obj, region, top) @classmethod def register_signals(cls, service_name, connections): """Register all the signals that a particular service wants to be attached of. @service_name: id of the service @connections: list of dictionaries for the connection with: - 'target': 'the_other_service_name', - 'signal_name': 'name of the signal in the other service', - 'slot': function object in this service""" cls.__IDECONNECTIONS[service_name] = connections if cls.__created: cls.__instance._connect_signals() def _connect_signals(self): """Connect the signals between the different services.""" for service_name in IDE.__IDECONNECTIONS: connections = IDE.__IDECONNECTIONS[service_name] for connection in connections: if connection.get('connected', False): continue target = IDE.__IDESERVICES.get(connection['target'], None) slot = connection['slot'] signal_name = connection['signal_name'] if target and isinstance(slot, collections.Callable): self.connect(target, SIGNAL(signal_name), slot) connection['connected'] = True @classmethod def register_shortcut(cls, shortcut_name, shortcut, action=None): """Register a shortcut and action.""" cls.__IDESHORTCUTS[shortcut_name] = (shortcut, action) @classmethod def register_menuitem(cls, menu_action, section, weight): """Register a QAction or QMenu in the IDE to be loaded later in the menubar using the section(string) to define where is going to be contained, and the weight define the order where is going to be placed. @menu_action: QAction or QMenu @section: String (name) @weight: int""" cls.__IDEMENUS[menu_action] = (section, weight) @classmethod def register_toolbar(cls, action, section, weight): """Register a QAction in the IDE to be loaded later in the toolbar using the section(string) to define where is going to be contained, and the weight define the order where is going to be placed. @action: QAction @section: String (name) @weight: int""" cls.__IDETOOLBAR[action] = (section, weight) @classmethod def register_bar_category(cls, category_name, weight): """Register a Menu Category to be created with the proper weight. @category_name: string @weight: int""" cls.__IDEBARCATEGORIES[category_name] = weight @classmethod def update_shortcut(cls, shortcut_name): """Update all the shortcuts of the application.""" short = resources.get_shortcut shortcut, action = cls.__IDESHORTCUTS.get(shortcut_name) if shortcut: shortcut.setKey(short(shortcut_name)) if action: action.setShortcut(short(shortcut_name)) def get_or_create_nfile(self, filename): """For convenience access to files from ide""" return self.filesystem.get_file(nfile_path=filename) def get_or_create_editable(self, filename="", nfile=None): if nfile is None: nfile = self.filesystem.get_file(nfile_path=filename) editable = self.__neditables.get(nfile) if editable is None: editable = neditable.NEditable(nfile) self.connect(editable, SIGNAL("fileClosing(PyQt_PyObject)"), self._unload_neditable) self.__neditables[nfile] = editable return editable def _unload_neditable(self, editable): self.__neditables.pop(editable.nfile) editable.nfile.deleteLater() editable.editor.deleteLater() editable.deleteLater() @property def opened_files(self): return tuple(self.__neditables.keys()) def get_project_for_file(self, filename): project = None if filename: project = self.filesystem.get_project_for_file(filename) return project def create_project(self, path): nproj = nproject.NProject(path) self.filesystem.open_project(nproj) return nproj def close_project(self, project_path): self.filesystem.close_project(project_path) def get_projects(self): return self.filesystem.get_projects() def get_current_project(self): current_project = None projects = self.filesystem.get_projects() for project in projects: if projects[project].is_current: current_project = projects[project] break return current_project @classmethod def select_current(cls, widget): """Show the widget with a 4px lightblue border line.""" widget.setProperty("highlight", True) widget.style().unpolish(widget) widget.style().polish(widget) @classmethod def unselect_current(cls, widget): """Remove the 4px lightblue border line from the widget.""" widget.setProperty("highlight", False) widget.style().unpolish(widget) widget.style().polish(widget) def _close_tray_icon(self): """Close the System Tray Icon.""" self.trayIcon.hide() self.trayIcon.deleteLater() def _change_tab_index(self): """Change the tabs of the current TabWidget using alt+numbers.""" widget = QApplication.focusWidget() shortcut_index = getattr(widget, 'shortcut_index', None) if shortcut_index: obj = self.sender() shortcut_index(obj.index) def _process_connection(self): """Read the ipc input from another instance of ninja.""" connection = self.s_listener.nextPendingConnection() connection.waitForReadyRead() data = connection.readAll() connection.close() if data: files, projects = str(data).split(ipc.project_delimiter, 1) files = [(x.split(':')[0], int(x.split(':')[1])) for x in files.split(ipc.file_delimiter)] projects = projects.split(ipc.project_delimiter) self.load_session_files_projects(files, [], projects, None) def fullscreen_mode(self): """Change to fullscreen mode.""" if self.isFullScreen(): self.showMaximized() else: self.showFullScreen() def change_toolbar_visibility(self): """Switch the toolbar visibility""" if self.toolbar.isVisible(): self.toolbar.hide() else: self.toolbar.show() def load_external_plugins(self, paths): """Load external plugins, the ones added to ninja throw the cmd.""" for path in paths: self.plugin_manager.add_plugin_dir(path) #load all plugins! self.plugin_manager.discover() self.plugin_manager.load_all() def _last_tab_closed(self): """ Called when the last tasb is closed """ self.explorer.cleanup_tabs() def show_preferences(self): """Open the Preferences Dialog.""" pref = preferences.Preferences(self) main_container = IDE.get_service("main_container") if main_container: main_container.show_dialog(pref) else: pref.show() def load_session_files_projects(self, files, projects, current_file, recent_files=None): """Load the files and projects from previous session.""" main_container = IDE.get_service('main_container') projects_explorer = IDE.get_service('projects_explorer') if main_container and files: for fileData in files: if file_manager.file_exists(fileData[0]): mtime = os.stat(fileData[0]).st_mtime ignore_checkers = (mtime == fileData[2]) main_container.open_file(fileData[0], fileData[1], ignore_checkers=ignore_checkers) if current_file: main_container.open_file(current_file) if projects_explorer and projects: projects_explorer.load_session_projects(projects) #if recent_files is not None: #menu_file = IDE.get_service('menu_file') #menu_file.update_recent_files(recent_files) #def _set_editors_project_data(self): #self.__project_to_open -= 1 #if self.__project_to_open == 0: #self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"), #self._set_editors_project_data) #self.mainContainer.update_editor_project() #def open_file(self, filename): #if filename: #self.mainContainer.open_file(filename) #def open_project(self, project): #if project: #self.actions.open_project(project) def __get_session(self): return self._session def __set_session(self, sessionName): self._session = sessionName if self._session is not None: self.setWindowTitle(translations.TR_SESSION_IDE_HEADER % {'session': self._session}) else: self.setWindowTitle( 'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') Session = property(__get_session, __set_session) def change_window_title(self, title): """Change the title of the Application.""" if self._session is None: self.setWindowTitle('NINJA-IDE - %s' % title) else: self.setWindowTitle((translations.TR_SESSION_IDE_HEADER % { 'session': self._session }) + ' - %s' % title) def wheelEvent(self, event): """Change the opacity of the application.""" if event.modifiers() == Qt.ShiftModifier: if event.delta() == 120 and self.opacity < settings.MAX_OPACITY: self.opacity += 0.1 elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY: self.opacity -= 0.1 self.setWindowOpacity(self.opacity) event.ignore() else: QMainWindow.wheelEvent(self, event) @classmethod def ninja_settings(cls): qsettings = nsettings.NSettings(resources.SETTINGS_PATH, prefix="ns") if cls.__created: cls.__instance.connect( qsettings, SIGNAL("valueChanged(QString, PyQt_PyObject)"), cls.__instance._settings_value_changed) return qsettings @classmethod def data_settings(cls): qsettings = nsettings.NSettings(resources.DATA_SETTINGS_PATH, prefix="ds") if cls.__created: cls.__instance.connect( qsettings, SIGNAL("valueChanged(QString, PyQt_PyObject)"), cls.__instance._settings_value_changed) return qsettings def _settings_value_changed(self, key, value): signal_name = "%s(PyQt_PyObject)" % key.replace("/", "_") self.emit(SIGNAL(signal_name), value) def save_settings(self): """Save the settings before the application is closed with QSettings. Info saved: Tabs and projects opened, windows state(size and position). """ qsettings = IDE.ninja_settings() data_qsettings = IDE.data_settings() main_container = self.get_service("main_container") editor_widget = None if main_container: editor_widget = main_container.get_current_editor() current_file = '' if editor_widget is not None: current_file = editor_widget.file_path if qsettings.value('preferences/general/loadFiles', True, type=bool): openedFiles = self.filesystem.get_files() projects_obj = self.filesystem.get_projects() projects = [projects_obj[proj].path for proj in projects_obj] data_qsettings.setValue('lastSession/projects', projects) files_info = [] for path in openedFiles: editable = self.__neditables.get(openedFiles[path]) if editable is not None and editable.is_dirty: stat_value = 0 else: stat_value = os.stat(path).st_mtime files_info.append( [path, editable.editor.get_cursor_position(), stat_value]) data_qsettings.setValue('lastSession/openedFiles', files_info) if current_file is not None: data_qsettings.setValue('lastSession/currentFile', current_file) data_qsettings.setValue('lastSession/recentFiles', settings.LAST_OPENED_FILES) data_qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS) data_qsettings.setValue('preferences/editor/breakpoints', settings.BREAKPOINTS) # Session if self._session is not None: val = QMessageBox.question( self, translations.TR_SESSION_ACTIVE_IDE_CLOSING_TITLE, (translations.TR_SESSION_ACTIVE_IDE_CLOSING_BODY % { 'session': self.Session }), QMessageBox.Yes, QMessageBox.No) if val == QMessageBox.Yes: session_manager.SessionsManager.save_session_data( self.Session, self) #qsettings.setValue('preferences/general/toolbarArea', #self.toolBarArea(self.toolbar)) #Save if the windows state is maximixed if (self.isMaximized()): qsettings.setValue("window/maximized", True) else: qsettings.setValue("window/maximized", False) #Save the size and position of the mainwindow qsettings.setValue("window/size", self.size()) qsettings.setValue("window/pos", self.pos()) self.central.save_configuration() #Save the toolbar visibility #if not self.toolbar.isVisible() and self.menuBar().isVisible(): #qsettings.setValue("window/hide_toolbar", True) #else: #qsettings.setValue("window/hide_toolbar", False) #Save Misc state #qsettings.setValue("window/show_region1", self.misc.isVisible()) #Save Profiles #if self.profile is not None: #self.actions.save_profile(self.profile) #else: #qsettings.setValue('ide/profiles', settings.PROFILES) def activate_profile(self): """Show the Session Manager dialog.""" profilesLoader = session_manager.SessionsManager(self) profilesLoader.show() def deactivate_profile(self): """Close the Session Session.""" self.Session = None def load_window_geometry(self): """Load from QSettings the window size of de Ninja IDE""" qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) if qsettings.value("window/maximized", True, type=bool): self.setWindowState(Qt.WindowMaximized) else: self.resize( qsettings.value("window/size", QSizeF(800, 600).toSize(), type='QSize')) self.move( qsettings.value("window/pos", QPointF(100, 100).toPoint(), type='QPoint')) def _get_unsaved_files(self): """Return an array with the path of the unsaved files.""" unsaved = [] files = self.opened_files for f in files: editable = self.__neditables.get(f) if editable is not None and editable.editor.is_modified: unsaved.append(f) return unsaved def _save_unsaved_files(self, files): """Save the files from the paths in the array.""" for f in files: editable = self.get_or_create_editable(f) editable.ignore_checkers = True editable.save_content() def closeEvent(self, event): """Saves some global settings before closing.""" if self.s_listener: self.s_listener.close() main_container = self.get_service("main_container") unsaved_files = self._get_unsaved_files() if (settings.CONFIRM_EXIT and unsaved_files): txt = '\n'.join([nfile.file_name for nfile in unsaved_files]) val = QMessageBox.question( self, translations.TR_IDE_CONFIRM_EXIT_TITLE, (translations.TR_IDE_CONFIRM_EXIT_BODY % { 'files': txt }), QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel) if val == QMessageBox.Yes: #Saves all open files self._save_unsaved_files(unsaved_files) if val == QMessageBox.Cancel: event.ignore() return self.save_settings() self.emit(SIGNAL("goingDown()")) #close python documentation server (if running) main_container.close_python_doc() #Shutdown PluginManager self.plugin_manager.shutdown() #completion_daemon.shutdown_daemon() super(IDE, self).closeEvent(event) def notify_plugin_errors(self): #TODO: Check if the Plugin Error dialog can be improved errors = self.plugin_manager.errors if errors: plugin_error_dialog = traceback_widget.PluginErrorDialog() for err_tuple in errors: plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1]) #show the dialog plugin_error_dialog.exec_() def show_message(self, message, duration=3000): """Show status message.""" self.notification.set_message(message, duration) self.notification.show() def show_plugins_store(self): """Open the Plugins Manager to install/uninstall plugins.""" store = plugins_store.PluginsStore(self) main_container = IDE.get_service("main_container") if main_container: main_container.show_dialog(store) else: store.show() def show_languages(self): """Open the Language Manager to install/uninstall languages.""" manager = language_manager.LanguagesManagerWidget(self) manager.show() def show_schemes(self): """Open the Schemes Manager to install/uninstall schemes.""" manager = schemes_manager.SchemesManagerWidget(self) manager.show() def show_about_qt(self): """Show About Qt Dialog.""" QMessageBox.aboutQt(self, translations.TR_ABOUT_QT) def show_about_ninja(self): """Show About NINJA-IDE Dialog.""" about = about_ninja.AboutNinja(self) about.show() def show_python_detection(self): """Show Python detection dialog for windows.""" #TODO: Notify the user when no python version could be found suggested = settings.detect_python_path() if suggested: dialog = python_detect_dialog.PythonDetectDialog(suggested, self) dialog.show()
class __IDE(QMainWindow): ############################################################################### # SIGNALS # # goingDown() ############################################################################### def __init__(self, start_server=False): QMainWindow.__init__(self) self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') self.setMinimumSize(700, 500) #Load the size and the position of the main window self.load_window_geometry() #Start server if needed self.s_listener = None if start_server: self.s_listener = QLocalServer() self.s_listener.listen("ninja_ide") self.connect(self.s_listener, SIGNAL("newConnection()"), self._process_connection) #Profile handler self.profile = None #Opacity self.opacity = settings.MAX_OPACITY #Define Actions object before the UI self.actions = actions.Actions() #StatusBar self.status = status_bar.StatusBar(self) self.status.hide() self.setStatusBar(self.status) #Main Widget - Create first than everything else self.central = central_widget.CentralWidget(self) self.load_ui(self.central) self.setCentralWidget(self.central) #ToolBar self.toolbar = QToolBar(self) self.toolbar.setToolTip(self.tr("Press and Drag to Move")) self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) self.addToolBar(settings.TOOLBAR_AREA, self.toolbar) if settings.HIDE_TOOLBAR: self.toolbar.hide() #Install Shortcuts after the UI has been initialized self.actions.install_shortcuts(self) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.actions.update_explorer) #Menu menubar = self.menuBar() file_ = menubar.addMenu(self.tr("&File")) edit = menubar.addMenu(self.tr("&Edit")) view = menubar.addMenu(self.tr("&View")) source = menubar.addMenu(self.tr("&Source")) project = menubar.addMenu(self.tr("&Project")) self.pluginsMenu = menubar.addMenu(self.tr("&Addins")) about = menubar.addMenu(self.tr("Abou&t")) #The order of the icons in the toolbar is defined by this calls self._menuFile = menu_file.MenuFile(file_, self.toolbar, self) self._menuView = menu_view.MenuView(view, self.toolbar, self) self._menuEdit = menu_edit.MenuEdit(edit, self.toolbar) self._menuSource = menu_source.MenuSource(source) self._menuProject = menu_project.MenuProject(project, self.toolbar) self._menuPlugins = menu_plugins.MenuPlugins(self.pluginsMenu) self._menuAbout = menu_about.MenuAbout(about) self.load_toolbar() #Plugin Manager services = { 'editor': plugin_services.MainService(), 'toolbar': plugin_services.ToolbarService(self.toolbar), 'menuApp': plugin_services.MenuAppService(self.pluginsMenu), 'explorer': plugin_services.ExplorerService(), 'misc': plugin_services.MiscContainerService(self.misc)} serviceLocator = plugin_manager.ServiceLocator(services) self.plugin_manager = plugin_manager.PluginManager(resources.PLUGINS, serviceLocator) self.plugin_manager.discover() #load all plugins! self.plugin_manager.load_all() #Tray Icon self.trayIcon = updates.TrayIconUpdates(self) self.trayIcon.show() self.connect(self._menuFile, SIGNAL("openFile(QString)"), self.mainContainer.open_file) self.connect(self.mainContainer, SIGNAL("fileSaved(QString)"), self.show_status_message) self.connect(self.mainContainer, SIGNAL("recentTabsModified(QStringList)"), self._menuFile.update_recent_files) def _process_connection(self): connection = self.s_listener.nextPendingConnection() connection.waitForReadyRead() data = connection.readAll() connection.close() if data: files, projects = data.split(ipc.project_delimiter, 1) files = map(lambda x: (x.split(':')[0], int(x.split(':')[1])), files.split(ipc.file_delimiter)) projects = projects.split(ipc.project_delimiter) self.load_session_files_projects(files, [], projects, None) def load_toolbar(self): self.toolbar.clear() toolbar_items = {} toolbar_items.update(self._menuFile.toolbar_items) toolbar_items.update(self._menuView.toolbar_items) toolbar_items.update(self._menuEdit.toolbar_items) toolbar_items.update(self._menuSource.toolbar_items) toolbar_items.update(self._menuProject.toolbar_items) for item in settings.TOOLBAR_ITEMS: if item == 'separator': self.toolbar.addSeparator() else: tool_item = toolbar_items.get(item, None) if tool_item is not None: self.toolbar.addAction(tool_item) #load action added by plugins, This is a special case when reload #the toolbar after save the preferences widget for toolbar_action in settings.get_toolbar_item_for_plugins(): self.toolbar.addAction(toolbar_action) def load_external_plugins(self, paths): for path in paths: self.plugin_manager.add_plugin_dir(path) #load all plugins! self.plugin_manager.discover() self.plugin_manager.load_all() def show_status_message(self, message): self.status.showMessage(message, 2000) def load_ui(self, centralWidget): #Set Application Font for ToolTips QToolTip.setFont(QFont(settings.FONT_FAMILY, 10)) #Create Main Container to manage Tabs self.mainContainer = main_container.MainContainer(self) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.change_window_title) self.connect(self.mainContainer, SIGNAL("locateFunction(QString, QString, bool)"), self.actions.locate_function) self.connect(self.mainContainer, SIGNAL("navigateCode(bool, int)"), self.actions.navigate_code_history) self.connect(self.mainContainer, SIGNAL("addBackItemNavigation()"), self.actions.add_back_item_navigation) self.connect(self.mainContainer, SIGNAL("updateFileMetadata()"), self.actions.update_explorer) self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"), self.actions.update_explorer) self.connect(self.mainContainer, SIGNAL("openPreferences()"), self._show_preferences) self.connect(self.mainContainer, SIGNAL("dontOpenStartPage()"), self._dont_show_start_page_again) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.status.handle_tab_changed) # Update symbols self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"), self.status.explore_file_code) #Create Explorer Panel self.explorer = explorer_container.ExplorerContainer(self) self.connect(self.central, SIGNAL("splitterCentralRotated()"), self.explorer.rotate_tab_position) self.connect(self.explorer, SIGNAL("updateLocator()"), self.status.explore_code) self.connect(self.explorer, SIGNAL("goToDefinition(int)"), self.actions.editor_go_to_line) self.connect(self.explorer, SIGNAL("projectClosed(QString)"), self.actions.close_files_from_project) #Create Misc Bottom Container self.misc = misc_container.MiscContainer(self) self.connect(self.mainContainer, SIGNAL("findOcurrences(QString)"), self.misc.show_find_occurrences) centralWidget.insert_central_container(self.mainContainer) centralWidget.insert_lateral_container(self.explorer) centralWidget.insert_bottom_container(self.misc) self.connect(self.mainContainer, SIGNAL("cursorPositionChange(int, int)"), self.central.lateralPanel.update_line_col) # TODO: Change current symbol on move #self.connect(self.mainContainer, #SIGNAL("cursorPositionChange(int, int)"), #self.explorer.update_current_symbol) self.connect(self.mainContainer, SIGNAL("enabledFollowMode(bool)"), self.central.enable_follow_mode_scrollbar) if settings.SHOW_START_PAGE: self.mainContainer.show_start_page() def _show_preferences(self): pref = preferences.PreferencesWidget(self.mainContainer) pref.show() def _dont_show_start_page_again(self): settings.SHOW_START_PAGE = False qsettings = QSettings() qsettings.beginGroup('preferences') qsettings.beginGroup('general') qsettings.setValue('showStartPage', settings.SHOW_START_PAGE) qsettings.endGroup() qsettings.endGroup() self.mainContainer.actualTab.close_tab() def load_session_files_projects(self, filesTab1, filesTab2, projects, current_file, recent_files=None): self.mainContainer.open_files(filesTab1, notIDEStart=False) self.mainContainer.open_files(filesTab2, mainTab=False, notIDEStart=False) self.explorer.open_session_projects(projects, notIDEStart=False) if current_file: self.mainContainer.open_file(current_file, notStart=False) if recent_files is not None: self._menuFile.update_recent_files(recent_files) def open_file(self, filename): if filename: self.mainContainer.open_file(filename) def open_project(self, project): if project: self.actions.open_project(project) def __get_profile(self): return self.profile def __set_profile(self, profileName): self.profile = profileName if self.profile is not None: self.setWindowTitle('NINJA-IDE (PROFILE: %s)' % self.profile) else: self.setWindowTitle( 'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') Profile = property(__get_profile, __set_profile) def change_window_title(self, title): if self.profile is None: self.setWindowTitle('NINJA-IDE - %s' % title) else: self.setWindowTitle('NINJA-IDE (PROFILE: %s) - %s' % ( self.profile, title)) currentEditor = self.mainContainer.get_actual_editor() if currentEditor is not None: line = currentEditor.textCursor().blockNumber() + 1 col = currentEditor.textCursor().columnNumber() self.central.lateralPanel.update_line_col(line, col) def wheelEvent(self, event): if event.modifiers() == Qt.ShiftModifier: if event.delta() == 120 and self.opacity < settings.MAX_OPACITY: self.opacity += 0.1 elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY: self.opacity -= 0.1 self.setWindowOpacity(self.opacity) event.ignore() else: QMainWindow.wheelEvent(self, event) def save_settings(self): """Save the settings before the application is closed with QSettings. Info saved: Tabs and projects opened, windows state(size and position). """ qsettings = QSettings() editor_widget = self.mainContainer.get_actual_editor() current_file = '' if editor_widget is not None: current_file = editor_widget.ID if qsettings.value('preferences/general/loadFiles', 'true') == 'true': openedFiles = self.mainContainer.get_opened_documents() projects_obj = self.explorer.get_opened_projects() projects = [p.path for p in projects_obj] qsettings.setValue('openFiles/projects', projects) if len(openedFiles) > 0: qsettings.setValue('openFiles/mainTab', openedFiles[0]) if len(openedFiles) == 2: qsettings.setValue('openFiles/secondaryTab', openedFiles[1]) qsettings.setValue('openFiles/currentFile', current_file) qsettings.setValue('openFiles/recentFiles', self.mainContainer._tabMain.get_recent_files_list()) qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS) qsettings.setValue('preferences/editor/breakpoints', settings.BREAKPOINTS) qsettings.setValue('preferences/general/toolbarArea', self.toolBarArea(self.toolbar)) #Save if the windows state is maximixed if(self.isMaximized()): qsettings.setValue("window/maximized", True) else: qsettings.setValue("window/maximized", False) #Save the size and position of the mainwindow qsettings.setValue("window/size", self.size()) qsettings.setValue("window/pos", self.pos()) #Save the size of de splitters qsettings.setValue("window/central/areaSize", self.central.get_area_sizes()) qsettings.setValue("window/central/mainSize", self.central.get_main_sizes()) #Save the toolbar visibility qsettings.setValue("window/hide_toolbar", not self.toolbar.isVisible()) #Save Profiles if self.profile is not None: self.actions.save_profile(self.profile) else: qsettings.setValue('ide/profiles', settings.PROFILES) def load_window_geometry(self): """Load from QSettings the window size of de Ninja IDE""" qsettings = QSettings() if qsettings.value("window/maximized", 'true') == 'true': self.setWindowState(Qt.WindowMaximized) else: print dir(QSizeF) self.resize(qsettings.value("window/size", QSizeF(800, 600)).toSize()) self.move(qsettings.value("window/pos", QPointF(100, 100)).toPoint()) def closeEvent(self, event): if self.s_listener: self.s_listener.close() if settings.CONFIRM_EXIT and \ self.mainContainer.check_for_unsaved_tabs(): unsaved_files = self.mainContainer.get_unsaved_files() txt = '\n'.join(unsaved_files) val = QMessageBox.question(self, self.tr("Some changes were not saved"), self.tr("%s\n\nDo you want to exit anyway?" % txt), QMessageBox.Yes, QMessageBox.No) if val == QMessageBox.No: event.ignore() QApplication.instance().setCursorFlashTime(cursor_flash_time) self.emit(SIGNAL("goingDown()")) self.save_settings() completion_daemon.shutdown_daemon() #close python documentation server (if running) self.mainContainer.close_python_doc() #Shutdown PluginManager self.plugin_manager.shutdown() def notify_plugin_errors(self): errors = self.plugin_manager.errors if errors: plugin_error_dialog = traceback_widget.PluginErrorDialog() for err_tuple in errors: plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1]) #show the dialog plugin_error_dialog.exec_()
class IDE(QMainWindow): """This class is like the Sauron's Ring: One ring to rule them all, One ring to find them, One ring to bring them all and in the darkness bind them. This Class knows all the containers, and its know by all the containers, but the containers don't need to know between each other, in this way we can keep a better api without the need to tie the behaviour between the widgets, and let them just consume the 'actions' they need.""" ############################################################################### # SIGNALS # # goingDown() ############################################################################### __IDESERVICES = {} __IDECONNECTIONS = {} __IDESHORTCUTS = {} __IDEMENUSCATEGORY = {} __IDEMENUS = {} # CONNECTIONS structure: # ({'target': service_name, 'signal_name': string, 'slot': function_obj},) # On modify add: {connected: True} __instance = None __created = False def __init__(self, start_server=False): QMainWindow.__init__(self) self.setWindowTitle("NINJA-IDE {Ninja-IDE Is Not Just Another IDE}") self.setMinimumSize(700, 500) QToolTip.setFont(QFont(settings.FONT_FAMILY, 10)) # Load the size and the position of the main window self.load_window_geometry() self.__project_to_open = 0 # Editables self.__neditables = {} # Filesystem self.filesystem = nfilesystem.NVirtualFileSystem() # Start server if needed self.s_listener = None if start_server: self.s_listener = QLocalServer() self.s_listener.listen("ninja_ide") self.connect(self.s_listener, SIGNAL("newConnection()"), self._process_connection) # Profile handler self.profile = None # Opacity self.opacity = settings.MAX_OPACITY # ToolBar self.toolbar = QToolBar(self) self.toolbar.setToolTip(self.tr("Press and Drag to Move")) self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) self.addToolBar(settings.TOOLBAR_AREA, self.toolbar) if settings.HIDE_TOOLBAR: self.toolbar.hide() # Notificator self.notification = notification.Notification(self) # Plugin Manager # services = { #'editor': plugin_services.MainService(), #'toolbar': plugin_services.ToolbarService(self.toolbar), ##'menuApp': plugin_services.MenuAppService(self.pluginsMenu), #'menuApp': plugin_services.MenuAppService(None), #'explorer': plugin_services.ExplorerService(), #'misc': plugin_services.MiscContainerService(self.misc)} # serviceLocator = plugin_manager.ServiceLocator(services) serviceLocator = plugin_manager.ServiceLocator(None) self.plugin_manager = plugin_manager.PluginManager(resources.PLUGINS, serviceLocator) self.plugin_manager.discover() # load all plugins! self.plugin_manager.load_all() # Tray Icon self.trayIcon = updates.TrayIconUpdates(self) self.connect(self.trayIcon, SIGNAL("closeTrayIcon()"), self._close_tray_icon) self.trayIcon.show() key = Qt.Key_1 for i in range(10): if settings.IS_MAC_OS: short = ui_tools.TabShortcuts(QKeySequence(Qt.CTRL + Qt.ALT + key), self, i) else: short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + key), self, i) key += 1 self.connect(short, SIGNAL("activated()"), self._change_tab_index) short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self, 10) self.connect(short, SIGNAL("activated()"), self._change_tab_index) # Register menu categories IDE.register_menu_category(translations.TR_MENU_FILE, 100) IDE.register_menu_category(translations.TR_MENU_EDIT, 110) IDE.register_menu_category(translations.TR_MENU_VIEW, 120) IDE.register_menu_category(translations.TR_MENU_SOURCE, 130) IDE.register_menu_category(translations.TR_MENU_PROJECT, 140) IDE.register_menu_category(translations.TR_MENU_ADDINS, 150) IDE.register_menu_category(translations.TR_MENU_ABOUT, 160) # Register General Menu Items ui_tools.install_shortcuts(self, actions.ACTIONS_GENERAL, self) self.register_service("ide", self) self.register_service("toolbar", self.toolbar) # Register signals connections connections = ( {"target": "main_container", "signal_name": "fileSaved(QString)", "slot": self.show_status_message}, {"target": "main_container", "signal_name": "currentTabChanged(QString)", "slot": self.change_window_title}, {"target": "main_container", "signal_name": "openPreferences()", "slot": self.show_preferences}, {"target": "main_container", "signal_name": "allTabsClosed()", "slot": self._last_tab_closed}, { "target": "explorer_container", "signal_name": "changeWindowTitle(QString)", "slot": self.change_window_title, }, {"target": "explorer_container", "signal_name": "projectClosed(QString)", "slot": self.close_project}, ) self.register_signals("ide", connections) # Central Widget MUST always exists self.central = IDE.get_service("central_container") self.setCentralWidget(self.central) # Install Services for service_name in self.__IDESERVICES: self.install_service(service_name) self.__created = True menu_bar = IDE.get_service("menu_bar") if menu_bar: menu_bar.load_menu(self) # These two are the same service, I think that's ok menu_bar.load_toolbar(self) @classmethod def get_service(cls, service_name): """Return the instance of a registered service.""" return cls.__IDESERVICES.get(service_name, None) def get_menuitems(self): """Return a dictionary with the registered menu items.""" return self.__IDEMENUS def get_menu_categories(self): """Get the registered Categories for the Application menus.""" return self.__IDEMENUSCATEGORY @classmethod def register_service(cls, service_name, obj): """Register a service providing the service name and the instance.""" cls.__IDESERVICES[service_name] = obj if cls.__created: cls.__instance.install_service(service_name) def install_service(self, service_name): """Activate the registered service.""" obj = self.__IDESERVICES.get(service_name, None) func = getattr(obj, "install", None) if isinstance(func, collections.Callable): func() self._connect_signals() def place_me_on(self, name, obj, region="central", top=False): """Place a widget in some of the areas in the IDE. @name: id to access to that widget later if needed. @obj: the instance of the widget to be placed. @region: the area where to put the widget [central, lateral] @top: place the widget as the first item in the split.""" self.central.add_to_region(name, obj, region, top) @classmethod def register_signals(cls, service_name, connections): """Register all the signals that a particular service wants to be attached of. @service_name: id of the service @connections: list of dictionaries for the connection with: - 'target': 'the_other_service_name', - 'signal_name': 'name of the signal in the other service', - 'slot': function object in this service""" cls.__IDECONNECTIONS[service_name] = connections if cls.__created: cls.__instance._connect_signals() def _connect_signals(self): """Connect the signals between the different services.""" for service_name in self.__IDECONNECTIONS: connections = self.__IDECONNECTIONS[service_name] for connection in connections: if connection.get("connected", False): continue target = self.__IDESERVICES.get(connection["target"], None) slot = connection["slot"] signal_name = connection["signal_name"] if target and isinstance(slot, collections.Callable): self.connect(target, SIGNAL(signal_name), slot) connection["connected"] = True @classmethod def register_shortcut(cls, shortcut_name, shortcut, action=None): """Register a shortcut and action.""" cls.__IDESHORTCUTS[shortcut_name] = (shortcut, action) @classmethod def register_menuitem(cls, menu_action, section, weight): """Register a QAction or QMenu in the IDE to be loaded later in the menubar using the section(string) to define where is going to be contained, and the weight define the order where is going to be placed. @menu_action: QAction or QMenu @section: String (name) @weight: int""" cls.__IDEMENUS[menu_action] = (section, weight) @classmethod def register_menu_category(cls, category_name, weight): """Register a Menu Category to be created with the proper weight. @category_name: string @weight: int""" cls.__IDEMENUSCATEGORY[category_name] = weight @classmethod def update_shortcut(cls, shortcut_name): """Update all the shortcuts of the application.""" short = resources.get_shortcut shortcut, action = cls.__IDESHORTCUTS.get(shortcut_name) if shortcut: shortcut.setKey(short(shortcut_name)) if action: action.setShortcut(short(shortcut_name)) def get_or_create_editable(self, filename): nfile = self.filesystem.get_file(nfile_path=filename) editable = self.__neditables.get(nfile) if editable is None: editable = neditable.NEditable(nfile) self.__neditables[nfile] = editable return editable def get_project_for_file(self, filename): project = None if filename: project = self.filesystem.get_project_for_file(filename) return project def create_project(self, path): nproj = nproject.NProject(path) self.filesystem.open_project(nproj) return nproj def close_project(self, project_path): self.filesystem.close_project(project_path) def get_projects(self): return self.filesystem.get_projects() def get_current_project(self): current_project = None projects = self.filesystem.get_projects() for project in projects: if projects[project].is_current: current_project = projects[project] break return current_project def select_current(self, widget): """Show the widget with a 4px lightblue border line.""" self.setProperty("highlight", True) self.style().unpolish(self) self.style().polish(self) def unselect_current(self, widget): """Remove the 4px lightblue border line from the widget.""" self.setProperty("highlight", False) self.style().unpolish(widget) self.style().polish(widget) def get_opened_projects(self): return self.__projects def _close_tray_icon(self): """Close the System Tray Icon.""" self.trayIcon.hide() self.trayIcon.deleteLater() def _change_tab_index(self): """Change the tabs of the current TabWidget using alt+numbers.""" widget = QApplication.focusWidget() shortcut_index = getattr(widget, "shortcut_index", None) if shortcut_index: obj = self.sender() shortcut_index(obj.index) def switch_focus(self): """Switch the current keyboard focus to the next widget.""" widget = QApplication.focusWidget() main_container = IDE.get_service("main_container") tools_dock = IDE.get_service("tools_dock") explorer_container = IDE.get_service("explorer_container") if widget and main_container and tools_dock and explorer_container: if widget in (main_container.actualTab, main_container.actualTab.currentWidget()): explorer_container.currentWidget().setFocus() elif widget in (explorer_container, explorer_container.currentWidget()): if tools_dock.isVisible(): tools_dock.stack.currentWidget().setFocus() else: main_container.actualTab.currentWidget().setFocus() elif widget.parent() is tools_dock.stack: main_container.actualTab.currentWidget().setFocus() def _process_connection(self): """Read the ipc input from another instance of ninja.""" connection = self.s_listener.nextPendingConnection() connection.waitForReadyRead() data = connection.readAll() connection.close() if data: files, projects = str(data).split(ipc.project_delimiter, 1) files = [(x.split(":")[0], int(x.split(":")[1])) for x in files.split(ipc.file_delimiter)] projects = projects.split(ipc.project_delimiter) self.load_session_files_projects(files, [], projects, None) def fullscreen_mode(self): """Change to fullscreen mode.""" if self.isFullScreen(): self.showMaximized() else: self.showFullScreen() def change_toolbar_visibility(self): """Switch the toolbar visibility""" if self.toolbar.isVisible(): self.toolbar.hide() else: self.toolbar.show() def load_external_plugins(self, paths): """Load external plugins, the ones added to ninja throw the cmd.""" for path in paths: self.plugin_manager.add_plugin_dir(path) # load all plugins! self.plugin_manager.discover() self.plugin_manager.load_all() def _last_tab_closed(self): """ Called when the last tasb is closed """ self.explorer.cleanup_tabs() def show_preferences(self): """Open the Preferences Dialog.""" pref = preferences.PreferencesWidget(self) pref.show() def load_session_files_projects(self, filesTab1, filesTab2, projects, current_file, recent_files=None): """Load the files and projects from previous session.""" self.__project_to_open = len(projects) explorer = IDE.get_service("explorer_container") main_container = IDE.get_service("main_container") if explorer and main_container: self.connect(explorer, SIGNAL("projectOpened(QString)"), self._set_editors_project_data) explorer.open_session_projects(projects, notIDEStart=False) main_container.open_files(filesTab1, notIDEStart=False) main_container.open_files(filesTab2, mainTab=False, notIDEStart=False) if current_file: main_container.open_file(current_file, notStart=False) # if recent_files is not None: # menu_file = IDE.get_service('menu_file') # menu_file.update_recent_files(recent_files) # def _set_editors_project_data(self): # self.__project_to_open -= 1 # if self.__project_to_open == 0: # self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"), # self._set_editors_project_data) # self.mainContainer.update_editor_project() # def open_file(self, filename): # if filename: # self.mainContainer.open_file(filename) # def open_project(self, project): # if project: # self.actions.open_project(project) def __get_profile(self): return self.profile def __set_profile(self, profileName): self.profile = profileName if self.profile is not None: self.setWindowTitle("NINJA-IDE (PROFILE: %s)" % self.profile) else: self.setWindowTitle("NINJA-IDE {Ninja-IDE Is Not Just Another IDE}") Profile = property(__get_profile, __set_profile) def change_window_title(self, title): """Change the title of the Application.""" if self.profile is None: self.setWindowTitle("NINJA-IDE - %s" % title) else: self.setWindowTitle("NINJA-IDE (PROFILE: %s) - %s" % (self.profile, title)) def wheelEvent(self, event): """Change the opacity of the application.""" if event.modifiers() == Qt.ShiftModifier: if event.delta() == 120 and self.opacity < settings.MAX_OPACITY: self.opacity += 0.1 elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY: self.opacity -= 0.1 self.setWindowOpacity(self.opacity) event.ignore() else: QMainWindow.wheelEvent(self, event) def save_settings(self): """Save the settings before the application is closed with QSettings. Info saved: Tabs and projects opened, windows state(size and position). """ qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) editor_widget = self.mainContainer.get_actual_editor() current_file = "" if editor_widget is not None: current_file = editor_widget.ID if qsettings.value("preferences/general/loadFiles", True, type=bool): openedFiles = self.mainContainer.get_opened_documents() projects_obj = self.get_opened_projects() projects = [p.path for p in projects_obj] qsettings.setValue("openFiles/projects", projects) if len(openedFiles) > 0: qsettings.setValue("openFiles/mainTab", openedFiles[0]) if len(openedFiles) == 2: qsettings.setValue("openFiles/secondaryTab", openedFiles[1]) qsettings.setValue("openFiles/currentFile", current_file) qsettings.setValue("openFiles/recentFiles", self.mainContainer._tabMain.get_recent_files_list()) qsettings.setValue("preferences/editor/bookmarks", settings.BOOKMARKS) qsettings.setValue("preferences/editor/breakpoints", settings.BREAKPOINTS) qsettings.setValue("preferences/general/toolbarArea", self.toolBarArea(self.toolbar)) # Save if the windows state is maximixed if self.isMaximized(): qsettings.setValue("window/maximized", True) else: qsettings.setValue("window/maximized", False) # Save the size and position of the mainwindow qsettings.setValue("window/size", self.size()) qsettings.setValue("window/pos", self.pos()) # Save the size of de splitters qsettings.setValue("window/central/baseSize", self.central.get_area_sizes()) qsettings.setValue("window/central/insideSize", self.central.get_main_sizes()) # Save the toolbar visibility if not self.toolbar.isVisible() and self.menuBar().isVisible(): qsettings.setValue("window/hide_toolbar", True) else: qsettings.setValue("window/hide_toolbar", False) # Save Misc state qsettings.setValue("window/show_region1", self.misc.isVisible()) # Save Profiles if self.profile is not None: self.actions.save_profile(self.profile) else: qsettings.setValue("ide/profiles", settings.PROFILES) def create_profile(self): """Create a profile binding files and projects to a key.""" profileInfo = QInputDialog.getText( None, self.tr("Create Profile"), self.tr("The Current Files and Projects will " "be associated to this profile.\n" "Profile Name:"), ) if profileInfo[1]: profileName = profileInfo[0] if not profileName or profileName in settings.PROFILES: QMessageBox.information( self, self.tr("Profile Name Invalid"), self.tr("The Profile name is invalid or already exists.") ) return self.save_profile(profileName) return profileName def save_profile(self, profileName): """Save the updates from a profile.""" ide = IDE.get_service("ide") main_container = IDE.get_service("main_container") if main_container: projects_obj = ide.get_opened_projects() projects = [p.path for p in projects_obj] files = main_container.get_opened_documents() files = files[0] + files[1] settings.PROFILES[profileName] = [files, projects] qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.setValue("ide/profiles", settings.PROFILES) def activate_profile(self): """Show the Profile Manager dialog.""" profilesLoader = ui_tools.ProfilesLoader( self._load_profile_data, self.create_profile, self.save_profile, settings.PROFILES, self.ide ) profilesLoader.show() def deactivate_profile(self): """Close the Profile Session.""" self.Profile = None def _load_profile_data(self, key): """Activate the selected profile, closing the current files/projects""" explorer = IDE.get_service("explorer_container") main_container = IDE.get_service("main_container") if explorer and main_container: explorer.close_opened_projects() main_container.open_files(settings.PROFILES[key][0]) explorer.open_session_projects(settings.PROFILES[key][1]) def load_window_geometry(self): """Load from QSettings the window size of de Ninja IDE""" qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) if qsettings.value("window/maximized", True, type=bool): self.setWindowState(Qt.WindowMaximized) else: self.resize(qsettings.value("window/size", QSizeF(800, 600).toSize(), type="QSize")) self.move(qsettings.value("window/pos", QPointF(100, 100).toPoint(), type="QPoint")) def closeEvent(self, event): """Saves some global settings before closing.""" if self.s_listener: self.s_listener.close() # if (settings.CONFIRM_EXIT and # self.mainContainer.check_for_unsaved_tabs()): # unsaved_files = self.mainContainer.get_unsaved_files() # txt = '\n'.join(unsaved_files) # val = QMessageBox.question(self, # self.tr("Some changes were not saved"), # (self.tr("%s\n\nDo you want to save them?") % txt), # QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel) # if val == QMessageBox.Yes: ##Saves all open files # main_container = IDE.get_service('main_container') # if main_container: # main_container.save_all() # if val == QMessageBox.Cancel: # event.ignore() # self.emit(SIGNAL("goingDown()")) # self.save_settings() # completion_daemon.shutdown_daemon() ##close python documentation server (if running) # self.mainContainer.close_python_doc() ##Shutdown PluginManager self.plugin_manager.shutdown() super(IDE, self).closeEvent(event) def notify_plugin_errors(self): # TODO: Check if the Plugin Error dialog can be improved errors = self.plugin_manager.errors if errors: plugin_error_dialog = traceback_widget.PluginErrorDialog() for err_tuple in errors: plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1]) # show the dialog plugin_error_dialog.exec_() def show_status_message(self, message, duration=3000): """Show status message.""" self.notification.set_message(message, duration) self.notification.show() def show_manager(self): """Open the Plugins Manager to install/uninstall plugins.""" manager = plugins_manager.PluginsManagerWidget(self) manager.show() if manager._requirements: dependencyDialog = plugins_manager.DependenciesHelpDialog(manager._requirements) dependencyDialog.show() def show_languages(self): """Open the Language Manager to install/uninstall languages.""" manager = language_manager.LanguagesManagerWidget(self) manager.show() def show_themes(self): """Open the Themes Manager to install/uninstall themes.""" manager = themes_manager.ThemesManagerWidget(self) manager.show() def show_about_qt(self): """Show About Qt Dialog.""" QMessageBox.aboutQt(self, translations.TR_ABOUT_QT) def show_about_ninja(self): """Show About NINJA-IDE Dialog.""" about = about_ninja.AboutNinja(self) about.show() def show_python_detection(self): """Show Python detection dialog for windows.""" # TODO: Notify the user when no python version could be found suggested = settings.detect_python_path() if suggested: dialog = python_detect_dialog.PythonDetectDialog(suggested, self) dialog.show()
class IDE(QMainWindow): """This class is like the Sauron's Ring: One ring to rule them all, One ring to find them, One ring to bring them all and in the darkness bind them. This Class knows all the containers, and its know by all the containers, but the containers don't need to know between each other, in this way we can keep a better api without the need to tie the behaviour between the widgets, and let them just consume the 'actions' they need.""" ############################################################################### # SIGNALS # # goingDown() ############################################################################### __IDESERVICES = {} __IDECONNECTIONS = {} __IDESHORTCUTS = {} __IDEBARCATEGORIES = {} __IDEMENUS = {} __IDETOOLBAR = {} # CONNECTIONS structure: # ({'target': service_name, 'signal_name': string, 'slot': function_obj},) # On modify add: {connected: True} __instance = None __created = False def __init__(self, start_server=False): QMainWindow.__init__(self) self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') self.setMinimumSize(750, 500) QToolTip.setFont(QFont(settings.FONT_FAMILY, 10)) #Load the size and the position of the main window self.load_window_geometry() self.__project_to_open = 0 #Editables self.__neditables = {} #Filesystem self.filesystem = nfilesystem.NVirtualFileSystem() #Start server if needed self.s_listener = None if start_server: self.s_listener = QLocalServer() self.s_listener.listen("ninja_ide") self.connect(self.s_listener, SIGNAL("newConnection()"), self._process_connection) #Profile handler self.profile = None #Opacity self.opacity = settings.MAX_OPACITY #ToolBar self.toolbar = QToolBar(self) self.toolbar.setToolTip(self.tr("Press and Drag to Move")) self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) self.addToolBar(settings.TOOLBAR_AREA, self.toolbar) if settings.HIDE_TOOLBAR: self.toolbar.hide() #Notificator self.notification = notification.Notification(self) #Plugin Manager #services = { #'editor': plugin_services.MainService(), #'toolbar': plugin_services.ToolbarService(self.toolbar), ##'menuApp': plugin_services.MenuAppService(self.pluginsMenu), #'menuApp': plugin_services.MenuAppService(None), #'explorer': plugin_services.ExplorerService(), #'misc': plugin_services.MiscContainerService(self.misc)} #serviceLocator = plugin_manager.ServiceLocator(services) serviceLocator = plugin_manager.ServiceLocator(None) self.plugin_manager = plugin_manager.PluginManager( resources.PLUGINS, serviceLocator) self.plugin_manager.discover() #load all plugins! self.plugin_manager.load_all() #Tray Icon self.trayIcon = updates.TrayIconUpdates(self) self.connect(self.trayIcon, SIGNAL("closeTrayIcon()"), self._close_tray_icon) self.trayIcon.show() key = Qt.Key_1 for i in range(10): if settings.IS_MAC_OS: short = ui_tools.TabShortcuts( QKeySequence(Qt.CTRL + Qt.ALT + key), self, i) else: short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + key), self, i) key += 1 self.connect(short, SIGNAL("activated()"), self._change_tab_index) short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self, 10) self.connect(short, SIGNAL("activated()"), self._change_tab_index) # Register menu categories IDE.register_bar_category(translations.TR_MENU_FILE, 100) IDE.register_bar_category(translations.TR_MENU_EDIT, 110) IDE.register_bar_category(translations.TR_MENU_VIEW, 120) IDE.register_bar_category(translations.TR_MENU_SOURCE, 130) IDE.register_bar_category(translations.TR_MENU_PROJECT, 140) IDE.register_bar_category(translations.TR_MENU_ADDINS, 150) IDE.register_bar_category(translations.TR_MENU_ABOUT, 160) # Register General Menu Items ui_tools.install_shortcuts(self, actions.ACTIONS_GENERAL, self) self.register_service('ide', self) self.register_service('toolbar', self.toolbar) #Register signals connections connections = ( { 'target': 'main_container', 'signal_name': 'fileSaved(QString)', 'slot': self.show_status_message }, { 'target': 'main_container', 'signal_name': 'currentEditorChanged(QString)', 'slot': self.change_window_title }, { 'target': 'main_container', 'signal_name': 'openPreferences()', 'slot': self.show_preferences }, { 'target': 'main_container', 'signal_name': 'allTabsClosed()', 'slot': self._last_tab_closed }, { 'target': 'explorer_container', 'signal_name': 'changeWindowTitle(QString)', 'slot': self.change_window_title }, { 'target': 'explorer_container', 'signal_name': 'projectClosed(QString)', 'slot': self.close_project }, ) self.register_signals('ide', connections) # Central Widget MUST always exists self.central = IDE.get_service('central_container') self.setCentralWidget(self.central) # Install Services for service_name in self.__IDESERVICES: self.install_service(service_name) self.__created = True menu_bar = IDE.get_service('menu_bar') if menu_bar: menu_bar.load_menu(self) #These two are the same service, I think that's ok menu_bar.load_toolbar(self) @classmethod def get_service(cls, service_name): """Return the instance of a registered service.""" return cls.__IDESERVICES.get(service_name, None) def get_menuitems(self): """Return a dictionary with the registered menu items.""" return self.__IDEMENUS def get_bar_categories(self): """Get the registered Categories for the Application menus.""" return self.__IDEBARCATEGORIES def get_toolbaritems(self): """Return a dictionary with the registered menu items.""" return self.__IDETOOLBAR @classmethod def register_service(cls, service_name, obj): """Register a service providing the service name and the instance.""" cls.__IDESERVICES[service_name] = obj if cls.__created: cls.__instance.install_service(service_name) def install_service(self, service_name): """Activate the registered service.""" obj = self.__IDESERVICES.get(service_name, None) func = getattr(obj, 'install', None) if isinstance(func, collections.Callable): func() self._connect_signals() def place_me_on(self, name, obj, region="central", top=False): """Place a widget in some of the areas in the IDE. @name: id to access to that widget later if needed. @obj: the instance of the widget to be placed. @region: the area where to put the widget [central, lateral] @top: place the widget as the first item in the split.""" self.central.add_to_region(name, obj, region, top) @classmethod def register_signals(cls, service_name, connections): """Register all the signals that a particular service wants to be attached of. @service_name: id of the service @connections: list of dictionaries for the connection with: - 'target': 'the_other_service_name', - 'signal_name': 'name of the signal in the other service', - 'slot': function object in this service""" cls.__IDECONNECTIONS[service_name] = connections if cls.__created: cls.__instance._connect_signals() def _connect_signals(self): """Connect the signals between the different services.""" for service_name in self.__IDECONNECTIONS: connections = self.__IDECONNECTIONS[service_name] for connection in connections: if connection.get('connected', False): continue target = self.__IDESERVICES.get(connection['target'], None) slot = connection['slot'] signal_name = connection['signal_name'] if target and isinstance(slot, collections.Callable): self.connect(target, SIGNAL(signal_name), slot) connection['connected'] = True @classmethod def register_shortcut(cls, shortcut_name, shortcut, action=None): """Register a shortcut and action.""" cls.__IDESHORTCUTS[shortcut_name] = (shortcut, action) @classmethod def register_menuitem(cls, menu_action, section, weight): """Register a QAction or QMenu in the IDE to be loaded later in the menubar using the section(string) to define where is going to be contained, and the weight define the order where is going to be placed. @menu_action: QAction or QMenu @section: String (name) @weight: int""" cls.__IDEMENUS[menu_action] = (section, weight) @classmethod def register_toolbar(cls, action, section, weight): """Register a QAction in the IDE to be loaded later in the toolbar using the section(string) to define where is going to be contained, and the weight define the order where is going to be placed. @action: QAction @section: String (name) @weight: int""" cls.__IDETOOLBAR[action] = (section, weight) @classmethod def register_bar_category(cls, category_name, weight): """Register a Menu Category to be created with the proper weight. @category_name: string @weight: int""" cls.__IDEBARCATEGORIES[category_name] = weight @classmethod def update_shortcut(cls, shortcut_name): """Update all the shortcuts of the application.""" short = resources.get_shortcut shortcut, action = cls.__IDESHORTCUTS.get(shortcut_name) if shortcut: shortcut.setKey(short(shortcut_name)) if action: action.setShortcut(short(shortcut_name)) def get_or_create_editable(self, filename): nfile = self.filesystem.get_file(nfile_path=filename) editable = self.__neditables.get(nfile) if editable is None: editable = neditable.NEditable(nfile) self.__neditables[nfile] = editable return editable def get_project_for_file(self, filename): project = None if filename: project = self.filesystem.get_project_for_file(filename) return project def create_project(self, path): nproj = nproject.NProject(path) self.filesystem.open_project(nproj) return nproj def close_project(self, project_path): self.filesystem.close_project(project_path) def get_projects(self): return self.filesystem.get_projects() def get_current_project(self): current_project = None projects = self.filesystem.get_projects() for project in projects: if projects[project].is_current: current_project = projects[project] break return current_project def select_current(self, widget): """Show the widget with a 4px lightblue border line.""" self.setProperty("highlight", True) self.style().unpolish(self) self.style().polish(self) def unselect_current(self, widget): """Remove the 4px lightblue border line from the widget.""" self.setProperty("highlight", False) self.style().unpolish(widget) self.style().polish(widget) def get_opened_projects(self): return self.__projects def _close_tray_icon(self): """Close the System Tray Icon.""" self.trayIcon.hide() self.trayIcon.deleteLater() def _change_tab_index(self): """Change the tabs of the current TabWidget using alt+numbers.""" widget = QApplication.focusWidget() shortcut_index = getattr(widget, 'shortcut_index', None) if shortcut_index: obj = self.sender() shortcut_index(obj.index) def switch_focus(self): """Switch the current keyboard focus to the next widget.""" widget = QApplication.focusWidget() main_container = IDE.get_service('main_container') tools_dock = IDE.get_service('tools_dock') explorer_container = IDE.get_service('explorer_container') if widget and main_container and tools_dock and explorer_container: if widget in (main_container.actualTab, main_container.actualTab.currentWidget()): explorer_container.currentWidget().setFocus() elif widget in (explorer_container, explorer_container.currentWidget()): if tools_dock.isVisible(): tools_dock.stack.currentWidget().setFocus() else: main_container.actualTab.currentWidget().setFocus() elif widget.parent() is tools_dock.stack: main_container.actualTab.currentWidget().setFocus() def _process_connection(self): """Read the ipc input from another instance of ninja.""" connection = self.s_listener.nextPendingConnection() connection.waitForReadyRead() data = connection.readAll() connection.close() if data: files, projects = str(data).split(ipc.project_delimiter, 1) files = [(x.split(':')[0], int(x.split(':')[1])) for x in files.split(ipc.file_delimiter)] projects = projects.split(ipc.project_delimiter) self.load_session_files_projects(files, [], projects, None) def fullscreen_mode(self): """Change to fullscreen mode.""" if self.isFullScreen(): self.showMaximized() else: self.showFullScreen() def change_toolbar_visibility(self): """Switch the toolbar visibility""" if self.toolbar.isVisible(): self.toolbar.hide() else: self.toolbar.show() def load_external_plugins(self, paths): """Load external plugins, the ones added to ninja throw the cmd.""" for path in paths: self.plugin_manager.add_plugin_dir(path) #load all plugins! self.plugin_manager.discover() self.plugin_manager.load_all() def _last_tab_closed(self): """ Called when the last tasb is closed """ self.explorer.cleanup_tabs() def show_preferences(self): """Open the Preferences Dialog.""" pref = preferences.PreferencesWidget(self) pref.show() def load_session_files_projects(self, filesTab1, filesTab2, projects, current_file, recent_files=None): """Load the files and projects from previous session.""" self.__project_to_open = len(projects) explorer = IDE.get_service('explorer_container') main_container = IDE.get_service('main_container') if explorer and main_container: self.connect(explorer, SIGNAL("projectOpened(QString)"), self._set_editors_project_data) explorer.open_session_projects(projects, notIDEStart=False) main_container.open_files(filesTab1, notIDEStart=False) main_container.open_files(filesTab2, mainTab=False, notIDEStart=False) if current_file: main_container.open_file(current_file, notStart=False) #if recent_files is not None: #menu_file = IDE.get_service('menu_file') #menu_file.update_recent_files(recent_files) #def _set_editors_project_data(self): #self.__project_to_open -= 1 #if self.__project_to_open == 0: #self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"), #self._set_editors_project_data) #self.mainContainer.update_editor_project() #def open_file(self, filename): #if filename: #self.mainContainer.open_file(filename) #def open_project(self, project): #if project: #self.actions.open_project(project) def __get_profile(self): return self.profile def __set_profile(self, profileName): self.profile = profileName if self.profile is not None: self.setWindowTitle('NINJA-IDE (PROFILE: %s)' % self.profile) else: self.setWindowTitle( 'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}') Profile = property(__get_profile, __set_profile) def change_window_title(self, title): """Change the title of the Application.""" if self.profile is None: self.setWindowTitle('NINJA-IDE - %s' % title) else: self.setWindowTitle('NINJA-IDE (PROFILE: %s) - %s' % (self.profile, title)) def wheelEvent(self, event): """Change the opacity of the application.""" if event.modifiers() == Qt.ShiftModifier: if event.delta() == 120 and self.opacity < settings.MAX_OPACITY: self.opacity += 0.1 elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY: self.opacity -= 0.1 self.setWindowOpacity(self.opacity) event.ignore() else: QMainWindow.wheelEvent(self, event) def save_settings(self): """Save the settings before the application is closed with QSettings. Info saved: Tabs and projects opened, windows state(size and position). """ qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) editor_widget = self.mainContainer.get_actual_editor() current_file = '' if editor_widget is not None: current_file = editor_widget.ID if qsettings.value('preferences/general/loadFiles', True, type=bool): openedFiles = self.mainContainer.get_opened_documents() projects_obj = self.get_opened_projects() projects = [p.path for p in projects_obj] qsettings.setValue('openFiles/projects', projects) if len(openedFiles) > 0: qsettings.setValue('openFiles/mainTab', openedFiles[0]) if len(openedFiles) == 2: qsettings.setValue('openFiles/secondaryTab', openedFiles[1]) qsettings.setValue('openFiles/currentFile', current_file) qsettings.setValue( 'openFiles/recentFiles', self.mainContainer._tabMain.get_recent_files_list()) qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS) qsettings.setValue('preferences/editor/breakpoints', settings.BREAKPOINTS) qsettings.setValue('preferences/general/toolbarArea', self.toolBarArea(self.toolbar)) #Save if the windows state is maximixed if (self.isMaximized()): qsettings.setValue("window/maximized", True) else: qsettings.setValue("window/maximized", False) #Save the size and position of the mainwindow qsettings.setValue("window/size", self.size()) qsettings.setValue("window/pos", self.pos()) #Save the size of de splitters qsettings.setValue("window/central/baseSize", self.central.get_area_sizes()) qsettings.setValue("window/central/insideSize", self.central.get_main_sizes()) #Save the toolbar visibility if not self.toolbar.isVisible() and self.menuBar().isVisible(): qsettings.setValue("window/hide_toolbar", True) else: qsettings.setValue("window/hide_toolbar", False) #Save Misc state qsettings.setValue("window/show_region1", self.misc.isVisible()) #Save Profiles if self.profile is not None: self.actions.save_profile(self.profile) else: qsettings.setValue('ide/profiles', settings.PROFILES) def create_profile(self): """Create a profile binding files and projects to a key.""" profileInfo = QInputDialog.getText( None, self.tr("Create Profile"), self.tr("The Current Files and Projects will " "be associated to this profile.\n" "Profile Name:")) if profileInfo[1]: profileName = profileInfo[0] if not profileName or profileName in settings.PROFILES: QMessageBox.information( self, self.tr("Profile Name Invalid"), self.tr("The Profile name is invalid or already exists.")) return self.save_profile(profileName) return profileName def save_profile(self, profileName): """Save the updates from a profile.""" ide = IDE.get_service('ide') main_container = IDE.get_service('main_container') if main_container: projects_obj = ide.get_opened_projects() projects = [p.path for p in projects_obj] files = main_container.get_opened_documents() files = files[0] + files[1] settings.PROFILES[profileName] = [files, projects] qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.setValue('ide/profiles', settings.PROFILES) def activate_profile(self): """Show the Profile Manager dialog.""" profilesLoader = ui_tools.ProfilesLoader(self._load_profile_data, self.create_profile, self.save_profile, settings.PROFILES, self.ide) profilesLoader.show() def deactivate_profile(self): """Close the Profile Session.""" self.Profile = None def _load_profile_data(self, key): """Activate the selected profile, closing the current files/projects""" explorer = IDE.get_service('explorer_container') main_container = IDE.get_service('main_container') if explorer and main_container: explorer.close_opened_projects() main_container.open_files(settings.PROFILES[key][0]) explorer.open_session_projects(settings.PROFILES[key][1]) def load_window_geometry(self): """Load from QSettings the window size of de Ninja IDE""" qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) if qsettings.value("window/maximized", True, type=bool): self.setWindowState(Qt.WindowMaximized) else: self.resize( qsettings.value("window/size", QSizeF(800, 600).toSize(), type='QSize')) self.move( qsettings.value("window/pos", QPointF(100, 100).toPoint(), type='QPoint')) def closeEvent(self, event): """Saves some global settings before closing.""" if self.s_listener: self.s_listener.close() #if (settings.CONFIRM_EXIT and #self.mainContainer.check_for_unsaved_tabs()): #unsaved_files = self.mainContainer.get_unsaved_files() #txt = '\n'.join(unsaved_files) #val = QMessageBox.question(self, #self.tr("Some changes were not saved"), #(self.tr("%s\n\nDo you want to save them?") % txt), #QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel) #if val == QMessageBox.Yes: ##Saves all open files #main_container = IDE.get_service('main_container') #if main_container: #main_container.save_all() #if val == QMessageBox.Cancel: #event.ignore() #self.emit(SIGNAL("goingDown()")) #self.save_settings() #completion_daemon.shutdown_daemon() ##close python documentation server (if running) #self.mainContainer.close_python_doc() ##Shutdown PluginManager self.plugin_manager.shutdown() super(IDE, self).closeEvent(event) def notify_plugin_errors(self): #TODO: Check if the Plugin Error dialog can be improved errors = self.plugin_manager.errors if errors: plugin_error_dialog = traceback_widget.PluginErrorDialog() for err_tuple in errors: plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1]) #show the dialog plugin_error_dialog.exec_() def show_status_message(self, message, duration=3000): """Show status message.""" self.notification.set_message(message, duration) self.notification.show() def show_manager(self): """Open the Plugins Manager to install/uninstall plugins.""" manager = plugins_manager.PluginsManagerWidget(self) manager.show() if manager._requirements: dependencyDialog = plugins_manager.DependenciesHelpDialog( manager._requirements) dependencyDialog.show() def show_languages(self): """Open the Language Manager to install/uninstall languages.""" manager = language_manager.LanguagesManagerWidget(self) manager.show() def show_themes(self): """Open the Themes Manager to install/uninstall themes.""" manager = themes_manager.ThemesManagerWidget(self) manager.show() def show_about_qt(self): """Show About Qt Dialog.""" QMessageBox.aboutQt(self, translations.TR_ABOUT_QT) def show_about_ninja(self): """Show About NINJA-IDE Dialog.""" about = about_ninja.AboutNinja(self) about.show() def show_python_detection(self): """Show Python detection dialog for windows.""" #TODO: Notify the user when no python version could be found suggested = settings.detect_python_path() if suggested: dialog = python_detect_dialog.PythonDetectDialog(suggested, self) dialog.show()
class __IDE(QMainWindow): ############################################################################### # SIGNALS # # goingDown() ############################################################################### def __init__(self, start_server=False): QMainWindow.__init__(self) self.setWindowTitle( 'AL\'EXA-IDE {Al\'exa-IDE Is Not Just Another IDE}') self.setMinimumSize(700, 500) #Load the size and the position of the main window self.load_window_geometry() self.__project_to_open = 0 #Start server if needed self.s_listener = None if start_server: self.s_listener = QLocalServer() self.s_listener.listen("ninja_ide") self.connect(self.s_listener, SIGNAL("newConnection()"), self._process_connection) #Profile handler self.profile = None #Opacity self.opacity = settings.MAX_OPACITY #Define Actions object before the UI self.actions = actions.Actions() #StatusBar self.status = status_bar.StatusBar(self) self.status.hide() self.setStatusBar(self.status) #Main Widget - Create first than everything else self.central = central_widget.CentralWidget(self) self.load_ui(self.central) self.setCentralWidget(self.central) #ToolBar self.toolbar = QToolBar(self) self.toolbar.setToolTip(self.tr("Press and Drag to Move")) self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) self.addToolBar(settings.TOOLBAR_AREA, self.toolbar) if settings.HIDE_TOOLBAR: self.toolbar.hide() #Install Shortcuts after the UI has been initialized self.actions.install_shortcuts(self) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.actions.update_explorer) #Menu menubar = self.menuBar() file_ = menubar.addMenu(self.tr("&File")) edit = menubar.addMenu(self.tr("&Edit")) view = menubar.addMenu(self.tr("&View")) source = menubar.addMenu(self.tr("&Source")) project = menubar.addMenu(self.tr("&Project")) self.pluginsMenu = menubar.addMenu(self.tr("&Addins")) about = menubar.addMenu(self.tr("Abou&t")) #The order of the icons in the toolbar is defined by this calls self._menuFile = menu_file.MenuFile(file_, self.toolbar, self) self._menuView = menu_view.MenuView(view, self.toolbar, self) self._menuEdit = menu_edit.MenuEdit(edit, self.toolbar) self._menuSource = menu_source.MenuSource(source) self._menuProject = menu_project.MenuProject(project, self.toolbar) self._menuPlugins = menu_plugins.MenuPlugins(self.pluginsMenu) self._menuAbout = menu_about.MenuAbout(about) self.load_toolbar() #Plugin Manager services = { 'editor': plugin_services.MainService(), 'toolbar': plugin_services.ToolbarService(self.toolbar), 'menuApp': plugin_services.MenuAppService(self.pluginsMenu), 'explorer': plugin_services.ExplorerService(), 'misc': plugin_services.MiscContainerService(self.misc) } serviceLocator = plugin_manager.ServiceLocator(services) self.plugin_manager = plugin_manager.PluginManager( resources.PLUGINS, serviceLocator) self.plugin_manager.discover() #load all plugins! self.plugin_manager.load_all() #Tray Icon self.trayIcon = updates.TrayIconUpdates(self) self.trayIcon.show() self.connect(self._menuFile, SIGNAL("openFile(QString)"), self.mainContainer.open_file) self.connect(self.mainContainer, SIGNAL("fileSaved(QString)"), self.show_status_message) self.connect(self.mainContainer, SIGNAL("recentTabsModified(QStringList)"), self._menuFile.update_recent_files) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.actions.update_migration_tips) self.connect(self.mainContainer, SIGNAL("updateFileMetadata(QString)"), self.actions.update_migration_tips) self.connect(self.mainContainer, SIGNAL("migrationAnalyzed()"), self.actions.update_migration_tips) def _process_connection(self): connection = self.s_listener.nextPendingConnection() connection.waitForReadyRead() data = connection.readAll() connection.close() if data: files, projects = str(data).split(ipc.project_delimiter, 1) files = [(x.split(':')[0], int(x.split(':')[1])) for x in files.split(ipc.file_delimiter)] projects = projects.split(ipc.project_delimiter) self.load_session_files_projects(files, [], projects, None) def load_toolbar(self): self.toolbar.clear() toolbar_items = {} toolbar_items.update(self._menuFile.toolbar_items) toolbar_items.update(self._menuView.toolbar_items) toolbar_items.update(self._menuEdit.toolbar_items) toolbar_items.update(self._menuSource.toolbar_items) toolbar_items.update(self._menuProject.toolbar_items) for item in settings.TOOLBAR_ITEMS: if item == 'separator': self.toolbar.addSeparator() else: tool_item = toolbar_items.get(item, None) if tool_item is not None: self.toolbar.addAction(tool_item) #load action added by plugins, This is a special case when reload #the toolbar after save the preferences widget for toolbar_action in settings.get_toolbar_item_for_plugins(): self.toolbar.addAction(toolbar_action) def load_external_plugins(self, paths): for path in paths: self.plugin_manager.add_plugin_dir(path) #load all plugins! self.plugin_manager.discover() self.plugin_manager.load_all() def show_status_message(self, message): self.status.showMessage(message, 2000) def load_ui(self, centralWidget): #Set Application Font for ToolTips QToolTip.setFont(QFont(settings.FONT_FAMILY, 10)) #Create Main Container to manage Tabs self.mainContainer = main_container.MainContainer(self) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.change_window_title) self.connect(self.mainContainer, SIGNAL("locateFunction(QString, QString, bool)"), self.actions.locate_function) self.connect(self.mainContainer, SIGNAL("navigateCode(bool, int)"), self.actions.navigate_code_history) self.connect(self.mainContainer, SIGNAL("addBackItemNavigation()"), self.actions.add_back_item_navigation) self.connect(self.mainContainer, SIGNAL("updateFileMetadata()"), self.actions.update_explorer) self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"), self.actions.update_explorer) self.connect(self.mainContainer, SIGNAL("openPreferences()"), self._show_preferences) self.connect(self.mainContainer, SIGNAL("dontOpenStartPage()"), self._dont_show_start_page_again) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.status.handle_tab_changed) # When close the last tab cleanup self.connect(self.mainContainer, SIGNAL("allTabsClosed()"), self._last_tab_closed) # Update symbols self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"), self.status.explore_file_code) #Create Explorer Panel self.explorer = explorer_container.ExplorerContainer(self) self.connect(self.central, SIGNAL("splitterCentralRotated()"), self.explorer.rotate_tab_position) self.connect(self.explorer, SIGNAL("updateLocator()"), self.status.explore_code) self.connect(self.explorer, SIGNAL("goToDefinition(int)"), self.actions.editor_go_to_line) self.connect(self.explorer, SIGNAL("projectClosed(QString)"), self.actions.close_files_from_project) #Create Misc Bottom Container self.misc = misc_container.MiscContainer(self) self.connect(self.mainContainer, SIGNAL("findOcurrences(QString)"), self.misc.show_find_occurrences) centralWidget.insert_central_container(self.mainContainer) centralWidget.insert_lateral_container(self.explorer) centralWidget.insert_bottom_container(self.misc) if self.explorer.count() == 0: centralWidget.change_explorer_visibility(force_hide=True) self.connect(self.mainContainer, SIGNAL("cursorPositionChange(int, int)"), self.central.lateralPanel.update_line_col) # TODO: Change current symbol on move #self.connect(self.mainContainer, #SIGNAL("cursorPositionChange(int, int)"), #self.explorer.update_current_symbol) self.connect(self.mainContainer, SIGNAL("enabledFollowMode(bool)"), self.central.enable_follow_mode_scrollbar) if settings.SHOW_START_PAGE: self.mainContainer.show_start_page() def _last_tab_closed(self): """ Called when the last tasb is closed """ self.explorer.cleanup_tabs() def _show_preferences(self): pref = preferences.PreferencesWidget(self.mainContainer) pref.show() def _dont_show_start_page_again(self): settings.SHOW_START_PAGE = False qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('general') qsettings.setValue('showStartPage', settings.SHOW_START_PAGE) qsettings.endGroup() qsettings.endGroup() self.mainContainer.actualTab.close_tab() def load_session_files_projects(self, filesTab1, filesTab2, projects, current_file, recent_files=None): self.__project_to_open = len(projects) self.connect(self.explorer, SIGNAL("projectOpened(QString)"), self._set_editors_project_data) self.explorer.open_session_projects(projects, notIDEStart=False) self.mainContainer.open_files(filesTab1, notIDEStart=False) self.mainContainer.open_files(filesTab2, mainTab=False, notIDEStart=False) if current_file: self.mainContainer.open_file(current_file, notStart=False) if recent_files is not None: self._menuFile.update_recent_files(recent_files) def _set_editors_project_data(self): self.__project_to_open -= 1 if self.__project_to_open == 0: self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"), self._set_editors_project_data) self.mainContainer.update_editor_project() def open_file(self, filename): if filename: self.mainContainer.open_file(filename) def open_project(self, project): if project: self.actions.open_project(project) def __get_profile(self): return self.profile def __set_profile(self, profileName): self.profile = profileName if self.profile is not None: self.setWindowTitle('AL\'EXA-IDE (PROFILE: %s)' % self.profile) else: self.setWindowTitle( 'AL\'EXA-IDE {Al\'exa-IDE Is Not Just Another IDE}') Profile = property(__get_profile, __set_profile) def change_window_title(self, title): if self.profile is None: self.setWindowTitle('AL\'EXA-IDE - %s' % title) else: self.setWindowTitle('AL\'EXA-IDE (PROFILE: %s) - %s' % (self.profile, title)) currentEditor = self.mainContainer.get_actual_editor() if currentEditor is not None: line = currentEditor.textCursor().blockNumber() + 1 col = currentEditor.textCursor().columnNumber() self.central.lateralPanel.update_line_col(line, col) def wheelEvent(self, event): if event.modifiers() == Qt.ShiftModifier: if event.delta() == 120 and self.opacity < settings.MAX_OPACITY: self.opacity += 0.1 elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY: self.opacity -= 0.1 self.setWindowOpacity(self.opacity) event.ignore() else: QMainWindow.wheelEvent(self, event) def save_settings(self): """Save the settings before the application is closed with QSettings. Info saved: Tabs and projects opened, windows state(size and position). """ qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) editor_widget = self.mainContainer.get_actual_editor() current_file = '' if editor_widget is not None: current_file = editor_widget.ID if qsettings.value('preferences/general/loadFiles', True, type=bool): openedFiles = self.mainContainer.get_opened_documents() projects_obj = self.explorer.get_opened_projects() projects = [p.path for p in projects_obj] qsettings.setValue('openFiles/projects', projects) if len(openedFiles) > 0: qsettings.setValue('openFiles/mainTab', openedFiles[0]) if len(openedFiles) == 2: qsettings.setValue('openFiles/secondaryTab', openedFiles[1]) qsettings.setValue('openFiles/currentFile', current_file) qsettings.setValue( 'openFiles/recentFiles', self.mainContainer._tabMain.get_recent_files_list()) qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS) qsettings.setValue('preferences/editor/breakpoints', settings.BREAKPOINTS) qsettings.setValue('preferences/general/toolbarArea', self.toolBarArea(self.toolbar)) #Save if the windows state is maximixed if (self.isMaximized()): qsettings.setValue("window/maximized", True) else: qsettings.setValue("window/maximized", False) #Save the size and position of the mainwindow qsettings.setValue("window/size", self.size()) qsettings.setValue("window/pos", self.pos()) #Save the size of de splitters qsettings.setValue("window/central/areaSize", self.central.get_area_sizes()) qsettings.setValue("window/central/mainSize", self.central.get_main_sizes()) #Save the toolbar visibility if not self.toolbar.isVisible() and self.menuBar().isVisible(): qsettings.setValue("window/hide_toolbar", True) else: qsettings.setValue("window/hide_toolbar", False) #Save Misc state qsettings.setValue("window/show_misc", self.misc.isVisible()) #Save Profiles if self.profile is not None: self.actions.save_profile(self.profile) else: qsettings.setValue('ide/profiles', settings.PROFILES) def load_window_geometry(self): """Load from QSettings the window size of de Ninja IDE""" qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) if qsettings.value("window/maximized", True, type=bool): self.setWindowState(Qt.WindowMaximized) else: self.resize( qsettings.value("window/size", QSizeF(800, 600).toSize(), type='QSize')) self.move( qsettings.value("window/pos", QPointF(100, 100).toPoint(), type='QPoint')) def CloseHideAllWindow(self, close=True): if sys.platform == 'win32': import win32gui import win32con import re toplist, winlist = [], [] def enum_callback(hwnd, results): winlist.append((hwnd, win32gui.GetWindowText(hwnd))) win32gui.EnumWindows(enum_callback, toplist) #^(?!.*details\.cfm).*selector=size.*$ #window = [(hwnd, curTitle) for hwnd, curTitle in winlist if re.match("al.*exa.*(tool|nagios utilities|mouse and keyboard).*", curTitle, re.DOTALL | re.IGNORECASE) != None and win32gui.IsWindowVisible(hwnd) != 0] windows = [(hwnd, curTitle) for hwnd, curTitle in winlist] # just grab the hwnd for first window matching title #print "toplist", top if len(windows) > 0: toplist, winlist = [], [] #print windows for window in windows: try: #if openedWindow[0].lower() == 'Al\'exa tools': title = window[1] if 'al\'exa tools' == title.lower( ) or 'al\'exa - nagios utilities' == title.lower( ) or 'al\'exa - mouse and keyboard' == title.lower( ) or 'al\'exa - windows and region' == title.lower( ) or 'al\'exa - process utilities' == title.lower( ) or 'al\'exa - e-mail' == title.lower(): if close is True: win32gui.PostMessage(window[0], win32con.WM_CLOSE, 0, 0) else: win32gui.ShowWindow(window[0], 0) except: continue def closeEvent(self, event): if self.s_listener: self.s_listener.close() if (settings.CONFIRM_EXIT and self.mainContainer.check_for_unsaved_tabs()): unsaved_files = self.mainContainer.get_unsaved_files() txt = '\n'.join(unsaved_files) self.CloseHideAllWindow(False) val = QMessageBox.question( self, self.tr("Some changes were not saved"), (self.tr("%s\n\nDo you want to save them?") % txt), QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel) ''' msgBox = QMessageBox() msgBox.setWindowTitle("QMessageBox Always On Top") msgBox.setText("This is an always-on-top QMessageBox!") msgBox.setWindowFlags(Qt.WindowStaysOnTopHint) msgBox.show() ''' #val.setWindowFlags(Qt.WindowStaysOnTopHint) if val == QMessageBox.Yes: #Saves all open files self.mainContainer.save_all() if val == QMessageBox.Cancel: event.ignore() return self.emit(SIGNAL("goingDown()")) self.save_settings() completion_daemon.shutdown_daemon() #close python documentation server (if running) self.mainContainer.close_python_doc() #Shutdown PluginManager self.plugin_manager.shutdown() self.CloseHideAllWindow(True) def notify_plugin_errors(self): errors = self.plugin_manager.errors if errors: plugin_error_dialog = traceback_widget.PluginErrorDialog() for err_tuple in errors: plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1]) #show the dialog plugin_error_dialog.exec_() def show_python_detection(self): suggested = settings.detect_python_path() if suggested: dialog = python_detect_dialog.PythonDetectDialog(suggested, self) dialog.show()
class __IDE(QMainWindow): ############################################################################### # SIGNALS # # goingDown() ############################################################################### def __init__(self, start_server=False): QMainWindow.__init__(self) self.setWindowTitle('AL\'EXA-IDE {Al\'exa-IDE Is Not Just Another IDE}') self.setMinimumSize(700, 500) #Load the size and the position of the main window self.load_window_geometry() self.__project_to_open = 0 #Start server if needed self.s_listener = None if start_server: self.s_listener = QLocalServer() self.s_listener.listen("ninja_ide") self.connect(self.s_listener, SIGNAL("newConnection()"), self._process_connection) #Profile handler self.profile = None #Opacity self.opacity = settings.MAX_OPACITY #Define Actions object before the UI self.actions = actions.Actions() #StatusBar self.status = status_bar.StatusBar(self) self.status.hide() self.setStatusBar(self.status) #Main Widget - Create first than everything else self.central = central_widget.CentralWidget(self) self.load_ui(self.central) self.setCentralWidget(self.central) #ToolBar self.toolbar = QToolBar(self) self.toolbar.setToolTip(self.tr("Press and Drag to Move")) self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) self.addToolBar(settings.TOOLBAR_AREA, self.toolbar) if settings.HIDE_TOOLBAR: self.toolbar.hide() #Install Shortcuts after the UI has been initialized self.actions.install_shortcuts(self) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.actions.update_explorer) #Menu menubar = self.menuBar() file_ = menubar.addMenu(self.tr("&File")) edit = menubar.addMenu(self.tr("&Edit")) view = menubar.addMenu(self.tr("&View")) source = menubar.addMenu(self.tr("&Source")) project = menubar.addMenu(self.tr("&Project")) self.pluginsMenu = menubar.addMenu(self.tr("&Addins")) about = menubar.addMenu(self.tr("Abou&t")) #The order of the icons in the toolbar is defined by this calls self._menuFile = menu_file.MenuFile(file_, self.toolbar, self) self._menuView = menu_view.MenuView(view, self.toolbar, self) self._menuEdit = menu_edit.MenuEdit(edit, self.toolbar) self._menuSource = menu_source.MenuSource(source) self._menuProject = menu_project.MenuProject(project, self.toolbar) self._menuPlugins = menu_plugins.MenuPlugins(self.pluginsMenu) self._menuAbout = menu_about.MenuAbout(about) self.load_toolbar() #Plugin Manager services = { 'editor': plugin_services.MainService(), 'toolbar': plugin_services.ToolbarService(self.toolbar), 'menuApp': plugin_services.MenuAppService(self.pluginsMenu), 'explorer': plugin_services.ExplorerService(), 'misc': plugin_services.MiscContainerService(self.misc)} serviceLocator = plugin_manager.ServiceLocator(services) self.plugin_manager = plugin_manager.PluginManager(resources.PLUGINS, serviceLocator) self.plugin_manager.discover() #load all plugins! self.plugin_manager.load_all() #Tray Icon self.trayIcon = updates.TrayIconUpdates(self) self.trayIcon.show() self.connect(self._menuFile, SIGNAL("openFile(QString)"), self.mainContainer.open_file) self.connect(self.mainContainer, SIGNAL("fileSaved(QString)"), self.show_status_message) self.connect(self.mainContainer, SIGNAL("recentTabsModified(QStringList)"), self._menuFile.update_recent_files) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.actions.update_migration_tips) self.connect(self.mainContainer, SIGNAL("updateFileMetadata(QString)"), self.actions.update_migration_tips) self.connect(self.mainContainer, SIGNAL("migrationAnalyzed()"), self.actions.update_migration_tips) def _process_connection(self): connection = self.s_listener.nextPendingConnection() connection.waitForReadyRead() data = connection.readAll() connection.close() if data: files, projects = str(data).split(ipc.project_delimiter, 1) files = [(x.split(':')[0], int(x.split(':')[1])) for x in files.split(ipc.file_delimiter)] projects = projects.split(ipc.project_delimiter) self.load_session_files_projects(files, [], projects, None) def load_toolbar(self): self.toolbar.clear() toolbar_items = {} toolbar_items.update(self._menuFile.toolbar_items) toolbar_items.update(self._menuView.toolbar_items) toolbar_items.update(self._menuEdit.toolbar_items) toolbar_items.update(self._menuSource.toolbar_items) toolbar_items.update(self._menuProject.toolbar_items) for item in settings.TOOLBAR_ITEMS: if item == 'separator': self.toolbar.addSeparator() else: tool_item = toolbar_items.get(item, None) if tool_item is not None: self.toolbar.addAction(tool_item) #load action added by plugins, This is a special case when reload #the toolbar after save the preferences widget for toolbar_action in settings.get_toolbar_item_for_plugins(): self.toolbar.addAction(toolbar_action) def load_external_plugins(self, paths): for path in paths: self.plugin_manager.add_plugin_dir(path) #load all plugins! self.plugin_manager.discover() self.plugin_manager.load_all() def show_status_message(self, message): self.status.showMessage(message, 2000) def load_ui(self, centralWidget): #Set Application Font for ToolTips QToolTip.setFont(QFont(settings.FONT_FAMILY, 10)) #Create Main Container to manage Tabs self.mainContainer = main_container.MainContainer(self) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.change_window_title) self.connect(self.mainContainer, SIGNAL("locateFunction(QString, QString, bool)"), self.actions.locate_function) self.connect(self.mainContainer, SIGNAL("navigateCode(bool, int)"), self.actions.navigate_code_history) self.connect(self.mainContainer, SIGNAL("addBackItemNavigation()"), self.actions.add_back_item_navigation) self.connect(self.mainContainer, SIGNAL("updateFileMetadata()"), self.actions.update_explorer) self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"), self.actions.update_explorer) self.connect(self.mainContainer, SIGNAL("openPreferences()"), self._show_preferences) self.connect(self.mainContainer, SIGNAL("dontOpenStartPage()"), self._dont_show_start_page_again) self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"), self.status.handle_tab_changed) # When close the last tab cleanup self.connect(self.mainContainer, SIGNAL("allTabsClosed()"), self._last_tab_closed) # Update symbols self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"), self.status.explore_file_code) #Create Explorer Panel self.explorer = explorer_container.ExplorerContainer(self) self.connect(self.central, SIGNAL("splitterCentralRotated()"), self.explorer.rotate_tab_position) self.connect(self.explorer, SIGNAL("updateLocator()"), self.status.explore_code) self.connect(self.explorer, SIGNAL("goToDefinition(int)"), self.actions.editor_go_to_line) self.connect(self.explorer, SIGNAL("projectClosed(QString)"), self.actions.close_files_from_project) #Create Misc Bottom Container self.misc = misc_container.MiscContainer(self) self.connect(self.mainContainer, SIGNAL("findOcurrences(QString)"), self.misc.show_find_occurrences) centralWidget.insert_central_container(self.mainContainer) centralWidget.insert_lateral_container(self.explorer) centralWidget.insert_bottom_container(self.misc) if self.explorer.count() == 0: centralWidget.change_explorer_visibility(force_hide=True) self.connect(self.mainContainer, SIGNAL("cursorPositionChange(int, int)"), self.central.lateralPanel.update_line_col) # TODO: Change current symbol on move #self.connect(self.mainContainer, #SIGNAL("cursorPositionChange(int, int)"), #self.explorer.update_current_symbol) self.connect(self.mainContainer, SIGNAL("enabledFollowMode(bool)"), self.central.enable_follow_mode_scrollbar) if settings.SHOW_START_PAGE: self.mainContainer.show_start_page() def _last_tab_closed(self): """ Called when the last tasb is closed """ self.explorer.cleanup_tabs() def _show_preferences(self): pref = preferences.PreferencesWidget(self.mainContainer) pref.show() def _dont_show_start_page_again(self): settings.SHOW_START_PAGE = False qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) qsettings.beginGroup('preferences') qsettings.beginGroup('general') qsettings.setValue('showStartPage', settings.SHOW_START_PAGE) qsettings.endGroup() qsettings.endGroup() self.mainContainer.actualTab.close_tab() def load_session_files_projects(self, filesTab1, filesTab2, projects, current_file, recent_files=None): self.__project_to_open = len(projects) self.connect(self.explorer, SIGNAL("projectOpened(QString)"), self._set_editors_project_data) self.explorer.open_session_projects(projects, notIDEStart=False) self.mainContainer.open_files(filesTab1, notIDEStart=False) self.mainContainer.open_files(filesTab2, mainTab=False, notIDEStart=False) if current_file: self.mainContainer.open_file(current_file, notStart=False) if recent_files is not None: self._menuFile.update_recent_files(recent_files) def _set_editors_project_data(self): self.__project_to_open -= 1 if self.__project_to_open == 0: self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"), self._set_editors_project_data) self.mainContainer.update_editor_project() def open_file(self, filename): if filename: self.mainContainer.open_file(filename) def open_project(self, project): if project: self.actions.open_project(project) def __get_profile(self): return self.profile def __set_profile(self, profileName): self.profile = profileName if self.profile is not None: self.setWindowTitle('AL\'EXA-IDE (PROFILE: %s)' % self.profile) else: self.setWindowTitle( 'AL\'EXA-IDE {Al\'exa-IDE Is Not Just Another IDE}') Profile = property(__get_profile, __set_profile) def change_window_title(self, title): if self.profile is None: self.setWindowTitle('AL\'EXA-IDE - %s' % title) else: self.setWindowTitle('AL\'EXA-IDE (PROFILE: %s) - %s' % ( self.profile, title)) currentEditor = self.mainContainer.get_actual_editor() if currentEditor is not None: line = currentEditor.textCursor().blockNumber() + 1 col = currentEditor.textCursor().columnNumber() self.central.lateralPanel.update_line_col(line, col) def wheelEvent(self, event): if event.modifiers() == Qt.ShiftModifier: if event.delta() == 120 and self.opacity < settings.MAX_OPACITY: self.opacity += 0.1 elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY: self.opacity -= 0.1 self.setWindowOpacity(self.opacity) event.ignore() else: QMainWindow.wheelEvent(self, event) def save_settings(self): """Save the settings before the application is closed with QSettings. Info saved: Tabs and projects opened, windows state(size and position). """ qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) editor_widget = self.mainContainer.get_actual_editor() current_file = '' if editor_widget is not None: current_file = editor_widget.ID if qsettings.value('preferences/general/loadFiles', True, type=bool): openedFiles = self.mainContainer.get_opened_documents() projects_obj = self.explorer.get_opened_projects() projects = [p.path for p in projects_obj] qsettings.setValue('openFiles/projects', projects) if len(openedFiles) > 0: qsettings.setValue('openFiles/mainTab', openedFiles[0]) if len(openedFiles) == 2: qsettings.setValue('openFiles/secondaryTab', openedFiles[1]) qsettings.setValue('openFiles/currentFile', current_file) qsettings.setValue('openFiles/recentFiles', self.mainContainer._tabMain.get_recent_files_list()) qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS) qsettings.setValue('preferences/editor/breakpoints', settings.BREAKPOINTS) qsettings.setValue('preferences/general/toolbarArea', self.toolBarArea(self.toolbar)) #Save if the windows state is maximixed if(self.isMaximized()): qsettings.setValue("window/maximized", True) else: qsettings.setValue("window/maximized", False) #Save the size and position of the mainwindow qsettings.setValue("window/size", self.size()) qsettings.setValue("window/pos", self.pos()) #Save the size of de splitters qsettings.setValue("window/central/areaSize", self.central.get_area_sizes()) qsettings.setValue("window/central/mainSize", self.central.get_main_sizes()) #Save the toolbar visibility if not self.toolbar.isVisible() and self.menuBar().isVisible(): qsettings.setValue("window/hide_toolbar", True) else: qsettings.setValue("window/hide_toolbar", False) #Save Misc state qsettings.setValue("window/show_misc", self.misc.isVisible()) #Save Profiles if self.profile is not None: self.actions.save_profile(self.profile) else: qsettings.setValue('ide/profiles', settings.PROFILES) def load_window_geometry(self): """Load from QSettings the window size of de Ninja IDE""" qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) if qsettings.value("window/maximized", True, type=bool): self.setWindowState(Qt.WindowMaximized) else: self.resize(qsettings.value("window/size", QSizeF(800, 600).toSize(), type='QSize')) self.move(qsettings.value("window/pos", QPointF(100, 100).toPoint(), type='QPoint')) def CloseHideAllWindow(self, close=True): if sys.platform == 'win32': import win32gui import win32con import re toplist, winlist = [], [] def enum_callback(hwnd, results): winlist.append((hwnd, win32gui.GetWindowText(hwnd))) win32gui.EnumWindows(enum_callback, toplist) #^(?!.*details\.cfm).*selector=size.*$ #window = [(hwnd, curTitle) for hwnd, curTitle in winlist if re.match("al.*exa.*(tool|nagios utilities|mouse and keyboard).*", curTitle, re.DOTALL | re.IGNORECASE) != None and win32gui.IsWindowVisible(hwnd) != 0] windows = [(hwnd, curTitle) for hwnd, curTitle in winlist] # just grab the hwnd for first window matching title #print "toplist", top if len(windows) > 0: toplist, winlist = [], [] #print windows for window in windows: try: #if openedWindow[0].lower() == 'Al\'exa tools': title = window[1] if 'al\'exa tools' == title.lower() or 'al\'exa - nagios utilities' == title.lower() or 'al\'exa - mouse and keyboard' == title.lower() or 'al\'exa - windows and region' == title.lower() or 'al\'exa - process utilities' == title.lower() or 'al\'exa - e-mail' == title.lower(): if close is True: win32gui.PostMessage(window[0], win32con.WM_CLOSE, 0, 0) else: win32gui.ShowWindow(window[0], 0) except: continue def closeEvent(self, event): if self.s_listener: self.s_listener.close() if (settings.CONFIRM_EXIT and self.mainContainer.check_for_unsaved_tabs()): unsaved_files = self.mainContainer.get_unsaved_files() txt = '\n'.join(unsaved_files) self.CloseHideAllWindow(False) val = QMessageBox.question(self, self.tr("Some changes were not saved"), (self.tr("%s\n\nDo you want to save them?") % txt), QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel) ''' msgBox = QMessageBox() msgBox.setWindowTitle("QMessageBox Always On Top") msgBox.setText("This is an always-on-top QMessageBox!") msgBox.setWindowFlags(Qt.WindowStaysOnTopHint) msgBox.show() ''' #val.setWindowFlags(Qt.WindowStaysOnTopHint) if val == QMessageBox.Yes: #Saves all open files self.mainContainer.save_all() if val == QMessageBox.Cancel: event.ignore() return self.emit(SIGNAL("goingDown()")) self.save_settings() completion_daemon.shutdown_daemon() #close python documentation server (if running) self.mainContainer.close_python_doc() #Shutdown PluginManager self.plugin_manager.shutdown() self.CloseHideAllWindow(True) def notify_plugin_errors(self): errors = self.plugin_manager.errors if errors: plugin_error_dialog = traceback_widget.PluginErrorDialog() for err_tuple in errors: plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1]) #show the dialog plugin_error_dialog.exec_() def show_python_detection(self): suggested = settings.detect_python_path() if suggested: dialog = python_detect_dialog.PythonDetectDialog(suggested, self) dialog.show()