Example #1
0
    def _createContractionToolbar(self):
        toolbar = QToolBar(parent=self)
        toolbar.setFloatable(True)
        toolbar.setMovable(True)
        toolbar.hide()

        action = toolbar.addAction('next')
        action.triggered.connect(self.next_hg)
        action = toolbar.addAction('previous')
        action.triggered.connect(self.previous_hg)
        action = toolbar.addAction('stop')
        action.triggered.connect(self.stopContractionPlayerSlot)

        self.contraction_toolbar = toolbar
Example #2
0
class PMGWebBrowser(QWidget):
    def __init__(self, parent=None, toolbar='standard'):
        """

        :param parent:
        :param toolbar:多种选项:‘no’,‘standard’,'no_url_input'
        """
        super().__init__(parent)
        self.webview = PMGWebEngineView()
        # self.webview.load(QUrl("https://cn.bing.com"))
        self.setLayout(QVBoxLayout())
        self.toolbar = QToolBar()
        self.url_input = QLineEdit()
        # self.url_input.setText('https://cn.bing.com')
        # self.load_url()
        self.toolbar.addWidget(self.url_input)
        self.toolbar.addAction('go').triggered.connect(
            lambda b: self.load_url())
        self.toolbar.addAction('back').triggered.connect(self.webview.back)
        self.toolbar.addAction('forward').triggered.connect(
            self.webview.forward)
        self.layout().addWidget(self.toolbar)
        if toolbar == 'no':
            self.toolbar.hide()
        elif toolbar == 'no_url_input':
            self.url_input.hide()

        self.layout().addWidget(self.webview)
        self.setWindowTitle('My Browser')
        self.showMaximized()

        # command:>
        # jupyter notebook --port 5000 --no-browser --ip='*' --NotebookApp.token=''
        # --NotebookApp.password='' c:\users\12957\

        # self.webview.load(QUrl("http://127.0.0.1:5000/notebooks/desktop/Untitled.ipynb"))  # 直接请求页面。
        # self.webview.load(QUrl("E:\Python\pyminer_bin\PyMiner\bin\pmgwidgets\display\browser\show_formula.html"))  # 直接请求页面。
        # self.setCentralWidget(self.webview)

    def load_url(self, url: str = ''):
        if url == '':
            url = self.url_input.text().strip()
        # print('',url)
        else:
            self.url_input.setText(url)
        self.webview.load(QUrl(url))
Example #3
0
class ToolbarUI(object):
    def __init__(self, main_window: QMainWindow):
        """
        工具导航
        外观模式
        :param main_window:
        """
        self.main_window = main_window
        self.toolbar = QToolBar(self.main_window)

        # 子类
        self.ui_list = []
        self.server_start_ui = ServerStartUI(self.toolbar)  # 服务器开关
        self.file_manager_ui = FileManagerUI(self.toolbar)  # 文件管理
        self.terminal_ui = TerminalUI(self.toolbar)  # 远程终端
        self.remote_control_ui = RemoteControlUI(self.toolbar)  # 远程控制
        self.video_monitor_ui = VideoMonitorUI(self.toolbar)  # 视频监控
        self.voice_monitor_ui = VoiceMonitorUI(self.toolbar)  # 语音监控
        self.keyboard_ui = KeyboardUI(self.toolbar)  # 键盘记录
        self.make_client_ui = MakeClientUI(self.toolbar)  # 创建客户端
        self.service_manager_ui = ServiceManagerUI(self.toolbar)  # 服务管理
        self.exit_ui = ExitUI(self.toolbar, self.main_window)  # 退出程序

        # 子类信号
        self.connect_list = []
        self.server_start_connect = ServerStartConnect(self.server_start_ui)

    def options(self) -> None:
        """
        参数设置
        :return:
        """
        self.toolbar.setObjectName("toolBar")

        # 设置是否可以移动
        self.toolbar.setMovable(True)

        # 设置是否可以悬浮在主窗口
        self.toolbar.setFloatable(True)

        # 设置图标尺寸
        self.toolbar.setIconSize(QSize(25, 25))

        # 字体在右边
        # self.tools_main.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        # 字体在下面
        if settings.LOAD_EFFECT_ON:
            self.toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
            load_animation.load_animation(self.toolbar)

        # 窗口添加工具导航栏
        self.main_window.addToolBar(Qt.TopToolBarArea, self.toolbar)

    def setup_ui(self) -> None:
        self.options()

        if not settings.TOOLBAR_SHOW:
            self.toolbar.hide()

        self.load_ui()
        self.load_connect()
        self.show_ui()

        # QDockWidget 位置发生变动
        self.toolbar.orientationChanged.connect(self.orientation_changed)

    def orientation_changed(self, event) -> None:
        """
        位置变动事件
        :param event:
        :return:
        """
        if event:
            pass
        if settings.LOAD_EFFECT_ON:
            load_animation.load_animation(self.toolbar)

    # noinspection PyArgumentList
    def retranslate_ui(self) -> None:
        self.toolbar.setWindowTitle(_translate("ToolbarUI", "工具导航"))

    def load_ui(self) -> None:
        """
        加载模块
        :return:
        """
        self.ui_list.append(self.server_start_ui)
        self.ui_list.append(self.file_manager_ui)
        self.ui_list.append(self.terminal_ui)
        self.ui_list.append(self.remote_control_ui)
        self.ui_list.append(self.video_monitor_ui)
        self.ui_list.append(self.voice_monitor_ui)
        self.ui_list.append(self.keyboard_ui)
        self.ui_list.append(self.make_client_ui)
        self.ui_list.append(self.service_manager_ui)
        self.ui_list.append(self.exit_ui)

    def load_connect(self) -> None:
        """
        加载 链接信号
        :return:
        """
        self.connect_list.append(self.server_start_connect)

    def show_ui(self) -> None:
        """
        显示数据
        :return:
        """
        all_list = self.ui_list + self.connect_list
        for item in all_list:
            item.setup_ui()
            item.retranslate_ui()
Example #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()
###############################################################################

    __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()
Example #5
0
class SMLSurveyor(object):
    def __init__(self, iface):
        # save reference to the QGIS interface
        self.iface = iface
        # get plugin directory
        self.plugin_dir = os.path.dirname(os.path.realpath(__file__))
        self.uri = None
        self.connection = None
        self.crs = None
        self.database = None
        self.datetime = datetime.now()
        self.required_layers = []
        # 1. beacons
        # 2. parcels
        self.required_layers.append(
            RequiredLayer('Beacon', 'Beacons', 'beacons', 'gid', 'points'))
        self.required_layers.append(
            RequiredLayer('Parcel', 'Parcels', 'parcels', 'parcel_id',
                          'polygons'))

    def initGui(self):
        """ Initialize gui
        """
        # create plugin toolbar
        self.create_plugin_toolbar()

    def unload(self):
        """ Uninitialize gui
        """
        # remove plugin toolbar
        self.remove_plugin_toolbar()
        # remove layers
        self.refresh_layers()
        for l in self.required_layers:
            if bool(l.layer):
                QgsProject.instance().removeMapLayers([l.layer.id()])

    def create_plugin_toolbar(self):
        """ Create plugin toolbar to house buttons
        """
        # create plugin toolbar

        self.plugin_toolbar = QToolBar('Parcel Plugin')
        self.plugin_toolbar.setObjectName('Parcel Plugin')
        # create Database Selection button
        self.select_database_action = QAction(
            QIcon(os.path.join(self.plugin_dir, "images", "database.png")),
            "Select Database Connection", self.iface.mainWindow())
        self.select_database_action.setWhatsThis("Select database connection")
        self.select_database_action.setStatusTip("Select database connection")
        self.select_database_action.triggered.connect(
            self.manage_database_connection)
        # create Beardist button
        self.bearing_distance_action = QAction(
            QIcon(os.path.join(self.plugin_dir, "images", "beardist.png")),
            "Manage Bearings and Distances", self.iface.mainWindow())
        self.bearing_distance_action.setWhatsThis(
            "Manage bearings and distances")
        self.bearing_distance_action.setStatusTip(
            "Manage bearings and distances")
        self.bearing_distance_action.triggered.connect(
            self.manage_bearing_distance)
        # create Beacons button
        self.beacons_action = QAction(
            QIcon(os.path.join(self.plugin_dir, "images", "beacon.gif")),
            "Manage Beacons", self.iface.mainWindow())
        self.beacons_action.setWhatsThis("Manage beacons")
        self.beacons_action.setStatusTip("Manage beacons")
        self.beacons_action.triggered.connect(self.manage_beacons)
        # create Parcels button
        self.parcels_action = QAction(
            QIcon(os.path.join(self.plugin_dir, "images", "parcel.png")),
            "Manage Parcels", self.iface.mainWindow())
        self.parcels_action.setWhatsThis("Manage parcels")
        self.parcels_action.setStatusTip("Manage parcels")
        self.parcels_action.triggered.connect(self.manage_parcels)
        # populate plugin toolbar
        self.plugin_toolbar.addAction(self.select_database_action)
        self.plugin_toolbar.addAction(self.bearing_distance_action)
        self.plugin_toolbar.addAction(self.beacons_action)
        self.plugin_toolbar.addAction(self.parcels_action)
        # add plugin toolbar to gui
        self.iface.mainWindow().addToolBar(self.plugin_toolbar)

    def remove_plugin_toolbar(self):
        """ Remove plugin toolbar which houses buttons
        """
        # remove app toolbar from gui
        if hasattr(self, "pluginToolBar"):
            self.iface.mainWindow().removeToolBar(self.plugin_toolbar)
            self.plugin_toolbar.hide()

    def set_database_connection(self, connection=None, crs=None):
        """ Create a database connection
        """
        # fetch settings
        settings_plugin = QSettings()
        settings_postgis = QSettings()
        settings_plugin.beginGroup('CoGo Plugin')
        settings_postgis.beginGroup('PostgreSQL/connections')
        self.connection = connection
        if not bool(self.connection):
            # fetch pre-chosen database connection
            self.connection = settings_plugin.value("DatabaseConnection", None)
        # check if still exists
        if bool(self.connection):
            if self.connection not in settings_postgis.childGroups():
                settings_plugin.setValue("DatabaseConnection", "")
                self.connection = None
        # fetch from user if necessary
        if not bool(self.connection):
            dialog = DatabaseConnectionDialog()
            dialog.show()
            if bool(dialog.exec_()):
                self.connection = dialog.get_database_connection()
                if dialog.get_crs():
                    self.crs = QgsCoordinateReferenceSystem(
                        dialog.get_crs().get('auth_id'))
                settings_plugin.setValue("DatabaseConnection", self.connection)
        # validate database connection
        if bool(self.connection):
            db_service = settings_postgis.value(self.connection + '/service')
            db_host = settings_postgis.value(self.connection + '/host')
            db_port = settings_postgis.value(self.connection + '/port')
            db_name = settings_postgis.value(self.connection + '/database')
            db_username = settings_postgis.value(self.connection + '/username')
            db_password = settings_postgis.value(self.connection + '/password')

            max_attempts = 3
            self.uri = QgsDataSourceUri()
            self.uri.setConnection(db_host, db_port, db_name, db_username,
                                   db_password)

            if db_username and db_password:
                for i in range(max_attempts):
                    error_message = self.connect_to_db(db_service, db_host,
                                                       db_port, db_name,
                                                       db_username,
                                                       db_password)
                    if error_message:
                        ok, db_username, db_password = (
                            QgsCredentials.instance().get(
                                self.uri.connectionInfo(), db_username,
                                db_password, error_message))
                        if not ok:
                            break
                    else:
                        break

            else:
                msg = "Please enter the username and password."
                for i in range(max_attempts):
                    ok, db_username, db_password = (
                        QgsCredentials.instance().get(
                            self.uri.connectionInfo(), db_username,
                            db_password, msg))
                    if not ok:
                        break
                    error_message = self.connect_to_db(db_service, db_host,
                                                       db_port, db_name,
                                                       db_username,
                                                       db_password)
                    if not error_message:
                        break

        settings_plugin.endGroup()
        settings_postgis.endGroup()

    def connect_to_db(self, service, host, port, name, username, password):
        username.replace(" ", "")
        password.replace(" ", "")
        try:
            self.database = database.Manager({
                "CONNECTION": self.connection,
                "SERVICE": service,
                "HOST": host,
                "NAME": name,
                "PORT": port,
                "USER": username,
                "PASSWORD": password
            })
            self.uri.setConnection(host, port, name, username, password)
            self.datetime = datetime.now()
            return None
        except Exception as e:
            self.database = None
            msg = "Invalid username and password."
            return msg

    def refresh_layers(self):
        """ Ensure all required layers exist
        """
        if bool(self.database):

            # Comment out below section because connection name is better
            # to be a group name than crs name IMHO.

            # # first, we need to check the layer group for the crs used by
            # # current database
            # query = "SELECT Find_SRID('public', 'beacons', 'the_geom');"
            # self.database.connect(self.database.parameters)
            # cursor = self.database.cursor
            # cursor.execute(query)
            # crs_id = int(cursor.fetchall()[0][0])
            # del cursor
            # group_name = None
            # for key, value in crs_options.iteritems():
            #     if value == crs_id:
            #         group_name = key

            group_name = self.connection

            root = QgsProject.instance().layerTreeRoot()
            target_group = root.findGroup(group_name)
            if not bool(target_group):
                target_group = root.insertGroup(0, group_name)
            target_group.setItemVisibilityChecked(Qt.Checked)

            for required_layer in reversed(self.required_layers):
                for layer_node in target_group.findLayers():
                    layer = layer_node.layer()
                    if required_layer.name_plural.lower() == \
                            layer.name().lower():
                        target_group.removeLayer(layer)

            for required_layer in self.required_layers:
                self.uri.setDataSource(required_layer.schema,
                                       required_layer.table,
                                       required_layer.geometry_column, '',
                                       required_layer.primary_key)
                added_layer = QgsVectorLayer(self.uri.uri(),
                                             required_layer.name_plural,
                                             "postgres")
                QgsProject.instance().addMapLayer(added_layer, False)
                target_group.addLayer(added_layer)
                for layer_node in target_group.findLayers():
                    layer = layer_node.layer()
                    if required_layer.name_plural == layer.name():
                        required_layer.layer = layer
                        layer_node.setItemVisibilityChecked(Qt.Checked)
                        if self.crs:
                            layer.setCrs(self.crs)
                self.iface.zoomToActiveLayer()

    def manage_beacons(self):
        """ Portal which enables the management of beacons
        """
        if self.datetime.date() != datetime.now().date():
            self.database = None
        if self.database is None:
            self.set_database_connection()
            if self.database is None:
                return
        BeaconManager(self.iface, self.database, self.required_layers)
        validate_plugin_actions(self, self.database)
        self.iface.mapCanvas().refresh()

    def manage_parcels(self):
        """ Portal which enables the management of parcels
        """
        if self.datetime.date() != datetime.now().date():
            self.database = None
        if self.database is None:
            self.set_database_connection()
            if self.database is None:
                return
        ParcelManager(self.iface, self.database, self.required_layers)
        validate_plugin_actions(self, self.database)
        self.iface.mapCanvas().refresh()

    def manage_bearing_distance(self):
        """ Portal which enables the management of
        bearings and distances
        """
        if self.datetime.date() != datetime.now().date():
            self.database = None
        if self.database is None:
            self.set_database_connection()
            if self.database is None:
                return

        result = validate_plugin_actions(self, self.database)
        if not result:
            QMessageBox.warning(
                None, "SML Surveyor",
                ("No Beacons available in the table. "
                 "Please use Beacon Manager tool to create a Beacon."))
        else:
            BearDistManager(self.iface, self.database, self.required_layers)

        self.iface.mapCanvas().refresh()

    def manage_database_connection(self):
        """ Action to select the db connection to work with.
        """
        database_manager = DatabaseManager()
        connection = database_manager.get_current_connection()
        crs = database_manager.get_current_crs()
        if connection:
            self.set_database_connection(connection=connection, crs=crs)
            self.refresh_layers()
        if self.database:
            validate_plugin_actions(self, self.database)
Example #6
0
class MainWindow(QMainWindow):
    """This is the main application window class

    it defines the GUI window for the browser
    """
    def parse_config(self, file_config, options):
        self.config = {}
        options = vars(options)
        for key, metadata in CONFIG_OPTIONS.items():
            options_val = options.get(key)
            file_val = file_config.get(key)
            env_val = os.environ.get(metadata.get("env", ''))
            default_val = metadata.get("default")
            vals = metadata.get("values")
            debug("key: {}, default: {}, file: {}, options: {}".format(
                key, default_val, file_val, options_val))
            if vals:
                options_val = (options_val in vals and options_val) or None
                file_val = (file_val in vals and file_val) or None
                env_val = (env_val in vals and env_val) or None
            if metadata.get("is_file"):
                filename = options_val or env_val
                if not filename:
                    self.config[key] = default_val
                else:
                    try:
                        with open(filename, 'r') as fh:
                            self.config[key] = fh.read()
                    except IOError:
                        debug("Could not open file {} for reading.".format(
                            filename))
                        self.config[key] = default_val
            else:
                set_values = [
                    val for val in (options_val, env_val, file_val)
                    if val is not None
                ]
                if len(set_values) > 0:
                    self.config[key] = set_values[0]
                else:
                    self.config[key] = default_val
            if metadata.get("type") and self.config[key]:
                debug("{} cast to {}".format(key, metadata.get("type")))
                self.config[key] = metadata.get("type")(self.config[key])
        debug(repr(self.config))

    def createAction(self,
                     text,
                     slot=None,
                     shortcut=None,
                     icon=None,
                     tip=None,
                     checkable=False,
                     signal="triggered"):
        """Return a QAction given a number of common QAction attributes

        Just a shortcut function Originally borrowed from
        'Rapid GUI Development with PyQT' by Mark Summerset
        """
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(
                QIcon.fromTheme(icon, QIcon(":/{}.png".format(icon))))
        if shortcut is not None and not shortcut.isEmpty():
            action.setShortcut(shortcut)
            tip += " ({})".format(shortcut.toString())
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            action.__getattr__(signal).connect(slot)
        if checkable:
            action.setCheckable()
        return action

    def __init__(self, options, parent=None):
        """Construct a MainWindow Object."""
        super(MainWindow, self).__init__(parent)
        # Load config file
        self.setWindowTitle("Browser")
        debug("loading configuration from '{}'".format(options.config_file))
        configfile = {}
        if options.config_file:
            configfile = yaml.safe_load(open(options.config_file, 'r'))
        self.parse_config(configfile, options)
        # self.popup will hold a reference to the popup window
        # if it gets opened
        self.popup = None

        # Stylesheet support
        if self.config.get("stylesheet"):
            try:
                with open(self.config.get("stylesheet")) as ss:
                    self.setStyleSheet(ss.read())
            except:
                debug(('Problem loading stylesheet file "{}", '
                       'using default style.').format(
                           self.config.get("stylesheet")))
        self.setObjectName("global")

        # Set proxy server environment variable before creating web views
        if self.config.get("proxy_server"):
            os.environ["http_proxy"] = self.config.get("proxy_server")
            os.environ["https_proxy"] = self.config.get("proxy_server")

        # If the whitelist is activated, add the bookmarks and start_url
        if self.config.get("whitelist"):
            # we can just specify whitelist = True,
            # which should whitelist just the start_url and bookmark urls.
            if type(self.config.get("whitelist")) is not list:
                self.whitelist = []
            self.whitelist.append(
                str(QUrl(self.config.get("start_url")).host()))
            bookmarks = self.config.get("bookmarks")
            if bookmarks:
                self.whitelist += [
                    str(QUrl(b.get("url")).host())
                    for k, b in bookmarks.items()
                ]
                self.whitelist = set(self.whitelist)  # uniquify and optimize
            debug("Generated whitelist: " + str(self.whitelist))

        # create the web engine profile
        self.create_webprofile()

        # Now construct the UI
        self.build_ui()

    # ## END OF CONSTRUCTOR ## #

    def create_webprofile(self):
        """Create a webengineprofile to use in all views."""
        if self.config.get("privacy_mode"):
            webprofile = QWebEngineProfile()
        else:
            webprofile = QWebEngineProfile.defaultProfile()
        debug("Browser session is private: {}".format(
            webprofile.isOffTheRecord()))
        if self.config.get("user_agent"):
            webprofile.setHttpUserAgent(self.config["user_agent"])
            debug('Set user agent to "{}"'.format(webprofile.httpUserAgent()))
        self.webprofile = webprofile

    def build_ui(self):
        """Set up the user interface for the main window.

        Unlike the constructor, this method is re-run
        whenever the browser is "reset" by the user.
        """

        debug("build_ui")
        inactivity_timeout = self.config.get("timeout")
        quit_button_tooltip = ("Click here to quit"
                               if self.config.get('quit_button_mode')
                               == 'close' else "Click here when you are done"
                               "\nIt will clear your browsing history"
                               " and return you to the start page.")
        qb_mode_callbacks = {'close': self.close, 'reset': self.reset_browser}
        to_mode_callbacks = {
            'close': self.close,
            'reset': self.reset_browser,
            'screensaver': self.screensaver
        }
        self.screensaver_active = False

        # ##Start GUI configuration## #
        self.browser_window = AdmWebView(self.config,
                                         webprofile=self.webprofile)
        self.browser_window.setObjectName("web_content")
        self.setCentralWidget(self.browser_window)

        # Icon theme setting
        QIcon.setThemeName(self.config.get("icon_theme"))

        self.setCentralWidget(self.browser_window)
        debug("loading {}".format(self.config.get("start_url")))
        self.browser_window.setUrl(QUrl(self.config.get("start_url")))

        # Window size settings
        window_size = self.config.get("window_size", '').lower()
        if window_size == 'full':
            self.showFullScreen()
        elif window_size == 'max':
            self.showMaximized()
        elif window_size:
            size = re.match(r"(\d+)x(\d+)", window_size)
            if size:
                width, height = size.groups()
                self.setFixedSize(int(width), int(height))
            else:
                debug('Ignoring invalid window size "{}"'.format(window_size))

        # Set up the top navigation bar if it's configured to exist
        if self.config.get("navigation"):
            self.navigation_bar = QToolBar("Navigation")
            self.navigation_bar.setObjectName("navigation")
            self.addToolBar(Qt.TopToolBarArea, self.navigation_bar)
            self.navigation_bar.setMovable(False)
            self.navigation_bar.setFloatable(False)

            #  Standard navigation tools
            self.nav_items = {
                "back":
                self.browser_window.pageAction(AdmWebPage.Back),
                "forward":
                self.browser_window.pageAction(AdmWebPage.Forward),
                "refresh":
                self.browser_window.pageAction(AdmWebPage.Reload),
                "stop":
                self.browser_window.pageAction(AdmWebPage.Stop),
                "quit":
                self.createAction(
                    self.config.get("quit_button_text"),
                    qb_mode_callbacks.get(self.config.get("quit_button_mode"),
                                          self.reset_browser),
                    QKeySequence("Alt+F"), None, quit_button_tooltip),
                "zoom_in":
                self.createAction(
                    "Zoom In", self.zoom_in, QKeySequence("Alt++"), "zoom-in",
                    "Increase the size of the text and images on the page"),
                "zoom_out":
                self.createAction(
                    "Zoom Out", self.zoom_out, QKeySequence("Alt+-"),
                    "zoom-out",
                    "Decrease the size of text and images on the page")
            }
            if self.config.get("allow_printing"):
                self.nav_items["print"] = self.createAction(
                    "Print", self.browser_window.print_webpage,
                    QKeySequence("Ctrl+p"), "document-print",
                    "Print this page")

            # Add all the actions to the navigation bar.
            for item in self.config.get("navigation_layout"):
                if item == "separator":
                    self.navigation_bar.addSeparator()
                elif item == "spacer":
                    # an expanding spacer.
                    spacer = QWidget()
                    spacer.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Preferred)
                    self.navigation_bar.addWidget(spacer)
                elif item == "bookmarks":
                    # Insert bookmarks buttons here.
                    self.bookmark_buttons = []
                    for bookmark in self.config.get("bookmarks", {}).items():
                        debug("Bookmark:\n" + bookmark.__str__())
                        # bookmark name will use the "name" attribute,
                        # if present, or else just the key:
                        bookmark_name = bookmark[1].get("name") or bookmark[0]
                        # Create a button for the bookmark as a QAction,
                        # which we'll add to the toolbar
                        button = self.createAction(
                            bookmark_name, (lambda url=bookmark[1].get(
                                "url"): self.browser_window.load(QUrl(url))),
                            QKeySequence.mnemonic(bookmark_name), None,
                            bookmark[1].get("description"))
                        self.navigation_bar.addAction(button)
                        (self.navigation_bar.widgetForAction(
                            button).setObjectName("navigation_button"))
                else:
                    action = self.nav_items.get(item, None)
                    if action:
                        self.navigation_bar.addAction(action)
                        (self.navigation_bar.widgetForAction(
                            action).setObjectName("navigation_button"))

            # This removes the ability to toggle off the navigation bar:
            self.nav_toggle = self.navigation_bar.toggleViewAction()
            self.nav_toggle.setVisible(False)
            # End "if show_navigation is True" block

        # set hidden quit action
        # For reasons I haven't adequately ascertained,
        # this shortcut fails now and then claiming
        # "Ambiguous shortcut overload".
        # No idea why, as it isn't consistent.
        self.really_quit = self.createAction("", self.close,
                                             QKeySequence("Ctrl+Alt+Q"), None,
                                             "")
        self.addAction(self.really_quit)

        # Call a reset function after timeout
        if inactivity_timeout != 0:
            self.event_filter = InactivityFilter(inactivity_timeout)
            QCoreApplication.instance().installEventFilter(self.event_filter)
            self.browser_window.page().installEventFilter(self.event_filter)
            self.event_filter.timeout.connect(
                to_mode_callbacks.get(self.config.get("timeout_mode"),
                                      self.reset_browser))
        else:
            self.event_filter = None

        # ##END OF UI SETUP## #

    def screensaver(self):
        """Enter "screensaver" mode

        This method puts the browser in screensaver mode, where a URL
        is displayed while the browser is idle.  Activity causes the browser to
        return to the home screen.
        """
        debug("screensaver started")
        self.screensaver_active = True
        if self.popup:
            self.popup.close()
        if self.config.get("navigation"):
            self.navigation_bar.hide()
        self.browser_window.setZoomFactor(self.config.get("zoom_factor"))
        self.browser_window.load(QUrl(self.config.get("screensaver_url")))
        self.event_filter.timeout.disconnect()
        self.event_filter.activity.connect(self.reset_browser)

    def reset_browser(self):
        """Clear the history and reset the UI.

        Called whenever the inactivity filter times out,
        or when the user clicks the "finished" button in
        'reset' mode.
        """
        # Clear out the memory cache
        #QWebEngineSettings.clearMemoryCaches()
        self.browser_window.history().clear()
        # self.navigation_bar.clear() doesn't do its job,
        # so remove the toolbar first, then rebuild the UI.
        debug("RESET BROWSER")
        if self.event_filter:
            self.event_filter.blockSignals(True)
        if self.screensaver_active is True:
            self.screensaver_active = False
            self.event_filter.activity.disconnect()
        if self.event_filter:
            self.event_filter.blockSignals(False)
        if hasattr(self, "navigation_bar"):
            self.removeToolBar(self.navigation_bar)
        self.build_ui()

    def zoom_in(self):
        """Zoom in action callback.

        Note that we cap zooming in at a factor of 3x.
        """
        if self.browser_window.zoomFactor() < 3.0:
            self.browser_window.setZoomFactor(
                self.browser_window.zoomFactor() + 0.1)
            self.nav_items["zoom_out"].setEnabled(True)
        else:
            self.nav_items["zoom_in"].setEnabled(False)

    def zoom_out(self):
        """Zoom out action callback.

        Note that we cap zooming out at 0.1x.
        """
        if self.browser_window.zoomFactor() > 0.1:
            self.browser_window.setZoomFactor(
                self.browser_window.zoomFactor() - 0.1)
            self.nav_items["zoom_in"].setEnabled(True)
        else:
            self.nav_items["zoom_out"].setEnabled(False)
Example #7
0
class MainWindow(QtWidgets.QMainWindow):
    MaxRecentFiles = 5

    def __init__(self, app_title="CSV Viewer"):
        super().__init__()
        self.progress = QProgressBar()
        self.threadpool = QThreadPool()
        self.app_title = app_title
        self.setMinimumSize(600, 300)
        self.df = None
        self.round_num = 2
        self.recentFileActs = []

        # settings
        self.settings = QtCore.QSettings('CSV_Viewer', 'CSV_Viewer')
        self.round_num = self.settings.value('round_numbers', self.round_num, int)

        # toolbar
        self.toolbar = QToolBar("MainToolbar")
        self.toolbar.setIconSize(QSize(16, 16))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.addToolBar(self.toolbar)

        # open action
        style = self.toolbar.style()
        icon = style.standardIcon(QStyle.SP_FileDialogStart)
        self.button_open = QAction(icon, "Open", self)
        self.button_open.setShortcut('Ctrl+O')
        self.button_open.setStatusTip("Open CSV file...")
        self.button_open.triggered.connect(self.onToolbarOpenButtonClick)
        self.toolbar.addAction(self.button_open)
        self.button_open.setEnabled(True)

        # summary action
        style_summary = self.toolbar.style()
        icon = style_summary.standardIcon(QStyle.SP_FileDialogListView)
        self.button_summary = QAction(icon, "Summary", self)
        self.button_summary.setShortcut('Ctrl+S')
        self.button_summary.setStatusTip("Show summary for the current file")
        self.button_summary.triggered.connect(self.onToolbarSummaryButtonClick)
        self.toolbar.addAction(self.button_summary)

        # info action
        style_info = self.toolbar.style()
        icon = style_info.standardIcon(QStyle.SP_FileIcon)
        self.button_info = QAction(icon, "Info", self)
        self.button_info.setShortcut('Ctrl+I')
        self.button_info.setStatusTip("Show summary for the current file")
        self.button_info.triggered.connect(self.onToolbarInfoButtonClick)
        self.toolbar.addAction(self.button_info)

        # resize action
        style_resize = self.toolbar.style()
        icon = style_resize.standardIcon(QStyle.SP_BrowserReload)
        self.button_resize = QAction(icon, "Resize columns", self)
        self.button_resize.setShortcut('Ctrl+R')
        self.button_resize.setStatusTip("Resize columns width to content")
        self.button_resize.triggered.connect(self.onResizeColumns)
        self.toolbar.addAction(self.button_resize)

        # export to xlsx action
        style_xlsx = self.toolbar.style()
        icon = style_xlsx.standardIcon(QStyle.SP_FileLinkIcon)
        self.button_xlsx = QAction(icon, "Xlsx", self)
        self.button_xlsx.setStatusTip("Export data to xlsx file")
        self.button_xlsx.triggered.connect(self.onExportXlsx)
        self.toolbar.addAction(self.button_xlsx)

        # export to sqlite action
        style_sqlite = self.toolbar.style()
        icon = style_sqlite.standardIcon(QStyle.SP_FileLinkIcon)
        self.button_sqlite = QAction(icon, "SQLite", self)
        self.button_sqlite.setStatusTip("Export data to SQLite database")
        self.button_sqlite.triggered.connect(self.onExportSQLite)
        self.toolbar.addAction(self.button_sqlite)

        # export to html action
        style_html = self.toolbar.style()
        icon = style_html.standardIcon(QStyle.SP_FileLinkIcon)
        self.button_html = QAction(icon, "HTML", self)
        self.button_html.setStatusTip("Export data to HTML file")
        self.button_html.triggered.connect(self.onExportHTML)
        self.toolbar.addAction(self.button_html)

        # export to CSV action
        style_csv = self.toolbar.style()
        icon = style_csv.standardIcon(QStyle.SP_FileLinkIcon)
        self.button_csv = QAction(icon, "CSV", self)
        self.button_csv.setStatusTip("Export data to CSV file")
        self.button_csv.triggered.connect(self.onExportCSV)
        self.toolbar.addAction(self.button_csv)

        # export to Markdown action
        style_mark = self.toolbar.style()
        icon = style_mark.standardIcon(QStyle.SP_FileLinkIcon)
        self.button_mark = QAction(icon, "Markdown", self)
        self.button_mark.setStatusTip("Export data to Markdown file")
        self.button_mark.triggered.connect(self.onExportMarkdown)
        self.toolbar.addAction(self.button_mark)

        # import data from world bank climate api
        style_api = self.toolbar.style()
        icon = style_api.standardIcon(QStyle.SP_DialogSaveButton)
        self.button_api = QAction(icon, "API", self)
        self.button_api.setStatusTip("Import data from World Bank Climate API")
        self.button_api.triggered.connect(self.onImportFromAPI)
        self.toolbar.addAction(self.button_api)
        self.button_api.setEnabled(True)

        # close action
        style_close = self.toolbar.style()
        icon = style_close.standardIcon(QStyle.SP_DialogCloseButton)
        self.button_close = QAction(icon, "Close", self)
        self.button_close.setShortcut('Ctrl+X')
        self.button_close.setStatusTip("Close CSV file...")
        self.button_close.triggered.connect(self.onToolbarCloseButtonClick)
        self.toolbar.addAction(self.button_close)

        # quit action
        self.button_quit = QAction("Quit", self)
        self.button_quit.setShortcut('Ctrl+Q')
        self.button_quit.setStatusTip("Quit application")
        self.button_quit.triggered.connect(self.close)

        # toolbar show/hide action
        self.button_tool = QAction("Show/Hide toolbar", self)
        self.button_tool.setShortcut('Ctrl+T')
        self.button_tool.setStatusTip("Show or hide toolbar")
        self.button_tool.triggered.connect(self.showToolbar)

        # remove NaN
        self.button_nan = QAction("Remove NaN", self)
        self.button_nan.setShortcut('Ctrl+R')
        self.button_nan.setStatusTip("Remove rows with missing values")
        self.button_nan.triggered.connect(self.onRemoveNaN)

        # settings action
        style_settings = self.toolbar.style()
        icon = style_settings.standardIcon(QStyle.SP_ComputerIcon)
        self.button_settings = QAction(icon, "Settings", self)
        self.button_settings.setStatusTip("Application settings")
        self.button_settings.triggered.connect(self.onSettings)
        self.toolbar.addAction(self.button_settings)
        self.button_settings.setEnabled(True)

        # about action
        style_about = self.toolbar.style()
        icon = style_about.standardIcon(QStyle.SP_FileDialogInfoView)
        self.button_about = QAction(icon, "About", self)
        self.button_about.setStatusTip("About application")
        self.button_about.triggered.connect(self.about)
        self.toolbar.addAction(self.button_about)
        self.button_about.setEnabled(True)

        self.setButtons(False)

        # recent menu action
        for i in range(MainWindow.MaxRecentFiles):
            self.recentFileActs.append(
                QAction(self, visible=False, triggered=self.openRecentFile)
            )

        # menu bar
        menu = self.menuBar()
        file_menu = menu.addMenu("&File")
        file_menu.addAction(self.button_open)
        file_menu.addAction(self.button_close)
        file_menu.addSeparator()
        file_menu.addAction(self.button_nan)
        file_menu.addSeparator()
        file_menu.addAction(self.button_settings)
        self.separatorAct = file_menu.addSeparator()

        for i in range(MainWindow.MaxRecentFiles):
            file_menu.addAction(self.recentFileActs[i])
        file_menu.addSeparator()

        file_menu.addAction(self.button_quit)

        view_menu = menu.addMenu("Vie&w")
        view_menu.addAction(self.button_summary)
        view_menu.addAction(self.button_info)
        view_menu.addSeparator()
        view_menu.addAction(self.button_resize)
        view_menu.addSeparator()
        view_menu.addAction(self.button_tool)

        export_menu = menu.addMenu("&Export")
        export_menu.addAction(self.button_xlsx)
        export_menu.addAction(self.button_sqlite)
        export_menu.addAction(self.button_html)
        export_menu.addAction(self.button_csv)
        export_menu.addAction(self.button_mark)

        import_menu = menu.addMenu("&Import")
        import_menu.addAction(self.button_api)

        help_menu = menu.addMenu("&Help")
        help_menu.addAction(self.button_about)

        self.updateRecentFileActions()

        # status bar
        self.my_status = QStatusBar(self)
        self.my_status.addPermanentWidget(self.progress)
        self.progress.hide()
        self.labelStatus = QLabel("Rows: 0 Cols: 0")
        self.my_status.addPermanentWidget(self.labelStatus)
        self.setStatusBar(self.my_status)

        # set TableView
        self.table = QtWidgets.QTableView()
        self.table.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
        self.table.setSelectionMode(QtWidgets.QTableView.SingleSelection)

        self.setCentralWidget(self.table)
        self.setWindowTitle(self.app_title)
        self.setMinimumSize(400, 250)
        self.setGeometry(200, 100, 1000, 600)

    def onToolbarOpenButtonClick(self) -> None:
        """ Show open dialog """

        dlg = ParameterDialog()
        dlg.setWindowTitle("Open")
        if dlg.exec_():
            file_name = dlg.filename.text()
            separator = dlg.separator
            decimal = dlg.decimal
            header = dlg.header
            index = dlg.index
        else:
            file_name = None

        if file_name:
            self.saveRecent(file_name)
            self.open_csv_file(file_name, separator, decimal, header, index)

    def onOpenRecentFile(self, file_name: str, sep=',', decimal='.') -> None:
        """ Open file from recent list, show open dialog """

        dlg = ParameterDialog(file_name, sep, decimal)
        dlg.setWindowTitle("Open")
        if dlg.exec_():
            file_name = dlg.filename.text()
            separator = dlg.separator
            decimal = dlg.decimal
            header = dlg.header
            index = dlg.index
        else:
            file_name = None

        if file_name:
            self.saveRecent(file_name)
            self.open_csv_file(file_name, separator, decimal, header, index)

    def open_csv_file(self, file_name: str, sep=',', decimal=".", header=True, index=True) -> None:
        """ Open csv file, load to tableview, set statusbar, enable close icon"""
        try:
            if header:
                my_header = 'infer'
            else:
                my_header = None

            if index:
                my_index = 0
            else:
                my_index = False

            print(my_header, my_index)
            data = pd.read_csv(file_name, sep=sep, decimal=decimal, header=my_header, index_col=my_index)
            self.df = data
            self.model = TableModel(self.df, self.round_num)
            self.labelStatus.setText(f"Rows: {self.df.shape[0]} Cols: {self.df.shape[1]}")
            self.table.setModel(self.model)
            if data.shape[0] > 0:
                self.table.selectRow(0)

            self.setButtons(True)
            self.setWindowTitle(self.app_title + ": " + file_name)
        except Exception as e:
            QMessageBox.warning(self, 'Error', f"Error loading the file:\n {file_name}")

    def setButtons(self, state: bool) -> None:
        """ Set state of buttons/actions """
        self.button_close.setEnabled(state)
        self.button_summary.setEnabled(state)
        self.button_info.setEnabled(state)
        self.button_resize.setEnabled(state)
        self.button_xlsx.setEnabled(state)
        self.button_sqlite.setEnabled(state)
        self.button_html.setEnabled(state)
        self.button_csv.setEnabled(state)
        self.button_nan.setEnabled(state)
        self.button_mark.setEnabled(state)

    def onResizeColumns(self) -> None:
        """Resize columns action, run from menu View->Resize columns"""
        self.table.resizeColumnsToContents()

    def onToolbarCloseButtonClick(self) -> None:
        """Clear tableview, set statusbar and disable toolbar close, summary and info icons"""
        self.table.setModel(None)
        self.df = None
        self.setButtons(False)
        self.setWindowTitle(self.app_title)
        self.labelStatus.setText("Rows: 0 Cols: 0")

    def onToolbarSummaryButtonClick(self) -> None:
        """Show Summary dialog"""
        dlg = SummaryDialog(self.df.describe())
        dlg.setWindowTitle("Summary")
        dlg.exec_()

    def onToolbarInfoButtonClick(self) -> None:
        """Show Info dialog"""
        # buf = io.StringIO()
        # self.df.info(buf=buf)
        # tmp = buf.getvalue()

        dlg = InfoDialog(self.df)
        dlg.setWindowTitle("Info")
        dlg.exec_()

    def onExportXlsx(self) -> None:
        """ Export data to xlsx file """
        file_name, _ = QFileDialog.getSaveFileName(self, 'Export to xlsx', '', ".xlsx(*.xlsx)")
        if file_name:
            self.df.to_excel(file_name, engine='xlsxwriter')

    def onExportSQLite(self) -> None:
        file_name, _ = QFileDialog.getSaveFileName(self, 'Export to sqlite db', '', ".sqlite(*.sqlite)")
        if file_name:
            engine = create_engine(f'sqlite:///{file_name}', echo=False)
            self.df.to_sql('csv_data', con=engine)

    def onExportHTML(self) -> None:
        """ Export data to HTML file """
        file_name, _ = QFileDialog.getSaveFileName(self, 'Export to HTML file', '', ".html(*.html)")
        if file_name:
            self.df.to_html(file_name)

    def onExportCSV(self) -> None:
        """ Export data to new CSV file """
        file_name, _ = QFileDialog.getSaveFileName(self, 'Export to CSV file', '', ".csv(*.csv)")
        if file_name:
            self.df.to_csv(file_name, sep=',', decimal='.')

    def onImportFromAPI(self) -> None:
        dlg = ApiDialog()
        dlg.setWindowTitle("Import a Data CSV via API")
        if dlg.exec_():
            file_name = dlg.filename.text()
            address = dlg.address.text()
        else:
            file_name = None

        if file_name and address:
            res, text = dataload.import_data_by_api(address)
            if res:
                with open(file_name, "w") as f:
                    f.write(text)
                self.onOpenRecentFile(file_name)
            else:
                QMessageBox.warning(self, "Error", text)

    def closeEvent(self, event) -> None:
        """ Quit application, ask user before """
        if not app_test:
            result = QMessageBox.question(
                self, self.app_title,
                "Are you sure you want to quit?",
                QMessageBox.Yes | QMessageBox.No,
            )

        if app_test or result == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

    def about(self) -> None:
        """ Show About dialog (info about application)"""
        dlg = AboutDialog()
        dlg.exec_()

    def openRecentFile(self) -> None:
        """ Open file from recent list action """
        action = self.sender()
        if action:
            self.onOpenRecentFile(action.data())

    def saveRecent(self, file_name) -> None:
        """ Save information about currently opened file, update recent list"""
        settings = QSettings('CSV_Viewer', 'CSV_Viewer')
        files = settings.value('recentFileList', [])

        try:
            files.remove(file_name)
        except ValueError:
            pass

        files.insert(0, file_name)
        del files[MainWindow.MaxRecentFiles:]

        settings.setValue('recentFileList', files)

        for widget in QtWidgets.QApplication.topLevelWidgets():
            if isinstance(widget, MainWindow):
                widget.updateRecentFileActions()

    def updateRecentFileActions(self) -> None:
        """ Update recent file list """
        settings = QSettings('CSV_Viewer', 'CSV_Viewer')
        files = settings.value('recentFileList', [])

        numRecentFiles = min(len(files), MainWindow.MaxRecentFiles)

        for i in range(numRecentFiles):
            text = "&%d %s" % (i + 1, self.strippedName(files[i]))
            self.recentFileActs[i].setText(text)
            self.recentFileActs[i].setData(files[i])
            self.recentFileActs[i].setVisible(True)

        for j in range(numRecentFiles, MainWindow.MaxRecentFiles):
            self.recentFileActs[j].setVisible(False)

        self.separatorAct.setVisible((numRecentFiles > 0))

    def strippedName(self, fullFileName: str) -> str:
        """ Return only file name, without path"""
        return QFileInfo(fullFileName).fileName()

    def showToolbar(self):
        """ show / hide main toolbar"""
        if self.toolbar.isHidden():
            self.toolbar.show()
        else:
            self.toolbar.hide()

    def onSettings(self):
        """ Round numbers to """
        n, result = QInputDialog.getInt(self, "Settings", "Round numbers to:", self.round_num, 0, 10, 1)
        if result and n != self.round_num:
            self.round_num = n
            self.settings.setValue('round_numbers', self.round_num)
            if self.df.shape[0] > 0:
                index = self.table.currentIndex()
                self.model = TableModel(self.df, self.round_num)
                self.table.setModel(self.model)
                self.table.selectRow(index.row())

    def onRemoveNaN(self):
        """ Remove rows with missing values """
        if self.df.shape[0] > 0:
            button = QMessageBox.question(self, "Remove NaN", "Remove rows with missing values?")
            if button == QMessageBox.Yes:
                self.df.dropna(axis=0, how='any', inplace=True)
                self.model = TableModel(self.df, self.round_num)
                self.table.setModel(self.model)
                self.table.selectRow(0)
                self.labelStatus.setText(f"Rows: {self.df.shape[0]} Cols: {self.df.shape[1]}")

    def onExportMarkdown(self):
        """ export to markdown table in thread """
        file_name, _ = QFileDialog.getSaveFileName(self, 'Export to Markdown file', '', ".md(*.md)")
        if file_name:
            worker = MarkdownWorker(file_name, self.df)
            worker.signals.progress.connect(self.update_progress)
            worker.signals.status.connect(self.update_status)
            self.threadpool.start(worker)

    def update_progress(self, progress):
        self.progress.setValue(progress)

    def update_status(self, status):
        if status:
            self.progress.show()
        else:
            self.progress.hide()
Example #8
0
class MainWindow(QMainWindow):

    """This is the main application window class

    it defines the GUI window for the browser
    """

    def parse_config(self, file_config, options):
        self.config = {}
        options = vars(options)
        for key, metadata in CONFIG_OPTIONS.items():
            options_val = options.get(key)
            file_val = file_config.get(key)
            env_val = os.environ.get(metadata.get("env", ''))
            default_val = metadata.get("default")
            vals = metadata.get("values")
            debug("key: {}, default: {}, file: {}, options: {}".format(
                key, default_val, file_val, options_val
            ))
            if vals:
                options_val = (options_val in vals and options_val) or None
                file_val = (file_val in vals and file_val) or None
                env_val = (env_val in vals and env_val) or None
            if metadata.get("is_file"):
                filename = options_val or env_val
                if not filename:
                    self.config[key] = default_val
                else:
                    try:
                        with open(filename, 'r') as fh:
                            self.config[key] = fh.read()
                    except IOError:
                        debug("Could not open file {} for reading.".format(
                            filename)
                        )
                        self.config[key] = default_val
            else:
                set_values = [
                    val for val in (options_val, env_val, file_val)
                    if val is not None
                ]
                if len(set_values) > 0:
                    self.config[key] = set_values[0]
                else:
                    self.config[key] = default_val
            if metadata.get("type") and self.config[key]:
                debug("{} cast to {}".format(key, metadata.get("type")))
                self.config[key] = metadata.get("type")(self.config[key])
        debug(repr(self.config))

    def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None,
                     checkable=False, signal="triggered"):
        """Return a QAction given a number of common QAction attributes

        Just a shortcut function Originally borrowed from
        'Rapid GUI Development with PyQT' by Mark Summerset
        """
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon.fromTheme(
                icon, QIcon(":/{}.png".format(icon))
            ))
        if shortcut is not None and not shortcut.isEmpty():
            action.setShortcut(shortcut)
            tip += " ({})".format(shortcut.toString())
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            action.__getattr__(signal).connect(slot)
        if checkable:
            action.setCheckable()
        return action

    def __init__(self, options, parent=None):
        """Construct a MainWindow Object."""
        super(MainWindow, self).__init__(parent)
        # Load config file
        self.setWindowTitle("Browser")
        debug("loading configuration from '{}'".format(options.config_file))
        configfile = {}
        if options.config_file:
            configfile = yaml.safe_load(open(options.config_file, 'r'))
        self.parse_config(configfile, options)
        # self.popup will hold a reference to the popup window
        # if it gets opened
        self.popup = None

        # Stylesheet support
        if self.config.get("stylesheet"):
            try:
                with open(self.config.get("stylesheet")) as ss:
                    self.setStyleSheet(ss.read())
            except:
                debug(
                    """Problem loading stylesheet file "{}", """
                    """using default style."""
                    .format(self.config.get("stylesheet"))
                )
        self.setObjectName("global")

        # If the whitelist is activated, add the bookmarks and start_url
        if self.config.get("whitelist"):
            # we can just specify whitelist = True,
            # which should whitelist just the start_url and bookmark urls.
            whitelist = self.config.get("whitelist")
            if type(whitelist) is not list:
                whitelist = []
            whitelist.append(str(QUrl(
                self.config.get("start_url")
            ).host()))
            bookmarks = self.config.get("bookmarks")
            if bookmarks:
                whitelist += [
                    str(QUrl(b.get("url")).host())
                    for k, b in bookmarks.items()
                ]
                self.config["whitelist"] = set(whitelist)  # uniquify and optimize
            debug("Generated whitelist: " + str(whitelist))

        # If diagnostic is enabled, connect CTRL+ALT+? to show some diagnistic info
        if (self.config.get("enable_diagnostic")):
            self.diagnostic_action = self.createAction(
                "Show Diagnostic",
                self.show_diagnostic,
                QKeySequence("Ctrl+Alt+/"),
                tip=''
            )
            self.addAction(self.diagnostic_action)

        self.build_ui()

    # ## END OF CONSTRUCTOR ## #

    def build_ui(self):
        """Set up the user interface for the main window.

        Unlike the constructor, this method is re-run
        whenever the browser is "reset" by the user.
        """

        debug("build_ui")
        inactivity_timeout = self.config.get("timeout")
        quit_button_tooltip = (
            self.config.get("quit_button_mode") == 'close'
            and "Click here to quit the browser."
            or """Click here when you are done.
            It will clear your browsing history"""
            """ and return you to the start page.""")
        qb_mode_callbacks = {'close': self.close, 'reset': self.reset_browser}
        to_mode_callbacks = {'close': self.close,
                             'reset': self.reset_browser,
                             'screensaver': self.screensaver}
        self.screensaver_active = False

        # ##Start GUI configuration## #
        self.browser_window = WcgWebView(self.config)
        self.browser_window.setObjectName("web_content")

        if (
            self.config.get("icon_theme") is not None
            and QT_VERSION_STR > '4.6'
        ):
            QIcon.setThemeName(self.config.get("icon_theme"))
        self.setCentralWidget(self.browser_window)
        debug("loading {}".format(self.config.get("start_url")))
        self.browser_window.setUrl(QUrl(self.config.get("start_url")))
        if self.config.get("fullscreen"):
            self.showFullScreen()
        elif (
            self.config.get("window_size") and
            self.config.get("window_size").lower() == 'max'
        ):
            self.showMaximized()
        elif self.config.get("window_size"):
            size = re.match(r"(\d+)x(\d+)", self.config.get("window_size"))
            if size:
                width, height = size.groups()
                self.setFixedSize(int(width), int(height))
            else:
                debug('Ignoring invalid window size "{}"'.format(
                    self.config.get("window_size")
                ))

        # Set up the top navigation bar if it's configured to exist
        if self.config.get("navigation"):
            self.navigation_bar = QToolBar("Navigation")
            self.navigation_bar.setObjectName("navigation")
            self.addToolBar(Qt.TopToolBarArea, self.navigation_bar)
            self.navigation_bar.setMovable(False)
            self.navigation_bar.setFloatable(False)

            #  Standard navigation tools
            self.nav_items = {}
            self.nav_items["back"] = self.browser_window.pageAction(QWebPage.Back)
            self.nav_items["forward"] = self.browser_window.pageAction(QWebPage.Forward)
            self.nav_items["refresh"] = self.browser_window.pageAction(QWebPage.Reload)
            self.nav_items["stop"] = self.browser_window.pageAction(QWebPage.Stop)
            # The "I'm finished" button.
            self.nav_items["quit"] = self.createAction(
                self.config.get("quit_button_text"),
                qb_mode_callbacks.get(self.config.get("quit_button_mode"), self.reset_browser),
                QKeySequence("Alt+F"),
                None,
                quit_button_tooltip)
            # Zoom buttons
            self.nav_items["zoom_in"] = self.createAction(
                "Zoom In",
                self.zoom_in,
                QKeySequence("Alt++"),
                "zoom-in",
                "Increase the size of the text and images on the page")
            self.nav_items["zoom_out"] = self.createAction(
                "Zoom Out",
                self.zoom_out,
                QKeySequence("Alt+-"),
                "zoom-out",
                "Decrease the size of text and images on the page")
            if self.config.get("allow_printing"):
                self.nav_items["print"] = self.createAction(
                    "Print",
                    self.browser_window.print_webpage,
                    QKeySequence("Ctrl+p"),
                    "document-print",
                    "Print this page")

            # Add all the actions to the navigation bar.
            for item in self.config.get("navigation_layout"):
                if item == "separator":
                    self.navigation_bar.addSeparator()
                elif item == "spacer":
                    # an expanding spacer.
                    spacer = QWidget()
                    spacer.setSizePolicy(
                        QSizePolicy.Expanding, QSizePolicy.Preferred)
                    self.navigation_bar.addWidget(spacer)
                elif item == "bookmarks":
                    # Insert bookmarks buttons here.
                    self.bookmark_buttons = []
                    for bookmark in self.config.get("bookmarks", {}).items():
                        debug("Bookmark:\n" + bookmark.__str__())
                        # bookmark name will use the "name" attribute, if present
                        # or else just the key:
                        bookmark_name = bookmark[1].get("name") or bookmark[0]
                        # Create a button for the bookmark as a QAction,
                        # which we'll add to the toolbar
                        button = self.createAction(
                            bookmark_name,
                            lambda url=bookmark[1].get("url"): self.browser_window.load(QUrl(url)),
                            QKeySequence.mnemonic(bookmark_name),
                            None,
                            bookmark[1].get("description")
                            )
                        self.navigation_bar.addAction(button)
                        self.navigation_bar.widgetForAction(button).setObjectName("navigation_button")
                else:
                    action = self.nav_items.get(item, None)
                    if action:
                        self.navigation_bar.addAction(action)
                        self.navigation_bar.widgetForAction(action).setObjectName("navigation_button")

            # This removes the ability to toggle off the navigation bar:
            self.nav_toggle = self.navigation_bar.toggleViewAction()
            self.nav_toggle.setVisible(False)
            # End "if show_navigation is True" block

        # set hidden quit action
        # For reasons I haven't adequately ascertained,
        # this shortcut fails now and then claiming
        # "Ambiguous shortcut overload".
        # No idea why, as it isn't consistent.
        self.really_quit = self.createAction(
            "", self.close, QKeySequence("Ctrl+Alt+Q"), None, ""
        )
        self.addAction(self.really_quit)

        # Call a reset function after timeout
        if inactivity_timeout != 0:
            self.event_filter = InactivityFilter(inactivity_timeout)
            QCoreApplication.instance().installEventFilter(self.event_filter)
            self.browser_window.page().installEventFilter(self.event_filter)
            self.event_filter.timeout.connect(
                to_mode_callbacks.get(self.config.get("timeout_mode"),
                                      self.reset_browser))
        else:
            self.event_filter = None

        # ##END OF UI SETUP## #

    def screensaver(self):
        """Enter "screensaver" mode

        This method puts the browser in screensaver mode, where a URL
        is displayed while the browser is idle.  Activity causes the browser to
        return to the home screen.
        """
        debug("screensaver started")
        self.screensaver_active = True
        if self.popup:
            self.popup.close()
        if self.config.get("navigation"):
            self.navigation_bar.hide()
        self.browser_window.setZoomFactor(self.config.get("zoom_factor"))
        self.browser_window.load(QUrl(self.config.get("screensaver_url")))
        self.event_filter.timeout.disconnect()
        self.event_filter.activity.connect(self.reset_browser)

    def reset_browser(self):
        """Clear the history and reset the UI.

        Called whenever the inactivity filter times out,
        or when the user clicks the "finished" button in
        'reset' mode.
        """
        # Clear out the memory cache
        QWebSettings.clearMemoryCaches()
        self.browser_window.history().clear()
        # self.navigation_bar.clear() doesn't do its job,
        # so remove the toolbar first, then rebuild the UI.
        debug("RESET BROWSER")
        if self.event_filter:
            self.event_filter.blockSignals(True)
        if self.screensaver_active is True:
            self.screensaver_active = False
            self.event_filter.activity.disconnect()
        if self.event_filter:
            self.event_filter.blockSignals(False)
        if hasattr(self, "navigation_bar"):
            self.removeToolBar(self.navigation_bar)
        self.build_ui()

    def zoom_in(self):
        """Zoom in action callback.

        Note that we cap zooming in at a factor of 3x.
        """
        if self.browser_window.zoomFactor() < 3.0:
            self.browser_window.setZoomFactor(
                self.browser_window.zoomFactor() + 0.1
            )
            self.nav_items["zoom_out"].setEnabled(True)
        else:
            self.nav_items["zoom_in"].setEnabled(False)

    def zoom_out(self):
        """Zoom out action callback.

        Note that we cap zooming out at 0.1x.
        """
        if self.browser_window.zoomFactor() > 0.1:
            self.browser_window.setZoomFactor(
                self.browser_window.zoomFactor() - 0.1
            )
            self.nav_items["zoom_in"].setEnabled(True)
        else:
            self.nav_items["zoom_out"].setEnabled(False)

    def show_diagnostic(self):
        "Display a dialog box with some diagnostic info"
        data = {
            "OS": os.uname(),
            "USER": (os.environ.get("USER")
                     or os.environ.get("USERNAME")),
            "Python": sys.version,
            "Qt": QT_VERSION_STR,
            "Script Date": (
                datetime.datetime.fromtimestamp(
                    os.stat(__file__).st_mtime).isoformat()
            )
        }
        html = "\n".join([
            "<h1>System Information</h1>",
            "<h2>Please click &quot;",
            self.config.get("quit_button_text").replace("&", ''),
            "&quot; when you are finished.</h2>",
            "<ul>",
            "\n".join([
                "<li><b>{}</b>: {}</li>".format(k, v)
                for k, v in data.items()
            ]),
            "</ul>"
        ])
        self.browser_window.setHtml(html)
Example #9
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()
Example #10
0
class FileViewWindow(QMainWindow):

    def __init__(self, controller: 'Controller') -> None:
        super().__init__()

        self._message_area: Optional[MessageArea] = None

        self.controller = controller
        self.actions = self.controller.actions

        self.make_window()
        self.make_menubar()
        self.make_toolbar()
        self.addToolBarBreak()
        self.make_location_toolbar()
        self.make_filter_toolbar()
        self.make_search_toolbar()
        self.make_shortcut()

        self.file_view.setFocus()

        self.resize(1024, 768)
        self.move(QCursor.pos().x() - self.width() / 2,
                  QCursor.pos().y() - self.height() / 2)

        self.addAction(self.actions.rename)

    def closeEvent(self, ev):
        self.controller.on_exit()

    def make_shortcut(self):
        shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_L), self)
        shortcut.setContext(Qt.WindowShortcut)
        shortcut.activated.connect(self.controller.show_location_toolbar)

        def show_filter():
            self.file_filter.setFocus(Qt.ShortcutFocusReason)
            self.filter_toolbar.show()

        shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_K), self)
        shortcut.setContext(Qt.WindowShortcut)
        shortcut.activated.connect(show_filter)

        shortcut = QShortcut(QKeySequence(Qt.Key_F3), self)
        shortcut.setContext(Qt.WindowShortcut)
        shortcut.activated.connect(self.controller.show_search)

        shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_F), self)
        shortcut.setContext(Qt.WindowShortcut)
        shortcut.activated.connect(self.controller.show_search)

        shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_W), self)
        shortcut.setContext(Qt.WindowShortcut)
        shortcut.activated.connect(self.controller.close_window)

        shortcut = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Up), self)
        shortcut.setContext(Qt.WindowShortcut)
        shortcut.activated.connect(self.controller.parent_directory)

        shortcut = QShortcut(Qt.Key_Home, self)
        shortcut.setContext(Qt.WindowShortcut)
        shortcut.activated.connect(lambda: self.file_view.ensureVisible(0, 0, 1, 1))

        shortcut = QShortcut(Qt.Key_End, self)
        shortcut.setContext(Qt.WindowShortcut)
        shortcut.activated.connect(
            lambda: self.file_view.ensureVisible(
                0, self.file_view._layout.get_bounding_rect().height(), 1, 1))

        shortcut = QShortcut(Qt.Key_PageUp, self)
        shortcut.setContext(Qt.WindowShortcut)
        shortcut.activated.connect(lambda: self.file_view.scroll_by(0, -self.file_view.viewport().height()))

        shortcut = QShortcut(Qt.Key_PageDown, self)
        shortcut.setContext(Qt.WindowShortcut)
        shortcut.activated.connect(lambda: self.file_view.scroll_by(0, self.file_view.viewport().height()))

    def make_window(self):
        self.setWindowTitle("dt-fileview")
        self.setWindowIcon(QIcon(resource_filename("dirtools", "fileview/dt-fileview.svg")))
        self.vbox = QVBoxLayout()
        self.vbox.setContentsMargins(0, 0, 0, 0)

        self.file_view = FileView(self.controller)

        self.search_lineedit = SearchLineEdit(self.controller)
        self.location_lineedit = LocationLineEdit(self.controller)
        self.location_buttonbar = LocationButtonBar(self.controller)
        self.file_filter = FilterLineEdit(self.controller)
        # self.file_filter.setText("File Pattern Here")
        self.file_view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.status_bar = self.statusBar()

        self.info = QLabel("")
        self.status_bar.addPermanentWidget(self.info)

        self.vbox.addWidget(self.file_view, Qt.AlignLeft)

        self._message_area = MessageArea()
        self._message_area.hide()
        self.vbox.addWidget(self._message_area)

        vbox_widget = QWidget()
        vbox_widget.setLayout(self.vbox)
        self.setCentralWidget(vbox_widget)

    def hide_filter(self):
        self.filter_toolbar.hide()

    def make_filter_toolbar(self):
        hbox = QHBoxLayout()

        form = QFormLayout()
        label = QLabel("Filter:")
        label.setBuddy(self.file_filter)
        form.addRow(label, self.file_filter)
        form.setContentsMargins(0, 0, 0, 0)
        hbox.addLayout(form)

        help_button = QPushButton("Help")
        help_button.clicked.connect(self.controller.show_filter_help)
        hbox.addWidget(help_button)
        hbox.setContentsMargins(0, 0, 0, 0)

        widget = QWidget()
        widget.setLayout(hbox)

        self.filter_toolbar = QToolBar("Filter")
        self.filter_toolbar.addWidget(widget)
        self.addToolBar(Qt.BottomToolBarArea, self.filter_toolbar)
        self.filter_toolbar.hide()

    def make_search_toolbar(self):
        hbox = QHBoxLayout()

        form = QFormLayout()
        label = QLabel("Search:")
        label.setBuddy(self.search_lineedit)
        form.addRow(label, self.search_lineedit)
        form.setContentsMargins(0, 0, 0, 0)
        hbox.addLayout(form)

        help_button = QPushButton("Help")
        help_button.clicked.connect(self.controller.show_search_help)
        hbox.addWidget(help_button)
        hbox.setContentsMargins(0, 0, 0, 0)

        widget = QWidget()
        widget.setLayout(hbox)

        self.search_toolbar = QToolBar("Search")
        self.search_toolbar.addWidget(widget)
        self.addToolBar(Qt.TopToolBarArea, self.search_toolbar)
        self.search_toolbar.hide()

    def make_location_toolbar(self):
        self.location_toolbar = self.addToolBar("Location")
        widget = QWidget()
        form = QFormLayout()
        label = Label("Location:")
        label.clicked.connect(self.controller.show_location_toolbar)

        def show_location_menu(pos):
            self.controller.on_context_menu(label.mapToGlobal(pos))

        label.setContextMenuPolicy(Qt.CustomContextMenu)
        label.customContextMenuRequested.connect(show_location_menu)

        both = QVBoxLayout()
        both.addWidget(self.location_buttonbar)
        both.addWidget(self.location_lineedit)
        form.addRow(label, both)

        form.setContentsMargins(0, 0, 0, 0)
        widget.setLayout(form)
        self.location_toolbar.addWidget(widget)

        self.addToolBarBreak(Qt.TopToolBarArea)

    def make_group_menu(self):
        menu = QMenu("Group Options")
        menu.addAction(self.actions.group_by_none)
        menu.addAction(self.actions.group_by_day)
        menu.addAction(self.actions.group_by_directory)
        menu.addAction(self.actions.group_by_duration)
        return menu

    def make_sort_menu(self):
        menu = QMenu("Sort Options")
        menu.addSeparator().setText("Sort Options")
        menu.addAction(self.actions.sort_directories_first)
        menu.addAction(self.actions.sort_reversed)
        menu.addSeparator().setText("Sort by")
        menu.addAction(self.actions.sort_by_name)
        menu.addAction(self.actions.sort_by_size)
        menu.addAction(self.actions.sort_by_ext)
        menu.addAction(self.actions.sort_by_date)
        menu.addAction(self.actions.sort_by_duration)
        menu.addAction(self.actions.sort_by_aspect_ratio)
        menu.addAction(self.actions.sort_by_framerate)
        menu.addAction(self.actions.sort_by_area)
        menu.addAction(self.actions.sort_by_user)
        menu.addAction(self.actions.sort_by_group)
        menu.addAction(self.actions.sort_by_permission)
        menu.addAction(self.actions.sort_by_random)
        return menu

    def make_view_menu(self):
        menu = QMenu("View Options")
        menu.addSeparator().setText("View Options")
        menu.addAction(self.actions.show_abspath)
        menu.addAction(self.actions.show_basename)
        menu.addSeparator()
        menu.addAction(self.actions.toggle_timegaps)
        return menu

    def make_menubar(self):
        self.menubar = self.menuBar()
        file_menu = self.menubar.addMenu('&File')
        file_menu.addAction(self.actions.new_window)
        file_menu.addAction(self.actions.parent_directory)
        file_menu.addAction(self.actions.debug)
        file_menu.addAction(self.actions.save_as)
        file_menu.addSeparator()
        file_menu.addAction(self.actions.exit)

        edit_menu = self.menubar.addMenu('&Edit')
        # edit_menu.addAction(self.actions.undo)
        # edit_menu.addAction(self.actions.redo)
        # edit_menu.addSeparator()
        edit_menu.addAction(self.actions.edit_cut)
        edit_menu.addAction(self.actions.edit_copy)
        edit_menu.addAction(self.actions.edit_paste)
        edit_menu.addSeparator()
        edit_menu.addAction(self.actions.edit_select_all)
        edit_menu.addSeparator()
        edit_menu.addAction(self.actions.show_preferences)

        view_menu = self.menubar.addMenu('&View')
        view_menu.addSeparator().setText("View Style")
        view_menu.addAction(self.actions.view_detail_view)
        view_menu.addAction(self.actions.view_icon_view)
        view_menu.addAction(self.actions.view_small_icon_view)
        view_menu.addSeparator().setText("Filter")
        view_menu.addAction(self.actions.show_hidden)
        view_menu.addAction(self.actions.show_filtered)
        view_menu.addSeparator().setText("Path Options")
        view_menu.addAction(self.actions.show_abspath)
        view_menu.addAction(self.actions.show_basename)
        view_menu.addSeparator().setText("Sort Options")
        view_menu.addAction(self.actions.sort_directories_first)
        view_menu.addAction(self.actions.sort_reversed)
        view_menu.addAction(self.actions.sort_by_name)
        view_menu.addAction(self.actions.sort_by_size)
        view_menu.addAction(self.actions.sort_by_ext)
        view_menu.addAction(self.actions.sort_by_duration)
        view_menu.addAction(self.actions.sort_by_aspect_ratio)
        view_menu.addAction(self.actions.sort_by_framerate)
        view_menu.addAction(self.actions.sort_by_area)
        view_menu.addAction(self.actions.sort_by_user)
        view_menu.addAction(self.actions.sort_by_group)
        view_menu.addAction(self.actions.sort_by_permission)
        view_menu.addAction(self.actions.sort_by_random)
        view_menu.addSeparator().setText("Zoom")
        view_menu.addAction(self.actions.zoom_in)
        view_menu.addAction(self.actions.zoom_out)
        view_menu.addSeparator()

        self.bookmarks_menu = Menu('&Bookmarks')
        self.menubar.addMenu(self.bookmarks_menu)

        def create_bookmarks_menu():
            bookmarks = self.controller.app.bookmarks

            entries = bookmarks.get_entries()

            self.bookmarks_menu.clear()

            if self.controller.location is None:
                action = self.bookmarks_menu.addAction(QIcon.fromTheme("user-bookmarks"), "Can't bookmark file lists")
                action.setEnabled(False)
            elif self.controller.location in entries:
                self.bookmarks_menu.addAction(
                    QIcon.fromTheme("edit-delete"), "Remove This Location's Bookmark",
                    lambda loc=self.controller.location:
                    bookmarks.remove(loc))
            else:
                self.bookmarks_menu.addAction(
                    QIcon.fromTheme("bookmark-new"), "Bookmark This Location",
                    lambda loc=self.controller.location:
                    bookmarks.append(loc))
            self.bookmarks_menu.addSeparator()

            self.bookmarks_menu.addDoubleAction(
                QIcon.fromTheme("folder"), "View Bookmarks",
                lambda: self.controller.set_location(Location("bookmarks", "/", [])),
                lambda: self.controller.new_controller().set_location(Location("bookmarks", "/", [])))

            self.bookmarks_menu.addSeparator()

            icon = QIcon.fromTheme("folder")
            for entry in entries:
                action = self.bookmarks_menu.addDoubleAction(
                    icon, entry.as_url(),
                    lambda entry=entry: self.controller.set_location(entry),
                    lambda entry=entry: self.controller.app.show_location(entry))

                if not entry.exists():
                    action.setEnabled(False)
        self.bookmarks_menu.aboutToShow.connect(create_bookmarks_menu)

        self.history_menu = Menu('&History')
        self.menubar.addMenu(self.history_menu)

        def create_history_menu():
            history = self.controller.app.location_history

            self.history_menu.clear()
            self.history_menu.addAction(self.actions.back)
            self.history_menu.addAction(self.actions.forward)
            self.history_menu.addSeparator()

            self.history_menu.addDoubleAction(
                QIcon.fromTheme("folder"), "View File History",
                self.controller.show_file_history,
                lambda: self.controller.new_controller().show_file_history())

            self.history_menu.addSection("Location History")

            entries = history.get_unique_entries(35)
            make_history_menu_entries(self.controller, self.history_menu, entries)

        self.history_menu.aboutToShow.connect(create_history_menu)

        help_menu = self.menubar.addMenu('&Help')
        help_menu.addAction(self.actions.about)

    def make_toolbar(self):
        self.toolbar = self.addToolBar("FileView")

        button = ToolButton()
        button.setDefaultAction(self.actions.parent_directory)
        button.sig_middle_click.connect(lambda: self.controller.parent_directory(new_window=True))
        button.setIcon(self.actions.parent_directory.icon())
        self.toolbar.addWidget(button)

        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions.home)
        self.toolbar.addSeparator()

        history_back_btn = ToolButton()
        history_back_btn.setDefaultAction(self.actions.back)
        history_back_btn.setContextMenuPolicy(Qt.CustomContextMenu)
        history_back_btn.customContextMenuRequested.connect(
            lambda pos: self.controller.show_history_context_menu(history_back_btn, False))
        self.toolbar.addWidget(history_back_btn)

        history_forward_btn = ToolButton()
        history_forward_btn.setDefaultAction(self.actions.forward)
        history_forward_btn.setContextMenuPolicy(Qt.CustomContextMenu)
        history_forward_btn.customContextMenuRequested.connect(
            lambda pos: self.controller.show_history_context_menu(history_forward_btn, False))
        self.toolbar.addWidget(history_forward_btn)

        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions.reload)
        self.toolbar.addAction(self.actions.prepare)
        # self.toolbar.addSeparator()
        # self.toolbar.addAction(self.actions.undo)
        # self.toolbar.addAction(self.actions.redo)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions.show_hidden)

        button = QToolButton()
        button.setIcon(QIcon.fromTheme("view-restore"))
        button.setMenu(self.make_view_menu())
        button.setPopupMode(QToolButton.InstantPopup)
        self.toolbar.addWidget(button)

        button = QToolButton()
        button.setIcon(QIcon.fromTheme("view-sort-ascending"))
        button.setMenu(self.make_sort_menu())
        button.setPopupMode(QToolButton.InstantPopup)
        self.toolbar.addWidget(button)

        button = QToolButton()
        button.setIcon(QIcon.fromTheme("view-sort-ascending"))
        button.setMenu(self.make_group_menu())
        button.setPopupMode(QToolButton.InstantPopup)
        self.toolbar.addWidget(button)

        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions.view_icon_view)
        self.toolbar.addAction(self.actions.view_small_icon_view)
        self.toolbar.addAction(self.actions.view_detail_view)

        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions.zoom_in)
        self.toolbar.addAction(self.actions.zoom_out)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions.lod_in)
        self.toolbar.addAction(self.actions.lod_out)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions.crop_thumbnails)

        # Spacer to force right alignment for all following widget
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.toolbar.addWidget(spacer)

        self.toolbar.addAction(self.actions.debug)
        self.toolbar.addAction(self.controller.app.actions.enable_filesystem)

        # Loading icon
        self.loading_movie = QMovie(resource_filename("dirtools", "fileview/icons/gears.gif"))
        self.loading_label = QLabel()
        self.toolbar.addWidget(self.loading_label)

    def show_loading(self):
        self.show_info("Loading...")
        self.loading_label.setMovie(self.loading_movie)
        self.loading_movie.start()
        self.loading_label.show()

    def hide_loading(self):
        self.show_info("")
        self.loading_movie.stop()
        self.loading_label.setMovie(None)
        self.loading_label.setVisible(False)

    def zoom_in(self):
        self.file_view.zoom_in()

    def zoom_out(self):
        self.file_view.zoom_out()

    def set_location(self, location: Location):
        self.location_lineedit.set_location(location)
        self.location_buttonbar.set_location(location)
        self.setWindowTitle("{} - dt-fileview".format(location.as_human()))

    def set_file_list(self):
        self.location_lineedit.set_unused_text()

    def show_info(self, text):
        self.info.setText("  " + text)

    def show_current_filename(self, filename):
        # FIXME: this causes quite substantial keyboard lag when
        # scrolling with PageUp/Down
        self.status_bar.showMessage(filename)
Example #11
0
class WidgetDemo(QWidget):
    def __init__(self,mode):
        super(WidgetDemo,self).__init__()
        self.mode = mode
        self.initUI(mode)

    def initUI(self,mode):
        self.resize(800,800)
        #这里初始化,便于直接输入数据
        self.data = [['']*100 for i in range(15000)]
        self.res_pos = []
        self.focus_pos = None
        self.var_list = None
        #菜单栏
        self.menu = QMenuBar()
        self.file = self.menu.addMenu('文件')
        self.edit = self.menu.addMenu('编辑')
        self.view = self.menu.addMenu('视图')
        self.help = self.menu.addMenu('帮助')
        #各菜单下的子菜单

        #文件菜单下的子菜单
        self.new = self.file.addAction('新建')
        self.open = self.file.addAction('打开')
        self.save = self.file.addAction('保存')
        # self.save_as = self.file.addAction('另存为')


        #编辑菜单下的子菜单
        self.cut = self.edit.addAction('剪切')
        self.copy = self.edit.addAction('复制')
        self.paste = self.edit.addAction('粘贴')
        self.delete = self.edit.addAction('删除')
        self.find = self.edit.addAction('查找')
        self.replace = self.edit.addAction('替换')

        # 快捷键
        self.open.setShortcut('Ctrl+O')
        self.save.setShortcut('Ctrl+S')
        self.new.setShortcut('Ctrl+N')
        self.find.setShortcut('Ctrl+F')

        #视图菜单下的子菜单
        self.tool_view = QAction('工具栏',checkable=True)
        self.tool_view.setChecked(True)
        self.view.addAction(self.tool_view)

        self.statu_view = QAction('状态栏',checkable=True)
        self.statu_view.setChecked(True)
        self.view.addAction(self.statu_view)


        #帮助菜单下的子菜单
        self.about = self.help.addAction('关于')

        #工具栏
        self.tool_bar = QToolBar()
        self.tool_bar.addAction(self.new)
        self.tool_bar.addAction(self.open)
        self.tool_bar.addAction(self.save)
        self.tool_bar.addAction(self.cut)
        self.tool_bar.addAction(self.copy)
        self.tool_bar.addAction(self.paste)
        self.tool_bar.addAction(self.find)
        self.setting = QAction('变量设置')
        self.setting.setEnabled(False)
        self.tool_bar.addAction(self.setting)
        # self.tool_bar.addAction(self.replace)

        # #tool文本显示在下方
        # self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)

        #findWidge
        self.find_widget = FindWidget()
        self.find_widget.hide()

        #表格
        self.table_view = QTableView()
        self.table_view.setModel(self.mode)

        #状态栏
        self.status_bar = QStatusBar()
        self.status_bar.showMessage('这是一个状态栏')


        #创建布局
        layout = QVBoxLayout()
        layout.addWidget(self.menu)
        layout.addWidget(self.tool_bar)
        layout.addWidget(self.find_widget)
        layout.addWidget(self.table_view)
        layout.addWidget(self.status_bar)
        self.setLayout(layout)

        #关联信号
        self.open.triggered.connect(self.triggeredOpen)
        self.save.triggered.connect(self.triggeredSave)
        self.mode.itemChanged.connect(self.dealItemChanged)
        self.tool_view.triggered.connect(self.triggeredView)
        self.statu_view.triggered.connect(self.triggeredView)
        self.new.triggered.connect(self.triggeredNew)
        self.find.triggered.connect(self.triggeredFind)
        self.find_widget.search.triggered.connect(self.dataLocation)
        self.find_widget.down.triggered.connect(self.downLocation)
        self.find_widget.up.triggered.connect(self.upLocation)
        self.find_widget.close.triggered.connect(self.triggeredHideFind)
        self.find_widget.repalce_button.clicked.connect(self.onClickReplace)
        self.find_widget.repalceAll_button.clicked.connect(self.onClickReplaceAll)
        self.setting.triggered.connect(self.triggeredSetting)


        #美化
        icon = QIcon()
        icon.addPixmap(QPixmap('../image/打开.png'), QIcon.Normal, QIcon.Off)
        self.open.setIcon(icon)
        icon.addPixmap(QPixmap('../image/保存.png'), QIcon.Normal, QIcon.Off)
        self.save.setIcon(icon)
        icon.addPixmap(QPixmap('../image/新建.png'), QIcon.Normal, QIcon.Off)
        self.new.setIcon(icon)
        icon.addPixmap(QPixmap('../image/剪切.png'), QIcon.Normal, QIcon.Off)
        self.cut.setIcon(icon)
        icon.addPixmap(QPixmap('../image/复制.png'), QIcon.Normal, QIcon.Off)
        self.copy.setIcon(icon)
        icon.addPixmap(QPixmap('../image/粘贴.png'), QIcon.Normal, QIcon.Off)
        self.paste.setIcon(icon)
        icon.addPixmap(QPixmap('../image/查找1.png'), QIcon.Normal, QIcon.Off)
        self.find.setIcon(icon)
        icon.addPixmap(QPixmap('../image/设置.png'), QIcon.Normal, QIcon.Off)
        self.setting.setIcon(icon)
        # icon.addPixmap(QPixmap('../image/替换.png'), QIcon.Normal, QIcon.Off)
        # self.replace.setIcon(icon)



    def triggeredOpen(self):
        self.status_bar.showMessage('打开文件',5000)
        self.dialog = QFileDialog()
        self.dialog.setFileMode(QFileDialog.AnyFile)
        dir = r'../data'
        self.dialog.setDirectory(dir)
        self.dialog.setFilter(QDir.Files)
        if self.dialog.exec_():
            try:
                start = time.time()
                file_name = self.dialog.selectedFiles()[0]
                #这里读取数据返回列表便于表格中数据的更新
                data_list = read_xlsx(file_name)
                self.data = data_list
                self.mode = QStandardItemModel()
                for rows in data_list:
                    row = [QStandardItem(str(cell)) for cell in rows]
                    self.mode.appendRow(row)
                self.mode.itemChanged.connect(self.dealItemChanged)
                self.table_view.setModel(self.mode)
                # self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
                self.table_view.resizeColumnsToContents()
                self.table_view.resizeRowsToContents()
                end = time.time()
                self.status_bar.showMessage('数据加载完毕,耗时{}秒'.format(end-start))
                self.setting.setEnabled(True)
            except Exception as e:
                print(e)
                pass

    def triggeredSave(self):
        self.status_bar.showMessage('保存文件', 5000)
        file_path, _ = QFileDialog.getSaveFileName(self, '保存文件', '../data',
                                                           'ALL Files(*);;xlsx(*.xlsx);;xls(*.xls);;csv(*.csv)')
        if file_path == '':
            return
        # 文件中写入数据
        try:
            wb = workbook.Workbook()
            wb.encoding = 'utf-8'
            wa = wb.active
            for item in self.data:
                # 过滤无效数据
                try:
                    if ''.join(item) == '':
                        continue
                except:
                    pass
                wa.append(item)
            wb.save(file_path)
            self.status_bar.showMessage('保存完毕!')
        except Exception as e:
            print(e)


    #数据变化信号处理
    def dealItemChanged(self,item):
        try:
            row,column = item.row(),item.column()
            self.data[row][column] = item.text()
        except Exception as e:
            print(e)
            pass

    #状态栏与工具栏的显示和隐藏
    def triggeredView(self,state):
        sender = self.sender().text()
        if sender == '工具栏':
            if state:
                self.tool_bar.show()
            else:
                self.tool_bar.hide()
        else:
            if state:
                self.status_bar.show()
            else:
                self.status_bar.hide()

    def triggeredNew(self):
        print('New')
        pass

    def triggeredFind(self):
        self.find_widget.show()

    #重载信号,实现ESC隐藏查找窗口
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.find_widget.hide()

    #聚焦到某个cell
    def positionFocus(self,x,y):
        self.table_view.verticalScrollBar().setSliderPosition(x)
        self.table_view.horizontalScrollBar().setSliderPosition(y)
        self.table_view.openPersistentEditor(self.mode.index(x, y))
        self.table_view.setFocus()

    #得到所以匹配项的位置
    def dataLocation(self):
        text = self.find_widget.line_edit1.text()
        self.res_pos = []
        flag = 0
        for i,row in enumerate(self.data):
            try:
                if ''.join(row) == '':
                    continue
            except:
                pass
            for j,cell in enumerate(row):
                if text == str(cell):
                    # print(i,j)
                    self.res_pos.append((i,j))
                    item = self.mode.item(i,j)
                    item.setBackground(QColor(255, 255, 0))
                    item.setForeground(QColor(255, 0, 0))
                    #转到到第一个匹配值的位置,并处于可编辑状态
                    if not flag:
                        flag = 1
                        self.positionFocus(i,j)
                        self.focus_pos = 0

    #向下跳转
    def downLocation(self):
        cnt = len(self.res_pos)
        if cnt == 0 or self.focus_pos == cnt-1:
            return
        try:
            self.table_view.closePersistentEditor(
                self.mode.index(self.res_pos[self.focus_pos][0],self.res_pos[self.focus_pos][1]))
            x, y = self.res_pos[self.focus_pos + 1]
            self.positionFocus(x,y)
            self.focus_pos += 1
        except Exception as e:
            print(e)

    # 向上跳转
    def upLocation(self):
        cnt = len(self.res_pos)
        if cnt == 0 or self.focus_pos == 0:
            return
        try:
            self.table_view.closePersistentEditor(
                self.mode.index(self.res_pos[self.focus_pos][0], self.res_pos[self.focus_pos][1]))
            x, y = self.res_pos[self.focus_pos - 1]
            self.positionFocus(x, y)
            self.focus_pos -= 1
        except Exception as e:
            print(e)


    def triggeredHideFind(self):
        if self.res_pos is not None and len(self.res_pos):
            self.table_view.closePersistentEditor(
                self.mode.index(self.res_pos[self.focus_pos][0], self.res_pos[self.focus_pos][1]))
            for item in self.res_pos:
                x,y = item
                item = self.mode.item(x,y)
                item.setBackground(QColor(255, 255, 255))
                item.setForeground(QColor(0, 0, 0))
        self.find_widget.hide()


    #不清楚如何修改cell值,替换功能暂时无法上线
    def onClickReplace(self):
        print('-'*50)
        cnt = len(self.res_pos)
        if self.res_pos is None or cnt == 0:
            return
        try:

            x, y = self.res_pos[self.focus_pos]
            text = self.find_widget.line_edit2.text()
            self.data[x][y] = text
            print(self.data[x][y])
        except Exception as e:
            print(e)

    def onClickReplaceAll(self):
        pass
    #设置变量
    def triggeredSetting(self):
        # 设置变量窗口
        var_list = self.data[0] if self.data[0][0]!='' else self.data[0][1:]
        self.dialog = VariableSettingWindow.VariableSettingWindowDemo(var_list)
        self.dialog.signal.sender.connect(self.getVarList)
        self.dialog.show()

    def getVarList(self,lst):
        self.var_list = lst
Example #12
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()
Example #13
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()
Example #14
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()
Example #15
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()
Example #16
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()
Example #17
0
class WidgetDemo(QWidget):
    def __init__(self, model, rows, columns):
        super(WidgetDemo, self).__init__()
        self.init_rows = rows
        self.init_columns = columns
        self.model = model
        self.initUI(model)

    def initUI(self, model):
        self.resize(800, 800)
        self.res_pos = []
        self.focus_pos = None
        self.var_list = None
        self.signal = MySignal()
        #菜单栏
        self.menu = QMenuBar()
        self.file = self.menu.addMenu('文件')
        self.edit = self.menu.addMenu('编辑')
        self.view = self.menu.addMenu('视图')
        self.help = self.menu.addMenu('帮助')
        #各菜单下的子菜单

        #文件菜单下的子菜单
        # self.new = self.file.addAction('新建')
        self.open = self.file.addAction('打开')
        self.save = self.file.addAction('保存')
        # self.save_as = self.file.addAction('另存为')

        #编辑菜单下的子菜单
        self.cut = self.edit.addAction('剪切')
        self.copy = self.edit.addAction('复制')
        self.paste = self.edit.addAction('粘贴')
        self.delete = self.edit.addAction('删除')
        self.find = self.edit.addAction('查找')
        self.replace = self.edit.addAction('替换')

        # 快捷键
        self.open.setShortcut('Ctrl+O')
        self.save.setShortcut('Ctrl+S')
        # self.new.setShortcut('Ctrl+N')
        self.find.setShortcut('Ctrl+F')

        #视图菜单下的子菜单
        self.tool_view = QAction('工具栏', checkable=True)
        self.tool_view.setChecked(True)
        self.view.addAction(self.tool_view)

        self.statu_view = QAction('状态栏', checkable=True)
        self.statu_view.setChecked(True)
        self.view.addAction(self.statu_view)

        #帮助菜单下的子菜单
        self.about = self.help.addAction('关于')

        #工具栏
        self.tool_bar = QToolBar()
        # self.tool_bar.addAction(self.new)
        self.tool_bar.addAction(self.open)
        self.tool_bar.addAction(self.save)
        self.tool_bar.addAction(self.cut)
        self.tool_bar.addAction(self.copy)
        self.tool_bar.addAction(self.paste)
        self.tool_bar.addAction(self.find)
        # self.setting = QAction('变量设置')
        # self.setting.setEnabled(False)
        # self.tool_bar.addAction(self.setting)
        # self.tool_bar.addAction(self.replace)

        # #tool文本显示在下方
        # self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)

        #findWidge
        self.find_widget = FindWidget()
        self.find_widget.hide()

        #表格
        self.table_view = QTableView()
        self.table_view.setModel(self.model)

        #状态栏
        self.status_bar = QStatusBar()
        self.status_bar.showMessage('状态栏')

        # 右键菜单栏
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.context_menu = QMenu()
        self.addRow_action = self.context_menu.addAction('增加一行')
        self.addRow_action.triggered.connect(self.addRow)
        self.delRow_action = self.context_menu.addAction('删除一行')
        self.delRow_action.triggered.connect(
            lambda: self.model.removeRow(self.table_view.currentIndex().row()))
        self.addColumn_action = self.context_menu.addAction('增加一列')
        self.addColumn_action.triggered.connect(self.addColumn)
        self.delColumn_action = self.context_menu.addAction('删除一列')
        self.delColumn_action.triggered.connect(
            lambda: self.model.removeColumn(self.table_view.currentIndex().
                                            column()))
        self.customContextMenuRequested.connect(self.rightMenuShow)

        #创建布局
        layout = QVBoxLayout()
        layout.addWidget(self.menu)
        layout.addWidget(self.tool_bar)
        layout.addWidget(self.find_widget)
        layout.addWidget(self.table_view)
        layout.addWidget(self.status_bar)
        self.setLayout(layout)

        #关联信号
        self.open.triggered.connect(self.triggeredOpen)
        self.save.triggered.connect(self.triggeredSave)
        self.tool_view.triggered.connect(self.triggeredView)
        self.statu_view.triggered.connect(self.triggeredView)
        # self.new.triggered.connect(self.triggeredNew)
        self.find.triggered.connect(self.triggeredFind)
        self.find_widget.search.triggered.connect(self.dataLocation)
        self.find_widget.down_aciton.triggered.connect(self.downAcitonLocation)
        self.find_widget.up_aciton.triggered.connect(self.upAcitonLocation)
        self.find_widget.close_aciton.triggered.connect(self.triggeredHideFind)
        self.find_widget.repalce_button.clicked.connect(self.onClickReplace)
        self.find_widget.repalceAll_button.clicked.connect(
            self.onClickReplaceAll)
        # self.setting.triggered.connect(self.triggeredSetting)

        #美化
        icon = QIcon()
        icon.addPixmap(QPixmap('./image/打开.png'), QIcon.Normal, QIcon.Off)
        self.open.setIcon(icon)
        icon.addPixmap(QPixmap('./image/保存.png'), QIcon.Normal, QIcon.Off)
        self.save.setIcon(icon)
        # icon.addPixmap(QPixmap('./image/新建.png'), QIcon.Normal, QIcon.Off)
        # self.new.setIcon(icon)
        icon.addPixmap(QPixmap('./image/剪切.png'), QIcon.Normal, QIcon.Off)
        self.cut.setIcon(icon)
        icon.addPixmap(QPixmap('./image/复制.png'), QIcon.Normal, QIcon.Off)
        self.copy.setIcon(icon)
        icon.addPixmap(QPixmap('./image/粘贴.png'), QIcon.Normal, QIcon.Off)
        self.paste.setIcon(icon)
        icon.addPixmap(QPixmap('./image/查找1.png'), QIcon.Normal, QIcon.Off)
        self.find.setIcon(icon)
        # icon.addPixmap(QPixmap('./image/设置.png'), QIcon.Normal, QIcon.Off)
        # self.setting.setIcon(icon)
        # icon.addPixmap(QPixmap('./image/替换.png'), QIcon.Normal, QIcon.Off)
        # self.replace.setIcon(icon)

    def showProgress(self, msg):
        self.status_bar.showMessage(msg)

    def loadData(self, model):
        print('load...')
        self.model = model
        self.table_view.setModel(self.model)
        qApp.processEvents()

    def triggeredOpen(self):
        self.status_bar.showMessage('打开文件', 5000)
        self.dialog = QFileDialog()
        self.dialog.setFileMode(QFileDialog.AnyFile)
        dir = r'D:/Learn-python-notes/projects/demo/Work/TCM_DSAS/data'
        self.dialog.setDirectory(dir)
        self.dialog.setFilter(QDir.Files)
        if self.dialog.exec_():
            try:
                file_name = self.dialog.selectedFiles()[0]
                #这里线程实例化一定要实例化成员变量,否则线程容易销毁
                self.thread = ReaderExcelThread(file_name)
                self.thread.standarModel_signal.connect(self.loadData)
                self.thread.progressRate_signal.connect(self.showProgress)
                self.thread.finished_signal.connect(self.thread.quit)
                self.thread.start()
                # self.setting.setEnabled(True)
            except Exception as e:
                print(e)
                pass

    def triggeredSave(self):
        self.status_bar.showMessage('保存文件', 5000)
        file_path, _ = QFileDialog.getSaveFileName(
            self, '保存文件', './data',
            'ALL Files(*);;xlsx(*.xlsx);;xls(*.xls);;csv(*.csv)')
        if file_path == '':
            return
        # 文件中写入数据
        try:
            self.write_thread = WriteExcelThread(file_path, self.model)
            self.write_thread.start_signal.connect(self.showProgress)
            self.write_thread.end_signal.connect(self.write_thread.quit)
            self.write_thread.start()
            self.status_bar.showMessage('保存完毕!')
        except Exception as e:
            print(e)

    #状态栏与工具栏的显示和隐藏
    def triggeredView(self, state):
        sender = self.sender().text()
        if sender == '工具栏':
            if state:
                self.tool_bar.show()
            else:
                self.tool_bar.hide()
        else:
            if state:
                self.status_bar.show()
            else:
                self.status_bar.hide()

    # def triggeredNew(self):
    #     print('New')
    #     pass

    def triggeredFind(self):
        self.find_widget.show()

    #重载信号,实现ESC隐藏查找窗口
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.find_widget.hide()

    #聚焦到某个cell
    def positionFocus(self, x, y):
        self.table_view.verticalScrollBar().setSliderPosition(x)
        self.table_view.horizontalScrollBar().setSliderPosition(y)
        self.table_view.openPersistentEditor(self.model.index(x, y))
        self.table_view.setFocus()

    #得到所以匹配项的位置
    def dataLocation(self):
        self.changeCellColor()
        text = self.find_widget.line_edit1.text()
        self.res_pos = []
        flag = 0
        rows = self.model.rowCount()
        columns = self.model.columnCount()
        try:
            for row in range(rows):
                for column in range(columns):
                    if text == self.model.index(row, column).data():
                        self.res_pos.append((row, column))
                        item = self.model.item(row, column)
                        item.setBackground(QColor(255, 255, 0))
                        item.setForeground(QColor(255, 0, 0))
                        #转到到第一个匹配值的位置,并处于可编辑状态
                        if not flag:
                            flag = 1
                            self.positionFocus(row, column)
                            self.focus_pos = 0
        except Exception as e:
            print(e)

    #向下跳转
    def downAcitonLocation(self):
        cnt = len(self.res_pos)
        if cnt == 0 or self.focus_pos == cnt - 1:
            return
        try:
            self.table_view.closePersistentEditor(
                self.model.index(self.res_pos[self.focus_pos][0],
                                 self.res_pos[self.focus_pos][1]))
            x, y = self.res_pos[self.focus_pos + 1]
            self.positionFocus(x, y)
            self.focus_pos += 1
        except Exception as e:
            print(e)

    # 向上跳转
    def upAcitonLocation(self):
        cnt = len(self.res_pos)
        if cnt == 0 or self.focus_pos == 0:
            return
        try:
            self.table_view.closePersistentEditor(
                self.model.index(self.res_pos[self.focus_pos][0],
                                 self.res_pos[self.focus_pos][1]))
            x, y = self.res_pos[self.focus_pos - 1]
            self.positionFocus(x, y)
            self.focus_pos -= 1
        except Exception as e:
            print(e)

    def triggeredHideFind(self):
        self.changeCellColor()
        self.find_widget.hide()

    def changeCellColor(self):
        if self.res_pos is not None and len(self.res_pos):
            self.table_view.closePersistentEditor(
                self.model.index(self.res_pos[self.focus_pos][0],
                                 self.res_pos[self.focus_pos][1]))
            for item in self.res_pos:
                x, y = item
                item = self.model.item(x, y)
                item.setBackground(QColor(255, 255, 255))
                item.setForeground(QColor(0, 0, 0))

    def onClickReplace(self):
        cnt = len(self.res_pos)
        text = self.find_widget.line_edit2.text()
        if self.res_pos is None or cnt == 0:
            return
        try:
            x, y = self.res_pos[self.focus_pos]
            self.model.setItem(x, y, QStandardItem(text))
        except Exception as e:
            print(e)

    def onClickReplaceAll(self):
        cnt = len(self.res_pos)
        if self.res_pos is None or cnt == 0:
            return
        try:
            text = self.find_widget.line_edit2.text()
            for x, y in self.res_pos:
                self.model.setItem(x, y, QStandardItem(text))
        except Exception as e:
            print(e)

    # #设置变量
    # def triggeredSetting(self):
    #     self.getVar_thread = GetVarThread(self.model)
    #     self.getVar_thread.send_signal.connect(self.initVarList)
    #     self.getVar_thread.end_signal.connect(self.getVar_thread.quit)
    #     self.getVar_thread.start()
    #
    #
    #
    # def initVarList(self,var_list):
    #     dialog = VariableSettingWindow.VariableSettingWindowDemo(var_list)
    #     dialog.signal.sender.connect(self.getVarList)
    #     dialog.show()
    #
    # def getVarList(self,lst):
    #     self.var_list = lst
    # print(lst)

    def addRow(self):
        # 当前行的下方添加一行
        try:
            self.model.insertRows(self.table_view.currentIndex().row() + 1, 1)
        except Exception as e:
            print(e)

    def addColumn(self):
        self.model.insertColumns(self.table_view.currentIndex().column() + 1,
                                 1)

    def rightMenuShow(self):
        try:
            #菜单显示的位置
            self.context_menu.popup(QCursor.pos())
            self.context_menu.show()
        except Exception as e:
            print(e)
Example #18
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()
Example #19
0
class IMonUi(QMainWindow):
    """Main Window."""
    def __init__(self, parent=None):
        """Initializer."""
        super().__init__(parent)
        self.setWindowTitle('imon: Image Monitor')
        self.setMinimumSize(1024, 768)
        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.setWindowIcon(QIcon('icon.ico'))

        # create variables
        self.watchdir = ''
        self.image_types = ('*.bmp', '*.gif', '*jpg', '*.jpeg', '*.png',
                            '*.pbm', '*.pgm', '*.ppm', '*.xbm', '*.xpm',
                            '*.svg', '*.BMP', '*.GIF', '*JPG', '*.JPEG',
                            '*.PNG', '*.PBM', '*.PGM', '*.PPM', '*.XBM',
                            '*.XPM', '*.SVG')
        self.wasMaximized = True

        # create border widgets
        self._createstatusbar()
        self._createtoolbar()

        # create central widget
        self.display = QLabel()
        self.display.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.display.pixmap = QPixmap('default.png')
        self.display.setPixmap(self.display.pixmap)
        self.display.setAlignment(Qt.AlignCenter)
        self.setCentralWidget(self.display)

        self.installEventFilter(self)

        self.status_message = '{0: <100}'.format(os.path.dirname(os.path.realpath(__file__)) + '/default.png') + '  ' +\
                              '{0: <50}'.format(
                                  str(self.display.pixmap.width()) + 'x' + str(self.display.pixmap.height())) + '  ' +\
                              '{0: <50}'.format(str(f'{os.path.getsize("default.png") / 1000000:.2f}') + 'MB')
        self.status.showMessage(self.status_message)
        self.setStatusBar(self.status)

        # create timer action to update image
        self.timer = QTimer()
        self.timer.timeout.connect(self.updateimage)
        self.timer.start(100)

    def updateimage(self):
        img = get_latest_image(self.watchdir, self.image_types)
        if img != '':
            # update display
            self.display.pixmap = QPixmap(img)
            self.display.setPixmap(self.display.pixmap)
            self.display.setPixmap(
                self.display.pixmap.scaled(self.centralWidget().size(),
                                           Qt.KeepAspectRatio))

            # update status bar
            self.status_message = '{0: <100}'.format(img) + '  ' +\
                '{0: <50}'.format(str(self.display.pixmap.width()) + 'x' + str(self.display.pixmap.height())) + '  ' +\
                '{0: <50}'.format(str(f'{os.path.getsize(img) / 1000000:.2f}') + 'MB')
            self.status.showMessage(self.status_message)
            self.setStatusBar(self.status)

    def _createtoolbar(self):
        self.tools = QToolBar()
        self.addToolBar(self.tools)
        self.tools.addAction('Exit', self.close)
        self.tools.addAction('Select Watch Directory', self._selectdir)
        self.tools.addAction('Fullscreen', self._gofullscreen)

    def _createstatusbar(self):
        self.status = QStatusBar()
        self.setStatusBar(self.status)

    def _selectdir(self):
        self.watchdir = str(
            QFileDialog.getExistingDirectory(self, "Select Directory"))

    def _gofullscreen(self):
        if self.isMaximized() == True:
            self.wasMaximized = True
        else:
            self.wasMaximized = False
        self.showFullScreen()
        self.tools.hide()
        self.status.hide()

    def eventFilter(self, source, event):
        if source is self and event.type() == QEvent.Resize:
            self.display.setPixmap(
                self.display.pixmap.scaled(self.centralWidget().size(),
                                           Qt.KeepAspectRatio))
        return super(IMonUi, self).eventFilter(source, event)

    def keyPressEvent(self, e):
        # TODO: when running showMaximized after being fullscreen, it behaves like showNormal
        if e.key() == Qt.Key_Escape:
            if self.isFullScreen():
                if self.wasMaximized == True:
                    self.showMaximized()
                    self.tools.show()
                    self.status.show()
                else:
                    self.showNormal()
                    self.tools.show()
                    self.status.show()
        if e.key() == Qt.Key_F11:
            if self.isFullScreen():
                if self.wasMaximized == True:
                    self.showMaximized()
                    self.tools.show()
                    self.status.show()
                else:
                    self.showNormal()
                    self.tools.show()
                    self.status.show()
            else:
                self._gofullscreen()
Example #20
0
class GEditor(QMainWindow, _HalWidgetBase):
    percentDone = pyqtSignal(int)

    def __init__(self, parent=None, designer=False):
        if not designer:
            parent = None
        super().__init__(parent)
        self._show_editor = True
        self.load_dialog_code = 'LOAD'
        self.save_dialog_code = 'SAVE'
        self.dialog_code = 'KEYBOARD'

        STATUS.connect('general', self.returnFromDialog)

        self.isCaseSensitive = 0

        self.setMinimumSize(QSize(300, 200))
        self.setWindowTitle("PyQt5 editor test example")

        # make editor
        self.editor = GcodeDisplay(self)
        self.setCentralWidget(self.editor)

        # class patch editor's function to ours
        # so we get the lines percent update
        self.editor.emit_percent = self.emit_percent

        self.editor.setReadOnly(True)
        self.editor.setModified(False)

        self.createActions()

        # Create toolbar and add action
        self.toolBar = QToolBar('File')
        self.toolBar.setObjectName('{}_toolbarfile'.format(self.objectName()))
        self.toolBar.addAction(self.newAction)
        self.toolBar.addAction(self.openAction)
        self.toolBar.addAction(self.saveAction)
        self.toolBar.addAction(self.exitAction)
        self.addToolBar(Qt.TopToolBarArea, self.toolBar)

        #self.toolBar.addSeparator()
        self.toolBarLexer = QToolBar('Lexer')
        self.toolBarLexer.setObjectName('{}_toolbarlexer'.format(
            self.objectName()))

        # add lexer actions
        self.toolBarLexer.addAction(self.gCodeLexerAction)
        self.toolBarLexer.addAction(self.pythonLexerAction)

        self.toolBarLexer.addSeparator()
        self.label = QLabel(
            '''<html><head/><body><p><span style=" font-size:20pt;
                         font-weight:600;">Edit Mode</span></p></body></html>'''
        )
        self.toolBarLexer.addWidget(self.label)

        self.addToolBar(Qt.TopToolBarArea, self.toolBarLexer)

        # Create toolbar and add action
        self.toolBarEdit = QToolBar('Edit')
        self.toolBarEdit.setObjectName('{}_toolbaredit'.format(
            self.objectName()))

        self.toolBarEdit.addAction(self.undoAction)
        self.toolBarEdit.addAction(self.redoAction)
        self.toolBarEdit.addSeparator()
        self.toolBarEdit.addAction(self.replaceAction)
        self.toolBarEdit.addAction(self.findAction)
        self.toolBarEdit.addAction(self.previousAction)
        self.toolBarEdit.addSeparator()
        self.toolBarEdit.addAction(self.caseAction)
        self.addToolBar(Qt.BottomToolBarArea, self.toolBarEdit)

        self.toolBarEntry = QToolBar('entry')
        self.toolBarEntry.setObjectName('{}_toolbarentry'.format(
            self.objectName()))

        frame = QFrame()
        box = QHBoxLayout()
        box.addWidget(self.searchText)
        box.addWidget(self.replaceText)
        box.addStretch(1)
        frame.setLayout(box)
        self.toolBarEntry.addWidget(frame)
        self.addToolBar(Qt.BottomToolBarArea, self.toolBarEntry)
        self.readOnlyMode(save=False)

    def createActions(self):
        # Create new action
        self.newAction = QAction(QIcon.fromTheme('document-new'), 'New', self)
        self.newAction.setShortcut('Ctrl+N')
        self.newAction.setStatusTip('New document')
        self.newAction.triggered.connect(self.newCall)

        # Create open action
        self.openAction = QAction(QIcon.fromTheme('document-open'), '&Open',
                                  self)
        self.openAction.setShortcut('Ctrl+O')
        self.openAction.setStatusTip('Open document')
        self.openAction.triggered.connect(self.openCall)

        # Create save action
        self.saveAction = QAction(QIcon.fromTheme('document-save'), '&Save',
                                  self)
        self.saveAction.setShortcut('Ctrl+S')
        self.saveAction.setStatusTip('Save document')
        self.saveAction.triggered.connect(self.saveCall)

        # Create exit action
        self.exitAction = QAction(QIcon.fromTheme('application-exit'), '&Exit',
                                  self)
        self.exitAction.setShortcut('Ctrl+Q')
        self.exitAction.setToolTip('Exit Edit Mode')
        self.exitAction.setStatusTip('Exit Edit Mode')
        self.exitAction.triggered.connect(self.exitCall)

        # Create gcode lexer action
        self.gCodeLexerAction = QAction(QIcon.fromTheme('lexer.png'),
                                        '&Gcode\nLexer', self)
        self.gCodeLexerAction.setCheckable(1)
        self.gCodeLexerAction.setShortcut('Ctrl+G')
        self.gCodeLexerAction.setStatusTip('Set Gcode highlighting')
        self.gCodeLexerAction.triggered.connect(self.gcodeLexerCall)

        # Create gcode lexer action
        self.pythonLexerAction = QAction(QIcon.fromTheme('lexer.png'),
                                         '&Python\nLexer', self)
        self.pythonLexerAction.setShortcut('Ctrl+P')
        self.pythonLexerAction.setStatusTip('Set Python highlighting')
        self.pythonLexerAction.triggered.connect(self.pythonLexerCall)

        self.searchText = QLineEdit(self)
        self.searchText.setToolTip('Search Text')
        self.searchText.setStatusTip('Text to search for')
        self.searchText.installEventFilter(self)

        self.replaceText = QLineEdit(self)
        self.replaceText.setToolTip('Replace Text')
        self.replaceText.setStatusTip('Replace search text with this text')
        self.replaceText.installEventFilter(self)

        # Create new action
        self.undoAction = QAction(QIcon.fromTheme('edit-undo'), 'Undo', self)
        self.undoAction.setStatusTip('Undo')
        self.undoAction.triggered.connect(self.undoCall)

        # create redo action
        self.redoAction = QAction(QIcon.fromTheme('edit-redo'), 'Redo', self)
        self.redoAction.setStatusTip('Redo')
        self.redoAction.triggered.connect(self.redoCall)

        # create replace action
        self.replaceAction = QAction(QIcon.fromTheme('edit-find-replace'),
                                     'Replace', self)
        self.replaceAction.setStatusTip('Replace text')
        self.replaceAction.triggered.connect(self.replaceCall)

        # create find action
        self.findAction = QAction(QIcon.fromTheme('edit-find'), 'Find', self)
        self.findAction.setStatusTip('Find next occurrence of text')
        self.findAction.triggered.connect(self.findCall)

        # create next action
        self.previousAction = QAction(QIcon.fromTheme('go-previous'),
                                      'Find Previous', self)
        self.previousAction.setStatusTip('Find previous occurrence of text')
        self.previousAction.triggered.connect(self.previousCall)

        # create case action
        self.caseAction = QAction(QIcon.fromTheme('edit-case'), 'Aa', self)
        self.caseAction.setToolTip('Toggle Match Case')
        self.caseAction.setStatusTip('Toggle between any case and match case')
        self.caseAction.setCheckable(1)
        self.caseAction.triggered.connect(self.caseCall)

    # catch focusIn event to pop keyboard dialog
    def eventFilter(self, obj, event):
        if event.type() == QEvent.FocusIn:
            if isinstance(obj, QLineEdit):
                # only if mouse selected
                if event.reason() == 0:
                    self.popEntry(obj)
        return super().eventFilter(obj, event)

    # callback functions built for easy class patching ##########
    # want to refrain from renaming these functions as it will break
    # any class patch user's use
    # we split them like this so a user can intercept the callback
    # but still call the original functionality

    def caseCall(self):
        self.case()

    def case(self):
        self.isCaseSensitive -= 1
        self.isCaseSensitive *= -1

    def exitCall(self):
        self.exit()

    def exit(self):
        if self.editor.isModified():
            result = self.killCheck()
            if result:
                try:
                    self.editor.reload_last(None)
                except Exception as e:
                    print(e)
                self.readOnlyMode()
            return result
        return True

    def findCall(self):
        self.find()

    def find(self):
        self.editor.search(str(self.searchText.text()),
                           re=False,
                           case=self.isCaseSensitive,
                           word=False,
                           wrap=True,
                           fwd=True)

    def previousCall(self):
        self.previous()

    def previous(self):
        self.editor.setCursorPosition(self.editor.getSelection()[0],
                                      self.editor.getSelection()[1])
        self.editor.search(str(self.searchText.text()),
                           re=False,
                           case=self.isCaseSensitive,
                           word=False,
                           wrap=True,
                           fwd=False)

    def gcodeLexerCall(self):
        self.gcodeLexer()

    def gcodeLexer(self):
        self.editor.set_gcode_lexer()

    def nextCall(self):
        self.next()

    def next(self):
        self.editor.search(str(self.searchText.text()),
                           re=False,
                           case=self.isCaseSensitive,
                           word=False,
                           wrap=True,
                           fwd=False)
        self.editor.search_Next()

    def newCall(self):
        self.new()

    def new(self):
        if self.editor.isModified():
            result = self.killCheck()
            if result:
                self.editor.new_text()
        else:
            self.editor.new_text()

    def openCall(self):
        self.open()

    def open(self):
        self.getFileName()

    def openReturn(self, f):
        ACTION.OPEN_PROGRAM(f)
        self.editor.setModified(False)

    def pythonLexerCall(self):
        self.pythonLexer()

    def pythonLexer(self):
        self.editor.set_python_lexer()

    def redoCall(self):
        self.redo()

    def redo(self):
        self.editor.redo()

    def replaceCall(self):
        self.replace()

    def replace(self):
        self.editor.replace_text(str(self.replaceText.text()))
        self.editor.search(str(self.searchText.text()),
                           re=False,
                           case=self.isCaseSensitive,
                           word=False,
                           wrap=True,
                           fwd=True)

    def saveCall(self):
        self.save()

    def save(self):
        self.getSaveFileName()

    def saveReturn(self, fname):
        saved = ACTION.SAVE_PROGRAM(self.editor.text(), fname)
        if saved is not None:
            self.editor.setModified(False)
            ACTION.OPEN_PROGRAM(saved)

    def undoCall(self):
        self.undo()

    def undo(self):
        self.editor.undo()

    # helper functions ############################################

    def saveSettings(self):
        self.SETTINGS_.beginGroup("geditor-{}".format(self.objectName()))
        self.SETTINGS_.setValue('state', QVariant(self.saveState().data()))
        self.SETTINGS_.endGroup()

    def restoreSettings(self):
        # set recorded toolbar settings
        self.SETTINGS_.beginGroup("geditor-{}".format(self.objectName()))
        state = self.SETTINGS_.value('state')
        self.SETTINGS_.endGroup()
        if not state is None:
            try:
                self.restoreState(QByteArray(state))
            except Exception as e:
                print(e)
            else:
                return True
        return False

    def editMode(self):
        self.editor.setReadOnly(False)
        result = self.restoreSettings()
        check = (self.toolBar.toggleViewAction().isChecked()
                 and self.toolBarEdit.toggleViewAction().isChecked()
                 and self.toolBarEntry.toggleViewAction().isChecked()
                 and self.toolBarLexer.toggleViewAction().isChecked())
        if not check:
            self.toolBar.toggleViewAction().setChecked(False)
            self.toolBar.toggleViewAction().trigger()
        if not result:
            self.toolBarEdit.toggleViewAction().setChecked(False)
            self.toolBarEdit.toggleViewAction().trigger()
            self.toolBarEntry.toggleViewAction().setChecked(False)
            self.toolBarEntry.toggleViewAction().trigger()
            self.toolBarLexer.toggleViewAction().setChecked(False)
            self.toolBarLexer.toggleViewAction().trigger()

    def readOnlyMode(self, save=True):
        if save:
            self.saveSettings()
        self.editor.setReadOnly(True)
        self.toolBar.toggleViewAction().setChecked(True)
        self.toolBar.toggleViewAction().trigger()
        self.toolBarEdit.toggleViewAction().setChecked(True)
        self.toolBarEdit.toggleViewAction().trigger()
        self.toolBarEntry.toggleViewAction().setChecked(True)
        self.toolBarEntry.toggleViewAction().trigger()
        self.toolBarLexer.toggleViewAction().setChecked(True)
        self.toolBarLexer.toggleViewAction().trigger()

    def getFileName(self):
        mess = {
            'NAME': self.load_dialog_code,
            'ID': '%s__' % self.objectName(),
            'TITLE': 'Load Editor'
        }
        STATUS.emit('dialog-request', mess)

    def getSaveFileName(self):
        mess = {
            'NAME': self.save_dialog_code,
            'ID': '%s__' % self.objectName(),
            'TITLE': 'Save Editor'
        }
        STATUS.emit('dialog-request', mess)

    def popEntry(self, obj):
        mess = {
            'NAME': self.dialog_code,
            'ID': '%s__' % self.objectName(),
            'OVERLAY': False,
            'OBJECT': obj,
            'TITLE': 'Set Entry for {}'.format(obj.objectName().upper()),
            'GEONAME': '_{}'.format(self.dialog_code)
        }
        STATUS.emit('dialog-request', mess)
        LOG.debug('message sent:{}'.format(mess))

    # process the STATUS return message
    def returnFromDialog(self, w, message):
        if message.get('NAME') == self.load_dialog_code:
            path = message.get('RETURN')
            code = bool(message.get('ID') == '%s__' % self.objectName())
            if path and code:
                self.openReturn(path)
        elif message.get('NAME') == self.save_dialog_code:
            path = message.get('RETURN')
            code = bool(message.get('ID') == '%s__' % self.objectName())
            if path and code:
                self.saveReturn(path)
        elif message.get('NAME') == self.dialog_code:
            txt = message['RETURN']
            code = bool(message.get('ID') == '%s__' % self.objectName())
            if code and txt is not None:
                LOG.debug('message return:{}'.format(message))
                obj = message.get('OBJECT')
                obj.setText(str(txt))

    def killCheck(self):
        choice = QMessageBox.question(
            self, 'Warning!', """This file has changed since loading and
has not been saved. You will lose your changes.
Still want to proceed?""", QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            return True
        else:
            return False

    def emit_percent(self, percent):
        self.percentDone.emit(percent)

    def select_lineup(self):
        self.editor.select_lineup(None)

    def select_linedown(self):
        self.editor.select_linedown(None)

    def select_line(self, line):
        self.editor.highlight_line(None, line)

    def jump_line(self, jump):
        self.editor.jump_line(jump)

    def get_line(self):
        return self.editor.getCursorPosition()[0] + 1

    def set_margin_width(self, width):
        self.editor.set_margin_width(width)

    def set_font(self, font):
        self.editor.font = font
        for i in range(0, 4):
            self.editor.lexer.setFont(font, i)

    def set_background_color(self, color):
        self.editor.set_background_color(color)

    def isReadOnly(self):
        return self.editor.isReadOnly()

    # designer recognized getter/setters

    # auto_show_mdi status
    # These adjust the self.editor instance
    def set_auto_show_mdi(self, data):
        self.editor.auto_show_mdi = data

    def get_auto_show_mdi(self):
        return self.editor.auto_show_mdi

    def reset_auto_show_mdi(self):
        self.editor.auto_show_mdi = True

    auto_show_mdi_status = pyqtProperty(bool, get_auto_show_mdi,
                                        set_auto_show_mdi, reset_auto_show_mdi)

    # designer recognized getter/setters
    # auto_show_manual status
    def set_auto_show_manual(self, data):
        self.editor.auto_show_manual = data

    def get_auto_show_manual(self):
        return self.editor.auto_show_manual

    def reset_auto_show_manual(self):
        self.editor.auto_show_manual = True

    auto_show_manual_status = pyqtProperty(bool, get_auto_show_manual,
                                           set_auto_show_manual,
                                           reset_auto_show_manual)

    # designer recognized getter/setters
    # show_editor status
    def set_show_editor(self, data):
        self._show_editor = data
        if data:
            self.toolBar.show()
            self.toolBarLexer.show()
            self.toolBarEntry.show()
            self.toolBarEdit.show()
        else:
            self.toolBar.hide()
            self.toolBarLexer.hide()
            self.toolBarEntry.hide()
            self.toolBarEdit.hide()

    def get_show_editor(self):
        return self._show_editor

    def reset_show_editor(self):
        self._show_editor = True
        self.toolBar.show()
        self.toolBarLexer.show()
        self.toolBarEntry.show()
        self.toolBarEdit.show()

    show_editor = pyqtProperty(bool, get_show_editor, set_show_editor,
                               reset_show_editor)
Example #21
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()
Example #22
0
class QuickPanel(QWidget, WidgetManager):
    """
    一个快捷面板。类似于KDE的桌面。只不过功能会简单些。主要的方法有:
    addQuickAccessShortcut() 供插件添加一个系统快捷方式。
    removeQuickAccessShortcut() 删除插件添加的系统快捷方式。
    toggle() 如果快捷面板已经显示就隐藏它。如果处于隐藏状态则显示它。
    showAndGetFocus() 显示快捷面板并且将焦点放置于常用的位置。
    registerWidget() 注册部件
    unregisterWidget() 反注册部件
    """

    def __init__(self, platform):
        QWidget.__init__(self, None, Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setWindowModality(Qt.ApplicationModal)
        self.platform = platform
        self.db = QuickPanelDatabase(platform.databaseFile)
        self.createActions()
        self.createControls()
        self.loadSettings()
        self.makeConnections()
        #Besteam系统快捷方式作为QuickPanel提供的一个服务,必须定义在这里
        #虽然部件可能没有运行。QuickPanel也应该记住其它插件添加的快捷方式,以便在
        #用户添加QuickAccessWidget之后,可以显示所有的系统快捷方式
        self.quickAccessModel = QuickAccessModel()

    def createActions(self):
        self.actionChangeBackground = QAction(self)
        self.actionChangeBackground.setIcon(QIcon(":/images/change_background.png"))
        self.actionChangeBackground.setText(self.tr("Change &Background"))
        self.actionChangeBackground.setIconText(self.tr("Background"))
        self.actionChangeBackground.setToolTip(self.tr("Change Background"))
        self.actionClose = QAction(self)
        self.actionClose.setIcon(QIcon(":/images/close.png"))
        self.actionClose.setText(self.tr("&Close"))
        self.actionClose.setIconText(self.tr("Close"))
        self.actionClose.setToolTip(self.tr("Close"))
        self.actionChangeLayout = QAction(self)
        self.actionChangeLayout.setIcon(QIcon(":/images/change_layout.png"))
        self.actionChangeLayout.setText(self.tr("Change &Layout"))
        self.actionChangeLayout.setIconText(self.tr("Layout"))
        self.actionChangeLayout.setToolTip(self.tr("Change Layout"))
        self.actionChangeLayout.setCheckable(True)
        self.actionSelectWidgets = QAction(self)
        self.actionSelectWidgets.setIcon(QIcon(":/images/select_widgets.png"))
        self.actionSelectWidgets.setText(self.tr("&Select Widgets"))
        self.actionSelectWidgets.setIconText(self.tr("Widgets"))
        self.actionSelectWidgets.setToolTip(self.tr("Select Widgets"))
        self.actionResetBackground = QAction(self)
        self.actionResetBackground.setIcon(QIcon(":/images/reset.png"))
        self.actionResetBackground.setText(self.tr("&Reset Background"))
        self.actionResetBackground.setIconText(self.tr("Reset"))
        self.actionResetBackground.setToolTip(self.tr("Reset Background"))
        self.actionResetDefaultLayout = QAction(self)
        self.actionResetDefaultLayout.setIcon(QIcon(":/images/reset.png"))
        self.actionResetDefaultLayout.setText(self.tr("Reset &Layout"))
        self.actionResetDefaultLayout.setIconText(self.tr("Reset"))
        self.actionResetDefaultLayout.setToolTip(self.tr("Reset Layout"))

    def createControls(self):
        self.toolBarMain = QToolBar(self)
        self.toolBarMain.addAction(self.actionChangeBackground)
        self.toolBarMain.addAction(self.actionResetBackground)
        self.toolBarMain.addAction(self.actionChangeLayout)
        self.toolBarMain.addAction(self.actionClose)
        self.toolBarMain.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.toolBarMain.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.toolBarLayout = QToolBar(self)
        self.toolBarLayout.addAction(self.actionSelectWidgets)
        self.toolBarLayout.addAction(self.actionResetDefaultLayout)
        self.toolBarLayout.addAction(self.actionChangeLayout)
        self.toolBarLayout.addAction(self.actionClose)
        self.toolBarLayout.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.toolBarLayout.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        self.canvas = Canvas(self)
        self.layoutEditor = LayoutEditor(self)

        self.lblTitle = QLabel(self)
        self.lblTitle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        self.setLayout(QVBoxLayout())
        self.layoutTop = QHBoxLayout()
        self.layoutTop.addWidget(self.lblTitle)
        self.layoutTop.addWidget(self.toolBarMain)
        self.layoutTop.addWidget(self.toolBarLayout)
        self.toolBarLayout.hide()
        self.layout().addLayout(self.layoutTop)
        self.layout().addWidget(self.canvas)
        self.layout().addWidget(self.layoutEditor)
        self.layoutEditor.hide()

    def loadSettings(self):
        settings = self.platform.getSettings()
        filepath = settings.value("background", "background.png")
        if not os.path.exists(filepath):
            filepath = os.path.join(os.path.dirname(__file__), filepath)
        if not os.path.exists(filepath):
            return
        image = QImage(filepath)
        self._makeBackground(image)

    def makeConnections(self):
        self.actionClose.triggered.connect(self.close)
        self.actionChangeLayout.triggered.connect(self.toggleLayoutEditor)
        self.actionChangeBackground.triggered.connect(self.changeBackground)
        self.actionResetBackground.triggered.connect(self.useDefaultBackground)
        self.actionSelectWidgets.triggered.connect(self.selectWidgets)
        self.actionResetDefaultLayout.triggered.connect(self.resetDefaultLayout)
        QApplication.instance().focusChanged.connect(self.onWindowFocusChanged)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawImage(event.rect(), self._background_image, event.rect())

    def keyPressEvent(self, event):
        QWidget.keyPressEvent(self, event)
        if not event.isAccepted() and event.key() == Qt.Key_Escape:
            if self.layoutEditor.isVisible():
                self.leaveLayoutEditor()
                self.actionChangeLayout.setChecked(False)
            else:
                self.close()

    def onWindowFocusChanged(self, old, new):
        "实现类似于Qt.Popup那样点击其它窗口就立即关闭本窗口的效果。"
        if self.isVisible() and not self.isActiveWindow():
            self.close()

    def showEvent(self, event):
        settings = self.platform.getSettings()
        key = settings.value("globalkey", "Alt+`")
        if key is not None:
            if os.name == "nt": #在Windows系统下,Meta键习惯叫Win键
                key = key.replace("Meta", "Win")
            title = self.tr("提示:在任何位置按<b>{0}</b>打开快捷面板。").format(key)
            self.lblTitle.setText('<span style=" font-size:14pt;font-style:italic;">{0}</span>'.format(title))
        else:
            title = self.tr("快捷面板")
            self.lblTitle.setText('<span style=" font-size:14pt;font-style:italic;">{0}</span>'.format(title))

        #如果有时候运行全屏程序,快捷面板的位置就会发生改变
        self._makeBackground(self._background_image)
        moveToCenter(self)
        self.canvas.positWidgets()
        QWidget.showEvent(self, event)

    def showAndGetFocus(self):
        if not self.isVisible():
            self.show()
        if self.windowState() & Qt.WindowMinimized:
            self.setWindowState(self.windowState() ^ Qt.WindowMinimized)
        self.raise_()
        if os.name == "nt":
            ctypes.windll.user32.BringWindowToTop(int(self.winId()))
            ctypes.windll.user32.SwitchToThisWindow(int(self.winId()), 1)
        self.activateWindow()

    def toggle(self):
        if self.isVisible():
            self.hide()
        else:
            self.showAndGetFocus()

    def addQuickAccessShortcut(self, name, icon, callback):
        """
        添加一个快捷方式。有一个widget专门用于显示Besteam内部各种工具的快捷方式。
        name 快捷方式的名字
        icon 快捷方式的图标
        callback 当用户点击快捷方式的时候调用的回调函数。不会传入任何参数。
        """
        self.quickAccessModel.addShortcut(name, icon, callback)

    def removeQuickAccessShortcut(self, name):
        """删除一个系统快捷方式。参数name是快捷方式的名字。"""
        self.quickAccessModel.removeShortcut(name)

    def enterLayoutEditor(self):
        self.layoutEditor.show()
        self.canvas.hide()
        self.toolBarLayout.show()
        self.toolBarMain.hide()
        self.layoutEditor.beginEdit(self.widgets)

    def leaveLayoutEditor(self):
        self.layoutEditor.hide()
        self.canvas.show()
        self.toolBarLayout.hide()
        self.toolBarMain.show()
        changedWidgets = self.layoutEditor.saveLayout(self.widgets)
        for widget in changedWidgets:
            conf = {}
            conf["left"] = widget.rect.left()
            conf["top"] = widget.rect.top()
            conf["width"] = widget.rect.width()
            conf["height"] = widget.rect.height()
            conf["enabled"] = widget.enabled
            conf["id"] = widget.id
            self.db.saveWidgetConfig(conf)
            if widget.enabled:
                self._enableWidget(widget, False)
            else:
                self._disableWidget(widget, False)
        self.canvas.positWidgets(True)
        self.layoutEditor.endEdit()

    def toggleLayoutEditor(self, checked):
        if checked:
            self.enterLayoutEditor()
        else:
            self.leaveLayoutEditor()

    def changeBackground(self):
        filename, selectedFilter = QFileDialog.getOpenFileName(self, self.tr("Change Background"), \
            QStandardPaths.writableLocation(QStandardPaths.PicturesLocation), \
            self.tr("Image Files (*.png *.gif *.jpg *.jpeg *.bmp *.mng *ico)"))
        if not filename:
            return
        image = QImage(filename)
        if image.isNull():
            QMessageBox.information(self, self.tr("Change Background"), \
                    self.tr("不能读取图像文件,请检查文件格式是否正确,或者图片是否已经损坏。"))
            return
        if image.width() < 800 or image.height() < 600:
            answer = QMessageBox.information(self, self.tr("Change Background"), \
                    self.tr("不建议设置小于800x600的图片作为背景图案。如果继续,可能会使快捷面板显示错乱。是否继续?"),
                    QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No)
            if answer == QMessageBox.No:
                return
        self._makeBackground(image)
        moveToCenter(self)
        self.canvas.positWidgets()
        self.update()
        with self.platform.getSettings() as settings:
            settings.setValue("background", filename)

    def useDefaultBackground(self):
        filename = "background.png"
        if not os.path.exists(filename):
            filename = os.path.join(os.path.dirname(__file__), filename)
        if os.path.exists(filename):
            image = QImage(filename)
            if not image.isNull():
                self._makeBackground(image)
                moveToCenter(self)
                self.canvas.positWidgets()
                self.update()
        settings = self.platform.getSettings()
        settings.remove("background")

    def _makeBackground(self, image):
        desktopSize = QApplication.desktop().screenGeometry(self).size()
        if desktopSize.width() < image.width() or desktopSize.height() < image.height():
            self._background_image = image.scaled(desktopSize, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        else:
            self._background_image = image
        self.resize(self._background_image.size())

    def runDialog(self, *args, **kwargs):
        """
        在QuickPanel中显示一个对话框。主要是为了避免对话框显示的时候,快捷面板会隐藏。
        接受两种形式的参数,其中d是对话框:
            runDialog(d, d.exec_)
            runDialog(d.exec_, *args, **kwargs)
        建议使用第二种
        """
        if isinstance(args[0], QDialog):
            return self._runDialog2(args[0], args[1])
        else:
            callback, args = args[0], args[1:]
            return self._runDialog3(callback, args, kwargs)

    def _runDialog2(self, d, callback):
        return self._runDialog(d, self.canvas, callback)

    def _runDialog3(self, callback, args, kwargs):
        d = callback.__self__
        f = functools.partial(callback, *args, **kwargs)
        return self._runDialog(d, self.canvas, f)

    def _runDialog(self, d, container, callback):
        shutter = ShutterWidget(container)
        newPaintEvent = functools.partial(self._dialog_paintEvent, d)
        oldPaintEvent = d.paintEvent
        d.paintEvent = newPaintEvent
        r = d.geometry()
        r.moveCenter(container.rect().center())
        d.setGeometry(r)
        d.setWindowFlags(Qt.Widget)
        d.setParent(container)
        d.setFocus(Qt.OtherFocusReason)
        try:
            shutter.show()
            d.raise_()
            return callback()
        finally:
            d.paintEvent = oldPaintEvent
            shutter.close()
            shutter.setParent(None)

    def _dialog_paintEvent(self, d, event):
        QDialog.paintEvent(d, event)
        pen = QPen()
        pen.setWidth(2)
        pen.setColor(QColor(200, 200, 200))
        rect = d.rect()
        rect = rect.adjusted(0, 0, -1, -1)
        painter = QPainter(d)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setPen(pen)
        painter.setOpacity(0.8)
        painter.setBrush(QBrush(QColor(Qt.white)))
        painter.drawRoundedRect(rect, 15, 15)
Example #23
0
class MainWindow(QMainWindow):
    """

    """
    def __init__(self):
        super(MainWindow, self).__init__()

        self.menu_bar = QMenuBar()

        self.media_menu = QMenu("媒体(M)", self.menu_bar)
        self.local_action = QAction("本地文件(L)", self.media_menu)
        self.local_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_L))
        self.local_action.triggered.connect(self.show_local_widget)
        self.live_search_action = QAction("直播搜索(F)", self.media_menu)
        self.live_search_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_F))
        self.live_search_action.triggered.connect(self.show_search_widget)
        self.tv_live_search_action = QAction("高清电视(T)", self.media_menu)
        self.tv_live_search_action.setShortcut(QKeySequence(Qt.CTRL +
                                                            Qt.Key_T))
        self.tv_live_search_action.triggered.connect(self.show_tv_widget)
        self.radio_station_search_action = QAction("广播电台(R)", self.media_menu)
        self.radio_station_search_action.setShortcut(
            QKeySequence(Qt.CTRL + Qt.Key_R))
        self.radio_station_search_action.triggered.connect(
            self.show_radio_station_widget)
        self.close_action = QAction("关闭(C)", self.media_menu)
        self.close_action.setShortcut(
            QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_C))
        self.close_action.triggered.connect(self.answer_close_action_triggered)
        self.quit_action = QAction("退出(Q)", self.media_menu)
        self.quit_action.setShortcut(
            QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Q))
        self.quit_action.triggered.connect(lambda: sys.exit())

        self.tool_menu = QMenu("工具(T)", self.menu_bar)
        self.screenshot_action = QAction("截图(J)", self.tool_menu)
        self.screenshot_action.setShortcut(QKeySequence(Qt.SHIFT + Qt.Key_J))
        self.screenshot_action.triggered.connect(
            self.answer_screenshot_action_triggered)
        self.gif_action = QAction("动图(G)", self.tool_menu)
        self.gif_action.setShortcut(QKeySequence(Qt.SHIFT + Qt.Key_G))
        self.gif_action.triggered.connect(self.answer_gif_action_triggered)
        self.screen_record_action = QAction("录屏(L)", self.tool_menu)
        self.screen_record_action.setShortcut(QKeySequence(Qt.SHIFT +
                                                           Qt.Key_L))
        self.screen_record_action.triggered.connect(
            self.answer_screen_record_action_triggered)

        self.enhance_menu = QMenu("增强(E)", self.menu_bar)
        self.hot_live_action = QAction("热门直播(H)", self.enhance_menu)
        self.hot_live_action.setShortcut(QKeySequence(Qt.ALT + Qt.Key_H))
        self.attention_action = QAction("我的关注(A)", self.enhance_menu)
        self.attention_action.setShortcut(QKeySequence(Qt.ALT + Qt.Key_A))
        self.nlp_action = QAction("智能字幕(N)", self.enhance_menu)
        self.nlp_action.setShortcut(QKeySequence(Qt.ALT + Qt.Key_N))
        self.note_action = QAction("边看边记(W)", self.enhance_menu)
        self.note_action.setShortcut(QKeySequence(Qt.ALT + Qt.Key_W))
        self.preferences_action = QAction("偏好设置(P)", self.enhance_menu)
        self.preferences_action.setShortcut(QKeySequence(Qt.ALT + Qt.Key_P))
        self.preferences_action.triggered.connect(self.show_preferences_widget)

        self.my_menu = QMenu("我的(P)", self.menu_bar)
        self.connect_github_action = QAction("连接到GitHub(C)", self.my_menu)
        self.connect_github_action.setShortcut(
            QKeySequence(Qt.ALT + Qt.SHIFT + Qt.Key_C))
        self.backup_data_action = QAction("备份数据(B)", self.my_menu)
        self.backup_data_action.setShortcut(
            QKeySequence(Qt.ALT + Qt.SHIFT + Qt.Key_B))
        self.restore_from_backup_action = QAction("从备份中恢复(R)", self.my_menu)
        self.restore_from_backup_action.setShortcut(
            QKeySequence(Qt.ALT + Qt.SHIFT + Qt.Key_R))

        self.help_menu = QMenu("帮助(H)", self.menu_bar)
        self.help_action = QAction("帮助文档(H)", self.help_menu)
        self.help_action.setShortcut(QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_H))
        self.help_action.triggered.connect(self.answer_help_action_triggered)
        self.change_log_action = QAction("更新日志(U)", self.help_menu)
        self.change_log_action.setShortcut(
            QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_U))
        self.change_log_action.triggered.connect(
            self.answer_change_log_action_triggered)
        self.check_version_action = QAction("检查版本(C)", self.help_menu)
        self.check_version_action.setShortcut(
            QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_C))
        self.check_version_action.triggered.connect(
            self.answer_check_version_action_triggered)
        self.about_action = QAction("关于软件(A)", self.help_menu)
        self.about_action.setShortcut(QKeySequence(Qt.CTRL + Qt.ALT +
                                                   Qt.Key_A))
        self.about_action.triggered.connect(self.answer_about_action_triggered)

        self.tool_bar = QToolBar()
        self.tool_bar.setFloatable(False)
        self.tool_bar.setMovable(False)
        self.tool_bar.setIconSize(QSize(40, 40))
        # self.tool_bar.allowedAreasChanged.connect(set_tool_bar_pos)
        # self.tool_bar.setStyleSheet("QToolBar{border: 1px solid #313335; spacing:5px; }")
        self.live_tool_action = QAction("", self.tool_bar)
        self.live_tool_action.setToolTip("直播搜索")
        self.live_tool_action.setIcon(
            QIcon(PathHelper.get_img_path("*****@*****.**")))
        self.search_widget = SearchWidget()
        self.live_tool_action.triggered.connect(self.show_search_widget)
        self.search_widget.watch_live_signal.connect(
            self.answer_play_action_triggered)

        self.tv_live_tool_action = QAction("", self.tool_bar)
        self.tv_live_tool_action.setToolTip("高清电视")
        self.tv_live_tool_action.setIcon(
            QIcon(PathHelper.get_img_path("*****@*****.**")))
        self.tv_widget = TvWidget()
        self.tv_live_tool_action.triggered.connect(self.show_tv_widget)
        self.tv_widget.watch_tv_signal.connect(
            self.answer_play_action_triggered)

        self.radio_station_tool_action = QAction("", self.tool_bar)
        self.radio_station_tool_action.setToolTip("广播电台")
        self.radio_station_tool_action.setIcon(
            QIcon(PathHelper.get_img_path("*****@*****.**")))
        self.radio_station_widget = RadioStationWidget()
        self.radio_station_tool_action.triggered.connect(
            self.show_radio_station_widget)
        self.radio_station_widget.listen_radio_station_signal.connect(
            self.answer_play_action_triggered)

        self.hot_live_tool_action = QAction("", self.tool_bar)
        self.hot_live_tool_action.setToolTip("热门直播")
        self.hot_live_tool_action.setIcon(
            QIcon(PathHelper.get_img_path("*****@*****.**")))

        self.attention_tool_action = QAction("", self.tool_bar)
        self.attention_tool_action.setToolTip("我的关注")
        self.attention_tool_action.setIcon(
            QIcon(PathHelper.get_img_path("*****@*****.**")))

        self.nlp_tool_action = QAction("", self.tool_bar)
        self.nlp_tool_action.setToolTip("智能字幕")
        self.nlp_tool_action.setIcon(
            QIcon(PathHelper.get_img_path("*****@*****.**")))

        self.note_tool_action = QAction("", self.tool_bar)
        self.note_tool_action.setToolTip("边看边记")
        self.note_tool_action.setIcon(
            QIcon(PathHelper.get_img_path("*****@*****.**")))

        self.preferences_tool_action = QAction("", self.tool_bar)
        self.preferences_tool_action.setToolTip("偏好设置")
        self.preferences_tool_action.setIcon(
            QIcon(PathHelper.get_img_path("*****@*****.**")))
        self.preferences_tool_action.triggered.connect(
            self.show_preferences_widget)

        self.vlc_widget = VlcPlayerWidget()
        self.vlc_widget.showFullScreen_signal.connect(
            self.on_showFullScreen_signal)
        self.vlc_widget.showNormal_signal.connect(self.on_showNormal_signal)

        self._init_ui()
        self.init_cfg()

    def _init_ui(self):
        """

        :return:
        """
        # 菜单栏
        self.media_menu.addAction(self.local_action)
        self.media_menu.addAction(self.live_search_action)
        self.media_menu.addAction(self.tv_live_search_action)
        self.media_menu.addAction(self.radio_station_search_action)
        self.media_menu.addSeparator()
        self.media_menu.addAction(self.close_action)
        self.media_menu.addAction(self.quit_action)

        self.tool_menu.addAction(self.screenshot_action)
        self.tool_menu.addAction(self.gif_action)
        self.tool_menu.addAction(self.screen_record_action)

        self.enhance_menu.addAction(self.hot_live_action)
        self.enhance_menu.addAction(self.attention_action)
        self.enhance_menu.addSeparator()
        self.enhance_menu.addAction(self.nlp_action)
        self.enhance_menu.addAction(self.note_action)
        self.enhance_menu.addSeparator()
        self.enhance_menu.addAction(self.preferences_action)

        self.my_menu.addAction(self.connect_github_action)
        self.my_menu.addSeparator()
        self.my_menu.addAction(self.backup_data_action)
        self.my_menu.addAction(self.restore_from_backup_action)

        self.help_menu.addAction(self.help_action)
        self.help_menu.addAction(self.change_log_action)
        self.help_menu.addAction(self.check_version_action)
        self.help_menu.addSeparator()
        self.help_menu.addAction(self.about_action)

        self.menu_bar.addMenu(self.media_menu)
        self.menu_bar.addMenu(self.enhance_menu)
        self.menu_bar.addMenu(self.tool_menu)
        self.menu_bar.addMenu(self.my_menu)
        self.menu_bar.addMenu(self.help_menu)

        self.setMenuBar(self.menu_bar)

        # 工具栏
        self.tool_bar.addAction(self.live_tool_action)
        self.tool_bar.addAction(self.tv_live_tool_action)
        self.tool_bar.addAction(self.radio_station_tool_action)
        self.tool_bar.addAction(self.hot_live_tool_action)
        self.tool_bar.addAction(self.attention_tool_action)
        self.tool_bar.addSeparator()
        self.tool_bar.addAction(self.nlp_tool_action)
        self.tool_bar.addAction(self.note_tool_action)
        self.tool_bar.addSeparator()
        self.tool_bar.addAction(self.preferences_tool_action)

        # 显示区域
        self.setCentralWidget(self.vlc_widget)

        self.setContextMenuPolicy(Qt.NoContextMenu)
        self.set_window_info()

    def init_cfg(self):
        """

        :return:
        """

        # 工具栏设置
        visible = get_tool_bar_visible()
        if visible:
            self.tool_bar.show()
        else:
            self.tool_bar.hide()
        pos = get_tool_bar_pos()
        if pos == CommonEnum.ToolBarPosTop:
            self.addToolBar(Qt.TopToolBarArea, self.tool_bar)
        elif pos == CommonEnum.ToolBarPosRight:
            self.addToolBar(Qt.RightToolBarArea, self.tool_bar)
        elif pos == CommonEnum.ToolBarPosBottom:
            self.addToolBar(Qt.BottomToolBarArea, self.tool_bar)
        elif pos == CommonEnum.ToolBarPosLeft:
            self.addToolBar(Qt.LeftToolBarArea, self.tool_bar)
        else:
            self.addToolBar(Qt.TopToolBarArea, self.tool_bar)

        # 皮肤设置
        skin = get_skin()
        set_skin(skin)

        # 语言设置
        language = get_language()
        set_language(language)

        # 字体设置
        font_dict = get_font()
        font = QFont()
        font.setFamily(font_dict["font_family"])
        font.setStyleName(font_dict["font_style"])
        font.setPointSize(font_dict["font_size"])
        qApp.setFont(font)
        qApp.processEvents()

    def set_window_info(self):
        """

        :return:
        """
        desktop_widget = QDesktopWidget()
        screen_rect = desktop_widget.screenGeometry()
        self.setGeometry(screen_rect)
        _app_info = get_app_info()
        title = _app_info["name"] + " " + _app_info["version"]
        self.setWindowTitle(title)
        self.setWindowIcon(QIcon(PathHelper.get_img_path("*****@*****.**")))
        self.showMaximized()

    def show_local_widget(self):
        """

        :return:
        """
        file_name, file_type = QFileDialog.getOpenFileName(
            self, "选择音视频文件", ".", "All Files (*)")
        if file_name:
            _type = os.path.splitext(file_name)[-1][1:]
            _type_list = [
                "3g2", "3gp", "3gp2", "3gpp", "amv", "asf", "avi", "bik",
                "bin", "divx", "drc", "dv", "f4v", "flv", "gvi", "gxf", "iso",
                "m1v", "m2v", "m2t", "m2ts", "m4v", "mkv", "mov", "mp2", "mp4",
                "mp4v", "mpe", "mpeg", "mpeg1", "mpeg2", "mpeg4", "mpg",
                "mpv2", "mts", "mxf", "mxg", "nsv", "nuv", "ogg", "ogm", "ogv",
                "ps", "rec", "rm", "rmvb", "rpl", "thp", "tod", "ts", "tts",
                "txd", "vob", "vro", "webm", "wm", "wmv", "wtv", "xesc", "3ga",
                "669", "a52", "acc", "ac3", "adt", "adts", "aif", "aiff",
                "amr", "aob", "ape", "awb", "caf", "dts", "flac", "it", "kar",
                "m4a", "m4b", "m4p", "m5p", "mid", "mka", "mlp", "mod", "mpa",
                "mp1", "mp2", "mp3", "mpc", "mpga", "mus", "oga", "ogg", "oma",
                "opus", "qcp", "ra", "rmi", "s3m", "sid", "spx", "thd", "tta",
                "voc", "vqf", "w64", "wav", "wma", "wv", "xa", "xm"
            ]
            if _type in _type_list:
                self.answer_play_action_triggered(
                    file_name, PlayerEnum.MrlTypeLocal.value[1])
            else:
                _box = PromptBox(2, "音视频文件错误!", 1)
                width, height = get_window_center_point(_box)
                _box.move(width, height)
                _box.exec_()

    def show_search_widget(self):
        """

        :return:
        """
        width, height = get_window_center_point(self.search_widget)
        self.search_widget.move(width, height)
        self.search_widget.exec_()

    def show_tv_widget(self):
        """

        :return:
        """
        width, height = get_window_center_point(self.tv_widget)
        self.tv_widget.move(width, height)
        self.tv_widget.exec_()

    def show_radio_station_widget(self):
        """

        :return:
        """
        width, height = get_window_center_point(self.radio_station_widget)
        self.radio_station_widget.move(width, height)
        self.radio_station_widget.exec_()

    @staticmethod
    def show_preferences_widget():
        """

        :return:
        """
        preferences_widget = PreferencesWidget()
        width, height = get_window_center_point(preferences_widget)
        preferences_widget.move(width, height)
        preferences_widget.exec_()

    @staticmethod
    def answer_help_action_triggered():
        """

        :return:
        """
        desktop_services = QDesktopServices()
        _app_info = get_app_info()
        desktop_services.openUrl(QUrl(_app_info["help_url"]))

    @staticmethod
    def answer_change_log_action_triggered():
        """

        :return:
        """
        desktop_services = QDesktopServices()
        _app_info = get_app_info()
        desktop_services.openUrl(QUrl(_app_info["change_log_url"]))

    @staticmethod
    def answer_check_version_action_triggered():
        """

        :return:
        """
        # TODO: 获取 GitHub API 进行检查并弹窗
        desktop_services = QDesktopServices()
        _app_info = get_app_info()
        desktop_services.openUrl(QUrl(_app_info["update"]))

    @staticmethod
    def answer_about_action_triggered():
        """

        :return:
        """
        about_widget = AboutWidget()
        width, height = get_window_center_point(about_widget)
        about_widget.move(width, height)
        about_widget.exec_()

    def answer_play_action_triggered(self, url: str, url_type: PlayerEnum):
        """

        :param url:
        :param url_type:
        :return:
        """
        # if url_type == PlayerEnum.MrlTypeRS.value[1]:
        #     self.vlc_widget = VlcPlayerWidget("--audio-visual=visual", "--effect-list=spectrometer",
        #                                       "--effect-fft-window=flattop")
        # else:
        #     self.vlc_widget = VlcPlayerWidget()
        # loading_widget = LoadingWidget()
        # width, height = get_window_center_point(loading_widget)
        # loading_widget.move(width, height)
        # loading_widget.show()
        self.vlc_widget.vlc_play(url, url_type)
        # while PlayerState.Load == PlayerEnum.LoadPlaying:
        #     loading_widget.close()

    def answer_close_action_triggered(self):
        """

        :return:
        """
        self.vlc_widget.vlc_stop()

    @staticmethod
    def answer_screenshot_action_triggered():
        """

        :return:
        """
        pass

    def answer_gif_action_triggered(self):
        """

        :return:
        """
        pass

    def answer_screen_record_action_triggered(self):
        """

        :return:
        """
        pass

    def closeEvent(self, event) -> None:
        """

        :param event:
        :return:
        """
        self.vlc_widget.vlc_release()

    def on_showFullScreen_signal(self):
        """ 媒体播放器全屏显示

        :return:
        """
        self.showFullScreen()
        self.menu_bar.hide()
        self.tool_bar.hide()

    def on_showNormal_signal(self):
        """ 媒体播放器恢复默认

        :return:
        """
        self.menu_bar.show()
        self.tool_bar.show()
        self.showNormal()
        self.showMaximized()
Example #24
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_()
Example #25
0
class MyTextEditDialog(QMainWindow):
    def __init__(self, parent):
        super(MyTextEditDialog, self).__init__(parent)

        layout = QVBoxLayout()
        self.editor = TextEdit()
        # Setup the QTextEdit editor configuration
        self.editor.setAutoFormatting(QTextEdit.AutoAll)
        self.editor.selectionChanged.connect(self.update_format)
        # Initialize default font size.
        font = QFont('Times', 12)
        self.editor.setFont(font)
        # We need to repeat the size to init the current format.
        self.editor.setFontPointSize(12)

        # self.path holds the path of the currently open file.
        # If none, we haven't got a file open yet (or creating new).
        self.path = None

        layout.addWidget(self.editor)

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

        self.status = QStatusBar()
        self.setStatusBar(self.status)

        # Uncomment to disable native menubar on Mac
        # self.menuBar().setNativeMenuBar(False)

        self.file_toolbar = QToolBar("File")
        self.file_toolbar.setIconSize(QSize(14, 14))
        self.addToolBar(self.file_toolbar)
        file_menu = self.menuBar().addMenu("&File")

        open_file_action = QAction(
            QIcon(os.path.join('images', 'blue-folder-open-document.png')),
            "Open file...", self)
        open_file_action.setStatusTip("Open file")
        open_file_action.triggered.connect(self.file_open)
        file_menu.addAction(open_file_action)
        self.file_toolbar.addAction(open_file_action)

        save_file_action = QAction(QIcon(os.path.join('images', 'disk.png')),
                                   "Save", self)
        save_file_action.setStatusTip("Save current page")
        save_file_action.triggered.connect(self.file_save)
        file_menu.addAction(save_file_action)
        self.file_toolbar.addAction(save_file_action)

        saveas_file_action = QAction(
            QIcon(os.path.join('images', 'disk--pencil.png')), "Save As...",
            self)
        saveas_file_action.setStatusTip("Save current page to specified file")
        saveas_file_action.triggered.connect(self.file_saveas)
        file_menu.addAction(saveas_file_action)
        self.file_toolbar.addAction(saveas_file_action)

        print_action = QAction(QIcon(os.path.join('images', 'printer.png')),
                               "Print...", self)
        print_action.setStatusTip("Print current page")
        print_action.triggered.connect(self.file_print)
        file_menu.addAction(print_action)
        self.file_toolbar.addAction(print_action)

        self.edit_toolbar = QToolBar("Edit")
        self.edit_toolbar.setIconSize(QSize(16, 16))
        self.addToolBar(self.edit_toolbar)
        edit_menu = self.menuBar().addMenu("&Edit")

        undo_action = QAction(
            QIcon(os.path.join('images', 'arrow-curve-180-left.png')), "Undo",
            self)
        undo_action.setStatusTip("Undo last change")
        undo_action.triggered.connect(self.editor.undo)
        edit_menu.addAction(undo_action)

        redo_action = QAction(QIcon(os.path.join('images', 'arrow-curve.png')),
                              "Redo", self)
        redo_action.setStatusTip("Redo last change")
        redo_action.triggered.connect(self.editor.redo)
        self.edit_toolbar.addAction(redo_action)
        edit_menu.addAction(redo_action)

        edit_menu.addSeparator()

        cut_action = QAction(QIcon(os.path.join('images', 'scissors.png')),
                             "Cut", self)
        cut_action.setStatusTip("Cut selected text")
        cut_action.setShortcut(QKeySequence.Cut)
        cut_action.triggered.connect(self.editor.cut)
        self.edit_toolbar.addAction(cut_action)
        edit_menu.addAction(cut_action)

        copy_action = QAction(
            QIcon(os.path.join('images', 'document-copy.png')), "Copy", self)
        copy_action.setStatusTip("Copy selected text")
        cut_action.setShortcut(QKeySequence.Copy)
        copy_action.triggered.connect(self.editor.copy)
        self.edit_toolbar.addAction(copy_action)
        edit_menu.addAction(copy_action)

        paste_action = QAction(
            QIcon(os.path.join('images', 'clipboard-paste-document-text.png')),
            "Paste", self)
        paste_action.setStatusTip("Paste from clipboard")
        cut_action.setShortcut(QKeySequence.Paste)
        paste_action.triggered.connect(self.editor.paste)
        self.edit_toolbar.addAction(paste_action)
        edit_menu.addAction(paste_action)

        select_action = QAction(
            QIcon(os.path.join('images', 'selection-input.png')), "Select all",
            self)
        select_action.setStatusTip("Select all text")
        cut_action.setShortcut(QKeySequence.SelectAll)
        select_action.triggered.connect(self.editor.selectAll)
        edit_menu.addAction(select_action)

        edit_menu.addSeparator()

        wrap_action = QAction(
            QIcon(os.path.join('images', 'arrow-continue.png')),
            "Wrap text to window", self)
        wrap_action.setStatusTip("Toggle wrap text to window")
        wrap_action.setCheckable(True)
        wrap_action.setChecked(True)
        wrap_action.triggered.connect(self.edit_toggle_wrap)
        edit_menu.addAction(wrap_action)

        self.format_toolbar = QToolBar("Format")
        self.format_toolbar.setIconSize(QSize(16, 16))
        self.addToolBar(self.format_toolbar)
        self.format_menu = self.menuBar().addMenu("&Format")

        # We need references to these actions/settings to update as selection changes, so attach to self.
        # self.bt_ = QPushButton()
        # self.fonts.currentFontChanged.connect(self.editor.setCurrentFont)
        # self.format_toolbar.addWidget(self.fonts)

        self.fonts = QFontComboBox()
        self.fonts.currentFontChanged.connect(self.editor.setCurrentFont)
        self.format_toolbar.addWidget(self.fonts)

        self.fontsize = QComboBox()
        self.fontsize.addItems([str(s) for s in FONT_SIZES])

        # Connect to the signal producing the text of the current selection. Convert the string to float
        # and set as the pointsize. We could also use the index + retrieve from FONT_SIZES.
        self.fontsize.currentIndexChanged[str].connect(
            lambda s: self.editor.setFontPointSize(float(s)))
        self.format_toolbar.addWidget(self.fontsize)

        self.bold_action = QAction(
            QIcon(os.path.join('images', 'edit-bold.png')), "Bold", self)
        self.bold_action.setStatusTip("Bold")
        self.bold_action.setShortcut(QKeySequence.Bold)
        self.bold_action.setCheckable(True)
        self.bold_action.toggled.connect(lambda x: self.editor.setFontWeight(
            QFont.Bold if x else QFont.Normal))
        self.format_toolbar.addAction(self.bold_action)
        self.format_menu.addAction(self.bold_action)

        self.italic_action = QAction(
            QIcon(os.path.join('images', 'edit-italic.png')), "Italic", self)
        self.italic_action.setStatusTip("Italic")
        self.italic_action.setShortcut(QKeySequence.Italic)
        self.italic_action.setCheckable(True)
        self.italic_action.toggled.connect(self.editor.setFontItalic)
        self.format_toolbar.addAction(self.italic_action)
        self.format_menu.addAction(self.italic_action)

        self.underline_action = QAction(
            QIcon(os.path.join('images', 'edit-underline.png')), "Underline",
            self)
        self.underline_action.setStatusTip("Underline")
        self.underline_action.setShortcut(QKeySequence.Underline)
        self.underline_action.setCheckable(True)
        self.underline_action.toggled.connect(self.editor.setFontUnderline)
        self.format_toolbar.addAction(self.underline_action)
        self.format_menu.addAction(self.underline_action)

        self.format_menu.addSeparator()

        self.alignl_action = QAction(
            QIcon(os.path.join('images', 'edit-alignment.png')), "Align left",
            self)
        self.alignl_action.setStatusTip("Align text left")
        self.alignl_action.setCheckable(True)
        self.alignl_action.triggered.connect(
            lambda: self.editor.setAlignment(Qt.AlignLeft))
        self.format_toolbar.addAction(self.alignl_action)
        self.format_menu.addAction(self.alignl_action)

        self.alignc_action = QAction(
            QIcon(os.path.join('images', 'edit-alignment-center.png')),
            "Align center", self)
        self.alignc_action.setStatusTip("Align text center")
        self.alignc_action.setCheckable(True)
        self.alignc_action.triggered.connect(
            lambda: self.editor.setAlignment(Qt.AlignCenter))
        self.format_toolbar.addAction(self.alignc_action)
        self.format_menu.addAction(self.alignc_action)

        self.alignr_action = QAction(
            QIcon(os.path.join('images', 'edit-alignment-right.png')),
            "Align right", self)
        self.alignr_action.setStatusTip("Align text right")
        self.alignr_action.setCheckable(True)
        self.alignr_action.triggered.connect(
            lambda: self.editor.setAlignment(Qt.AlignRight))
        self.format_toolbar.addAction(self.alignr_action)
        self.format_menu.addAction(self.alignr_action)

        self.alignj_action = QAction(
            QIcon(os.path.join('images', 'edit-alignment-justify.png')),
            "Justify", self)
        self.alignj_action.setStatusTip("Justify text")
        self.alignj_action.setCheckable(True)
        self.alignj_action.triggered.connect(
            lambda: self.editor.setAlignment(Qt.AlignJustify))
        self.format_toolbar.addAction(self.alignj_action)
        self.format_menu.addAction(self.alignj_action)

        format_group = QActionGroup(self)
        format_group.setExclusive(True)
        format_group.addAction(self.alignl_action)
        format_group.addAction(self.alignc_action)
        format_group.addAction(self.alignr_action)
        format_group.addAction(self.alignj_action)

        self.format_menu.addSeparator()

        # A list of all format-related widgets/actions, so we can disable/enable signals when updating.
        self._format_actions = [
            self.fonts,
            self.fontsize,
            self.bold_action,
            self.italic_action,
            self.underline_action,
            # We don't need to disable signals for alignment, as they are paragraph-wide.
        ]

        # Initialize.
        self.update_format()
        self.update_title()
        self.hideAllButTextEdit(False)
        self.show()

    def block_signals(self, objects, b):
        for o in objects:
            o.blockSignals(b)

    def update_format(self):
        """
        Update the font format toolbar/actions when a new text selection is made. This is neccessary to keep
        toolbars/etc. in sync with the current edit state.
        :return:
        """
        # Disable signals for all format widgets, so changing values here does not trigger further formatting.
        self.block_signals(self._format_actions, True)

        self.fonts.setCurrentFont(self.editor.currentFont())
        # Nasty, but we get the font-size as a float but want it was an int
        self.fontsize.setCurrentText(str(int(self.editor.fontPointSize())))

        self.italic_action.setChecked(self.editor.fontItalic())
        self.underline_action.setChecked(self.editor.fontUnderline())
        self.bold_action.setChecked(self.editor.fontWeight() == QFont.Bold)

        self.alignl_action.setChecked(self.editor.alignment() == Qt.AlignLeft)
        self.alignc_action.setChecked(
            self.editor.alignment() == Qt.AlignCenter)
        self.alignr_action.setChecked(self.editor.alignment() == Qt.AlignRight)
        self.alignj_action.setChecked(
            self.editor.alignment() == Qt.AlignJustify)

        self.block_signals(self._format_actions, False)

    def dialog_critical(self, s):
        dlg = QMessageBox(self)
        dlg.setText(s)
        dlg.setIcon(QMessageBox.Critical)
        dlg.show()

    def file_open(self):
        path, _ = QFileDialog.getOpenFileName(
            self, "Open file", "",
            "HTML documents (*.html);Text documents (*.txt);All files (*.*)")

        try:
            with open(path, 'rU') as f:
                text = f.read()

        except Exception as e:
            self.dialog_critical(str(e))

        else:
            self.path = path
            # Qt will automatically try and guess the format as txt/html
            self.editor.setText(text)
            self.update_title()

    def file_save(self):
        if self.path is None:
            # If we do not have a path, we need to use Save As.
            return self.file_saveas()

        text = self.editor.toHtml() if splitext(
            self.path) in HTML_EXTENSIONS else self.editor.toPlainText()

        try:
            with open(self.path, 'w') as f:
                f.write(text)

        except Exception as e:
            self.dialog_critical(str(e))

    def file_saveas(self):
        path, _ = QFileDialog.getSaveFileName(
            self, "Save file", "",
            "HTML documents (*.html);Text documents (*.txt);All files (*.*)")

        if not path:
            # If dialog is cancelled, will return ''
            return

        text = self.editor.toHtml() if splitext(
            path) in HTML_EXTENSIONS else self.editor.toPlainText()

        try:
            with open(path, 'w') as f:
                f.write(text)

        except Exception as e:
            self.dialog_critical(str(e))

        else:
            self.path = path
            self.update_title()

    def file_print(self):
        dlg = QPrintDialog()
        if dlg.exec_():
            self.editor.print_(dlg.printer())

    def update_title(self):
        self.setWindowTitle(
            "%s - Megasolid Idiom" %
            (os.path.basename(self.path) if self.path else "Untitled"))

    def edit_toggle_wrap(self):
        self.editor.setLineWrapMode(1 if self.editor.lineWrapMode() ==
                                    0 else 0)

    def hideAllButTextEdit(self, bShow):
        if (not bShow):
            # hide all but text
            self.format_toolbar.hide()
            self.file_toolbar.hide()
            self.edit_toolbar.hide()
            self.menuWidget().hide()
            self.status.hide()
            pass
        else:
            # show all
            self.format_toolbar.show()
            self.file_toolbar.show()
            self.edit_toolbar.show()
            self.menuWidget().show()
            self.status.show()
            pass
Example #26
0
class MainWindow(QMainWindow):
    """

    """
    def __init__(self):
        super(MainWindow, self).__init__()

        self.menu_bar = QMenuBar()

        self.file_menu = QMenu("文件(&F)", self.menu_bar)
        self.open_action = QAction("打开(O)", self.file_menu)
        self.open_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_O))
        self.search_action = QAction("搜索(F)", self.file_menu)
        self.search_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_F))
        self.search_action.triggered.connect(self.show_search_widget)
        self.close_action = QAction("关闭(C)", self.file_menu)
        self.close_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_C))
        self.close_action.triggered.connect(self.answer_close_action_triggered)
        self.quit_action = QAction("退出(Q)", self.file_menu)
        self.quit_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Q))
        self.quit_action.triggered.connect(lambda: sys.exit())

        self.play_menu = QMenu("播放(&L)", self.menu_bar)
        self.play_action = QAction("播放(P)", self.play_menu)
        self.stop_action = QAction("停止(S)", self.play_menu)
        self.pause_action = QAction("暂停(P)", self.play_menu)
        self.resume_action = QAction("恢复(R)", self.play_menu)
        self.rate_action = QAction("倍速(R)", self.play_menu)
        self.jump_action = QAction("跳转(J)", self.play_menu)
        self.mute_action = QAction("静音(M)", self.play_menu)
        self.volume_up_action = QAction("增大音量(U)", self.play_menu)
        self.volume_down_action = QAction("减小音量(D)", self.play_menu)

        self.enhance_menu = QMenu("增强(&E)", self.menu_bar)
        self.skin_menu = QMenu("换肤", self.enhance_menu)
        self.dark_skin_action = QAction("暗黑模式", self.skin_menu)
        self.dark_skin_action.setCheckable(True)
        self.dark_skin_action.triggered.connect(
            self.answer_dark_skin_action_triggered)
        self.white_skin_action = QAction("纯白模式", self.skin_menu)
        self.white_skin_action.setCheckable(True)
        self.white_skin_action.triggered.connect(
            self.answer_white_skin_action_triggered)
        self.blue_skin_action = QAction("浅蓝模式", self.skin_menu)
        self.blue_skin_action.setCheckable(True)
        self.blue_skin_action.triggered.connect(
            self.answer_blue_skin_action_triggered)
        self.skin_action_group = QActionGroup(self)
        self.skin_action_group.addAction(self.dark_skin_action)
        self.skin_action_group.addAction(self.white_skin_action)
        self.skin_action_group.addAction(self.blue_skin_action)

        self.language_menu = QMenu("语言", self.enhance_menu)
        self.zh_CN_action = QAction("简体中文", self.language_menu)
        self.zh_CN_action.setCheckable(True)
        self.zh_CN_action.triggered.connect(self.answer_zh_CN_action_triggered)
        self.en_action = QAction("English", self.language_menu)
        self.en_action.setCheckable(True)
        self.en_action.triggered.connect(self.answer_en_action_triggered)
        self.language_action_group = QActionGroup(self)
        self.language_action_group.addAction(self.zh_CN_action)
        self.language_action_group.addAction(self.en_action)

        self.font_action = QAction("字体(F)", self.enhance_menu)
        self.font_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_S))
        self.font_action.triggered.connect(self.answer_font_action_triggered)
        self.hide_action = QAction("隐藏(V)", self.enhance_menu)
        self.hide_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_V))
        self.hide_action.setCheckable(True)
        self.hide_action.triggered.connect(self.answer_hide_action_triggered)

        self.tool_menu = QMenu("工具(&T)", self.menu_bar)
        self.screenshot_action = QAction("截图(J)", self.tool_menu)
        self.screenshot_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_J))
        self.screenshot_action.triggered.connect(
            self.answer_screenshot_action_triggered)
        self.gif_action = QAction("动图(G)", self.tool_menu)
        self.gif_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_G))
        self.gif_action.triggered.connect(self.answer_gif_action_triggered)
        self.screen_record_action = QAction("录屏(L)", self.tool_menu)
        self.screen_record_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_L))
        self.screen_record_action.triggered.connect(
            self.answer_screen_record_action_triggered)

        self.help_menu = QMenu("帮助(&H)", self.menu_bar)
        self.help_action = QAction("帮助文档(H)", self.help_menu)
        self.help_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_H))
        self.help_action.triggered.connect(self.answer_help_action_triggered)
        self.change_log_action = QAction("更新日志(U)", self.help_menu)
        self.change_log_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_U))
        self.change_log_action.triggered.connect(
            self.answer_change_log_action_triggered)
        self.check_version_action = QAction("检查版本(C)", self.help_menu)
        self.check_version_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_C))
        self.check_version_action.triggered.connect(
            self.answer_check_version_action_triggered)
        self.about_action = QAction("关于软件(A)", self.help_menu)
        self.about_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_A))
        self.about_action.triggered.connect(self.answer_about_action_triggered)

        self.tool_bar = QToolBar()
        self.tool_bar.setFloatable(False)
        self.tool_bar.setMovable(True)
        self.tool_bar.setIconSize(QSize(32, 32))
        # self.tool_bar.setStyleSheet("QToolBar{border: 1px solid #313335; spacing:5px; }")
        self.addToolBar(Qt.TopToolBarArea, self.tool_bar)
        self.search_tool_action = QAction("", self.tool_bar)
        self.search_tool_action.setToolTip("信息搜索")
        self.search_tool_action.setIcon(
            QIcon("./resources/img/[email protected]"))
        self.search_widget = SearchWidget()
        self.search_tool_action.triggered.connect(self.show_search_widget)
        self.search_widget.watch_live_signal.connect(
            self.answer_watch_live_signal)

        self.attention_tool_action = QAction("", self.tool_bar)
        self.attention_tool_action.setToolTip("历史关注")
        self.attention_tool_action.setIcon(
            QIcon("./resources/img/[email protected]"))
        # self.attention_tool_action.triggered.connect(self.show_search_widget)

        self.pure_tool_action = QAction("", self.tool_bar)
        self.pure_tool_action.setToolTip("纯净模式")
        self.pure_tool_action.setIcon(
            QIcon("./resources/img/[email protected]"))
        # self.pure_tool_action.triggered.connect(self.show_search_widget)

        self.nlp_tool_action = QAction("", self.tool_bar)
        self.nlp_tool_action.setToolTip("智能字幕")
        self.nlp_tool_action.setIcon(QIcon("./resources/img/[email protected]"))
        # self.nlp_tool_action.triggered.connect(self.show_search_widget)

        self.note_tool_action = QAction("", self.tool_bar)
        self.note_tool_action.setToolTip("边看边记")
        self.note_tool_action.setIcon(
            QIcon("./resources/img/[email protected]"))
        # self.note_tool_action.triggered.connect(self.show_search_widget)

        self.live_widget = LiveWidget()

        self.init_ui()
        self._init_cfg()

    def init_ui(self):
        """

        :return:
        """
        # 菜单栏
        self.file_menu.addAction(self.open_action)
        self.file_menu.addAction(self.search_action)
        self.file_menu.addSeparator()
        self.file_menu.addAction(self.close_action)
        self.file_menu.addAction(self.quit_action)

        self.play_menu.addAction(self.play_action)
        self.play_menu.addAction(self.stop_action)
        self.play_menu.addAction(self.pause_action)
        self.play_menu.addAction(self.resume_action)
        self.play_menu.addSeparator()
        self.play_menu.addAction(self.rate_action)
        self.play_menu.addAction(self.jump_action)
        self.play_menu.addSeparator()
        self.play_menu.addAction(self.mute_action)
        self.play_menu.addAction(self.volume_up_action)
        self.play_menu.addAction(self.volume_down_action)

        self.enhance_menu.addMenu(self.skin_menu)
        self.skin_menu.addAction(self.dark_skin_action)
        self.skin_menu.addAction(self.white_skin_action)
        self.skin_menu.addAction(self.blue_skin_action)
        self.enhance_menu.addMenu(self.language_menu)
        self.language_menu.addAction(self.zh_CN_action)
        self.language_menu.addAction(self.en_action)
        self.enhance_menu.addAction(self.font_action)
        self.enhance_menu.addSeparator()
        self.enhance_menu.addAction(self.hide_action)

        self.tool_menu.addAction(self.screenshot_action)
        self.tool_menu.addAction(self.gif_action)
        self.tool_menu.addAction(self.screen_record_action)
        self.tool_menu.addSeparator()

        self.help_menu.addAction(self.help_action)
        self.help_menu.addAction(self.change_log_action)
        self.help_menu.addAction(self.check_version_action)
        self.help_menu.addSeparator()
        self.help_menu.addAction(self.about_action)

        self.menu_bar.addMenu(self.file_menu)
        self.menu_bar.addMenu(self.play_menu)
        self.menu_bar.addMenu(self.enhance_menu)
        self.menu_bar.addMenu(self.tool_menu)
        self.menu_bar.addMenu(self.help_menu)

        self.setMenuBar(self.menu_bar)

        # 工具栏
        self.tool_bar.addAction(self.search_tool_action)
        self.tool_bar.addSeparator()
        self.tool_bar.addAction(self.attention_tool_action)
        self.tool_bar.addSeparator()
        self.tool_bar.addAction(self.pure_tool_action)
        self.tool_bar.addSeparator()
        self.tool_bar.addAction(self.nlp_tool_action)
        self.tool_bar.addSeparator()
        self.tool_bar.addAction(self.note_tool_action)
        self.tool_bar.addSeparator()

        # 显示区域
        self.setCentralWidget(self.live_widget)

        self.set_window_info()

    def _init_cfg(self):
        """

        :return:
        """
        # 工具栏可见性
        visible = retrieve_content("preferences", "tool_bar_visible")
        if visible == "True":
            self.hide_action.setChecked(False)
            self.hide_action.triggered.emit(False)
        else:
            self.hide_action.setChecked(True)
            self.hide_action.triggered.emit(True)

        # 工具栏位置
        position = retrieve_content("preferences", "tool_bar_position")

        # 皮肤设置
        skin = retrieve_content("preferences", "skin")
        if skin == "dark":
            self.dark_skin_action.setChecked(True)
            self.dark_skin_action.triggered.emit()
        elif skin == "white":
            self.white_skin_action.setChecked(True)
            self.white_skin_action.triggered.emit()
        else:
            self.blue_skin_action.setChecked(True)
            self.blue_skin_action.triggered.emit()

        # 字体设置
        font_family = retrieve_content("preferences", "font-family")
        font_style = retrieve_content("preferences", "font-style")
        font_size = retrieve_content("preferences", "font-size")
        font = QFont()
        font.setFamily(font_family)
        font.setStyleName(font_style)
        font.setPointSize(int(font_size))
        qApp.setFont(font)
        qApp.processEvents()

    def set_window_info(self):
        """

        :return:
        """
        desktop_widget = QDesktopWidget()
        screen_rect = desktop_widget.screenGeometry()
        self.setGeometry(screen_rect)
        title = retrieve_content("software_info",
                                 "software_name") + " " + retrieve_content(
                                     "software_info", "software_version")
        self.setWindowTitle(title)
        self.setWindowIcon(QIcon('./resources/img/[email protected]'))
        self.showMaximized()

    def show_search_widget(self):
        """

        :return:
        """
        width, height = get_window_center_point(self.search_widget)
        self.search_widget.move(width, height)
        self.search_widget.exec_()

    @staticmethod
    def answer_help_action_triggered():
        """

        :return:
        """
        desktop_services = QDesktopServices()
        desktop_services.openUrl(
            QUrl(
                "https://github.com/parzulpan/real-live/blob/master/resources/Help.md"
            ))

    @staticmethod
    def answer_change_log_action_triggered():
        """

        :return:
        """
        desktop_services = QDesktopServices()
        desktop_services.openUrl(
            QUrl(
                "https://github.com/parzulpan/real-live/blob/master/resources/ChangeLog.md"
            ))

    @staticmethod
    def answer_check_version_action_triggered():
        """

        :return:
        """
        # TODO: 获取GitHub API 进行检查并弹窗
        desktop_services = QDesktopServices()
        desktop_services.openUrl(
            QUrl("https://github.com/parzulpan/real-live/releases"))

    @staticmethod
    def answer_about_action_triggered():
        """

        :return:
        """
        about_widget = AboutWidget()
        width, height = get_window_center_point(about_widget)
        about_widget.move(width, height)
        about_widget.exec_()

    def answer_watch_live_signal(self, url):
        """

        :param url:
        :return:
        """
        # self.live_widget.vlc_widget.get_player()
        self.live_widget.vlc_widget.play_url(url)
        self.live_widget.set_player_widget(True)

    def answer_close_action_triggered(self):
        """

        :return:
        """
        self.live_widget.set_player_widget(False)
        # self.live_widget.vlc_widget.get_player()
        # self.live_widget.vlc_widget.release_player()
        self.live_widget.vlc_widget.stop()

    def answer_hide_action_triggered(self, checked):
        """

        :param checked:
        :return:
        """
        if checked:
            self.tool_bar.hide()
            update_contents("preferences", "tool_bar_visible", "False")
        else:
            self.tool_bar.show()
            update_contents("preferences", "tool_bar_visible", "True")

    def answer_tool_bar_top_level_changed(self, area):
        """

        :return:
        """
        print("top_level_changed")
        print(area)
        if self.tool_bar.allowedAreas() == Qt.LeftToolBarArea:
            print("LeftToolBarArea")
        elif self.tool_bar.allowedAreas() == Qt.RightToolBarArea:
            print("RightToolBarArea")
        elif self.tool_bar.allowedAreas() == Qt.BottomToolBarArea:
            print("BottomToolBarArea")
        elif self.tool_bar.allowedAreas() == Qt.TopToolBarArea:
            print("TopToolBarArea")
        else:
            print("else")

    @staticmethod
    def answer_screenshot_action_triggered():
        """

        :return:
        """
        # dll = windll.LoadLibrary('./bin/OEScreenshot.dll')
        # dll = WinDLL('./bin/OEScreenshot.dll')
        # dll = cdll.LoadLibrary('./bin/OEScreenshot.dll')
        dll = CDLL('./bin/screenshot/OEScreenshot.dll')
        print(dll)

    def answer_gif_action_triggered(self):
        """

        :return:
        """
        pass

    def answer_screen_record_action_triggered(self):
        """

        :return:
        """
        pass

    @staticmethod
    def answer_dark_skin_action_triggered(checked):
        """

        :param checked:
        :return:
        """
        with open("./resources/qss/dark.qss", "r", encoding="utf-8") as f:
            qss = f.read()
            qApp.setStyleSheet(qss)
        update_contents("preferences", "skin", "dark")

    @staticmethod
    def answer_white_skin_action_triggered(checked):
        """

        :param checked:
        :return:
        """
        with open("./resources/qss/white.qss", "r", encoding="utf-8") as f:
            qss = f.read()
            qApp.setStyleSheet(qss)
        update_contents("preferences", "skin", "white")

    @staticmethod
    def answer_blue_skin_action_triggered(checked):
        """

        :param checked:
        :return:
        """
        with open("./resources/qss/blue.qss", "r", encoding="utf-8") as f:
            qss = f.read()
            qApp.setStyleSheet(qss)
        update_contents("preferences", "skin", "blue")

    def answer_font_action_triggered(self, checked):
        """

        :param checked:
        :return:
        """
        font = qApp.font()
        font, changed = QFontDialog().getFont(font, self, caption="字体设置")
        if changed:
            qApp.setFont(font)
            qApp.processEvents()
            update_contents("preferences", "font-family", font.family())
            update_contents("preferences", "font-style", font.styleName())
            update_contents("preferences", "font-size", str(font.pointSize()))

    def answer_zh_CN_action_triggered(self, checked):
        """

        :param checked:
        :return:
        """
        pass

    def answer_en_action_triggered(self, checked):
        """

        :param checked:
        :return:
        """
        pass

    def closeEvent(self, event) -> None:
        """

        :param event:
        :return:
        """
        print("closeEvent")
        self.live_widget.vlc_widget.release_player()
Example #27
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()
Example #28
0
class MultiLayerSelect:
    """QGIS Plugin Implementation."""
    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value("locale/userLocale")[0:2]
        locale_path = os.path.join(self.plugin_dir, "i18n",
                                   "MultiLayerSelect_{}.qm".format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        # Init settings
        self.settings = QSettings()
        self.settings.beginGroup("plugins/multilayerselect")

    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # pylint: disable=invalid-name

        return QCoreApplication.translate("MultiLayerSelect", message)

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""
        # pylint: disable=invalid-name

        # Create settings dialog
        self.settings_dialog = SettingsDialog(self.settings,
                                              self.iface.mainWindow())
        self.expression_dialog = None

        try:
            QgsProject.instance().selectionColorChanged.connect(
                self.on_color_changed)
            QgsProject.instance().selectionColorChanged.connect(
                self.settings_dialog.on_project_color_changed)
        except AttributeError:  # QGIS < 3.10
            self.settings_dialog.colorChanged.connect(self.on_color_changed)
            QgsProject.instance().readProject.connect(
                self.settings_dialog.on_project_color_changed)

        self.settings_dialog.settingsChanged.connect(self.on_settings_changed)

        self.toolbar = QToolBar("Multilayer Select", self.iface.mainWindow())
        self.toolbar.setObjectName("MultiSelectToolbar")

        self.about_action = QAction(
            QIcon(":/plugins/multilayerselect/icons/about.svg"),
            self.tr("About"),
            parent=self.iface.mainWindow(),
        )
        self.about_action.triggered.connect(self.show_about)

        self.settings_action = QAction(
            QIcon(":/images/themes/default/console/iconSettingsConsole.svg"),
            self.tr("Settings"),
            parent=self.iface.mainWindow(),
        )
        self.settings_action.setObjectName("actionMultiLayerSelectSettings")
        self.settings_action.setToolTip(
            self.tr("<b>Multilayer Select Settings</b>"))

        self.settings_action.triggered.connect(self.show_settings)

        self.plugin_menu = self.iface.pluginMenu().addMenu(
            QIcon(":/plugins/multilayerselect/icons/icon.svg"),
            "Multilayer Select")
        self.plugin_menu.addAction(self.about_action)
        self.plugin_menu.addAction(self.settings_action)

        self.selection_tool_button = QToolButton(self.toolbar)
        self.selection_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
        self.selection_tool_button.setObjectName("selectionToolButton")

        self.advanced_selection_tool_button = QToolButton(self.toolbar)
        self.advanced_selection_tool_button.setPopupMode(
            QToolButton.MenuButtonPopup)
        self.advanced_selection_tool_button.setObjectName(
            "advancedSelectionToolButton")

        self.select_rect_tool = MultiSelectionAreaTool(self.iface.mapCanvas())
        self.select_polygon_tool = MultiSelectionPolygonTool(
            self.iface.mapCanvas())
        self.select_freehand_tool = MultiSelectionFreehandTool(
            self.iface.mapCanvas())
        self.select_radius_tool = MultiSelectionRadiusTool(
            self.iface.mapCanvas())

        self.actions_settings = [
            SelectAction(
                text=self.tr("Select Features"),
                tooltip=self.tr(
                    "<b>Select Features by area or single click</b>"),
                icon=":/plugins/multilayerselect/icons/selectRectangle.svg",
                objectname="actionMultiSelectByRectangle",
                tool=self.select_rect_tool,
            ),
            SelectAction(
                text=self.tr("Select Features by Polygon"),
                icon=":/plugins/multilayerselect/icons/selectPolygon.svg",
                objectname="actionMultiSelectByPolygon",
                tool=self.select_polygon_tool,
            ),
            SelectAction(
                text=self.tr("Select Features by Freehand"),
                icon=":/plugins/multilayerselect/icons/selectFreehand.svg",
                objectname="actionMultiSelectByFreehand",
                tool=self.select_freehand_tool,
            ),
            SelectAction(
                text=self.tr("Select Features by Radius"),
                icon=":/plugins/multilayerselect/icons/selectRadius.svg",
                objectname="actionMultiSelectByRadius",
                tool=self.select_radius_tool,
            ),
        ]

        def on_select_tool(tool, action):
            self.selection_tool_button.setDefaultAction(action)
            if self.embedded_selection_tool_button:
                self.embedded_selection_tool_button.setDefaultAction(action)
            self.iface.mapCanvas().setMapTool(tool)

        self.select_actions = []

        for select_action in self.actions_settings:
            action = QAction(select_action.text)
            action.setToolTip(select_action.tooltip)
            action.setObjectName(select_action.objectname)
            action.setCheckable(True)
            select_action.tool.setAction(action)

            action.triggered.connect(
                partial(on_select_tool, select_action.tool, action))
            self.selection_tool_button.addAction(action)
            if not self.selection_tool_button.defaultAction():
                self.selection_tool_button.setDefaultAction(action)
            self.select_actions.append(action)

        self.toolbar.addWidget(self.selection_tool_button)

        self.select_all_action = QAction(
            self.tr("Select all features from all layers"), )
        self.select_all_action.setToolTip("<b>{}</b>".format(
            self.select_all_action.text()))
        self.select_all_action.setObjectName("actionMultiSelectAll")
        self.select_all_action.triggered.connect(self.select_all)
        self.advanced_selection_tool_button.addAction(self.select_all_action)
        self.advanced_selection_tool_button.setDefaultAction(
            self.select_all_action)

        self.invert_all_action = QAction(
            self.tr("Invert selection for all layers"), )
        self.invert_all_action.setToolTip("<b>{}</b>".format(
            self.invert_all_action.text()))
        self.invert_all_action.setObjectName("actionMultiSelectInvert")
        self.invert_all_action.triggered.connect(self.invert_all)
        self.advanced_selection_tool_button.addAction(self.invert_all_action)

        self.select_by_expr_action = QAction(
            QIcon(":/images/themes/default/mIconExpressionSelect.svg"),
            self.tr("Select Features by Expression..."),
        )
        self.select_by_expr_action.setToolTip("<b>{}</b>".format(
            self.select_by_expr_action.text()))
        self.select_by_expr_action.setObjectName("actionMultiSelectExpr")
        self.select_by_expr_action.triggered.connect(self.select_by_expression)
        self.advanced_selection_tool_button.addAction(
            self.select_by_expr_action)

        self.toolbar.addWidget(self.advanced_selection_tool_button)

        self.deselect_all_action = QAction(
            self.tr("Deselect features from all layers"))
        self.deselect_all_action.setToolTip("<b>{}</b>".format(
            self.deselect_all_action.text()))
        self.deselect_all_action.setObjectName("actionDeselectAll")
        self.deselect_all_action.triggered.connect(self.deselect_all)
        self.toolbar.addAction(self.deselect_all_action)

        self.toolbar.addAction(self.settings_action)

        self.iface.mainWindow().addToolBar(self.toolbar)

        # Embedded actions
        self.embedded_selection_tool_button_action = None
        self.embedded_selection_tool_button = None
        self.embedded_advanced_tool_button_action = None
        self.embedded_advanced_tool_button = None

        self.on_color_changed()
        self.on_settings_changed()

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""

        # Delete Settings dialog
        self.settings_dialog.deleteLater()

        # Remove menu from plugins menu
        self.iface.pluginMenu().removeAction(self.plugin_menu.menuAction())

        self.select_freehand_tool.deleteLater()
        self.select_polygon_tool.deleteLater()
        self.select_radius_tool.deleteLater()
        self.select_rect_tool.deleteLater()

        self.iface.mainWindow().removeToolBar(self.toolbar)
        self.toolbar.deleteLater()

        self.replace_default_action(False)

        try:
            QgsProject.instance().selectionColorChanged.disconnect(
                self.on_color_changed)
            QgsProject.instance().selectionColorChanged.disconnect(
                self.settings_dialog.on_project_color_changed)

        except AttributeError:  # QGIS < 3.10
            pass

    def show_about(self):
        """ Show the about dialog """

        # Used to display plugin icon in the about message box
        bogus = QWidget(self.iface.mainWindow())
        bogus.setWindowIcon(QIcon(":/plugins/multilayerselect/icons/icon.svg"))

        cfg = configparser.ConfigParser()
        cfg.read(os.path.join(os.path.dirname(__file__), "metadata.txt"))
        version = cfg.get("general", "version")
        homepage = cfg.get("general", "homepage")
        tracker = cfg.get("general", "tracker")
        repository = cfg.get("general", "repository")

        QMessageBox.about(
            bogus,
            self.tr("About Multilayer Select"),
            "<b>Version</b> {3}<br><br>"
            "<b>{4}</b> : <a href={0}>GitHub</a><br>"
            "<b>{5}</b> : <a href={1}>GitHub</a><br>"
            "<b>{6}</b> : <a href={2}>GitHub Pages</a>".format(
                repository,
                tracker,
                homepage,
                version,
                self.tr("Source code"),
                self.tr("Report issues"),
                self.tr("Documentation"),
            ),
        )

        bogus.deleteLater()

    def show_settings(self):
        """ Show the settings dialog """

        geometry = self.settings_dialog.geometry()

        # The first time the dialog is shown (y=0), explicitely set its geometry
        # which allow to restore the geometry on subsequent calls
        if geometry.y() == 0:
            self.settings_dialog.show()
            self.settings_dialog.raise_()
            self.settings_dialog.setGeometry(self.settings_dialog.geometry())
            return

        self.settings_dialog.show()
        self.settings_dialog.raise_()

    def on_color_changed(self):
        """ Called when the selection color has changed. Replace every icon """
        color = self.iface.mapCanvas().selectionColor()
        color = QColor.fromHsv(color.hue(),
                               color.saturation() * 0.9,
                               color.value() * 0.95, color.alpha())
        for i in range(len(self.select_actions)):
            path = self.actions_settings[i].icon
            icon = create_icon(path, color)
            self.select_actions[i].setIcon(icon)

        icon = create_icon(":/plugins/multilayerselect/icons/deselectAll.svg",
                           color)
        self.deselect_all_action.setIcon(icon)

        icon = select_all_icon(color)
        self.select_all_action.setIcon(icon)

        icon = invert_selection_icon(color)
        self.invert_all_action.setIcon(icon)

        icon = expression_select_icon(color)
        self.select_by_expr_action.setIcon(icon)

    def on_settings_changed(self):
        """ Called when any setting has changed """
        if self.settings.value("show_settings", True, bool):
            self.toolbar.addAction(self.settings_action)
        else:
            self.toolbar.removeAction(self.settings_action)

        self.replace_default_action(
            self.settings.value("replace_actions", False, bool))

    def deselect_all(self):
        """ Deselect every feature """
        for layer in QgsProject.instance().mapLayers().values():
            if isinstance(layer, QgsVectorLayer):
                layer.removeSelection()
        update_status_message()

    def select_all(self):
        """ Select all the features from every vector layer """
        for layer in vector_layers():
            layer.selectAll()
        self.advanced_selection_tool_button.setDefaultAction(
            self.select_all_action)

        if self.embedded_advanced_tool_button:
            self.embedded_advanced_tool_button.setDefaultAction(
                self.select_all_action)

        update_status_message()

    def invert_all(self):
        """ Invert the selection of every vector layer """
        for layer in vector_layers():
            layer.invertSelection()
        self.advanced_selection_tool_button.setDefaultAction(
            self.invert_all_action)

        if self.embedded_advanced_tool_button:
            self.embedded_advanced_tool_button.setDefaultAction(
                self.invert_all_action)
        update_status_message()

    def select_by_expression(self):
        """ Create and open the Expression builder dialog"""

        if self.expression_dialog:
            self.expression_dialog.deleteLater()
        self.expression_dialog = MultiLayerSelectionExpressionBuilder()
        self.expression_dialog.show()

        self.advanced_selection_tool_button.setDefaultAction(
            self.select_by_expr_action)

        if self.embedded_advanced_tool_button:
            self.embedded_advanced_tool_button.setDefaultAction(
                self.select_by_expr_action)
        update_status_message()

    def replace_default_action(self, value):
        """Replace the default QGIS selection action with the multilayer ones

        Args:
            value (bool): If true, replace the actions, else put the multi actions
                inside their own toolbar
        """

        toolbar = self.iface.attributesToolBar()
        main_window = self.iface.mainWindow()
        main_window.findChild(QAction, "ActionSelect").setVisible(not value)
        main_window.findChild(QAction, "ActionSelection").setVisible(not value)
        main_window.findChild(QAction,
                              "mActionDeselectAll").setVisible(not value)

        actiontable = main_window.findChild(QAction, "mActionOpenTable")
        actionform = main_window.findChild(QAction, "mActionSelectByForm")

        # Remove the multi layer tool buttons from the QGIS attribute toolbar
        toolbar.removeAction(self.embedded_selection_tool_button_action)
        toolbar.removeAction(self.embedded_advanced_tool_button_action)

        if value:

            # Create the QToolButtons that will be added to the default toolbar
            self.embedded_selection_tool_button = QToolButton()
            self.embedded_selection_tool_button.setPopupMode(
                QToolButton.MenuButtonPopup)

            # Add selection tools action to the button (Rect, Polygon, Radius, Freehand)
            self.embedded_selection_tool_button.addActions(self.select_actions)
            self.embedded_selection_tool_button.setDefaultAction(
                self.select_actions[0])

            self.embedded_advanced_tool_button = QToolButton()
            self.embedded_advanced_tool_button.setPopupMode(
                QToolButton.MenuButtonPopup)

            # Add Invert, Select All, Select from value and Select from expressions
            self.embedded_advanced_tool_button.addAction(
                self.select_all_action)
            self.embedded_advanced_tool_button.setDefaultAction(
                self.select_all_action)
            self.embedded_advanced_tool_button.addAction(
                self.invert_all_action)
            self.embedded_advanced_tool_button.addAction(
                self.select_by_expr_action)
            self.embedded_advanced_tool_button.addAction(actionform)

            self.embedded_selection_tool_button_action = toolbar.insertWidget(
                actiontable, self.embedded_selection_tool_button)
            self.embedded_advanced_tool_button_action = toolbar.insertWidget(
                actiontable, self.embedded_advanced_tool_button)

            # Add the deselect all action
            toolbar.insertAction(actiontable, self.deselect_all_action)

            # If the settigns is enabled add the show settings action
            if self.settings.value("show_settings", True, bool):
                toolbar.insertAction(actiontable, self.settings_action)
            else:
                toolbar.removeAction(self.settings_action)
            self.toolbar.hide()

        else:

            # Remove the multi actions from the default toolbar, and show
            # the custom toolbar
            self.embedded_selection_tool_button = None
            self.embedded_advanced_tool_button = None
            toolbar.removeAction(self.deselect_all_action)
            toolbar.removeAction(self.settings_action)
            self.toolbar.show()
Example #29
0
File: info.py Project: dik-b/bauh
class InfoDialog(QDialog):
    def __init__(self, app: dict, icon_cache: MemoryCache, i18n: dict,
                 screen_size: QSize()):
        super(InfoDialog, self).__init__()
        self.setWindowTitle(str(app['__app__']))
        self.screen_size = screen_size
        self.i18n = i18n
        layout = QVBoxLayout()
        self.setLayout(layout)

        self.toolbar_field = QToolBar()
        self.bt_back = QPushButton(i18n['back'].capitalize())
        self.bt_back.clicked.connect(self.back_to_info)
        self.toolbar_field.addWidget(self.bt_back)
        self.layout().addWidget(self.toolbar_field)
        self.toolbar_field.hide()

        # shows complete field string
        self.text_field = QPlainTextEdit()
        self.layout().addWidget(self.text_field)
        self.text_field.hide()

        self.gbox_info = QGroupBox()
        self.gbox_info.setMaximumHeight(self.screen_size.height() -
                                        self.screen_size.height() * 0.1)
        self.gbox_info_layout = QGridLayout()
        self.gbox_info.setLayout(self.gbox_info_layout)

        layout.addWidget(self.gbox_info)

        # THERE ARE CRASHES WITH SOME RARE ICONS ( like insomnia ). IT CAN BE A QT BUG. IN THE MEANTIME, ONLY THE TYPE ICON WILL BE RENDERED
        #
        # icon_data = icon_cache.get(app['__app__'].model.icon_url)
        #
        # if icon_data and icon_data.get('icon'):
        #     self.setWindowIcon(icon_data.get('icon'))
        self.setWindowIcon(QIcon(app['__app__'].model.get_type_icon_path()))

        for idx, attr in enumerate(sorted(app.keys())):
            if attr not in IGNORED_ATTRS and app[attr]:
                i18n_key = app['__app__'].model.get_type().lower(
                ) + '.info.' + attr.lower()

                if isinstance(app[attr], list):
                    val = ' '.join([str(e).strip() for e in app[attr] if e])
                    show_val = '\n'.join(
                        ['* ' + str(e).strip() for e in app[attr] if e])
                else:
                    val = str(app[attr]).strip()
                    show_val = val

                i18n_val = i18n.get('{}.{}'.format(i18n_key, val.lower()))

                if i18n_val:
                    val = i18n_val
                    show_val = val

                text = QLineEdit()
                text.setToolTip(show_val)
                text.setText(val)
                text.setCursorPosition(0)
                text.setStyleSheet("width: 400px")
                text.setReadOnly(True)

                label = QLabel(
                    i18n.get(i18n_key, i18n.get(attr.lower(),
                                                attr)).capitalize())
                label.setStyleSheet("font-weight: bold")

                self.gbox_info_layout.addWidget(label, idx, 0)
                self.gbox_info_layout.addWidget(text, idx, 1)
                self._gen_show_button(idx, show_val)

        self.adjustSize()

    def _gen_show_button(self, idx: int, val):
        def show_full_field():
            self.gbox_info.hide()
            self.toolbar_field.show()
            self.text_field.show()
            self.text_field.setPlainText(val)

        bt_full_field = QPushButton(self.i18n['show'].capitalize())
        bt_full_field.clicked.connect(show_full_field)
        self.gbox_info_layout.addWidget(bt_full_field, idx, 2)

    def back_to_info(self):
        self.text_field.setPlainText("")
        self.text_field.hide()
        self.toolbar_field.hide()
        self.gbox_info.show()
Example #30
0
class ImageViewer(QMainWindow):
	def __init__(self, db):
		super(ImageViewer, self).__init__()

		self.db = db
		self.currentIndex = -1
		self.files = []

		self._init_widgets()

	def _init_widgets(self):
		#self.toolbar = AutoHideToolBar()
		self.toolbar = QToolBar()
		self.addToolBar(self.toolbar)
		self.toolbar.hide()
		self.toolbar.addAction('Z 1:1').triggered.connect(self.doNormalZoom)
		self.toolbar.addAction('Z Fit').triggered.connect(self.doFitAllZoom)
		self.toolbar.addAction('Z FitExp').triggered.connect(self.doFitCutZoom)
		self.toolbar.addAction('Z x1.5').triggered.connect(self.zoom)
		self.toolbar.addAction('Z /1.5').triggered.connect(self.unzoom)

		self.toolbar.addAction('Copy tags').triggered.connect(self.copyPreviousTags)

		self.fullscreenAction = self.toolbar.addAction('Fullscreen')
		self.fullscreenAction.setCheckable(True)
		self.fullscreenAction.toggled.connect(self.setFullscreen)

		act = self.toolbar.addAction('Prev')
		act.setShortcut(QKeySequence(Qt.Key_Backspace))
		act.triggered.connect(self.showPreviousFile)
		act = self.toolbar.addAction('Next')
		act.setShortcut(QKeySequence(Qt.Key_Space))
		act.triggered.connect(self.showNextFile)
		#~ act.setShortcutContext(Qt.WidgetWithChildrenShortcut)

		self.tageditor = TagEditor(self.db)
		self.docktagger = AutoHideDock()
		self.docktagger.setWidget(self.tageditor)
		self.addDockWidget(Qt.LeftDockWidgetArea, self.docktagger)
		self.docktagger.hide()

		self.scrollview = ImageViewerCenter()
		self.scrollview.installEventFilter(self) ### !
		self.setCentralWidget(self.scrollview)

		self.scrollview.topZoneEntered.connect(self.toolbar.show)
		self.scrollview.topZoneLeft.connect(self.toolbar.hide)
		self.scrollview.leftZoneEntered.connect(self.docktagger.show)
		self.scrollview.leftZoneLeft.connect(self.docktagger.hide)

		#~ self.setWindowState(self.windowState() | Qt.WindowMaximized)

		'''
		self.qtagwl = QListWidget()
		self.qtagwl.setParent(self)
		self.qtagwl.hide()
		#self.qtagwl.setFixedSize(self.qtagwl.minimumSizeHint())
		self.qtagwl.setFrameShape(QFrame.NoFrame)
		self.qtagwl.setStyleSheet('QListWidget{background-color: rgba(255,255,255,200);}\n *{background-color:rgba(0,255,255,255);}')
		self.qtagwl.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)

		self.qthtimer = QTimer()
		self.connect(self.qthtimer, SIGNAL('timeout()'), self.qtaghide)
		'''

	def eventFilter(self, sview, ev):
		if ev.type() == QEvent.KeyPress:
			if ev.key() == Qt.Key_Escape:
				self.fullscreenAction.setChecked(False)
				return True
			elif ev.key() in [Qt.Key_PageUp, Qt.Key_Backspace]: # qactions
				self.showPreviousFile()
				return True
			elif ev.key() in [Qt.Key_PageDown, Qt.Key_Space]:
				self.showNextFile()
				return True
		return super(ImageViewer, self).eventFilter(sview, ev)

	@Slot()
	def doNormalZoom(self):
		self.scrollview.setZoomFactor(1.)

	@Slot()
	def doFitAllZoom(self):
		self.scrollview.setZoomMode(ZOOM_FITALL)

	@Slot()
	def doFitCutZoom(self):
		self.scrollview.setZoomMode(ZOOM_FITCUT)

	@Slot()
	def zoom(self):
		self.scrollview.multiplyZoomFactor(1.5)

	@Slot()
	def unzoom(self):
		self.scrollview.multiplyZoomFactor(1/1.5)

	def spawn(self, files, currentFile):
		self.files = files
		self.currentIndex = files.index(currentFile)

		self.setFile(currentFile)
		if self.isHidden():
			#~ self.setWindowState(self.windowState() | Qt.WindowMaximized)
			#~ self.show()
			self.fullscreenAction.setChecked(False)
			self.fullscreenAction.setChecked(True)
			#~ self.showMaximized()
		else:
			self.show()

	def setFile(self, file):
		self.tageditor.setFile(file)
		self.scrollview.setFile(file)

	@Slot()
	def copyPreviousTags(self):
		tags = self.db.find_tags_by_file(self.files[self.currentIndex - 1])
		self.db.tag_file(self.files[self.currentIndex], tags)
		self.tageditor.setFile(self.files[self.currentIndex])

	def setFullscreen(self, full):
		if full:
			self.showFullScreen()
		else:
			self.showNormal()

	@Slot()
	def showPreviousFile(self):
		if self.currentIndex > 0:
			self.currentIndex -= 1
			self.setFile(self.files[self.currentIndex])

	@Slot()
	def showNextFile(self):
		if self.currentIndex < len(self.files) - 1:
			self.currentIndex += 1
			self.setFile(self.files[self.currentIndex])
Example #31
0
class ImageViewer(QMainWindow):
	def __init__(self, db, *args, **kwargs):
		super(ImageViewer, self).__init__(*args, **kwargs)

		self.db = db
		self.currentIndex = -1
		self.files = []

		self._init_widgets()

	def _init_widgets(self):
		#self.toolbar = AutoHideToolBar()
		self.toolbar = QToolBar()
		self.addToolBar(self.toolbar)
		self.toolbar.hide()

		act = self.toolbar.addAction(QIcon.fromTheme('go-previous'), 'Previous')
		act.setShortcut(QKeySequence(Qt.Key_Backspace))
		act.triggered.connect(self.showPreviousFile)
		act = self.toolbar.addAction(QIcon.fromTheme('go-next'), 'Next')
		act.setShortcut(QKeySequence(Qt.Key_Space))
		act.triggered.connect(self.showNextFile)
		self.toolbar.addSeparator()

		self.toolbar.addAction(QIcon.fromTheme('zoom-original'), 'Z 1:1').triggered.connect(self.doNormalZoom)
		self.toolbar.addAction(QIcon.fromTheme('zoom-fit-best'), 'Z Fit').triggered.connect(self.doFitAllZoom)
		self.toolbar.addAction(QIcon.fromTheme('zoom-fit-best'), 'Z FitExp').triggered.connect(self.doFitCutZoom)
		self.toolbar.addAction(QIcon.fromTheme('zoom-in'), 'Z x1.5').triggered.connect(self.zoom)
		self.toolbar.addAction(QIcon.fromTheme('zoom-out'), 'Z /1.5').triggered.connect(self.unzoom)

		self.fullscreenAction = self.toolbar.addAction(QIcon.fromTheme('view-fullscreen'), 'Fullscreen')
		self.fullscreenAction.setCheckable(True)
		self.fullscreenAction.toggled.connect(self.setFullscreen)
		self.toolbar.addSeparator()

		self.toolbar.addAction('Copy tags').triggered.connect(self.copyPreviousTags)

		self.tageditor = TagEditor()
		self.tageditor.setDb(self.db)

		self.docktagger = AutoHideDock()
		self.docktagger.setWidget(self.tageditor)
		self.addDockWidget(Qt.LeftDockWidgetArea, self.docktagger)
		self.docktagger.hide()

		self.scrollview = ImageViewerCenter()
		self.scrollview.installEventFilter(self) ### !
		self.setCentralWidget(self.scrollview)

		self.scrollview.topZoneEntered.connect(self.toolbar.show)
		self.scrollview.topZoneLeft.connect(self.toolbar.hide)
		self.scrollview.leftZoneEntered.connect(self.docktagger.show)
		self.scrollview.leftZoneLeft.connect(self.docktagger.hide)

	def eventFilter(self, sview, ev):
		if ev.type() == QEvent.KeyPress:
			if ev.key() == Qt.Key_Escape:
				self.fullscreenAction.setChecked(False)
				return True
			elif ev.key() in [Qt.Key_PageUp, Qt.Key_Backspace]: # qactions
				self.showPreviousFile()
				return True
			elif ev.key() in [Qt.Key_PageDown, Qt.Key_Space]:
				self.showNextFile()
				return True
		return super(ImageViewer, self).eventFilter(sview, ev)

	@Slot()
	def doNormalZoom(self):
		self.scrollview.setZoomFactor(1.)

	@Slot()
	def doFitAllZoom(self):
		self.scrollview.setZoomMode(ZOOM_FITALL)

	@Slot()
	def doFitCutZoom(self):
		self.scrollview.setZoomMode(ZOOM_FITCUT)

	@Slot()
	def zoom(self):
		self.scrollview.multiplyZoomFactor(1.5)

	@Slot()
	def unzoom(self):
		self.scrollview.multiplyZoomFactor(1 / 1.5)

	def spawn(self, files, currentFile):
		self.files = files
		self.currentIndex = files.index(currentFile)

		self.setFile(currentFile)
		if self.isHidden():
			#~ self.setWindowState(self.windowState() | Qt.WindowMaximized)
			#~ self.show()
			self.fullscreenAction.setChecked(False)
			self.fullscreenAction.setChecked(True)
			#~ self.showMaximized()
		else:
			self.show()

	def setFile(self, file):
		self.tageditor.setFile(file)
		self.scrollview.setFile(file)
		self.setWindowTitle(file)

	@Slot()
	def copyPreviousTags(self):
		tags = self.db.find_tags_by_file(self.files[self.currentIndex - 1])
		with self.db:
			self.db.tag_file(self.files[self.currentIndex], tags)
		self.tageditor.setFile(self.files[self.currentIndex])

	def setFullscreen(self, full):
		if full:
			self.showFullScreen()
		else:
			self.showNormal()

	@Slot()
	def showPreviousFile(self):
		if self.currentIndex > 0:
			self.currentIndex -= 1
			self.setFile(self.files[self.currentIndex])

	@Slot()
	def showNextFile(self):
		if self.currentIndex < len(self.files) - 1:
			self.currentIndex += 1
			self.setFile(self.files[self.currentIndex])
Example #32
0
class LineTrace(BaseGraph):
    def __init__(self, data: DataBuffer = None, parent=None):
        """
        TODO: Fix documentation, i might have changed things and forgot to update documentation (axis_data)
        Inherits: BaseGraph()

        Used to display 2D type of graphs. Enables user to to transformations to 2D type of data (x, y)

        :param data: DataBuffer(): If DataBuffer is being opened, this is a reference to that data buffer
        :param axis_data [Optional]: If what is being open is a line trace created from Heatmap window, then there is
                                    no actual data buffer rather just x and y axis data
        """
        print("Instantiating 2d window . . .")
        super().__init__()

        # Axis data exists when what is being opened is a line trace created by Heatmap window. In that case no
        # reference to data buffer is made.

        self.parent = parent

        self.data_buffer = data
        self.x_values = self.data_buffer.data["x"]
        self.y_values = self.data_buffer.data["y"]

        self.active_data_set = self.y_values[0]
        self.displayed_data_set = self.active_data_set

        self.title = self.data_buffer.name

        # indicates in which modes the window is currently working.
        self.modes = {"fit": False}
        # used in fit mode of this window, holds data about created fit curves to enable hiding and displaying them at
        # any point
        self.fit_curves = {}
        for index, data in enumerate(self.y_values):
            self.fit_curves[index] = {}

        self.init_ui()

    """
    # ###########################################
    # ############ USER INTERFACE ###############
    # ###########################################
    """

    def init_ui(self):
        """
        Method that builds user interface

        :return:
        """

        print("Creating 2d window . . .")
        # set dimensions, title and icon of the window
        self.setGeometry(50, 50, 640, 400)
        self.setWindowTitle("Line trace window")
        self.setWindowIcon(QIcon("../img/lineGraph.png"))

        # Elements for ploting data
        self.plt = pg.GraphicsView()
        self.central_item = pg.GraphicsLayout()
        self.main_subplot = pg.PlotItem(x=self.x_values,
                                        y=self.active_data_set,
                                        pen=(60, 60, 60),
                                        title=self.title)
        self.fit_plot = pg.PlotItem(pen=(60, 60, 60))
        self.fit_plot.sigRangeChanged.connect(self.update_region_area)

        for plot_item in [self.main_subplot, self.fit_plot]:
            plot_item.layout.removeItem(plot_item.getAxis("top"))
            if "extra_axis" in self.data_buffer.data:
                extra_view_box = pg.ViewBox()
                extra_axis = pg.AxisItem("top")
                extra_axis.setPen((60, 60, 60))
                axis_data = self.data_buffer.axis_values["extra_axis"]
                label_style = {'font-size': '9pt'}
                extra_axis.setLabel(axis_data["name"], axis_data["unit"],
                                    **label_style)
                plot_item.layout.addItem(extra_axis, 0, 1)
                extra_axis.linkToView(extra_view_box)
                extra_view_box.setXLink(plot_item.vb)
                if plot_item == self.main_subplot:
                    extra_view_box_main = extra_view_box
                else:
                    extra_view_box_fit = extra_view_box

        self.setCentralWidget(self.plt)

        # set the color of the background and set the central widget. All other elements are added to this central
        # widget, which makes them resizable and scalable with the window size.
        self.plt.setBackground("w")
        self.plt.setCentralWidget(self.central_item)

        print("Creating axis elements . . .")
        legend = {"left": "y", "bottom": "x", "top": "extra_axis"}
        self.central_item.addItem(self.main_subplot, colspan=2)

        self.central_item.nextRow()
        self.central_item.addItem(self.fit_plot)
        self.fit_plot.hide()

        self.plot_elements = {
            "main_subplot": self.main_subplot,
            "fit_plot": self.fit_plot
        }
        if "extra_axis" in self.data_buffer.data:
            self.plot_elements["extra_view_box_main"] = extra_view_box_main
            self.plot_elements["extra_view_box_fit"] = extra_view_box_fit

        # connect the range changed signal of main subplot to a method that updates the range of the extra axis.
        self.main_subplot.sigRangeChanged.connect(self.update_extra_axis_main)
        self.fit_plot.sigRangeChanged.connect(self.update_extra_axis_fit)

        print("Configuring axis data . . .")
        self.update_axis_labels()

        self.init_toolbar()

        self.show()

    def init_toolbar(self):
        """
        Create toolbar of the line trace window

        :return: NoneType
        """
        print("Creating toolbar . . .")
        self.tools = self.addToolBar("Tools")
        self.tools.actionTriggered[QAction].connect(self.perform_action)

        print("Filling selection combobox . . .")
        self.data_selection_combobox = QComboBox()
        for index, data in enumerate(self.data_buffer.data["y"]):
            display_member = "param{}".format(index)
            value_member = data
            self.data_selection_combobox.addItem(display_member, value_member)
        self.data_selection_combobox.currentIndexChanged.connect(
            lambda: self.change_active_set(index=self.data_selection_combobox.
                                           currentIndex()))
        self.tools.addWidget(self.data_selection_combobox)

        print("Adding actions . . .")
        self.toggle_fit_mode = QAction(QIcon("img/fit_curve_icon.png"),
                                       "Fit_mode", self)
        self.tools.addAction(self.toggle_fit_mode)
        self.toggle_fit_mode.setCheckable(True)

        self.window_toolbar = QToolBar("Window toolbar")
        self.window_toolbar.actionTriggered[QAction].connect(
            self.perform_action)
        self.addToolBar(self.window_toolbar)

        self.customize_font_btn = QAction(QIcon("img/editFontIcon.png"),
                                          "Font", self)
        self.customize_font_btn.setToolTip(
            "Open a widget that allows user to customise font and axis values")
        self.window_toolbar.addAction(self.customize_font_btn)

        self.exit_action_Btn = QAction(QIcon("img/closeIcon.png"), "Exit",
                                       self)
        self.window_toolbar.addAction(self.exit_action_Btn)

        self.init_fit_toolbar()

    def init_fit_toolbar(self):
        """
        Additional toolbar that is displayed when window is operating in "fit" mode

        :return: NoneType
        """
        print("Initialising fit toolbar . . .")
        self.fit_toolbar = QToolBar("Fitting options")
        self.fit_toolbar.actionTriggered[QAction].connect(self.perform_action)
        self.addToolBar(Qt.RightToolBarArea, self.fit_toolbar)
        self.fit_toolbar.hide()

        self.default_graph = QAction(QIcon("img/noneIcon.png"),
                                     "Default_graph", self)
        self.default_graph.setCheckable(True)
        self.default_graph.setChecked(True)
        self.fit_toolbar.addAction(self.default_graph)

        self.linear = QAction(QIcon("img/linear_fit_icon.png"), "Linear_fit",
                              self)
        self.linear.setCheckable(True)
        self.fit_toolbar.addAction(self.linear)

        self.gauss = QAction(QIcon("img/gaussianIcon.png"), "Gaussian_fit",
                             self)
        self.gauss.setCheckable(True)
        self.fit_toolbar.addAction(self.gauss)

        self.sinus = QAction(QIcon("img/sinusIcon.png"), "Sinus_fit", self)
        self.sinus.setCheckable(True)
        self.fit_toolbar.addAction(self.sinus)

    def define_plot_parameters(self):
        pass

    """
    # ###########################################
    # ################# HELPERS #################
    # ###########################################
    """

    def get_selection_area(self):
        """
        This method gets data points on x axis that are inside of the area selected by LinearRegionItem
        (self.region_select). It then also selects coresponding data points on y axis and returns two numpy arrays
        containing x and y data points.

        :return: np.array, np.array: data points on x and y axis that are inside the selected area
        """
        min_x, max_x = self.region_select.getRegion()
        x = []
        y = []
        for i, value in enumerate(self.x_values):
            if value >= min_x and value <= max_x:
                x.append(value)
                y.append(self.active_data_set[i])
        x = np.array(x)
        y = np.array(y)
        return x, y

    def change_active_set(self, index):
        """
        TODO: Write documentation

        :param index:
        :return:
        """
        data = self.y_values[index]
        self.active_data_set = data
        self.change_displayed_data_set(data, index)

    def change_displayed_data_set(self, data_set, index):
        """
        TODO: Write documentation

        :param data_set:
        :param index:
        :return:
        """
        self.displayed_data_set = data_set
        self.plot_elements["main_subplot"].clear()
        self.plot_elements["main_subplot"].plot(self.x_values,
                                                self.active_data_set,
                                                pen=(60, 60, 60))
        self.plot_elements["fit_plot"].clear()
        self.plot_elements["fit_plot"].plot(self.x_values,
                                            self.active_data_set)

        self.update_axis_labels(index)

        if self.modes["fit"]:
            self.modes["fit"] = False
            self.fit_mode_action()

    """
    # ###########################################
    # ############### ACTIONS ###################
    # ###########################################
    """

    def fit_mode_action(self):
        """
        TODO: This might have a memory leak, i keep creating new LinerRegionItems. Probably should reuse the old one

        Called when user clicks on an action that activates fit mode of the window. Shows / hides additional toolbar
        used to fit different kinds of curves to the displayed data.

        :return: NoneType
        """

        if self.modes["fit"]:
            self.modes["fit"] = False
            self.fit_toolbar.hide()
            self.main_subplot.removeItem(self.region_select)
            del self.region_select
            self.fit_plot.hide()
            self.fit_plot.clear()
        else:
            self.modes["fit"] = True
            self.fit_toolbar.show()
            self.region_select = pg.LinearRegionItem([0, 1])
            self.region_select.sigRegionChanged.connect(
                self.update_selected_region)
            self.region_select.setZValue(10)
            self.region_select.setRegion([self.x_values[0], self.x_values[-1]])
            self.main_subplot.addItem(self.region_select, ignoreBounds=True)
            self.set_initial_fit_graph_state()
            self.fit_plot.show()
            # add only selected data to graph

    def set_initial_fit_graph_state(self):
        """
        Default state of the fit mode displays only mirrored source of the data. Other fits need to be manually turned
        on for them to be displayed.

        :return: NoneType
        """

        self.fit_plot.clear()
        plot_item = pg.PlotDataItem(self.x_values, self.active_data_set)
        self.fit_plot.addItem(plot_item)
        self.fit_curves["default"] = plot_item

        # Set button states as they should be
        self.default_graph.setChecked(True)
        self.gauss.setChecked(False)

    def font_action(self):
        """
        Opens a new widget that allows user to modify font sizes on axis of all graphs in this window

        :return: NoneType
        """

        self.eaw = Edit2DAxisWidget(self)
        self.eaw.submitted.connect(self.edit_axis_data)
        self.eaw.show()

    # ###############################
    # ############ FITS #############
    # ###############################
    def default_graph_action(self):
        """
        Mirror the source data of the window to the graph for fits. Turning off this action removes the curve from graph
        for displaying fit curves.

        :return: NoneType
        """
        if self.default_graph.isChecked():
            x = self.x_values
            y = self.active_data_set

            plot_item = pg.PlotDataItem(x, y)
            self.fit_plot.addItem(plot_item)
            self.fit_curves["default"] = plot_item
        else:
            self.fit_plot.removeItem(self.fit_curves["default"])

    def gaussian_fit_action(self):
        """
        As suggested by the name of the function, it tries to apply gaussian fit the the selected data. Data is then
        added and displayed in the graph for fir curves.

        Turning off the action that prompts this function removes curve from the graph

        :return:
        """
        def gauss(x, a, x0, sigma):
            """

            :param x:
            :param a:
            :param x0:
            :param sigma:
            :return:
            """
            return a * exp(-(x - x0)**2 / (2 * sigma**2))

        if self.gauss.isChecked():
            try:
                x, y = self.get_selection_area()
                mean = sum(x * y) / sum(
                    y
                )  # https://en.wikipedia.org/wiki/Weighted_arithmetic_mean
                sigma = np.sqrt(sum(y * (x - mean)**2) / sum(y))

                p0 = array([max(y), mean, sigma])
                popt, pcov = curve_fit(gauss, x, y, p0=p0)
                plot_item = pg.PlotDataItem(x, gauss(x, *popt))
                plot_item.setPen(255, 0, 0)
                self.fit_plot.addItem(plot_item)
                self.fit_curves["gauss"] = plot_item
            except Exception as e:
                print(str(e))
                show_error_message("Could not find fit", str(e))
        else:
            self.fit_plot.removeItem(self.fit_curves["gauss"])
            del self.fit_curves["gauss"]

    def sinus_fit_action(self):
        """
        As suggested by the name of the function, it tries to apply sinus fit the the selected data. Data is then
        added and displayed in the graph for fir curves.

        Turning off the action that prompts this function removes curve from the graph

        :return:
        """
        def sin(x, freq, amplitude, phase, offset):
            """

            :param x:
            :param freq:
            :param amplitude:
            :param phase:
            :param offset:
            :return:
            """
            return np.sin(x * freq + phase) * amplitude + offset

        if self.sinus.isChecked():
            x, y = self.get_selection_area()
            guess_freq = 1
            guess_amplitude = 3 * np.std(y) / (2**0.5)
            guess_phase = 0
            guess_offset = np.mean(y)
            p0 = array(
                [guess_freq, guess_amplitude, guess_phase, guess_offset])
            fit = curve_fit(sin, x, y, p0=p0)
            fit_data = sin(x, *fit[0])
            plot_item = pg.PlotDataItem(x, fit_data)
            plot_item.setPen(0, 0, 255)
            self.fit_plot.addItem(plot_item)
            self.fit_curves["sinus"] = plot_item
        else:
            self.fit_plot.removeItem(self.fit_curves["sinus"])
            del self.fit_curves["sinus"]

    def linear_fit_action(self):
        """

        :return:
        """
        def linear(a, b, k):
            """

            :param a:
            :param b:
            :param k:
            :return:
            """
            return a * k + b

        if self.linear.isChecked():
            x, y = self.get_selection_area()
            initial_guess = np.polyfit(x, y, 1)
            fit = curve_fit(linear, x, y, initial_guess)
            fit_data = linear(x, *fit[0])
            plot_item = pg.PlotDataItem(x, fit_data)
            plot_item.setPen(0, 255, 0)
            self.fit_plot.addItem(plot_item)
            self.fit_curves["linear"] = plot_item
        else:
            self.fit_plot.removeItem(self.fit_curves["linear"])
            del self.fit_curves["linear"]

    """
    # ###########################################
    # ################# Helpers #################
    # ###########################################
    """

    def update_axis_labels(self, y_index=0):
        legend = {"left": "y", "bottom": "x", "top": "extra_axis"}
        for plot_item in [self.main_subplot, self.fit_plot]:
            for axis in ['left', 'bottom']:
                pi = plot_item
                ax = pi.getAxis(axis)
                ax.setPen((60, 60, 60))
                axis_data = self.data_buffer.axis_values[legend[axis]]
                label_style = {'font-size': '10pt'}
                if axis == "left":
                    ax.setLabel(axis_data[y_index]["name"],
                                axis_data[y_index]["unit"], **label_style)
                else:
                    ax.setLabel(axis_data["name"], axis_data["unit"],
                                **label_style)

    """
    # ###########################################
    # ################## EVENTS #################
    # ###########################################
    """

    def update_selected_region(self):
        """
        Event that upon changing bounds of region select element changes limits of the fit graph in a way that it only
        displays the selected portion of the data.

        :return: NoneType
        """
        min_x, max_x = self.region_select.getRegion()
        self.fit_plot.setXRange(min_x, max_x, padding=0)
        return

    def update_region_area(self, element, view_range):
        """
        Method that updates region select when fit graph axis changes range.

        :param element: pyqtgraph element that sent the signal
        :param view_range: range on x and y axis
        :return: NoneType
        """
        min_x, max_x = view_range[0]
        self.region_select.setRegion([min_x, max_x])
        return

    def edit_axis_data(self, data):
        """
        Method that applies the changes to axis (font, labels, ...)

        :param data: data passed trough the signal. It contains user input (values to apply to font/label/...)
        :return: NoneType
        """
        for element, sides in data.items():
            for side, options in sides.items():
                if side != "top":
                    axis = self.plot_elements[element].getAxis(side)
                    axis.setLabel(options["name"], options["unit"],
                                  **options["label_style"])
                    if options["ticks"]["font"] != "":
                        font = QFont()
                        font.setPixelSize(int(options["ticks"]["font"]))
                        axis.tickFont = font
                        axis.setStyle(tickTextOffset=int(
                            int(options["ticks"]["font"]) / 2))
        return

    def update_extra_axis_fit(self):
        """

        :return:
        """
        self.update_extra_axis(self.fit_plot.vb,
                               self.plot_elements["extra_view_box_fit"])

    def update_extra_axis_main(self):
        """

        :return:
        """
        self.update_extra_axis(self.main_subplot.vb,
                               self.plot_elements["extra_view_box_main"])

    def update_extra_axis(self, view_box, extra_view_box):
        """

        :return:
        """
        point1 = self.parent.line_segment_roi["ROI"].getSceneHandlePositions(0)
        _, scene_coords = point1
        start_coords = self.parent.line_segment_roi["ROI"].mapSceneToParent(
            scene_coords)
        point2 = self.parent.line_segment_roi["ROI"].getSceneHandlePositions(1)
        _, scene_coords = point2
        end_coords = self.parent.line_segment_roi["ROI"].mapSceneToParent(
            scene_coords)

        x_diff = end_coords.x() - start_coords.x()
        y_diff = end_coords.y() - start_coords.y()

        angle = atan2(y_diff, x_diff)

        angle_tan = tan(angle)

        start_value_x_axis = view_box.state["viewRange"][0][0]
        end_value_x_axis = view_box.state["viewRange"][0][1]
        # padding = view_box.suggestPadding(0)

        if (start_coords.y() > start_value_x_axis) and (end_coords.y() <
                                                        end_value_x_axis):
            delta_x_start = start_coords.y() - start_value_x_axis
            delta_y_start = delta_x_start / angle_tan
            delta_x_end = end_value_x_axis - end_coords.y()
            delta_y_end = delta_x_end / angle_tan
            start_value_extra_axis = start_coords.x() - delta_y_start
            end_value_extra_axis = end_coords.x() + delta_y_end
        # Option 2: starting point has been dragged outside of the visible range on line trace graph
        elif (start_coords.y() < start_value_x_axis) and (end_coords.y() <
                                                          end_value_x_axis):
            delta_x_start = start_value_x_axis - start_coords.y()
            delta_y_start = delta_x_start / angle_tan
            delta_x_end = end_value_x_axis - end_coords.y()
            delta_y_end = delta_x_end / angle_tan
            start_value_extra_axis = start_coords.x() + delta_y_start
            end_value_extra_axis = end_coords.x() + delta_y_end
        # Option 3: ending point has been draged outside of the visible range of line trace graph
        elif (start_coords.y() > start_value_x_axis) and (end_coords.y() >
                                                          end_value_x_axis):
            delta_x_start = start_coords.y() - start_value_x_axis
            delta_y_start = delta_x_start / angle_tan
            delta_x_end = end_coords.y() - end_value_x_axis
            delta_y_end = delta_x_end / angle_tan
            start_value_extra_axis = start_coords.x() - delta_y_start
            end_value_extra_axis = end_coords.x() - delta_y_end
        # Option 4: graph has been zoomed in and neither starting or ending point are visible on the line trace
        # graph
        else:
            delta_x_start = start_value_x_axis - start_coords.y()
            delta_y_start = delta_x_start / angle_tan
            delta_x_end = end_coords.y() - end_value_x_axis
            delta_y_end = delta_x_end / angle_tan
            start_value_extra_axis = start_coords.x() + delta_y_start
            end_value_extra_axis = end_coords.x() - delta_y_end

        extra_view_box.setXRange(start_value_extra_axis,
                                 end_value_extra_axis,
                                 padding=0)