Ejemplo n.º 1
0
 def _view_toolbar(self, toolbar: QToolBar, viewToolbarAction: QAction):
     if toolbar.isVisible():
         viewToolbarAction.setText(self.tr("Show Toolbar"))
         toolbar.close()
     else:
         viewToolbarAction.setText(self.tr("Hide Toolbar"))
         toolbar.show()
Ejemplo n.º 2
0
class PreparePanel(QWidget, TaskManager):

    signal_status = pyqtSignal(object, object)
    signal_password_response = pyqtSignal(str, bool)

    def __init__(self, context: ApplicationContext, manager: SoftwareManager,
                 screen_size: QSize, i18n: I18n, manage_window: QWidget):
        super(PreparePanel,
              self).__init__(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
        self.i18n = i18n
        self.context = context
        self.manage_window = manage_window
        self.setWindowTitle('{} ({})'.format(
            __app_name__, self.i18n['prepare_panel.title.start'].lower()))
        self.setMinimumWidth(screen_size.width() * 0.5)
        self.setMinimumHeight(screen_size.height() * 0.35)
        self.setMaximumHeight(screen_size.height() * 0.95)
        self.setLayout(QVBoxLayout())
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
        self.manager = manager
        self.tasks = {}
        self.output = {}
        self.ntasks = 0
        self.ftasks = 0
        self.self_close = False

        self.prepare_thread = Prepare(self.context, manager, self.i18n)
        self.prepare_thread.signal_register.connect(self.register_task)
        self.prepare_thread.signal_update.connect(self.update_progress)
        self.prepare_thread.signal_finished.connect(self.finish_task)
        self.prepare_thread.signal_started.connect(self.start)
        self.prepare_thread.signal_ask_password.connect(self.ask_root_password)
        self.prepare_thread.signal_output.connect(self.update_output)
        self.signal_password_response.connect(
            self.prepare_thread.set_password_reply)

        self.check_thread = CheckFinished()
        self.signal_status.connect(self.check_thread.update)
        self.check_thread.signal_finished.connect(self.finish)

        self.skip_thread = EnableSkip()
        self.skip_thread.signal_timeout.connect(self._enable_skip_button)

        self.progress_thread = AnimateProgress()
        self.progress_thread.signal_change.connect(self._change_progress)

        self.label_top = QLabel()
        self.label_top.setCursor(QCursor(Qt.WaitCursor))
        self.label_top.setText("{}...".format(
            self.i18n['prepare_panel.title.start'].capitalize()))
        self.label_top.setAlignment(Qt.AlignHCenter)
        self.label_top.setStyleSheet(
            "QLabel { font-size: 14px; font-weight: bold; }")
        self.layout().addWidget(self.label_top)
        self.layout().addWidget(QLabel())

        self.table = QTableWidget()
        self.table.setCursor(QCursor(Qt.WaitCursor))
        self.table.setStyleSheet(
            "QTableWidget { background-color: transparent; }")
        self.table.setFocusPolicy(Qt.NoFocus)
        self.table.setShowGrid(False)
        self.table.verticalHeader().setVisible(False)
        self.table.horizontalHeader().setVisible(False)
        self.table.horizontalHeader().setSizePolicy(
            QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
        self.table.setColumnCount(4)
        self.table.setHorizontalHeaderLabels(['' for _ in range(4)])
        self.layout().addWidget(self.table)

        self.textarea_output = QPlainTextEdit(self)
        self.textarea_output.resize(self.table.size())
        self.textarea_output.setStyleSheet("background: black; color: white;")
        self.layout().addWidget(self.textarea_output)
        self.textarea_output.setVisible(False)
        self.textarea_output.setReadOnly(True)
        self.textarea_output.setMaximumHeight(100)
        self.current_output_task = None

        self.bottom_widget = QWidget()
        self.bottom_widget.setLayout(QHBoxLayout())
        self.bottom_widget.layout().addStretch()
        bt_hide_output = QPushButton(self.i18n['prepare.bt_hide_details'])
        bt_hide_output.setStyleSheet(
            'QPushButton { text-decoration: underline; border: 0px; background: none } '
        )
        bt_hide_output.clicked.connect(self.hide_output)
        bt_hide_output.setCursor(QCursor(Qt.PointingHandCursor))
        self.bottom_widget.layout().addWidget(bt_hide_output)
        self.bottom_widget.layout().addStretch()
        self.layout().addWidget(self.bottom_widget)
        self.bottom_widget.setVisible(False)

        self.bt_bar = QToolBar()
        self.bt_close = QPushButton(self.i18n['close'].capitalize())
        self.bt_close.setCursor(QCursor(Qt.PointingHandCursor))
        self.bt_close.clicked.connect(self.close)
        self.bt_close.setVisible(False)
        self.ref_bt_close = self.bt_bar.addWidget(self.bt_close)

        self.bt_bar.addWidget(new_spacer())
        self.progress_bar = QProgressBar()
        self.progress_bar.setStyleSheet(styles.PROGRESS_BAR)
        self.progress_bar.setMaximumHeight(10 if QApplication.instance().style(
        ).objectName().lower() == 'windows' else 4)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.setVisible(False)
        self.progress_bar.setCursor(QCursor(Qt.WaitCursor))
        self.ref_progress_bar = self.bt_bar.addWidget(self.progress_bar)
        self.bt_bar.addWidget(new_spacer())

        self.bt_skip = QPushButton(
            self.i18n['prepare_panel.bt_skip.label'].capitalize())
        self.bt_skip.clicked.connect(self.finish)
        self.bt_skip.setEnabled(False)
        self.bt_skip.setCursor(QCursor(Qt.WaitCursor))
        self.bt_bar.addWidget(self.bt_skip)

        self.layout().addWidget(self.bt_bar)

    def hide_output(self):
        self.current_output_task = None
        self.textarea_output.setVisible(False)
        self.textarea_output.clear()
        self.bottom_widget.setVisible(False)
        self._resize_columns()
        self.setFocus(Qt.NoFocusReason)

        if not self.bt_bar.isVisible():
            self.bt_bar.setVisible(True)

    def ask_root_password(self):
        root_pwd, ok = root.ask_root_password(self.context, self.i18n)
        self.signal_password_response.emit(root_pwd, ok)

    def _enable_skip_button(self):
        self.bt_skip.setEnabled(True)
        self.bt_skip.setCursor(QCursor(Qt.PointingHandCursor))

    def _change_progress(self, value: int):
        self.progress_bar.setValue(value)

    def get_table_width(self) -> int:
        return reduce(operator.add, [
            self.table.columnWidth(i) for i in range(self.table.columnCount())
        ])

    def _resize_columns(self):
        header_horizontal = self.table.horizontalHeader()
        for i in range(self.table.columnCount()):
            header_horizontal.setSectionResizeMode(
                i, QHeaderView.ResizeToContents)

        self.resize(self.get_table_width() * 1.05, self.sizeHint().height())

    def show(self):
        super(PreparePanel, self).show()
        self.prepare_thread.start()
        centralize(self)

    def start(self):
        self.ref_bt_close.setVisible(True)
        self.check_thread.start()
        self.skip_thread.start()

        self.ref_progress_bar.setVisible(True)
        self.progress_thread.start()

    def closeEvent(self, QCloseEvent):
        if not self.self_close:
            QCoreApplication.exit()

    def register_task(self, id_: str, label: str, icon_path: str):
        self.ntasks += 1
        self.table.setRowCount(self.ntasks)

        task_row = self.ntasks - 1

        icon_widget = QWidget()
        icon_widget.setLayout(QHBoxLayout())
        icon_widget.layout().setContentsMargins(10, 0, 10, 0)
        bt_icon = QToolButton()
        bt_icon.setCursor(QCursor(Qt.WaitCursor))
        bt_icon.setEnabled(False)
        bt_icon.setToolTip(self.i18n['prepare.bt_icon.no_output'])
        bt_icon.setFixedSize(QSize(24, 24))
        bt_icon.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)

        if icon_path:
            bt_icon.setIcon(QIcon(icon_path))

        def _show_output():
            lines = self.output[id_]

            if lines:
                self.current_output_task = id_
                self.textarea_output.clear()
                self.textarea_output.setVisible(True)

                for l in lines:
                    self.textarea_output.appendPlainText(l)

                self.bottom_widget.setVisible(True)

            self.setFocus(Qt.NoFocusReason)

            if self.bt_bar.isVisible():
                self.bt_bar.setVisible(False)

        bt_icon.clicked.connect(_show_output)
        icon_widget.layout().addWidget(bt_icon)

        self.table.setCellWidget(task_row, 0, icon_widget)

        lb_status = QLabel(label)
        lb_status.setCursor(Qt.WaitCursor)
        lb_status.setMinimumWidth(50)
        lb_status.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
        lb_status.setStyleSheet("QLabel { font-weight: bold; }")
        self.table.setCellWidget(task_row, 1, lb_status)

        lb_sub = QLabel()
        lb_status.setCursor(Qt.WaitCursor)
        lb_sub.setContentsMargins(10, 0, 10, 0)
        lb_sub.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
        lb_sub.setMinimumWidth(50)
        self.table.setCellWidget(task_row, 2, lb_sub)

        lb_progress = QLabel('{0:.2f}'.format(0) + '%')
        lb_progress.setCursor(Qt.WaitCursor)
        lb_progress.setContentsMargins(10, 0, 10, 0)
        lb_progress.setStyleSheet("QLabel { font-weight: bold; }")
        lb_progress.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)

        self.table.setCellWidget(task_row, 3, lb_progress)

        self.tasks[id_] = {
            'bt_icon': bt_icon,
            'lb_status': lb_status,
            'lb_prog': lb_progress,
            'progress': 0,
            'lb_sub': lb_sub,
            'finished': False
        }

        self.signal_status.emit(self.ntasks, self.ftasks)

    def update_progress(self, task_id: str, progress: float, substatus: str):
        task = self.tasks[task_id]

        if progress != task['progress']:
            task['progress'] = progress
            task['lb_prog'].setText('{0:.2f}'.format(progress) + '%')

        if substatus:
            task['lb_sub'].setText('( {} )'.format(substatus))
        else:
            task['lb_sub'].setText('')

        self._resize_columns()

    def update_output(self, task_id: str, output: str):
        full_output = self.output.get(task_id)

        if full_output is None:
            full_output = []
            self.output[task_id] = full_output
            task = self.tasks[task_id]
            task['bt_icon'].setEnabled(True)
            task['bt_icon'].setCursor(QCursor(Qt.PointingHandCursor))
            task['bt_icon'].setToolTip(self.i18n['prepare.bt_icon.output'])

        full_output.append(output)

        if self.current_output_task == task_id:
            self.textarea_output.appendPlainText(output)

    def finish_task(self, task_id: str):
        task = self.tasks[task_id]
        task['lb_sub'].setText('')

        for key in ('lb_prog', 'lb_status'):
            task[key].setStyleSheet(
                'QLabel { color: %s; text-decoration: line-through; }' % GREEN)

        task['finished'] = True
        self._resize_columns()

        self.ftasks += 1
        self.signal_status.emit(self.ntasks, self.ftasks)

        if self.ntasks == self.ftasks:
            self.label_top.setText(self.i18n['ready'].capitalize())

    def finish(self):
        if self.isVisible():
            self.manage_window.begin_refresh_packages()
            self.manage_window.show()
            self.self_close = True
            self.close()
Ejemplo n.º 3
0
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


    MessageStatusChanged = pyqtSignal(str)

    goingDown = pyqtSignal()
    # # ns_preferences_editor_font = pyqtSignal()
    # ns_preferences_editor_showTabsAndSpaces = pyqtSignal()
    # ns_preferences_editor_showIndentationGuide = pyqtSignal()
    # ns_preferences_editor_indent = pyqtSignal()
    # ns_preferences_editor_marginLine = pyqtSignal()#podría tener un argumento
    # ns_preferences_editor_showLineNumbers = pyqtSignal()
    # ns_preferences_editor_showMigrationTips = pyqtSignal()
    # ns_preferences_editor_checkStyle = pyqtSignal()
    # ns_preferences_editor_errors = pyqtSignal()
    # ds_lastSession_projects = pyqtSignal()
    # ds_lastSession_openedFiles = pyqtSignal()
    # ds_lastSession_currentFile = pyqtSignal()
    # ds_lastSession_recentFiles = pyqtSignal()
    # ns_preferences_editor_bookmarks = pyqtSignal()
    # ns_preferences_editor_breakpoints = pyqtSignal()
    # ns_window_maximized = pyqtSignal()
    # ns_preferences_general_loadFiles = pyqtSignal()
    # ns_preferences_general_activatePlugins = pyqtSignal()
    # ns_preferences_general_notifyUpdates = pyqtSignal()
    # ns_preferences_general_showStartPage = pyqtSignal(bool)
    # ns_preferences_general_confirmExit = pyqtSignal(bool)
    # ns_preferences_general_workspace = pyqtSignal()
    ns_preferences_general_supportedExtensions = pyqtSignal("QStringList")
    #ns_preferences_general_notification_position = pyqtSignal(int)
    #...
    ns_preferences_general_loadFiles = pyqtSignal(bool)# dato: 'True'

    ns_preferences_general_activatePlugins = pyqtSignal(bool)# dato: 'True'

    ns_preferences_general_notifyUpdates = pyqtSignal(bool)# dato: 'True'

    ns_preferences_general_showStartPage = pyqtSignal(bool)# dato: 'True'

    ns_preferences_general_confirmExit = pyqtSignal(bool)# dato: 'True'

    ns_preferences_general_workspace = pyqtSignal(str)# dato: ''

    #ns_preferences_general_supportedExtensions = pyqtSignal(list)# dato: '['.py', '.pyw', '.html', '.jpg','.png', '.ui', '.css', '.json', '.js', '.ini']'

    ns_preferences_general_notification_position = pyqtSignal(int)# dato: '0'

    ns_preferences_general_notification_color = pyqtSignal(str)# dato: '#000'

    ns_pythonPath = pyqtSignal(str)# dato: 'D:\Python34\python.exe'

    ns_executionOptions = pyqtSignal(str)# dato: ''

    ns_Show_Code_Nav = pyqtSignal(str)# dato: 'Ctrl+3'

    ns_Follow_mode = pyqtSignal(str)# dato: 'Ctrl+F10'

    ns_Change_Tab = pyqtSignal(str)# dato: 'Ctrl+PgDown'

    ns_Change_Tab_Reverse = pyqtSignal(str)# dato: 'Ctrl+PgUp'

    ns_Close_file = pyqtSignal(str)# dato: 'Ctrl+W'

    ns_Close_Split = pyqtSignal(str)# dato: 'Shift+F9'

    ns_Comment = pyqtSignal(str)# dato: 'Ctrl+G'

    ns_Complete_Declarations = pyqtSignal(str)# dato: 'Alt+Return'

    ns_copy = pyqtSignal(str)# dato: 'Ctrl+C'

    ns_History_Copy = pyqtSignal(str)# dato: 'Ctrl+Alt+C'

    ns_New_project = pyqtSignal(str)# dato: 'Ctrl+Shift+N'

    ns_New_file = pyqtSignal(str)# dato: 'Ctrl+N'

    ns_cut = pyqtSignal(str)# dato: 'Ctrl+X'

    ns_Debug = pyqtSignal(str)# dato: 'F7'

    ns_Duplicate = pyqtSignal(str)# dato: 'Ctrl+R'

    ns_Run_file = pyqtSignal(str)# dato: 'Ctrl+F6'

    ns_Run_project = pyqtSignal(str)# dato: 'F6'

    ns_expand_file_combo = pyqtSignal(str)# dato: 'Ctrl+Tab'

    ns_expand_symbol_combo = pyqtSignal(str)# dato: 'Ctrl+2'

    ns_Find = pyqtSignal(str)# dato: 'Ctrl+F'

    ns_Find_replace = pyqtSignal(str)# dato: 'Ctrl+H'

    ns_Find_in_files = pyqtSignal(str)# dato: 'Ctrl+L'

    ns_Find_next = pyqtSignal(str)# dato: 'Ctrl+F3'

    ns_Find_previous = pyqtSignal(str)# dato: 'Shift+F3'

    ns_Find_with_word = pyqtSignal(str)# dato: 'Ctrl+Shift+F'

    ns_Full_screen = pyqtSignal(str)# dato: 'Ctrl+F11'

    ns_Go_to_definition = pyqtSignal(str)# dato: 'Ctrl+Return'

    ns_Hide_all = pyqtSignal(str)# dato: 'F11'

    ns_Hide_editor = pyqtSignal(str)# dato: 'F3'

    ns_Hide_explorer = pyqtSignal(str)# dato: 'F2'

    ns_Hide_misc = pyqtSignal(str)# dato: 'F4'

    ns_Highlight_Word = pyqtSignal(str)# dato: 'Ctrl+Down'

    ns_Import = pyqtSignal(str)# dato: 'Ctrl+I'

    ns_Indent_less = pyqtSignal(str)# dato: 'Shift+Tab'

    ns_Indent_more = pyqtSignal(str)# dato: 'Tab'

    ns_Add_Bookmark_or_Breakpoint = pyqtSignal(str)# dato: 'Ctrl+B'

    ns_Title_comment = pyqtSignal(str)# dato: ''

    ns_Horizontal_line = pyqtSignal(str)# dato: ''

    ns_Move_down = pyqtSignal(str)# dato: 'Alt+Down'

    ns_Move_up = pyqtSignal(str)# dato: 'Alt+Up'

    ns_Move_Tab_to_left = pyqtSignal(str)# dato: 'Ctrl+Shift+9'

    ns_Move_Tab_to_right = pyqtSignal(str)# dato: 'Ctrl+Shift+0'

    ns_Navigate_back = pyqtSignal(str)# dato: 'Alt+Left'

    ns_Navigate_forward = pyqtSignal(str)# dato: 'Alt+Right'

    ns_Open_file = pyqtSignal(str)# dato: 'Ctrl+O'

    ns_Open_project = pyqtSignal(str)# dato: 'Ctrl+Shift+O'

    ns_Open_recent_closed = pyqtSignal(str)# dato: 'Ctrl+Shift+T'

    ns_paste = pyqtSignal(str)# dato: 'Ctrl+V'

    ns_History_Paste = pyqtSignal(str)# dato: 'Ctrl+Alt+V'

    ns_Print_file = pyqtSignal(str)# dato: 'Ctrl+P'

    ns_Redo = pyqtSignal(str)# dato: 'Ctrl+Y'

    ns_Reload_file = pyqtSignal(str)# dato: 'F5'

    ns_Remove_line = pyqtSignal(str)# dato: 'Ctrl+E'

    ns_Save_file = pyqtSignal(str)# dato: 'Ctrl+S'

    ns_Save_project = pyqtSignal(str)# dato: 'Ctrl+Shift+S'

    ns_Code_locator = pyqtSignal(str)# dato: 'Ctrl+K'

    ns_Show_Paste_History = pyqtSignal(str)# dato: 'Ctrl+4'

    ns_File_Opener = pyqtSignal(str)# dato: 'Ctrl+Alt+O'

    ns_Help = pyqtSignal(str)# dato: 'F1'

    ns_Show_Selector = pyqtSignal(str)# dato: 'Ctrl+`'

    ns_Split_assistance = pyqtSignal(str)# dato: 'F10'

    ns_change_tab_visibility = pyqtSignal(str)# dato: 'Shift+F1'

    ns_Split_horizontal = pyqtSignal(str)# dato: 'F9'

    ns_Split_vertical = pyqtSignal(str)# dato: 'Ctrl+F9'

    ns_Stop_execution = pyqtSignal(str)# dato: 'Ctrl+Shift+F6'

    ns_Uncomment = pyqtSignal(str)# dato: 'Ctrl+Shift+G'

    ns_undo = pyqtSignal(str)# dato: 'Ctrl+Z'

    ns_preferences_interface_showProjectExplorer = pyqtSignal(bool)# dato: 'True'

    ns_preferences_interface_showSymbolsList = pyqtSignal(bool)# dato: 'True'

    ns_preferences_interface_showWebInspector = pyqtSignal(bool)# dato: 'False'

    ns_preferences_interface_showErrorsList = pyqtSignal(bool)# dato: 'True'

    ns_preferences_interface_showMigrationList = pyqtSignal(bool)# dato: 'True'

    ns_preferences_interface_language = pyqtSignal(str)# dato: 'English'

    ns_preferences_editor_font = pyqtSignal(QFont)# dato: '<PyQt5.QtGui.QFont object at 0x089D32F0>'

    ns_preferences_editor_minimapMaxOpacity = pyqtSignal(float)# dato: '0.8'

    ns_preferences_editor_minimapMinOpacity = pyqtSignal(float)# dato: '0.1'

    ns_preferences_editor_minimapSizeProportion = pyqtSignal(float)# dato: '0.17'

    ns_preferences_editor_minimapShow = pyqtSignal(bool)# dato: 'False'

    ns_preferences_editor_scheme = pyqtSignal(str)# dato: 'default'

    ns_preferences_editor_useTabs = pyqtSignal(bool)# dato: 'False'

    ns_preferences_editor_marginLine = pyqtSignal(int)# dato: '80'

    ns_preferences_editor_showMarginLine = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_indent = pyqtSignal(int)# dato: '4'

    ns_preferences_editor_platformEndOfLine = pyqtSignal(bool)# dato: 'False'

    ns_preferences_editor_errorsUnderlineBackground = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_errors = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_errorsInLine = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_checkStyle = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_showMigrationTips = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_checkStyleInline = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_centerOnScroll = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_removeTrailingSpaces = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_allowWordWrap = pyqtSignal(bool)# dato: 'False'

    ns_preferences_editor_showTabsAndSpaces = pyqtSignal(bool)# dato: 'False'

    ns_preferences_editor_showIndentationGuide = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_checkForDocstrings = pyqtSignal(bool)# dato: 'False'

    ns_preferences_editor_showLineNumbers = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_parentheses = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_brackets = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_keys = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_simpleQuotes = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_doubleQuotes = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_codeCompletion = pyqtSignal(bool)# dato: 'True'

    ns_preferences_editor_completeDeclarations = pyqtSignal(bool)# dato: 'True'

    ns_preferences_theme_skin = pyqtSignal(str)# dato: 'Default'

    ds_lastSession_projects = pyqtSignal(list)# dato: '[]'

    ds_lastSession_openedFiles = pyqtSignal(list)# dato: '[]'

    ds_lastSession_currentFile = pyqtSignal(str)# dato: ''

    ds_lastSession_recentFiles = pyqtSignal(list)# dato: '[]'

    ns_preferences_editor_bookmarks = pyqtSignal(dict)# dato: '{}'

    ns_preferences_editor_breakpoints = pyqtSignal(dict)# dato: '{}'

    ns_window_maximized = pyqtSignal(bool)# dato: 'True'

    ns_window_central_baseSplitterSize = pyqtSignal(QByteArray)# dato: 'b'\x00\x00\x00\xff\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x03\x84\x00\x00\x00\xc8\x01\xff\xff\xff\xff\x01\x00\x00\x00\x01\x01''

    ns_window_central_insideSplitterSize = pyqtSignal(QByteArray)# dato: 'b'\x00\x00\x00\xff\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x01B\x00\x00\x00\xa8\x01\xff\xff\xff\xff\x01\x00\x00\x00\x02\x01''

    ns_window_central_lateralVisible = pyqtSignal(bool)# dato: 'True'

    ns_window_hide_toolbar = pyqtSignal(bool)# dato: 'False'

    ns_tools_dock_visible = pyqtSignal(bool)# dato: 'True'

    #...
    ds_recentProjects = pyqtSignal(dict)
    ns_window_size = pyqtSignal(QSize)
    ns_window_pos = pyqtSignal(QPoint)

    def __init__(self, start_server=False):
        super(IDE, self).__init__()
        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

        IDE.__instance = self

        wid = QWidget()#adjustSize
        wid.setContentsMargins(0, 0, 0, 0)
        box = QHBoxLayout(wid)
        box.setContentsMargins(0, 0, 0, 0)
        # l1 = QLabel("Info Here")
        # l1.setObjectName("Info")
        # l1.setStyleSheet("background-color: rgb(88, 255, 85);")
        # box.addWidget(l1)
        space = QSpacerItem(10,10, QSizePolicy.Expanding)#, QSizePolicy.Maximum)
        box.addSpacerItem(space)
        l2 = QLabel("Tab Size: "+str(settings.INDENT))#int(qsettings.value('preferences/editor/indent', 4, type=int))))
        l2.setObjectName("Det1")

        font = l2.font()
        font.setPointSize(8)
        l2.setFont(font)
        box.addWidget(l2)

        box.addSpacing(50)

        l3 = QLabel("Python")
        l3.setObjectName("Det2")
        font.setPointSize(9)
        l3.setFont(font)
        box.addWidget(l3)

        box.addSpacing(30)

        status = self.statusBar()
        status.setMaximumHeight(20)
        status.addPermanentWidget(wid)
        # wid.show()
        # self.__wid = wid
        status.reformat()
        status.showMessage("Info Here")
        status.setStyleSheet("background-color: rgb(85, 85, 85);")

        #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)

        self.statusBar().messageChanged[str].connect(self.MessageStatusChanged.emit)

        #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.trayIcon.closeTrayIcon.connect(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
            short.activated.connect(self._change_tab_index)
        short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self, 10)
        short.activated.connect(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')
        print("self.central:", self.central)
        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.s_listener.newConnection.connect(self._process_connection)


    @classmethod
    def hasCreated(clss):
        return clss.__created

    @classmethod
    def getInstance(clss):
        return clss.__instance

    @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.hasCreated():
            cls.getInstance().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.hasCreated():
            cls.getInstance()._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):
                    getattr(target, signal_name).connect(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)
            editable.fileClosing.connect(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

    def showMessageStatus(self, msg):
        QTimer.singleShot(1, Qt.PreciseTimer, lambda: self.statusBar().showMessage(msg))
        # self.statusBar().showMessage(msg)

    @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")
        print("\n\npreferences!!")
        if main_container:
            main_container.show_dialog(pref)
            print("\n\nmain_container---")
        else:
            pref.show()
            print("\n\nNONE---")

    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])
                    line, col = fileData[1][0], fileData[1][1]
                    main_container.open_file(fileData[0], line, col,
                                             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:
            super(IDE, self).wheelEvent(event)

    @classmethod
    def ninja_settings(cls):
        qsettings = nsettings.NSettings(resources.SETTINGS_PATH,
                                        prefix="ns")
        if cls.hasCreated():
            qsettings.valueChanged.connect(cls.getInstance()._settings_value_changed)
        return qsettings

    @classmethod
    def data_settings(cls):
        qsettings = nsettings.NSettings(resources.DATA_SETTINGS_PATH,
                                        prefix="ds")
        if cls.hasCreated():
            qsettings.valueChanged.connect(cls.getInstance()._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)
        key = key.replace("/", "_").replace("-", "_")
        try:
            getattr(self, key).emit(value)
        except TypeError as reason:
            print("\n:::", key, value, type(value))
            print("\n\nerrors:-:", reason)
            getattr(self, key).emit()
        except AttributeError:
            print("\n:::", key, value, type(value))

        # if not value:
        #     try:
        #         getattr(self, key.replace("/", "_")).emit(value)
        #     except TypeError:
        #         getattr(self, key.replace("/", "_")).emit()

        #     return

        # try:
        #     getattr(self, key.replace("/", "_")).emit(value)
        # except TypeError as e:
        #     print("\n\nerrors", e)
        #     getattr(self, key.replace("/", "_")).emit()
        ##getattr(self, key.replace("/", "_").replace("-", "_")).emit(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:
                if not openedFiles[path]._exists():
                    print("\n\ncontinue", path)
                    continue
                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.getCursorPosition(),
                                  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)
        qsettings.setValue('preferences/editor/bookmarks',
                           settings.BOOKMARKS)
        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
        qsettings.setValue("window/hide_toolbar", not self.toolbar.isVisible())

        #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 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",
                QSize(800, 600), type='QSize'))
            self.move(qsettings.value(
                "window/pos",
                QPoint(100, 100), 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)
            print("\n\neditable::", editable, getattr(editable, "editor", "-"))
            if editable is not None and  editable.editor 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.goingDown.emit()
        #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")
        print("\nshow_plugins_store")
        if main_container:
            print("\nshow_plugins_store::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()
Ejemplo n.º 4
0
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 = pyqtSignal()
    filesAndProjectsLoaded = pyqtSignal()

    __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.trayIcon.closeTrayIcon.connect(self._close_tray_icon)
        # self.trayIcon.show()

        # TODO:
        # 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
        #    short.activated.connect(self._change_tab_index)
        # short = ui_tools.TabShortcuts(
        #       QKeySequence(Qt.ALT + Qt.Key_0), self, 10)
        # short.activated.connect(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",
            "slot": self.show_message
        }, {
            "target": "main_container",
            "signal_name": "currentEditorChanged",
            "slot": self.change_window_title
        }, {
            "target": "main_container",
            "signal_name": "openPreferences",
            "slot": self.show_preferences
        }, {
            "target": "main_container",
            "signal_name": "currentEditorChanged",
            "slot": self._change_item_in_project
        })
        self.register_signals('ide', connections)
        # connections = (
        #    {'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},
        #    )
        # 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:
            # These two are the same service, I think that's ok
            menu_bar.load_menu(self)
            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.s_listener.newConnection.connect(self._process_connection)

        IDE.__instance = self

    def _change_item_in_project(self, filename):
        project_explorer = IDE.get_service("projects_explorer")
        if project_explorer is not None:
            project_explorer.set_current_item(filename)

    @classmethod
    def get_service(cls, service_name):
        """Return the instance of a registered service."""

        service = cls.__IDESERVICES.get(service_name, None)
        if service is None:
            logger.debug("Service '{}' unregistered".format(service_name))
        return service

    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):
                    # FIXME:
                    sl = getattr(target, signal_name, None)

                    if sl is not None:
                        sl.connect(slot)
                        connection['connected'] = True

                    # print("Falta conectar {} a {}".format(signal_name,
                    #                                      slot.__name__))
                    # 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)
            editable.fileClosing['PyQt_PyObject'].connect(
                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.setModal(True)
        # 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."""
        # Load projects
        projects_explorer = IDE.get_service('projects_explorer')
        projects_explorer.load_session_projects(projects)
        # Load files
        main_container = IDE.get_service('main_container')
        for path, cursor_pos in files:
            line, col = cursor_pos
            main_container.open_file(path, line, col)
        if current_file:
            main_container.open_file(current_file)
        self.filesAndProjectsLoaded.emit()
        # 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])
        #            line, col = fileData[1][0], fileData[1][1]
        #            main_container.open_file(fileData[0], line, col,
        #                                     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."""
        # display_name - project - NINJA-IDE
        if self._session is None:
            self.setWindowTitle('%s - NINJA-IDE' % 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 = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        return qsettings

    @classmethod
    def data_settings(cls):
        qsettings = QSettings(resources.DATA_SETTINGS_PATH,
                              QSettings.IniFormat)
        return qsettings

    # @classmethod
    # def ninja_settings(cls, qobject=None):
    #    qsettings = nsettings.NSettings(resources.SETTINGS_PATH, qobject,
    #                                    prefix="ns")
    #    if cls.__created:
    #        qsettings.valueChanged['PyQt_PyObject',
    #                               'QString',
    #                               'PyQt_PyObject'].connect(
    #                                   cls.__instance._settings_value_changed)
    #    return qsettings

    # @classmethod
    # def data_settings(cls):
    #    qsettings = nsettings.NSettings(resources.DATA_SETTINGS_PATH,
    #                                    prefix="ds")
    #    if cls.__created:
    #        qsettings.valueChanged['PyQt_PyObject',
    #                               'QString',
    #                               'PyQt_PyObject'].connect(
    #                                   cls.__instance._settings_value_changed)
    #    return qsettings

    # def _settings_value_changed(self, qobject, key, value):
    # signal_name = "%s(PyQt_PyObject)" % key.replace("/", "_")
    # signal_name = "%s" % key.replace("/", "_")
    #    print(qobject, key)
    # callback = getattr(self, signal_name, None)
    # if hasattr(callback, "__call__"):
    #    callback()
    # print(signal_name, value)
    # self.emit(SIGNAL(signal_name), value)
    # print("Falta emitir {}".format(signal_name))

    def save_settings(self):
        """
        Save the settings before the application is closed with QSettings.

        Info saved: files and projects opened,
        windows state(size and position).
        """

        data_settings = IDE.data_settings()
        ninja_settings = IDE.ninja_settings()
        # Get opened files
        opened_files = self.filesystem.get_files()
        files_info = []
        for path in opened_files:
            editable = self.__neditables.get(opened_files[path])
            files_info.append((path, editable.editor.cursor_position))
        data_settings.setValue('last_session/opened_files', files_info)
        # Current opened file
        main_container = self.get_service("main_container")
        neditor = main_container.get_current_editor()
        current_file = ''
        if neditor is not None:
            current_file = neditor.file_path
        data_settings.setValue('last_session/current_file', current_file)
        # Save toolbar visibility
        ninja_settings.setValue('window/hide_toolbar',
                                not self.toolbar.isVisible())

        # Get opened projects
        projects_obj = self.filesystem.get_projects()
        # rojects = [projects_obj[project].path for project in projects_obj]
        projects = [projects_obj[project].path for project in projects_obj]
        data_settings.setValue('last_session/projects', projects)
        # Save window state
        if self.isMaximized():
            ninja_settings.setValue("window/maximized", True)
        else:
            ninja_settings.setValue("window/maximized", False)
            ninja_settings.setValue("window/size", self.size())
            ninja_settings.setValue("window/pos", self.pos())
        data_settings.setValue('lastSession/openedFiles', files_info)
        # projects_obj = self.filesystem.get_projects()
        # projects = [projects_obj[proj].path for proj in projects_obj]
        # data_settings.setValue('lastSession/projects', projects)
        # 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.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)
        # qsettings.setValue('preferences/editor/bookmarks',
        #                   settings.BOOKMARKS)
        # 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
        # qsettings.setValue("window/hide_toolbar", not self.toolbar.isVisible())
        # 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 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)))
            self.move(qsettings.value("window/pos", QPointF(100, 100)))

    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(nfile=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.goingDown.emit()
        # 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."""
        print(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()
Ejemplo n.º 5
0
class App(QMainWindow):

    @property
    def QSETTINGS(self):
        return QSettings(QSettings.IniFormat, QSettings.UserScope, "mdviewer", "session")

    def __init__(self, parent=None, filename=""):
        QMainWindow.__init__(self, parent)
        self.filename = filename or os.path.join(app_dir, u"README.md")

        # Set environment variables
        self.set_env()

        # Configure Preview window
        self.set_window_title()
        self.resize(self.QSETTINGS.value("size", QSize(800,400)))
        self.move(self.QSETTINGS.value("pos", QPoint(0,0)))

        # Activate WebView
        self.web_view = QWebView()
        self.setCentralWidget(self.web_view)

        self.scroll_pos = {}

        # Configure and start file watcher thread
        self.thread1 = WatcherThread(self.filename)
        self.thread1.update.connect(self.update)
        self.watcher = QFileSystemWatcher([self.filename])
        self.watcher.fileChanged.connect(self.thread1.run)
        self.thread1.run()

        self.web_view.loadFinished.connect(self.after_update)

        # Get style sheet
        self.stylesheet = self.QSETTINGS.value("stylesheet", "default.css")

        # Set GUI menus and toolbars
        self.set_menus()
        self.set_search_bar()

    def set_env(self):
        path, name = os.path.split(os.path.abspath(self.filename))
        ext = os.path.splitext(name)[-1].lower()
        os.environ["MDVIEWER_EXT"] = ext[1:]
        os.environ["MDVIEWER_FILE"] = name
        os.environ["MDVIEWER_ORIGIN"] = path

    def set_window_title(self):
        _path, name = os.path.split(os.path.abspath(self.filename))
        self.setWindowTitle(u"%s – MDviewer" % (name))

    def update(self, text, warn):
        "Update Preview."

        self.web_view.settings().setAttribute(QWebSettings.JavascriptEnabled, True)
        self.web_view.settings().setAttribute(QWebSettings.PluginsEnabled, True)
        self.web_view.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True)

        # Configure link behavior
        self.web_view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
        self.web_view.linkClicked.connect(lambda url: self.handle_link_clicked(url))

        # Save scroll position
        if not self.web_view.page().currentFrame().scrollPosition() == QPoint(0,0):
            self.scroll_pos[self.filename] = self.web_view.page().currentFrame().scrollPosition()

        # Update Preview
        self.web_view.setHtml(text, baseUrl=QUrl.fromLocalFile(os.path.join(os.getcwd(), self.filename)))

        # Load JavaScript and core CSS
        scr = os.path.join(app_dir, "mdviewer.js")
        css = os.path.join(app_dir, "mdviewer.css")
        add_resources = """
        (function() {
            var head = document.querySelector("head");
            var css = document.createElement("link");
            css.rel = "stylesheet";
            css.href = "%s";
            css.id = "coreCSS";
            head.appendChild(css);
            var script = document.createElement("script");
            script.type = "text/javascript";
            script.src = "%s";
            script.id = "coreJS";
            script.setAttribute("defer", "");
            head.appendChild(script);
        })()
        """ % (css, scr)
        self.web_view.page().currentFrame().evaluateJavaScript(add_resources)

        # Display processor warnings
        if warn: QMessageBox.warning(self, "Processor message", warn)

    def after_update(self):
        "Restore scroll position."

        try:
            pos = self.scroll_pos[self.filename]
        except KeyError:
            pass
        else:
            self.web_view.page().currentFrame().evaluateJavaScript("window.scrollTo(%s, %s)" % (pos.x(), pos.y()))

    def open_file(self):
        filename, _filter = QFileDialog.getOpenFileName(self, "Open File", os.path.dirname(self.filename))
        if filename != "":
            self.filename = self.thread1.filename = filename
            self.set_env()
            self.set_window_title()
            self.thread1.run()
        else:
            pass

    def save_html(self):
        filename, _filter = QFileDialog.getSaveFileName(self, "Save File", os.path.dirname(self.filename))
        if filename != "":
            proc = Settings.get("processor_path", "pandoc")
            args = Settings.get("processor_args", "")
            args = ("%s" % (args)).split() + [self.filename]
            caller = QProcess()
            caller.start(proc, args)
            caller.waitForFinished()
            html = str(caller.readAllStandardOutput(), "utf8")
            with io.open(filename, "w", encoding="utf8") as f:
                f.writelines(html)
                f.close()
        else:
            pass

    def find(self, text, btn=""):
        page = self.web_view.page()
        back = page.FindFlags(1) if btn is self.prev else page.FindFlags(0)
        case = page.FindFlags(2) if self.case.isChecked() else page.FindFlags(0)
        wrap = page.FindFlags(4) if self.wrap.isChecked() else page.FindFlags(0)
        page.findText("", page.FindFlags(8))
        page.findText(text, back | wrap | case)

    def set_search_bar(self):
        self.search_bar = QToolBar()
        self.search_bar.setMovable(False)
        self.search_bar.setFloatable(False)
        self.search_bar.setVisible(False)
        self.search_bar.layout().setSpacing(1)
        self.addToolBar(0x8, self.search_bar)

        self.text = QLineEdit(self)
        self.text.setClearButtonEnabled(True)
        self.text.setPlaceholderText(u"Search")
        self.case = QCheckBox(u"Case sensitive", self)
        self.wrap = QCheckBox(u"Wrap", self)
        self.next = QPushButton(u"Next", self)
        self.next.setToolTip(u"Find next")
        self.next.setShortcut(QKeySequence("Return"))
        self.next.setDisabled(True)
        self.prev = QPushButton(u"Previous", self)
        self.prev.setToolTip(u"Find previous")
        self.prev.setShortcut(QKeySequence("Shift+Return"))
        self.prev.setDisabled(True)
        self.done = QPushButton(u"Done", self)
        self.done.setToolTip(u"Hide Search bar")
        self.done.setShortcut(QKeySequence("Esc"))

        def _enable_nav():
            if self.text.text() == "":
                self.next.setDisabled(True)
                self.prev.setDisabled(True)
            else:
                self.next.setDisabled(False)
                self.prev.setDisabled(False)

        def _toggle_btn(btn=""):
            self.text.setFocus()
            self.find(self.text.text(), btn)

        def _hide():
            if self.search_bar.isVisible():
                self.search_bar.hide()

        self.search_bar.addWidget(self.done)
        self.search_bar.addSeparator()
        self.search_bar.addWidget(self.case)
        self.search_bar.addWidget(self.wrap)
        self.search_bar.addWidget(self.text)
        self.search_bar.addSeparator()
        self.search_bar.addWidget(self.next)
        self.search_bar.addWidget(self.prev)
        for btn in (self.prev, self.next):
            btn.pressed[()].connect(lambda btn = btn: _toggle_btn(btn))
        self.done.pressed.connect(_hide)
        self.text.textChanged.connect(self.find)
        self.text.textChanged.connect(_enable_nav)

    def show_search_bar(self):
        self.search_bar.show()
        self.text.setFocus()
        self.text.selectAll()

    def print_doc(self):
        dialog = QPrintPreviewDialog()
        dialog.paintRequested.connect(self.web_view.print_)
        dialog.exec_()

    def quit(self, QCloseEvent):
        self.QSETTINGS.setValue("size", self.size())
        self.QSETTINGS.setValue("pos", self.pos())
        self.QSETTINGS.setValue("stylesheet", self.stylesheet)

        QtWidgets.qApp.quit()

    def zoom_in(self):
        self.web_view.setZoomFactor(self.web_view.zoomFactor()+.1)

    def zoom_out(self):
        self.web_view.setZoomFactor(self.web_view.zoomFactor()-.1)

    def zoom_reset(self):
        self.web_view.setZoomFactor(1)

    def scroll_down(self):
        self.web_view.page().currentFrame().scroll(0, +self.web_view.page().viewportSize().height())

    def scroll_up(self):
        self.web_view.page().currentFrame().scroll(0, -self.web_view.page().viewportSize().height())

    def toggle_toc(self):
        self.web_view.page().currentFrame().evaluateJavaScript("toggleTOC()")

    def handle_link_clicked(self, url):
        if url.isLocalFile():
            if url.toLocalFile() == os.path.abspath(self.filename) and url.hasFragment():
                self.web_view.page().currentFrame().scrollToAnchor(url.fragment())
                return
            else:
                QDesktopServices.openUrl(url)
        else:
            QDesktopServices.openUrl(url)

    @staticmethod
    def set_stylesheet(self, stylesheet="default.css"):
        path = os.path.join(css_dir, stylesheet)
        url = QUrl.fromLocalFile(path)
        self.web_view.settings().setUserStyleSheetUrl(url)
        self.stylesheet = stylesheet

    def about(self):
        msg_about = QMessageBox(0, "About MDviewer", u"MDviewer\n\nVersion: %s" % (VERSION), parent=self)
        msg_about.show()

    def report_issue(self):
        url = QUrl.fromUserInput("https://github.com/saf-dmitry/mdviewer/issues")
        QDesktopServices.openUrl(url)

    def set_menus(self):
        menubar = self.menuBar()

        file_menu = menubar.addMenu("&File")

        for d in (
                {"name": u"&Open...",      "shct": "Ctrl+O", "func": self.open_file},
                {"name": u"&Save HTML...", "shct": "Ctrl+S", "func": self.save_html},
                {"name": u"&Find...",      "shct": "Ctrl+F", "func": self.show_search_bar},
                {"name": u"&Print...",     "shct": "Ctrl+P", "func": self.print_doc},
                {"name": u"&Quit",         "shct": "Ctrl+Q", "func": self.quit}
                 ):
            action = QAction(d["name"], self)
            action.setShortcut(QKeySequence(d["shct"]))
            action.triggered.connect(d["func"])
            file_menu.addAction(action)

        view_menu = menubar.addMenu("&View")

        for d in (
                {"name": u"Zoom &In",     "shct": "Ctrl++", "func": self.zoom_in},
                {"name": u"Zoom &Out",    "shct": "Ctrl+-", "func": self.zoom_out},
                {"name": u"&Actual Size", "shct": "Ctrl+0", "func": self.zoom_reset}
                 ):
            action = QAction(d["name"], self)
            action.setShortcut(QKeySequence(d["shct"]))
            action.triggered.connect(d["func"])
            view_menu.addAction(action)

        style_menu = menubar.addMenu("&Style")
        style_menu.setStyleSheet("menu-scrollable: 1")
        style_menu.setDisabled(True)

        if os.path.exists(css_dir):
            files = sorted(os.listdir(css_dir))
            files = [f for f in files if f.endswith(".css")]
            if len(files) > 0:
                style_menu.setDisabled(False)
                group = QActionGroup(self, exclusive=True)
                for i, f in enumerate(files, start=1):
                    name = os.path.splitext(f)[0].replace("&", "&&")
                    action = group.addAction(QtWidgets.QAction(name, self))
                    action.triggered.connect(
                        lambda x, stylesheet = f: self.set_stylesheet(self, stylesheet))
                    if i < 10: action.setShortcut(QKeySequence("Ctrl+%d" % i))
                    action.setCheckable(True)
                    style_menu.addAction(action)
                    if f == self.stylesheet: action.trigger()

        help_menu = menubar.addMenu("&Help")

        for d in (
                {"name": u"&About...",        "func": self.about},
                {"name": u"&Report an Issue", "func": self.report_issue},
                 ):
            action = QAction(d["name"], self)
            action.triggered.connect(d["func"])
            help_menu.addAction(action)

        # Redefine reload action
        reload_action = self.web_view.page().action(QWebPage.Reload)
        reload_action.setShortcut(QKeySequence.Refresh)
        reload_action.triggered.connect(self.thread1.run)
        self.web_view.addAction(reload_action)

        # Define additional shortcuts
        QShortcut(QKeySequence("j"), self, activated=self.scroll_down)
        QShortcut(QKeySequence("k"), self, activated=self.scroll_up)
        QShortcut(QKeySequence("t"), self, activated=self.toggle_toc)

    def closeEvent(self, event):
        self.quit(event)
        event.accept()
Ejemplo n.º 6
0
class MainWindow(QMainWindow):
    """MainWindow class for Paint4Brains.

    This class contains the implementation of a series of functions required for the Main Windows of the Paint4Brains GUI.
    Wraps MainWidget to allow a Menu.
    To operate, the constructor class calls the MainWidget class which contains the bulk of the gui and enables the use of menus.
    Because of this most of this class is dedicated to defining menu entries and actions to be added to these entries
    Another thing it does is load the nib files from a string containing the path.

    Args:
        file (str): Path leading to the location of the brain data file.
        label_file (str): Path to the location of the labeled data file.
    """
    def __init__(self, file, label_file=None):

        super(MainWindow, self).__init__()

        if file is None:
            file = self.load_initial()

        self.brain = BrainData(file, label_file)
        self.main_widget = MainWidget(self.brain, self)
        self.setCentralWidget(self.main_widget)
        self.setWindowTitle("Paint4Brains")

        # Making a menu
        menu_bar = self.menuBar()
        self.file = menu_bar.addMenu("File")
        self.edit = menu_bar.addMenu("Edit")
        self.view_menu = menu_bar.addMenu("View")
        self.tools = menu_bar.addMenu("Tools")
        self.help = menu_bar.addMenu("Help")

        # Actions in file bar (This enables shortcuts too)
        loadAction = QAction('Load Label', self)
        loadAction.setShortcut('Ctrl+L')
        loadAction.setStatusTip('Load Labels')
        loadAction.triggered.connect(self.load)
        self.file.addAction(loadAction)

        saveAction = QAction('Save', self)
        saveAction.setStatusTip('Save Labels')
        saveAction.triggered.connect(self.save)
        self.file.addAction(saveAction)

        saveAsAction = QAction('Save As', self)
        saveAsAction.setShortcut('Ctrl+S')
        saveAsAction.setStatusTip('Save labels to selected file')
        saveAsAction.triggered.connect(self.save_as)
        self.file.addAction(saveAsAction)

        # Predefined actions that usually appear when you right click. Recycling one that resets the view here.
        viewBoxActionsList = self.main_widget.win.view.menu.actions()

        oldButtonsAction = QAction('Single View Mode', self)
        oldButtonsAction.setStatusTip('Sets layout to single window mode')
        oldButtonsAction.triggered.connect(
            self.main_widget.revert_to_old_buttons)

        viewToolbarAction = QAction("Editing Toolbar", self)
        viewToolbarAction.setStatusTip("View Editing Toolbar")
        viewToolbarAction.triggered.connect(self.view_edit_tools)

        for i in [0]:  # range(1, len(viewBoxActionsList)):
            ViewActions = viewBoxActionsList[i]
            self.view_menu.addAction(ViewActions)

        self.view_menu.addSeparator()
        self.view_menu.addAction(oldButtonsAction)
        self.view_menu.addSeparator()
        self.view_menu.addAction(viewToolbarAction)

        viewVisualizationAction = QAction("Visualization Toolbar", self)
        viewVisualizationAction.setStatusTip("View Visualization Toolbar")
        viewVisualizationAction.triggered.connect(
            self.view_visualization_tools)
        self.view_menu.addAction(viewVisualizationAction)
        self.view_menu.addSeparator()

        resetViewAction = viewBoxActionsList[0]
        resetViewAction.setText("Recenter View")
        resetViewAction.setShortcut('Ctrl+V')
        self.view_menu.addAction(resetViewAction)

        seeAllAction = QAction('All Labels', self)
        seeAllAction.setShortcut('Ctrl+A')
        seeAllAction.setStatusTip('Edit Next Segmented label')
        seeAllAction.triggered.connect(self.main_widget.win.view_back_labels)
        self.view_menu.addAction(seeAllAction)

        nextLabelAction = QAction('Next Label', self)
        nextLabelAction.setShortcut('Ctrl+N')
        nextLabelAction.setStatusTip('Edit Next Segmented label')
        nextLabelAction.triggered.connect(self.main_widget.win.next_label)
        self.edit.addAction(nextLabelAction)

        prevLabelAction = QAction('Previous Label', self)
        prevLabelAction.setShortcut('Ctrl+M')
        prevLabelAction.setStatusTip('Edit Previous Segmented label')
        prevLabelAction.triggered.connect(self.main_widget.win.previous_label)
        self.edit.addAction(prevLabelAction)

        selectLabelAction = QAction('Select Label', self)
        selectLabelAction.setStatusTip('Select Label to be edited')
        selectLabelAction.triggered.connect(self.main_widget.win.select_label)
        self.edit.addAction(selectLabelAction)
        self.edit.addSeparator()

        nodrawAction = QAction('Drawing Mode', self)
        nodrawAction.setShortcut('Ctrl+D')
        nodrawAction.setStatusTip('Activate/Deactivate drawing mode')
        nodrawAction.triggered.connect(self.main_widget.win.disable_drawing)
        self.edit.addAction(nodrawAction)
        self.edit.addSeparator()

        undoAction = QAction('Undo', self)
        undoAction.setShortcut('Ctrl+Z')
        undoAction.setStatusTip('Undo previous edit')
        undoAction.triggered.connect(self.main_widget.win.undo_previous_edit)
        self.edit.addAction(undoAction)

        undoAction = QAction('Redo', self)
        undoAction.setShortcut('Ctrl+Shift+Z')
        undoAction.setStatusTip('Revert to previous edit')
        undoAction.triggered.connect(self.main_widget.win.redo_previous_edit)
        self.edit.addAction(undoAction)

        histogramAction = QAction('Adjust Brain Intensity', self)
        histogramAction.setShortcut('Ctrl+H')
        histogramAction.setStatusTip('View Intensity Histogram')
        histogramAction.triggered.connect(self.view_histogram)
        histogramAction.triggered.connect(self.main_widget.normalize_intensity)
        self.tools.addAction(histogramAction)

        extractAction = QAction('Extract Brain', self)
        extractAction.setShortcut('Ctrl+E')
        extractAction.setStatusTip('Extract Brain')
        extractAction.triggered.connect(self.main_widget.extract)
        self.tools.addAction(extractAction)

        unextractAction = QAction('See Full Brain', self)
        unextractAction.setShortcut('Ctrl+U')
        unextractAction.setStatusTip('See Full Brain')
        unextractAction.triggered.connect(self.main_widget.full_brain)
        self.tools.addAction(unextractAction)

        segmentAction = QAction('Segment Brain', self)
        segmentAction.setShortcut('Ctrl+W')
        segmentAction.setStatusTip('Segment Brain')
        segmentAction.triggered.connect(self.segment)
        self.tools.addAction(segmentAction)

        # Editing tools as a toolbar
        current_directory = os.path.dirname(os.path.realpath(__file__))

        pen = QAction(QIcon(current_directory + "/images/pen.png"),
                      "Pencil: Draw Pixels", self)
        pen.triggered.connect(self.main_widget.win.edit_button1)

        rubber = QAction(QIcon(current_directory + "/images/eraser.png"),
                         "Eraser: Remove Drawn Pixels", self)
        rubber.triggered.connect(self.main_widget.win.edit_button2)

        cross = QAction(QIcon(current_directory + "/images/cross.png"),
                        "Brush: Draw Multiple Pixels", self)
        cross.triggered.connect(self.main_widget.win.edit_button3)

        bonus = QAction(QIcon(current_directory + "/images/star.png"),
                        "Custom: Make Your Own Brush", self)
        bonus.triggered.connect(self.main_widget.win.bonus_brush)

        left = QAction(QIcon(current_directory + "/images/left.png"),
                       "Previous Label: Go To Previously Selected Label", self)
        left.triggered.connect(self.main_widget.win.previous_label)

        label = QAction(QIcon(current_directory + "/images/label.png"),
                        "Select Label: Select Individual Label", self)
        label.triggered.connect(self.main_widget.win.select_label)

        right = QAction(QIcon(current_directory + "/images/right.png"),
                        "Next Label: Go To The Next Label", self)
        right.triggered.connect(self.main_widget.win.next_label)

        self.edit_toolbar = self.addToolBar("Editing Tools")
        self.edit_toolbar.setIconSize(QSize(40, 40))
        self.edit_toolbar.addSeparator()
        self.edit_toolbar.addAction(pen)
        self.edit_toolbar.addAction(cross)
        self.edit_toolbar.addAction(rubber)
        self.edit_toolbar.addAction(bonus)
        self.edit_toolbar.addSeparator()
        self.edit_toolbar.addSeparator()
        self.edit_toolbar.addAction(left)
        self.edit_toolbar.addAction(label)
        self.edit_toolbar.addAction(right)
        self.edit_toolbar.addSeparator()
        self.edit_toolbar.addSeparator()
        self.edit_toolbar.addWidget(self.main_widget.win.dropbox)
        self.edit_toolbar.setVisible(False)

        self.optional_sliders = QToolBar()
        self.addToolBar(Qt.RightToolBarArea, self.optional_sliders)
        self.optional_sliders.addWidget(OptionalSliders(self.main_widget.win))
        self.optional_sliders.setVisible(False)

        # Making the Histogram Tab invisible as long as Intensity Histogram has not yet been clicked
        self.hist_widget = HistogramWidget(self.main_widget.win)
        self.hist_widget.setVisible(False)

    def load_initial(self):
        """Original brain loader 

        Loads the "base" brain.
        The pre-segmentation scan has to be uploaded before the gui is initialised!
        This can be done either through the command line beforehand (this can be set in pycharm too) or through a window that appears on start (gets annoying).
        If you try to open it with nothing it complains and gives you an error message.

        Raises:
            Error: Failed to load!
        """
        self.data_filename = QFileDialog.getOpenFileName(
            self, "Load brain MRI", "Please select full MRI scan",
            "Nii Files (*.nii *.nii.gz)")
        if isinstance(self.data_filename, tuple):
            self.data_filename = self.data_filename[0]  # Qt4/5 API difference
        if self.data_filename == '':
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Warning)
            msg.setText("Can not start without initial brain")
            msg.setInformativeText(
                "Please load a brain image either via the command line or via the initial load window"
            )
            msg.setWindowTitle("Error: Failed to load")
            msg.setStandardButtons(QMessageBox.Ok)
            msg.exec_()
            return  # self.load_initial()
        return self.data_filename

    def load(self):
        """Labelled data loader.

        This function loads labelled data.
        Opens a loading window through which you can select what label data file to load. 
        Once a file is uploaded it automatically sets the app to drawing mode
        """
        self.label_filename = QFileDialog.getOpenFileName(
            self, "Load labeled data", os.path.dirname(self.brain.filename),
            "Nii Files (*.nii *.nii.gz)")
        if isinstance(self.label_filename, tuple):
            self.label_filename = self.label_filename[0]
        if self.label_filename == '':
            return
        self.brain.load_label_data(self.label_filename)
        self.main_widget.win.see_all_labels = True
        self.main_widget.win.enable_drawing()
        self.main_widget.win.update_colormap()
        self.main_widget.win.refresh_image()

    def save_as(self):
        """Labelled data saver with a new name

        This function saves the edited labelled data into a new file
        Saves edits into a new .nii file. Opens a window in which you can type the name of the new file you are saving.
        It still does not copy the headers (something to do)
        """
        saving_filename = QFileDialog.getSaveFileName(
            self, "Save Image", os.path.dirname(self.brain.filename),
            "Nii Files (*.nii *.nii.gz)")
        if (saving_filename[0])[-4:] != ".nii" and (
                saving_filename[0])[-7:] != ".nii.gz":
            saving_filename = saving_filename[0] + ".nii.gz"
        else:
            saving_filename = saving_filename[0]
        self.brain.save_label_data(saving_filename)

    def save(self):
        """Labelled data saver

        Saves the edited labelled data into a previously saved file
        Saves edits into a new .nii file. If no file has been saved before it reverts to save_as
        It still does not copy the headers (something to do)
        """
        if self.brain.saving_filename is None:
            self.save_as()
        else:
            self.brain.save_label_data([self.brain.saving_filename])

    def view_edit_tools(self):
        """Toggle editing toolbar

        Switch the toolbar with editing buttons to visible or invisible
        Makes it the opposite of what it was previously
        """
        switch = not self.edit_toolbar.isVisible()
        self.edit_toolbar.setVisible(switch)

    def view_visualization_tools(self):
        """Toggle visualization toolbar

        Switch the toolbar with editing buttons to visible or invisible
        Makes it the opposite of what it was previously
        """
        switch = not self.optional_sliders.isVisible()
        self.optional_sliders.setVisible(switch)

    def view_histogram(self):
        """Toggle histogram widget

        Opens an intensity histogram for all voxels in the nii file.
        This enables the user to make intensity corrections on the loaded brain.
        """
        switch = not self.hist_widget.isVisible()
        self.hist_widget.setVisible(switch)

    def segment(self):
        """Call the segmentation function

        Method that returns a segmented brain
        This function calls the brainSegmentation function in BrainData, which transforms (pre-processes) the brain file and then calls QuickNAT for running the file.
        """
        SegmentManager(self)
Ejemplo n.º 7
0
class Pireal(QMainWindow):
    """
    Main Window class

    This class is responsible for installing all application services.
    """

    __SERVICES = {}
    __ACTIONS = {}

    # The name of items is the connection text
    TOOLBAR_ITEMS = [
        'create_database',
        'open_database',
        'save_database',
        '',  # Is a separator!
        'new_query',
        'open_query',
        'save_query',
        '',
        'relation_menu',
        '',
        # 'create_new_relation',
        # 'remove_relation',
        # '',
        # 'add_tuple',
        # 'delete_tuple',
        # 'add_column',
        # 'delete_column',
        # '',
        'execute_queries'
    ]

    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle(self.tr("Pireal"))
        self.setMinimumSize(880, 600)
        # Load window geometry
        qsettings = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat)
        window_maximized = qsettings.value('window_max', True, type=bool)
        if window_maximized:
            self.showMaximized()
        else:
            size = qsettings.value('window_size')
            self.resize(size)
            position = qsettings.value('window_pos')
            self.move(position)
        # Toolbar
        self.toolbar = QToolBar(self)
        self.toolbar.setFixedWidth(38)
        self.toolbar.setIconSize(QSize(38, 38))
        self.toolbar.setMovable(False)
        self.addToolBar(Qt.RightToolBarArea, self.toolbar)

        # Menu bar
        menubar = self.menuBar()
        self.__load_menubar(menubar)
        # Load notification widget after toolbar actions
        notification_widget = Pireal.get_service("notification")
        self.toolbar.addWidget(notification_widget)
        # Message error
        # self._msg_error_widget = message_error.MessageError(self)
        # Central widget
        central_widget = Pireal.get_service("central")
        central_widget.databaseSaved.connect(notification_widget.show_text)
        central_widget.querySaved.connect(notification_widget.show_text)
        central_widget.databaseConected.connect(self.change_title)
        self.setCentralWidget(central_widget)
        central_widget.add_start_page()

        # Install service
        Pireal.load_service("pireal", self)

    @classmethod
    def get_service(cls, service):
        """ Return the instance of a loaded service """

        return cls.__SERVICES.get(service, None)

    @classmethod
    def load_service(cls, name, instance):
        """ Load a service providing the service name and the instance """

        cls.__SERVICES[name] = instance

    @classmethod
    def get_action(cls, name):
        """ Return the instance of a loaded QAction """

        return cls.__ACTIONS.get(name, None)

    @classmethod
    def load_action(cls, name, action):
        """ Load a QAction """

        cls.__ACTIONS[name] = action

    def __load_menubar(self, menubar):
        """
        This method installs the menubar and toolbar, menus and QAction's,
        also connects to a slot each QAction.
        """

        # Keymap
        kmap = keymap.KEYMAP

        central = Pireal.get_service("central")

        # Load menu bar
        rela_actions = []
        for item in menu_actions.MENU:
            menubar_item = menu_actions.MENU[item]
            menu_name = menubar_item['name']
            items = menubar_item['items']
            menu = menubar.addMenu(menu_name)
            for menu_item in items:
                if isinstance(menu_item, str):
                    # Is a separator
                    menu.addSeparator()
                else:
                    action = menu_item['name']
                    obj_name, connection = menu_item['slot'].split(':')
                    obj = central
                    if obj_name.startswith('pireal'):
                        obj = self
                    qaction = menu.addAction(action)
                    # Icon name is connection
                    icon = QIcon(":img/%s" % connection)
                    qaction.setIcon(icon)

                    # Install shortcuts
                    shortcut = kmap.get(connection, None)
                    if shortcut is not None:
                        qaction.setShortcut(shortcut)

                    # The name of QAction is the connection
                    if item == "relation":
                        if connection != "execute_queries":
                            rela_actions.append(qaction)
                    Pireal.load_action(connection, qaction)
                    slot = getattr(obj, connection, None)
                    if isinstance(slot, Callable):
                        qaction.triggered.connect(slot)

        # Install toolbar
        # self.__install_toolbar(toolbar_items, rela_actions)
        self.__install_toolbar(rela_actions)
        # Disable some actions
        self.set_enabled_db_actions(False)
        self.set_enabled_relation_actions(False)
        self.set_enabled_query_actions(False)
        self.set_enabled_editor_actions(False)

    def __install_toolbar(self, rela_actions):
        menu = QMenu()
        tool_button = QToolButton()
        tool_button.setIcon(QIcon(":img/create_new_relation"))
        tool_button.setMenu(menu)
        tool_button.setPopupMode(QToolButton.InstantPopup)
        for item in self.TOOLBAR_ITEMS:
            if item:
                if item == "relation_menu":
                    # Install menu for relation
                    menu.addActions(rela_actions)
                    self.toolbar.addWidget(tool_button)
                else:
                    self.toolbar.addAction(self.__ACTIONS[item])
            else:
                self.toolbar.addSeparator()

    def __show_status_message(self, msg):
        status = Pireal.get_service("status")
        status.show_message(msg)

    def __on_thread_update_finished(self):
        self._thread.quit()
        # Clear notificator
        notification_widget = Pireal.get_service("notification")
        notification_widget.clear()

        msg = QMessageBox(self)
        if not self._updater.error and self._updater.version:
            version = self._updater.version
            msg.setWindowTitle(self.tr("New version available!"))
            msg.setText(
                self.tr("Check the web site to "
                        "download <b>Pireal {}</b>".format(version)))
            download_btn = msg.addButton(self.tr("Download!"),
                                         QMessageBox.YesRole)
            msg.addButton(self.tr("Cancel"), QMessageBox.RejectRole)
            msg.exec_()
            r = msg.clickedButton()
            if r == download_btn:
                webbrowser.open_new("http://centaurialpha.github.io/pireal")
        self._thread.deleteLater()
        self._updater.deleteLater()

    def change_title(self, title=''):
        if title:
            _title = title + " - Pireal "
        else:
            _title = "Pireal"
        self.setWindowTitle(_title)

    def set_enabled_db_actions(self, value):
        """ Public method. Enables or disables db QAction """

        actions = [
            'new_query', 'open_query', 'close_database', 'save_database',
            'save_database_as', 'load_relation'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def set_enabled_relation_actions(self, value):
        """ Public method. Enables or disables relation's QAction """

        actions = [
            'create_new_relation',
            'remove_relation',
            'add_tuple',
            'delete_tuple',
            # 'add_column',
            # 'delete_column'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def set_enabled_query_actions(self, value):
        """ Public method. Enables or disables queries QAction """

        actions = ['execute_queries', 'save_query']

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def set_enabled_editor_actions(self, value):
        """ Public slot. Enables or disables editor actions """

        actions = [
            'undo_action', 'redo_action', 'copy_action', 'cut_action',
            'paste_action', 'zoom_in', 'zoom_out', 'comment', 'uncomment',
            'search'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def about_qt(self):
        """ Show about qt dialog """

        QMessageBox.aboutQt(self)

    def about_pireal(self):
        """ Show the bout Pireal dialog """

        from src.gui.dialogs import about_dialog
        dialog = about_dialog.AboutDialog(self)
        dialog.exec_()

    def report_issue(self):
        """ Open in the browser the page to create new  issue """

        webbrowser.open("http://github.com/centaurialpha/pireal/issues/new")

    def show_hide_menubar(self):
        """ Change visibility of menu bar """

        if self.menuBar().isVisible():
            self.menuBar().hide()
        else:
            self.menuBar().show()

    def show_hide_toolbar(self):
        """ Change visibility of tool bar """

        if self.toolbar.isVisible():
            self.toolbar.hide()
        else:
            self.toolbar.show()

    def show_error_message(self, text, syntax_error=True):
        self._msg_error_widget.show_msg(text, syntax_error)
        self._msg_error_widget.show()

    def save_user_settings(self):
        central_widget = Pireal.get_service("central")
        CONFIG.set_value("lastOpenFolder", central_widget.last_open_folder)
        CONFIG.set_value("recentFiles", central_widget.recent_databases)

        # Write settings
        CONFIG.save_settings()

    def closeEvent(self, event):
        self.save_user_settings()

        # Qt settings
        qsettings = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat)
        # # Save window geometry
        if self.isMaximized():
            qsettings.setValue('window_max', True)
        else:
            qsettings.setValue('window_max', False)
            qsettings.setValue('window_pos', self.pos())
            qsettings.setValue('window_size', self.size())

        central_widget = Pireal.get_service("central")
        db = central_widget.get_active_db()
        if db is not None:
            # Save splitters size
            db.save_sizes()
            # Databases unsaved
            if db.modified:
                msg = QMessageBox(self)
                msg.setIcon(QMessageBox.Question)
                msg.setWindowTitle(
                    self.tr("Algunos cambios no fueron guardados"))
                msg.setText(
                    self.tr("Desea guardar los cambios en la base de datos?"))
                cancel_btn = msg.addButton(self.tr("Cancelar"),
                                           QMessageBox.RejectRole)
                msg.addButton(self.tr("No"), QMessageBox.NoRole)
                yes_btn = msg.addButton(self.tr("Si"), QMessageBox.YesRole)
                msg.exec_()
                r = msg.clickedButton()
                if r == yes_btn:
                    central_widget.save_database()
                if r == cancel_btn:
                    event.ignore()
            # Query files
            unsaved_editors = central_widget.get_unsaved_queries()
            if unsaved_editors:
                msg = QMessageBox(self)
                msg.setIcon(QMessageBox.Question)
                msg.setWindowTitle(self.tr("Consultas no guardadas"))
                text = '\n'.join([editor.name for editor in unsaved_editors])
                msg.setText(
                    self.tr(
                        "{files}\n\nQuiere guardarlas?".format(files=text)))
                cancel_btn = msg.addButton(self.tr("Cancelar"),
                                           QMessageBox.RejectRole)
                msg.addButton(self.tr("No"), QMessageBox.NoRole)
                yes_btn = msg.addButton(self.tr("Si"), QMessageBox.YesRole)
                msg.exec_()
                if msg.clickedButton() == yes_btn:
                    for editor in unsaved_editors:
                        central_widget.save_query(editor)
                if msg.clickedButton() == cancel_btn:
                    event.ignore()
Ejemplo n.º 8
0
class ManageWindow(QWidget):
    __BASE_HEIGHT__ = 400

    signal_user_res = pyqtSignal(bool)
    signal_table_update = pyqtSignal()

    def __init__(self,
                 i18n: dict,
                 icon_cache: MemoryCache,
                 manager: SoftwareManager,
                 disk_cache: bool,
                 download_icons: bool,
                 screen_size,
                 suggestions: bool,
                 display_limit: int,
                 config: Configuration,
                 context: ApplicationContext,
                 notifications: bool,
                 tray_icon=None):
        super(ManageWindow, self).__init__()
        self.i18n = i18n
        self.manager = manager
        self.tray_icon = tray_icon
        self.working = False  # restrict the number of threaded actions
        self.pkgs = []  # packages current loaded in the table
        self.pkgs_available = []  # all packages loaded in memory
        self.pkgs_installed = []  # cached installed packages
        self.display_limit = display_limit
        self.icon_cache = icon_cache
        self.disk_cache = disk_cache
        self.download_icons = download_icons
        self.screen_size = screen_size
        self.config = config
        self.context = context
        self.notifications = notifications

        self.icon_app = QIcon(resource.get_path('img/logo.svg'))
        self.resize(ManageWindow.__BASE_HEIGHT__, ManageWindow.__BASE_HEIGHT__)
        self.setWindowIcon(self.icon_app)

        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        self.toolbar_top = QToolBar()
        self.toolbar_top.addWidget(new_spacer())

        self.label_status = QLabel()
        self.label_status.setText('')
        self.label_status.setStyleSheet("font-weight: bold")
        self.toolbar_top.addWidget(self.label_status)

        self.toolbar_search = QToolBar()
        self.toolbar_search.setStyleSheet("spacing: 0px;")
        self.toolbar_search.setContentsMargins(0, 0, 0, 0)

        label_pre_search = QLabel()
        label_pre_search.setStyleSheet(
            "background: white; border-top-left-radius: 5px; border-bottom-left-radius: 5px;"
        )
        self.toolbar_search.addWidget(label_pre_search)

        self.input_search = QLineEdit()
        self.input_search.setMaxLength(20)
        self.input_search.setFrame(False)
        self.input_search.setPlaceholderText(
            self.i18n['window_manage.input_search.placeholder'] + "...")
        self.input_search.setToolTip(
            self.i18n['window_manage.input_search.tooltip'])
        self.input_search.setStyleSheet(
            "QLineEdit { background-color: white; color: gray; spacing: 0; height: 30px; font-size: 12px; width: 300px}"
        )
        self.input_search.returnPressed.connect(self.search)
        self.toolbar_search.addWidget(self.input_search)

        label_pos_search = QLabel()
        label_pos_search.setPixmap(QPixmap(
            resource.get_path('img/search.svg')))
        label_pos_search.setStyleSheet(
            "background: white; padding-right: 10px; border-top-right-radius: 5px; border-bottom-right-radius: 5px;"
        )
        self.toolbar_search.addWidget(label_pos_search)

        self.ref_toolbar_search = self.toolbar_top.addWidget(
            self.toolbar_search)
        self.toolbar_top.addWidget(new_spacer())
        self.layout.addWidget(self.toolbar_top)

        self.toolbar = QToolBar()
        self.toolbar.setStyleSheet(
            'QToolBar {spacing: 4px; margin-top: 15px; margin-bottom: 5px}')

        self.checkbox_updates = QCheckBox()
        self.checkbox_updates.setText(self.i18n['updates'].capitalize())
        self.checkbox_updates.stateChanged.connect(self._handle_updates_filter)
        self.ref_checkbox_updates = self.toolbar.addWidget(
            self.checkbox_updates)

        self.checkbox_only_apps = QCheckBox()
        self.checkbox_only_apps.setText(
            self.i18n['manage_window.checkbox.only_apps'])
        self.checkbox_only_apps.setChecked(True)
        self.checkbox_only_apps.stateChanged.connect(
            self._handle_filter_only_apps)
        self.ref_checkbox_only_apps = self.toolbar.addWidget(
            self.checkbox_only_apps)

        self.any_type_filter = 'any'
        self.cache_type_filter_icons = {}
        self.combo_filter_type = QComboBox()
        self.combo_filter_type.setStyleSheet('QLineEdit { height: 2px}')
        self.combo_filter_type.setEditable(True)
        self.combo_filter_type.lineEdit().setReadOnly(True)
        self.combo_filter_type.lineEdit().setAlignment(Qt.AlignCenter)
        self.combo_filter_type.activated.connect(self._handle_type_filter)
        self.combo_filter_type.addItem(
            load_icon(resource.get_path('img/logo.svg'), 14),
            self.i18n[self.any_type_filter].capitalize(), self.any_type_filter)
        self.ref_combo_filter_type = self.toolbar.addWidget(
            self.combo_filter_type)

        self.input_name_filter = InputFilter(self.apply_filters_async)
        self.input_name_filter.setMaxLength(10)
        self.input_name_filter.setPlaceholderText(
            self.i18n['manage_window.name_filter.placeholder'] + '...')
        self.input_name_filter.setToolTip(
            self.i18n['manage_window.name_filter.tooltip'])
        self.input_name_filter.setStyleSheet(
            "QLineEdit { background-color: white; color: gray;}")
        self.input_name_filter.setFixedWidth(130)
        self.ref_input_name_filter = self.toolbar.addWidget(
            self.input_name_filter)

        self.toolbar.addWidget(new_spacer())

        self.bt_installed = QPushButton()
        self.bt_installed.setToolTip(
            self.i18n['manage_window.bt.installed.tooltip'])
        self.bt_installed.setIcon(QIcon(resource.get_path('img/disk.png')))
        self.bt_installed.setText(
            self.i18n['manage_window.bt.installed.text'].capitalize())
        self.bt_installed.clicked.connect(self._show_installed)
        self.bt_installed.setStyleSheet(toolbar_button_style('#A94E0A'))
        self.ref_bt_installed = self.toolbar.addWidget(self.bt_installed)

        self.bt_refresh = QPushButton()
        self.bt_refresh.setToolTip(i18n['manage_window.bt.refresh.tooltip'])
        self.bt_refresh.setIcon(QIcon(resource.get_path('img/refresh.svg')))
        self.bt_refresh.setText(self.i18n['manage_window.bt.refresh.text'])
        self.bt_refresh.setStyleSheet(toolbar_button_style('#2368AD'))
        self.bt_refresh.clicked.connect(
            lambda: self.refresh_apps(keep_console=False))
        self.ref_bt_refresh = self.toolbar.addWidget(self.bt_refresh)

        self.bt_upgrade = QPushButton()
        self.bt_upgrade.setToolTip(i18n['manage_window.bt.upgrade.tooltip'])
        self.bt_upgrade.setIcon(QIcon(resource.get_path('img/app_update.svg')))
        self.bt_upgrade.setText(i18n['manage_window.bt.upgrade.text'])
        self.bt_upgrade.setStyleSheet(toolbar_button_style('#20A435'))
        self.bt_upgrade.clicked.connect(self.update_selected)
        self.ref_bt_upgrade = self.toolbar.addWidget(self.bt_upgrade)

        self.layout.addWidget(self.toolbar)

        self.table_apps = AppsTable(self,
                                    self.icon_cache,
                                    disk_cache=self.disk_cache,
                                    download_icons=self.download_icons)
        self.table_apps.change_headers_policy()

        self.layout.addWidget(self.table_apps)

        toolbar_console = QToolBar()

        self.checkbox_console = QCheckBox()
        self.checkbox_console.setText(
            self.i18n['manage_window.checkbox.show_details'])
        self.checkbox_console.stateChanged.connect(self._handle_console)
        self.checkbox_console.setVisible(False)
        self.ref_checkbox_console = toolbar_console.addWidget(
            self.checkbox_console)

        toolbar_console.addWidget(new_spacer())

        self.label_displayed = QLabel()
        toolbar_console.addWidget(self.label_displayed)

        self.layout.addWidget(toolbar_console)

        self.textarea_output = QPlainTextEdit(self)
        self.textarea_output.resize(self.table_apps.size())
        self.textarea_output.setStyleSheet("background: black; color: white;")
        self.layout.addWidget(self.textarea_output)
        self.textarea_output.setVisible(False)
        self.textarea_output.setReadOnly(True)

        self.toolbar_substatus = QToolBar()
        self.toolbar_substatus.addWidget(new_spacer())
        self.label_substatus = QLabel()
        self.toolbar_substatus.addWidget(self.label_substatus)
        self.toolbar_substatus.addWidget(new_spacer())
        self.layout.addWidget(self.toolbar_substatus)
        self._change_label_substatus('')

        self.thread_update = self._bind_async_action(
            UpdateSelectedApps(self.manager, self.i18n),
            finished_call=self._finish_update_selected)
        self.thread_refresh = self._bind_async_action(
            RefreshApps(self.manager),
            finished_call=self._finish_refresh_apps,
            only_finished=True)
        self.thread_uninstall = self._bind_async_action(
            UninstallApp(self.manager, self.icon_cache),
            finished_call=self._finish_uninstall)
        self.thread_get_info = self._bind_async_action(
            GetAppInfo(self.manager), finished_call=self._finish_get_info)
        self.thread_get_history = self._bind_async_action(
            GetAppHistory(self.manager, self.i18n),
            finished_call=self._finish_get_history)
        self.thread_search = self._bind_async_action(
            SearchPackages(self.manager),
            finished_call=self._finish_search,
            only_finished=True)
        self.thread_downgrade = self._bind_async_action(
            DowngradeApp(self.manager, self.i18n),
            finished_call=self._finish_downgrade)
        self.thread_suggestions = self._bind_async_action(
            FindSuggestions(man=self.manager),
            finished_call=self._finish_search,
            only_finished=True)
        self.thread_run_app = self._bind_async_action(
            LaunchApp(self.manager),
            finished_call=self._finish_run_app,
            only_finished=False)
        self.thread_custom_action = self._bind_async_action(
            CustomAction(manager=self.manager),
            finished_call=self._finish_custom_action)

        self.thread_apply_filters = ApplyFilters()
        self.thread_apply_filters.signal_finished.connect(
            self._finish_apply_filters_async)
        self.thread_apply_filters.signal_table.connect(
            self._update_table_and_upgrades)
        self.signal_table_update.connect(
            self.thread_apply_filters.stop_waiting)

        self.thread_install = InstallPackage(manager=self.manager,
                                             disk_cache=self.disk_cache,
                                             icon_cache=self.icon_cache,
                                             locale_keys=self.i18n)
        self._bind_async_action(self.thread_install,
                                finished_call=self._finish_install)

        self.thread_animate_progress = AnimateProgress()
        self.thread_animate_progress.signal_change.connect(
            self._update_progress)

        self.thread_verify_models = VerifyModels()
        self.thread_verify_models.signal_updates.connect(
            self._notify_model_data_change)

        self.toolbar_bottom = QToolBar()
        self.toolbar_bottom.setIconSize(QSize(16, 16))
        self.toolbar_bottom.setStyleSheet('QToolBar { spacing: 3px }')

        self.toolbar_bottom.addWidget(new_spacer())

        self.progress_bar = QProgressBar()
        self.progress_bar.setMaximumHeight(10 if QApplication.instance().style(
        ).objectName().lower() == 'windows' else 4)

        self.progress_bar.setTextVisible(False)
        self.ref_progress_bar = self.toolbar_bottom.addWidget(
            self.progress_bar)

        self.toolbar_bottom.addWidget(new_spacer())

        self.combo_styles = StylesComboBox(
            parent=self, i18n=i18n, show_panel_after_restart=bool(tray_icon))
        self.combo_styles.setStyleSheet('QComboBox {font-size: 12px;}')
        self.ref_combo_styles = self.toolbar_bottom.addWidget(
            self.combo_styles)

        bt_settings = IconButton(
            icon_path=resource.get_path('img/app_settings.svg'),
            action=self._show_settings_menu,
            background='#12ABAB',
            tooltip=self.i18n['manage_window.bt_settings.tooltip'])
        self.ref_bt_settings = self.toolbar_bottom.addWidget(bt_settings)

        self.layout.addWidget(self.toolbar_bottom)

        qt_utils.centralize(self)

        self.filter_only_apps = True
        self.type_filter = self.any_type_filter
        self.filter_updates = False
        self._maximized = False
        self.progress_controll_enabled = True
        self.recent_installation = False

        self.dialog_about = None
        self.first_refresh = suggestions

        self.thread_warnings = ListWarnings(man=manager, locale_keys=i18n)
        self.thread_warnings.signal_warnings.connect(self._show_warnings)

    def set_tray_icon(self, tray_icon):
        self.tray_icon = tray_icon
        self.combo_styles.show_panel_after_restart = bool(tray_icon)

    def _update_process_progress(self, val: int):
        if self.progress_controll_enabled:
            self.thread_animate_progress.set_progress(val)

    def apply_filters_async(self):
        self.label_status.setText(self.i18n['manage_window.status.filtering'] +
                                  '...')

        self.ref_toolbar_search.setVisible(False)

        if self.ref_input_name_filter.isVisible():
            self.input_name_filter.setReadOnly(True)

        self.thread_apply_filters.filters = self._gen_filters()
        self.thread_apply_filters.pkgs = self.pkgs_available
        self.thread_apply_filters.start()

    def _update_table_and_upgrades(self, pkgs_info: dict):
        self._update_table(pkgs_info=pkgs_info, signal=True)
        self.update_bt_upgrade(pkgs_info)

    def _finish_apply_filters_async(self, success: bool):
        self.label_status.setText('')
        self.ref_toolbar_search.setVisible(True)

        if self.ref_input_name_filter.isVisible():
            self.input_name_filter.setReadOnly(False)

    def _bind_async_action(self,
                           action: AsyncAction,
                           finished_call,
                           only_finished: bool = False) -> AsyncAction:
        action.signal_finished.connect(finished_call)

        if not only_finished:
            action.signal_confirmation.connect(self._ask_confirmation)
            action.signal_output.connect(self._update_action_output)
            action.signal_message.connect(self._show_message)
            action.signal_status.connect(self._change_label_status)
            action.signal_substatus.connect(self._change_label_substatus)
            action.signal_progress.connect(self._update_process_progress)

            self.signal_user_res.connect(action.confirm)

        return action

    def _ask_confirmation(self, msg: dict):
        self.thread_animate_progress.pause()
        diag = ConfirmationDialog(title=msg['title'],
                                  body=msg['body'],
                                  locale_keys=self.i18n,
                                  components=msg['components'],
                                  confirmation_label=msg['confirmation_label'],
                                  deny_label=msg['deny_label'])
        res = diag.is_confirmed()
        self.thread_animate_progress.animate()
        self.signal_user_res.emit(res)

    def _show_message(self, msg: dict):
        self.thread_animate_progress.pause()
        dialog.show_message(title=msg['title'],
                            body=msg['body'],
                            type_=msg['type'])
        self.thread_animate_progress.animate()

    def _show_warnings(self, warnings: List[str]):
        if warnings:
            dialog.show_message(title=self.i18n['warning'].capitalize(),
                                body='<p>{}</p>'.format(
                                    '<br/><br/>'.join(warnings)),
                                type_=MessageType.WARNING)

    def show(self):
        super(ManageWindow, self).show()
        if not self.thread_warnings.isFinished():
            self.thread_warnings.start()

    def verify_warnings(self):
        self.thread_warnings.start()

    def _show_installed(self):
        if self.pkgs_installed:
            self.finish_action()
            self.ref_bt_upgrade.setVisible(True)
            self.ref_checkbox_only_apps.setVisible(True)
            self.input_search.setText('')
            self.input_name_filter.setText('')
            self.update_pkgs(new_pkgs=None, as_installed=True)

    def _show_about(self):
        if self.dialog_about is None:
            self.dialog_about = AboutDialog(self.i18n)

        self.dialog_about.show()

    def _handle_updates_filter(self, status: int):
        self.filter_updates = status == 2
        self.apply_filters_async()

    def _handle_filter_only_apps(self, status: int):
        self.filter_only_apps = status == 2
        self.apply_filters_async()

    def _handle_type_filter(self, idx: int):
        self.type_filter = self.combo_filter_type.itemData(idx)
        self.apply_filters_async()

    def _notify_model_data_change(self):
        self.table_apps.fill_async_data()

    def changeEvent(self, e: QEvent):
        if isinstance(e, QWindowStateChangeEvent):
            self._maximized = self.isMaximized()
            policy = QHeaderView.Stretch if self._maximized else QHeaderView.ResizeToContents
            self.table_apps.change_headers_policy(policy)

    def closeEvent(self, event):

        if self.tray_icon:
            event.ignore()
            self.hide()
            self._handle_console_option(False)

    def _handle_console(self, checked: bool):

        if checked:
            self.textarea_output.show()
        else:
            self.textarea_output.hide()

    def _handle_console_option(self, enable: bool):

        if enable:
            self.textarea_output.clear()

        self.ref_checkbox_console.setVisible(enable)
        self.checkbox_console.setChecked(False)
        self.textarea_output.hide()

    def refresh_apps(self,
                     keep_console: bool = True,
                     top_app: PackageView = None,
                     pkg_types: Set[Type[SoftwarePackage]] = None):
        self.recent_installation = False
        self.type_filter = None
        self.input_search.clear()

        if not keep_console:
            self._handle_console_option(False)

        self.ref_checkbox_updates.setVisible(False)
        self.ref_checkbox_only_apps.setVisible(False)
        self._begin_action(self.i18n['manage_window.status.refreshing'],
                           keep_bt_installed=False,
                           clear_filters=True)

        self.thread_refresh.app = top_app  # the app will be on top when refresh happens
        self.thread_refresh.pkg_types = pkg_types
        self.thread_refresh.start()

    def _finish_refresh_apps(self, res: dict, as_installed: bool = True):
        self.finish_action()
        self.ref_checkbox_only_apps.setVisible(bool(res['installed']))
        self.ref_bt_upgrade.setVisible(True)
        self.update_pkgs(res['installed'],
                         as_installed=as_installed,
                         types=res['types'])
        self.first_refresh = False

    def uninstall_app(self, app: PackageView):
        pwd = None
        requires_root = self.manager.requires_root('uninstall', app.model)

        if not is_root() and requires_root:
            pwd, ok = ask_root_password(self.i18n)

            if not ok:
                return

        self._handle_console_option(True)
        self._begin_action('{} {}'.format(
            self.i18n['manage_window.status.uninstalling'], app.model.name))

        self.thread_uninstall.app = app
        self.thread_uninstall.root_password = pwd
        self.thread_uninstall.start()

    def run_app(self, app: PackageView):
        self._begin_action(
            self.i18n['manage_window.status.running_app'].format(
                app.model.name))
        self.thread_run_app.app = app
        self.thread_run_app.start()

    def _finish_uninstall(self, pkgv: PackageView):
        self.finish_action()

        if pkgv:
            if self._can_notify_user():
                util.notify_user('{} ({}) {}'.format(pkgv.model.name,
                                                     pkgv.model.get_type(),
                                                     self.i18n['uninstalled']))

            self.refresh_apps(pkg_types={pkgv.model.__class__})
        else:
            if self._can_notify_user():
                util.notify_user('{}: {}'.format(
                    pkgv.model.name,
                    self.i18n['notification.uninstall.failed']))

            self.checkbox_console.setChecked(True)

    def _can_notify_user(self):
        return self.notifications and (self.isHidden() or self.isMinimized())

    def _finish_downgrade(self, res: dict):
        self.finish_action()

        if res['success']:
            if self._can_notify_user():
                util.notify_user('{} {}'.format(res['app'],
                                                self.i18n['downgraded']))

            self.refresh_apps(pkg_types={res['app'].model.__class__})

            if self.tray_icon:
                self.tray_icon.verify_updates(notify_user=False)
        else:
            if self._can_notify_user():
                util.notify_user(self.i18n['notification.downgrade.failed'])

            self.checkbox_console.setChecked(True)

    def _change_label_status(self, status: str):
        self.label_status.setText(status)

    def _change_label_substatus(self, substatus: str):
        self.label_substatus.setText('<p>{}</p>'.format(substatus))
        if not substatus:
            self.toolbar_substatus.hide()
        elif not self.toolbar_substatus.isVisible():
            self.toolbar_substatus.show()

    def _update_table(self, pkgs_info: dict, signal: bool = False):
        self.pkgs = pkgs_info['pkgs_displayed']

        self.table_apps.update_pkgs(
            self.pkgs, update_check_enabled=pkgs_info['not_installed'] == 0)

        if not self._maximized:
            self.table_apps.change_headers_policy(QHeaderView.Stretch)
            self.table_apps.change_headers_policy()
            self.resize_and_center(accept_lower_width=len(self.pkgs) > 0)
            self.label_displayed.setText('{} / {}'.format(
                len(self.pkgs), len(self.pkgs_available)))
        else:
            self.label_displayed.setText('')

        if signal:
            self.signal_table_update.emit()

    def update_bt_upgrade(self, pkgs_info: dict = None):
        show_bt_upgrade = False

        if not pkgs_info or pkgs_info['not_installed'] == 0:
            for app_v in (pkgs_info['pkgs_displayed']
                          if pkgs_info else self.pkgs):
                if app_v.update_checked:
                    show_bt_upgrade = True
                    break

        self.ref_bt_upgrade.setVisible(show_bt_upgrade)

    def change_update_state(self,
                            pkgs_info: dict,
                            trigger_filters: bool = True):
        self.update_bt_upgrade(pkgs_info)

        if pkgs_info['updates'] > 0:

            if pkgs_info['not_installed'] == 0:
                if not self.ref_checkbox_updates.isVisible():
                    self.ref_checkbox_updates.setVisible(True)

                if not self.filter_updates:
                    self._change_checkbox(self.checkbox_updates, True,
                                          'filter_updates', trigger_filters)

            if pkgs_info['napp_updates'] > 0 and self.filter_only_apps:
                self._change_checkbox(self.checkbox_only_apps, False,
                                      'filter_only_apps', trigger_filters)
        else:
            self._change_checkbox(self.checkbox_updates, False,
                                  'filter_updates', trigger_filters)

            self.ref_checkbox_updates.setVisible(False)

    def _change_checkbox(self,
                         checkbox: QCheckBox,
                         checked: bool,
                         attr: str = None,
                         trigger: bool = True):
        if not trigger:
            checkbox.blockSignals(True)

        checkbox.setChecked(checked)

        if not trigger:
            setattr(self, attr, checked)
            checkbox.blockSignals(False)

    def _gen_filters(self,
                     updates: int = 0,
                     ignore_updates: bool = False) -> dict:
        return {
            'only_apps':
            self.filter_only_apps,
            'type':
            self.type_filter,
            'updates':
            False if ignore_updates else self.filter_updates,
            'name':
            self.input_name_filter.get_text().lower()
            if self.input_name_filter.get_text() else None,
            'display_limit':
            self.display_limit if updates <= 0 else None
        }

    def update_pkgs(self,
                    new_pkgs: List[SoftwarePackage],
                    as_installed: bool,
                    types: Set[type] = None,
                    ignore_updates: bool = False):
        self.input_name_filter.setText('')
        pkgs_info = commons.new_pkgs_info()
        filters = self._gen_filters(ignore_updates)

        if new_pkgs is not None:
            old_installed = None

            if as_installed:
                old_installed = self.pkgs_installed
                self.pkgs_installed = []

            for pkg in new_pkgs:
                app_model = PackageView(model=pkg)
                commons.update_info(app_model, pkgs_info)
                commons.apply_filters(app_model, filters, pkgs_info)

            if old_installed and types:
                for pkgv in old_installed:
                    if not pkgv.model.__class__ in types:
                        commons.update_info(pkgv, pkgs_info)
                        commons.apply_filters(pkgv, filters, pkgs_info)

        else:  # use installed
            for pkgv in self.pkgs_installed:
                commons.update_info(pkgv, pkgs_info)
                commons.apply_filters(pkgv, filters, pkgs_info)

        if pkgs_info['apps_count'] == 0:
            if self.first_refresh:
                self._begin_search('')
                self.thread_suggestions.start()
                return
            else:
                self._change_checkbox(self.checkbox_only_apps,
                                      False,
                                      'filter_only_apps',
                                      trigger=False)
                self.checkbox_only_apps.setCheckable(False)
        else:
            self.checkbox_only_apps.setCheckable(True)
            self._change_checkbox(self.checkbox_only_apps,
                                  True,
                                  'filter_only_apps',
                                  trigger=False)

        self.change_update_state(pkgs_info=pkgs_info, trigger_filters=False)
        self._apply_filters(pkgs_info, ignore_updates=ignore_updates)
        self.change_update_state(pkgs_info=pkgs_info, trigger_filters=False)

        self.pkgs_available = pkgs_info['pkgs']

        if as_installed:
            self.pkgs_installed = pkgs_info['pkgs']

        self.pkgs = pkgs_info['pkgs_displayed']

        if self.pkgs:
            self.ref_input_name_filter.setVisible(True)

        self._update_type_filters(pkgs_info['available_types'])

        self._update_table(pkgs_info=pkgs_info)

        if new_pkgs:
            self.thread_verify_models.apps = self.pkgs
            self.thread_verify_models.start()

        if self.pkgs_installed:
            self.ref_bt_installed.setVisible(not as_installed
                                             and not self.recent_installation)

        self.resize_and_center(accept_lower_width=self.pkgs_installed)

    def _apply_filters(self, pkgs_info: dict, ignore_updates: bool):
        pkgs_info['pkgs_displayed'] = []
        filters = self._gen_filters(updates=pkgs_info['updates'],
                                    ignore_updates=ignore_updates)
        for pkgv in pkgs_info['pkgs']:
            commons.apply_filters(pkgv, filters, pkgs_info)

    def _update_type_filters(self, available_types: dict = None):

        if available_types is None:
            self.ref_combo_filter_type.setVisible(
                self.combo_filter_type.count() > 1)
        else:
            self.type_filter = self.any_type_filter

            if available_types and len(available_types) > 1:
                if self.combo_filter_type.count() > 1:
                    for _ in range(self.combo_filter_type.count() - 1):
                        self.combo_filter_type.removeItem(1)

                for app_type, icon_path in available_types.items():
                    icon = self.cache_type_filter_icons.get(app_type)

                    if not icon:
                        icon = load_icon(icon_path, 14)
                        self.cache_type_filter_icons[app_type] = icon

                    self.combo_filter_type.addItem(icon, app_type.capitalize(),
                                                   app_type)

                self.ref_combo_filter_type.setVisible(True)
            else:
                self.ref_combo_filter_type.setVisible(False)

    def resize_and_center(self, accept_lower_width: bool = True):
        if self.pkgs:
            new_width = reduce(operator.add, [
                self.table_apps.columnWidth(i)
                for i in range(self.table_apps.columnCount())
            ])

            if self.ref_bt_upgrade.isVisible(
            ) or self.ref_bt_settings.isVisible():
                new_width *= 1.07
        else:
            new_width = self.toolbar_top.width()

        if accept_lower_width or new_width > self.width():
            self.resize(new_width, self.height())

            if self.ref_bt_upgrade.isVisible(
            ) and self.bt_upgrade.visibleRegion().isEmpty():
                self.adjustSize()

        qt_utils.centralize(self)

    def update_selected(self):
        if self.pkgs:
            requires_root = False

            to_update = []

            for app_v in self.pkgs:
                if app_v.update_checked:
                    to_update.append(app_v)

                    if self.manager.requires_root('update', app_v.model):
                        requires_root = True

            if to_update and dialog.ask_confirmation(
                    title=self.i18n['manage_window.upgrade_all.popup.title'],
                    body=self.i18n['manage_window.upgrade_all.popup.body'],
                    locale_keys=self.i18n,
                    widgets=[
                        UpdateToggleButton(
                            None, self, self.i18n, clickable=False)
                    ]):
                pwd = None

                if not is_root() and requires_root:
                    pwd, ok = ask_root_password(self.i18n)

                    if not ok:
                        return

                self._handle_console_option(True)
                self.progress_controll_enabled = len(to_update) == 1
                self._begin_action(self.i18n['manage_window.status.upgrading'])
                self.thread_update.apps_to_update = to_update
                self.thread_update.root_password = pwd
                self.thread_update.start()

    def _finish_update_selected(self, res: dict):
        self.finish_action()

        if res['success']:
            if self._can_notify_user():
                util.notify_user('{} {}'.format(
                    res['updated'],
                    self.i18n['notification.update_selected.success']))

            self.refresh_apps(pkg_types=res['types'])

            if self.tray_icon:
                self.tray_icon.verify_updates()
        else:
            if self._can_notify_user():
                util.notify_user(
                    self.i18n['notification.update_selected.failed'])

            self.ref_bt_upgrade.setVisible(True)
            self.checkbox_console.setChecked(True)

    def _update_action_output(self, output: str):
        self.textarea_output.appendPlainText(output)

    def _begin_action(self,
                      action_label: str,
                      keep_search: bool = False,
                      keep_bt_installed: bool = True,
                      clear_filters: bool = False):
        self.ref_input_name_filter.setVisible(False)
        self.ref_combo_filter_type.setVisible(False)
        self.ref_bt_settings.setVisible(False)
        self.thread_animate_progress.stop = False
        self.thread_animate_progress.start()
        self.ref_progress_bar.setVisible(True)
        self.ref_combo_styles.setVisible(False)

        self.label_status.setText(action_label + "...")
        self.ref_bt_upgrade.setVisible(False)
        self.ref_bt_refresh.setVisible(False)
        self.checkbox_only_apps.setEnabled(False)
        self.table_apps.setEnabled(False)
        self.checkbox_updates.setEnabled(False)

        if not keep_bt_installed:
            self.ref_bt_installed.setVisible(False)
        elif self.ref_bt_installed.isVisible():
            self.ref_bt_installed.setEnabled(False)

        if keep_search:
            self.ref_toolbar_search.setVisible(True)
        else:
            self.ref_toolbar_search.setVisible(False)

        if clear_filters:
            self._update_type_filters({})
        else:
            self.combo_filter_type.setEnabled(False)

    def finish_action(self):
        self.ref_combo_styles.setVisible(True)
        self.thread_animate_progress.stop = True
        self.thread_animate_progress.wait()
        self.ref_progress_bar.setVisible(False)
        self.progress_bar.setValue(0)
        self.progress_bar.setTextVisible(False)

        self._change_label_substatus('')
        self.ref_bt_settings.setVisible(True)

        self.ref_bt_refresh.setVisible(True)
        self.checkbox_only_apps.setEnabled(True)
        self.table_apps.setEnabled(True)
        self.input_search.setEnabled(True)
        self.label_status.setText('')
        self.label_substatus.setText('')
        self.ref_toolbar_search.setVisible(True)
        self.ref_toolbar_search.setEnabled(True)
        self.combo_filter_type.setEnabled(True)
        self.checkbox_updates.setEnabled(True)
        self.progress_controll_enabled = True

        if self.pkgs:
            self.ref_input_name_filter.setVisible(True)
            self.update_bt_upgrade()
            self._update_type_filters()

            if self.ref_bt_installed.isVisible():
                self.ref_bt_installed.setEnabled(True)

    def downgrade(self, pkgv: PackageView):
        pwd = None
        requires_root = self.manager.requires_root('downgrade', pkgv.model)

        if not is_root() and requires_root:
            pwd, ok = ask_root_password(self.i18n)

            if not ok:
                return

        self._handle_console_option(True)
        self._begin_action('{} {}'.format(
            self.i18n['manage_window.status.downgrading'], pkgv.model.name))

        self.thread_downgrade.app = pkgv
        self.thread_downgrade.root_password = pwd
        self.thread_downgrade.start()

    def get_app_info(self, pkg: dict):
        self._handle_console_option(False)
        self._begin_action(self.i18n['manage_window.status.info'])

        self.thread_get_info.app = pkg
        self.thread_get_info.start()

    def get_app_history(self, app: dict):
        self._handle_console_option(False)
        self._begin_action(self.i18n['manage_window.status.history'])

        self.thread_get_history.app = app
        self.thread_get_history.start()

    def _finish_get_info(self, app_info: dict):
        self.finish_action()
        dialog_info = InfoDialog(app=app_info,
                                 icon_cache=self.icon_cache,
                                 locale_keys=self.i18n,
                                 screen_size=self.screen_size)
        dialog_info.exec_()

    def _finish_get_history(self, res: dict):
        self.finish_action()

        if res.get('error'):
            self._handle_console_option(True)
            self.textarea_output.appendPlainText(res['error'])
            self.checkbox_console.setChecked(True)
        else:
            dialog_history = HistoryDialog(res['history'], self.icon_cache,
                                           self.i18n)
            dialog_history.exec_()

    def _begin_search(self, word):
        self._handle_console_option(False)
        self.ref_checkbox_only_apps.setVisible(False)
        self.ref_checkbox_updates.setVisible(False)
        self.filter_updates = False
        self._begin_action('{} {}'.format(
            self.i18n['manage_window.status.searching'], word if word else ''),
                           clear_filters=True)

    def search(self):
        word = self.input_search.text().strip()
        if word:
            self._begin_search(word)
            self.thread_search.word = word
            self.thread_search.start()

    def _finish_search(self, res: dict):
        self.finish_action()

        if not res['error']:
            self.ref_bt_upgrade.setVisible(False)
            self.update_pkgs(res['pkgs_found'],
                             as_installed=False,
                             ignore_updates=True)
        else:
            dialog.show_message(title=self.i18n['warning'].capitalize(),
                                body=self.i18n[res['error']],
                                type_=MessageType.WARNING)

    def install(self, pkg: PackageView):
        pwd = None
        requires_root = self.manager.requires_root('install', pkg.model)

        if not is_root() and requires_root:
            pwd, ok = ask_root_password(self.i18n)

            if not ok:
                return

        self._handle_console_option(True)
        self._begin_action('{} {}'.format(
            self.i18n['manage_window.status.installing'], pkg.model.name))

        self.thread_install.pkg = pkg
        self.thread_install.root_password = pwd
        self.thread_install.start()

    def _finish_install(self, res: dict):
        self.input_search.setText('')
        self.finish_action()

        console_output = self.textarea_output.toPlainText()

        if console_output:
            log_path = '/tmp/bauh/logs/install/{}/{}'.format(
                res['pkg'].model.get_type(), res['pkg'].model.name)
            try:
                Path(log_path).mkdir(parents=True, exist_ok=True)

                log_file = log_path + '/{}.log'.format(int(time.time()))
                with open(log_file, 'w+') as f:
                    f.write(console_output)

                self.textarea_output.appendPlainText(
                    self.i18n['console.install_logs.path'].format(
                        '"{}"'.format(log_file)))
            except:
                self.textarea_output.appendPlainText(
                    "[warning] Could not write install log file to '{}'".
                    format(log_path))

        if res['success']:
            self.recent_installation = True
            if self._can_notify_user():
                util.notify_user(msg='{} ({}) {}'.format(
                    res['pkg'].model.name, res['pkg'].model.get_type(),
                    self.i18n['installed']))

            self._finish_refresh_apps({
                'installed': [res['pkg'].model],
                'total': 1,
                'types': None
            })
            self.ref_bt_installed.setVisible(False)
            self.ref_checkbox_only_apps.setVisible(False)
        else:
            if self._can_notify_user():
                util.notify_user('{}: {}'.format(
                    res['pkg'].model.name,
                    self.i18n['notification.install.failed']))

            self.checkbox_console.setChecked(True)

    def _update_progress(self, value: int):
        self.progress_bar.setValue(value)

    def _finish_run_app(self, success: bool):
        self.finish_action()

    def execute_custom_action(self, pkg: PackageView, action: PackageAction):
        pwd = None

        if not is_root() and action.requires_root:
            pwd, ok = ask_root_password(self.i18n)

            if not ok:
                return

        self._handle_console_option(True)
        self._begin_action('{} {}'.format(self.i18n[action.i18n_status_key],
                                          pkg.model.name))

        self.thread_custom_action.pkg = pkg
        self.thread_custom_action.root_password = pwd
        self.thread_custom_action.custom_action = action
        self.thread_custom_action.start()

    def _finish_custom_action(self, res: dict):
        self.finish_action()
        if res['success']:
            self.refresh_apps(pkg_types={res['pkg'].model.__class__})
        else:
            self.checkbox_console.setChecked(True)

    def show_gems_selector(self):
        gem_panel = GemSelectorPanel(window=self,
                                     manager=self.manager,
                                     i18n=self.i18n,
                                     config=self.config,
                                     show_panel_after_restart=bool(
                                         self.tray_icon))
        gem_panel.show()

    def _show_settings_menu(self):
        menu_row = QMenu()

        if isinstance(self.manager, GenericSoftwareManager):
            action_gems = QAction(self.i18n['manage_window.settings.gems'])
            action_gems.setIcon(self.icon_app)

            action_gems.triggered.connect(self.show_gems_selector)
            menu_row.addAction(action_gems)

        action_about = QAction(self.i18n['manage_window.settings.about'])
        action_about.setIcon(QIcon(resource.get_path('img/about.svg')))
        action_about.triggered.connect(self._show_about)
        menu_row.addAction(action_about)

        menu_row.adjustSize()
        menu_row.popup(QCursor.pos())
        menu_row.exec_()
Ejemplo n.º 9
0
class MainWindow(QMainWindow):
    """Main Window."""
    def __init__(self, parent=None):
        """Initialize MainWindow."""
        super(MainWindow, self).__init__(parent)
        self.ram_info, self.ram_timer = QLabel(self), QTimer(self)
        self.menubar, self.view = QMenu(self), WebView(self)
        self.ram_timer.timeout.connect(self.update_statusbar)
        self.ram_timer.start(60000)  # Every 60 seconds
        self.statusBar().insertPermanentWidget(0, self.ram_info)
        self.setMinimumSize(640, 480)
        self.setMaximumSize(QDesktopWidget().screenGeometry().width() * 2,
                            QDesktopWidget().screenGeometry().height() * 2)
        self.palette().setBrush(QPalette.Base, Qt.transparent)
        self.setPalette(self.palette())  # Transparent palette
        self.setAttribute(Qt.WA_OpaquePaintEvent, False)  # no opaque paint
        self.setAttribute(Qt.WA_TranslucentBackground, True)  # translucent
        QShortcut("Ctrl+q", self, activated=self.close)
        self.make_toolbar()
        self.make_menubar()
        self.update_statusbar()
        self.setCentralWidget(self.view)
        if qdarkstyle:
            self.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())

    def paintEvent(self, event):
        """Paint transparent background,animated pattern,background text."""
        painter, font = QPainter(self), self.font()
        painter.fillRect(event.rect(), Qt.transparent)  # fill transparent rect
        painter.setPen(QPen(QColor(randint(9, 255), randint(9, 255), 255)))
        painter.rotate(30)  # Rotate painter ~30 Degree
        font.setBold(True)  # Set painter Font for text
        font.setPixelSize(100)
        painter.setFont(font)
        painter.drawText(99, 99, "Python Qt")  # draw the background text
        painter.rotate(-30)  # Rotate -30 the QPen back
        painter.setPen(Qt.NoPen)  # set the pen to no pen
        painter.setBrush(QColor("black"))  # Background Color
        painter.setOpacity(0.9)  # Background Opacity
        painter.drawRoundedRect(self.rect(), 25, 25)  # Back Rounded Borders
        for i in range(2048):  # animated random dots background pattern
            x = randint(10, self.size().width() - 10)
            y = randint(10, self.size().height() - 10)
            painter.setPen(QPen(QColor(randint(9, 255), randint(9, 255), 255)))
            painter.drawPoint(x, y)
        QMainWindow.paintEvent(self, event)

    def make_toolbar(self, list_of_actions=None):
        """Make or Update the main Tool Bar."""
        self.toolbar = QToolBar(self)
        self.left_spacer, self.right_spacer = QWidget(self), QWidget(self)
        self.left_spacer.setSizePolicy(1 | 2, 1 | 2)  # Expanding, Expanding
        self.right_spacer.setSizePolicy(1 | 2, 1 | 2)  # Expanding, Expanding
        self.toolbar.addAction("Menu",
                               lambda: self.menubar.exec_(QCursor.pos()))
        self.toolbar.addWidget(self.left_spacer)
        self.toolbar.addSeparator()
        self.toolbar.addAction(QIcon.fromTheme("help-contents"),
                               "Help and Docs", lambda: open_new_tab(__url__))
        self.toolbar.addAction(QIcon.fromTheme("help-about"), "About Qt 5",
                               lambda: QMessageBox.aboutQt(self))
        self.toolbar.addAction(QIcon.fromTheme("help-about"), "About Python 3",
                               lambda: open_new_tab('http://python.org/about'))
        self.toolbar.addAction(QIcon.fromTheme("application-exit"), "Quit",
                               self.close)
        self.toolbar.addSeparator()
        if list_of_actions and len(list_of_actions):
            for action in list_of_actions:  # if list_of_actions, add actions.
                self.toolbar.addAction(action)
            self.toolbar.addSeparator()
        self.toolbar.addWidget(self.right_spacer)
        self.addToolBar(self.toolbar)
        if sys.platform.startswith("win"):
            self.toolbar.hide()  # windows dont have QIcon.fromTheme,so hide.
        return self.toolbar

    def make_menubar(self, list_of_actions=None):
        """Make or Update the main Tool Bar."""
        self.menuBar().addMenu("&File").addAction("Exit", self.close)
        self.menubar.addMenu("&File").addAction("Exit", self.close)
        viewMenu = self.menuBar().addMenu("&View")
        viewMenu.addAction(
            "Toggle ToolBar",
            lambda: self.toolbar.setVisible(not self.toolbar.isVisible()))
        viewMenu.addAction(
            "Toggle StatusBar",
            lambda: self.statusBar().setVisible(not self.statusBar().isVisible(
            )))
        viewMenu.addAction(
            "Toggle MenuBar",
            lambda: self.menuBar().setVisible(not self.menuBar().isVisible()))
        viewMenu2 = self.menubar.addMenu("&View")
        for action in viewMenu.actions():
            viewMenu2.addAction(action)
        windowMenu = self.menuBar().addMenu("&Window")
        windowMenu.addAction("Minimize", lambda: self.showMinimized())
        windowMenu.addAction("Maximize", lambda: self.showMaximized())
        windowMenu.addAction("Restore", lambda: self.showNormal())
        windowMenu.addAction("Full-Screen", lambda: self.showFullScreen())
        windowMenu.addAction("Center", lambda: self.center())
        windowMenu.addAction("Top-Left", lambda: self.move(0, 0))
        windowMenu.addAction("To Mouse", lambda: self.move(QCursor.pos()))
        windowMenu.addSeparator()
        windowMenu.addAction(
            "Increase size", lambda: self.resize(self.size().width() * 1.5,
                                                 self.size().height() * 1.5))
        windowMenu.addAction(
            "Decrease size", lambda: self.resize(self.size().width() // 1.5,
                                                 self.size().height() // 1.5))
        windowMenu.addAction("Minimum size",
                             lambda: self.resize(self.minimumSize()))
        windowMenu.addAction("Maximum size",
                             lambda: self.resize(self.maximumSize()))
        windowMenu.addAction(
            "Horizontal Wide",
            lambda: self.resize(self.maximumSize().width(),
                                self.minimumSize().height()))
        windowMenu.addAction(
            "Vertical Tall", lambda: self.resize(self.minimumSize().width(),
                                                 self.maximumSize().height()))
        windowMenu.addSeparator()
        windowMenu.addAction("Disable Resize",
                             lambda: self.setFixedSize(self.size()))
        windowMenu2 = self.menubar.addMenu("&Window")
        for action in windowMenu.actions():
            windowMenu2.addAction(action)
        optionMenu = self.menuBar().addMenu("&Options")
        optionMenu.addAction(
            "Set Interface Font...",
            lambda: self.setFont(QFontDialog.getFont(self)[0]))
        optionMenu.addAction("Load CSS Skin...",
                             lambda: self.setStyleSheet(self.skin()))
        optionMenu.addAction(
            "Take ScreenShoot...", lambda: self.grab().save(
                QFileDialog.
                getSaveFileName(self, "Save", os.path.expanduser("~"),
                                "(*.png) PNG image file", "png")[0]))
        optionMenu2 = self.menubar.addMenu("&Options")
        for action in optionMenu.actions():
            optionMenu2.addAction(action)
        helpMenu = self.menuBar().addMenu("&Help")
        helpMenu.addAction("About Qt 5", lambda: QMessageBox.aboutQt(self))
        helpMenu.addAction(
            "About Python 3",
            lambda: open_new_tab('https://www.python.org/about'))
        helpMenu.addSeparator()
        if sys.platform.startswith('linux'):
            helpMenu.addAction("View Source Code",
                               lambda: open_new_tab(__file__))
        helpMenu.addAction("View GitHub Repo", lambda: open_new_tab(__url__))
        helpMenu.addAction(
            "Report Bugs",
            lambda: open_new_tab(__url__ + '/issues?state=open'))
        helpMenu2 = self.menubar.addMenu("&Help")
        for action in helpMenu.actions():
            helpMenu2.addAction(action)
        return self.menuBar()

    def update_statusbar(self, custom_message=None):
        """Make or Update the Status Bar."""
        statusbar = self.statusBar()
        if resource:
            ram_use = int(
                resource.getrusage(resource.RUSAGE_SELF).ru_maxrss *
                resource.getpagesize() / 1024 / 1024)
            ram_byt = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES')
            ram_all = int(ram_byt / 1024 / 1024)
            self.ram_info.setText("{0} / {1} Mb".format(ram_use, ram_all))
            self.ram_info.setToolTip(
                "{0} of {1} MegaBytes of RAM Memory.".format(ram_use, ram_all))
        if custom_message and len(custom_message):
            return statusbar.showMessage(custom_message)
        return statusbar.showMessage(__doc__)

    def skin(self, filename=None):
        """Open QSS from filename,if no QSS return None,if no filename ask."""
        if not filename:
            filename = str(
                QFileDialog.getOpenFileName(
                    self, __doc__ + " - Open QSS Skin",
                    os.path.expanduser("~"),
                    "CSS Cascading Style Sheet for Qt 5 (*.qss);;All (*.*)")
                [0])
        if filename and os.path.isfile(filename):
            with open(filename, 'r', encoding="utf-8-sig") as file_to_read:
                text = file_to_read.read().strip()
        if text:
            return text

    def center(self):
        """Center and resize the window."""
        self.showNormal()
        self.resize(QDesktopWidget().screenGeometry().width() // 1.25,
                    QDesktopWidget().screenGeometry().height() // 1.25)
        qr = self.frameGeometry()
        qr.moveCenter(QDesktopWidget().availableGeometry().center())
        return self.move(qr.topLeft())

    def closeEvent(self, event):
        """Ask to Quit."""
        return event.accept() if QMessageBox.question(
            self, "Close", "<h1>Quit ?.", QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No) == QMessageBox.Yes else event.ignore()
Ejemplo n.º 10
0
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

    MessageStatusChanged = pyqtSignal(str)

    goingDown = pyqtSignal()
    # # ns_preferences_editor_font = pyqtSignal()
    # ns_preferences_editor_showTabsAndSpaces = pyqtSignal()
    # ns_preferences_editor_showIndentationGuide = pyqtSignal()
    # ns_preferences_editor_indent = pyqtSignal()
    # ns_preferences_editor_marginLine = pyqtSignal()#podría tener un argumento
    # ns_preferences_editor_showLineNumbers = pyqtSignal()
    # ns_preferences_editor_showMigrationTips = pyqtSignal()
    # ns_preferences_editor_checkStyle = pyqtSignal()
    # ns_preferences_editor_errors = pyqtSignal()
    # ds_lastSession_projects = pyqtSignal()
    # ds_lastSession_openedFiles = pyqtSignal()
    # ds_lastSession_currentFile = pyqtSignal()
    # ds_lastSession_recentFiles = pyqtSignal()
    # ns_preferences_editor_bookmarks = pyqtSignal()
    # ns_preferences_editor_breakpoints = pyqtSignal()
    # ns_window_maximized = pyqtSignal()
    # ns_preferences_general_loadFiles = pyqtSignal()
    # ns_preferences_general_activatePlugins = pyqtSignal()
    # ns_preferences_general_notifyUpdates = pyqtSignal()
    # ns_preferences_general_showStartPage = pyqtSignal(bool)
    # ns_preferences_general_confirmExit = pyqtSignal(bool)
    # ns_preferences_general_workspace = pyqtSignal()
    ns_preferences_general_supportedExtensions = pyqtSignal("QStringList")
    #ns_preferences_general_notification_position = pyqtSignal(int)
    #...
    ns_preferences_general_loadFiles = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_general_activatePlugins = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_general_notifyUpdates = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_general_showStartPage = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_general_confirmExit = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_general_workspace = pyqtSignal(str)  # dato: ''

    #ns_preferences_general_supportedExtensions = pyqtSignal(list)# dato: '['.py', '.pyw', '.html', '.jpg','.png', '.ui', '.css', '.json', '.js', '.ini']'

    ns_preferences_general_notification_position = pyqtSignal(int)  # dato: '0'

    ns_preferences_general_notification_color = pyqtSignal(str)  # dato: '#000'

    ns_pythonPath = pyqtSignal(str)  # dato: 'D:\Python34\python.exe'

    ns_executionOptions = pyqtSignal(str)  # dato: ''

    ns_Show_Code_Nav = pyqtSignal(str)  # dato: 'Ctrl+3'

    ns_Follow_mode = pyqtSignal(str)  # dato: 'Ctrl+F10'

    ns_Change_Tab = pyqtSignal(str)  # dato: 'Ctrl+PgDown'

    ns_Change_Tab_Reverse = pyqtSignal(str)  # dato: 'Ctrl+PgUp'

    ns_Close_file = pyqtSignal(str)  # dato: 'Ctrl+W'

    ns_Close_Split = pyqtSignal(str)  # dato: 'Shift+F9'

    ns_Comment = pyqtSignal(str)  # dato: 'Ctrl+G'

    ns_Complete_Declarations = pyqtSignal(str)  # dato: 'Alt+Return'

    ns_copy = pyqtSignal(str)  # dato: 'Ctrl+C'

    ns_History_Copy = pyqtSignal(str)  # dato: 'Ctrl+Alt+C'

    ns_New_project = pyqtSignal(str)  # dato: 'Ctrl+Shift+N'

    ns_New_file = pyqtSignal(str)  # dato: 'Ctrl+N'

    ns_cut = pyqtSignal(str)  # dato: 'Ctrl+X'

    ns_Debug = pyqtSignal(str)  # dato: 'F7'

    ns_Duplicate = pyqtSignal(str)  # dato: 'Ctrl+R'

    ns_Run_file = pyqtSignal(str)  # dato: 'Ctrl+F6'

    ns_Run_project = pyqtSignal(str)  # dato: 'F6'

    ns_expand_file_combo = pyqtSignal(str)  # dato: 'Ctrl+Tab'

    ns_expand_symbol_combo = pyqtSignal(str)  # dato: 'Ctrl+2'

    ns_Find = pyqtSignal(str)  # dato: 'Ctrl+F'

    ns_Find_replace = pyqtSignal(str)  # dato: 'Ctrl+H'

    ns_Find_in_files = pyqtSignal(str)  # dato: 'Ctrl+L'

    ns_Find_next = pyqtSignal(str)  # dato: 'Ctrl+F3'

    ns_Find_previous = pyqtSignal(str)  # dato: 'Shift+F3'

    ns_Find_with_word = pyqtSignal(str)  # dato: 'Ctrl+Shift+F'

    ns_Full_screen = pyqtSignal(str)  # dato: 'Ctrl+F11'

    ns_Go_to_definition = pyqtSignal(str)  # dato: 'Ctrl+Return'

    ns_Hide_all = pyqtSignal(str)  # dato: 'F11'

    ns_Hide_editor = pyqtSignal(str)  # dato: 'F3'

    ns_Hide_explorer = pyqtSignal(str)  # dato: 'F2'

    ns_Hide_misc = pyqtSignal(str)  # dato: 'F4'

    ns_Highlight_Word = pyqtSignal(str)  # dato: 'Ctrl+Down'

    ns_Import = pyqtSignal(str)  # dato: 'Ctrl+I'

    ns_Indent_less = pyqtSignal(str)  # dato: 'Shift+Tab'

    ns_Indent_more = pyqtSignal(str)  # dato: 'Tab'

    ns_Add_Bookmark_or_Breakpoint = pyqtSignal(str)  # dato: 'Ctrl+B'

    ns_Title_comment = pyqtSignal(str)  # dato: ''

    ns_Horizontal_line = pyqtSignal(str)  # dato: ''

    ns_Move_down = pyqtSignal(str)  # dato: 'Alt+Down'

    ns_Move_up = pyqtSignal(str)  # dato: 'Alt+Up'

    ns_Move_Tab_to_left = pyqtSignal(str)  # dato: 'Ctrl+Shift+9'

    ns_Move_Tab_to_right = pyqtSignal(str)  # dato: 'Ctrl+Shift+0'

    ns_Navigate_back = pyqtSignal(str)  # dato: 'Alt+Left'

    ns_Navigate_forward = pyqtSignal(str)  # dato: 'Alt+Right'

    ns_Open_file = pyqtSignal(str)  # dato: 'Ctrl+O'

    ns_Open_project = pyqtSignal(str)  # dato: 'Ctrl+Shift+O'

    ns_Open_recent_closed = pyqtSignal(str)  # dato: 'Ctrl+Shift+T'

    ns_paste = pyqtSignal(str)  # dato: 'Ctrl+V'

    ns_History_Paste = pyqtSignal(str)  # dato: 'Ctrl+Alt+V'

    ns_Print_file = pyqtSignal(str)  # dato: 'Ctrl+P'

    ns_Redo = pyqtSignal(str)  # dato: 'Ctrl+Y'

    ns_Reload_file = pyqtSignal(str)  # dato: 'F5'

    ns_Remove_line = pyqtSignal(str)  # dato: 'Ctrl+E'

    ns_Save_file = pyqtSignal(str)  # dato: 'Ctrl+S'

    ns_Save_project = pyqtSignal(str)  # dato: 'Ctrl+Shift+S'

    ns_Code_locator = pyqtSignal(str)  # dato: 'Ctrl+K'

    ns_Show_Paste_History = pyqtSignal(str)  # dato: 'Ctrl+4'

    ns_File_Opener = pyqtSignal(str)  # dato: 'Ctrl+Alt+O'

    ns_Help = pyqtSignal(str)  # dato: 'F1'

    ns_Show_Selector = pyqtSignal(str)  # dato: 'Ctrl+`'

    ns_Split_assistance = pyqtSignal(str)  # dato: 'F10'

    ns_change_tab_visibility = pyqtSignal(str)  # dato: 'Shift+F1'

    ns_Split_horizontal = pyqtSignal(str)  # dato: 'F9'

    ns_Split_vertical = pyqtSignal(str)  # dato: 'Ctrl+F9'

    ns_Stop_execution = pyqtSignal(str)  # dato: 'Ctrl+Shift+F6'

    ns_Uncomment = pyqtSignal(str)  # dato: 'Ctrl+Shift+G'

    ns_undo = pyqtSignal(str)  # dato: 'Ctrl+Z'

    ns_preferences_interface_showProjectExplorer = pyqtSignal(
        bool)  # dato: 'True'

    ns_preferences_interface_showSymbolsList = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_interface_showWebInspector = pyqtSignal(
        bool)  # dato: 'False'

    ns_preferences_interface_showErrorsList = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_interface_showMigrationList = pyqtSignal(
        bool)  # dato: 'True'

    ns_preferences_interface_language = pyqtSignal(str)  # dato: 'English'

    ns_preferences_editor_font = pyqtSignal(
        QFont)  # dato: '<PyQt5.QtGui.QFont object at 0x089D32F0>'

    ns_preferences_editor_minimapMaxOpacity = pyqtSignal(float)  # dato: '0.8'

    ns_preferences_editor_minimapMinOpacity = pyqtSignal(float)  # dato: '0.1'

    ns_preferences_editor_minimapSizeProportion = pyqtSignal(
        float)  # dato: '0.17'

    ns_preferences_editor_minimapShow = pyqtSignal(bool)  # dato: 'False'

    ns_preferences_editor_scheme = pyqtSignal(str)  # dato: 'default'

    ns_preferences_editor_useTabs = pyqtSignal(bool)  # dato: 'False'

    ns_preferences_editor_marginLine = pyqtSignal(int)  # dato: '80'

    ns_preferences_editor_showMarginLine = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_indent = pyqtSignal(int)  # dato: '4'

    ns_preferences_editor_platformEndOfLine = pyqtSignal(bool)  # dato: 'False'

    ns_preferences_editor_errorsUnderlineBackground = pyqtSignal(
        bool)  # dato: 'True'

    ns_preferences_editor_errors = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_errorsInLine = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_checkStyle = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_showMigrationTips = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_checkStyleInline = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_centerOnScroll = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_removeTrailingSpaces = pyqtSignal(
        bool)  # dato: 'True'

    ns_preferences_editor_allowWordWrap = pyqtSignal(bool)  # dato: 'False'

    ns_preferences_editor_showTabsAndSpaces = pyqtSignal(bool)  # dato: 'False'

    ns_preferences_editor_showIndentationGuide = pyqtSignal(
        bool)  # dato: 'True'

    ns_preferences_editor_checkForDocstrings = pyqtSignal(
        bool)  # dato: 'False'

    ns_preferences_editor_showLineNumbers = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_parentheses = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_brackets = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_keys = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_simpleQuotes = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_doubleQuotes = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_codeCompletion = pyqtSignal(bool)  # dato: 'True'

    ns_preferences_editor_completeDeclarations = pyqtSignal(
        bool)  # dato: 'True'

    ns_preferences_theme_skin = pyqtSignal(str)  # dato: 'Default'

    ds_lastSession_projects = pyqtSignal(list)  # dato: '[]'

    ds_lastSession_openedFiles = pyqtSignal(list)  # dato: '[]'

    ds_lastSession_currentFile = pyqtSignal(str)  # dato: ''

    ds_lastSession_recentFiles = pyqtSignal(list)  # dato: '[]'

    ns_preferences_editor_bookmarks = pyqtSignal(dict)  # dato: '{}'

    ns_preferences_editor_breakpoints = pyqtSignal(dict)  # dato: '{}'

    ns_window_maximized = pyqtSignal(bool)  # dato: 'True'

    ns_window_central_baseSplitterSize = pyqtSignal(
        QByteArray
    )  # dato: 'b'\x00\x00\x00\xff\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x03\x84\x00\x00\x00\xc8\x01\xff\xff\xff\xff\x01\x00\x00\x00\x01\x01''

    ns_window_central_insideSplitterSize = pyqtSignal(
        QByteArray
    )  # dato: 'b'\x00\x00\x00\xff\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x01B\x00\x00\x00\xa8\x01\xff\xff\xff\xff\x01\x00\x00\x00\x02\x01''

    ns_window_central_lateralVisible = pyqtSignal(bool)  # dato: 'True'

    ns_window_hide_toolbar = pyqtSignal(bool)  # dato: 'False'

    ns_tools_dock_visible = pyqtSignal(bool)  # dato: 'True'

    #...
    ds_recentProjects = pyqtSignal(dict)
    ns_window_size = pyqtSignal(QSize)
    ns_window_pos = pyqtSignal(QPoint)

    def __init__(self, start_server=False):
        super(IDE, self).__init__()
        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

        IDE.__instance = self

        wid = QWidget()  #adjustSize
        wid.setContentsMargins(0, 0, 0, 0)
        box = QHBoxLayout(wid)
        box.setContentsMargins(0, 0, 0, 0)
        # l1 = QLabel("Info Here")
        # l1.setObjectName("Info")
        # l1.setStyleSheet("background-color: rgb(88, 255, 85);")
        # box.addWidget(l1)
        space = QSpacerItem(10, 10,
                            QSizePolicy.Expanding)  #, QSizePolicy.Maximum)
        box.addSpacerItem(space)
        l2 = QLabel(
            "Tab Size: " + str(settings.INDENT)
        )  #int(qsettings.value('preferences/editor/indent', 4, type=int))))
        l2.setObjectName("Det1")

        font = l2.font()
        font.setPointSize(8)
        l2.setFont(font)
        box.addWidget(l2)

        box.addSpacing(50)

        l3 = QLabel("Python")
        l3.setObjectName("Det2")
        font.setPointSize(9)
        l3.setFont(font)
        box.addWidget(l3)

        box.addSpacing(30)

        status = self.statusBar()
        status.setMaximumHeight(20)
        status.addPermanentWidget(wid)
        # wid.show()
        # self.__wid = wid
        status.reformat()
        status.showMessage("Info Here")
        status.setStyleSheet("background-color: rgb(85, 85, 85);")

        #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)

        self.statusBar().messageChanged[str].connect(
            self.MessageStatusChanged.emit)

        #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.trayIcon.closeTrayIcon.connect(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
            short.activated.connect(self._change_tab_index)
        short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self,
                                      10)
        short.activated.connect(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')
        print("self.central:", self.central)
        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.s_listener.newConnection.connect(self._process_connection)

    @classmethod
    def hasCreated(clss):
        return clss.__created

    @classmethod
    def getInstance(clss):
        return clss.__instance

    @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.hasCreated():
            cls.getInstance().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.hasCreated():
            cls.getInstance()._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):
                    getattr(target, signal_name).connect(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)
            editable.fileClosing.connect(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

    def showMessageStatus(self, msg):
        QTimer.singleShot(1, Qt.PreciseTimer,
                          lambda: self.statusBar().showMessage(msg))
        # self.statusBar().showMessage(msg)

    @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")
        print("\n\npreferences!!")
        if main_container:
            main_container.show_dialog(pref)
            print("\n\nmain_container---")
        else:
            pref.show()
            print("\n\nNONE---")

    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])
                    line, col = fileData[1][0], fileData[1][1]
                    main_container.open_file(fileData[0],
                                             line,
                                             col,
                                             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:
            super(IDE, self).wheelEvent(event)

    @classmethod
    def ninja_settings(cls):
        qsettings = nsettings.NSettings(resources.SETTINGS_PATH, prefix="ns")
        if cls.hasCreated():
            qsettings.valueChanged.connect(
                cls.getInstance()._settings_value_changed)
        return qsettings

    @classmethod
    def data_settings(cls):
        qsettings = nsettings.NSettings(resources.DATA_SETTINGS_PATH,
                                        prefix="ds")
        if cls.hasCreated():
            qsettings.valueChanged.connect(
                cls.getInstance()._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)
        key = key.replace("/", "_").replace("-", "_")
        try:
            getattr(self, key).emit(value)
        except TypeError as reason:
            print("\n:::", key, value, type(value))
            print("\n\nerrors:-:", reason)
            getattr(self, key).emit()
        except AttributeError:
            print("\n:::", key, value, type(value))

        # if not value:
        #     try:
        #         getattr(self, key.replace("/", "_")).emit(value)
        #     except TypeError:
        #         getattr(self, key.replace("/", "_")).emit()

        #     return

        # try:
        #     getattr(self, key.replace("/", "_")).emit(value)
        # except TypeError as e:
        #     print("\n\nerrors", e)
        #     getattr(self, key.replace("/", "_")).emit()
        ##getattr(self, key.replace("/", "_").replace("-", "_")).emit(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:
                if not openedFiles[path]._exists():
                    print("\n\ncontinue", path)
                    continue
                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.getCursorPosition(), 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)
        qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS)
        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
        qsettings.setValue("window/hide_toolbar", not self.toolbar.isVisible())

        #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 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", QSize(800, 600), type='QSize'))
            self.move(
                qsettings.value("window/pos", QPoint(100, 100), 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)
            print("\n\neditable::", editable, getattr(editable, "editor", "-"))
            if editable is not None and editable.editor 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.goingDown.emit()
        #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")
        print("\nshow_plugins_store")
        if main_container:
            print("\nshow_plugins_store::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()
Ejemplo n.º 11
0
class _s_IDE(QMainWindow):
###############################################################################
# SIGNALS
#
# goingDown()
###############################################################################
    goingDown = pyqtSignal()
    def __init__(self, start_server=False):
        super(_s_IDE, self).__init__()
        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()
        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.s_listener.newConnection.connect(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(_translate("_s_IDE", "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.mainContainer.currentTabChanged[str].connect(self.actions.update_explorer)

        #Menu
        menubar = self.menuBar()
        file_ = menubar.addMenu(_translate("_s_IDE", "&File"))
        edit = menubar.addMenu(_translate("_s_IDE", "&Edit"))
        view = menubar.addMenu(_translate("_s_IDE", "&View"))
        source = menubar.addMenu(_translate("_s_IDE", "&Source"))
        project = menubar.addMenu(_translate("_s_IDE", "&Project"))
        self.pluginsMenu = menubar.addMenu(_translate("_s_IDE", "&Addins"))
        about = menubar.addMenu(_translate("_s_IDE", "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._menuFile.openFile[str].connect(self.mainContainer.open_file)
        self.mainContainer.fileSaved[str].connect(self.show_status_message)
        self.mainContainer.recentTabsModified[list].connect(self._menuFile.update_recent_files)#'QStringList'
        self.mainContainer.currentTabChanged[str].connect(self.actions.update_migration_tips)
        self.mainContainer.updateFileMetadata.connect(self.actions.update_migration_tips)
        self.mainContainer.migrationAnalyzed.connect(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.mainContainer.currentTabChanged[str].connect(self.change_window_title)
        self.mainContainer.locateFunction[str, str, bool].connect(self.actions.locate_function)
        self.mainContainer.navigateCode[bool, int].connect(self.actions.navigate_code_history)
        self.mainContainer.addBackItemNavigation.connect(self.actions.add_back_item_navigation)
        self.mainContainer.updateFileMetadata.connect(self.actions.update_explorer)
        self.mainContainer.updateLocator[str].connect(self.actions.update_explorer)
        self.mainContainer.openPreferences.connect(self._show_preferences)
        self.mainContainer.dontOpenStartPage.connect(self._dont_show_start_page_again)
        self.mainContainer.currentTabChanged[str].connect(self.status.handle_tab_changed)
        # When close the last tab cleanup
        self.mainContainer.allTabsClosed.connect(self._last_tab_closed)
        # Update symbols
        self.mainContainer.updateLocator[str].connect(self.status.explore_file_code)
        #Create Explorer Panel
        self.explorer = explorer_container.ExplorerContainer(self)
        self.central.splitterCentralRotated.connect(self.explorer.rotate_tab_position)
        self.explorer.updateLocator.connect(self.status.explore_code)
        self.explorer.goToDefinition[int].connect(self.actions.editor_go_to_line)
        self.explorer.projectClosed[str].connect(self.actions.close_files_from_project)
        #Create Misc Bottom Container
        self.misc = misc_container.MiscContainer(self)
        self.mainContainer.findOcurrences[str].connect(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.mainContainer.cursorPositionChange[int, int].connect(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.mainContainer.enabledFollowMode[bool].connect(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.explorer.projectOpened[str].connect(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.explorer.projectOpened[str].disconnect(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):
        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(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 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,
                _translate("_s_IDE", "Some changes were not saved"),
                (_translate("_s_IDE", "%s\n\nDo you want to save them?") % txt),
                QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
            if val == QMessageBox.Yes:
                #Saves all open files
                self.mainContainer.save_all()
            if val == QMessageBox.Cancel:
                event.ignore()
        self.goingDown.emit()
        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_()

    def show_python_detection(self):
        suggested = settings.detect_python_path()
        if suggested:
            dialog = python_detect_dialog.PythonDetectDialog(suggested, self)
            dialog.show()
Ejemplo n.º 12
0
class Pireal(QMainWindow):

    """
    Main Window class

    This class is responsible for installing all application services.
    """

    __SERVICES = {}
    __ACTIONS = {}

    # The name of items is the connection text
    TOOLBAR_ITEMS = [
        'create_database',
        'open_database',
        'save_database',
        '',  # Is a separator!
        'new_query',
        'open_query',
        'save_query',
        '',
        'undo_action',
        'redo_action',
        'cut_action',
        'copy_action',
        'paste_action',
        '',
        'create_new_relation',
        'remove_relation',
        'edit_relation',
        '',
        'execute_queries'
    ]

    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle(self.tr("Pireal"))
        self.setMinimumSize(700, 500)

        # Load window geometry
        qsettings = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat)
        window_maximized = qsettings.value('window_max', True, type=bool)
        if window_maximized:
            self.showMaximized()
        else:
            size = qsettings.value('window_size')
            self.resize(size)
            position = qsettings.value('window_pos')
            self.move(position)
        # Toolbar
        self.toolbar = QToolBar(self)
        self.toolbar.setIconSize(QSize(22, 22))
        self.toolbar.setMovable(False)
        self.addToolBar(self.toolbar)

        # Menu bar
        menubar = self.menuBar()
        self.__load_menubar(menubar)
        # Load notification widget after toolbar actions
        notification_widget = Pireal.get_service("notification")
        self.toolbar.addWidget(notification_widget)
        # Message error
        self._msg_error_widget = message_error.MessageError(self)
        # Central widget
        central_widget = Pireal.get_service("central")
        central_widget.databaseSaved.connect(notification_widget.show_text)
        central_widget.querySaved.connect(notification_widget.show_text)
        self.setCentralWidget(central_widget)
        central_widget.add_start_page()

        # Check for updates
        self._thread = QThread()
        self._updater = updater.Updater()
        self._updater.moveToThread(self._thread)
        self._thread.started.connect(self._updater.check_updates)
        self._updater.finished.connect(self.__on_thread_update_finished)
        self._thread.start()
        notification_widget.show_text(
            self.tr("Checking for updates..."), time_out=0)

        # Install service
        Pireal.load_service("pireal", self)

    @classmethod
    def get_service(cls, service):
        """ Return the instance of a loaded service """

        return cls.__SERVICES.get(service, None)

    @classmethod
    def load_service(cls, name, instance):
        """ Load a service providing the service name and the instance """

        cls.__SERVICES[name] = instance

    @classmethod
    def get_action(cls, name):
        """ Return the instance of a loaded QAction """

        return cls.__ACTIONS.get(name, None)

    @classmethod
    def load_action(cls, name, action):
        """ Load a QAction """

        cls.__ACTIONS[name] = action

    def __load_menubar(self, menubar):
        """
        This method installs the menubar and toolbar, menus and QAction's,
        also connects to a slot each QAction.
        """

        from src.gui import menu_actions
        from src import keymap

        # Keymap
        kmap = keymap.KEYMAP
        # Toolbar items
        toolbar_items = {}

        central = Pireal.get_service("central")

        # Load menu bar
        for item in menu_actions.MENU:
            menubar_item = menu_actions.MENU[item]
            menu_name = menubar_item['name']
            items = menubar_item['items']
            menu = menubar.addMenu(menu_name)
            for menu_item in items:
                if isinstance(menu_item, str):
                    # Is a separator
                    menu.addSeparator()
                else:
                    action = menu_item['name']
                    obj, connection = menu_item['slot'].split(':')
                    if obj.startswith('central'):
                        obj = central
                    else:
                        obj = self
                    qaction = menu.addAction(action)
                    # Icon name is connection
                    icon = QIcon(":img/%s" % connection)
                    qaction.setIcon(icon)

                    # Install shortcuts
                    shortcut = kmap.get(connection, None)
                    if shortcut is not None:
                        qaction.setShortcut(shortcut)

                    # Items for toolbar
                    if connection in Pireal.TOOLBAR_ITEMS:
                        toolbar_items[connection] = qaction

                    # The name of QAction is the connection
                    Pireal.load_action(connection, qaction)
                    slot = getattr(obj, connection, None)
                    if isinstance(slot, Callable):
                        qaction.triggered.connect(slot)

        # Install toolbar
        self.__install_toolbar(toolbar_items)
        # Disable some actions
        self.set_enabled_db_actions(False)
        self.set_enabled_relation_actions(False)
        self.set_enabled_query_actions(False)
        self.set_enabled_editor_actions(False)

    def __install_toolbar(self, toolbar_items):
        for action in Pireal.TOOLBAR_ITEMS:
            qaction = toolbar_items.get(action, None)
            if qaction is not None:
                self.toolbar.addAction(qaction)
            else:
                self.toolbar.addSeparator()

    def __show_status_message(self, msg):
        status = Pireal.get_service("status")
        status.show_message(msg)

    def __on_thread_update_finished(self):
        self._thread.quit()
        # Clear notificator
        notification_widget = Pireal.get_service("notification")
        notification_widget.clear()

        msg = QMessageBox(self)
        if not self._updater.error:
            if self._updater.version:
                version = self._updater.version
                msg.setWindowTitle(self.tr("New version available!"))
                msg.setText(self.tr("Check the web site to "
                                    "download <b>Pireal {}</b>".format(
                                        version)))
                download_btn = msg.addButton(self.tr("Download!"),
                                             QMessageBox.YesRole)
                msg.addButton(self.tr("Cancel"),
                              QMessageBox.RejectRole)
                msg.exec_()
                r = msg.clickedButton()
                if r == download_btn:
                    webbrowser.open_new(
                        "http://centaurialpha.github.io/pireal")
        self._thread.deleteLater()
        self._updater.deleteLater()

    def change_title(self, title):
        self.setWindowTitle("Pireal " + '[' + title + ']')

    def set_enabled_db_actions(self, value):
        """ Public method. Enables or disables db QAction """

        actions = [
            'new_query',
            'open_query',
            'close_database',
            'save_database',
            'save_database_as',
            'load_relation'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def set_enabled_relation_actions(self, value):
        """ Public method. Enables or disables relation's QAction """

        actions = [
            'create_new_relation',
            'remove_relation',
            'edit_relation'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def set_enabled_query_actions(self, value):
        """ Public method. Enables or disables queries QAction """

        actions = [
            'execute_queries',
            'save_query'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def set_enabled_editor_actions(self, value):
        """ Public slot. Enables or disables editor actions """

        actions = [
            'undo_action',
            'redo_action',
            'copy_action',
            'cut_action',
            'paste_action',
            'zoom_in',
            'zoom_out',
            'comment',
            'uncomment'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def about_qt(self):
        """ Show about qt dialog """

        QMessageBox.aboutQt(self)

    def about_pireal(self):
        """ Show the bout Pireal dialog """

        from src.gui.dialogs import about_dialog
        dialog = about_dialog.AboutDialog(self)
        dialog.exec_()

    def report_issue(self):
        """ Open in the browser the page to create new  issue """

        webbrowser.open("http://github.com/centaurialpha/pireal/issues/new")

    def show_hide_menubar(self):
        """ Change visibility of menu bar """

        if self.menuBar().isVisible():
            self.menuBar().hide()
        else:
            self.menuBar().show()

    def show_hide_toolbar(self):
        """ Change visibility of tool bar """

        if self.toolbar.isVisible():
            self.toolbar.hide()
        else:
            self.toolbar.show()

    def show_error_message(self, text, syntax_error=True):
        self._msg_error_widget.show_msg(text, syntax_error)
        self._msg_error_widget.show()

    def closeEvent(self, event):
        qsettings = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat)
        # Save window geometry
        if self.isMaximized():
            qsettings.setValue('window_max', True)
        else:
            qsettings.setValue('window_max', False)
            qsettings.setValue('window_pos', self.pos())
            qsettings.setValue('window_size', self.size())

        central_widget = Pireal.get_service("central")
        # Save recent databases
        qsettings.setValue('recent_databases',
                           central_widget.recent_databases)
        db = central_widget.get_active_db()
        if db is not None:
            # Save splitters size
            db.save_sizes()
            # Databases unsaved
            if db.modified:
                msg = QMessageBox(self)
                msg.setIcon(QMessageBox.Question)
                msg.setWindowTitle(self.tr("Some changes where not saved"))
                msg.setText(
                    self.tr("Do you want to save changes to the database?"))
                cancel_btn = msg.addButton(self.tr("Cancel"),
                                           QMessageBox.RejectRole)
                msg.addButton(self.tr("No"),
                              QMessageBox.NoRole)
                yes_btn = msg.addButton(self.tr("Yes"),
                                        QMessageBox.YesRole)
                msg.exec_()
                r = msg.clickedButton()
                if r == yes_btn:
                    central_widget.save_database()
                if r == cancel_btn:
                    event.ignore()
            # Query files
            unsaved_editors = central_widget.get_unsaved_queries()
            if unsaved_editors:
                msg = QMessageBox(self)
                msg.setIcon(QMessageBox.Question)
                msg.setWindowTitle(self.tr("Unsaved Queries"))
                text = '\n'.join([editor.name for editor in unsaved_editors])
                msg.setText(self.tr("{files}<br><br>Do you want to "
                                    "save them?".format(files=text)))
                cancel_btn = msg.addButton(self.tr("Cancel"),
                                           QMessageBox.RejectRole)
                msg.addButton(self.tr("No"),
                              QMessageBox.NoRole)
                yes_btn = msg.addButton(self.tr("Yes"),
                                        QMessageBox.YesRole)
                msg.exec_()
                if msg.clickedButton() == yes_btn:
                    for editor in unsaved_editors:
                        central_widget.save_query(editor)
                if msg.clickedButton() == cancel_btn:
                    event.ignore()
Ejemplo n.º 13
0
class Pireal(QMainWindow):

    """
    Main Window class

    This class is responsible for installing all application services.
    """

    __SERVICES = {}
    __ACTIONS = {}

    # The name of items is the connection text
    TOOLBAR_ITEMS = [
        'create_database',
        'open_database',
        'save_database',
        '',  # Is a separator!
        'new_query',
        'open_query',
        'save_query',
        '',
        'relation_menu',
        '',
        # 'create_new_relation',
        # 'remove_relation',
        # '',
        # 'add_tuple',
        # 'delete_tuple',
        # 'add_column',
        # 'delete_column',
        # '',
        'execute_queries'
    ]

    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle(self.tr("Pireal"))
        self.setMinimumSize(880, 600)
        # Load window geometry
        qsettings = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat)
        window_maximized = qsettings.value('window_max', True, type=bool)
        if window_maximized:
            self.showMaximized()
        else:
            size = qsettings.value('window_size')
            self.resize(size)
            position = qsettings.value('window_pos')
            self.move(position)
        # Toolbar
        self.toolbar = QToolBar(self)
        self.toolbar.setFixedWidth(38)
        self.toolbar.setIconSize(QSize(38, 38))
        self.toolbar.setMovable(False)
        self.addToolBar(Qt.RightToolBarArea, self.toolbar)

        # Menu bar
        menubar = self.menuBar()
        self.__load_menubar(menubar)
        # Load notification widget after toolbar actions
        notification_widget = Pireal.get_service("notification")
        self.toolbar.addWidget(notification_widget)
        # Message error
        # self._msg_error_widget = message_error.MessageError(self)
        # Central widget
        central_widget = Pireal.get_service("central")
        central_widget.databaseSaved.connect(notification_widget.show_text)
        central_widget.querySaved.connect(notification_widget.show_text)
        central_widget.databaseConected.connect(self.change_title)
        self.setCentralWidget(central_widget)
        central_widget.add_start_page()

        # Install service
        Pireal.load_service("pireal", self)

    @classmethod
    def get_service(cls, service):
        """ Return the instance of a loaded service """

        return cls.__SERVICES.get(service, None)

    @classmethod
    def load_service(cls, name, instance):
        """ Load a service providing the service name and the instance """

        cls.__SERVICES[name] = instance

    @classmethod
    def get_action(cls, name):
        """ Return the instance of a loaded QAction """

        return cls.__ACTIONS.get(name, None)

    @classmethod
    def load_action(cls, name, action):
        """ Load a QAction """

        cls.__ACTIONS[name] = action

    def __load_menubar(self, menubar):
        """
        This method installs the menubar and toolbar, menus and QAction's,
        also connects to a slot each QAction.
        """

        # Keymap
        kmap = keymap.KEYMAP

        central = Pireal.get_service("central")

        # Load menu bar
        rela_actions = []
        for item in menu_actions.MENU:
            menubar_item = menu_actions.MENU[item]
            menu_name = menubar_item['name']
            items = menubar_item['items']
            menu = menubar.addMenu(menu_name)
            for menu_item in items:
                if isinstance(menu_item, str):
                    # Is a separator
                    menu.addSeparator()
                else:
                    action = menu_item['name']
                    obj_name, connection = menu_item['slot'].split(':')
                    obj = central
                    if obj_name.startswith('pireal'):
                        obj = self
                    qaction = menu.addAction(action)
                    # Icon name is connection
                    icon = QIcon(":img/%s" % connection)
                    qaction.setIcon(icon)

                    # Install shortcuts
                    shortcut = kmap.get(connection, None)
                    if shortcut is not None:
                        qaction.setShortcut(shortcut)

                    # The name of QAction is the connection
                    if item == "relation":
                        if connection != "execute_queries":
                            rela_actions.append(qaction)
                    Pireal.load_action(connection, qaction)
                    slot = getattr(obj, connection, None)
                    if isinstance(slot, Callable):
                        qaction.triggered.connect(slot)

        # Install toolbar
        # self.__install_toolbar(toolbar_items, rela_actions)
        self.__install_toolbar(rela_actions)
        # Disable some actions
        self.set_enabled_db_actions(False)
        self.set_enabled_relation_actions(False)
        self.set_enabled_query_actions(False)
        self.set_enabled_editor_actions(False)

    def __install_toolbar(self, rela_actions):
        menu = QMenu()
        tool_button = QToolButton()
        tool_button.setIcon(QIcon(":img/create_new_relation"))
        tool_button.setMenu(menu)
        tool_button.setPopupMode(QToolButton.InstantPopup)
        for item in self.TOOLBAR_ITEMS:
            if item:
                if item == "relation_menu":
                    # Install menu for relation
                    menu.addActions(rela_actions)
                    self.toolbar.addWidget(tool_button)
                else:
                    self.toolbar.addAction(self.__ACTIONS[item])
            else:
                self.toolbar.addSeparator()

    def __show_status_message(self, msg):
        status = Pireal.get_service("status")
        status.show_message(msg)

    def __on_thread_update_finished(self):
        self._thread.quit()
        # Clear notificator
        notification_widget = Pireal.get_service("notification")
        notification_widget.clear()

        msg = QMessageBox(self)
        if not self._updater.error and self._updater.version:
            version = self._updater.version
            msg.setWindowTitle(self.tr("New version available!"))
            msg.setText(self.tr("Check the web site to "
                                "download <b>Pireal {}</b>".format(
                                    version)))
            download_btn = msg.addButton(self.tr("Download!"),
                                         QMessageBox.YesRole)
            msg.addButton(self.tr("Cancel"),
                          QMessageBox.RejectRole)
            msg.exec_()
            r = msg.clickedButton()
            if r == download_btn:
                webbrowser.open_new("http://centaurialpha.github.io/pireal")
        self._thread.deleteLater()
        self._updater.deleteLater()

    def change_title(self, title=''):
        if title:
            _title = title + " - Pireal "
        else:
            _title = "Pireal"
        self.setWindowTitle(_title)

    def set_enabled_db_actions(self, value):
        """ Public method. Enables or disables db QAction """

        actions = [
            'new_query',
            'open_query',
            'close_database',
            'save_database',
            'save_database_as',
            'load_relation'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def set_enabled_relation_actions(self, value):
        """ Public method. Enables or disables relation's QAction """

        actions = [
            'create_new_relation',
            'remove_relation',
            'add_tuple',
            'delete_tuple',
            # 'add_column',
            # 'delete_column'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def set_enabled_query_actions(self, value):
        """ Public method. Enables or disables queries QAction """

        actions = [
            'execute_queries',
            'save_query'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def set_enabled_editor_actions(self, value):
        """ Public slot. Enables or disables editor actions """

        actions = [
            'undo_action',
            'redo_action',
            'copy_action',
            'cut_action',
            'paste_action',
            'zoom_in',
            'zoom_out',
            'comment',
            'uncomment',
            'search'
        ]

        for action in actions:
            qaction = Pireal.get_action(action)
            qaction.setEnabled(value)

    def about_qt(self):
        """ Show about qt dialog """

        QMessageBox.aboutQt(self)

    def about_pireal(self):
        """ Show the bout Pireal dialog """

        from src.gui.dialogs import about_dialog
        dialog = about_dialog.AboutDialog(self)
        dialog.exec_()

    def report_issue(self):
        """ Open in the browser the page to create new  issue """

        webbrowser.open("http://github.com/centaurialpha/pireal/issues/new")

    def show_hide_menubar(self):
        """ Change visibility of menu bar """

        if self.menuBar().isVisible():
            self.menuBar().hide()
        else:
            self.menuBar().show()

    def show_hide_toolbar(self):
        """ Change visibility of tool bar """

        if self.toolbar.isVisible():
            self.toolbar.hide()
        else:
            self.toolbar.show()

    def show_error_message(self, text, syntax_error=True):
        self._msg_error_widget.show_msg(text, syntax_error)
        self._msg_error_widget.show()

    def save_user_settings(self):
        central_widget = Pireal.get_service("central")
        CONFIG.set_value("lastOpenFolder", central_widget.last_open_folder)
        CONFIG.set_value("recentFiles", central_widget.recent_databases)

        # Write settings
        CONFIG.save_settings()

    def closeEvent(self, event):
        self.save_user_settings()

        # Qt settings
        qsettings = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat)
        # # Save window geometry
        if self.isMaximized():
            qsettings.setValue('window_max', True)
        else:
            qsettings.setValue('window_max', False)
            qsettings.setValue('window_pos', self.pos())
            qsettings.setValue('window_size', self.size())

        central_widget = Pireal.get_service("central")
        db = central_widget.get_active_db()
        if db is not None:
            # Save splitters size
            db.save_sizes()
            # Databases unsaved
            if db.modified:
                msg = QMessageBox(self)
                msg.setIcon(QMessageBox.Question)
                msg.setWindowTitle(self.tr("Algunos cambios no fueron guardados"))
                msg.setText(
                    self.tr("Desea guardar los cambios en la base de datos?"))
                cancel_btn = msg.addButton(self.tr("Cancelar"),
                                           QMessageBox.RejectRole)
                msg.addButton(self.tr("No"),
                              QMessageBox.NoRole)
                yes_btn = msg.addButton(self.tr("Si"),
                                        QMessageBox.YesRole)
                msg.exec_()
                r = msg.clickedButton()
                if r == yes_btn:
                    central_widget.save_database()
                if r == cancel_btn:
                    event.ignore()
            # Query files
            unsaved_editors = central_widget.get_unsaved_queries()
            if unsaved_editors:
                msg = QMessageBox(self)
                msg.setIcon(QMessageBox.Question)
                msg.setWindowTitle(self.tr("Consultas no guardadas"))
                text = '\n'.join([editor.name for editor in unsaved_editors])
                msg.setText(self.tr("{files}\n\nQuiere guardarlas?".format(
                    files=text)))
                cancel_btn = msg.addButton(self.tr("Cancelar"),
                                           QMessageBox.RejectRole)
                msg.addButton(self.tr("No"),
                              QMessageBox.NoRole)
                yes_btn = msg.addButton(self.tr("Si"),
                                        QMessageBox.YesRole)
                msg.exec_()
                if msg.clickedButton() == yes_btn:
                    for editor in unsaved_editors:
                        central_widget.save_query(editor)
                if msg.clickedButton() == cancel_btn:
                    event.ignore()
Ejemplo n.º 14
0
class MainWindow(QMainWindow):

    """Main Window."""

    def __init__(self, parent=None):
        """Initialize MainWindow."""
        super(MainWindow, self).__init__(parent)
        self.ram_info, self.ram_timer = QLabel(self), QTimer(self)
        self.menubar, self.view = QMenu(self), WebView(self)
        self.ram_timer.timeout.connect(self.update_statusbar)
        self.ram_timer.start(60000)  # Every 60 seconds
        self.statusBar().insertPermanentWidget(0, self.ram_info)
        self.setMinimumSize(640, 480)
        self.setMaximumSize(QDesktopWidget().screenGeometry().width() * 2,
                            QDesktopWidget().screenGeometry().height() * 2)
        self.palette().setBrush(QPalette.Base, Qt.transparent)
        self.setPalette(self.palette())  # Transparent palette
        self.setAttribute(Qt.WA_OpaquePaintEvent, False)  # no opaque paint
        self.setAttribute(Qt.WA_TranslucentBackground, True)  # translucent
        QShortcut("Ctrl+q", self, activated=self.close)
        self.make_toolbar()
        self.make_menubar()
        self.update_statusbar()
        self.setCentralWidget(self.view)
        if qdarkstyle:
            self.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())

    def paintEvent(self, event):
        """Paint transparent background,animated pattern,background text."""
        painter, font = QPainter(self), self.font()
        painter.fillRect(event.rect(), Qt.transparent)  # fill transparent rect
        painter.setPen(QPen(QColor(randint(9, 255), randint(9, 255), 255)))
        painter.rotate(30)  # Rotate painter ~30 Degree
        font.setBold(True)  # Set painter Font for text
        font.setPixelSize(100)
        painter.setFont(font)
        painter.drawText(99, 99, "Python Qt")  # draw the background text
        painter.rotate(-30)  # Rotate -30 the QPen back
        painter.setPen(Qt.NoPen)  # set the pen to no pen
        painter.setBrush(QColor("black"))  # Background Color
        painter.setOpacity(0.9)  # Background Opacity
        painter.drawRoundedRect(self.rect(), 25, 25)  # Back Rounded Borders
        for i in range(2048):  # animated random dots background pattern
            x = randint(10, self.size().width() - 10)
            y = randint(10, self.size().height() - 10)
            painter.setPen(QPen(QColor(randint(9, 255), randint(9, 255), 255)))
            painter.drawPoint(x, y)
        QMainWindow.paintEvent(self, event)

    def make_toolbar(self, list_of_actions=None):
        """Make or Update the main Tool Bar."""
        self.toolbar = QToolBar(self)
        self.left_spacer, self.right_spacer = QWidget(self), QWidget(self)
        self.left_spacer.setSizePolicy(1 | 2, 1 | 2)  # Expanding, Expanding
        self.right_spacer.setSizePolicy(1 | 2, 1 | 2)  # Expanding, Expanding
        self.toolbar.addAction("Menu",
                               lambda: self.menubar.exec_(QCursor.pos()))
        self.toolbar.addWidget(self.left_spacer)
        self.toolbar.addSeparator()
        self.toolbar.addAction(QIcon.fromTheme("help-contents"),
                               "Help and Docs", lambda: open_new_tab(__url__))
        self.toolbar.addAction(QIcon.fromTheme("help-about"), "About Qt 5",
                               lambda: QMessageBox.aboutQt(self))
        self.toolbar.addAction(QIcon.fromTheme("help-about"), "About Python 3",
                               lambda: open_new_tab('http://python.org/about'))
        self.toolbar.addAction(QIcon.fromTheme("application-exit"),
                               "Quit", self.close)
        self.toolbar.addSeparator()
        if list_of_actions and len(list_of_actions):
            for action in list_of_actions:  # if list_of_actions, add actions.
                self.toolbar.addAction(action)
            self.toolbar.addSeparator()
        self.toolbar.addWidget(self.right_spacer)
        self.addToolBar(self.toolbar)
        if sys.platform.startswith("win"):
            self.toolbar.hide()  # windows dont have QIcon.fromTheme,so hide.
        return self.toolbar

    def make_menubar(self, list_of_actions=None):
        """Make or Update the main Tool Bar."""
        self.menuBar().addMenu("&File").addAction("Exit", self.close)
        self.menubar.addMenu("&File").addAction("Exit", self.close)
        viewMenu = self.menuBar().addMenu("&View")
        viewMenu.addAction(
            "Toggle ToolBar", lambda:
                self.toolbar.setVisible(not self.toolbar.isVisible()))
        viewMenu.addAction(
            "Toggle StatusBar", lambda:
                self.statusBar().setVisible(not self.statusBar().isVisible()))
        viewMenu.addAction(
            "Toggle MenuBar", lambda:
                self.menuBar().setVisible(not self.menuBar().isVisible()))
        viewMenu2 = self.menubar.addMenu("&View")
        for action in viewMenu.actions():
            viewMenu2.addAction(action)
        windowMenu = self.menuBar().addMenu("&Window")
        windowMenu.addAction("Minimize", lambda: self.showMinimized())
        windowMenu.addAction("Maximize", lambda: self.showMaximized())
        windowMenu.addAction("Restore", lambda: self.showNormal())
        windowMenu.addAction("Full-Screen", lambda: self.showFullScreen())
        windowMenu.addAction("Center", lambda: self.center())
        windowMenu.addAction("Top-Left", lambda: self.move(0, 0))
        windowMenu.addAction("To Mouse", lambda: self.move(QCursor.pos()))
        windowMenu.addSeparator()
        windowMenu.addAction("Increase size", lambda: self.resize(
            self.size().width() * 1.5, self.size().height() * 1.5))
        windowMenu.addAction("Decrease size", lambda: self.resize(
            self.size().width() // 1.5, self.size().height() // 1.5))
        windowMenu.addAction("Minimum size", lambda:
                             self.resize(self.minimumSize()))
        windowMenu.addAction("Maximum size", lambda:
                             self.resize(self.maximumSize()))
        windowMenu.addAction("Horizontal Wide", lambda: self.resize(
            self.maximumSize().width(), self.minimumSize().height()))
        windowMenu.addAction("Vertical Tall", lambda: self.resize(
            self.minimumSize().width(), self.maximumSize().height()))
        windowMenu.addSeparator()
        windowMenu.addAction("Disable Resize", lambda:
                             self.setFixedSize(self.size()))
        windowMenu2 = self.menubar.addMenu("&Window")
        for action in windowMenu.actions():
            windowMenu2.addAction(action)
        optionMenu = self.menuBar().addMenu("&Options")
        optionMenu.addAction("Set Interface Font...", lambda:
                             self.setFont(QFontDialog.getFont(self)[0]))
        optionMenu.addAction("Load CSS Skin...", lambda:
                             self.setStyleSheet(self.skin()))
        optionMenu.addAction("Take ScreenShoot...", lambda: self.grab().save(
            QFileDialog.getSaveFileName(self, "Save", os.path.expanduser("~"),
                                        "(*.png) PNG image file", "png")[0]))
        optionMenu2 = self.menubar.addMenu("&Options")
        for action in optionMenu.actions():
            optionMenu2.addAction(action)
        helpMenu = self.menuBar().addMenu("&Help")
        helpMenu.addAction("About Qt 5", lambda: QMessageBox.aboutQt(self))
        helpMenu.addAction("About Python 3", lambda:
                           open_new_tab('https://www.python.org/about'))
        helpMenu.addSeparator()
        if sys.platform.startswith('linux'):
            helpMenu.addAction("View Source Code",
                               lambda: open_new_tab(__file__))
        helpMenu.addAction("View GitHub Repo", lambda: open_new_tab(__url__))
        helpMenu.addAction("Report Bugs", lambda:
                           open_new_tab(__url__ + '/issues?state=open'))
        helpMenu2 = self.menubar.addMenu("&Help")
        for action in helpMenu.actions():
            helpMenu2.addAction(action)
        return self.menuBar()

    def update_statusbar(self, custom_message=None):
        """Make or Update the Status Bar."""
        statusbar = self.statusBar()
        if resource:
            ram_use = int(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss *
                          resource.getpagesize() / 1024 / 1024)
            ram_byt = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES')
            ram_all = int(ram_byt / 1024 / 1024)
            self.ram_info.setText("{0} / {1} Mb".format(ram_use, ram_all))
            self.ram_info.setToolTip(
                "{0} of {1} MegaBytes of RAM Memory.".format(ram_use, ram_all))
        if custom_message and len(custom_message):
            return statusbar.showMessage(custom_message)
        return statusbar.showMessage(__doc__)

    def skin(self, filename=None):
        """Open QSS from filename,if no QSS return None,if no filename ask."""
        if not filename:
            filename = str(QFileDialog.getOpenFileName(
                self, __doc__ + " - Open QSS Skin", os.path.expanduser("~"),
                "CSS Cascading Style Sheet for Qt 5 (*.qss);;All (*.*)")[0])
        if filename and os.path.isfile(filename):
            with open(filename, 'r', encoding="utf-8-sig") as file_to_read:
                text = file_to_read.read().strip()
        if text:
            return text

    def center(self):
        """Center and resize the window."""
        self.showNormal()
        self.resize(QDesktopWidget().screenGeometry().width() // 1.25,
                    QDesktopWidget().screenGeometry().height() // 1.25)
        qr = self.frameGeometry()
        qr.moveCenter(QDesktopWidget().availableGeometry().center())
        return self.move(qr.topLeft())

    def closeEvent(self, event):
        """Ask to Quit."""
        return event.accept() if QMessageBox.question(
            self, "Close", "<h1>Quit ?.", QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No) == QMessageBox.Yes else event.ignore()