class ByteBufferWidget(QWidget):
    on_data_selected = pyqtSignal(QObject)

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        #toolbar = QToolBar()
        #self.splitIntoPacketsAction = toolbar.addAction("Split into packets")
        #self.splitIntoPacketsAction.triggered.connect(self.splitIntoPackets)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.setLayout(layout)

        self.tabWidget = QTabWidget()
        self.tabWidget.setContentsMargins(0, 0, 0, 0)
        self.tabWidget.setDocumentMode(True)
        layout.addWidget(self.tabWidget)
        self.textbox = HexView2()
        self.textbox.on_data_selected.connect(self.on_data_selected.emit)
        self.textbox.onNewSubflowCategory.connect(self.newSubflowCategory)
        self.textbox.formatInfoUpdated.connect(self.onFormatInfoUpdated)
        self.tabWidget.addTab(self.textbox, "Raw buffer")
        #layout.addWidget(toolbar)

    def setContents(self, bufObj):
        self.bufferObject = bufObj
        self.setWindowTitle(str(bufObj))
        self.textbox.setBuffer(bufObj)
        self.bufferObject.on_new_data.connect(self.onNewData)

    def onFormatInfoUpdated(self):
        self.tabWidget.clear()
        self.tabWidget.addTab(self.textbox, "Raw buffer")

    def newSubflowCategory(self, category, parse_context):
        for i in range(self.tabWidget.count()):
            if self.tabWidget.tabText(i) == category:
                break
        widget = PacketListWidget()
        widget.setContents(parse_context.subflow_categories[category])
        self.tabWidget.addTab(widget, category)
        widget.on_data_selected.connect(self.on_data_selected.emit)

    def onNewData(self):
        #self.textbox.showHex(bufObj.buffer)
        self.textbox.redraw()

    def splitIntoPackets(self):
        pass

    def run_ndis(self):
        pass
Exemple #2
0
class MainWindow(QtWidgets.QMainWindow):
    filter = pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.data = TracedData()
        for directory in sys.argv[1:]:
            self.data.load(directory)

        self.graph = GraphWidget(self.data)

        central = QWidget()
        central.setLayout(QVBoxLayout())
        self.setCentralWidget(central)

        self.filterGui = QLineEdit()
        self.filterGui.returnPressed.connect(
            lambda: self.filter.emit(self.filterGui.text()))

        self.tab = QTabWidget()
        self.tab.setMinimumHeight(200)

        central.layout().addWidget(self.filterGui)

        splitter = QSplitter(Qt.Vertical)
        central.layout().addWidget(splitter)
        splitter.addWidget(self.graph)
        splitter.addWidget(self.tab)

        self.filter.connect(self.filter_handler)
        self.filter.emit(DEFAULT_FILTER)
        self.graph.onSelected.connect(self.display)

        self.widgets = {}
        self.widgets['region'] = RegionWidget(self)

    def display(self, base):
        self.tab.clear()
        if getattr(base, 'action', None):
            base.action.window = self  # XXX: not elegant solution
            base.action.gui(self.tab, self.graph)

    def filter_handler(self, text):
        self.filterGui.setText(text)
        try:
            self.graph.apply_filter(text)
        except FilterException as e:
            QMessageBox().critical(self, "Filter error", e.message)
class WritePanel(SubWindow, WindowSystemController):

    '''
    'Write' main panel. Detachable
    '''

    def __init__(self, parent=None, parent_window_system_controller=None):
        '''

        '''
        super(WritePanel, self).__init__(
            parent=parent, parent_window_system_controller=parent_window_system_controller)

        self.setWindowTitle(_("Write"))
        self.setObjectName("write_sub_window")
        self.dock_system = DockSystem(
            self, self,  DockSystem.DockTypes.WritePanelDock)

        # connect core signal to open sheets :
        cfg.core.tree_sheet_manager.sheet_is_opening.connect(
            self.open_write_tab)

        # Project tree view dock :
        self.dock_system.add_dock("write-tree-dock",  Qt.LeftDockWidgetArea)

        # set TabWidget:
        self.tab_widget = QTabWidget(self)
        self.setCentralWidget(self.tab_widget)

        # subscribe
        cfg.core.subscriber.subscribe_update_func_to_domain(
            self._clear_project,  "core.project.close")

    def open_write_tab(self, tree_sheet):

        new_write_tab = WriteTab(self, self)
        new_write_tab.tree_sheet = tree_sheet
        self.tab_widget.addTab(new_write_tab, new_write_tab.tab_title)
        # temp for test:
        new_write_tab.dock_system.add_dock("properties-dock")

    def _clear_project(self):
        # TODO: make that cleaner
        for i in range(0, self.tab_widget.count()):
            widget = self.tab_widget.widget(i)
            widget.close()
            widget.deleteLater()
        self.tab_widget.clear()
Exemple #4
0
class WritePanel(SubWindow, WindowSystemController):
    '''
    'Write' main panel. Detachable
    '''
    def __init__(self, parent=None, parent_window_system_controller=None):
        '''

        '''
        super(WritePanel, self).__init__(
            parent=parent,
            parent_window_system_controller=parent_window_system_controller)

        self.setWindowTitle(_("Write"))
        self.setObjectName("write_sub_window")
        self.dock_system = DockSystem(self, self,
                                      DockSystem.DockTypes.WritePanelDock)

        # connect core signal to open sheets :
        cfg.core.tree_sheet_manager.sheet_is_opening.connect(
            self.open_write_tab)

        # Project tree view dock :
        self.dock_system.add_dock("write-tree-dock", Qt.LeftDockWidgetArea)

        # set TabWidget:
        self.tab_widget = QTabWidget(self)
        self.setCentralWidget(self.tab_widget)

        # subscribe
        cfg.core.subscriber.subscribe_update_func_to_domain(
            self._clear_project, "core.project.close")

    def open_write_tab(self, tree_sheet):

        new_write_tab = WriteTab(self, self)
        new_write_tab.tree_sheet = tree_sheet
        self.tab_widget.addTab(new_write_tab, new_write_tab.tab_title)
        # temp for test:
        new_write_tab.dock_system.add_dock("properties-dock")

    def _clear_project(self):
        # TODO: make that cleaner
        for i in range(0, self.tab_widget.count()):
            widget = self.tab_widget.widget(i)
            widget.close()
            widget.deleteLater()
        self.tab_widget.clear()
Exemple #5
0
class PipelineWidget(QWidget):
    def __init__(self, controller: EzCVController, parent=None):
        super().__init__(parent=parent)
        self._controller = controller

        self.operators_tabs = QTabWidget(self)
        self.add_operator_button = QPushButton('Add Operator', parent=self)
        self.add_operator_popup = AddOperatorWidget(self._controller)

        self.add_operator_button.clicked.connect(
            self.on_add_operator_button_click)
        self.add_operator_button.setShortcut("A")
        self._controller.operators_changed.connect(self.on_operators_changed)

        self.operators_config_widgets: List[OperatorConfigWidget] = list()

        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout(self)

        layout.addWidget(self.add_operator_button, alignment=Qt.AlignHCenter)
        layout.addWidget(self.operators_tabs)

        self.setLayout(layout)

    def on_add_operator_button_click(self):
        self.add_operator_popup.show()

    def on_operators_changed(self):
        for operator_config_widget in self.operators_config_widgets:
            operator_config_widget.setParent(None)
        self.operators_tabs.clear()

        for operator_name, operator in self._controller.operators.items():
            op_config_widget = OperatorConfigWidget(operator)
            self.operators_tabs.addTab(op_config_widget, operator_name)
            op_config_widget.updated.connect(
                self._create_operator_updated_callback(operator_name))

    def _create_operator_updated_callback(
            self, operator_name: str) -> Callable[[], None]:
        def callback(param_name: str, param_value: Any):
            self._controller.update_operator(operator_name, param_name,
                                             param_value)

        return callback
Exemple #6
0
class pl_main(QWidget,tab_base):

	lines=[]
	edit_list=[]
	file_name=""
	line_number=[]
	save_file_name=""
	name="Luminescence"


	def __init__(self):
		QWidget.__init__(self)
		self.main_vbox = QVBoxLayout()
		self.notebook = QTabWidget()

		self.main_vbox.addWidget(self.notebook)
		self.setLayout(self.main_vbox)

		self.notebook.setTabsClosable(True)
		self.notebook.setMovable(True)
		bar=QHTabBar()
		bar.setStyleSheet("QTabBar::tab { height: 35px; width: 200px; }")
		self.notebook.setTabBar(bar)
		self.notebook.setTabPosition(QTabWidget.West)



	def update(self):
		self.notebook.clear()

		files=epitaxy_get_dos_files()
		for i in range(0,epitaxy_get_layers()):
			pl_file=epitaxy_get_pl_file(i)
			if pl_file.startswith("pl")==True:
				widget	= QWidget()
 
				name="Luminescence of "+epitaxy_get_name(i)
				print(pl_file,files)

				widget=tab_class()
				widget.init(pl_file+".inp",name)

				self.notebook.addTab(widget,name)


	def help(self):
		help_window().help_set_help(["tab.png","<big><b>Luminescence</b></big>\nIf you set 'Turn on luminescence' to true, the simulation will assume recombination is a raditave process and intergrate it to produce Voltage-Light intensity curves (lv.dat).  Each number in the tab tells the model how efficient each recombination mechanism is at producing photons."])
Exemple #7
0
class VectorFrame(GenericFrame):
    def __init__(self, parent=None):
        super().__init__(QHBoxLayout(), 'Vector Frame', parent=parent)
        self.tabs = QTabWidget()
        self.vectors = None

        self.init_frame()

    def init_frame(self):
        self.setFrameShape(QFrame.StyledPanel)
        self.populate_tabs()

        self.layout.addWidget(self.tabs)

    def populate_tabs(self):
        self.tabs.clear()
        self.vectors = get_vector_list()

        if self.vectors:
            for vector in self.vectors:
                print(vector)
                self.initialize_tab(vector)

        # TODO: Program to open vector from vector db
        # self.tabs.addTab(QWidget(), '+')

        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(lambda i: self.tabs.removeTab(i))

    def initialize_tab(self, vector):
        frame = GenericFrame(QHBoxLayout(), 'vector tab', self)

        splitter = QSplitter(Qt.Horizontal)

        # TODO: Make generic table class, populate with vector nodes & significant log entries
        splitter.addWidget(NodeTableFrame())
        splitter.addWidget(GraphFrame(vector, frame))
        splitter.setSizes([600, 600])

        frame.layout.addWidget(splitter)
        self.tabs.addTab(frame, vector.get("Name"))

    def delete_tab(self, vector):
        for i in range(0, self.tabs.count()):
            if vector.get("Name") == self.tabs.tabText(i):
                self.tabs.removeTab(i)
Exemple #8
0
class NavigatorWidget(QWidget):
    open_file = pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.layout = QVBoxLayout(self)
        self.tab_view = QTabWidget(self)
        self.tab_view.setSizePolicy(QSizePolicy.Expanding,
                                    QSizePolicy.Expanding)

        self.layout.addWidget(self.tab_view)
        self.setLayout(self.layout)

    @pyqtSlot(Workspace)
    def setWorkspace(self, w):
        self.workspace = w
        if w is None:
            self.models = []
        else:
            self.models = [FileModel(w)]
            for i in BasePlugin.PLUGIN_TYPES:
                self.models.extend(
                    [x(w) for x in i.get_additional_models_for_navigator()])
        self._add_tabs()

    def _add_tabs(self):
        self.tab_view.clear()
        for i in self.models:
            tree_widget = QTreeView(self)
            proxy = QSortFilterProxyModel(self)
            proxy.setSourceModel(i)
            tree_widget.setModel(proxy)
            tree_widget.setSortingEnabled(True)
            self.tab_view.addTab(tree_widget, i.tabName())
            tree_widget.doubleClicked.connect(self.double_click)

    @pyqtSlot(QModelIndex)
    def double_click(self, index: QModelIndex):
        resource_location = index.data(Qt.UserRole)
        if hasattr(resource_location, "get_real_path"):
            resource_location = resource_location.get_real_path()
        self.open_file.emit(resource_location)
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.tabs = QTabWidget()
        self.init_ui()

    def update_file(self):
        self.tabs.clear()
        for f in QApplication.instance().diagram_list():
            self.add(f)

    def init_ui(self):
        file_menu = QMenu("&File", self)

        update_action = file_menu.addAction("update")
        update_action.triggered.connect(self.update_file)

        quit_action = file_menu.addAction("E&xit")
        quit_action.triggered.connect(QApplication.instance().quit)
        self.menuBar().addMenu(file_menu)
        self.setCentralWidget(self.tabs)

    def add(self, file):
        tab_name = os.path.basename(file)

        view = None
        for i in range(self.tabs.count()):
            if self.tabs.tabText(i) == tab_name:
                view = self.tabs.widget(i)
                break

        if view is None:
            m = magic.from_file(file)
            if m[:3] == 'PNG':
                view = PngView()
            elif m[:3] == 'SVG':
                view = SvgView()

            self.tabs.addTab(view, tab_name)

        view.open_file(file)
Exemple #10
0
class pl_main(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.main_vbox = QVBoxLayout()
        self.notebook = QTabWidget()

        self.main_vbox.addWidget(self.notebook)
        self.setLayout(self.main_vbox)

        self.notebook.setTabsClosable(True)
        self.notebook.setMovable(True)
        bar = QHTabBar()
        bar.setStyleSheet("QTabBar::tab { height: 35px; width: 200px; }")
        self.notebook.setTabBar(bar)
        self.notebook.setTabPosition(QTabWidget.West)

        global_object_register("pl_update", self.update)

    def update(self):
        self.notebook.clear()

        files = epitaxy_get_dos_files()
        for i in range(0, epitaxy_get_layers()):
            pl_file = epitaxy_get_pl_file(i)
            if pl_file.startswith("pl") == True:
                widget = QWidget()

                name = _("Luminescence of ") + epitaxy_get_name(i)

                widget = tab_class()
                widget.init(pl_file + ".inp", name)

                self.notebook.addTab(widget, name)

    def help(self):
        help_window().help_set_help([
            "tab.png",
            _("<big><b>Luminescence</b></big>\nIf you set 'Turn on luminescence' to true, the simulation will assume recombination is a raditave process and intergrate it to produce Voltage-Light intensity curves (lv.dat).  Each number in the tab tells the model how efficient each recombination mechanism is at producing photons."
              )
        ])
Exemple #11
0
class SessionDetails(QWidget):
    def __init__(self, parent=None):
        super(SessionDetails, self).__init__(parent)
        self.tabs = QTabWidget(self)
        layout = QVBoxLayout(self)
        layout.addWidget(self.tabs)
        self.setMinimumSize(600, 600)
        self.tabs.currentChanged.connect(self.selectInterval)
        self.session = None

    def setSession(self, session):
        self.session = session
        self.tabs.clear()
        self.tabs.addTab(IntervalPage(session, self), str(session.start_time))
        self.selectInterval(0)
        if not session.waypoints():
            self.parent().map_holder.hide_map()
        else:
            self.parent().map_holder.show_map()

    def setTab(self, tab):
        t = self.tabs.currentWidget()
        t.setTab(tab)

    def addInterval(self, interval):
        self.tabs.addTab(IntervalPage(interval, self), str(interval.timestamp))
        if interval.waypoints():
            self.parent().map.draw_segment(interval.timestamp,
                                           interval.duration)

    def selectInterval(self, ix):
        if self.session.waypoints():
            if ix == 0:
                self.parent().map.draw_map(self.session)
                # pass
            elif ix > 0:
                page = self.tabs.currentWidget()
                self.parent().map.draw_segment(page.interval.timestamp,
                                               page.interval.duration)
Exemple #12
0
class dos_main(QWidget, tab_base):
    def __init__(self):
        QWidget.__init__(self)
        self.main_vbox = QVBoxLayout()
        self.notebook = QTabWidget()

        self.main_vbox.addWidget(self.notebook)
        self.setLayout(self.main_vbox)

        self.notebook.setTabsClosable(True)
        self.notebook.setMovable(True)
        self.notebook.setTabBar(QHTabBar())
        self.notebook.setTabPosition(QTabWidget.West)

        global_object_register("dos_update", self.update)

    def update(self):
        self.notebook.clear()

        files = epitaxy_get_dos_files()
        for i in range(0, epitaxy_get_layers()):
            dos_layer = epitaxy_get_electrical_layer(i)
            if dos_layer.startswith("dos") == True:
                #add_to_widget=True

                name = "DoS of " + epitaxy_get_name(i)

                widget = tab_class()
                widget.init(dos_layer + ".inp", name)

                self.notebook.addTab(widget, name)

    def help(self):
        help_window().help_set_help([
            "tab.png",
            "<big><b>Density of States</b></big>\nThis tab contains the electrical model parameters, such as mobility, tail slope energy, and band gap."
        ])
Exemple #13
0
class LanguagesManagerWidget(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self, parent, Qt.Dialog)
        self.setWindowTitle(self.tr("Language Manager"))
        self.resize(700, 500)

        vbox = QVBoxLayout(self)
        self._tabs = QTabWidget()
        vbox.addWidget(self._tabs)
        # Footer
        hbox = QHBoxLayout()
        btn_close = QPushButton(self.tr('Close'))
        btnReload = QPushButton(self.tr("Reload"))
        hbox.addWidget(btn_close)
        hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding))
        hbox.addWidget(btnReload)
        vbox.addLayout(hbox)
        self.overlay = ui_tools.Overlay(self)
        self.overlay.show()

        self._languages = []
        self._loading = True
        self.downloadItems = []

        # Load Themes with Thread
        btnReload.clicked.connect(self._reload_languages)
        self._thread = ui_tools.ThreadExecution(self.execute_thread)
        self._thread.finished.connect(self.load_languages_data)
        btn_close.clicked.connect(self.close)
        self._reload_languages()

    def _reload_languages(self):
        self.overlay.show()
        self._loading = True
        self._thread.execute = self.execute_thread
        self._thread.start()

    def load_languages_data(self):
        if self._loading:
            self._tabs.clear()
            self._languageWidget = LanguageWidget(self, self._languages)
            self._tabs.addTab(self._languageWidget, self.tr("Languages"))
            self._loading = False
        self.overlay.hide()
        self._thread.wait()

    def download_language(self, language):
        self.overlay.show()
        self.downloadItems = language
        self._thread.execute = self._download_language_thread
        self._thread.start()

    def resizeEvent(self, event):
        self.overlay.resize(event.size())
        event.accept()

    def execute_thread(self):
        try:
            descriptor_languages = urlopen(resources.LANGUAGES_URL)
            languages = json_manager.parse(descriptor_languages)
            languages = [[name, languages[name]] for name in languages]
            local_languages = self.get_local_languages()
            languages = [
                languages[i] for i in range(len(languages))
                if os.path.basename(languages[i][1]) not in local_languages
            ]
            self._languages = languages
        except URLError:
            self._languages = []

    def get_local_languages(self):
        if not file_manager.folder_exists(resources.LANGS_DOWNLOAD):
            file_manager.create_tree_folders(resources.LANGS_DOWNLOAD)
        languages = os.listdir(resources.LANGS_DOWNLOAD) + \
            os.listdir(resources.LANGS)
        languages = [s for s in languages if s.lower().endswith('.qm')]
        return languages

    def _download_language_thread(self):
        for d in self.downloadItems:
            self.download(d[1], resources.LANGS_DOWNLOAD)

    def download(self, url, folder):
        fileName = os.path.join(folder, os.path.basename(url))
        try:
            content = urlopen(url)
            with open(fileName, 'wb') as f:
                f.write(content.read())
        except URLError:
            return
class PluginsManagerWidget(QDialog):
    """Plugin Manager widget"""

    def __init__(self, parent):
        super(PluginsManagerWidget, self).__init__(parent, Qt.Dialog)
        self.setWindowTitle(translations.TR_PLUGIN_MANAGER)
        self.resize(700, 600)

        vbox = QVBoxLayout(self)
        self._tabs = QTabWidget()
        vbox.addWidget(self._tabs)
        self._txt_data = QTextBrowser()
        self._txt_data.setOpenLinks(False)
        vbox.addWidget(QLabel(translations.TR_PROJECT_DESCRIPTION))
        vbox.addWidget(self._txt_data)
        # Footer
        hbox = QHBoxLayout()
        btn_close = QPushButton(translations.TR_CLOSE)
        btnReload = QPushButton(translations.TR_RELOAD)
        hbox.addWidget(btn_close)
        hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding))
        hbox.addWidget(btnReload)
        vbox.addLayout(hbox)
        self.overlay = ui_tools.Overlay(self)
        self.overlay.hide()

        self._oficial_available = []
        self._community_available = []
        self._locals = []
        self._updates = []
        self._loading = True
        self._requirements = {}

        btnReload.clicked['bool'].connect(self._reload_plugins)
        self.thread = ThreadLoadPlugins(self)
        self.thread.finished.connect(self._load_plugins_data)
        self.thread.plugin_downloaded.connect(self._after_download_plugin)
        self.thread.plugin_manually_installed.connect(self._after_manual_install_plugin)
        self.thread.plugin_uninstalled.connect(self._after_uninstall_plugin)
        self._txt_data.anchorClicked['const QUrl&'].connect(self._open_link)
        btn_close.clicked['bool'].connect(self.close)
        self.overlay.show()
        self._reload_plugins()

    def show_plugin_info(self, data):
        """Takes data argument, format for HTML and display it"""
        plugin_description = data[2].replace('\n', '<br>')
        html = HTML_STYLE.format(name=data[0],
            version=data[1], description=plugin_description,
            author=data[3], link=data[4])
        self._txt_data.setHtml(html)

    def _open_link(self, url):
        """Takes an url argument and open the link on web browser"""
        link = url.toString()
        if link.startswith('/plugins/'):
            link = 'http://ninja-ide.org' + link
        webbrowser.open(link)

    def _reload_plugins(self):
        """Reload all plugins"""
        self.overlay.show()
        self._loading = True
        self.thread.runnable = self.thread.collect_data_thread
        self.thread.start()

    def _after_manual_install_plugin(self, plugin):
        """After installing, take plugin and add it to installedWidget items"""
        data = {}
        data['name'] = plugin[0]
        data['version'] = plugin[1]
        data['description'] = ''
        data['authors'] = ''
        data['home'] = ''
        self._installedWidget.add_table_items([data])

    def _after_download_plugin(self, plugin):
        """After installing, take plugin and add it to installedWidget items"""
        oficial_plugin = _get_plugin(plugin[0], self._oficial_available)
        community_plugin = _get_plugin(plugin[0], self._community_available)
        if oficial_plugin:
            self._installedWidget.add_table_items([oficial_plugin])
            self._availableOficialWidget.remove_item(plugin[0])
        elif community_plugin:
            self._installedWidget.add_table_items([community_plugin])
            self._availableCommunityWidget.remove_item(plugin[0])

    def _after_uninstall_plugin(self, plugin):
        """After uninstall plugin,make available plugin corresponding to type"""
        oficial_plugin = _get_plugin(plugin[0], self._oficial_available)
        community_plugin = _get_plugin(plugin[0], self._community_available)
        if oficial_plugin:
            self._availableOficialWidget.add_table_items([oficial_plugin])
            self._installedWidget.remove_item(plugin[0])
        elif community_plugin:
            self._availableCommunityWidget.add_table_items([community_plugin])
            self._installedWidget.remove_item(plugin[0])

    def _load_plugins_data(self):
        """Load all the plugins data"""
        if self._loading:
            self._tabs.clear()
            self._updatesWidget = UpdatesWidget(self, copy(self._updates))
            self._availableOficialWidget = AvailableWidget(self,
                copy(self._oficial_available))
            self._availableCommunityWidget = AvailableWidget(self,
                copy(self._community_available))
            self._installedWidget = InstalledWidget(self, copy(self._locals))
            self._tabs.addTab(self._availableOficialWidget,
                translations.TR_OFFICIAL_AVAILABLE)
            self._tabs.addTab(self._availableCommunityWidget,
                translations.TR_COMMUNITY_AVAILABLE)
            self._tabs.addTab(self._updatesWidget, translations.TR_UPDATE)
            self._tabs.addTab(self._installedWidget, translations.TR_INSTALLED)
            self._manualWidget = ManualInstallWidget(self)
            self._tabs.addTab(self._manualWidget,
                translations.TR_MANUAL_INSTALL)
            self._loading = False
        self.overlay.hide()
        self.thread.wait()

    def download_plugins(self, plugs):
        """
        Install
        """
        self.overlay.show()
        self.thread.plug = plugs
        #set the function to run in the thread
        self.thread.runnable = self.thread.download_plugins_thread
        self.thread.start()

    def install_plugins_manually(self, plug):
        """Install plugin from local zip."""
        self.overlay.show()
        self.thread.plug = plug
        #set the function to run in the thread
        self.thread.runnable = self.thread.manual_install_plugins_thread
        self.thread.start()

    def mark_as_available(self, plugs):
        """
        Uninstall
        """
        self.overlay.show()
        self.thread.plug = plugs
        #set the function to run in the thread
        self.thread.runnable = self.thread.uninstall_plugins_thread
        self.thread.start()

    def update_plugin(self, plugs):
        """
        Update
        """
        self.overlay.show()
        self.thread.plug = plugs
        #set the function to run in the thread
        self.thread.runnable = self.thread.update_plugin_thread
        self.thread.start()

    def reset_installed_plugins(self):
        """Reset all the installed plugins"""
        local_plugins = plugin_manager.local_plugins()
        plugins = _format_for_table(local_plugins)
        self._installedWidget.reset_table(plugins)

    def resizeEvent(self, event):
        """Handle Resize events"""
        self.overlay.resize(event.size())
        event.accept()
class SchemesManagerWidget(QDialog):

    def __init__(self, parent):
        super(SchemesManagerWidget, self).__init__(parent, Qt.Dialog)
        self.setWindowTitle(translations.TR_EDITOR_SCHEMES)
        self.resize(700, 500)

        vbox = QVBoxLayout(self)
        self._tabs = QTabWidget()
        vbox.addWidget(self._tabs)
        # Footer
        hbox = QHBoxLayout()
        btn_close = QPushButton(self.tr('Close'))
        btnReload = QPushButton(self.tr("Reload"))
        hbox.addWidget(btn_close)
        hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding))
        hbox.addWidget(btnReload)
        vbox.addLayout(hbox)
        self.overlay = ui_tools.Overlay(self)
        self.overlay.show()

        self._schemes = []
        self._loading = True
        self.downloadItems = []

        #Load Themes with Thread
        btnReload.clicked['bool'].connect(self._reload_themes)
        self._thread = ui_tools.ThreadExecution(self.execute_thread)
        self._thread.finished.connect(self.load_skins_data)
        btn_close.clicked['bool'].connect(self.close)
        self._reload_themes()

    def _reload_themes(self):
        self.overlay.show()
        self._loading = True
        self._thread.execute = self.execute_thread
        self._thread.start()

    def load_skins_data(self):
        if self._loading:
            self._tabs.clear()
            self._schemeWidget = SchemeWidget(self, self._schemes)
            self._tabs.addTab(self._schemeWidget, self.tr("Editor Schemes"))
            self._loading = False
        self.overlay.hide()
        self._thread.wait()

    def download_scheme(self, scheme):
        self.overlay.show()
        self.downloadItems = scheme
        self._thread.execute = self._download_scheme_thread
        self._thread.start()

    def resizeEvent(self, event):
        self.overlay.resize(event.size())
        event.accept()

    def execute_thread(self):
        try:
            descriptor_schemes = urlopen(resources.SCHEMES_URL)
            schemes = json_manager.parse(descriptor_schemes)
            schemes = [(d['name'], d['download']) for d in schemes]
            local_schemes = self.get_local_schemes()
            schemes = [schemes[i] for i in range(len(schemes)) if
                os.path.basename(schemes[i][1]) not in local_schemes]
            self._schemes = schemes
        except URLError:
            self._schemes = []

    def get_local_schemes(self):
        if not file_manager.folder_exists(resources.EDITOR_SKINS):
            file_manager.create_tree_folders(resources.EDITOR_SKINS)
        schemes = os.listdir(resources.EDITOR_SKINS)
        schemes = [s for s in schemes if s.lower().endswith('.color')]
        return schemes

    def _download_scheme_thread(self):
        for d in self.downloadItems:
            self.download(d[1], resources.EDITOR_SKINS)

    def download(self, url, folder):
        fileName = os.path.join(folder, os.path.basename(url))
        try:
            content = urlopen(url)
            with open(fileName, 'w') as f:
                f.write(content.read())
        except URLError:
            return
Exemple #16
0
class ChangeTargetsParametersWidget(QWidget):
    """Основной виджет, содержит кнопки управления и таблицу с редактированием параметров цели"""
    def __init__(self, parent=None) -> None:
        QWidget.__init__(self, parent)

        # Переменная для хранения списка выбранных номеров МФР, нужна для корректной отрисовки таблицы
        self.mfr_numbers_list = []
        # Хранят минимальное и максимально количество целей
        self.min_count_of_target = 1
        self.max_count_of_target = 10

        # Основные компоненты
        set_target_number_line = QLabel("Укажите количество целей")
        self.number_spin_box = QSpinBox()
        self.number_spin_box.setRange(self.min_count_of_target, self.max_count_of_target)
        self.accept_count_of_target_button = QPushButton("Изменить их параметры")
        self.cancel_count_of_target_button = QPushButton("Удалить все цели, начать ввод заново")
        # Горизонтальный контейнер для группы виджетов для указания номера цели
        set_numbers_layout = QHBoxLayout()
        set_numbers_layout.addWidget(set_target_number_line)
        set_numbers_layout.addWidget(self.number_spin_box)
        set_numbers_layout.addWidget(self.accept_count_of_target_button)
        set_numbers_layout.addWidget(self.cancel_count_of_target_button)
        # Таблица с параметрами целей
        self.target_parameters_tab = QTabWidget()
        # Контейнер с кнопками вперёд/назад
        control_layout = LayoutWithBackAndNextButtons()

        # Основной контейнер
        main_layout = QVBoxLayout(self)
        main_layout.addLayout(set_numbers_layout)
        main_layout.addWidget(self.target_parameters_tab)
        main_layout.addLayout(control_layout)

        # Это для более удобного обращения к ним
        self.next_button = control_layout.next_button
        self.back_button = control_layout.back_button

        # Связь сигналов и слотов
        self.accept_count_of_target_button.clicked.connect(self.clicked_on_accept_target_number_button)
        self.cancel_count_of_target_button.clicked.connect(self.clicked_on_cancel_target_number_button)

        # Отключение кнопок
        self.cancel_count_of_target_button.setEnabled(False)
        self.next_button.setEnabled(False)

    def append_for_each_tab_mfr_widgets_with_numbers(self, added_mfr_numbers: set) -> None:
        """Для вкладок таблицы добавить виджеты для параметров МФР с номерами

        :param added_mfr_numbers: Множество с номерами МФР, которые необходимо добавить
        :type added_mfr_numbers: set

        :return: None
        """
        for tab in self.all_tab_with_target_parameters:
            tab.add_mfr_widgets_with_numbers(added_mfr_numbers)

    def delete_for_each_tab_mfr_widgets_with_numbers(self, deleted_mfr_numbers: set) -> None:
        """Для вкладок таблицы удалить виджеты для параметров МФР с номерами

        :param deleted_mfr_numbers: Множество с номерами МФР, которые необходимо удалить
        :type deleted_mfr_numbers: set

        :return: None
        """
        for tab in self.all_tab_with_target_parameters:
            tab.delete_mfr_widgets_with_numbers(deleted_mfr_numbers)

    @property
    def parameters(self) -> dict:
        """
        :return: Параметры цели в виде словаря для каждой цели из таблицы
        :rtype: dict
        """
        return dict(zip(range(self.number_spin_box.value()),
                        [tab.parameters for tab in self.all_tab_with_target_parameters]))

    @parameters.setter
    def parameters(self, new_parameters: dict) -> None:
        """Устанавливает параметры целей

        :param new_parameters: Словарь со значением параметров для ключа равного номеру цели
        :type new_parameters: dict

        :return: None
        """
        self.mfr_numbers_list = list((new_parameters[0][KeyTarget.mfr].keys()))
        self.number_spin_box.setReadOnly(False)
        self.accept_count_of_target_button.setEnabled(True)
        self.cancel_count_of_target_button.setEnabled(False)
        self.number_spin_box.setValue(1 + int(max(new_parameters)))
        self.accept_count_of_target_button.click()
        for number_target, tab in enumerate(self.all_tab_with_target_parameters):
            tab.parameters = new_parameters[number_target]

    def clear(self) -> None:
        """Очищение виджета

        :return: None
        """
        self.number_spin_box.setReadOnly(False)
        self.number_spin_box.setValue(self.min_count_of_target)
        self.cancel_count_of_target_button.click()

    @property
    def all_tab_with_target_parameters(self) -> list:
        """
        :return: Все вкладки таблицы с параметрами цели
        :rtype: list
        """
        return [self.target_parameters_tab.widget(index) for index in range(self.target_parameters_tab.count())]

    @pyqtSlot()
    def clicked_on_accept_target_number_button(self) -> None:
        """Слот для отрисовки таблицы с параметрами для каждой цели

        :return: None
        """
        self.accept_count_of_target_button.setEnabled(False)
        self.cancel_count_of_target_button.setEnabled(True)
        self.next_button.setEnabled(True)
        self.number_spin_box.setReadOnly(True)
        for number_target in range(self.number_spin_box.value()):
            self.target_parameters_tab.addTab(TargetParametersWidget(number_target, self.mfr_numbers_list),
                                              f"Цель №{number_target}")

    @pyqtSlot()
    def clicked_on_cancel_target_number_button(self) -> None:
        """Слот для очищения таблицы

        :return: None
        """
        self.cancel_count_of_target_button.setEnabled(False)
        self.accept_count_of_target_button.setEnabled(True)
        self.next_button.setEnabled(False)
        self.number_spin_box.setReadOnly(False)
        self.number_spin_box.setValue(self.min_count_of_target)
        self.target_parameters_tab.clear()
Exemple #17
0
class MainWindow(QMainWindow):
    def __init__(self, appctx):
        super().__init__()
        self.appctx = appctx

        self.settings = QSettings("Vial", "Vial")
        if self.settings.value("size", None) and self.settings.value(
                "pos", None):
            self.resize(self.settings.value("size"))
            self.move(self.settings.value("pos"))
        else:
            self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)
        themes.set_theme(self.get_theme())

        self.combobox_devices = QComboBox()
        self.combobox_devices.currentIndexChanged.connect(
            self.on_device_selected)

        self.btn_refresh_devices = QToolButton()
        self.btn_refresh_devices.setToolButtonStyle(Qt.ToolButtonTextOnly)
        self.btn_refresh_devices.setText(tr("MainWindow", "Refresh"))
        self.btn_refresh_devices.clicked.connect(self.on_click_refresh)

        layout_combobox = QHBoxLayout()
        layout_combobox.addWidget(self.combobox_devices)
        layout_combobox.addWidget(self.btn_refresh_devices)

        self.layout_editor = LayoutEditor()
        self.keymap_editor = KeymapEditor(self.layout_editor)
        self.firmware_flasher = FirmwareFlasher(self)
        self.macro_recorder = MacroRecorder()
        self.tap_dance = TapDance()
        self.combos = Combos()
        QmkSettings.initialize(appctx)
        self.qmk_settings = QmkSettings()
        self.matrix_tester = MatrixTest(self.layout_editor)
        self.rgb_configurator = RGBConfigurator()

        self.editors = [(self.keymap_editor, "Keymap"),
                        (self.layout_editor, "Layout"),
                        (self.macro_recorder, "Macros"),
                        (self.rgb_configurator, "Lighting"),
                        (self.tap_dance, "Tap Dance"), (self.combos, "Combos"),
                        (self.qmk_settings, "QMK Settings"),
                        (self.matrix_tester, "Matrix tester"),
                        (self.firmware_flasher, "Firmware updater")]

        Unlocker.global_layout_editor = self.layout_editor

        self.current_tab = None
        self.tabs = QTabWidget()
        self.tabs.currentChanged.connect(self.on_tab_changed)
        self.refresh_tabs()

        no_devices = 'No devices detected. Connect a Vial-compatible device and press "Refresh"<br>' \
                     'or select "File" → "Download VIA definitions" in order to enable support for VIA keyboards.'
        if sys.platform.startswith("linux"):
            no_devices += '<br><br>On Linux you need to set up a custom udev rule for keyboards to be detected. ' \
                          'Follow the instructions linked below:<br>' \
                          '<a href="https://get.vial.today/manual/linux-udev.html">https://get.vial.today/manual/linux-udev.html</a>'
        self.lbl_no_devices = QLabel(tr("MainWindow", no_devices))
        self.lbl_no_devices.setTextFormat(Qt.RichText)
        self.lbl_no_devices.setAlignment(Qt.AlignCenter)

        layout = QVBoxLayout()
        layout.addLayout(layout_combobox)
        layout.addWidget(self.tabs)
        layout.addWidget(self.lbl_no_devices)
        layout.setAlignment(self.lbl_no_devices, Qt.AlignHCenter)
        self.tray_keycodes = TabbedKeycodes()
        self.tray_keycodes.make_tray()
        layout.addWidget(self.tray_keycodes)
        self.tray_keycodes.hide()
        w = QWidget()
        w.setLayout(layout)
        self.setCentralWidget(w)

        self.init_menu()

        self.autorefresh = Autorefresh()
        self.autorefresh.devices_updated.connect(self.on_devices_updated)

        # cache for via definition files
        self.cache_path = QStandardPaths.writableLocation(
            QStandardPaths.CacheLocation)
        if not os.path.exists(self.cache_path):
            os.makedirs(self.cache_path)

        # check if the via defitions already exist
        if os.path.isfile(os.path.join(self.cache_path, "via_keyboards.json")):
            with open(os.path.join(self.cache_path,
                                   "via_keyboards.json")) as vf:
                data = vf.read()
            try:
                self.autorefresh.load_via_stack(data)
            except JSONDecodeError as e:
                # the saved file is invalid - just ignore this
                logging.warning(
                    "Failed to parse stored via_keyboards.json: {}".format(e))

        # make sure initial state is valid
        self.on_click_refresh()

    def init_menu(self):
        layout_load_act = QAction(tr("MenuFile", "Load saved layout..."), self)
        layout_load_act.setShortcut("Ctrl+O")
        layout_load_act.triggered.connect(self.on_layout_load)

        layout_save_act = QAction(tr("MenuFile", "Save current layout..."),
                                  self)
        layout_save_act.setShortcut("Ctrl+S")
        layout_save_act.triggered.connect(self.on_layout_save)

        sideload_json_act = QAction(tr("MenuFile", "Sideload VIA JSON..."),
                                    self)
        sideload_json_act.triggered.connect(self.on_sideload_json)

        download_via_stack_act = QAction(
            tr("MenuFile", "Download VIA definitions"), self)
        download_via_stack_act.triggered.connect(self.load_via_stack_json)

        load_dummy_act = QAction(tr("MenuFile", "Load dummy JSON..."), self)
        load_dummy_act.triggered.connect(self.on_load_dummy)

        exit_act = QAction(tr("MenuFile", "Exit"), self)
        exit_act.setShortcut("Ctrl+Q")
        exit_act.triggered.connect(qApp.exit)

        file_menu = self.menuBar().addMenu(tr("Menu", "File"))
        file_menu.addAction(layout_load_act)
        file_menu.addAction(layout_save_act)
        file_menu.addSeparator()
        file_menu.addAction(sideload_json_act)
        file_menu.addAction(download_via_stack_act)
        file_menu.addAction(load_dummy_act)
        file_menu.addSeparator()
        file_menu.addAction(exit_act)

        keyboard_unlock_act = QAction(tr("MenuSecurity", "Unlock"), self)
        keyboard_unlock_act.triggered.connect(self.unlock_keyboard)

        keyboard_lock_act = QAction(tr("MenuSecurity", "Lock"), self)
        keyboard_lock_act.triggered.connect(self.lock_keyboard)

        keyboard_reset_act = QAction(
            tr("MenuSecurity", "Reboot to bootloader"), self)
        keyboard_reset_act.triggered.connect(self.reboot_to_bootloader)

        keyboard_layout_menu = self.menuBar().addMenu(
            tr("Menu", "Keyboard layout"))
        keymap_group = QActionGroup(self)
        selected_keymap = self.settings.value("keymap")
        for idx, keymap in enumerate(KEYMAPS):
            act = QAction(tr("KeyboardLayout", keymap[0]), self)
            act.triggered.connect(
                lambda checked, x=idx: self.change_keyboard_layout(x))
            act.setCheckable(True)
            if selected_keymap == keymap[0]:
                self.change_keyboard_layout(idx)
                act.setChecked(True)
            keymap_group.addAction(act)
            keyboard_layout_menu.addAction(act)
        # check "QWERTY" if nothing else is selected
        if keymap_group.checkedAction() is None:
            keymap_group.actions()[0].setChecked(True)

        self.security_menu = self.menuBar().addMenu(tr("Menu", "Security"))
        self.security_menu.addAction(keyboard_unlock_act)
        self.security_menu.addAction(keyboard_lock_act)
        self.security_menu.addSeparator()
        self.security_menu.addAction(keyboard_reset_act)

        self.theme_menu = self.menuBar().addMenu(tr("Menu", "Theme"))
        theme_group = QActionGroup(self)
        selected_theme = self.get_theme()
        for name, _ in [("System", None)] + themes.themes:
            act = QAction(tr("MenuTheme", name), self)
            act.triggered.connect(lambda x, name=name: self.set_theme(name))
            act.setCheckable(True)
            act.setChecked(selected_theme == name)
            theme_group.addAction(act)
            self.theme_menu.addAction(act)
        # check "System" if nothing else is selected
        if theme_group.checkedAction() is None:
            theme_group.actions()[0].setChecked(True)

        about_vial_act = QAction(tr("MenuAbout", "About Vial..."), self)
        about_vial_act.triggered.connect(self.about_vial)
        self.about_menu = self.menuBar().addMenu(tr("Menu", "About"))
        self.about_menu.addAction(about_vial_act)

    def on_layout_load(self):
        dialog = QFileDialog()
        dialog.setDefaultSuffix("vil")
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        dialog.setNameFilters(["Vial layout (*.vil)"])
        if dialog.exec_() == QDialog.Accepted:
            with open(dialog.selectedFiles()[0], "rb") as inf:
                data = inf.read()
            self.keymap_editor.restore_layout(data)
            self.rebuild()

    def on_layout_save(self):
        dialog = QFileDialog()
        dialog.setDefaultSuffix("vil")
        dialog.setAcceptMode(QFileDialog.AcceptSave)
        dialog.setNameFilters(["Vial layout (*.vil)"])
        if dialog.exec_() == QDialog.Accepted:
            with open(dialog.selectedFiles()[0], "wb") as outf:
                outf.write(self.keymap_editor.save_layout())

    def on_click_refresh(self):
        # we don't do check_protocol here either because if the matrix test tab is active,
        # that ends up corrupting usb hid packets
        self.autorefresh.update(check_protocol=False)

    def on_devices_updated(self, devices, hard_refresh):
        self.combobox_devices.blockSignals(True)

        self.combobox_devices.clear()
        for dev in devices:
            self.combobox_devices.addItem(dev.title())
            if self.autorefresh.current_device and dev.desc[
                    "path"] == self.autorefresh.current_device.desc["path"]:
                self.combobox_devices.setCurrentIndex(
                    self.combobox_devices.count() - 1)

        self.combobox_devices.blockSignals(False)

        if devices:
            self.lbl_no_devices.hide()
            self.tabs.show()
        else:
            self.lbl_no_devices.show()
            self.tabs.hide()

        if hard_refresh:
            self.on_device_selected()

    def on_device_selected(self):
        try:
            self.autorefresh.select_device(
                self.combobox_devices.currentIndex())
        except ProtocolError:
            QMessageBox.warning(
                self, "", "Unsupported protocol version!\n"
                "Please download latest Vial from https://get.vial.today/")

        if isinstance(self.autorefresh.current_device, VialKeyboard) \
                and self.autorefresh.current_device.keyboard.keyboard_id in EXAMPLE_KEYBOARDS:
            QMessageBox.warning(
                self, "", "An example keyboard UID was detected.\n"
                "Please change your keyboard UID to be unique before you ship!"
            )

        self.rebuild()
        self.refresh_tabs()

    def rebuild(self):
        # don't show "Security" menu for bootloader mode, as the bootloader is inherently insecure
        self.security_menu.menuAction().setVisible(
            isinstance(self.autorefresh.current_device, VialKeyboard))

        # if unlock process was interrupted, we must finish it first
        if isinstance(
                self.autorefresh.current_device, VialKeyboard
        ) and self.autorefresh.current_device.keyboard.get_unlock_in_progress(
        ):
            Unlocker.unlock(self.autorefresh.current_device.keyboard)
            self.autorefresh.current_device.keyboard.reload()

        for e in [
                self.layout_editor, self.keymap_editor, self.firmware_flasher,
                self.macro_recorder, self.tap_dance, self.combos,
                self.qmk_settings, self.matrix_tester, self.rgb_configurator
        ]:
            e.rebuild(self.autorefresh.current_device)

    def refresh_tabs(self):
        self.tabs.clear()
        for container, lbl in self.editors:
            if not container.valid():
                continue

            c = EditorContainer(container)
            self.tabs.addTab(c, tr("MainWindow", lbl))

    def load_via_stack_json(self):
        with urlopen(
                "https://github.com/vial-kb/via-keymap-precompiled/raw/main/via_keyboard_stack.json"
        ) as resp:
            data = resp.read()
        self.autorefresh.load_via_stack(data)
        # write to cache
        with open(os.path.join(self.cache_path, "via_keyboards.json"),
                  "wb") as cf:
            cf.write(data)

    def on_sideload_json(self):
        dialog = QFileDialog()
        dialog.setDefaultSuffix("json")
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        dialog.setNameFilters(["VIA layout JSON (*.json)"])
        if dialog.exec_() == QDialog.Accepted:
            with open(dialog.selectedFiles()[0], "rb") as inf:
                data = inf.read()
            self.autorefresh.sideload_via_json(data)

    def on_load_dummy(self):
        dialog = QFileDialog()
        dialog.setDefaultSuffix("json")
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        dialog.setNameFilters(["VIA layout JSON (*.json)"])
        if dialog.exec_() == QDialog.Accepted:
            with open(dialog.selectedFiles()[0], "rb") as inf:
                data = inf.read()
            self.autorefresh.load_dummy(data)

    def lock_ui(self):
        self.autorefresh._lock()
        self.tabs.setEnabled(False)
        self.combobox_devices.setEnabled(False)
        self.btn_refresh_devices.setEnabled(False)

    def unlock_ui(self):
        self.autorefresh._unlock()
        self.tabs.setEnabled(True)
        self.combobox_devices.setEnabled(True)
        self.btn_refresh_devices.setEnabled(True)

    def unlock_keyboard(self):
        if isinstance(self.autorefresh.current_device, VialKeyboard):
            Unlocker.unlock(self.autorefresh.current_device.keyboard)

    def lock_keyboard(self):
        if isinstance(self.autorefresh.current_device, VialKeyboard):
            self.autorefresh.current_device.keyboard.lock()

    def reboot_to_bootloader(self):
        if isinstance(self.autorefresh.current_device, VialKeyboard):
            Unlocker.unlock(self.autorefresh.current_device.keyboard)
            self.autorefresh.current_device.keyboard.reset()

    def change_keyboard_layout(self, index):
        self.settings.setValue("keymap", KEYMAPS[index][0])
        KeycodeDisplay.set_keymap_override(KEYMAPS[index][1])

    def get_theme(self):
        return self.settings.value("theme", "Dark")

    def set_theme(self, theme):
        themes.set_theme(theme)
        self.settings.setValue("theme", theme)
        msg = QMessageBox()
        msg.setText(
            tr(
                "MainWindow",
                "In order to fully apply the theme you should restart the application."
            ))
        msg.exec_()

    def on_tab_changed(self, index):
        TabbedKeycodes.close_tray()
        old_tab = self.current_tab
        new_tab = None
        if index >= 0:
            new_tab = self.tabs.widget(index)

        if old_tab is not None:
            old_tab.editor.deactivate()
        if new_tab is not None:
            new_tab.editor.activate()

        self.current_tab = new_tab

    def about_vial(self):
        QMessageBox.about(
            self, "About Vial", 'Vial {}<br><br>'
            'Licensed under the terms of the<br>GNU General Public License (version 2 or later)<br><br>'
            '<a href="https://get.vial.today/">https://get.vial.today/</a>'.
            format(self.appctx.build_settings["version"]))

    def closeEvent(self, e):
        self.settings.setValue("size", self.size())
        self.settings.setValue("pos", self.pos())

        e.accept()
Exemple #18
0
class ShardWidget(QWidget):
    def __init__(self, parent=None):
        super(ShardWidget, self).__init__(parent)

        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.add_shard_btn = QPushButton()
        self.add_shard_btn.setText("添加世界")
        self.add_shard_btn.setFixedWidth(100)
        self.del_shard_btn = QPushButton()
        self.del_shard_btn.setText("删除世界")
        self.del_shard_btn.setFixedWidth(100)
        self.save_shard_btn = QPushButton()
        self.save_shard_btn.setText("保存世界设置(修改完一定要保存)")
        self.save_as_btn = QPushButton()
        self.save_as_btn.setText("另存为预设")
        self.save_as_btn.setFixedWidth(100)
        self.load_default_btn = QPushButton()
        self.load_default_btn.setText("载入预设")
        self.load_default_btn.setFixedWidth(100)
        btn_layout = QHBoxLayout()
        btn_layout.addWidget(self.add_shard_btn)
        btn_layout.addWidget(self.del_shard_btn)
        btn_layout.addWidget(self.load_default_btn)
        btn_layout.addWidget(self.save_as_btn)
        btn_layout.addWidget(self.save_shard_btn)
        self.layout.addLayout(btn_layout)
        self.topFiller = QWidget()
        self.topFiller.setMinimumSize(835, 995)
        self.scroll = QScrollArea()
        self.scroll.setWidget(self.topFiller)
        # self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.shardtab = QTabWidget(self.topFiller)
        self.shardtab.setFixedSize(835, 995)
        # self.shardtab.setTabPosition(QTabWidget.West)
        self.layout.addWidget(self.scroll)
        self.setLayout(self.layout)
        self.initShardTab()
        self.save_shard_btn.clicked.connect(self.saveShardLevelData)
        self.del_shard_btn.clicked.connect(self.deleteShard)
        self.add_shard_btn.clicked.connect(self.addNewShard)

    def getServerList(self):
        s = SettingsWidget()
        return s.get_server_list()

    def addNewShard(self):
        slist = self.getServerList()
        self.newShardDialog = NewShardDialog(self)
        self.newShardDialog.setWindowTitle("添加服务器")
        self.newShardDialog.initServerList(slist)
        self.newShardDialog.serverSignal.connect(self.add_shard)
        self.newShardDialog.exec()

    # 初始化世界配置从文件加载世界配置并写入UI
    def loadServerIni(self, wtype, wid):
        file = os.path.join(CLUSTER_DIR, "Cluster_" + self.getCurrentCluster(),
                            wtype + "_" + str(wid), "server.ini")
        if not os.path.exists(file):
            file = os.path.join(CONFIG_DIR, "server.ini")
        self.serverconfig[wid] = GlobalConfig(file)
        self.serverconfig[wid].server_port = self.serverconfig[wid].get(
            "NETWORK", "server_port")
        self.serverconfig[wid].is_master = self.serverconfig[wid].getboolean(
            "SHARD", "is_master")
        self.serverconfig[wid].name = self.serverconfig[wid].get(
            "SHARD", "name")
        self.serverconfig[wid].id = self.serverconfig[wid].get("SHARD", "id")
        self.serverconfig[wid].master_server_port = self.serverconfig[wid].get(
            "STEAM", "master_server_port")
        self.serverconfig[wid].authentication_port = self.serverconfig[
            wid].get("STEAM", "authentication_port")
        if self.serverconfig[wid].has_section("SERVER"):
            self.serverconfig[wid].ip = self.serverconfig[wid].get(
                "SERVER", "ip")
            self.serverconfig[wid].alias = self.serverconfig[wid].get(
                "SERVER", "alias")
        else:
            self.serverconfig[wid].add_section("SERVER")
            self.serverconfig[wid].set("SERVER", "ip", "127.0.0.1")
            self.serverconfig[wid].set("SERVER", "alias",
                                       wtype + "_" + str(wid))

        if self.serverconfig[wid].server_port == "":
            self.serverconfig[wid].set("NETWORK", "server_port",
                                       str(10998 + random.randint(1, 100)))
        if self.serverconfig[wid].name == "":
            self.serverconfig[wid].set("SHARD", "name", wtype + str(wid))
        if self.serverconfig[wid].id == "":
            self.serverconfig[wid].set("SHARD", "id", str(wid))
        if self.serverconfig[wid].master_server_port == "":
            self.serverconfig[wid].set("STEAM", "master_server_port",
                                       str(27016 + random.randint(1, 100)))
        if self.serverconfig[wid].authentication_port == "":
            self.serverconfig[wid].set("STEAM", "authentication_port",
                                       str(8766 + random.randint(1, 100)))
        if self.serverconfig[wid].alias == "":
            self.serverconfig[wid].set("SERVER", "alias",
                                       wtype + "_" + str(wid))

    def setServerIni(self, wid):
        self.serverconfig[wid].set("SERVER", "ip", self.getShardIP())
        if self.ismasterR.isChecked():
            ismaster = True
        else:
            ismaster = False
        self.serverconfig[wid].setboolean("SHARD", "is_master", ismaster)

    def savaServerIni(self, wtype, wid):
        inidir = os.path.join(CLUSTER_DIR,
                              "Cluster_" + self.getCurrentCluster(),
                              wtype + "_" + str(wid))
        file = os.path.join(inidir, "server.ini")
        if not os.path.exists(inidir):
            os.mkdir(inidir)
        self.serverconfig[wid].save(file)

    def setShardIP(self, combox, ip):
        if ip == "":
            ip = "127.0.0.1"
        ClusterWidget().setServerIP(combox, ip)

    def getShardIP(self):
        iparr = self.serverCombox.currentText().split('@')
        if len(iparr) > 1:
            ip = iparr[1]
        else:
            ip = iparr[0]
        if ip == "":
            ip = "127.0.0.1"
        return ip

    def add_shard(self, shard):
        self.newShardDialog.hide()
        self.addShardTab(shard[3], 0, shard)

    def getCurrentCluster(self):
        if os.path.exists(TEMP_FILE):
            tc = GlobalConfig(TEMP_FILE)
            return tc.get("TEMP", "cluster_index")
        else:
            return "1"

    def loadShardValue(self, w, combos):
        levelfname = os.path.join(CLUSTER_DIR,
                                  "Cluster_" + self.getCurrentCluster(),
                                  w.type + "_" + str(w.id),
                                  "leveldataoverride.lua")
        saveFlag = False
        if not os.path.exists(levelfname):
            levelfname = os.path.join(CONFIG_DIR, w.type + ".lua")
            saveFlag = True
        f = open(levelfname, 'r', encoding='utf-8')
        data = f.read()
        f.close()
        data = data.replace("return", "")
        p1 = LuaTableParser()
        p1.load(data)
        w.shardValueDict = p1.dumpDict()
        if w.type == "forest" or w.type == "caves":
            for comk, comv in combos.items():
                value = w.shardValueDict["overrides"][comk]
                comindex = 0
                for v in comv.valuearr:
                    if v == value:
                        break
                    else:
                        comindex += 1
                    comv.setCurrentIndex(comindex)
        if saveFlag:
            self.saveShardLevelData()

    def initShardTab(self):
        self.shardtabs = []
        self.shardlayouts = []
        self.optionsCombobox = {}
        self.serverconfig = {}
        self.shardtab.clear()
        cdir = os.path.join(CLUSTER_DIR, "Cluster_" + self.getCurrentCluster())
        if os.path.exists(cdir):
            shard_type = ["forest", "caves", "aog", "lavaarena", "quagmire"]
            exist_shards = os.listdir(cdir)
            cindex = 0
            for file in exist_shards:
                arr = file.split("_")
                if len(arr) > 1 and arr[
                        0] in shard_type and file != "cluster_token.txt":
                    self.addShardTab(arr[0], int(arr[1]), shard=[])
                    cindex += 1

    def addShardTab(self, world, wid, shard):
        sindex = len(self.shardtabs)
        self.shardtabs.append(QWidget())
        self.shardtabs[sindex].type = world
        self.shardtabs[sindex].id = wid == 0 and str(random.randint(
            100, 999)) or wid
        self.loadServerIni(self.shardtabs[sindex].type,
                           self.shardtabs[sindex].id)
        wid = self.shardtabs[sindex].id
        if len(shard) > 0:
            self.serverconfig[wid].set("SERVER", "ip", shard[1])
            self.serverconfig[wid].set("SERVER", "alias", shard[0])
            self.serverconfig[wid].setboolean("SHARD", "is_master", shard[2])
            self.savaServerIni(self.shardtabs[sindex].type,
                               self.shardtabs[sindex].id)

        self.shardtab.addTab(
            self.shardtabs[sindex],
            self.serverconfig[wid].get("SERVER", "alias") + " " +
            str(sindex + 1))

        shardlayout = QVBoxLayout()
        comboboxObject = {}

        # shard ip, ismaster
        shardserverlayout = QHBoxLayout()
        serverlaber = QLabel()
        serverlaber.setText("服务器:")
        serverlaber.setFixedWidth(60)
        self.serverCombox = QComboBox()
        self.serverCombox.setStyleSheet(
            "QComboBox QAbstractItemView::item { min-height: 25px; min-width: 100px; }"
        )
        self.serverCombox.setItemDelegate(QStyledItemDelegate())
        shardserverlayout.addWidget(serverlaber)
        shardserverlayout.addWidget(self.serverCombox)
        serverlaber1 = QLabel()
        serverlaber1.setText("世界属性:")
        serverlaber1.setFixedWidth(70)
        ismasterGroup = QButtonGroup()
        self.ismasterR = QRadioButton()
        self.notmasterR = QRadioButton()
        self.ismasterR.setText("主世界")
        self.ismasterR.setFixedWidth(80)
        self.notmasterR.setText("附从世界")
        ismasterGroup.addButton(self.ismasterR)
        ismasterGroup.addButton(self.notmasterR)
        shardserverlayout.addWidget(serverlaber1)
        shardserverlayout.addWidget(self.ismasterR)
        shardserverlayout.addWidget(self.notmasterR)
        shardlayout.addLayout(shardserverlayout)

        if self.serverconfig[self.shardtabs[sindex].id].getboolean(
                "SHARD", "is_master"):
            self.ismasterR.setChecked(True)
        else:
            self.notmasterR.setChecked(True)
        self.setShardIP(
            self.serverCombox,
            self.serverconfig[self.shardtabs[sindex].id].get("SERVER", "ip"))
        if world == "forest" or world == "caves":
            optionsDict = self.readShardOptions(world)
            typedict = {
                "environment": "世界环境",
                "source": "资源",
                "food": "食物",
                "animal": "动物",
                "monster": "怪物"
            }
            for tk, tv in typedict.items():
                tlabel = QPushButton()
                tlabel.setText(tv)
                shardlayout.addWidget(tlabel)
                oindex, ooindex = 0, 0
                for olist in optionsDict[tk]:
                    if oindex + 4 < 5:
                        ollayout = QHBoxLayout()
                        shardlayout.addLayout(ollayout)
                        ollayout.setAlignment
                    ollabel = QLabel()
                    ollabel.setText(olist["name"])
                    ollabel.setFixedWidth(60)
                    comboboxObject[olist["key"]] = QComboBox()
                    comboboxObject[olist["key"]].setFixedWidth(120)
                    comboboxObject[olist["key"]].setStyleSheet(
                        "QComboBox QAbstractItemView::item { min-height: 25px; min-width: 80px; }"
                    )
                    comboboxObject[olist["key"]].setItemDelegate(
                        QStyledItemDelegate())
                    comboboxObject[olist["key"]].addItems(
                        olist["options"]["label"])
                    comboboxObject[
                        olist["key"]].valuearr = olist["options"]["value"]
                    ollayout.addWidget(ollabel, stretch=0)
                    ollayout.addWidget(comboboxObject[olist["key"]], stretch=0)

                    ollayout.addStretch()

                    if oindex < 3:
                        oindex += 1
                    else:
                        oindex = 0
                    ooindex += 1
        else:
            tip = QLabel()
            tip.setText("当前世界类型不支持修改设置!")
            tip.setAlignment(Qt.AlignHCenter)
            shardlayout.addWidget(tip)
        self.shardtabs[sindex].setLayout(shardlayout)

        self.optionsCombobox[self.shardtabs[sindex].id] = comboboxObject
        self.loadShardValue(self.shardtabs[sindex],
                            self.optionsCombobox[self.shardtabs[sindex].id])

    def readShardOptions(self, filename):
        file = os.path.join(CONFIG_DIR, filename + ".json")
        if os.path.exists(file):
            with open(file, 'r', encoding="utf-8") as f:
                data = json.load(f)
        else:
            data = {}
        return data

    def saveShardLevelData(self):
        for w in self.shardtabs:
            # w = self.shardtab.currentWidget()
            sdir = os.path.join(CLUSTER_DIR,
                                "Cluster_" + self.getCurrentCluster(),
                                w.type + "_" + str(w.id))
            levelfname = os.path.join(sdir, "leveldataoverride.lua")
            # print(w.shardValueDict)
            if w.type == "forest" or w.type == "caves":
                for comk, comv in self.optionsCombobox[w.id].items():
                    comindex = comv.currentIndex()
                    w.shardValueDict['overrides'][comk] = comv.valuearr[
                        comindex]
            p1 = LuaTableParser()
            p1.loadDict(w.shardValueDict)
            data = "return" + p1.dump()
            if not os.path.exists(sdir):
                os.mkdir(sdir)
            with open(levelfname, 'w', encoding='utf-8') as f:
                f.write(data)
                f.close()
            self.setServerIni(w.id)
            self.savaServerIni(w.type, w.id)

    def deleteShard(self):
        if len(self.shardtabs) > 0:
            w = self.shardtab.currentWidget()
            sdir = os.path.join(CLUSTER_DIR,
                                "Cluster_" + self.getCurrentCluster(),
                                w.type + "_" + str(w.id))
            if os.path.exists(sdir):
                shutil.rmtree(sdir)
            ci = self.shardtab.currentIndex()
            self.shardtab.removeTab(ci)
            self.shardtabs.remove(w)
Exemple #19
0
class Output2_Widget(QWidget):
    # Add a signal
    def __init__(self):
        super(Output2_Widget, self).__init__()
        self.init_ui()

    def init_ui(self):
        self.setGeometry(QtCore.QRect(10, 20, 700, 700))
        self.setAccessibleName("output2")
        self.setFont(QtGui.QFont("Calibri", 10))
        self.faqtree = QtWidgets.QTreeWidget(self)
        self.faqtree.setHeaderLabel('FAQ')
        self.faqtree.setUniformRowHeights(False)
        self.faqtree.setWordWrap(True)
        self.faqtree.setFont(QtGui.QFont("Calibri", 10))
        self.importfaq("output")
        self.faqtree.header().resizeSection(1, 200)
        self.faqtree.setItemDelegate(ItemWordWrap(self.faqtree))
        self.status2 = QTextEdit("Status.")
        self.status2.setReadOnly(True)
        self.status2.setVisible(False)
        self.status2.setFrameShape(QtWidgets.QFrame.NoFrame)

        script_dir = os.path.dirname(
            os.path.dirname(__file__))  # give parent path

        self.tab_summary = QTextEdit(
            "Choose simulation by checking from the list box. Simulation outputs are \
categorized into 5 types and are displayed individually in bottom tabbed panel."
        )
        self.tab_summary.setReadOnly(True)
        # no scroll bars
        self.tab_summary.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.tab_summary.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.tab_summary.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Minimum)  # horizontal, vertical
        self.tab_summary.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.tab_summary.setMaximumHeight(40)  # need it
        self.helpcheckbox = QCheckBox("Turn FAQ on?")
        self.helpcheckbox.setChecked(False)
        self.helpcheckbox.stateChanged.connect(self.controlfaq)
        self.faqtree.setVisible(False)

        self.vl1 = QVBoxLayout()
        self.hl1 = QHBoxLayout()
        self.vl1.setAlignment(QtCore.Qt.AlignTop)
        self.mainlayout1 = QGridLayout()
        self.spacer = QSpacerItem(10, 10, QSizePolicy.Minimum,
                                  QSizePolicy.Expanding)
        self.vl1.setContentsMargins(0, 0, 0, 0)
        self.vl1.setSpacing(1)
        self.vl1.addWidget(self.tab_summary)
        self.vl1.addWidget(self.helpcheckbox)
        self.table2 = QTableWidget()

        self.plotoutput = QPushButton("Select Simulation")
        self.deleteSim = QPushButton("Delete Simulation")
        self.buttonhlayout = QVBoxLayout()
        self.buttonhlayout.addWidget(self.plotoutput)
        self.buttonhlayout.addWidget(self.deleteSim)
        self.buttonhlayout.addStretch(1)
        self.display1 = QTabWidget()
        self.statistic_toollist = ['hourly', 'daily']

        self.table2.setSizeAdjustPolicy(
            QtWidgets.QAbstractScrollArea.AdjustToContents)
        self.table2.setFixedHeight(75)
        self.table2.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers)
        self.table2.setAlternatingRowColors(True)
        self.error_tuplelist = []
        self.populate()
        self.table2.setHorizontalHeaderLabels([
            'SimID', 'Site', 'Treatment', 'Station Type', 'Weather', 'Soil',
            'Year'
        ])
        self.table2.horizontalHeader().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.table2.horizontalHeader().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self.table2.horizontalHeader().setSectionResizeMode(
            2, QHeaderView.ResizeToContents)
        self.table2.horizontalHeader().setSectionResizeMode(
            3, QHeaderView.ResizeToContents)
        self.table2.horizontalHeader().setSectionResizeMode(
            4, QHeaderView.ResizeToContents)
        self.table2.horizontalHeader().setSectionResizeMode(
            5, QHeaderView.ResizeToContents)
        self.table2.horizontalHeader().setSectionResizeMode(
            6, QHeaderView.ResizeToContents)
        self.table2.verticalHeader().hide()
        self.table2.resizeColumnsToContents()
        self.plotoutput.clicked.connect(self.on_click_table_widget)
        self.deleteSim.clicked.connect(self.on_deletebuttonclick)

        self.hl1.addWidget(self.table2)
        self.hl1.addLayout(self.buttonhlayout)
        self.vl1.addLayout(self.hl1)
        self.vl1.addWidget(self.display1)
        self.vl1.addStretch(1)
        self.display1.setVisible(False)
        self.mainlayout1.addLayout(self.vl1, 0, 0)
        self.mainlayout1.setColumnStretch(0, 3)
        self.mainlayout1.addWidget(self.faqtree, 0, 4)

        #add a scroll bar to window
        self.scrollArea = QtWidgets.QScrollArea()  # self.centralWidget)
        self.scrollContent = QtWidgets.QWidget(self.scrollArea)
        self.scrollContent.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Expanding)
        self.scrollContent.setLayout(self.mainlayout1)
        self.scrollArea.setLayout(self.mainlayout1)
        self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.scrollArea)
        self.setLayout(self.layout)

    def make_connection(self, rotation_object):
        rotation_object.rotationsig.connect(self.populate)

    def populate(self):
        rlist = extract_pastrunsidDB()
        # When setRowCount is set to 0, the table gets refreshed.
        self.table2.setRowCount(0)
        self.table2.setRowCount(len(rlist))
        self.table2.setColumnCount(7)
        self.table2.simGrp = QButtonGroup()
        for row1 in range(len(rlist)):
            i = 0
            for col in range(len(rlist[row1])):
                if i == 0:
                    radioitem = QRadioButton(str(rlist[row1][col]))
                    self.table2.simGrp.addButton(radioitem, i)
                    self.table2.setCellWidget(row1, col, radioitem)
                else:
                    self.table2.setItem(
                        row1, col, QTableWidgetItem(str(rlist[row1][col])))
                i = i + 1

    def on_click_plotPlantTab(self):
        checkedVar = []
        for i, checkbox in enumerate(self.checkboxes):
            if checkbox.isChecked():
                checkedVar.append(checkbox.text())

    # Create dictionary to hold dataframes
        df_collection = {}
        print("Debug: Outputtab:plantTab_plot1, self.simulationID=",
              self.simulationID)
        t4 = extract_cropOutputData(self.g01Tablename, self.simulationID)
        tableID = self.g01Tablename + "_id"
        if (self.cropname == "corn"):
            t4.drop(columns=[tableID, 'jday', 'Note'], inplace=True)
        else:
            t4.drop(columns=[tableID, 'jday'], inplace=True)

        new_df = t4['Date_Time'].str.split(' ', n=1, expand=True)
        t4['Date'] = new_df[0]
        t4['Date'] = pd.to_datetime(t4['Date'])
        t4_grouped = t4.groupby(['Date'], as_index=False).agg(self.varFuncDict)
        t4_grouped.rename(columns={'Date': 'Date_Time'}, inplace=True)
        t4_grouped['Date_Time'] = pd.to_datetime(t4_grouped['Date_Time'])

        self.plantTab.fig.clear()
        self.plantTab.canvas.flush_events()
        self.plantTab.ax = self.plantTab.fig.add_subplot(111)
        self.plantTab.ax.cla()
        for var in checkedVar:
            self.plantTab.ax.plot(t4_grouped['Date_Time'],
                                  t4_grouped[var],
                                  label=var)

        self.plantTab.ax.legend(loc='upper left',
                                bbox_to_anchor=(1.05, 1),
                                ncol=2,
                                borderaxespad=0)
        self.plantTab.fig.subplots_adjust(right=0.55)
        self.plantTab.canvas.draw()

    def on_click_plotSoilTab(self):
        self.varFuncSoilDict = {
            'hNew': 'mean',
            'thNew': 'mean',
            'ConcN': 'mean',
            'Temp': 'mean'
        }
        self.varSoilDict = {"hNew":"Soil Matric Potential\n(cm suction)","thNew":"Soil Water Content\n(cm3/cm3)","ConcN":"Nitrogen Concentration\n(mg/L)",\
                               "Temp":"Temperature\n(oC)"}

        date = self.comboDate.currentText()
        df_collection = {}
        t3 = extract_cropOutputData(self.g03Tablename, self.simulationID)
        tableID = self.g03Tablename + "_id"
        t3.drop(columns={tableID}, inplace=True)
        new_df = t3['Date_Time'].str.split(' ', n=1, expand=True)
        t3['Date'] = new_df[0]
        t3_grouped = t3.groupby(['Date', 'X', 'Y'],
                                as_index=False).agg(self.varFuncSoilDict)
        t3_grouped.rename(columns={'Date': 'Date_Time'}, inplace=True)
        df_collection = t3_grouped.loc[t3_grouped['Date_Time'] == date].filter(
            ['hNew', 'thNew', 'ConcN', 'Temp', 'X', 'Y'], axis=1)

        rows = 1
        columns = 4
        param = ["hNew", "thNew", "ConcN", "Temp"]

        ## Create image
        self.soilwhnTab.fig.clear()
        self.soilwhnTab.canvas.flush_events()
        for var, i in zip(param, range(1, 5)):
            title = self.varSoilDict[var]
            #title, unit = self.varSoilDict[var]
            new_df = df_collection.filter(['X', 'Y', var], axis=1)
            new_arr = new_df.values
            colorMap = "cool"
            if var == "hNew":
                colorMap = "cool_r"
                new_df[var] = new_df[var].abs()
            nx = new_df['X'].nunique()
            ny = new_df['Y'].nunique()
            x = new_arr[:, 0].reshape(nx, ny)
            y = new_arr[:, 1].reshape(nx, ny)
            z = new_arr[:, 2].reshape(nx, ny)
            maxY = max(map(max, y))
            y = maxY - y
            self.soilwhnTab.ax = self.soilwhnTab.fig.add_subplot(
                rows, columns, i)
            self.soilwhnTab.ax.invert_yaxis()
            levels = MaxNLocator(nbins=15).tick_values(z.min(), z.max())
            norm = BoundaryNorm(levels, ncolors=colorMap, clip=True)
            if var == "hNew":
                cf = self.soilwhnTab.ax.contourf(x,
                                                 y,
                                                 z,
                                                 locator=ticker.LogLocator(),
                                                 cmap=colorMap)
            else:
                cf = self.soilwhnTab.ax.contourf(x,
                                                 y,
                                                 z,
                                                 levels=levels,
                                                 cmap=colorMap)
            cb = self.soilwhnTab.fig.colorbar(cf,
                                              ax=self.soilwhnTab.ax,
                                              shrink=0.9)
            if var == "hNew":
                cb.ax.invert_yaxis()
            cb.ax.tick_params(labelsize=7)
            self.soilwhnTab.ax.set_title(title, size="medium")
            self.soilwhnTab.ax.set_ylabel('Depth (cm)')
            if i > 1:
                self.soilwhnTab.ax.get_yaxis().set_visible(False)
            plt.tight_layout()
            self.soilwhnTab.canvas.draw()
            plt.tight_layout()

    def on_click_rootTab(self):
        self.varFuncRootDict = {'RDenT': 'max', 'RMassT': 'max'}
        self.varRootDict = {
            "RDenT": "Root Density Total (g/cm2)",
            "RMassT": "Root Mass Total (g/cm2)"
        }

        date = self.comboDateRoot.currentText()
        df_collection = {}
        t4 = extract_cropOutputData(self.g04Tablename, self.simulationID)
        tableID = self.g04Tablename + "_id"
        t4.drop(columns={tableID}, inplace=True)
        new_df = t4['Date_Time'].str.split(' ', n=1, expand=True)
        t4['Date'] = new_df[0]
        # Creating RDenT and RMassT variables
        t4['RDenT'] = t4['RDenM'] + t4['RDenY']
        t4['RMassT'] = t4['RMassM'] + t4['RMassY']
        t4_grouped = t4.groupby(['Date', 'X', 'Y'],
                                as_index=False).agg(self.varFuncRootDict)
        t4_grouped.rename(columns={'Date': 'Date_Time'}, inplace=True)
        df_collection = t4_grouped.loc[t4_grouped['Date_Time'] == date].filter(
            ['RDenT', 'RMassT', 'X', 'Y'], axis=1)

        rows = 1
        columns = 2
        param = ['RDenT', 'RMassT']

        ## Create image
        self.rootTab.fig.clear()
        self.rootTab.canvas.flush_events()
        for var, i in zip(param, range(1, 5)):
            title = self.varRootDict[var]
            new_df = df_collection.filter(['X', 'Y', var], axis=1)
            new_arr = new_df.values
            colorMap = "cool"
            nx = new_df['X'].nunique()
            ny = new_df['Y'].nunique()
            x = new_arr[:, 0].reshape(nx, ny)
            y = new_arr[:, 1].reshape(nx, ny)
            z = new_arr[:, 2].reshape(nx, ny)
            maxY = max(map(max, y))
            y = maxY - y
            self.rootTab.ax = self.rootTab.fig.add_subplot(rows, columns, i)
            self.rootTab.ax.invert_yaxis()
            levels = MaxNLocator(nbins=15).tick_values(z.min(), z.max())
            norm = BoundaryNorm(levels, ncolors=colorMap, clip=True)
            cf = self.rootTab.ax.contourf(x,
                                          y,
                                          z,
                                          levels=levels,
                                          cmap=colorMap)
            cb = self.rootTab.fig.colorbar(cf, ax=self.rootTab.ax, shrink=0.9)
            cb.ax.tick_params(labelsize=7)
            self.rootTab.ax.set_title(title, size="medium")
            self.rootTab.ax.set_ylabel('Depth (cm)')
            if i > 1:
                self.rootTab.ax.get_yaxis().set_visible(False)
            plt.tight_layout()
            self.rootTab.canvas.draw()
            plt.tight_layout()

    def on_click_plotSurfChaTab(self):
        self.surfChaVarDict = {'PSoilEvap':'Potential Soil Evaporation','ASoilEVap':'Actual Soil Evaporation','PE_T_int':'Potential Transpiration','transp':'Actual Tranpiration',\
                               'SeasPTran':'Cumulative Potential Transpiration','SeasATran':'Cumulative Actual Transpiration','SeasRain':'Cumulative Rain','SeasInfil':'Infiltration',\
                               'Runoff':'Runoff'}

        checkedVar = []
        for i, checkbox in enumerate(self.surfChaCheckboxes):
            if checkbox.isChecked():
                checkedVar.append(checkbox.text())

    # Create dictionary to hold dataframes
        df_collection = {}
        t5 = extract_cropOutputData(self.g05Tablename, self.simulationID)
        tableID = self.g05Tablename + "_id"

        new_df = t5['Date_Time'].str.split(' ', n=1, expand=True)
        t5['Date'] = new_df[0]
        t5_grouped = t5.groupby(['Date'],
                                as_index=False).agg(self.surfChaVarFuncDict)
        t5_grouped.rename(columns={'Date': 'Date_Time'}, inplace=True)
        t5_grouped['Date_Time'] = pd.to_datetime(t5_grouped['Date_Time'])

        self.surfChaTab.ax = self.surfChaTab.fig.add_subplot(111)
        self.surfChaTab.ax.cla()
        for var in checkedVar:
            # Convert to cm of water/cm2
            t5_grouped[var] = t5_grouped[var] * self.plantDensity / 10000
            self.surfChaTab.ax.plot(t5_grouped['Date_Time'],
                                    t5_grouped[var],
                                    label=var)

        self.surfChaTab.ax.legend(loc='upper left',
                                  bbox_to_anchor=(1.05, 1),
                                  ncol=2,
                                  borderaxespad=0)
        self.surfChaTab.fig.subplots_adjust(right=0.55)
        self.surfChaTab.canvas.draw()

    def on_deletebuttonclick(self):
        '''
        This function gets called when user chooses to delete a simulation. Simulations on cropOutput database tables and information on pastruns table on crop 
        database will be deleted and the simulation directory will be deleted as well.
        '''
        delete_flag = messageUserDelete(
            "Are you sure you want to delete this simulation?")
        if delete_flag:
            self.rowNumChecked = [
                self.table2.simGrp.buttons()[x].isChecked()
                for x in range(len(self.table2.simGrp.buttons()))
            ].index(True)
            self.simulationID = self.table2.simGrp.buttons()[
                self.rowNumChecked].text()
            self.cropname = self.table2.item(self.rowNumChecked,
                                             2).text().split('/')[0]

            # First delete the directory that was creates with the simulation information
            sim_dir = os.path.join(run_dir, self.simulationID)
            shutil.rmtree(sim_dir, ignore_errors=True)

            # Delete simulation from pastruns
            delete_pastrunsDB(self.simulationID)

            # Delete simulations on the cropOutput database tables
            delete_cropOutputSim(self.simulationID, self.cropname)
            self.populate()

    def on_click_table_widget(self):
        '''
        This gets called when USER clicks one of the old simulation row/column. 
        It will plot the graph(s) for the selected simulation
        '''
        global img, data, i, updateTime, fps
        regexp_num = QtCore.QRegExp('\d+(\.\d+)?')
        validator_num = QtGui.QRegExpValidator(regexp_num)

        self.rowNumChecked = [
            self.table2.simGrp.buttons()[x].isChecked()
            for x in range(len(self.table2.simGrp.buttons()))
        ].index(True)
        self.simulationID = self.table2.simGrp.buttons()[
            self.rowNumChecked].text()
        self.sitename = self.table2.item(self.rowNumChecked, 1).text()
        self.cropname = self.table2.item(self.rowNumChecked,
                                         2).text().split('/')[0]
        self.experimentname = self.table2.item(self.rowNumChecked,
                                               2).text().split('/')[1]
        self.treatmentname = self.table2.item(self.rowNumChecked,
                                              2).text().split('/')[2]
        self.stationtypename = self.table2.item(self.rowNumChecked, 3).text()
        self.soilname = self.table2.item(self.rowNumChecked, 4).text()
        self.g01Tablename = "g01_" + self.cropname
        self.g03Tablename = "g03_" + self.cropname
        self.g04Tablename = "g04_" + self.cropname
        self.g05Tablename = "g05_" + self.cropname

        self.display1.clear()
        self.simTab = QWidget()
        self.plantTab = QWidget()
        self.soilwhnTab = QWidget()
        self.rootTab = QWidget()
        self.surfChaTab = QWidget()  # g06 outputs

        ########## simTab ##########
        #add a scroll bar to window
        self.simTabWidget = QtWidgets.QWidget(self.simTab)
        self.simTab.mainlayout = QFormLayout(self.simTabWidget)
        self.simTabWidget.setLayout(self.simTab.mainlayout)
        self.simTab.setLayout(self.simTab.mainlayout)

        genInfoBox = QHBoxLayout()

        genInfoBoxSum = QVBoxLayout()
        genInfoBoxSumLabel = QLabel()
        genInfoBoxSum.addWidget(genInfoBoxSumLabel)
        genInfoBoxSum.setAlignment(Qt.AlignTop)

        genInfoBoxDates = QVBoxLayout()
        genInfoBoxDatesLabel = QLabel()
        genInfoBoxDates.addWidget(genInfoBoxDatesLabel)
        genInfoBoxDates.setAlignment(Qt.AlignTop)

        genInfoBoxAgroDates = QVBoxLayout()
        genInfoBoxAgroDatesLabel = QLabel()
        genInfoBoxAgroDates.addWidget(genInfoBoxAgroDatesLabel)
        genInfoBoxAgroDates.setAlignment(Qt.AlignTop)

        envInfoBoxData = QVBoxLayout()
        envInfoBoxDataLabel = QLabel()
        envInfoBoxData.addWidget(envInfoBoxDataLabel)
        envInfoBoxData.setAlignment(Qt.AlignTop)

        genInfoBox.addLayout(genInfoBoxSum)
        genInfoBox.addLayout(genInfoBoxDates)
        genInfoBox.addLayout(genInfoBoxAgroDates)
        genInfoBox.addLayout(envInfoBoxData)
        genInfoBox.setAlignment(Qt.AlignTop)

        self.simulationSumTable = QTableWidget()
        self.simTab.mainlayout.addRow(genInfoBox)
        self.simTab.mainlayout.addRow(self.simulationSumTable)
        searchlist = [
            'Initial Field Values', 'Simulation Start', 'Sowing',
            'Fertilizer-N', 'Emergence', 'Harvest'
        ]

        exid = read_experimentDB_id(self.cropname, self.experimentname)
        tid = read_treatmentDB_id(exid, self.treatmentname)
        plantDensity = getPlantDensity(tid)
        operationList = read_operationsDB_id(tid)  #gets all the operations
        FertilizerDateList = []
        for searchrecord in searchlist:
            for ii, jj in enumerate(operationList):
                if searchrecord in jj:
                    if searchrecord in 'Simulation Start':
                        BeginDate = jj[3]  #month/day/year

                    if searchrecord in 'Sowing':
                        SowingDate = jj[3]  #month/day/year

                    if searchrecord in "Fertilizer-N":
                        FertilizerDateList.append(jj[3])  #month/day/year

                    if searchrecord in 'Emergence':
                        EmergenceDate = jj[3]  #month/day/year

                    if searchrecord in 'Harvest':
                        HarvestDate = jj[3]  #month/day/year

                    if searchrecord in 'Initial Field Values':
                        self.cultivar = jj[12]
                        self.plantDensity = jj[4]

        FertilizerDate = ""
        if len(FertilizerDateList) >= 1:
            FertilizerDate = ", "
            FertilizerDate = FertilizerDate.join(FertilizerDateList)

        self.simSummaryGen = "<i>General Information </i>"
        self.simSummaryGen += "<br><i>Site: </i>" + self.sitename
        self.simSummaryGen += "<br><i>Soil: </i>" + self.soilname
        self.simSummaryGen += "<br><i>Weather: </i>" + self.stationtypename
        self.simSummaryGen += "<br><i>Crop: </i>" + self.cropname
        self.simSummaryGen += "<br><i>Cultivar: </i>" + self.cultivar
        self.simSummaryGen += "<br><i>Experiment: </i>" + self.experimentname
        self.simSummaryGen += "<br><i>Treatment: </i>" + self.treatmentname
        genInfoBoxSumLabel.setText(self.simSummaryGen)

        self.simSummaryDates = "<i>Simulation Dates </i>"
        self.simSummaryDates += "<br><i>Start Date: </i>" + BeginDate
        self.simSummaryDates += "<br><i>Planting Date: </i>" + SowingDate
        self.simSummaryDates += "<br><i>Fertilization Date: </i>" + FertilizerDate
        if self.cropname == "potato":
            TuberInitDate = getTuberInitDate(self.simulationID)
            MaturityDate = getMaturityDate(self.simulationID)
            self.simSummaryDates += "<br><i>Emergence Date: </i>" + EmergenceDate
            self.simSummaryDates += "<br><i>Tuber Initiation Date: </i>" + TuberInitDate
            self.simSummaryDates += "<br><i>Maturity Date: </i>" + MaturityDate
        elif self.cropname == "corn":
            EmergenceDate = getCornDateByDev(self.simulationID, "Emerged")
            TasseledDate = getCornDateByDev(self.simulationID, "Tasseled")
            SilkedDate = getCornDateByDev(self.simulationID, "Silked")
            MaturityDate = getCornDateByDev(self.simulationID, "Matured")
            self.simSummaryDates += "<br><i>Emergence Date: </i>" + EmergenceDate
            self.simSummaryDates += "<br><i>Tasseled Date: </i>" + TasseledDate
            self.simSummaryDates += "<br><i>Silked Date: </i>" + SilkedDate
            self.simSummaryDates += "<br><i>Maturity Date: </i>" + MaturityDate
        self.simSummaryDates += "<br><i>Harvest Date: </i>" + HarvestDate
        genInfoBoxDatesLabel.setText(self.simSummaryDates)

        if self.cropname == "potato":
            agroDataTuple = getPotatoAgronomicData(self.simulationID,
                                                   HarvestDate)
            NitrogenUptakeTuple = getPotatoNitrogenUptake(
                self.simulationID, HarvestDate)
            envDataTuple = getEnvironmentalData(self.simulationID, HarvestDate,
                                                self.cropname)
            self.envSummaryData = "<i>Simulation Environmental Data at <br>" + HarvestDate + " (harvest date)</i>"
            self.simSummaryAgroDates = "<i>Simulation Agronomic Data at <br>" + HarvestDate + " (harvest date)</i>"
            self.simSummaryAgroDates += "<br><i>Yield: </i>" + '{:3.2f}'.format(
                agroDataTuple[0] * plantDensity * 10) + " kg/ha"
            self.simSummaryAgroDates += "<br><i>Total biomass: </i>" + '{:3.2f}'.format(
                agroDataTuple[1] * plantDensity * 10) + " kg/ha"
            self.simSummaryAgroDates += "<br><i>Nitrogen Uptake: </i>" + '{:3.2f}'.format(
                NitrogenUptakeTuple[0] * plantDensity / 100) + " kg/ha"
            self.simSummaryAgroDates += "<br><i>Transpiration: </i>" + '{:3.2f}'.format(
                agroDataTuple[2] * plantDensity / 1000) + " mm"
        elif self.cropname == "corn":
            if (MaturityDate != "N/A"):
                agroDataTuple = getCornAgronomicData(self.simulationID,
                                                     MaturityDate)
                envDataTuple = getEnvironmentalData(self.simulationID,
                                                    MaturityDate,
                                                    self.cropname)
                self.envSummaryData = "<i>Simulation Environmental Data at <br>" + MaturityDate + " (maturity date)</i>"
                self.simSummaryAgroDates = "<i>Simulation Agronomic Data at <br>" + MaturityDate + " (maturity date)</i>"
            else:
                agroDataTuple = getCornAgronomicData(self.simulationID,
                                                     HarvestDate)
                envDataTuple = getEnvironmentalData(self.simulationID,
                                                    HarvestDate, self.cropname)
                self.envSummaryData = "<i>Simulation Environmental Data at <br>" + HarvestDate + " (harvest date)</i>"
                self.simSummaryAgroDates = "<i>Simulation Agronomic Data at <br>" + HarvestDate + " (harvest date)</i>"
            self.simSummaryAgroDates += "<br><i>Yield: </i>" + '{:3.2f}'.format(
                agroDataTuple[0] * plantDensity * 10) + " kg/ha"
            self.simSummaryAgroDates += "<br><i>Total biomass: </i>" + '{:3.2f}'.format(
                agroDataTuple[1] * plantDensity * 10) + " kg/ha"
            self.simSummaryAgroDates += "<br><i>Nitrogen Uptake: </i>" + '{:3.2f}'.format(
                agroDataTuple[2] * plantDensity * 10) + " kg/ha"
        genInfoBoxAgroDatesLabel.setText(self.simSummaryAgroDates)

        self.envSummaryData += "<br><i>Total Potential Transpiration: </i>" + '{:3.2f}'.format(
            envDataTuple[0] * plantDensity / 1000) + " mm"
        self.envSummaryData += "<br><i>Total Actual Transpiration: </i>" + '{:3.2f}'.format(
            envDataTuple[1] * plantDensity / 1000) + " mm"
        self.envSummaryData += "<br><i>Total Potential Soil Evaporation: </i>" + '{:3.2f}'.format(
            envDataTuple[2] * plantDensity / 1000) + " mm"
        self.envSummaryData += "<br><i>Total Actual Soil Evaporation: </i>" + '{:3.2f}'.format(
            envDataTuple[3] * plantDensity / 1000) + " mm"
        self.envSummaryData += "<br><i>Total Drainage: </i>" + '{:3.2f}'.format(
            envDataTuple[4] * plantDensity / 1000) + " mm"
        self.envSummaryData += "<br><i>Total Infiltration: </i>" + '{:3.2f}'.format(
            envDataTuple[5] * plantDensity / 1000) + " mm"
        self.envSummaryData += "<br><i>Total Runoff: </i>" + '{:3.2f}'.format(
            envDataTuple[6] * plantDensity / 1000) + " mm"
        self.envSummaryData += "<br><i>Total Rain: </i>" + '{:3.2f}'.format(
            envDataTuple[7] * plantDensity / 1000) + " mm"
        envInfoBoxDataLabel.setText(self.envSummaryData)

        if self.cropname == "potato":
            NitroWaterStressDatesTuple = getNitroWaterStressDates(
                self.simulationID)
            self.simulationSumTable.setSizeAdjustPolicy(
                QtWidgets.QAbstractScrollArea.AdjustToContents)
            self.simulationSumTable.setFixedHeight(220)
            self.simulationSumTable.setFixedWidth(510)
            self.simulationSumTable.setRowCount(
                len(NitroWaterStressDatesTuple))
            self.simulationSumTable.setColumnCount(5)
            i = 0
            for record in NitroWaterStressDatesTuple:
                j = 0
                for col in record:
                    if j == 0:
                        date = dt.strptime(
                            col, '%Y-%m-%d %H:%M:%S').strftime('%m/%d/%Y')
                        self.simulationSumTable.setItem(
                            i, j, QTableWidgetItem(str(date)))
                    else:
                        colFormat = '{:3.2f}'.format(col)
                        if col <= 0.75:
                            self.simulationSumTable.setItem(
                                i, j, QTableWidgetItem(str(colFormat)))
                            self.simulationSumTable.item(i, j).setForeground(
                                QColor(255, 0, 0))
                        else:
                            self.simulationSumTable.setItem(
                                i, j, QTableWidgetItem(str(colFormat)))
                        self.simulationSumTable.item(i, j).setTextAlignment(
                            Qt.AlignHCenter)
                    j = j + 1
                i = i + 1
            self.simulationSumTable.setHorizontalHeaderLabels(['Date','Water stress on\nleaf expansion','Nitrogen stress on\nleaf expansion',\
                                                               'Water stress on\nleaf photosynthesis','Nitrogen stress on\nphotosynthesis'])
            self.simulationSumTable.horizontalHeader().setSectionResizeMode(
                0, QHeaderView.ResizeToContents)
            self.simulationSumTable.horizontalHeader().setSectionResizeMode(
                1, QHeaderView.ResizeToContents)
            self.simulationSumTable.horizontalHeader().setSectionResizeMode(
                2, QHeaderView.ResizeToContents)
            self.simulationSumTable.horizontalHeader().setSectionResizeMode(
                3, QHeaderView.ResizeToContents)
            self.simulationSumTable.horizontalHeader().setSectionResizeMode(
                4, QHeaderView.ResizeToContents)
            self.simulationSumTable.verticalHeader().hide()
            self.simulationSumTable.resizeColumnsToContents()
        elif self.cropname == "corn":
            NitroWaterStressDatesTuple = getCornPlantStressDates(
                self.simulationID)
            self.simulationSumTable.setSizeAdjustPolicy(
                QtWidgets.QAbstractScrollArea.AdjustToContents)
            self.simulationSumTable.setFixedHeight(220)
            self.simulationSumTable.setFixedWidth(400)
            self.simulationSumTable.setRowCount(
                len(NitroWaterStressDatesTuple))
            self.simulationSumTable.setColumnCount(5)
            i = 0
            for record in NitroWaterStressDatesTuple:
                j = 0
                for col in record:
                    if j == 0:
                        date = dt.strptime(
                            col, '%Y-%m-%d %H:%M:%S').strftime('%m/%d/%Y')
                        self.simulationSumTable.setItem(
                            i, j, QTableWidgetItem(str(date)))
                    else:
                        colFormat = '{:3.2f}'.format(col)
                        if col >= 0.75:
                            self.simulationSumTable.setItem(
                                i, j, QTableWidgetItem(str(colFormat)))
                            self.simulationSumTable.item(i, j).setForeground(
                                QColor(255, 0, 0))
                        else:
                            self.simulationSumTable.setItem(
                                i, j, QTableWidgetItem(str(colFormat)))
                        self.simulationSumTable.item(i, j).setTextAlignment(
                            Qt.AlignHCenter)
                    j = j + 1
                i = i + 1
            self.simulationSumTable.setHorizontalHeaderLabels([
                'Date', 'Water stress', 'Nitrogen stress', 'Carbon stress',
                'Potential Area'
            ])
            self.simulationSumTable.horizontalHeader().setSectionResizeMode(
                0, QHeaderView.ResizeToContents)
            self.simulationSumTable.horizontalHeader().setSectionResizeMode(
                1, QHeaderView.ResizeToContents)
            self.simulationSumTable.horizontalHeader().setSectionResizeMode(
                2, QHeaderView.ResizeToContents)
            self.simulationSumTable.horizontalHeader().setSectionResizeMode(
                3, QHeaderView.ResizeToContents)
            self.simulationSumTable.horizontalHeader().setSectionResizeMode(
                4, QHeaderView.ResizeToContents)
            self.simulationSumTable.verticalHeader().hide()
            self.simulationSumTable.resizeColumnsToContents()

        self.simTab.setLayout(self.simTab.mainlayout)

        ########## plantTab ##########
        if (self.cropname == "corn"):
            self.varFuncDict = {
                'Leaves': 'max',
                'MaturLvs': 'max',
                'Dropped': 'max',
                'LA_pl': 'max',
                'LA_dead': 'max',
                'LAI': 'max',
                'RH': 'mean',
                'LeafWP': 'mean',
                'PFD': 'sum',
                'SolRad': 'sum',
                'SoilT': 'mean',
                'Tair': 'mean',
                'Tcan': 'mean',
                'ETdmd': 'sum',
                'ETsply': 'sum',
                'Pn': 'sum',
                'Pg': 'sum',
                'Respir': 'sum',
                'av_gs': 'mean',
                'VPD': 'mean',
                'Nitr': 'mean',
                'N_Dem': 'sum',
                'NUpt': 'sum',
                'LeafN': 'max',
                'PCRL': 'max',
                'totalDM': 'max',
                'shootDM': 'max',
                'earDM': 'max',
                'TotLeafDM': 'max',
                'DrpLfDM': 'max',
                'stemDM': 'max',
                'rootDM': 'max',
                'SoilRt': 'max',
                'MxRtDep': 'max',
                'AvailW': 'max',
                'solubleC': 'max'
            }
        else:
            self.varFuncDict = {
                'LAI': 'max',
                'Tcan': 'mean',
                'Pgross': 'sum',
                'Rg+Rm': 'sum',
                'Tr-Pot': 'sum',
                'Tr-Act': 'sum',
                'Stage': 'max',
                'totalDM': 'max',
                'leafDM': 'max',
                'stemDM': 'max',
                'rootDM': 'max',
                'tuberDM': 'max',
                'deadDM': 'max',
                'LWPpd': 'max',
                'LWPave': 'mean',
                'gs_ave': 'mean'
            }

        self.plantTab.fig = plt.figure()
        self.plantTab.canvas = FigureCanvas(self.plantTab.fig)

        self.plantTab.groupBox = QGroupBox("Select parameter to plot")
        self.plantTab.groupBox.setMaximumWidth(200)
        self.vboxLayout = QGridLayout()

        self.checkboxes = []
        i = 0
        for var in self.varFuncDict:
            checkbox = QtWidgets.QCheckBox(var)
            self.checkboxes.append(checkbox)
            j = i // 2
            if i % 2 == 0:
                self.vboxLayout.addWidget(checkbox, j, 0)
            else:
                self.vboxLayout.addWidget(checkbox, j, 1)
            i += 1
        j += 1

        self.plotButtom = QPushButton("Plot")

        self.vboxLayout.addWidget(self.plotButtom, j, 0, 1, 2)
        #self.vboxLayout.addStretch()
        self.plantTab.groupBox.setLayout(self.vboxLayout)
        self.plotButtom.clicked.connect(self.on_click_plotPlantTab)

        self.plantTab.mainlayout = QHBoxLayout()
        self.plantTab.mainlayout.addWidget(self.plantTab.groupBox)
        self.plantTab.mainlayout.addWidget(self.plantTab.canvas)
        self.plantTab.setLayout(self.plantTab.mainlayout)

        ########## Soil Water Heat Nitrogen components ##########
        self.soilwhnTab.fig = plt.figure()
        self.soilwhnTab.canvas = FigureCanvas(self.soilwhnTab.fig)
        self.soilwhnTab.groupBox = QGroupBox()

        # Create and populate date combo
        t3 = extract_cropOutputData(self.g03Tablename, self.simulationID)
        tableID = self.g03Tablename + "_id"
        t3.drop(columns={tableID}, inplace=True)
        new_df = t3['Date_Time'].str.split(' ', n=1, expand=True)
        t3['Date'] = new_df[0]
        dateList = t3['Date'].unique()

        self.comboDate = QComboBox()
        for date in dateList:
            self.comboDate.addItem(date)

        self.dateselectionlabel = QLabel("Select Date")
        self.hboxLayoutSoil = QHBoxLayout()
        self.hboxLayoutSoil.addWidget(self.dateselectionlabel)
        self.hboxLayoutSoil.addWidget(self.comboDate)

        self.plotButtomSoil = QPushButton("Plot")

        self.hboxLayoutSoil.addWidget(self.plotButtomSoil)
        self.hboxLayoutSoil.addStretch()
        self.soilwhnTab.groupBox.setLayout(self.hboxLayoutSoil)
        self.plotButtomSoil.clicked.connect(self.on_click_plotSoilTab)

        self.soilwhnTab.mainlayout = QVBoxLayout()
        self.soilwhnTab.mainlayout.addWidget(self.soilwhnTab.groupBox)
        self.soilwhnTab.mainlayout.addWidget(self.soilwhnTab.canvas)
        self.soilwhnTab.setLayout(self.soilwhnTab.mainlayout)

        ########## Root tab ##########
        self.rootTab.fig = plt.figure()
        self.rootTab.canvas = FigureCanvas(self.rootTab.fig)
        self.rootTab.groupBox = QGroupBox()

        t4 = extract_cropOutputData(self.g04Tablename, self.simulationID)
        tableID = self.g04Tablename + "_id"
        t4.drop(columns={tableID}, inplace=True)
        new_df = t4['Date_Time'].str.split(' ', n=1, expand=True)
        t4['Date'] = new_df[0]
        dateList = t4['Date'].unique()

        self.comboDateRoot = QComboBox()
        for date in dateList:
            self.comboDateRoot.addItem(date)

        self.dateselectionlabel = QLabel("Select Date")
        self.hboxLayoutRoot = QHBoxLayout()
        self.hboxLayoutRoot.addWidget(self.dateselectionlabel)
        self.hboxLayoutRoot.addWidget(self.comboDateRoot)

        self.plotButtomRoot = QPushButton("Plot")

        self.hboxLayoutRoot.addWidget(self.plotButtomRoot)
        self.hboxLayoutRoot.addStretch()
        self.rootTab.groupBox.setLayout(self.hboxLayoutRoot)
        self.plotButtomRoot.clicked.connect(self.on_click_rootTab)

        self.rootTab.mainlayout = QVBoxLayout()
        self.rootTab.mainlayout.addWidget(self.rootTab.groupBox)
        self.rootTab.mainlayout.addWidget(self.rootTab.canvas)
        self.rootTab.setLayout(self.rootTab.mainlayout)

        ### Surface Characteristics tab ###
        self.surfChaVarFuncDict = {'PSoilEvap':'max','ASoilEVap':'max','PE_T_int':'max','transp':'max','SeasPSoEv':'max',\
                                   'SeasASoEv':'max','SeasPTran':'max','SeasATran':'max','SeasRain':'max','SeasInfil':'max',\
                                   'Runoff':'max'}

        self.surfChaTab.fig = plt.figure()
        self.surfChaTab.canvas = FigureCanvas(self.surfChaTab.fig)

        self.surfChaTab.groupBox = QGroupBox("Select parameter to plot")
        self.surfChaTab.groupBox.setMaximumWidth(150)
        self.vboxSurfChaLayout = QVBoxLayout()

        self.surfChaCheckboxes = []
        for var in self.surfChaVarFuncDict:
            checkbox = QtWidgets.QCheckBox(var)
            self.surfChaCheckboxes.append(checkbox)
            self.vboxSurfChaLayout.addWidget(checkbox)

        self.surfChaPlotButtom = QPushButton("Plot")

        self.vboxSurfChaLayout.addWidget(self.surfChaPlotButtom)
        self.vboxSurfChaLayout.addStretch()
        self.surfChaTab.groupBox.setLayout(self.vboxSurfChaLayout)
        self.surfChaPlotButtom.clicked.connect(self.on_click_plotSurfChaTab)

        self.surfChaTab.mainlayout = QHBoxLayout()
        self.surfChaTab.mainlayout.addWidget(self.surfChaTab.groupBox)
        self.surfChaTab.mainlayout.addWidget(self.surfChaTab.canvas)
        self.surfChaTab.setLayout(self.surfChaTab.mainlayout)
        #################################################################################

        if self.simulationID != None:
            self.display1.addTab(self.simTab, "Simulation Summary")
            self.display1.addTab(self.plantTab, "Plant")
            self.display1.addTab(self.soilwhnTab, "Soil Water Heat Nitrogen")
            self.display1.addTab(self.rootTab, "Root Data")
            self.display1.addTab(self.surfChaTab, "Surface Characteristics")
            self.display1.setVisible(True)

    def importfaq(self, thetabname=None):
        faqlist = read_FaqDB(thetabname)
        faqcount = 0

        for item in faqlist:
            roottreeitem = QTreeWidgetItem(self.faqtree)
            roottreeitem.setText(0, item[2])
            childtreeitem = QTreeWidgetItem()
            childtreeitem.setText(0, item[3])
            roottreeitem.addChild(childtreeitem)

    def controlfaq(self):
        if self.helpcheckbox.isChecked():
            self.faqtree.setVisible(True)
        else:
            self.faqtree.setVisible(False)
	def clear ( self ):
		QTabWidget.clear ( self )
Exemple #21
0
class AppWindow(QMainWindow):
    onRestart = pyqtSignal(name='onRestart')
    onSystemUIElementCreated = pyqtSignal(str,
                                          QWidget,
                                          name='onSystemUIElementCreated')
    onSystemUIElementRemoved = pyqtSignal(str, name='onSystemUIElementRemoved')

    def __init__(self, dwarf_args, flags=None):
        super(AppWindow, self).__init__(flags)

        self.dwarf_args = dwarf_args

        self.session_manager = SessionManager(self)
        self.session_manager.sessionCreated.connect(self.session_created)
        self.session_manager.sessionStopped.connect(self.session_stopped)
        self.session_manager.sessionClosed.connect(self.session_closed)

        self._tab_order = [
            'debug', 'modules', 'ranges', 'jvm-inspector', 'jvm-debugger'
        ]

        self._is_newer_dwarf = False
        self.q_settings = QSettings("dwarf_window_pos.ini",
                                    QSettings.IniFormat)

        self.menu = self.menuBar()
        self.view_menu = None

        self._initialize_ui_elements()

        self.setWindowTitle(
            'Dwarf - A debugger for reverse engineers, crackers and security analyst'
        )

        # load external assets
        _app = QApplication.instance()

        self.remove_tmp_dir()

        # themes
        self.prefs = Prefs()
        utils.set_theme(self.prefs.get('dwarf_ui_theme', 'black'), self.prefs)

        # load font
        if os.path.exists(utils.resource_path('assets/Anton.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/Anton.ttf'))
        if os.path.exists(utils.resource_path('assets/OpenSans-Regular.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/OpenSans-Regular.ttf'))
            font = QFont("OpenSans", 9, QFont.Normal)
            # TODO: add settingsdlg
            font_size = self.prefs.get('dwarf_ui_font_size', 12)
            font.setPixelSize(font_size)
            _app.setFont(font)
            if os.path.exists(utils.resource_path('assets/OpenSans-Bold.ttf')):
                QFontDatabase.addApplicationFont(
                    utils.resource_path('assets/OpenSans-Bold.ttf'))

        # mainwindow statusbar
        self.progressbar = QProgressBar()
        self.progressbar.setRange(0, 0)
        self.progressbar.setVisible(False)
        self.progressbar.setFixedHeight(15)
        self.progressbar.setFixedWidth(100)
        self.progressbar.setTextVisible(False)
        self.progressbar.setValue(30)
        self.statusbar = QStatusBar(self)
        self.statusbar.setAutoFillBackground(False)
        self.statusbar.addPermanentWidget(self.progressbar)
        self.statusbar.setObjectName("statusbar")
        self.setStatusBar(self.statusbar)

        self.main_tabs = QTabWidget(self)
        self.main_tabs.setMovable(False)
        self.main_tabs.setTabsClosable(True)
        self.main_tabs.setAutoFillBackground(True)
        self.main_tabs.tabCloseRequested.connect(self._on_close_tab)
        self.setCentralWidget(self.main_tabs)

        # pluginmanager
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.reload_plugins()

        if dwarf_args.any == '':
            self.welcome_window = WelcomeDialog(self)
            self.welcome_window.setModal(True)
            self.welcome_window.onIsNewerVersion.connect(
                self._enable_update_menu)
            self.welcome_window.onUpdateComplete.connect(
                self._on_dwarf_updated)
            self.welcome_window.setWindowTitle(
                'Welcome to Dwarf - A debugger for reverse engineers, crackers and security analyst'
            )
            self.welcome_window.onSessionSelected.connect(self._start_session)
            # wait for welcome screen
            self.hide()
            self.welcome_window.show()
        else:
            print('* Starting new Session')
            self._start_session(dwarf_args.target)

    def _initialize_ui_elements(self):
        # dockwidgets
        self.watchers_dwidget = None
        self.hooks_dwiget = None
        self.bookmarks_dwiget = None
        self.registers_dock = None
        self.console_dock = None
        self.backtrace_dock = None
        self.threads_dock = None
        # panels
        self.asm_panel = None
        self.backtrace_panel = None
        self.bookmarks_panel = None
        self.console_panel = None
        self.context_panel = None
        self.debug_panel = None
        self.contexts_list_panel = None
        self.data_panel = None
        self.ftrace_panel = None
        self.hooks_panel = None
        self.java_inspector_panel = None
        self.java_explorer_panel = None
        self.java_trace_panel = None
        self.modules_panel = None
        self.ranges_panel = None
        self.search_panel = None
        self.smali_panel = None
        self.watchers_panel = None
        self.welcome_window = None

        self._ui_elems = []

    def _setup_main_menu(self):
        self.menu = self.menuBar()
        dwarf_menu = QMenu('Dwarf', self)
        theme = QMenu('Theme', dwarf_menu)
        theme.addAction('Black')
        theme.addAction('Dark')
        theme.addAction('Light')
        theme.triggered.connect(self._set_theme)
        dwarf_menu.addMenu(theme)
        dwarf_menu.addSeparator()

        if sys.platform == 'linux' or sys.platform == 'darwin':
            dwarf_bin_path = os.path.join(
                '/'.join(os.path.realpath(__file__).split('/')[:-2]),
                'bin/dwarf')
            if not os.path.exists(dwarf_bin_path):
                dwarf_menu.addAction('Create launcher', utils.create_launcher)
                dwarf_menu.addSeparator()

        if self._is_newer_dwarf:
            dwarf_menu.addAction('Update', self._update_dwarf)
        dwarf_menu.addAction('Close', self.session_manager.session.stop)
        self.menu.addMenu(dwarf_menu)

        session = self.session_manager.session
        if session is not None:
            session_menu = session.main_menu
            if isinstance(session_menu, list):
                for menu in session_menu:
                    self.menu.addMenu(menu)
            else:
                self.menu.addMenu(session_menu)

        # plugins
        if self.plugin_manager.plugins:
            self.plugin_menu = QMenu('Plugins', self)
            for plugin in self.plugin_manager.plugins:
                plugin_instance = self.plugin_manager.plugins[plugin]
                plugin_sub_menu = self.plugin_menu.addMenu(
                    plugin_instance.name)

                try:
                    actions = plugin_instance.__get_top_menu_actions__()
                    for action in actions:
                        plugin_sub_menu.addAction(action)
                except:
                    pass

                if not plugin_sub_menu.isEmpty():
                    plugin_sub_menu.addSeparator()

                about = plugin_sub_menu.addAction('About')
                about.triggered.connect(
                    lambda x, item=plugin: self._show_plugin_about(item))

            if not self.plugin_menu.isEmpty():
                self.menu.addMenu(self.plugin_menu)

        self.view_menu = QMenu('View', self)
        self.panels_menu = QMenu('Panels', self.view_menu)
        self.panels_menu.addAction('Search',
                                   lambda: self.show_main_tab('search'),
                                   shortcut=QKeySequence(Qt.CTRL + Qt.Key_F3))

        self.view_menu.addMenu(self.panels_menu)

        self.debug_view_menu = self.view_menu.addMenu('Debug')

        self.view_menu.addSeparator()

        self.view_menu.addAction('Hide all',
                                 self._hide_all_widgets,
                                 shortcut=QKeySequence(Qt.CTRL + Qt.Key_F1))
        self.view_menu.addAction('Show all',
                                 self._show_all_widgets,
                                 shortcut=QKeySequence(Qt.CTRL + Qt.Key_F2))

        self.view_menu.addSeparator()

        self.menu.addMenu(self.view_menu)

        if self.dwarf_args.debug_script:
            debug_menu = QMenu('Debug', self)
            debug_menu.addAction('Reload core', self._menu_reload_core)
            debug_menu.addAction('Debug dwarf js core',
                                 self._menu_debug_dwarf_js)
            self.menu.addMenu(debug_menu)

        # tools
        _tools = self.prefs.get('tools')
        if _tools:
            tools_menu = QMenu('Tools', self)

            for _tool in _tools:
                if _tool and _tool['name']:
                    if _tool['name'] == 'sep':
                        tools_menu.addSeparator()
                        continue

                    _cmd = _tool['cmd']

                    tools_menu.addAction(_tool['name'])

            if not tools_menu.isEmpty():
                tools_menu.triggered.connect(self._execute_tool)
                self.menu.addMenu(tools_menu)

        about_menu = QMenu('About', self)
        about_menu.addAction('Dwarf on GitHub', self._menu_github)
        about_menu.addAction('Documention', self._menu_documentation)
        about_menu.addAction('Api', self._menu_api)
        about_menu.addAction('Slack', self._menu_slack)
        about_menu.addSeparator()

        about_menu.addAction('Info', self._show_about_dlg)
        self.menu.addMenu(about_menu)

    def _show_plugin_about(self, plugin):
        plugin = self.plugin_manager.plugins[plugin]
        if plugin:
            info = plugin.__get_plugin_info__()

            version = utils.safe_read_map(info, 'version', '')
            description = utils.safe_read_map(info, 'description', '')
            author = utils.safe_read_map(info, 'author', '')
            homepage = utils.safe_read_map(info, 'homepage', '')
            license_ = utils.safe_read_map(info, 'license', '')

            utils.show_message_box(
                'Name: {0}\nVersion: {1}\nDescription: {2}\nAuthor: {3}\nHomepage: {4}\nLicense: {5}'
                .format(plugin.name, version, description, author, homepage,
                        license_))

    def _enable_update_menu(self):
        self._is_newer_dwarf = True

    def _update_dwarf(self):
        if self.welcome_window:
            self.welcome_window._update_dwarf()

    def _on_close_tab(self, index):
        tab_text = self.main_tabs.tabText(index)
        if tab_text:
            tab_text = tab_text.lower().replace(' ', '-')
            if tab_text in self.session_manager.session.non_closable:
                return
            try:
                self._ui_elems.remove(tab_text)
            except ValueError:  # recheck ValueError: list.remove(x): x not in list
                pass
            self.main_tabs.removeTab(index)

            self.onSystemUIElementRemoved.emit(tab_text)

    def _handle_tab_change(self):
        for index in range(self.main_tabs.count()):
            tab_name = self.main_tabs.tabText(index).lower().replace(' ', '-')
            if tab_name in self.session_manager.session.non_closable:
                self.main_tabs.tabBar().setTabButton(index, QTabBar.RightSide,
                                                     None)

                if tab_name in self._tab_order:
                    should_index = self._tab_order.index(tab_name)
                    if index != should_index:
                        self.main_tabs.tabBar().moveTab(index, should_index)

    def _on_dwarf_updated(self):
        self.onRestart.emit()

    def remove_tmp_dir(self):
        if os.path.exists('.tmp'):
            shutil.rmtree('.tmp', ignore_errors=True)

    def _execute_tool(self, qaction):
        if qaction:
            _tools = self.prefs.get('tools')
            if _tools:
                for _tool in _tools:
                    if _tool and _tool['name'] and _tool['name'] != 'sep':
                        if qaction.text() == _tool['name']:
                            try:
                                import subprocess
                                subprocess.Popen(_tool['cmd'],
                                                 creationflags=subprocess.
                                                 CREATE_NEW_CONSOLE)
                            except:
                                pass
                            break

    def _set_theme(self, qaction):
        if qaction:
            utils.set_theme(qaction.text(), self.prefs)

    def _hide_all_widgets(self):
        self.watchers_dwidget.hide()
        self.hooks_dwiget.hide()
        self.bookmarks_dwiget.hide()
        self.registers_dock.hide()
        self.console_dock.hide()
        self.backtrace_dock.hide()
        self.threads_dock.hide()

    def _show_all_widgets(self):
        self.watchers_dwidget.show()
        self.hooks_dwiget.show()
        self.bookmarks_dwiget.show()
        self.registers_dock.show()
        self.console_dock.show()
        self.backtrace_dock.show()
        self.threads_dock.show()

    def _menu_reload_core(self):
        self.dwarf.script.exports.reload()

    def _menu_debug_dwarf_js(self):
        you_know_what_to_do = json.loads(
            self.dwarf.script.exports.debugdwarfjs())
        return you_know_what_to_do

    def show_main_tab(self, name):
        name = name.lower()
        # elem doesnt exists? create it
        if name not in self._ui_elems:
            self._create_ui_elem(name)

        index = 0
        name = name.join(name.split()).lower()
        if name == 'ranges':
            index = self.main_tabs.indexOf(self.ranges_panel)
        elif name == 'search':
            index = self.main_tabs.indexOf(self.search_panel)
        elif name == 'modules':
            index = self.main_tabs.indexOf(self.modules_panel)
        elif name == 'data':
            index = self.main_tabs.indexOf(self.data_panel)
        elif name == 'jvm-tracer':
            index = self.main_tabs.indexOf(self.java_trace_panel)
        elif name == 'jvm-inspector':
            index = self.main_tabs.indexOf(self.java_inspector_panel)
        elif name == 'jvm-debugger':
            index = self.main_tabs.indexOf(self.java_explorer_panel)
        elif name == 'smali':
            index = self.main_tabs.indexOf(self.smali_panel)

        self.main_tabs.setCurrentIndex(index)

    def jump_to_address(self, ptr, view=0, show_panel=True):
        if show_panel:
            self.show_main_tab('debug')
        self.debug_panel.jump_to_address(ptr, view=view)

    @pyqtSlot(name='mainMenuGitHub')
    def _menu_github(self):
        QDesktopServices.openUrl(QUrl('https://github.com/iGio90/Dwarf'))

    @pyqtSlot(name='mainMenuApi')
    def _menu_api(self):
        QDesktopServices.openUrl(
            QUrl('http://www.giovanni-rocca.com/dwarf/javascript/'))

    @pyqtSlot(name='mainMenuDocumentation')
    def _menu_documentation(self):
        QDesktopServices.openUrl(QUrl('http://www.giovanni-rocca.com/dwarf/'))

    @pyqtSlot(name='mainMenuSlack')
    def _menu_slack(self):
        QDesktopServices.openUrl(
            QUrl('https://join.slack.com/t/resecret/shared_invite'
                 '/enQtMzc1NTg4MzE3NjA1LTlkNzYxNTIwYTc2ZTYyOWY1MT'
                 'Q1NzBiN2ZhYjQwYmY0ZmRhODQ0NDE3NmRmZjFiMmE1MDYwN'
                 'WJlNDVjZDcwNGE'))

    def _show_about_dlg(self):
        about_dlg = AboutDialog(self)
        about_dlg.show()

    def _create_ui_elem(self, elem):
        elem = elem.lower()

        if not isinstance(elem, str):
            return

        if elem not in self._ui_elems:
            self._ui_elems.append(elem)

        elem_wiget = None

        if elem == 'watchers':
            from ui.session_widgets.watchers import WatchersWidget
            self.watchers_dwidget = QDockWidget('Watchers', self)
            self.watchers_panel = WatchersWidget(self)
            # dont respond to dblclick mem cant be shown
            # self.watchers_panel.onItemDoubleClicked.connect(
            #    self._on_watcher_clicked)
            self.watchers_panel.onItemRemoved.connect(
                self._on_watcher_removeditem)
            self.watchers_panel.onItemAdded.connect(self._on_watcher_added)
            self.watchers_dwidget.setWidget(self.watchers_panel)
            self.watchers_dwidget.setObjectName('WatchersWidget')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.watchers_dwidget)
            self.view_menu.addAction(self.watchers_dwidget.toggleViewAction())
            elem_wiget = self.watchers_panel
        elif elem == 'hooks':
            from ui.session_widgets.hooks import HooksWidget
            self.hooks_dwiget = QDockWidget('Breakpoints', self)
            self.hooks_panel = HooksWidget(self)
            self.hooks_panel.onHookRemoved.connect(self._on_hook_removed)
            self.hooks_dwiget.setWidget(self.hooks_panel)
            self.hooks_dwiget.setObjectName('HooksWidget')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.hooks_dwiget)
            self.view_menu.addAction(self.hooks_dwiget.toggleViewAction())
            elem_wiget = self.hooks_panel
        elif elem == 'bookmarks':
            from ui.session_widgets.bookmarks import BookmarksWidget
            self.bookmarks_dwiget = QDockWidget('Boomarks', self)
            self.bookmarks_panel = BookmarksWidget(self)
            self.bookmarks_dwiget.setWidget(self.bookmarks_panel)
            self.bookmarks_dwiget.setObjectName('BookmarksWidget')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.bookmarks_dwiget)
            self.view_menu.addAction(self.bookmarks_dwiget.toggleViewAction())
            elem_wiget = self.bookmarks_panel
        elif elem == 'registers':
            from ui.session_widgets.context import ContextWidget
            self.registers_dock = QDockWidget('Context', self)
            self.context_panel = ContextWidget(self)
            self.registers_dock.setWidget(self.context_panel)
            self.registers_dock.setObjectName('ContextWidget')
            self.addDockWidget(Qt.RightDockWidgetArea, self.registers_dock)
            self.view_menu.addAction(self.registers_dock.toggleViewAction())
            elem_wiget = self.context_panel
        elif elem == 'debug':
            from ui.panels.panel_debug import QDebugPanel
            self.debug_panel = QDebugPanel(self)
            self.main_tabs.addTab(self.debug_panel, 'Debug')
            elem_wiget = self.debug_panel
        elif elem == 'jvm-debugger':
            from ui.panels.panel_java_explorer import JavaExplorerPanel
            self.java_explorer_panel = JavaExplorerPanel(self)
            self.main_tabs.addTab(self.java_explorer_panel, 'JVM debugger')
            self.main_tabs.tabBar().moveTab(
                self.main_tabs.indexOf(self.java_explorer_panel), 1)
            elem_wiget = self.java_explorer_panel
        elif elem == 'jvm-inspector':
            from ui.panels.panel_java_inspector import JavaInspector
            self.java_inspector_panel = JavaInspector(self)
            self.main_tabs.addTab(self.java_inspector_panel, 'JVM inspector')
            elem_wiget = self.java_inspector_panel
        elif elem == 'console':
            from ui.session_widgets.console import ConsoleWidget
            self.console_dock = QDockWidget('Console', self)
            self.console_panel = ConsoleWidget(self)
            if self.dwarf_args.script and len(
                    self.dwarf_args.script) > 0 and os.path.exists(
                        self.dwarf_args.script):
                with open(self.dwarf_args.script, 'r') as f:
                    self.console_panel.get_js_console(
                    ).script_file = self.dwarf_args.script
                    self.console_panel.get_js_console(
                    ).function_content = f.read()
            self.dwarf.onLogToConsole.connect(self._log_js_output)
            self.dwarf.onLogEvent.connect(self._log_event)
            self.console_dock.setWidget(self.console_panel)
            self.console_dock.setObjectName('ConsoleWidget')
            self.addDockWidget(Qt.BottomDockWidgetArea, self.console_dock)
            self.view_menu.addAction(self.console_dock.toggleViewAction())
            elem_wiget = self.console_panel
        elif elem == 'backtrace':
            from ui.session_widgets.backtrace import BacktraceWidget
            self.backtrace_dock = QDockWidget('Backtrace', self)
            self.backtrace_panel = BacktraceWidget(self)
            self.backtrace_dock.setWidget(self.backtrace_panel)
            self.backtrace_dock.setObjectName('BacktraceWidget')
            self.backtrace_panel.onShowMemoryRequest.connect(
                self._on_showmemory_request)
            self.addDockWidget(Qt.RightDockWidgetArea, self.backtrace_dock)
            self.view_menu.addAction(self.backtrace_dock.toggleViewAction())
            elem_wiget = self.backtrace_panel
        elif elem == 'threads':
            from ui.session_widgets.threads import ThreadsWidget
            self.threads_dock = QDockWidget('Threads', self)
            self.contexts_list_panel = ThreadsWidget(self)
            self.dwarf.onThreadResumed.connect(
                self.contexts_list_panel.resume_tid)
            self.contexts_list_panel.onItemDoubleClicked.connect(
                self._manually_apply_context)
            self.threads_dock.setWidget(self.contexts_list_panel)
            self.threads_dock.setObjectName('ThreadPanel')
            self.addDockWidget(Qt.RightDockWidgetArea, self.threads_dock)
            self.view_menu.addAction(self.threads_dock.toggleViewAction())
            elem_wiget = self.contexts_list_panel
        elif elem == 'modules':
            from ui.panels.panel_modules import ModulesPanel
            self.modules_panel = ModulesPanel(self)
            self.modules_panel.onModuleSelected.connect(
                self._on_module_dblclicked)
            self.modules_panel.onModuleFuncSelected.connect(
                self._on_modulefunc_dblclicked)
            self.modules_panel.onAddHook.connect(self._on_addmodule_hook)
            self.modules_panel.onDumpBinary.connect(self._on_dumpmodule)
            self.main_tabs.addTab(self.modules_panel, 'Modules')
            elem_wiget = self.modules_panel
        elif elem == 'ranges':
            from ui.panels.panel_ranges import RangesPanel
            self.ranges_panel = RangesPanel(self)
            self.ranges_panel.onItemDoubleClicked.connect(
                self._range_dblclicked)
            self.ranges_panel.onDumpBinary.connect(self._on_dumpmodule)
            # connect to watcherpanel func
            self.ranges_panel.onAddWatcher.connect(
                self.watchers_panel.do_addwatcher_dlg)
            self.main_tabs.addTab(self.ranges_panel, 'Ranges')
            elem_wiget = self.ranges_panel
        elif elem == 'search':
            from ui.panels.panel_search import SearchPanel
            self.search_panel = SearchPanel(self)
            self.main_tabs.addTab(self.search_panel, 'Search')
            elem_wiget = self.search_panel
        elif elem == 'data':
            from ui.panels.panel_data import DataPanel
            self.data_panel = DataPanel(self)
            self.main_tabs.addTab(self.data_panel, 'Data')
            elem_wiget = self.data_panel
        elif elem == 'jvm-tracer':
            from ui.panels.panel_java_trace import JavaTracePanel
            self.java_trace_panel = JavaTracePanel(self)
            self.main_tabs.addTab(self.java_trace_panel, 'JVM tracer')
            elem_wiget = self.java_trace_panel
        elif elem == 'smali':
            from ui.panels.panel_smali import SmaliPanel
            self.smali_panel = SmaliPanel()
            self.main_tabs.addTab(self.smali_panel, 'Smali')
            elem_wiget = self.smali_panel
        else:
            print('no handler for elem: ' + elem)

        # make tabs unclosable and sort
        self._handle_tab_change()

        if elem_wiget is not None:
            self.onSystemUIElementCreated.emit(elem, elem_wiget)

        # TODO: remove add @2x
        for item in self.findChildren(QDockWidget):
            if item:
                if 'darwin' in sys.platform:
                    item.setStyleSheet(
                        'QDockWidget::title { padding-left:-30px; }')

    def set_status_text(self, txt):
        self.statusbar.showMessage(txt)

    # ************************************************************************
    # **************************** Properties ********************************
    # ************************************************************************
    @property
    def disassembly(self):
        return self.asm_panel

    @property
    def backtrace(self):
        return self.backtrace_panel

    @property
    def console(self):
        return self.console_panel

    @property
    def context(self):
        return self.context_panel

    @property
    def threads(self):
        return self.contexts_list_panel

    @property
    def ftrace(self):
        return self.ftrace_panel

    @property
    def hooks(self):
        return self.hooks_panel

    @property
    def java_inspector(self):
        return self.java_inspector_panel

    @property
    def java_explorer(self):
        return self.java_explorer_panel

    @property
    def modules(self):
        return self.modules_panel

    @property
    def ranges(self):
        return self.ranges_panel

    @property
    def watchers(self):
        return self.watchers_panel

    @property
    def dwarf(self):
        if self.session_manager.session is not None:
            return self.session_manager.session.dwarf
        else:
            return None

    @property
    def ui_elements(self):
        return self._ui_elems

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    # session handlers
    def _start_session(self, session_type, session_data=None):
        if self.welcome_window is not None:
            self.welcome_window.close()
        self.session_manager.create_session(session_type,
                                            session_data=session_data)

    def _restore_session(self, session_data):
        if 'session' in session_data:
            session_type = session_data['session']
            self.dwarf_args.any = session_data['package']
            self._start_session(session_type, session_data=session_data)

    def session_created(self):
        # session init done create ui for it
        session = self.session_manager.session
        self._setup_main_menu()
        for ui_elem in session.session_ui_sections:
            ui_elem = ui_elem.join(ui_elem.split()).lower()
            self._create_ui_elem(ui_elem)

        self.dwarf.onProcessAttached.connect(self._on_attached)
        self.dwarf.onProcessDetached.connect(self._on_detached)
        self.dwarf.onScriptLoaded.connect(self._on_script_loaded)

        # hookup
        self.dwarf.onSetRanges.connect(self._on_setranges)
        self.dwarf.onSetModules.connect(self._on_setmodules)

        self.dwarf.onAddNativeHook.connect(self._on_add_hook)
        self.dwarf.onApplyContext.connect(self._apply_context)
        self.dwarf.onThreadResumed.connect(self.on_tid_resumed)

        self.dwarf.onSetData.connect(self._on_set_data)

        self.session_manager.start_session(self.dwarf_args)
        ui_state = self.q_settings.value('dwarf_ui_state')
        if ui_state:
            self.restoreGeometry(ui_state)
        window_state = self.q_settings.value('dwarf_ui_window',
                                             self.saveState())
        if window_state:
            self.restoreState(window_state)

        self.showMaximized()

    def session_stopped(self):
        self.remove_tmp_dir()
        self.menu.clear()

        self.main_tabs.clear()

        # actually we need to kill this. needs a refactor
        if self.java_trace_panel is not None:
            self.java_trace_panel = None

        for elem in self._ui_elems:
            if elem == 'watchers':
                self.watchers_panel.clear_list()
                self.watchers_panel.close()
                self.watchers_panel = None
                self.removeDockWidget(self.watchers_dwidget)
                self.watchers_dwidget = None
            elif elem == 'hooks':
                self.hooks_panel.close()
                self.hooks_panel = None
                self.removeDockWidget(self.hooks_dwiget)
                self.hooks_dwiget = None
            elif elem == 'registers':
                self.context_panel.close()
                self.context_panel = None
                self.removeDockWidget(self.registers_dock)
                self.registers_dock = None
            elif elem == 'debug':
                self.debug_panel.close()
                self.debug_panel = None
                self.main_tabs.removeTab(0)
                # self.main_tabs
            elif elem == 'jvm-debugger':
                self.java_explorer_panel.close()
                self.java_explorer_panel = None
                self.removeDockWidget(self.watchers_dwidget)
            elif elem == 'console':
                self.console_panel.close()
                self.console_panel = None
                self.removeDockWidget(self.console_dock)
                self.console_dock = None
            elif elem == 'backtrace':
                self.backtrace_panel.close()
                self.backtrace_panel = None
                self.removeDockWidget(self.backtrace_dock)
            elif elem == 'threads':
                self.contexts_list_panel.close()
                self.contexts_list_panel = None
                self.removeDockWidget(self.threads_dock)
                self.threads_dock = None
            elif elem == 'bookmarks':
                self.bookmarks_panel.close()
                self.bookmarks_panel = None
                self.removeDockWidget(self.bookmarks_dwiget)
                self.bookmarks_dwiget = None

        self._initialize_ui_elements()

    def session_closed(self):
        self._initialize_ui_elements()
        self.hide()
        if self.welcome_window is not None:
            self.welcome_window.exec()

        # close if it was a commandline session
        if self.welcome_window is None:
            if self.dwarf_args.any != '':
                self.close()

    # ui handler
    def closeEvent(self, event):
        """ Window closed
            save stuff or whatever at exit

            detaches dwarf
        """
        # save windowstuff
        self.q_settings.setValue('dwarf_ui_state', self.saveGeometry())
        self.q_settings.setValue('dwarf_ui_window', self.saveState())

        if self.dwarf:
            try:
                self.dwarf.detach()
            except:
                pass
        super().closeEvent(event)

    def _on_watcher_clicked(self, ptr):
        """ Address in Watcher/Hookpanel was clicked
            show Memory
        """
        if '.' in ptr:  # java_hook
            file_path = ptr.replace('.', os.path.sep)
            if os.path.exists('.tmp/smali/' + file_path + '.smali'):
                if self.smali_panel is None:
                    self._create_ui_elem('smali')
                self.smali_panel.set_file('.tmp/smali/' + file_path + '.smali')
                self.show_main_tab('smali')
        else:
            self.jump_to_address(ptr)

    def _on_watcher_added(self, ptr):
        """ Watcher Entry was added
        """
        try:
            # set highlight
            self.debug_panel.memory_panel.add_highlight(
                HighLight('watcher', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_watcher_removeditem(self, ptr):
        """ Watcher Entry was removed
            remove highlight too
        """
        self.debug_panel.memory_panel.remove_highlight(ptr)

    def _on_module_dblclicked(self, data):
        """ Module in ModulePanel was doubleclicked
        """
        addr, size = data
        addr = utils.parse_ptr(addr)
        self.jump_to_address(addr)

    def _on_modulefunc_dblclicked(self, ptr):
        """ Function in ModulePanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.jump_to_address(ptr)

    def _on_dumpmodule(self, data):
        """ DumpBinary MenuItem in ModulePanel was selected
        """
        ptr, size = data
        ptr = utils.parse_ptr(ptr)
        size = int(size, 10)
        self.dwarf.dump_memory(ptr=ptr, length=size)

    def _disassemble_range(self, dwarf_range):
        """ Disassemble MenuItem in Hexview was selected
        """
        if dwarf_range:
            if self.asm_panel is None:
                self._create_ui_elem('disassembly')

            self.asm_panel.disassemble(dwarf_range)
            self.show_main_tab('disassembly')

    def _range_dblclicked(self, ptr):
        """ Range in RangesPanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.jump_to_address(ptr)

    # dwarf handlers
    def _log_js_output(self, output):
        if self.console_panel is not None:
            time_prefix = True
            if len(output.split('\n')) > 1 or len(output.split('<br />')) > 1:
                time_prefix = False
            self.console_panel.get_js_console().log(output,
                                                    time_prefix=time_prefix)

    def _log_event(self, output):
        if self.console_panel is not None:
            self.console_panel.get_events_console().log(output)

    def _on_setranges(self, ranges):
        """ Dwarf wants to set Ranges
            only hooked up to switch tab or create ui
            its connected in panel after creation
        """
        if self.ranges_panel is None:
            self.show_main_tab('ranges')
            # forward only now to panel it connects after creation
            self.ranges_panel.set_ranges(ranges)

    def _on_setmodules(self, modules):
        """ Dwarf wants to set Modules
            only hooked up to switch tab or create ui
            its connected in panel after creation
        """
        if self.modules_panel is None:
            self._create_ui_elem('modules')
            self.modules_panel.set_modules(modules)

        if self.modules_panel is not None:
            self.show_main_tab('modules')

    def _manually_apply_context(self, context):
        """
        perform additional operation if the context has been manually applied from the context list
        """
        self._apply_context(context, manual=True)

    def _apply_context(self, context, manual=False):
        # update current context tid
        # this should be on top as any further api from js needs to be executed on that thread
        reason = context['reason']
        is_initial_setup = reason == -1
        if manual or (self.dwarf.context_tid and not is_initial_setup):
            self.dwarf.context_tid = context['tid']

        if is_initial_setup:
            self.debug_panel.on_context_setup()

        if 'context' in context:
            if not manual:
                self.threads.add_context(context)

            is_java = context['is_java']
            if is_java:
                if self.java_explorer_panel is None:
                    self._create_ui_elem('jvm-debugger')
                self.context_panel.set_context(context['ptr'], 1,
                                               context['context'])
                self.java_explorer_panel._set_handle_arg(-1)
                self.show_main_tab('jvm-debugger')
            else:
                self.context_panel.set_context(context['ptr'], 0,
                                               context['context'])

                if reason == 2:
                    # native on load
                    if self.debug_panel.memory_panel_range is None:
                        base = context['moduleBase']
                        self.jump_to_address(base)
                else:
                    if 'pc' in context['context']:
                        if self.debug_panel.disassembly_panel_range is None or manual:
                            self.jump_to_address(
                                context['context']['pc']['value'], view=1)

        if 'backtrace' in context:
            self.backtrace_panel.set_backtrace(context['backtrace'])

    def _on_add_hook(self, hook):
        try:
            # set highlight
            ptr = hook.get_ptr()
            ptr = utils.parse_ptr(ptr)
            self.debug_panel.memory_panel.add_highlight(
                HighLight('hook', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_hook_removed(self, ptr):
        ptr = utils.parse_ptr(ptr)
        self.debug_panel.memory_panel.remove_highlight(ptr)

    def _on_addmodule_hook(self, data):
        ptr, name = data
        self.dwarf.hook_native(ptr, own_input=name)

    def on_tid_resumed(self, tid):
        if self.dwarf:
            if self.dwarf.context_tid == tid:
                # clear backtrace
                if 'backtrace' in self._ui_elems:
                    if self.backtrace_panel is not None:
                        self.backtrace_panel.clear()

                # remove thread
                if 'threads' in self._ui_elems:
                    if self.contexts_list_panel is not None:
                        self.contexts_list_panel.resume_tid(tid)

                # clear registers
                if 'registers' in self._ui_elems:
                    if self.context_panel is not None:
                        self.context_panel.clear()

                # clear jvm explorer
                if 'jvm-debugger' in self._ui_elems:
                    if self.java_explorer_panel is not None:
                        self.java_explorer_panel.clear_panel()

                # invalidate dwarf context tid
                self.dwarf.context_tid = 0

    def _on_set_data(self, data):
        if not isinstance(data, list):
            return

        if self.data_panel is None:
            self.show_main_tab('data')

        if self.data_panel is not None:
            self.data_panel.append_data(data[0], data[1], data[2])

    def show_progress(self, text):
        self.progressbar.setVisible(True)
        self.set_status_text(text)

    def hide_progress(self):
        self.progressbar.setVisible(False)
        self.set_status_text('')

    def _on_attached(self, data):
        self.setWindowTitle('Dwarf - Attached to %s (%s)' % (data[1], data[0]))

    def _on_detached(self, data):
        reason = data[1]

        if reason == 'application-requested':
            self.session_manager.session.stop()
            return 0

        if self.dwarf is not None:
            ret = QDialogDetached.show_dialog(self.dwarf, data[0], data[1],
                                              data[2])
            if ret == 0:
                self.dwarf.restart_proc()
            elif ret == 1:
                self.session_manager.session.stop()

        return 0

    def _on_script_loaded(self):
        # restore the loaded session if any
        self.session_manager.restore_session()

    def on_add_bookmark(self, ptr):
        """
        provide ptr as int
        """
        if self.bookmarks_panel is not None:
            self.bookmarks_panel._create_bookmark(ptr=hex(ptr))

    def _on_showmemory_request(self, ptr):
        # its simple ptr show in memorypanel
        if isinstance(ptr, str):
            ptr = utils.parse_ptr(ptr)
            self.jump_to_address(ptr, 0)

        elif isinstance(ptr, list):
            # TODO: extend
            caller, ptr = ptr
            ptr = utils.parse_ptr(ptr)
            if caller == 'backtrace' or caller == 'bt':
                # jumpto in disasm
                self.jump_to_address(ptr, 1)
Exemple #22
0
class SchemesManagerWidget(QDialog):
    def __init__(self, parent):
        super(SchemesManagerWidget, self).__init__(parent, Qt.Dialog)
        self.setWindowTitle(translations.TR_EDITOR_SCHEMES)
        self.resize(700, 500)

        vbox = QVBoxLayout(self)
        self._tabs = QTabWidget()
        vbox.addWidget(self._tabs)
        # Footer
        hbox = QHBoxLayout()
        btn_close = QPushButton(self.tr('Close'))
        btnReload = QPushButton(self.tr("Reload"))
        hbox.addWidget(btn_close)
        hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding))
        hbox.addWidget(btnReload)
        vbox.addLayout(hbox)
        self.overlay = ui_tools.Overlay(self)
        self.overlay.show()

        self._schemes = []
        self._loading = True
        self.downloadItems = []

        #Load Themes with Thread
        #self.connect(btnReload, SIGNAL("clicked()"), self._reload_themes)
        btnReload.clicked.connect(self._reload_themes)
        self._thread = ui_tools.ThreadExecution(self.execute_thread)
        #self.connect(self._thread, SIGNAL("finished()"), self.load_skins_data)
        self._thread.finished.connect(self.load_skins_data)
        #self.connect(btn_close, SIGNAL('clicked()'), self.close)
        btn_close.clicked.connect(self.close)
        self._reload_themes()

    def _reload_themes(self):
        self.overlay.show()
        self._loading = True
        self._thread.execute = self.execute_thread
        self._thread.start()

    def load_skins_data(self):
        if self._loading:
            self._tabs.clear()
            self._schemeWidget = SchemeWidget(self, self._schemes)
            self._tabs.addTab(self._schemeWidget, self.tr("Editor Schemes"))
            self._loading = False
        self.overlay.hide()
        self._thread.wait()

    def download_scheme(self, scheme):
        self.overlay.show()
        self.downloadItems = scheme
        self._thread.execute = self._download_scheme_thread
        self._thread.start()

    def resizeEvent(self, event):
        self.overlay.resize(event.size())
        event.accept()

    def execute_thread(self):
        try:
            descriptor_schemes = urlopen(resources.SCHEMES_URL)
            schemes = json_manager.parse(descriptor_schemes)
            schemes = [(d['name'], d['download']) for d in schemes]
            local_schemes = self.get_local_schemes()
            schemes = [
                schemes[i] for i in range(len(schemes))
                if os.path.basename(schemes[i][1]) not in local_schemes
            ]
            self._schemes = schemes
        except URLError:
            self._schemes = []

    def get_local_schemes(self):
        if not file_manager.folder_exists(resources.EDITOR_SKINS):
            file_manager.create_tree_folders(resources.EDITOR_SKINS)
        schemes = os.listdir(resources.EDITOR_SKINS)
        schemes = [s for s in schemes if s.lower().endswith('.color')]
        return schemes

    def _download_scheme_thread(self):
        for d in self.downloadItems:
            self.download(d[1], resources.EDITOR_SKINS)

    def download(self, url, folder):
        fileName = os.path.join(folder, os.path.basename(url))
        try:
            content = urlopen(url)
            with open(fileName, 'w') as f:
                f.write(content.read())
        except URLError:
            return
Exemple #23
0
class ReTextWindow(QMainWindow):
	def __init__(self, parent=None):
		QMainWindow.__init__(self, parent)
		self.resize(950, 700)
		screenRect = QDesktopWidget().screenGeometry()
		if globalSettings.windowGeometry:
			self.restoreGeometry(globalSettings.windowGeometry)
		else:
			self.move((screenRect.width()-self.width())/2, (screenRect.height()-self.height())/2)
		if not screenRect.contains(self.geometry()):
			self.showMaximized()
		if globalSettings.iconTheme:
			QIcon.setThemeName(globalSettings.iconTheme)
		if QIcon.themeName() in ('hicolor', ''):
			if not QFile.exists(icon_path + 'document-new.png'):
				QIcon.setThemeName(get_icon_theme())
		if QFile.exists(icon_path+'retext.png'):
			self.setWindowIcon(QIcon(icon_path+'retext.png'))
		elif QFile.exists('/usr/share/pixmaps/retext.png'):
			self.setWindowIcon(QIcon('/usr/share/pixmaps/retext.png'))
		else:
			self.setWindowIcon(QIcon.fromTheme('retext',
				QIcon.fromTheme('accessories-text-editor')))
		self.tabs = []
		self.tabWidget = QTabWidget(self)
		self.initTabWidget()
		self.setCentralWidget(self.tabWidget)
		self.tabWidget.currentChanged.connect(self.changeIndex)
		self.tabWidget.tabCloseRequested.connect(self.closeTab)
		toolBar = QToolBar(self.tr('File toolbar'), self)
		self.addToolBar(Qt.TopToolBarArea, toolBar)
		self.editBar = QToolBar(self.tr('Edit toolbar'), self)
		self.addToolBar(Qt.TopToolBarArea, self.editBar)
		self.searchBar = QToolBar(self.tr('Search toolbar'), self)
		self.addToolBar(Qt.BottomToolBarArea, self.searchBar)
		toolBar.setVisible(not globalSettings.hideToolBar)
		self.editBar.setVisible(not globalSettings.hideToolBar)
		self.actionNew = self.act(self.tr('New'), 'document-new',
			self.createNew, shct=QKeySequence.New)
		self.actionNew.setPriority(QAction.LowPriority)
		self.actionOpen = self.act(self.tr('Open'), 'document-open',
			self.openFile, shct=QKeySequence.Open)
		self.actionOpen.setPriority(QAction.LowPriority)
		self.actionSetEncoding = self.act(self.tr('Set encoding'),
			trig=self.showEncodingDialog)
		self.actionSetEncoding.setEnabled(False)
		self.actionReload = self.act(self.tr('Reload'), 'view-refresh', trig=self.openFileMain)
		self.actionReload.setEnabled(False)
		self.actionSave = self.act(self.tr('Save'), 'document-save',
			self.saveFile, shct=QKeySequence.Save)
		self.actionSave.setEnabled(False)
		self.actionSave.setPriority(QAction.LowPriority)
		self.actionSaveAs = self.act(self.tr('Save as'), 'document-save-as',
			self.saveFileAs, shct=QKeySequence.SaveAs)
		self.actionNextTab = self.act(self.tr('Next tab'), 'go-next',
			lambda: self.switchTab(1), shct=Qt.CTRL+Qt.Key_PageDown)
		self.actionPrevTab = self.act(self.tr('Previous tab'), 'go-previous',
			lambda: self.switchTab(-1), shct=Qt.CTRL+Qt.Key_PageUp)
		self.actionPrint = self.act(self.tr('Print'), 'document-print',
			self.printFile, shct=QKeySequence.Print)
		self.actionPrint.setPriority(QAction.LowPriority)
		self.actionPrintPreview = self.act(self.tr('Print preview'), 'document-print-preview',
			self.printPreview)
		self.actionViewHtml = self.act(self.tr('View HTML code'), 'text-html', self.viewHtml)
		self.actionChangeEditorFont = self.act(self.tr('Change editor font'),
			trig=self.changeEditorFont)
		self.actionChangePreviewFont = self.act(self.tr('Change preview font'),
			trig=self.changePreviewFont)
		self.actionSearch = self.act(self.tr('Find text'), 'edit-find', shct=QKeySequence.Find)
		self.actionSearch.setCheckable(True)
		self.actionSearch.triggered[bool].connect(self.searchBar.setVisible)
		self.searchBar.visibilityChanged.connect(self.searchBarVisibilityChanged)
		self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E,
			trigbool=self.preview)
		if QIcon.hasThemeIcon('document-preview'):
			self.actionPreview.setIcon(QIcon.fromTheme('document-preview'))
		elif QIcon.hasThemeIcon('preview-file'):
			self.actionPreview.setIcon(QIcon.fromTheme('preview-file'))
		elif QIcon.hasThemeIcon('x-office-document'):
			self.actionPreview.setIcon(QIcon.fromTheme('x-office-document'))
		else:
			self.actionPreview.setIcon(QIcon(icon_path+'document-preview.png'))
		self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.Key_L,
		trigbool=self.enableLivePreview)
		menuPreview = QMenu()
		menuPreview.addAction(self.actionLivePreview)
		self.actionPreview.setMenu(menuPreview)
		self.actionTableMode = self.act(self.tr('Table mode'), shct=Qt.CTRL+Qt.Key_T,
			trigbool=lambda x: self.currentTab.editBox.enableTableMode(x))
		if ReTextFakeVimHandler:
			self.actionFakeVimMode = self.act(self.tr('FakeVim mode'),
				shct=Qt.CTRL+Qt.ALT+Qt.Key_V, trigbool=self.enableFakeVimMode)
			if globalSettings.useFakeVim:
				self.actionFakeVimMode.setChecked(True)
				self.enableFakeVimMode(True)
		self.actionFullScreen = self.act(self.tr('Fullscreen mode'), 'view-fullscreen',
			shct=Qt.Key_F11, trigbool=self.enableFullScreen)
		self.actionFullScreen.setPriority(QAction.LowPriority)
		self.actionConfig = self.act(self.tr('Preferences'), icon='preferences-system',
			trig=self.openConfigDialog)
		self.actionConfig.setMenuRole(QAction.PreferencesRole)
		self.actionSaveHtml = self.act('HTML', 'text-html', self.saveFileHtml)
		self.actionPdf = self.act('PDF', 'application-pdf', self.savePdf)
		self.actionOdf = self.act('ODT', 'x-office-document', self.saveOdf)
		self.getExportExtensionsList()
		self.actionQuit = self.act(self.tr('Quit'), 'application-exit', shct=QKeySequence.Quit)
		self.actionQuit.setMenuRole(QAction.QuitRole)
		self.actionQuit.triggered.connect(self.close)
		self.actionUndo = self.act(self.tr('Undo'), 'edit-undo',
			lambda: self.currentTab.editBox.undo(), shct=QKeySequence.Undo)
		self.actionRedo = self.act(self.tr('Redo'), 'edit-redo',
			lambda: self.currentTab.editBox.redo(), shct=QKeySequence.Redo)
		self.actionCopy = self.act(self.tr('Copy'), 'edit-copy',
			lambda: self.currentTab.editBox.copy(), shct=QKeySequence.Copy)
		self.actionCut = self.act(self.tr('Cut'), 'edit-cut',
			lambda: self.currentTab.editBox.cut(), shct=QKeySequence.Cut)
		self.actionPaste = self.act(self.tr('Paste'), 'edit-paste',
			lambda: self.currentTab.editBox.paste(), shct=QKeySequence.Paste)
		self.actionUndo.setEnabled(False)
		self.actionRedo.setEnabled(False)
		self.actionCopy.setEnabled(False)
		self.actionCut.setEnabled(False)
		qApp = QApplication.instance()
		qApp.clipboard().dataChanged.connect(self.clipboardDataChanged)
		self.clipboardDataChanged()
		if enchant_available:
			self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSpellCheck)
			self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale)
		self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit)
		self.actionWebKit.setChecked(globalSettings.useWebKit)
		self.actionShow = self.act(self.tr('Show directory'), 'system-file-manager', self.showInDir)
		self.actionFind = self.act(self.tr('Next'), 'go-next', self.find,
			shct=QKeySequence.FindNext)
		self.actionFindPrev = self.act(self.tr('Previous'), 'go-previous',
			lambda: self.find(back=True), shct=QKeySequence.FindPrevious)
		self.actionHelp = self.act(self.tr('Get help online'), 'help-contents', self.openHelp)
		self.aboutWindowTitle = self.tr('About ReText')
		self.actionAbout = self.act(self.aboutWindowTitle, 'help-about', self.aboutDialog)
		self.actionAbout.setMenuRole(QAction.AboutRole)
		self.actionAboutQt = self.act(self.tr('About Qt'))
		self.actionAboutQt.setMenuRole(QAction.AboutQtRole)
		self.actionAboutQt.triggered.connect(qApp.aboutQt)
		availableMarkups = markups.get_available_markups()
		if not availableMarkups:
			print('Warning: no markups are available!')
		self.defaultMarkup = availableMarkups[0] if availableMarkups else None
		if globalSettings.defaultMarkup:
			mc = markups.find_markup_class_by_name(globalSettings.defaultMarkup)
			if mc and mc.available():
				self.defaultMarkup = mc
		if len(availableMarkups) > 1:
			self.chooseGroup = QActionGroup(self)
			markupActions = []
			for markup in availableMarkups:
				markupAction = self.act(markup.name, trigbool=self.markupFunction(markup))
				if markup == self.defaultMarkup:
					markupAction.setChecked(True)
				self.chooseGroup.addAction(markupAction)
				markupActions.append(markupAction)
		self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold,
			trig=lambda: self.insertChars('**'))
		self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic,
			trig=lambda: self.insertChars('*'))
		self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline,
			trig=lambda: self.insertTag('u'))
		self.usefulTags = ('a', 'big', 'center', 'img', 's', 'small', 'span',
			'table', 'td', 'tr', 'u')
		self.usefulChars = ('deg', 'divide', 'dollar', 'hellip', 'laquo', 'larr',
			'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo',
			'rarr', 'rsquo', 'times')
		self.tagsBox = QComboBox(self.editBar)
		self.tagsBox.addItem(self.tr('Tags'))
		self.tagsBox.addItems(self.usefulTags)
		self.tagsBox.activated.connect(self.insertTag)
		self.symbolBox = QComboBox(self.editBar)
		self.symbolBox.addItem(self.tr('Symbols'))
		self.symbolBox.addItems(self.usefulChars)
		self.symbolBox.activated.connect(self.insertSymbol)
		self.updateStyleSheet()
		menubar = QMenuBar(self)
		menubar.setGeometry(QRect(0, 0, 800, 25))
		self.setMenuBar(menubar)
		menuFile = menubar.addMenu(self.tr('File'))
		menuEdit = menubar.addMenu(self.tr('Edit'))
		menuHelp = menubar.addMenu(self.tr('Help'))
		menuFile.addAction(self.actionNew)
		menuFile.addAction(self.actionOpen)
		self.menuRecentFiles = menuFile.addMenu(self.tr('Open recent'))
		self.menuRecentFiles.aboutToShow.connect(self.updateRecentFiles)
		menuFile.addMenu(self.menuRecentFiles)
		menuFile.addAction(self.actionShow)
		menuFile.addAction(self.actionSetEncoding)
		menuFile.addAction(self.actionReload)
		menuFile.addSeparator()
		menuFile.addAction(self.actionSave)
		menuFile.addAction(self.actionSaveAs)
		menuFile.addSeparator()
		menuFile.addAction(self.actionNextTab)
		menuFile.addAction(self.actionPrevTab)
		menuFile.addSeparator()
		menuExport = menuFile.addMenu(self.tr('Export'))
		menuExport.addAction(self.actionSaveHtml)
		menuExport.addAction(self.actionOdf)
		menuExport.addAction(self.actionPdf)
		if self.extensionActions:
			menuExport.addSeparator()
			for action, mimetype in self.extensionActions:
				menuExport.addAction(action)
			menuExport.aboutToShow.connect(self.updateExtensionsVisibility)
		menuFile.addAction(self.actionPrint)
		menuFile.addAction(self.actionPrintPreview)
		menuFile.addSeparator()
		menuFile.addAction(self.actionQuit)
		menuEdit.addAction(self.actionUndo)
		menuEdit.addAction(self.actionRedo)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionCut)
		menuEdit.addAction(self.actionCopy)
		menuEdit.addAction(self.actionPaste)
		menuEdit.addSeparator()
		if enchant_available:
			menuSC = menuEdit.addMenu(self.tr('Spell check'))
			menuSC.addAction(self.actionEnableSC)
			menuSC.addAction(self.actionSetLocale)
		menuEdit.addAction(self.actionSearch)
		menuEdit.addAction(self.actionChangeEditorFont)
		menuEdit.addAction(self.actionChangePreviewFont)
		menuEdit.addSeparator()
		if len(availableMarkups) > 1:
			self.menuMode = menuEdit.addMenu(self.tr('Default markup'))
			for markupAction in markupActions:
				self.menuMode.addAction(markupAction)
		menuFormat = menuEdit.addMenu(self.tr('Formatting'))
		menuFormat.addAction(self.actionBold)
		menuFormat.addAction(self.actionItalic)
		menuFormat.addAction(self.actionUnderline)
		menuEdit.addAction(self.actionWebKit)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionViewHtml)
		menuEdit.addAction(self.actionPreview)
		menuEdit.addAction(self.actionTableMode)
		if ReTextFakeVimHandler:
			menuEdit.addAction(self.actionFakeVimMode)
		menuEdit.addSeparator()
		menuEdit.addAction(self.actionFullScreen)
		menuEdit.addAction(self.actionConfig)
		menuHelp.addAction(self.actionHelp)
		menuHelp.addSeparator()
		menuHelp.addAction(self.actionAbout)
		menuHelp.addAction(self.actionAboutQt)
		menubar.addMenu(menuFile)
		menubar.addMenu(menuEdit)
		menubar.addMenu(menuHelp)
		toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
		toolBar.addAction(self.actionNew)
		toolBar.addSeparator()
		toolBar.addAction(self.actionOpen)
		toolBar.addAction(self.actionSave)
		toolBar.addAction(self.actionPrint)
		toolBar.addSeparator()
		toolBar.addAction(self.actionPreview)
		toolBar.addAction(self.actionFullScreen)
		self.editBar.addAction(self.actionUndo)
		self.editBar.addAction(self.actionRedo)
		self.editBar.addSeparator()
		self.editBar.addAction(self.actionCut)
		self.editBar.addAction(self.actionCopy)
		self.editBar.addAction(self.actionPaste)
		self.editBar.addSeparator()
		self.editBar.addWidget(self.tagsBox)
		self.editBar.addWidget(self.symbolBox)
		self.searchEdit = QLineEdit(self.searchBar)
		self.searchEdit.setPlaceholderText(self.tr('Search'))
		self.searchEdit.returnPressed.connect(self.find)
		self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar)
		self.searchBar.addWidget(self.searchEdit)
		self.searchBar.addSeparator()
		self.searchBar.addWidget(self.csBox)
		self.searchBar.addAction(self.actionFindPrev)
		self.searchBar.addAction(self.actionFind)
		self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
		self.searchBar.setVisible(False)
		self.autoSaveEnabled = globalSettings.autoSave
		if self.autoSaveEnabled:
			timer = QTimer(self)
			timer.start(60000)
			timer.timeout.connect(self.saveAll)
		self.ind = None
		if enchant_available:
			self.sl = globalSettings.spellCheckLocale
			if self.sl:
				try:
					enchant.Dict(self.sl)
				except Exception as e:
					print(e, file=sys.stderr)
					self.sl = None
			if globalSettings.spellCheck:
				self.actionEnableSC.setChecked(True)
				self.enableSpellCheck(True)
		self.fileSystemWatcher = QFileSystemWatcher()
		self.fileSystemWatcher.fileChanged.connect(self.fileChanged)

	def updateStyleSheet(self):
		if globalSettings.styleSheet:
			sheetfile = QFile(globalSettings.styleSheet)
			sheetfile.open(QIODevice.ReadOnly)
			self.ss = QTextStream(sheetfile).readAll()
			sheetfile.close()
		else:
			self.ss = ''

	def initTabWidget(self):
		def dragEnterEvent(e):
			e.acceptProposedAction()
		def dropEvent(e):
			fn = bytes(e.mimeData().data('text/plain')).decode().rstrip()
			if fn.startswith('file:'):
				fn = QUrl(fn).toLocalFile()
			self.openFileWrapper(fn)
		self.tabWidget.setTabsClosable(True)
		self.tabWidget.setAcceptDrops(True)
		self.tabWidget.dragEnterEvent = dragEnterEvent
		self.tabWidget.dropEvent = dropEvent

	def act(self, name, icon=None, trig=None, trigbool=None, shct=None):
		if not isinstance(shct, QKeySequence):
			shct = QKeySequence(shct)
		if icon:
			action = QAction(self.actIcon(icon), name, self)
		else:
			action = QAction(name, self)
		if trig:
			action.triggered.connect(trig)
		elif trigbool:
			action.setCheckable(True)
			action.triggered[bool].connect(trigbool)
		if shct:
			action.setShortcut(shct)
		return action

	def actIcon(self, name):
		return QIcon.fromTheme(name, QIcon(icon_path+name+'.png'))

	def printError(self):
		import traceback
		print('Exception occured while parsing document:', file=sys.stderr)
		traceback.print_exc()

	def getWebView(self):
		webView = QWebView()
		if not globalSettings.handleWebLinks:
			webView.page().setLinkDelegationPolicy(QWebPage.DelegateExternalLinks)
			webView.page().linkClicked.connect(QDesktopServices.openUrl)
		settings = webView.settings()
		settings.setAttribute(QWebSettings.LocalContentCanAccessFileUrls, False)
		settings.setDefaultTextEncoding('utf-8')
		return webView

	def createTab(self, fileName):
		self.currentTab = ReTextTab(self, fileName)
		self.tabs.append(self.currentTab)
		self.tabWidget.addTab(self.currentTab.getSplitter(), self.tr("New document"))

	def closeTab(self, ind):
		if self.maybeSave(ind):
			if self.tabWidget.count() == 1:
				self.createTab("")
			if self.currentTab.fileName:
				self.fileSystemWatcher.removePath(self.currentTab.fileName)
			del self.tabs[ind]
			self.tabWidget.removeTab(ind)

	def getMarkupClass(self, fileName=None):
		if fileName is None:
			fileName = self.currentTab.fileName
		if fileName:
			markupClass = markups.get_markup_for_file_name(
				fileName, return_class=True)
			if markupClass:
				return markupClass
		return self.defaultMarkup

	def getMarkup(self, fileName=None):
		if fileName is None:
			fileName = self.currentTab.fileName
		markupClass = self.getMarkupClass(fileName=fileName)
		if markupClass and markupClass.available():
			return markupClass(filename=fileName)

	def docTypeChanged(self):
		oldType = self.currentTab.highlighter.docType
		markupClass = self.getMarkupClass()
		newType = markupClass.name if markupClass else ''
		if oldType != newType:
			self.currentTab.markup = self.getMarkup()
			self.currentTab.updatePreviewBox()
			self.currentTab.highlighter.docType = newType
			self.currentTab.highlighter.rehighlight()
		dtMarkdown = (newType == DOCTYPE_MARKDOWN)
		dtMkdOrReST = (newType in (DOCTYPE_MARKDOWN, DOCTYPE_REST))
		self.tagsBox.setEnabled(dtMarkdown)
		self.symbolBox.setEnabled(dtMarkdown)
		self.actionUnderline.setEnabled(dtMarkdown)
		self.actionBold.setEnabled(dtMkdOrReST)
		self.actionItalic.setEnabled(dtMkdOrReST)
		canReload = bool(self.currentTab.fileName) and not self.autoSaveActive()
		self.actionSetEncoding.setEnabled(canReload)
		self.actionReload.setEnabled(canReload)

	def changeIndex(self, ind):
		if ind < 0:  # This can happen when enableWebKit is called
			return
		self.currentTab = self.tabs[ind]
		editBox = self.currentTab.editBox
		previewState = self.currentTab.previewState
		self.actionUndo.setEnabled(editBox.document().isUndoAvailable())
		self.actionRedo.setEnabled(editBox.document().isRedoAvailable())
		self.actionCopy.setEnabled(editBox.textCursor().hasSelection())
		self.actionCut.setEnabled(editBox.textCursor().hasSelection())
		self.actionPreview.setChecked(previewState >= PreviewLive)
		self.actionLivePreview.setChecked(previewState == PreviewLive)
		self.actionTableMode.setChecked(editBox.tableModeEnabled)
		self.editBar.setEnabled(previewState < PreviewNormal)
		self.ind = ind
		if self.currentTab.fileName:
			self.setCurrentFile()
		else:
			self.setWindowTitle(self.tr('New document') + '[*]')
			self.docTypeChanged()
		self.modificationChanged(editBox.document().isModified())
		if globalSettings.restorePreviewState:
			globalSettings.previewState = (previewState >= PreviewLive)
		editBox.setFocus(Qt.OtherFocusReason)

	def changeEditorFont(self):
		font, ok = QFontDialog.getFont(globalSettings.editorFont, self)
		if ok:
			globalSettings.editorFont = font
			for tab in self.tabs:
				tab.editBox.updateFont()

	def changePreviewFont(self):
		font, ok = QFontDialog.getFont(globalSettings.font, self)
		if ok:
			globalSettings.font = font
			for tab in self.tabs:
				tab.updatePreviewBox()

	def preview(self, viewmode):
		self.currentTab.previewState = viewmode * 2
		self.actionLivePreview.setChecked(False)
		self.editBar.setDisabled(viewmode)
		self.currentTab.updateBoxesVisibility()
		if viewmode:
			self.currentTab.updatePreviewBox()

	def enableLivePreview(self, livemode):
		if globalSettings.restorePreviewState:
			globalSettings.previewState = livemode
		self.currentTab.previewState = int(livemode)
		self.actionPreview.setChecked(livemode)
		self.editBar.setEnabled(True)
		self.currentTab.updateBoxesVisibility()
		if livemode:
			self.currentTab.updatePreviewBox()

	def enableWebKit(self, enable):
		globalSettings.useWebKit = enable
		restoreInd = self.ind
		self.tabWidget.clear()
		for tab in self.tabs:
			tab.previewBox = tab.createPreviewBox()
			splitter = tab.getSplitter()
			self.tabWidget.addTab(splitter, tab.getDocumentTitle(baseName=True))
			tab.updatePreviewBox()
			tab.updateBoxesVisibility()
		self.ind = restoreInd
		self.tabWidget.setCurrentIndex(self.ind)

	def enableCopy(self, copymode):
		self.actionCopy.setEnabled(copymode)
		self.actionCut.setEnabled(copymode)

	def enableFullScreen(self, yes):
		if yes:
			self.showFullScreen()
		else:
			self.showNormal()

	def openConfigDialog(self):
		dlg = ConfigDialog(self)
		dlg.setWindowTitle(self.tr('Preferences'))
		dlg.show()

	def enableFakeVimMode(self, yes):
		globalSettings.useFakeVim = yes
		if yes:
			FakeVimMode.init(self)
			for tab in self.tabs:
				tab.installFakeVimHandler()
		else:
			FakeVimMode.exit(self)

	def enableSpellCheck(self, yes):
		if yes:
			if self.sl:
				self.setAllDictionaries(enchant.Dict(self.sl))
			else:
				self.setAllDictionaries(enchant.Dict())
		else:
			self.setAllDictionaries(None)
		globalSettings.spellCheck = yes

	def setAllDictionaries(self, dictionary):
		for hl in self.highlighters:
			hl.dictionary = dictionary
			hl.rehighlight()

	def changeLocale(self):
		if self.sl:
			localedlg = LocaleDialog(self, defaultText=self.sl)
		else:
			localedlg = LocaleDialog(self)
		if localedlg.exec() != QDialog.Accepted:
			return
		sl = localedlg.localeEdit.text()
		setdefault = localedlg.checkBox.isChecked()
		if sl:
			try:
				sl = str(sl)
				enchant.Dict(sl)
			except Exception as e:
				QMessageBox.warning(self, '', str(e))
			else:
				self.sl = sl
				self.enableSpellCheck(self.actionEnableSC.isChecked())
		else:
			self.sl = None
			self.enableSpellCheck(self.actionEnableSC.isChecked())
		if setdefault:
			globalSettings.spellCheckLocale = sl

	def searchBarVisibilityChanged(self, visible):
		self.actionSearch.setChecked(visible)
		if visible:
			self.searchEdit.setFocus(Qt.ShortcutFocusReason)

	def find(self, back=False):
		flags = QTextDocument.FindFlags()
		if back:
			flags |= QTextDocument.FindBackward
		if self.csBox.isChecked():
			flags |= QTextDocument.FindCaseSensitively
		text = self.searchEdit.text()
		editBox = self.currentTab.editBox
		cursor = editBox.textCursor()
		newCursor = editBox.document().find(text, cursor, flags)
		if not newCursor.isNull():
			editBox.setTextCursor(newCursor)
			return self.setSearchEditColor(True)
		cursor.movePosition(QTextCursor.End if back else QTextCursor.Start)
		newCursor = editBox.document().find(text, cursor, flags)
		if not newCursor.isNull():
			editBox.setTextCursor(newCursor)
			return self.setSearchEditColor(True)
		self.setSearchEditColor(False)

	def setSearchEditColor(self, found):
		palette = self.searchEdit.palette()
		palette.setColor(QPalette.Active, QPalette.Base,
		                 Qt.white if found else QColor(255, 102, 102))
		self.searchEdit.setPalette(palette)

	def showInDir(self):
		if self.currentTab.fileName:
			path = QFileInfo(self.currentTab.fileName).path()
			QDesktopServices.openUrl(QUrl.fromLocalFile(path))
		else:
			QMessageBox.warning(self, '', self.tr("Please, save the file somewhere."))

	def setCurrentFile(self):
		self.setWindowTitle("")
		self.tabWidget.setTabText(self.ind, self.currentTab.getDocumentTitle(baseName=True))
		self.setWindowFilePath(self.currentTab.fileName)
		files = readListFromSettings("recentFileList")
		while self.currentTab.fileName in files:
			files.remove(self.currentTab.fileName)
		files.insert(0, self.currentTab.fileName)
		if len(files) > 10:
			del files[10:]
		writeListToSettings("recentFileList", files)
		QDir.setCurrent(QFileInfo(self.currentTab.fileName).dir().path())
		self.docTypeChanged()

	def createNew(self, text=None):
		self.createTab("")
		self.ind = self.tabWidget.count()-1
		self.tabWidget.setCurrentIndex(self.ind)
		if text:
			self.currentTab.editBox.textCursor().insertText(text)

	def switchTab(self, shift=1):
		self.tabWidget.setCurrentIndex((self.ind + shift) % self.tabWidget.count())

	def updateRecentFiles(self):
		self.menuRecentFiles.clear()
		self.recentFilesActions = []
		filesOld = readListFromSettings("recentFileList")
		files = []
		for f in filesOld:
			if QFile.exists(f):
				files.append(f)
				self.recentFilesActions.append(self.act(f, trig=self.openFunction(f)))
		writeListToSettings("recentFileList", files)
		for action in self.recentFilesActions:
			self.menuRecentFiles.addAction(action)

	def markupFunction(self, markup):
		return lambda: self.setDefaultMarkup(markup)

	def openFunction(self, fileName):
		return lambda: self.openFileWrapper(fileName)

	def extensionFuntion(self, data):
		return lambda: \
		self.runExtensionCommand(data['Exec'], data['FileFilter'], data['DefaultExtension'])

	def getExportExtensionsList(self):
		extensions = []
		for extsprefix in datadirs:
			extsdir = QDir(extsprefix+'/export-extensions/')
			if extsdir.exists():
				for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'],
				QDir.Files | QDir.Readable):
					extensions.append(self.readExtension(fileInfo.filePath()))
		locale = QLocale.system().name()
		self.extensionActions = []
		for extension in extensions:
			try:
				if ('Name[%s]' % locale) in extension:
					name = extension['Name[%s]' % locale]
				elif ('Name[%s]' % locale.split('_')[0]) in extension:
					name = extension['Name[%s]' % locale.split('_')[0]]
				else:
					name = extension['Name']
				data = {}
				for prop in ('FileFilter', 'DefaultExtension', 'Exec'):
					if 'X-ReText-'+prop in extension:
						data[prop] = extension['X-ReText-'+prop]
					elif prop in extension:
						data[prop] = extension[prop]
					else:
						data[prop] = ''
				action = self.act(name, trig=self.extensionFuntion(data))
				if 'Icon' in extension:
					action.setIcon(self.actIcon(extension['Icon']))
				mimetype = extension['MimeType'] if 'MimeType' in extension else None
			except KeyError:
				print('Failed to parse extension: Name is required', file=sys.stderr)
			else:
				self.extensionActions.append((action, mimetype))

	def updateExtensionsVisibility(self):
		markupClass = self.getMarkupClass()
		for action in self.extensionActions:
			if markupClass is None:
				action[0].setEnabled(False)
				continue
			mimetype = action[1]
			if mimetype == None:
				enabled = True
			elif markupClass == markups.MarkdownMarkup:
				enabled = (mimetype in ("text/x-retext-markdown", "text/x-markdown"))
			elif markupClass == markups.ReStructuredTextMarkup:
				enabled = (mimetype in ("text/x-retext-rst", "text/x-rst"))
			else:
				enabled = False
			action[0].setEnabled(enabled)

	def readExtension(self, fileName):
		extFile = QFile(fileName)
		extFile.open(QIODevice.ReadOnly)
		extension = {}
		stream = QTextStream(extFile)
		while not stream.atEnd():
			line = stream.readLine()
			if '=' in line:
				index = line.index('=')
				extension[line[:index].rstrip()] = line[index+1:].lstrip()
		extFile.close()
		return extension

	def openFile(self):
		supportedExtensions = ['.txt']
		for markup in markups.get_all_markups():
			supportedExtensions += markup.file_extensions
		fileFilter = ' (' + str.join(' ', ['*'+ext for ext in supportedExtensions]) + ');;'
		fileNames = QFileDialog.getOpenFileNames(self,
			self.tr("Select one or several files to open"), "",
			self.tr("Supported files") + fileFilter + self.tr("All files (*)"))
		for fileName in fileNames[0]:
			self.openFileWrapper(fileName)

	def openFileWrapper(self, fileName):
		if not fileName:
			return
		fileName = QFileInfo(fileName).canonicalFilePath()
		exists = False
		for i in range(self.tabWidget.count()):
			if self.tabs[i].fileName == fileName:
				exists = True
				ex = i
		if exists:
			self.tabWidget.setCurrentIndex(ex)
		elif QFile.exists(fileName):
			noEmptyTab = (
				(self.ind is None) or
				self.currentTab.fileName or
				self.currentTab.editBox.toPlainText() or
				self.currentTab.editBox.document().isModified()
			)
			if noEmptyTab:
				self.createTab(fileName)
				self.ind = self.tabWidget.count()-1
				self.tabWidget.setCurrentIndex(self.ind)
			if fileName:
				self.fileSystemWatcher.addPath(fileName)
			self.currentTab.fileName = fileName
			self.openFileMain()

	def openFileMain(self, encoding=None):
		openfile = QFile(self.currentTab.fileName)
		openfile.open(QIODevice.ReadOnly)
		stream = QTextStream(openfile)
		if encoding:
			stream.setCodec(encoding)
		elif globalSettings.defaultCodec:
			stream.setCodec(globalSettings.defaultCodec)
		text = stream.readAll()
		openfile.close()
		markupClass = markups.get_markup_for_file_name(
			self.currentTab.fileName, return_class=True)
		self.currentTab.highlighter.docType = (markupClass.name if markupClass else '')
		self.currentTab.markup = self.getMarkup()
		if self.defaultMarkup:
			self.currentTab.highlighter.docType = self.defaultMarkup.name
		editBox = self.currentTab.editBox
		modified = bool(encoding) and (editBox.toPlainText() != text)
		editBox.setPlainText(text)
		self.setCurrentFile()
		editBox.document().setModified(modified)
		self.setWindowModified(modified)

	def showEncodingDialog(self):
		if not self.maybeSave(self.ind):
			return
		encoding, ok = QInputDialog.getItem(self, '',
			self.tr('Select file encoding from the list:'),
			[bytes(b).decode() for b in QTextCodec.availableCodecs()],
			0, False)
		if ok:
			self.openFileMain(encoding)

	def saveFile(self):
		self.saveFileMain(dlg=False)

	def saveFileAs(self):
		self.saveFileMain(dlg=True)

	def saveAll(self):
		for tab in self.tabs:
			if tab.fileName and QFileInfo(tab.fileName).isWritable():
				tab.saveTextToFile()
				tab.editBox.document().setModified(False)

	def saveFileMain(self, dlg):
		if (not self.currentTab.fileName) or dlg:
			markupClass = self.getMarkupClass()
			if (markupClass is None) or not hasattr(markupClass, 'default_extension'):
				defaultExt = self.tr("Plain text (*.txt)")
				ext = ".txt"
			else:
				defaultExt = self.tr('%s files',
					'Example of final string: Markdown files') \
					% markupClass.name + ' (' + str.join(' ',
					('*'+extension for extension in markupClass.file_extensions)) + ')'
				if markupClass == markups.MarkdownMarkup:
					ext = globalSettings.markdownDefaultFileExtension
				elif markupClass == markups.ReStructuredTextMarkup:
					ext = globalSettings.restDefaultFileExtension
				else:
					ext = markupClass.default_extension
			newFileName = QFileDialog.getSaveFileName(self,
				self.tr("Save file"), "", defaultExt)[0]
			if newFileName:
				if not QFileInfo(newFileName).suffix():
					newFileName += ext
				if self.currentTab.fileName:
					self.fileSystemWatcher.removePath(self.currentTab.fileName)
				self.currentTab.fileName = newFileName
				self.actionSetEncoding.setDisabled(self.autoSaveActive())
		if self.currentTab.fileName:
			if self.currentTab.saveTextToFile():
				self.setCurrentFile()
				self.currentTab.editBox.document().setModified(False)
				self.setWindowModified(False)
				return True
			else:
				QMessageBox.warning(self, '',
				self.tr("Cannot save to file because it is read-only!"))
		return False

	def saveHtml(self, fileName):
		if not QFileInfo(fileName).suffix():
			fileName += ".html"
		try:
			htmltext = self.currentTab.getHtml(includeStyleSheet=False,
				includeMeta=True, webenv=True)
		except Exception:
			return self.printError()
		htmlFile = QFile(fileName)
		htmlFile.open(QIODevice.WriteOnly)
		html = QTextStream(htmlFile)
		if globalSettings.defaultCodec:
			html.setCodec(globalSettings.defaultCodec)
		html << htmltext
		htmlFile.close()

	def textDocument(self):
		td = QTextDocument()
		td.setMetaInformation(QTextDocument.DocumentTitle,
		                      self.currentTab.getDocumentTitle())
		if self.ss:
			td.setDefaultStyleSheet(self.ss)
		td.setHtml(self.currentTab.getHtml())
		td.setDefaultFont(globalSettings.font)
		return td

	def saveOdf(self):
		try:
			document = self.textDocument()
		except Exception:
			return self.printError()
		fileName = QFileDialog.getSaveFileName(self,
			self.tr("Export document to ODT"), "",
			self.tr("OpenDocument text files (*.odt)"))[0]
		if not QFileInfo(fileName).suffix():
			fileName += ".odt"
		writer = QTextDocumentWriter(fileName)
		writer.setFormat("odf")
		writer.write(document)

	def saveFileHtml(self):
		fileName = QFileDialog.getSaveFileName(self,
			self.tr("Save file"), "",
			self.tr("HTML files (*.html *.htm)"))[0]
		if fileName:
			self.saveHtml(fileName)

	def getDocumentForPrint(self):
		if globalSettings.useWebKit:
			return self.currentTab.previewBox
		try:
			return self.textDocument()
		except Exception:
			self.printError()

	def standardPrinter(self):
		printer = QPrinter(QPrinter.HighResolution)
		printer.setDocName(self.currentTab.getDocumentTitle())
		printer.setCreator('ReText %s' % app_version)
		return printer

	def savePdf(self):
		self.currentTab.updatePreviewBox()
		fileName = QFileDialog.getSaveFileName(self,
			self.tr("Export document to PDF"),
			"", self.tr("PDF files (*.pdf)"))[0]
		if fileName:
			if not QFileInfo(fileName).suffix():
				fileName += ".pdf"
			printer = self.standardPrinter()
			printer.setOutputFormat(QPrinter.PdfFormat)
			printer.setOutputFileName(fileName)
			document = self.getDocumentForPrint()
			if document != None:
				document.print(printer)

	def printFile(self):
		self.currentTab.updatePreviewBox()
		printer = self.standardPrinter()
		dlg = QPrintDialog(printer, self)
		dlg.setWindowTitle(self.tr("Print document"))
		if (dlg.exec() == QDialog.Accepted):
			document = self.getDocumentForPrint()
			if document != None:
				document.print(printer)

	def printPreview(self):
		document = self.getDocumentForPrint()
		if document == None:
			return
		printer = self.standardPrinter()
		preview = QPrintPreviewDialog(printer, self)
		preview.paintRequested.connect(document.print)
		preview.exec()

	def runExtensionCommand(self, command, filefilter, defaultext):
		of = ('%of' in command)
		html = ('%html' in command)
		if of:
			if defaultext and not filefilter:
				filefilter = '*'+defaultext
			fileName = QFileDialog.getSaveFileName(self,
				self.tr('Export document'), '', filefilter)[0]
			if not fileName:
				return
			if defaultext and not QFileInfo(fileName).suffix():
				fileName += defaultext
		basename = '.%s.retext-temp' % self.currentTab.getDocumentTitle(baseName=True)
		if html:
			tmpname = basename+'.html'
			self.saveHtml(tmpname)
		else:
			tmpname = basename+self.getMarkupClass().default_extension
			self.currentTab.saveTextToFile(fileName=tmpname, addToWatcher=False)
		command = command.replace('%of', '"out'+defaultext+'"')
		command = command.replace('%html' if html else '%if', '"'+tmpname+'"')
		try:
			Popen(str(command), shell=True).wait()
		except Exception as error:
			errorstr = str(error)
			QMessageBox.warning(self, '', self.tr('Failed to execute the command:')
			+ '\n' + errorstr)
		QFile(tmpname).remove()
		if of:
			QFile('out'+defaultext).rename(fileName)

	def autoSaveActive(self, ind=None):
		tab = self.currentTab if ind is None else self.tabs[ind]
		return (self.autoSaveEnabled and tab.fileName and
			QFileInfo(tab.fileName).isWritable())

	def modificationChanged(self, changed):
		if self.autoSaveActive():
			changed = False
		self.actionSave.setEnabled(changed)
		self.setWindowModified(changed)

	def clipboardDataChanged(self):
		mimeData = QApplication.instance().clipboard().mimeData()
		if mimeData is not None:
			self.actionPaste.setEnabled(mimeData.hasText())

	def insertChars(self, chars):
		tc = self.currentTab.editBox.textCursor()
		if tc.hasSelection():
			selection = tc.selectedText()
			if selection.startswith(chars) and selection.endswith(chars):
				if len(selection) > 2*len(chars):
					selection = selection[len(chars):-len(chars)]
					tc.insertText(selection)
			else:
				tc.insertText(chars+tc.selectedText()+chars)
		else:
			tc.insertText(chars)

	def insertTag(self, ut):
		if not ut:
			return
		if isinstance(ut, int):
			ut = self.usefulTags[ut - 1]
		arg = ' style=""' if ut == 'span' else ''
		tc = self.currentTab.editBox.textCursor()
		if ut == 'img':
			toinsert = ('<a href="' + tc.selectedText() +
			'" target="_blank"><img src="' + tc.selectedText() + '"/></a>')
		elif ut == 'a':
			toinsert = ('<a href="' + tc.selectedText() +
			'" target="_blank">' + tc.selectedText() + '</a>')
		else:
			toinsert = '<'+ut+arg+'>'+tc.selectedText()+'</'+ut+'>'
		tc.insertText(toinsert)
		self.tagsBox.setCurrentIndex(0)

	def insertSymbol(self, num):
		if num:
			self.currentTab.editBox.insertPlainText('&'+self.usefulChars[num-1]+';')
		self.symbolBox.setCurrentIndex(0)

	def fileChanged(self, fileName):
		ind = None
		for testind, tab in enumerate(self.tabs):
			if tab.fileName == fileName:
				ind = testind
		if ind is None:
			self.fileSystemWatcher.removePath(fileName)
		self.tabWidget.setCurrentIndex(ind)
		if not QFile.exists(fileName):
			self.tabs[ind].editBox.document().setModified(True)
			QMessageBox.warning(self, '', self.tr(
				'This file has been deleted by other application.\n'
				'Please make sure you save the file before exit.'))
		elif not self.tabs[ind].editBox.document().isModified():
			# File was not modified in ReText, reload silently
			self.openFileMain()
			self.tabs[ind].updatePreviewBox()
		else:
			text = self.tr(
				'This document has been modified by other application.\n'
				'Do you want to reload the file (this will discard all '
				'your changes)?\n')
			if self.autoSaveEnabled:
				text += self.tr(
					'If you choose to not reload the file, auto save mode will '
					'be disabled for this session to prevent data loss.')
			messageBox = QMessageBox(QMessageBox.Warning, '', text)
			reloadButton = messageBox.addButton(self.tr('Reload'), QMessageBox.YesRole)
			messageBox.addButton(QMessageBox.Cancel)
			messageBox.exec()
			if messageBox.clickedButton() is reloadButton:
				self.openFileMain()
				self.tabs[ind].updatePreviewBox()
			else:
				self.autoSaveEnabled = False
				self.tabs[ind].editBox.document().setModified(True)
		if fileName not in self.fileSystemWatcher.files():
			# https://github.com/retext-project/retext/issues/137
			self.fileSystemWatcher.addPath(fileName)

	def maybeSave(self, ind):
		if self.autoSaveActive(ind):
			self.tabs[ind].saveTextToFile()
			return True
		if not self.tabs[ind].editBox.document().isModified():
			return True
		self.tabWidget.setCurrentIndex(ind)
		ret = QMessageBox.warning(self, '',
			self.tr("The document has been modified.\nDo you want to save your changes?"),
			QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
		if ret == QMessageBox.Save:
			return self.saveFileMain(False)
		elif ret == QMessageBox.Cancel:
			return False
		return True

	def closeEvent(self, closeevent):
		for self.ind in range(self.tabWidget.count()):
			if not self.maybeSave(self.ind):
				return closeevent.ignore()
		if globalSettings.saveWindowGeometry and not self.isMaximized():
			globalSettings.windowGeometry = self.saveGeometry()
		closeevent.accept()

	def viewHtml(self):
		htmlDlg = HtmlDialog(self)
		try:
			htmltext = self.currentTab.getHtml(includeStyleSheet=False,
				includeTitle=False)
		except Exception:
			return self.printError()
		winTitle = self.currentTab.getDocumentTitle(baseName=True)
		htmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+")")
		htmlDlg.textEdit.setPlainText(htmltext.rstrip())
		htmlDlg.hl.rehighlight()
		htmlDlg.show()
		htmlDlg.raise_()
		htmlDlg.activateWindow()

	def openHelp(self):
		QDesktopServices.openUrl(QUrl('https://github.com/retext-project/retext/wiki'))

	def aboutDialog(self):
		QMessageBox.about(self, self.aboutWindowTitle,
		'<p><b>' + (self.tr('ReText %s (using PyMarkups %s)') % (app_version, markups.__version__))
		+'</b></p>' + self.tr('Simple but powerful editor'
		' for Markdown and reStructuredText')
		+'</p><p>'+self.tr('Author: Dmitry Shachnev, 2011').replace('2011', '2011\u2013' '2015')
		+'<br><a href="https://github.com/retext-project/retext">'+self.tr('Website')
		+'</a> | <a href="http://daringfireball.net/projects/markdown/syntax">'
		+self.tr('Markdown syntax')
		+'</a> | <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">'
		+self.tr('reStructuredText syntax')+'</a></p>')

	def setDefaultMarkup(self, markup):
		self.defaultMarkup = markup
		defaultName = markups.get_available_markups()[0].name
		writeToSettings('defaultMarkup', markup.name, defaultName)
		for self.currentTab in self.tabs:
			self.docTypeChanged()
		self.currentTab = self.tabs[self.ind]
Exemple #24
0
class digital_pulses(QWidget):
    # can be done as QWidget
    def __init__(self):
        self.active_channels = {}
        self.loadSchemes()  # schemes with saved groups and pulses
        groups = []  # list of all pulse_groups]
        self.output_data = {}
        super().__init__()
        if len(self.schemes) == 0:
            self.schemes['Default'] = [
                pulse_group(name='first'),
                pulse_group(name='qqq')
            ]
            self.schemes['Default10'] = [
                pulse_group(), pulse_group(name='dfg')
            ]
        self.refChannels = [0]
        self.refChannels.extend(
            [group.name for group in self.schemes[self.current_scheme]])
        print(self.refChannels)
        self.initUI()
        self.win = pg.GraphicsWindow(title="Three plot curves")
        self.win.resize(1000, 600)
        self.onAnyChange()

    def plotPulses(self):
        self.win.clear()
        for name in sorted(self.output_data):
            value = self.output_data[name]
            p = self.win.addPlot(title=name)
            xx = []
            yy = []
            for i, point in enumerate(value[1:]):
                if (not i == 0) and (not i == (len(value[1:]) - 1)):
                    xx.append(point[0])
                    yy.append(not point[1])
                xx.append(point[0])
                yy.append(point[1])
            p.plot(xx, yy)
            self.win.nextRow()

    def initUI(self):
        vbox = QVBoxLayout(self)
        topbox = QHBoxLayout(self)
        self.scheme_lbl = QLabel('Scheme')
        topbox.addWidget(self.scheme_lbl)

        self.scheme_combo_box = QComboBox()
        self.scheme_combo_box.addItems(self.schemes.keys())
        if self.current_scheme:
            self.scheme_combo_box.setCurrentText(self.current_scheme)
        self.scheme_combo_box.currentTextChanged.connect(self.updateScheme)
        topbox.addWidget(self.scheme_combo_box)

        self.add_group_button = QPushButton('Add group')
        self.add_group_button.clicked.connect(self.addGroup)
        topbox.addWidget(self.add_group_button)

        self.save_button = QPushButton('Save')
        self.save_button.clicked.connect(self.saveScheme)
        topbox.addWidget(self.save_button)

        self.save_as_button = QPushButton('Save as')
        self.save_as_button.clicked.connect(self.saveAsScheme)
        topbox.addWidget(self.save_as_button)

        vbox.addLayout(topbox)

        self.hor_box = QHBoxLayout(self)

        self.ch_grid = QGridLayout(self)
        self.hor_box.addLayout(self.ch_grid)

        self.tabbox = QTabWidget()
        self.tabbox.setMovable(True)
        self.current_scheme = self.scheme_combo_box.currentText()
        print('Current scheme: ', self.current_scheme)
        for group in self.schemes[self.current_scheme]:
            tab = group.PulseGroupWidget(parent=self, data=group)
            # tab.updateReferences()
            self.tabbox.addTab(tab, group.name)
        # tab1 = pulse_group(self)

        self.hor_box.addWidget(self.tabbox)
        vbox.addLayout(self.hor_box)
        # self.channels_vbox
        self.setLayout(vbox)
        # few buttons like save, add_scheme (to add current version of the scheme) and so on
        # self.current_scheme  # set_current_scheme (may be from last session)
        # self.pulses_tabs = QTabWidget()  # initialize tab widget with tabs for pulse_groups
        # for p_group in current_scheme:
        #     self.pulses_tabs.addTab(p_group.get_tab(SMTH), p_group.name)
        # probably here i have to return widget or smth

    def addGroup(self):
        print('addGroup')
        new_group = pulse_group(self)
        self.schemes[self.current_scheme].append(new_group)
        # self.tabbox.addTab(new_group,new_group.name)
        # self.tabbox.setCurrentWidget(new_group)
        # self.show()
        self.updateScheme()

    def updateScheme(self):
        print('updateScheme')
        self.tabbox.clear()
        self.current_scheme = self.scheme_combo_box.currentText()
        for group in self.schemes[self.current_scheme]:
            tab = group.PulseGroupWidget(parent=self, data=group)
            # tab.updateReferences()
            self.tabbox.addTab(tab, group.name)
        self.updateConfig()
        self.onAnyChange()

    def updateConfig(self):
        print('updateConfig')
        if not os.path.exists(
                os.path.join(digital_pulses_folder, config_scheme_file)):
            config = {}
        else:
            with open(os.path.join(digital_pulses_folder, config_scheme_file),
                      'rb') as f:
                print('here-there')
                config = pickle.load(f)
                if type(config) != type(dict()):
                    print('smth rong with config file')
                    config = {}
        config['current_scheme'] = self.current_scheme
        with open(os.path.join(digital_pulses_folder, config_scheme_file),
                  'wb') as f:
            pickle.dump(config, f)

    def saveScheme(self):
        print('saveScheme')
        if not os.path.exists(digital_pulses_folder):
            print('create folder')
            os.mkdir(digital_pulses_folder)
        print(self.schemes[self.current_scheme][0].__dict__)
        with open(os.path.join(digital_pulses_folder, self.current_scheme),
                  'wb') as f:
            pickle.dump(self.schemes[self.current_scheme], f)

    def saveAsScheme(self):
        print('saveAsScheme')
        if not os.path.exists(digital_pulses_folder):
            print('create folder')
            os.mkdir(digital_pulses_folder)
        fname = QFileDialog.getSaveFileName(self,
                                            directory=digital_pulses_folder)[0]
        with open(fname, 'wb') as f:
            pickle.dump(self.schemes[self.current_scheme], f)
        QWidget().setLayout(self.layout())
        fname = os.path.basename(fname)
        self.schemes[fname] = deepcopy(self.schemes[self.current_scheme])
        self.current_scheme = fname
        print('lll')
        self.initUI()

    def loadSchemes(self):
        print('loadSchemes')
        self.schemes = {}
        self.current_scheme = None
        if not os.path.exists(digital_pulses_folder):
            print('create folder')
            os.mkdir(digital_pulses_folder)
        files = os.listdir(digital_pulses_folder)
        if len(files) != 0:
            for fname in files:
                if not fname.startswith('config'):
                    with open(os.path.join(digital_pulses_folder, fname),
                              'rb') as f:
                        print('here')
                        self.schemes[fname] = pickle.load(f)
            if not os.path.exists(
                    os.path.join(digital_pulses_folder, config_scheme_file)):
                config = {}
            else:
                with open(
                        os.path.join(digital_pulses_folder,
                                     config_scheme_file), 'rb') as f:
                    print('here-there')
                    config = pickle.load(f)
                    if type(config) != type(dict()):
                        print('smth rong with config file')
                        config = {}
            if 'current_scheme' in config.keys():
                self.current_scheme = config['current_scheme']
            elif len(self.schemes):
                self.current_scheme = list(self.schemes.keys())[0]

    def pulseByName(self, name):
        group_names = [
            group.name for group in self.schemes[self.current_scheme]
        ]
        return self.schemes[self.current_scheme][group_names.index(name)]

    def calculateOutputData(self):
        self.output_data = {}
        end_time = 0
        first_time = 0
        for pulse_group in self.schemes[self.current_scheme]:
            if pulse_group.is_active:
                for pulse in pulse_group.pulses:
                    if pulse.is_active:
                        if not pulse.channel in self.output_data.keys():
                            self.output_data[pulse.channel] = []
                        new_points = self.getPoints(pulse, pulse_group)
                        for point in new_points:
                            if point[0] not in [
                                    point[0] for point in self.output_data[
                                        pulse.channel]
                            ]:
                                self.output_data[pulse.channel].append(point)
                            else:
                                self.output_data[pulse.channel].remove(
                                    (point[0], not point[1]))
                        # if new_points[-1][0] > end_time:
                        #     end_time = new_points[-1][0]

        for point_list in self.output_data.values():
            point_list.sort(key=lambda x: x[0])
            if point_list[0][0] != 0:
                point_list.insert(0, (0, 0))
        for points in self.output_data.values():
            if points[-1][0] > end_time:
                end_time = points[-1][0]
            if first_time == 0:
                first_time = end_time
            if points[1][0] < first_time:
                first_time = points[1][0]
        for points in self.output_data.values():
            points.append((end_time + 10, points[-1][1]))
            points.insert(1, (first_time - 100, points[0][1]))
        self.first_time = first_time
        self.end_time = end_time

    def getPoints(self, pulse, group):
        group_begin = group.delay + group.getReferencePoint(self)
        if not pulse.edge:
            if pulse.length == 0:
                return ((group_begin + pulse.delay, 1),
                        (group_begin + group.length, 0))
            elif pulse.length > 0:
                return ((group_begin + pulse.delay, 1),
                        (group_begin + pulse.delay + pulse.length, 0))
            else:
                return ((group_begin + pulse.delay, 1),
                        (group_begin + group.length + pulse.length, 0))
        else:
            if pulse.length == 0:
                return ((group_begin + pulse.delay, 1),
                        (group_begin + group.length, 0))
            elif pulse.length > 0:
                return ((group_begin + group.length + pulse.delay, 1),
                        (group_begin + group.length + pulse.delay +
                         pulse.length, 0))
            else:
                return ((group_begin + group.length + pulse.delay +
                         pulse.length, 1),
                        (group_begin + group.length + pulse.delay, 0))

    def onAnyChange(self):
        self.updateChannelPannel()
        self.calculateOutputData()
        # self.plotPulses()
        print(self.output_data)

    def updateChannelPannel(self):
        print('updateChannelPannel')
        flag_to_redraw = False
        channels_in_pulses = set()
        for pulse_group in self.schemes[self.current_scheme]:
            for pulse in pulse_group.pulses:
                channels_in_pulses.add(pulse.channel)
                if pulse.channel not in self.active_channels:
                    self.active_channels[pulse.channel] = 'StandBy'
                    flag_to_redraw = True
        to_remove = []
        for key in self.active_channels:
            if key not in channels_in_pulses:
                to_remove.append(key)
        for key in to_remove:
            self.active_channels.pop(key)
            flag_to_redraw = True
        print(self.active_channels)
        if not flag_to_redraw:
            self.showChannelPannel()
        else:
            QWidget().setLayout(self.layout())
            self.initUI()

    def showChannelPannel(self):
        print('Now', self.ch_grid)
        # self.ch_grid = QGridLayout()
        for i, channel in enumerate(sorted(self.active_channels)):
            self.ch_grid.addWidget(QLabel(channel), i, 0)
            alwais_on = QCheckBox()
            if self.active_channels[channel] == 'On':
                alwais_on.setChecked(True)
            self.ch_grid.addWidget(alwais_on, i, 1)
            alwais_off = QCheckBox()
            if self.active_channels[channel] == 'Off':
                alwais_on.setChecked(True)
            self.ch_grid.addWidget(alwais_off, i, 2)
        # self.initUI()

    def deleteGroup(self, group_name):
        print("deleteGroup")
        group_names = [
            group.name for group in self.schemes[self.current_scheme]
        ]
        # print(group_name, group_names.index(group_name))
        self.schemes[self.current_scheme].pop(group_names.index(group_name))
        self.updateScheme()
class SnifferFilter(QDialog):

    ## The constructor.
    #  Initialize lists to be filled with filtered payloads and
    #  create the dialog.
    def __init__(self, parent):
        super(SnifferFilter, self).__init__()
        self.parent = parent
        self.setWindowTitle('SnifferFilter')
        self.filteredPayloadList = []
        self.filteredIdList = []
        self.layoutingComplete = False

        self.calledBy = 'NONE'  # We store here which tab the filter belongs to, if there is no parent.(Like when the filter is global!)

        self.objectTabDict = {}
        self.filteredObjectDict = {}

        self.resize(670, 700)

    ## Filters the global payloadList by ID by iterating through it and
    #  appending to a new list by filter-criteria
    #  @param filterList a list of IDs that are to be transferred to the new list
    def filterPayloadsByID(self, filterList):
        for payload in Globals.payloadList:
            if hasattr(payload, 'payloadHead'):
                if Globals.tspDict[
                        payload.payloadHead.informationID][0] in filterList:
                    self.filteredPayloadList.append(payload)
                else:
                    #print('informationID is in filteredIdList, skipping packet')
                    pass

    ## Filters the global payloadList by Message by iterating through it and
    #  appending to a new list by filter-criteria
    #  @param filterDict a dictionary of Key: ObjectType and Value: ObjectsToKeep as a template
    #  of which items are to be kept in the filteredList
    def filterPayloadsByMessage(self, filterDict):
        localFilteredList = []
        for payload in self.filteredPayloadList:
            print('payloadID:' + str(payload.payloadHead.informationID))
            # If the ID has nothing to do with the object, we can safely add it.
            if payload.payloadHead.informationID is 23:
                x = 0
                pass
            if isRelatedToObject(payload):
                for objType, messageID in filterDict.items():
                    print(Globals.objectTypeDict[getObjectType(payload)])
                    if Globals.objectTypeDict[getObjectType(
                            payload
                    )] == objType:  # If the objectType matches the one in the dictionary
                        if objType == 0:
                            x = 0
                            pass
                        if getDataType(payload) == 2:
                            if payload.data2 not in messageID:  # and the message does not match the dictionary
                                print(
                                    'Passing data with msgid: ' +
                                    str(payload.data2)
                                )  # don't append, but print that we skipped this one
                            else:
                                localFilteredList.append(
                                    payload
                                )  # the message does match the dictionary -> we want to keep it, so we add it to the list

                        elif getDataType(payload) == 1:
                            if payload.data1 not in messageID:
                                print('Passing data with msgid: ' +
                                      str(payload.data1))
                            else:
                                localFilteredList.append(payload)
                        else:
                            localFilteredList.append(payload)
                    else:
                        # If the ID has nothing to do with the object, we can safely add it.
                        # Also, is the object is not even in the filterDict, we can add it too (this should not happen, but
                        # it's there for safety purposes
                        if getDataType(payload) == 0 or Globals.objectTypeDict[
                                getObjectType(payload)] not in filterDict:
                            localFilteredList.append(payload)
            else:
                localFilteredList.append(payload)

        # In every other case, append it to the list, since we only want to filter out specific objects
        self.filteredPayloadList = list(localFilteredList)

    ## Create the visible UI
    #  like the different tables, the searchbar etc.
    def setSnifferFilterUi(self):

        self.filterTabs = QTabWidget()
        self.H1layout = QHBoxLayout()
        self.Vlayout = QVBoxLayout()

        self.searchInputField = QLineEdit()
        self.searchInputField.setPlaceholderText(
            'Enter search term, then click search')
        self.searchButt = QPushButton('Search Table')
        self.saveFilterButt = QPushButton('Save Filter')
        self.filterTable = QTableWidget()

        self.saveFilterButt.clicked.connect(self.saveFilterList)
        self.searchButt.clicked.connect(
            lambda: self.searchInTable(self.searchInputField.text(), 0))

        # Create Table
        self.filterTableIndex = 0
        self.filterTable = QTableWidget()
        self.filterTableItem = QTableWidgetItem()
        self.filterTable.setRowCount(0)
        self.filterTable.setColumnCount(2)

        self.filterTable.setHorizontalHeaderLabels(
            'informationID;Enable'.split(';'))
        self.filterTable.resizeColumnsToContents()
        self.filterTableHeader = self.filterTable.horizontalHeader()
        self.filterTableHeader.setSectionResizeMode(0, QHeaderView.Stretch)
        self.filterTableHeader.setSectionResizeMode(
            1, QHeaderView.ResizeToContents)

        font = self.getFont()

        self.checkBoxAllIds = self.createCheckBox()
        self.filterTable.itemChanged.connect(self.filterAllIDs)
        self.checkBoxAllMessages = self.createCheckBox()

        # -- Add first Row for all -- #
        self.filterTable.insertRow(self.filterTableIndex)
        idFilterItem = QTableWidgetItem('FILTER ALL IDs')
        idFilterItem.setFont(font)
        messageFilterItem = QTableWidgetItem('FILTER ALL Messages')
        messageFilterItem.setFont(font)
        self.filterTable.setItem(self.filterTableIndex, 0, idFilterItem)
        self.filterTable.setItem(self.filterTableIndex, 1, self.checkBoxAllIds)
        self.filterTableIndex = self.filterTableIndex + 1

        # -- Add informationID filter rows -- #
        for keys, values in Globals.tspDict.items():
            if values[0].startswith('ID'):
                checkBoxFilter = self.createCheckBox()
                self.filterTable.insertRow(self.filterTableIndex)
                self.filterTable.setItem(self.filterTableIndex, 0,
                                         QTableWidgetItem(values[0]))
                self.filterTable.setItem(self.filterTableIndex, 1,
                                         checkBoxFilter)
                self.filterTableIndex = self.filterTableIndex + 1

        self.filterTabs.addTab(self.filterTable, 'Information ID')

        self.H1layout.addWidget(self.searchInputField)
        self.H1layout.addWidget(self.searchButt)
        self.Vlayout.addLayout(self.H1layout)
        self.Vlayout.addWidget(self.filterTabs)
        self.Vlayout.addWidget(self.saveFilterButt)
        #------------------------------------
        self.setLayout(self.Vlayout)
        self.layoutingComplete = True

    ## Updates the visible filter UI to the new objectList
    #  This function is called, when either a new measurement was executed or an old measurement was loaded.
    #  Since the objects shown in the Filter need to be updated.
    def updateObjectFilter(self):
        # -- Add message filter rows -- #
        font = self.getFont()
        self.filterTabs.clear()
        self.filterTabs.addTab(self.filterTable, 'Information ID')
        # For each object in objectList, create a new Table and add it to the tabs.
        for keys, values in Globals.objectDict.items():
            objectFilterTable = QTableWidget()
            objectFilterTable.setRowCount(0)
            objectFilterTable.setColumnCount(2)
            strSplit = keys + ';Enable'
            objectFilterTable.setHorizontalHeaderLabels(strSplit.split(';'))
            objectFilterTable.resizeColumnsToContents()
            filterTableHeader = objectFilterTable.horizontalHeader()
            filterTableHeader.setSectionResizeMode(0, QHeaderView.Stretch)
            filterTableHeader.setSectionResizeMode(
                1, QHeaderView.ResizeToContents)
            checkBoxFilter = self.createCheckBox()

            objectFilterTable.itemChanged.connect(
                lambda *a, table=objectFilterTable: self.filterAllObjectIDs(
                    *a, table=table))

            objectTableIndex = 0
            objectFilterTable.insertRow(objectTableIndex)
            objCat = QTableWidgetItem('FILTER ALL')
            objCat.setFont(font)
            objectFilterTable.setItem(objectTableIndex, 0, objCat)
            objectFilterTable.setItem(objectTableIndex, 1, checkBoxFilter)
            objectTableIndex = objectTableIndex + 1
            for keys2, values2 in values.items():
                objectFilterTable.insertRow(objectTableIndex)
                checkBoxFilter = self.createCheckBox()
                objectFilterTable.setItem(
                    objectTableIndex, 0,
                    QTableWidgetItem('ID: ' + str(keys2) + ' Name: ' +
                                     str(values2)))
                objectFilterTable.setItem(objectTableIndex, 1, checkBoxFilter)
                objectTableIndex = objectTableIndex + 1

            # Add the newly create table to the tabs.
            self.filterTabs.addTab(objectFilterTable, keys)
            self.objectTabDict[keys] = objectFilterTable

    ## Searches the table for a string and scrolls to the found item.
    #  @param textToSearch the string that needs to be found in the table
    #  @param column the column where the search needs to take place
    def searchInTable(self, textToSearch, column):
        # Create a model of the table, so we can match a text
        tableModel = self.filterTable.model()
        start = tableModel.index(0, column)
        matches = tableModel.match(start, Qt.DisplayRole, textToSearch, 1,
                                   Qt.MatchContains)
        # If there are multiple matches, we take the first one, select it and scroll to it
        if matches:
            index = matches[0]
            self.filterTable.selectionModel().select(
                index, QItemSelectionModel.Select)
            self.filterTable.scrollToItem(
                self.filterTable.itemFromIndex(index))

    ## CB: SaveFilterButton // Call the filterFunctions -> Filter the unfiltered list by ID and Object and call the update
    #  function of the executing tab in order to update their UI.
    def saveFilterList(self):
        self.filteredPayloadList.clear()

        #--Save by ID--#
        rowCnt = self.filterTable.rowCount()
        self.filteredIdList.clear()
        for rows in range(0, rowCnt):
            if self.filterTable.item(rows, 1).checkState() == Qt.Checked:
                #print(self.filterTable.item(rows,0).text())
                self.filteredIdList.append(
                    self.filterTable.item(rows, 0).text())
        self.filterPayloadsByID(self.filteredIdList)
        print(len(self.filteredPayloadList))
        print(len(Globals.payloadList))

        #--Save By Objects--#
        self.filteredObjectDict.clear()
        for objType, objectTable in self.objectTabDict.items():
            rowCnt = objectTable.rowCount()
            objectsToFilter = []
            for rows in range(0, rowCnt):
                if objectTable.item(rows, 1).checkState() == Qt.Checked:
                    #print(objectTable.item(rows,0).text())
                    try:
                        objectsToFilter.append(
                            int(
                                re.search('ID: (.*) Name:.*',
                                          objectTable.item(
                                              rows, 0).text()).group(1)))
                        self.filteredObjectDict[objType] = objectsToFilter
                        #print('Found Regex: '+re.search('ID: (.*) Name:.*',objectTable.item(rows,0).text()).group(1))
                    except:
                        print('Error when parsing TableRegex...this is okay')
        self.filterPayloadsByMessage(self.filteredObjectDict)

        # We filtered the list, now we hide the windows and call the update-function
        # If the maintainer of the tab did not follow the implementation guide, there is no update-function to call.
        # We still save the filtered list, so we print a message to show where to find it.
        self.hide()
        try:
            self.parent.filterUpdated(self.filteredIdList,
                                      self.filteredPayloadList)
        except AttributeError:
            print(
                'No corresponding callable function filterUpdated was found, you can access the most recent filteredList via self.snifferFilter.filteredIdList'
            )
        try:
            Globals.dockDict[self.calledBy].filterUpdated(
                self.filteredIdList, self.filteredPayloadList)
        except:
            print('not global!')

    ## Check whether the first checkbox was checked and then update the entire ID table to either checked or unchecked state
    #  @param checkBox a checkBox we perform the query on
    def filterAllIDs(self, checkBox):
        if self.layoutingComplete == True:
            if checkBox.column() == 1 and checkBox.row(
            ) == 0:  # We clicked the toggle ID checkbox
                if checkBox.checkState() == Qt.Checked:
                    rowCnt = self.filterTable.rowCount()
                    for rows in range(0, rowCnt):
                        try:
                            self.filterTable.item(rows,
                                                  1).setCheckState(Qt.Checked)
                        except AttributeError:
                            print(
                                'no items after' + str(rows) +
                                'found...Maybe this column has less items than'
                                + str(rowCnt) + '?')
                elif checkBox.checkState() == Qt.Unchecked:
                    rowCnt = self.filterTable.rowCount()
                    for rows in range(0, rowCnt):
                        try:
                            self.filterTable.item(rows, 1).setCheckState(
                                Qt.Unchecked)
                        except AttributeError:
                            print(
                                'no items after' + str(rows) +
                                'found...Maybe this column has less items than'
                                + str(rowCnt) + '?')
                else:
                    print(
                        'neither checked nor unchecked...should never be here..'
                    )

    ## Check whether the first checkbox was checked and then update the entire ObjectIDtable to either checked or unchecked state
    #  @param checkBox a checkBox we perform the query on
    #  @param table the table that is to be updated
    def filterAllObjectIDs(self, checkBox, table):
        if (self.objectTabDict):
            if checkBox.column() == 1 and checkBox.row(
            ) == 0:  # We clicked the toggle ID checkbox
                if checkBox.checkState() == Qt.Checked:
                    rowCnt = table.rowCount()
                    for rows in range(0, rowCnt):
                        try:
                            table.item(rows, 1).setCheckState(Qt.Checked)
                        except AttributeError:
                            print(
                                'no items after' + str(rows) +
                                'found...Maybe this column has less items than'
                                + str(rowCnt) + '?')
                elif checkBox.checkState() == Qt.Unchecked:
                    rowCnt = table.rowCount()
                    for rows in range(0, rowCnt):
                        try:
                            table.item(rows, 1).setCheckState(Qt.Unchecked)
                        except AttributeError:
                            print(
                                'no items after' + str(rows) +
                                'found...Maybe this column has less items than'
                                + str(rowCnt) + '?')
                else:
                    print(
                        'neither checked nor unchecked...should never be here..'
                    )

    # --- HELPER FUNCTIONS --- #
    ## Create a defined checkbox within a tableWidgetItem to facilitate filling the table
    #  @return the created checkbox
    def createCheckBox(self):
        myCheck = QTableWidgetItem()
        myCheck.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
        myCheck.setCheckState(Qt.Checked)
        return myCheck

    ## Create a defined font (bold,underlined) to facilitate filling the table
    #  @return the created font
    def getFont(self):
        font = QFont()
        font.setBold(True)
        font.setUnderline(True)
        return font
Exemple #26
0
class Window(QDialog):
    '''
    Initiates the PyQT5 GUI
    '''

    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        # read in extracted features
        with open('./features_extracted.json', 'r') as file:
            self.loaded_json_data = json.load(file)

        # init lists of widgets for tabs
        self.figures = []
        self.canvases = []

        self.toolbars = []

        # should've made this bottom bar not be per-tab, rather one and its get updated
        self.brainStateLabels = []
        self.pointClickedLabels = []
        self.predictAllButtons = []

        self.axes = []
        self.tabsList = []

        # create menubar
        self.menu_bar = QMenuBar()
        fileMenu = self.menu_bar.addMenu('File')

        # add the Import Data action (Under File)
        importDataAction = QAction('Import Data', self)
        importDataAction.setShortcut('Ctrl+I')
        importDataAction.triggered.connect(self.openFileDialog)

        fileMenu.addAction(importDataAction)

        self.tab_widget = QTabWidget()

        # when you change tabs, spark event
        self.tab_widget.currentChanged.connect(self.onChange)

        # Start the openFileDialog (First thing the user will see)
        self.openFileDialog()

        # if len(self.features) < 1:
        #     #no data imported exit
        #     exit(1)

        # load the model
        self.model = fNIR.load_model()

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.tab_widget)
        self.setLayout(main_layout)

    def createAllDataTab(self):

        # i = 0 because it is the first tab in all lists
        i = 0

        self.tabsList.append(QWidget())

        # a figure instance to plot on
        self.figures.append(plt.figure())

        # this is the Canvas Widget that displays the plot or (figure)
        self.canvases.append(FigureCanvas(self.figures[i]))

        # this is the Navigation Toolbar for the top of the plot
        self.toolbars.append(NavigationToolbar(self.canvases[i], self))

        # create the layout and menu
        layout = QVBoxLayout()

        layout.setMenuBar(self.menu_bar)
        layout.addWidget(self.toolbars[i])

        layout.addWidget(self.canvases[i])

        outputHBox = QHBoxLayout()

        # create the labels that we will need to access again
        self.brainStateLabels.append(QLabel("Brain State : "))
        self.brainStateLabels[i].setFixedSize(200, 20)

        self.pointClickedLabels.append(QLabel(""))
        self.pointClickedLabels[i].setFixedSize(400, 20)

        # predict on all Button
        predictButton = QPushButton("Predict on All Data", self)
        predictButton.setToolTip('Predicts on all data [0, (Time = Max)]')

        # connect the event to the button when clicked
        predictButton.clicked.connect(self.predictOnAll)
        predictButton.setFixedSize(200, 25)

        self.predictAllButtons.append(predictButton)

        # add all widgets to the layouts, 3 VBox's for 3 Columns
        predictionStatisticsVBoxLeft = QVBoxLayout()
        predictionStatisticsVBoxLeft.addWidget(self.brainStateLabels[i])

        predictionStatisticsVBoxMiddle = QVBoxLayout()
        predictionStatisticsVBoxMiddle.addWidget(self.pointClickedLabels[i])

        predictionStatisticsVBoxRight = QVBoxLayout()
        predictionStatisticsVBoxRight.addWidget(self.predictAllButtons[i])

        # add them all to the bottom HBox (row)
        outputHBox.addLayout(predictionStatisticsVBoxLeft)
        outputHBox.addLayout(predictionStatisticsVBoxMiddle)
        outputHBox.addLayout(predictionStatisticsVBoxRight)

        # set the layout
        layout.addLayout(outputHBox)
        self.tabsList[i].setLayout(layout)

        # add the new tab to the widget
        self.tab_widget.addTab(self.tabsList[i], "All Tasks Data")

        # motion_event_id = self.canvas.mpl_connect('motion_notify_event', self.on_move) #TODO IF A ON_MOUSE_MOVE EVENT IS NEEDED
        # press_event_id = self.canvases[i].mpl_connect('button_press_event', lambda event: self.onclick(event, i))
        # plot the data on the i'th tab
        self.plotAllData()

        self.tabsList[i].setLayout(layout)

    def createTab(self, i):
        '''
        Generates the tab for the task at the index given
        :param i: task number
        '''

        self.tabsList.append(QWidget())

        # a figure instance to plot on
        self.figures.append(plt.figure())

        # this is the Canvas Widget that displays the plot or (figure)
        self.canvases.append(FigureCanvas(self.figures[i]))

        # this is the Navigation Toolbar for the top of the plot
        self.toolbars.append(NavigationToolbar(self.canvases[i], self))

        # create the layout and menu
        layout = QVBoxLayout()

        layout.setMenuBar(self.menu_bar)
        layout.addWidget(self.toolbars[i])

        layout.addWidget(self.canvases[i])

        outputHBox = QHBoxLayout()

        # create the labels that we will need to access again
        self.brainStateLabels.append(QLabel("Brain State : "))
        self.brainStateLabels[i].setFixedSize(200, 20)

        self.pointClickedLabels.append(QLabel(""))
        self.pointClickedLabels[i].setFixedSize(400, 20)

        predictButton = QPushButton("Predict on Task Data", self)
        predictButton.setToolTip('Predicts on all task data')

        predictButton.clicked.connect(self.predictOnAll)
        predictButton.setFixedSize(200, 25)

        self.predictAllButtons.append(predictButton)

        predictionStatisticsVBoxLeft = QVBoxLayout()
        predictionStatisticsVBoxLeft.addWidget(self.brainStateLabels[i])

        predictionStatisticsVBoxMiddle = QVBoxLayout()
        predictionStatisticsVBoxMiddle.addWidget(self.pointClickedLabels[i])

        predictionStatisticsVBoxRight = QVBoxLayout()
        predictionStatisticsVBoxRight.addWidget(self.predictAllButtons[i])

        outputHBox.addLayout(predictionStatisticsVBoxLeft)
        outputHBox.addLayout(predictionStatisticsVBoxMiddle)
        outputHBox.addLayout(predictionStatisticsVBoxRight)

        layout.addLayout(outputHBox)
        self.tabsList[i].setLayout(layout)

        # add the new tab to the widget
        self.tab_widget.addTab(self.tabsList[i], "Task " + str(i))

        # plot the data on the i'th tab
        self.plot(i)

        self.tabsList[i].setLayout(layout)

    def openFileDialog(self):
        '''
        Select the file to import the data from.

        Important function, sets the lay for all tabs

        '''
        options = QFileDialog.Options()

        # return the file_path from the file chosen
        file_path, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "",
                                                   "All Files (*);;", options=options)

        # if filepath isnt None, get the data from the file imported
        if file_path:

            # clear all lists (just incase you import more than one file per session)
            self.tab_widget.clear()
            self.figures.clear()
            self.canvases.clear()

            self.toolbars.clear()

            self.brainStateLabels.clear()
            self.pointClickedLabels.clear()
            self.predictAllButtons.clear()

            self.axes.clear()
            self.tabsList.clear()

            # setting the features data
            data = fNIRLib.importSingleton(file_path)

            data_frames = []
            for df in data:
                data_frames.append(df)

            all_features = pd.concat(data_frames)

            # Drop the 16th column (time)
            self.features = all_features.drop(all_features.columns[[16]], axis=1)

            # first create the 'All Data' Tab
            self.createAllDataTab()

            # from 1-20, get the 20 tasks and make new tabs for each
            for i in range(1, int(len(self.features) / 260) + 1):
                self.createTab(i)

            # Set the window title to include the file_path
            self.setWindowTitle("fNIRS Analysis (%s)" % file_path)

    def onclick(self, event, i):
        '''
        Event Listener for when a user clicks on the Plot

        Left Mouse Click:
            Expand the Layout to Tight
            Clear the rectangles off the screen
        Right Mouse Click:
            Draw a rectangle to show where you clicked
            Predict on the data where you clicked
        :param event: The event the user invoked (left mouse button, right mouse button)
        :param i: which tab the user is currently on
        '''
        try:
            # print the data of the event
            print('%s click: button=%d, x=%d, y=%d, xdata=%f, ydata=%f' %
                  ('double' if event.dblclick else 'single', event.button,
                   event.x, event.y, event.xdata, event.ydata))
        except TypeError:
            # if you dont click on the plot
            print("Click on Plot")

        # If left mouse button, set tight layout and clear rectangles
        if event.button == 1:
            self.figures[i].tight_layout()
            # clear rectangles
            self.axes[i].patches.clear()

        # If right mouse button, draw rectangle and predict
        if event.button == 3:
            # draw rectangle from event.xdata (Point Clicked) to (i -1) * 260
            self.draw_rectangle(event.xdata, i)

            # round the point clicked
            x_point = int(round(event.xdata))

            # get the points from beginning of task to point clicked
            ml_data = self.features.iloc[(i - 1) * 260:x_point]

            # add the columns needed for tsfresh
            ml_data['column_id'] = ml_data.shape[0] * [0]
            ml_data['time_id'] = range(ml_data.shape[0])

            # load the scaler
            scaler = joblib.load("fNIRscaler.pkl")

            # extract the features
            features_extracted = feature_extraction.extract_features(ml_data,
                                                                     kind_to_fc_parameters=self.loaded_json_data,
                                                                     column_id="column_id",
                                                                     column_sort="time_id")

            features_extracted = pd.DataFrame(scaler.transform(features_extracted), columns=list(features_extracted))

            # predict the class with the features extracted
            predicted_class = self.model.predict_classes(features_extracted)[0][0]

            print(predicted_class)

            # set the labels and update
            if predicted_class == 0:
                self.brainStateLabels[i].setText("Brain State: Low")
            else:
                self.brainStateLabels[i].setText("Brain State: High")

            self.pointClickedLabels[i].setText("Predicted on Time from %s, %s " % ((i - 1) * 260, x_point))
            self.pointClickedLabels[i].update()
            self.brainStateLabels[i].update()

        # update the plot for the rectangle drawn or erased.
        self.canvases[i].draw()

    def onChange(self):
        '''
        If you change tab, set the button press to be activated on current canvas, and set the tab to tight layout
        :return:
        '''
        press_event_id = self.canvases[self.tab_widget.currentIndex()] \
            .mpl_connect('button_press_event',
                         lambda event: self.onclick(event, self.tab_widget.currentIndex()))

        self.figures[self.tab_widget.currentIndex()].tight_layout()  # TODO NOT ALWAYS WORKING

    def draw_rectangle(self, x, i):
        '''
        Draws a rectangle on the current canvas from x = (0 to x)
        :param x: width
        :param i: current tab
        '''
        self.axes[i].patches.clear()
        rectangle = Rectangle((0, -10), width=x, height=100, color='#0F0F0F2F')
        self.axes[i].add_patch(rectangle)
        self.canvases[i].draw()

    def plot(self, i):
        '''
        Plot the data on the current plot
        :param i: tab index
        '''
        # clear figures at i
        self.figures[i].clear()

        # create an axis
        self.axes.append(self.figures[i].add_subplot(111))

        # plot the data for the current task
        self.axes[i].plot(self.features.iloc[(i - 1) * 260: i * 260],
                          '-')  # TODO this may need to be dynamic if the data isnt evenly divisible by 260

        # set x and y labels
        self.axes[i].set_ylabel('Oxygenation or De-Oxygenation Level')
        self.axes[i].set_xlabel('Time Step')

        # create the legend up top with the column names from features
        self.axes[i].legend(self.axes[i].get_lines(), self.features.columns, bbox_to_anchor=(0., 1.02, 1., .102), loc=3,
                            ncol=8, mode="expand", borderaxespad=0.)

        # set the x limits for current task
        self.axes[i].set_xlim((i - 1) * 260, i * 260)

        # set tight layout
        self.figures[i].tight_layout()

        # refresh canvas
        self.canvases[i].draw()

    def plotAllData(self):
        '''
        Same as plot(), just from 0 to len(self.features)
        :return:
        '''
        # first tab for All Data (i = 0)
        i = 0

        self.figures[i].clear()

        # create an axis
        self.axes.append(self.figures[i].add_subplot(111))

        # self.axes[i].set_aspect('auto')

        self.axes[i].plot(self.features.iloc[i: len(self.features)],
                          '-')  # TODO this may need to be dynamic if the data isnt evenly divisible by 260

        self.axes[i].set_ylabel('Oxygenation or De-Oxygenation Level')
        self.axes[i].set_xlabel('Time Step')

        self.axes[i].legend(self.axes[i].get_lines(), self.features.columns, bbox_to_anchor=(0., 1.02, 1., .102), loc=3,
                            ncol=8, mode="expand", borderaxespad=0.)
        self.axes[i].set_xlim(0, len(self.features))
        self.figures[i].tight_layout()

        # refresh canvas
        self.canvases[i].draw()

    def predictOnAll(self):
        '''
        Predicts on the current task, start of task to the end of the task
        The same as onClick right mouse button.
        :return:
        '''
        i = self.tab_widget.currentIndex()

        ml_data = self.features.iloc[(i - 1) * 260: i * 260]

        ml_data['column_id'] = ml_data.shape[0] * [0]
        ml_data['time_id'] = range(ml_data.shape[0])
        features_extracted = feature_extraction.extract_features(ml_data,
                                                                 kind_to_fc_parameters=self.loaded_json_data,
                                                                 column_id="column_id",
                                                                 column_sort="time_id")
        predicted_class = self.model.predict_classes(features_extracted.values)[0][0]
        if predicted_class == 0:
            self.brainStateLabels[i].setText("Brain State: Low")
        else:
            self.brainStateLabels[i].setText("Brain State: High")

        self.pointClickedLabels[i].setText("Predicted on Data from %s, %s " % ((i - 1) * 260, i * 260))
        self.pointClickedLabels[i].update()
        self.brainStateLabels[i].update()

        self.axes[i].patches.clear()

        self.canvases[i].draw()
Exemple #27
0
class interface_editor(QWidget, tab_base):
    def __init__(self):
        QWidget.__init__(self)
        self.setMinimumSize(1000, 600)

        self.main_vbox = QVBoxLayout()

        self.setWindowIcon(icon_get("interfaces"))

        self.setWindowTitle(_("Interface editor") + " (https://www.gpvdm.com)")

        toolbar = QToolBar()
        toolbar.setIconSize(QSize(48, 48))

        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        toolbar.addWidget(spacer)

        self.help = QAction(icon_get("help"), _("Help"), self)
        self.help.setStatusTip(_("Help"))
        self.help.triggered.connect(self.callback_help)
        toolbar.addAction(self.help)

        self.main_vbox.addWidget(toolbar)

        self.notebook = QTabWidget()

        css_apply(self, "tab_default.css")

        self.main_vbox.addWidget(self.notebook)
        self.setLayout(self.main_vbox)

        #self.notebook.setTabsClosable(True)
        #self.notebook.setMovable(True)
        #self.notebook.setTabBar(QHTabBar())
        #self.notebook.setTabPosition(QTabWidget.West)

        global_object_register("interface_update", self.update)
        self.update()

    def update(self):
        self.notebook.clear()

        epi = get_epi()
        for i in range(0, len(epi.layers) - 1):
            l0 = epi.layers[i]
            l1 = epi.layers[i + 1]
            if l0.interface_file != "none":
                name = l0.name + "/" + l1.name
                widget = tab_class(l0.interface_file + ".inp")
                self.notebook.addTab(widget, name)

    def help(self):
        help_window().help_set_help([
            "tab.png",
            "<big><b>Density of States</b></big>\nThis tab contains the electrical model parameters, such as mobility, tail slope energy, and band gap."
        ])

    def callback_help(self, widget):
        webbrowser.open('http://www.gpvdm.com/man/index.html')
Exemple #28
0
class OperatorMaintain(QSplitter):
    def __init__(self, *args, **kwargs):
        super(OperatorMaintain, self).__init__(*args, **kwargs)
        layout = QHBoxLayout()
        layout.setContentsMargins(QMargins(0,0,0,1))
        layout.setSpacing(0)
        # 左侧管理项目列表
        self.operate_list = QListWidget(self)

        self.operate_list.clicked.connect(self.operate_list_clicked)
        # layout.addWidget(self.operate_list, alignment=Qt.AlignLeft)
        self.addWidget(self.operate_list)
        # 右侧tab显示
        self.frame_tab = QTabWidget()
        self.frame_tab.setDocumentMode(True)
        self.frame_tab.tabBar().hide()
        # layout.addWidget(self.frame_tab)
        self.addWidget(self.frame_tab)
        # self.setLayout(layout)
        self.setStretchFactor(1, 2)
        self.setStretchFactor(2, 8)
        self.setHandleWidth(1)
        self.operate_list.setObjectName('optsList')
        self.setStyleSheet("""
        #optsList{
            outline:none;
            border:none;
            border-right: 1px solid rgb(180,180,180);
            background-color: rgb(240,240,240);
        }
        #optsList::item{
           height:25px;
        }
        #optsList::item:selected{
           border-left:3px solid rgb(100,120,150);
           color:#000;
           background-color:rgb(180,220,230);
        }
        """)

    # 加入运营管理菜单
    def addOperatorItem(self):
        self.operate_list.addItems([u'用户管理', u'功能管理', u'品种管理', u'广告管理'])

    # 点击左侧管理菜单
    def operate_list_clicked(self):
        text = self.operate_list.currentItem().text()
        if text == u'用户管理':
            tab = UserManagePage(parent=self)
            tab.getCurrentUsers()
        elif text == u'功能管理':
            tab = ModuleManagePage(parent=self)
            tab.getCurrentModules()
        elif text == u'品种管理':
            tab = VarietyManagePage(parent=self)
            tab.getCurrentVarieties()
        elif text == u'广告管理':
            tab = AdvertisementPage(parent=self)
            tab.getAdvertisements()
        else:
            tab = QLabel(parent=self,
                         styleSheet='font-size:16px;font-weight:bold;color:rgb(230,50,50)',
                         alignment=Qt.AlignCenter)
            tab.setText("「" + text + "」正在加紧开放中...")
        self.frame_tab.clear()
        self.frame_tab.addTab(tab, text)
Exemple #29
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.trex_service: TrexService = Container.trex_service()
        self.trex_service.connected.connect(self.handle_connected)
        self.__init_ui()

    def __init_ui(self):
        self.create_menu()

        self.port_list = QListWidget(self)
        dock = QDockWidget(self.tr('Port list'), self)
        dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        dock.setFeatures(QDockWidget.DockWidgetMovable)
        dock.setWidget(self.port_list)
        self.addDockWidget(Qt.LeftDockWidgetArea, dock)

        self.central_widget = QTabWidget(self)
        self.setCentralWidget(self.central_widget)

        self.setMinimumSize(1000, 700)
        self.setWindowTitle('TRex Panel')
        self.show()

    def create_menu(self):
        menu_bar = self.menuBar()
        file_menu: QMenu = menu_bar.addMenu(self.tr('&File'))

        connect_act = QAction(self.tr('&Connect'), self)
        connect_act.setStatusTip(self.tr('Connect to TRex server'))
        connect_act.triggered.connect(self.open_connect_dialog)
        file_menu.addAction(connect_act)

    def open_connect_dialog(self):
        dlg = ConnectDialog(self)
        dlg.show()

    def closeEvent(self, ev):
        reply = QMessageBox.question(self, 'Message', 'Are you sure?',
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            ev.accept()
        else:
            ev.ignore()

    @pyqtSlot()
    def handle_connected(self):
        self.build_port_list()
        self.central_widget.clear()
        self.add_system_info_page()

    def add_system_info_page(self):
        system_info_page = SystemInfoPage(self)
        self.central_widget.addTab(system_info_page, self.tr('System info'))

    def build_port_list(self):
        client: ASTFClient = self.trex_service.get_client()
        info = client.get_server_system_info()
        ports = info.get('ports')
        font = QFont()
        font.setPointSize(14)
        for index, port in enumerate(ports, start=1):
            item = QListWidgetItem(self.port_list)
            item.setData(Qt.EditRole, port)
            item.setText(self.tr('Port ') + str(index))
            item.setFont(font)
Exemple #30
0
class AppWindow(QMainWindow):
    onRestart = pyqtSignal(name='onRestart')

    def __init__(self, dwarf_args, flags=None):
        super(AppWindow, self).__init__(flags)

        self.dwarf_args = dwarf_args

        self.session_manager = SessionManager(self)
        self.session_manager.sessionCreated.connect(self.session_created)
        self.session_manager.sessionStopped.connect(self.session_stopped)
        self.session_manager.sessionClosed.connect(self.session_closed)

        self._tab_order = [
            'memory', 'modules', 'ranges', 'jvm-inspector', 'jvm-debugger'
        ]

        self.menu = self.menuBar()
        self._is_newer_dwarf = False
        self.view_menu = None

        #dockwidgets
        self.watchers_dwidget = None
        self.hooks_dwiget = None
        self.bookmarks_dwiget = None
        self.registers_dock = None
        self.console_dock = None
        self.backtrace_dock = None
        self.threads_dock = None
        #panels
        self.asm_panel = None
        self.console_panel = None
        self.context_panel = None
        self.backtrace_panel = None
        self.contexts_list_panel = None
        self.data_panel = None
        self.emulator_panel = None
        self.ftrace_panel = None
        self.hooks_panel = None
        self.bookmarks_panel = None
        self.smali_panel = None
        self.java_inspector_panel = None
        self.java_explorer_panel = None
        self.java_trace_panel = None
        self.memory_panel = None
        self.modules_panel = None
        self.ranges_panel = None
        self.search_panel = None
        self.trace_panel = None
        self.watchers_panel = None
        self.welcome_window = None

        self._ui_elems = []

        self.setWindowTitle(
            'Dwarf - A debugger for reverse engineers, crackers and security analyst'
        )

        # load external assets
        _app = QApplication.instance()

        self.remove_tmp_dir()

        # themes
        self.prefs = Prefs()
        self.set_theme(self.prefs.get('dwarf_ui_theme', 'black'))

        # load font
        if os.path.exists(utils.resource_path('assets/Anton.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/Anton.ttf'))
        if os.path.exists(utils.resource_path('assets/OpenSans-Regular.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/OpenSans-Regular.ttf'))
            _app.setFont(QFont("OpenSans", 9, QFont.Normal))
            if os.path.exists(utils.resource_path('assets/OpenSans-Bold.ttf')):
                QFontDatabase.addApplicationFont(
                    utils.resource_path('assets/OpenSans-Bold.ttf'))

        # mainwindow statusbar
        self.progressbar = QProgressBar()
        self.progressbar.setRange(0, 0)
        self.progressbar.setVisible(False)
        self.progressbar.setFixedHeight(15)
        self.progressbar.setFixedWidth(100)
        self.progressbar.setTextVisible(False)
        self.progressbar.setValue(30)
        self.statusbar = QStatusBar(self)
        self.statusbar.setAutoFillBackground(False)
        self.statusbar.addPermanentWidget(self.progressbar)
        self.statusbar.setObjectName("statusbar")
        self.setStatusBar(self.statusbar)

        self.main_tabs = QTabWidget(self)
        self.main_tabs.setMovable(False)
        self.main_tabs.setTabsClosable(True)
        self.main_tabs.setAutoFillBackground(True)
        self.main_tabs.tabCloseRequested.connect(self._on_close_tab)
        self.setCentralWidget(self.main_tabs)

        if self.dwarf_args.package is None:
            self.welcome_window = WelcomeDialog(self)
            self.welcome_window.setModal(True)
            self.welcome_window.onIsNewerVersion.connect(
                self._enable_update_menu)
            self.welcome_window.onUpdateComplete.connect(
                self._on_dwarf_updated)
            self.welcome_window.setWindowTitle(
                'Welcome to Dwarf - A debugger for reverse engineers, crackers and security analyst'
            )
            self.welcome_window.onSessionSelected.connect(self._start_session)
            self.welcome_window.onSessionRestore.connect(self._restore_session)
            # wait for welcome screen
            self.hide()
            self.welcome_window.show()
        else:
            if dwarf_args.package is not None:
                if dwarf_args.type is None:
                    # no device given check if package is local path
                    if os.path.exists(dwarf_args.package):
                        print('* Starting new LocalSession')
                        self._start_session('local')
                    else:
                        print('use -t to set sessiontype')
                        exit(0)
                else:
                    print('* Starting new Session')
                    self._start_session(dwarf_args.type)

    def _setup_main_menu(self):
        self.menu = self.menuBar()
        dwarf_menu = QMenu('Dwarf', self)
        theme = QMenu('Theme', dwarf_menu)
        theme.addAction('Black')
        theme.addAction('Dark')
        theme.addAction('Light')
        theme.triggered.connect(self._set_theme)
        dwarf_menu.addMenu(theme)
        dwarf_menu.addSeparator()
        if self._is_newer_dwarf:
            dwarf_menu.addAction('Update', self._update_dwarf)
        dwarf_menu.addAction('Close', self.session_manager.session.stop)
        self.menu.addMenu(dwarf_menu)

        session = self.session_manager.session
        if session is not None:
            session_menu = session.main_menu
            if isinstance(session_menu, list):
                for menu in session_menu:
                    self.menu.addMenu(menu)
            else:
                self.menu.addMenu(session_menu)

        self.view_menu = QMenu('View', self)
        subview_menu = QMenu('Subview', self.view_menu)
        subview_menu.addAction('Search',
                               lambda: self.show_main_tab('search'),
                               shortcut=QKeySequence(Qt.CTRL + Qt.Key_F3))
        subview_menu.addAction('Emulator',
                               lambda: self.show_main_tab('emulator'),
                               shortcut=QKeySequence(Qt.CTRL + Qt.Key_F2))
        subview_menu.addAction('Disassembly',
                               lambda: self.show_main_tab('disassembly'),
                               shortcut=QKeySequence(Qt.CTRL + Qt.Key_F5))
        self.view_menu.addMenu(subview_menu)
        self.view_menu.addSeparator()
        self.menu.addMenu(self.view_menu)

        if self.dwarf_args.debug_script:
            debug_menu = QMenu('Debug', self)
            debug_menu.addAction('Reload core', self._menu_reload_core)
            debug_menu.addAction('Debug dwarf js core',
                                 self._menu_debug_dwarf_js)
            self.menu.addMenu(debug_menu)

        about_menu = QMenu('About', self)
        about_menu.addAction('Dwarf on GitHub', self._menu_github)
        about_menu.addAction('Documention', self._menu_documentation)
        about_menu.addAction('Api', self._menu_api)
        about_menu.addAction('Slack', self._menu_slack)
        about_menu.addSeparator()
        about_menu.addAction('Info', self._show_about_dlg)
        self.menu.addMenu(about_menu)

    def _enable_update_menu(self):
        self._is_newer_dwarf = True

    def _update_dwarf(self):
        if self.welcome_window:
            self.welcome_window._update_dwarf()

    def _on_close_tab(self, index):
        tab_text = self.main_tabs.tabText(index)
        if tab_text:
            if tab_text.lower() in self.session_manager.session.non_closable:
                return
            try:
                self._ui_elems.remove(tab_text.lower())
            except ValueError:  # recheck ValueError: list.remove(x): x not in list
                pass
            self.main_tabs.removeTab(index)

    def _handle_tab_change(self):
        for index in range(self.main_tabs.count()):
            tab_name = self.main_tabs.tabText(index).lower().replace(' ', '-')
            if tab_name in self.session_manager.session.non_closable:
                self.main_tabs.tabBar().setTabButton(index, QTabBar.RightSide,
                                                     None)

                if tab_name in self._tab_order:
                    should_index = self._tab_order.index(tab_name)
                    if index != should_index:
                        self.main_tabs.tabBar().moveTab(index, should_index)

    def _on_dwarf_updated(self):
        self.onRestart.emit()

    def remove_tmp_dir(self):
        if os.path.exists('.tmp'):
            shutil.rmtree('.tmp', ignore_errors=True)

    def _set_theme(self, qaction):
        if qaction:
            self.set_theme(qaction.text())

    def _menu_reload_core(self):
        self.dwarf.load_script()

    def _menu_debug_dwarf_js(self):
        you_know_what_to_do = json.loads(
            self.dwarf._script.exports.debugdwarfjs())
        return you_know_what_to_do

    def show_main_tab(self, name):
        # elem doesnt exists? create it
        if name not in self._ui_elems:
            self._create_ui_elem(name)

        index = 0
        name = name.join(name.split()).lower()
        if name == 'memory':
            index = self.main_tabs.indexOf(self.memory_panel)
        elif name == 'ranges':
            index = self.main_tabs.indexOf(self.ranges_panel)
        elif name == 'search':
            index = self.main_tabs.indexOf(self.search_panel)
        elif name == 'modules':
            index = self.main_tabs.indexOf(self.modules_panel)
        elif name == 'disassembly':
            index = self.main_tabs.indexOf(self.asm_panel)
        elif name == 'trace':
            index = self.main_tabs.indexOf(self.trace_panel)
        elif name == 'data':
            index = self.main_tabs.indexOf(self.data_panel)
        elif name == 'emulator':
            index = self.main_tabs.indexOf(self.emulator_panel)
        elif name == 'java-trace':
            index = self.main_tabs.indexOf(self.java_trace_panel)
        elif name == 'jvm-inspector':
            index = self.main_tabs.indexOf(self.java_inspector_panel)
        elif name == 'jvm-debugger':
            index = self.main_tabs.indexOf(self.java_explorer_panel)
        elif name == 'smali':
            index = self.main_tabs.indexOf(self.smali_panel)

        self.main_tabs.setCurrentIndex(index)

    def jump_to_address(self, ptr, show_panel=True):
        if self.memory_panel is not None:
            if show_panel:
                self.show_main_tab('memory')
            self.memory_panel.read_memory(ptr)

    @pyqtSlot(name='mainMenuGitHub')
    def _menu_github(self):
        QDesktopServices.openUrl(QUrl('https://github.com/iGio90/Dwarf'))

    @pyqtSlot(name='mainMenuDocumentation')
    def _menu_api(self):
        QDesktopServices.openUrl(QUrl('https://igio90.github.io/Dwarf/'))

    @pyqtSlot(name='mainMenuApi')
    def _menu_documentation(self):
        QDesktopServices.openUrl(QUrl('https://igio90.github.io/Dwarf/api'))

    @pyqtSlot(name='mainMenuSlack')
    def _menu_slack(self):
        QDesktopServices.openUrl(
            QUrl('https://join.slack.com/t/resecret/shared_invite'
                 '/enQtMzc1NTg4MzE3NjA1LTlkNzYxNTIwYTc2ZTYyOWY1MT'
                 'Q1NzBiN2ZhYjQwYmY0ZmRhODQ0NDE3NmRmZjFiMmE1MDYwN'
                 'WJlNDVjZDcwNGE'))

    def _show_about_dlg(self):
        about_dlg = AboutDialog(self)
        about_dlg.show()

    def _create_ui_elem(self, elem):
        if not isinstance(elem, str):
            return

        if elem not in self._ui_elems:
            self._ui_elems.append(elem)

        if elem == 'watchers':
            from ui.panel_watchers import WatchersPanel
            self.watchers_dwidget = QDockWidget('Watchers', self)
            self.watchers_panel = WatchersPanel(self)
            # dont respond to dblclick mem cant be shown
            # self.watchers_panel.onItemDoubleClicked.connect(
            #    self._on_watcher_clicked)
            self.watchers_panel.onItemRemoved.connect(
                self._on_watcher_removeditem)
            self.watchers_panel.onItemAdded.connect(self._on_watcher_added)
            self.watchers_dwidget.setWidget(self.watchers_panel)
            self.watchers_dwidget.setObjectName('WatchersPanel')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.watchers_dwidget)
            self.view_menu.addAction(self.watchers_dwidget.toggleViewAction())
        elif elem == 'hooks':
            from ui.panel_hooks import HooksPanel
            self.hooks_dwiget = QDockWidget('Breakpoints', self)
            self.hooks_panel = HooksPanel(self)
            self.hooks_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.hooks_panel.onHookRemoved.connect(self._on_hook_removed)
            self.hooks_dwiget.setWidget(self.hooks_panel)
            self.hooks_dwiget.setObjectName('HooksPanel')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.hooks_dwiget)
            self.view_menu.addAction(self.hooks_dwiget.toggleViewAction())
        elif elem == 'bookmarks':
            from ui.panel_bookmarks import BookmarksPanel
            self.bookmarks_dwiget = QDockWidget('Boomarks', self)
            self.bookmarks_panel = BookmarksPanel(self)
            self.bookmarks_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.bookmarks_dwiget.setWidget(self.bookmarks_panel)
            self.bookmarks_dwiget.setObjectName('BookmarksPanel')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.bookmarks_dwiget)
            self.view_menu.addAction(self.bookmarks_dwiget.toggleViewAction())
        elif elem == 'registers':
            from ui.panel_context import ContextPanel
            self.registers_dock = QDockWidget('Context', self)
            self.context_panel = ContextPanel(self)
            self.registers_dock.setWidget(self.context_panel)
            self.registers_dock.setObjectName('ContextsPanel')
            self.addDockWidget(Qt.RightDockWidgetArea, self.registers_dock)
            self.view_menu.addAction(self.registers_dock.toggleViewAction())
        elif elem == 'memory':
            from ui.panel_memory import MemoryPanel
            self.memory_panel = MemoryPanel(self)
            self.memory_panel.onShowDisassembly.connect(
                self._disassemble_range)
            self.memory_panel.dataChanged.connect(self._on_memory_modified)
            self.memory_panel.statusChanged.connect(self.set_status_text)
            self.main_tabs.addTab(self.memory_panel, 'Memory')
        elif elem == 'jvm-debugger':
            from ui.panel_java_explorer import JavaExplorerPanel
            self.java_explorer_panel = JavaExplorerPanel(self)
            self.main_tabs.addTab(self.java_explorer_panel, 'JVM debugger')
            self.main_tabs.tabBar().moveTab(
                self.main_tabs.indexOf(self.java_explorer_panel), 1)
        elif elem == 'jvm-inspector':
            from ui.panel_java_inspector import JavaInspector
            self.java_inspector_panel = JavaInspector(self)
            self.main_tabs.addTab(self.java_inspector_panel, 'JVM inspector')
        elif elem == 'console':
            from ui.panel_console import ConsolePanel
            self.console_dock = QDockWidget('Console', self)
            self.console_panel = ConsolePanel(self)
            self.dwarf.onLogToConsole.connect(self._log_js_output)
            self.console_dock.setWidget(self.console_panel)
            self.console_dock.setObjectName('ConsolePanel')
            self.addDockWidget(Qt.BottomDockWidgetArea, self.console_dock)
            self.view_menu.addAction(self.console_dock.toggleViewAction())
        elif elem == 'backtrace':
            from ui.panel_backtrace import BacktracePanel
            self.backtrace_dock = QDockWidget('Backtrace', self)
            self.backtrace_panel = BacktracePanel(self)
            self.backtrace_dock.setWidget(self.backtrace_panel)
            self.backtrace_dock.setObjectName('BacktracePanel')
            self.backtrace_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.addDockWidget(Qt.RightDockWidgetArea, self.backtrace_dock)
            self.view_menu.addAction(self.backtrace_dock.toggleViewAction())
        elif elem == 'threads':
            from ui.panel_contexts_list import ContextsListPanel
            self.threads_dock = QDockWidget('Threads', self)
            self.contexts_list_panel = ContextsListPanel(self)
            self.dwarf.onThreadResumed.connect(
                self.contexts_list_panel.resume_tid)
            self.contexts_list_panel.onItemDoubleClicked.connect(
                self._manually_apply_context)
            self.threads_dock.setWidget(self.contexts_list_panel)
            self.threads_dock.setObjectName('ThreadPanel')
            self.addDockWidget(Qt.RightDockWidgetArea, self.threads_dock)
            self.view_menu.addAction(self.threads_dock.toggleViewAction())
        elif elem == 'modules':
            from ui.panel_modules import ModulesPanel
            self.modules_panel = ModulesPanel(self)
            self.modules_panel.onModuleSelected.connect(
                self._on_module_dblclicked)
            self.modules_panel.onModuleFuncSelected.connect(
                self._on_modulefunc_dblclicked)
            self.modules_panel.onAddHook.connect(self._on_addmodule_hook)
            self.modules_panel.onDumpBinary.connect(self._on_dumpmodule)
            self.main_tabs.addTab(self.modules_panel, 'Modules')
        elif elem == 'ranges':
            from ui.panel_ranges import RangesPanel
            self.ranges_panel = RangesPanel(self)
            self.ranges_panel.onItemDoubleClicked.connect(
                self._range_dblclicked)
            self.ranges_panel.onDumpBinary.connect(self._on_dumpmodule)
            # connect to watcherpanel func
            self.ranges_panel.onAddWatcher.connect(
                self.watchers_panel.do_addwatcher_dlg)
            self.main_tabs.addTab(self.ranges_panel, 'Ranges')
        elif elem == 'search':
            from ui.panel_search import SearchPanel
            self.search_panel = SearchPanel(self)
            self.search_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.main_tabs.addTab(self.search_panel, 'Search')
        elif elem == 'data':
            from ui.panel_data import DataPanel
            self.data_panel = DataPanel(self)
            self.main_tabs.addTab(self.data_panel, 'Data')
        elif elem == 'trace':
            from ui.panel_trace import TracePanel
            self.trace_panel = TracePanel(self)
            self.main_tabs.addTab(self.trace_panel, 'Trace')
        elif elem == 'disassembly':
            from ui.widgets.disasm_view import DisassemblyView
            self.asm_panel = DisassemblyView(self)
            self.asm_panel.onShowMemoryRequest.connect(self._on_disasm_showmem)
            self.main_tabs.addTab(self.asm_panel, 'Disassembly')
        elif elem == 'emulator':
            from ui.panel_emulator import EmulatorPanel
            self.emulator_panel = EmulatorPanel(self)
            self.main_tabs.addTab(self.emulator_panel, 'Emulator')
        elif elem == 'java-trace':
            from ui.panel_java_trace import JavaTracePanel
            self.java_trace_panel = JavaTracePanel(self)
            self.main_tabs.addTab(self.java_trace_panel, 'JVM tracer')
        elif elem == 'smali':
            from ui.panel_smali import SmaliPanel
            self.smali_panel = SmaliPanel()
            self.main_tabs.addTab(self.smali_panel, 'Smali')
        else:
            print('no handler for elem: ' + elem)

        # make tabs unclosable and sort
        self._handle_tab_change()

        # TODO: remove add @2x
        for item in self.findChildren(QDockWidget):
            if item:
                if 'darwin' in sys.platform:
                    item.setStyleSheet(
                        'QDockWidget::title { padding-left:-30px; } QDockWidget::close-button, QDockWidget::float-button  { width: 10px; height:10px }'
                    )

    def set_theme(self, theme):
        if theme:
            theme = theme.replace(os.pardir, '').replace('.', '')
            theme = theme.join(theme.split()).lower()
            theme_style = 'assets/' + theme + '_style.qss'
            if not os.path.exists(utils.resource_path(theme_style)):
                return

            self.prefs.put('dwarf_ui_theme', theme)

            try:
                _app = QApplication.instance()
                with open(theme_style) as stylesheet:
                    _app.setStyleSheet(_app.styleSheet() + '\n' +
                                       stylesheet.read())
            except Exception as e:
                pass
                # err = self.dwarf.spawn(dwarf_args.package, dwarf_args.script)

    def set_status_text(self, txt):
        self.statusbar.showMessage(txt)

    # ************************************************************************
    # **************************** Properties ********************************
    # ************************************************************************
    @property
    def disassembly(self):
        return self.asm_panel

    @property
    def backtrace(self):
        return self.backtrace_panel

    @property
    def console(self):
        return self.console_panel

    @property
    def context(self):
        return self.context_panel

    @property
    def threads(self):
        return self.contexts_list_panel

    @property
    def emulator(self):
        return self.emulator_panel

    @property
    def ftrace(self):
        return self.ftrace_panel

    @property
    def hooks(self):
        return self.hooks_panel

    @property
    def java_inspector(self):
        return self.java_inspector_panel

    @property
    def java_explorer(self):
        return self.java_explorer_panel

    @property
    def memory(self):
        return self.memory_panel

    @property
    def modules(self):
        return self.memory_panel

    @property
    def ranges(self):
        return self.ranges_panel

    @property
    def trace(self):
        return self.trace_panel

    @property
    def watchers(self):
        return self.watchers_panel

    @property
    def dwarf(self):
        if self.session_manager.session is not None:
            return self.session_manager.session.dwarf
        else:
            return None

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    # session handlers
    def _start_session(self, session_type, session_data=None):
        if self.welcome_window is not None:
            self.welcome_window.close()
        self.session_manager.create_session(session_type,
                                            session_data=session_data)

    def _restore_session(self, session_data):
        if 'session' in session_data:
            session_type = session_data['session']
            self._start_session(session_type, session_data=session_data)

    def session_created(self):
        # session init done create ui for it
        session = self.session_manager.session
        self._setup_main_menu()
        for ui_elem in session.session_ui_sections:
            ui_elem = ui_elem.join(ui_elem.split()).lower()
            self._create_ui_elem(ui_elem)

        self.dwarf.onAttached.connect(self._on_attached)
        self.dwarf.onScriptLoaded.connect(self._on_script_loaded)

        # hookup
        self.dwarf.onSetRanges.connect(self._on_setranges)
        self.dwarf.onSetModules.connect(self._on_setmodules)

        self.dwarf.onAddNativeHook.connect(self._on_add_hook)
        self.dwarf.onApplyContext.connect(self._apply_context)
        self.dwarf.onThreadResumed.connect(self.on_tid_resumed)

        self.dwarf.onTraceData.connect(self._on_tracer_data)
        self.dwarf.onSetData.connect(self._on_set_data)

        self.session_manager.start_session(self.dwarf_args)
        q_settings = QSettings("dwarf_window_pos.ini", QSettings.IniFormat)
        ui_state = q_settings.value('dwarf_ui_state')
        if ui_state:
            self.restoreGeometry(ui_state)
        window_state = q_settings.value('dwarf_ui_window', self.saveState())
        if window_state:
            self.restoreState(window_state)

        self.showMaximized()

    def session_stopped(self):
        self.remove_tmp_dir()
        self.menu.clear()

        self.main_tabs.clear()

        # actually we need to kill this. needs a refactor
        if self.java_trace_panel is not None:
            self.java_trace_panel = None

        for elem in self._ui_elems:
            if elem == 'watchers':
                self.watchers_panel.clear_list()
                self.watchers_panel.close()
                self.watchers_panel = None
                self.removeDockWidget(self.watchers_dwidget)
                self.watchers_dwidget = None
            elif elem == 'hooks':
                self.hooks_panel.close()
                self.hooks_panel = None
                self.removeDockWidget(self.hooks_dwiget)
                self.hooks_dwiget = None
            elif elem == 'registers':
                self.context_panel.close()
                self.context_panel = None
                self.removeDockWidget(self.registers_dock)
                self.registers_dock = None
            elif elem == 'memory':
                self.memory_panel.close()
                self.memory_panel = None
                self.main_tabs.removeTab(0)
                # self.main_tabs
            elif elem == 'jvm-debugger':
                self.java_explorer_panel.close()
                self.java_explorer_panel = None
                self.removeDockWidget(self.watchers_dwidget)
            elif elem == 'console':
                self.console_panel.close()
                self.console_panel = None
                self.removeDockWidget(self.console_dock)
                self.console_dock = None
            elif elem == 'backtrace':
                self.backtrace_panel.close()
                self.backtrace_panel = None
                self.removeDockWidget(self.backtrace_dock)
            elif elem == 'threads':
                self.contexts_list_panel.close()
                self.contexts_list_panel = None
                self.removeDockWidget(self.threads_dock)
                self.threads_dock = None
            elif elem == 'bookmarks':
                self.bookmarks_panel.close()
                self.bookmarks_panel = None
                self.removeDockWidget(self.bookmarks_dwiget)
                self.bookmarks_dwiget = None

    def session_closed(self):
        self._ui_elems = []
        self.hide()
        if self.welcome_window is not None:
            self.welcome_window.exec()

        # close if it was a commandline session
        if self.welcome_window is None:
            if self.dwarf_args.package:
                self.close()

    # ui handler
    def closeEvent(self, event):
        """ Window closed
            save stuff or whatever at exit

            detaches dwarf
        """
        # save windowstuff
        q_settings = QSettings("dwarf_window_pos.ini", QSettings.IniFormat)
        q_settings.setValue('dwarf_ui_state', self.saveGeometry())
        q_settings.setValue('dwarf_ui_window', self.saveState())

        if self.dwarf:
            self.dwarf.detach()
        super().closeEvent(event)

    def _on_watcher_clicked(self, ptr):
        """ Address in Watcher/Hookpanel was clicked
            show Memory
        """
        if '.' in ptr:  # java_hook
            file_path = ptr.replace('.', os.path.sep)
            if os.path.exists('.tmp/smali/' + file_path + '.smali'):
                if self.smali_panel is None:
                    self._create_ui_elem('smali')
                self.smali_panel.set_file('.tmp/smali/' + file_path + '.smali')
                self.show_main_tab('smali')
        else:
            self.memory_panel.read_memory(ptr=ptr)
            self.show_main_tab('memory')

    def _on_disasm_showmem(self, ptr, length):
        """ Address in Disasm was clicked
            adds temphighlight for bytes from current instruction
        """
        self.memory_panel.read_memory(ptr)
        self.memory_panel.add_highlight(
            HighLight('attention', utils.parse_ptr(ptr), length))
        self.show_main_tab('memory')

    def _on_watcher_added(self, ptr):
        """ Watcher Entry was added
        """
        try:
            # set highlight
            self.memory_panel.add_highlight(
                HighLight('watcher', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_watcher_removeditem(self, ptr):
        """ Watcher Entry was removed
            remove highlight too
        """
        self.memory_panel.remove_highlight(ptr)

    def _on_module_dblclicked(self, data):
        """ Module in ModulePanel was doubleclicked
        """
        addr, size = data
        addr = utils.parse_ptr(addr)
        size = int(size, 10)
        self.memory_panel.read_memory(ptr=addr, length=size)
        self.show_main_tab('Memory')

    def _on_modulefunc_dblclicked(self, ptr):
        """ Function in ModulePanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.memory_panel.read_memory(ptr=ptr)
        self.show_main_tab('Memory')

    def _on_dumpmodule(self, data):
        """ DumpBinary MenuItem in ModulePanel was selected
        """
        ptr, size = data
        ptr = utils.parse_ptr(ptr)
        size = int(size, 10)
        self.dwarf.dump_memory(ptr=ptr, length=size)

    def _disassemble_range(self, mem_range):
        """ Disassemble MenuItem in Hexview was selected
        """
        if mem_range:
            if self.asm_panel is None:
                self._create_ui_elem('disassembly')

            if mem_range:
                self.asm_panel.disassemble(mem_range)
                self.show_main_tab('disassembly')

    def _range_dblclicked(self, ptr):
        """ Range in RangesPanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.memory_panel.read_memory(ptr=ptr)
        self.show_main_tab('Memory')

    # dwarf handlers
    def _log_js_output(self, output):
        if self.console_panel is not None:
            self.console_panel.get_js_console().log(output)

    def _on_setranges(self, ranges):
        """ Dwarf wants to set Ranges
            only hooked up to switch tab or create ui
            its connected in panel after creation
        """
        if self.ranges_panel is None:
            self.show_main_tab('ranges')
            # forward only now to panel it connects after creation
            self.ranges_panel.set_ranges(ranges)

    def _on_setmodules(self, modules):
        """ Dwarf wants to set Modules
            only hooked up to switch tab or create ui
            its connected in panel after creation
        """
        if self.modules_panel is None:
            self._create_ui_elem('modules')
            self.modules_panel.set_modules(modules)

        if self.modules_panel is not None:
            self.show_main_tab('modules')

    def _manually_apply_context(self, context):
        """
        perform additional operation if the context has been manually applied from the context list
        """
        self._apply_context(context, manual=True)

    def _apply_context(self, context, manual=False):
        # update current context tid
        # this should be on top as any further api from js needs to be executed on that thread
        is_initial_hook = context['reason'] >= 0
        if manual or (self.dwarf.context_tid and not is_initial_hook):
            self.dwarf.context_tid = context['tid']

        if 'context' in context:
            if not manual:
                self.threads.add_context(context)

            is_java = context['is_java']
            if is_java:
                if self.java_explorer_panel is None:
                    self._create_ui_elem('jvm-debugger')
                self.context_panel.set_context(context['ptr'], 1,
                                               context['context'])
                self.java_explorer_panel.set_handle_arg(-1)
                self.show_main_tab('jvm-debugger')
            else:
                self.context_panel.set_context(context['ptr'], 0,
                                               context['context'])

                if 'pc' in context['context']:
                    if not 'disassembly' in self._ui_elems:
                        from lib.range import Range
                        _range = Range(Range.SOURCE_TARGET, self.dwarf)
                        _range.init_with_address(
                            int(context['context']['pc']['value'], 16))

                        self._disassemble_range(_range)

        if 'backtrace' in context:
            self.backtrace_panel.set_backtrace(context['backtrace'])

    def _on_add_hook(self, hook):
        try:
            # set highlight
            ptr = hook.get_ptr()
            ptr = utils.parse_ptr(ptr)
            self.memory_panel.add_highlight(
                HighLight('hook', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_hook_removed(self, ptr):
        ptr = utils.parse_ptr(ptr)
        self.memory_panel.remove_highlight(ptr)

    def _on_addmodule_hook(self, data):
        ptr, name = data
        self.dwarf.hook_native(ptr, own_input=name)

    def on_tid_resumed(self, tid):
        if self.dwarf:
            if self.dwarf.context_tid == tid:
                # clear backtrace
                if 'backtrace' in self._ui_elems:
                    if self.backtrace_panel is not None:
                        self.backtrace_panel.clear()

                # remove thread
                if 'threads' in self._ui_elems:
                    if self.contexts_list_panel is not None:
                        self.contexts_list_panel.resume_tid(tid)

                # clear registers
                if 'registers' in self._ui_elems:
                    if self.context_panel is not None:
                        self.context_panel.clear()

                # clear jvm explorer
                if 'jvm-debugger' in self._ui_elems:
                    if self.java_explorer_panel is not None:
                        self.java_explorer_panel.clear_panel()

                # invalidate dwarf context tid
                self.dwarf.context_tid = 0

    def _on_tracer_data(self, data):
        if not data:
            return

        if self.trace_panel is None:
            self._create_ui_elem('trace')

        if self.trace_panel is not None:
            self.show_main_tab('Trace')
            self.trace_panel.start()

            trace_events_parts = data[1].split(',')
            while trace_events_parts:
                trace_event = TraceEvent(trace_events_parts.pop(0),
                                         trace_events_parts.pop(0),
                                         trace_events_parts.pop(0),
                                         trace_events_parts.pop(0))
                self.trace_panel.event_queue.append(trace_event)

    def _on_set_data(self, data):
        if not isinstance(data, list):
            return

        if self.data_panel is None:
            self._create_ui_elem('data')

        if self.data_panel is not None:
            self.show_main_tab('Data')
            self.data_panel.append_data(data[0], data[1], data[2])

    def show_progress(self, text):
        self.progressbar.setVisible(True)
        self.set_status_text(text)

    def hide_progress(self):
        self.progressbar.setVisible(False)
        self.set_status_text('')

    def _on_attached(self, data):
        self.setWindowTitle('Dwarf - Attached to %s (%s)' % (data[1], data[0]))

    def _on_script_loaded(self):
        # restore the loaded session if any
        self.session_manager.restore_session()

    def _on_memory_modified(self, pos, length):
        data_pos = self.memory_panel.base + pos
        data = self.memory_panel.data[pos:pos + length]
        data = [data[0]]  # todo: strange js part

        if self.dwarf.dwarf_api('writeBytes', [data_pos, data]):
            pass
        else:
            utils.show_message_box('Failed to write Memory')

    def on_add_bookmark(self, ptr):
        """
        provide ptr as int
        """
        if self.bookmarks_panel is not None:
            self.bookmarks_panel._create_bookmark(ptr=hex(ptr))
Exemple #31
0
class TableWindow(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.initUI()

    def initUI(self):
        self.setWindowTitle('R-Peaks')
        self.setGeometry(300, 400, 300, 500)

        self.tab = QTabWidget()

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

    def populateFromDict(self, data):
        self.tabs = []  # append tabs to this list
        self.tables = []  # append tables to this list
        self.tab.clear()  # clear any old data/widgets

        eButtons = []  # store export buttons in here

        for ev in data.keys():
            self.tabs.append(QWidget())  # create a tab for the event
            self.tab.addTab(
                self.tabs[-1],
                ev)  # add the tab to the tab widget with the event name

            self.tabs[-1].layout = QVBoxLayout(
            )  # create the layout for the tab

            m, _ = data[ev].shape  # get number of rows

            self.tables.append(self.createTable(
                m,
                2))  # create and append the m-by-2 table for time and voltage
            self.tables[-1].setHorizontalHeaderLabels(
                'Time [s];Voltage [mv]'.split(';'))  # set appropriate headers

            # set the data in the table
            for i in range(m):
                self.tables[-1].setItem(i, 0,
                                        QTableWidgetItem(str(data[ev][i, 0])))
                self.tables[-1].setItem(i, 1,
                                        QTableWidgetItem(str(data[ev][i, 1])))

            # create the export button
            eButtons.append(QPushButton('Export', self))
            eButtons[-1].clicked.connect(lambda: self.exportData(data[ev]))

            self.tables[-1].move(0, 0)
            self.tabs[-1].layout.addWidget(self.tables[-1])
            self.tabs[-1].layout.addWidget(eButtons[-1])
            self.tabs[-1].setLayout(self.tabs[-1].layout)

        # TODO add export button and function for R-peak data

    def exportData(self, data):
        """
        Export tabulated data to the chosen file.  If a csv file is chosen, delimiter is comma, if .txt, a space
        """
        saveFile, _ = QFileDialog.getSaveFileName(
            self,
            "Export Data",
            "",
            "CSV (*.csv);;TXT (*.txt);;All Files (*.*)",
            options=QFileDialog.Options())

        if saveFile:
            if '.csv' in saveFile:
                savetxt(saveFile,
                        data,
                        delimiter=',',
                        fmt='%.8f',
                        header='Time [s], Voltage[mv]')
            else:
                savetxt(saveFile,
                        data,
                        delimiter=' ',
                        fmt='%.8f',
                        header='Time [s], Voltage[mv]')

    def createTable(self, m, n):
        table = QTableWidget()
        table.setRowCount(m)
        table.setColumnCount(n)

        # self.table.setItem(0,0, QTableWidgetItem("Cell (1,1)"))

        # table selection change
        table.doubleClicked.connect(self.on_dclick)

        return table

    @pyqtSlot()
    def on_dclick(self):
        # do stuff
        pass
class LanguagesManagerWidget(QDialog):

    def __init__(self, parent):
        QDialog.__init__(self, parent, Qt.Dialog)
        self.setWindowTitle(self.tr("Language Manager"))
        self.resize(700, 500)

        vbox = QVBoxLayout(self)
        self._tabs = QTabWidget()
        vbox.addWidget(self._tabs)
        # Footer
        hbox = QHBoxLayout()
        btn_close = QPushButton(self.tr('Close'))
        btnReload = QPushButton(self.tr("Reload"))
        hbox.addWidget(btn_close)
        hbox.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding))
        hbox.addWidget(btnReload)
        vbox.addLayout(hbox)
        self.overlay = ui_tools.Overlay(self)
        self.overlay.show()

        self._languages = []
        self._loading = True
        self.downloadItems = []

        #Load Themes with Thread
        btnReload.clicked.connect(self._reload_languages)
        self._thread = ui_tools.ThreadExecution(self.execute_thread)
        self._thread.finished.connect(self.load_languages_data)
        btn_close.clicked.connect(self.close)
        self._reload_languages()

    def _reload_languages(self):
        self.overlay.show()
        self._loading = True
        self._thread.execute = self.execute_thread
        self._thread.start()

    def load_languages_data(self):
        if self._loading:
            self._tabs.clear()
            self._languageWidget = LanguageWidget(self, self._languages)
            self._tabs.addTab(self._languageWidget,
                self.tr("Languages"))
            self._loading = False
        self.overlay.hide()
        self._thread.wait()

    def download_language(self, language):
        self.overlay.show()
        self.downloadItems = language
        self._thread.execute = self._download_language_thread
        self._thread.start()

    def resizeEvent(self, event):
        self.overlay.resize(event.size())
        event.accept()

    def execute_thread(self):
        try:
            descriptor_languages = urlopen(resources.LANGUAGES_URL)
            languages = json_manager.parse(descriptor_languages)
            languages = [[name, languages[name]] for name in languages]
            local_languages = self.get_local_languages()
            languages = [languages[i] for i in range(len(languages)) if
                os.path.basename(languages[i][1]) not in local_languages]
            self._languages = languages
        except URLError:
            self._languages = []

    def get_local_languages(self):
        if not file_manager.folder_exists(resources.LANGS_DOWNLOAD):
            file_manager.create_tree_folders(resources.LANGS_DOWNLOAD)
        languages = os.listdir(resources.LANGS_DOWNLOAD) + \
            os.listdir(resources.LANGS)
        languages = [s for s in languages if s.lower().endswith('.qm')]
        return languages

    def _download_language_thread(self):
        for d in self.downloadItems:
            self.download(d[1], resources.LANGS_DOWNLOAD)

    def download(self, url, folder):
        fileName = os.path.join(folder, os.path.basename(url))
        try:
            content = urlopen(url)
            with open(fileName, 'wb') as f:
                f.write(content.read())
        except URLError:
            return
class IPStationView(QWidget):

    # class to ease the process of displaying the obspy inventory list
    savefile = None

    inventory_changed = pyqtSignal(obspy.core.inventory.inventory.Inventory)
    inventory_cleared = pyqtSignal()

    def __init__(self, parent):
        super().__init__()

        self.__parent = parent
        self.buildUI()

        # self.station_TabWidget.setTabsClosable(True)
        # self.station_TabWidget.tabCloseRequested.connect(self.closeMyTab)
        self.show()

    def buildUI(self):

        self.buildIcons()

        self.station_TabWidget = QTabWidget()
        # self.station_TabWidget.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)

        self.clearButton = QPushButton(' Clear Stations')
        self.clearButton.setIcon(self.clearIcon)
        self.saveButton = QPushButton(' Save Stations')
        self.saveButton.setIcon(self.saveIcon)
        self.saveAsButton = QPushButton(' Save Stations As...')
        self.saveAsButton.setIcon(self.saveAsIcon)

        self.loadButton = QPushButton(' Load...')
        self.loadButton.setIcon(self.openIcon)

        self.reconcileButton = QPushButton(' Reconcile Stations')
        self.reconcileButton.setToolTip(
            self.tr('Attempt to download stations for current waveforms'))

        savebuttonGroup = QWidget()
        savebuttonLayout = QGridLayout()

        savebuttonGroup.setLayout(savebuttonLayout)
        savebuttonLayout.addWidget(self.loadButton, 1, 0)
        savebuttonLayout.addWidget(self.clearButton, 1, 1)
        savebuttonLayout.addWidget(self.saveButton, 0, 0)
        savebuttonLayout.addWidget(self.saveAsButton, 0, 1)
        savebuttonLayout.addWidget(self.reconcileButton, 2, 1)
        savebuttonLayout.setSizeConstraint(QLayout.SetFixedSize)

        verticalLayout = QVBoxLayout()
        verticalLayout.addWidget(savebuttonGroup)
        verticalLayout.addStretch()

        mainLayout = QHBoxLayout()
        mainLayout.addWidget(self.station_TabWidget)
        mainLayout.addLayout(verticalLayout)

        # go ahead and make an instance of the matchDialog for later use
        self.matchDialog = IPStationMatchDialog.IPStationMatchDialog()

        self.setLayout(mainLayout)
        self.connectSignalsandSlots()

    def buildIcons(self):
        self.clearIcon = QIcon.fromTheme("edit-clear")
        self.openIcon = QIcon.fromTheme("document-open")
        self.saveIcon = QIcon.fromTheme("document-save")
        self.saveAsIcon = QIcon.fromTheme("document-save-as")

    def connectSignalsandSlots(self):
        self.clearButton.clicked.connect(self.clear)
        self.saveButton.clicked.connect(self.saveStations)
        self.saveAsButton.clicked.connect(self.saveStationsAs)
        self.loadButton.clicked.connect(self.loadStations)
        self.reconcileButton.clicked.connect(self.reconcileStations)

    def setInventory(self, inventory):
        if inventory is None:
            self.clear()
            return

        tab_names = []

        self.station_TabWidget.clear()

        for network in inventory.networks:
            for station in network.stations:
                print("# chans: {}".format(len(station.channels)))
                names = []
                if len(station.channels) > 0:
                    for channel in station.channels:
                        name = network.code + '.' + station.code + '.' + channel.location_code + '.' + channel.code

                        if name not in tab_names:
                            # Ok, need at least one, so lets assemble the interesting station info for display
                            newStationEdit = QTextEdit()
                            contents = station.get_contents()
                            ret = (
                                "<b>Network:</b> {network_code}<br/>"
                                "<b>Station:</b> {station_name}<br/>"
                                "<b>Station Code:</b> {station_code}<br/>"
                                "<b>Location Code:</b> {location_code}<br/>"
                                "<b>Channel Count:</b> {selected}/{total} (Selected/Total)<br/>"
                                "<b>Available Dates:</b> {start_date} - {end_date}<br/>"
                                "<b>Access:</b> {restricted} {alternate_code}{historical_code}<br/>"
                                "<b>Latitude:</b> {lat:.8f}<br/>"
                                "<b>Longitude:</b> {lng:.8f}<br/>"
                                "<b>Elevation:</b> {elevation:.2f} m<br/>")
                            ret = ret.format(
                                network_code=network.code,
                                station_name=contents["stations"][0],
                                station_code=station.code,
                                location_code=channel.location_code,
                                selected=station.selected_number_of_channels,
                                total=station.total_number_of_channels,
                                start_date=str(station.start_date),
                                end_date=str(station.end_date)
                                if station.end_date else "",
                                restricted=station.restricted_status,
                                lat=station.latitude,
                                lng=station.longitude,
                                elevation=station.elevation,
                                alternate_code="Alternate Code: %s " %
                                station.alternate_code
                                if station.alternate_code else "",
                                historical_code="Historical Code: %s " %
                                station.historical_code
                                if station.historical_code else "")

                            newStationEdit.setHtml(ret)
                            self.station_TabWidget.addTab(newStationEdit, name)
                else:
                    name = network.code + '.' + station.code

                    print("hi mom")
                    if name not in tab_names:
                        # Ok, need at least one, so lets assemble the interesting station info for display
                        newStationEdit = QTextEdit()
                        contents = station.get_contents()
                        ret = (
                            "<b>Network Code:</b> {network_code} <br/>"
                            "<b>Station Code:</b> {station_code} <br/>"
                            "<b>Location Code:</b> {location_code} </br>"
                            "<b>Channel Count:</b> {selected}/{total} (Selected/Total)<br/>"
                            "<Available Dates:</b> {start_date} - {end_date}<br/>"
                            "<b>Access:</b> {restricted} {alternate_code}{historical_code}<br/>"
                            "<b>Latitude:</b> {lat:.8f}<br/>"
                            "<b>Longitude:</b> {lng:.8f}<br/>"
                            "<b>Elevation:</b> {elevation:.2f} m<br/>")
                        ret = ret.format(
                            network_code=network.code,
                            station_name=contents["stations"][0],
                            station_code=station.code,
                            location_code='',
                            selected=station.selected_number_of_channels,
                            total=station.total_number_of_channels,
                            start_date=str(station.start_date),
                            end_date=str(station.end_date)
                            if station.end_date else "",
                            restricted=station.restricted_status,
                            lat=station.latitude,
                            lng=station.longitude,
                            elevation=station.elevation,
                            alternate_code="Alternate Code: %s " %
                            station.alternate_code
                            if station.alternate_code else "",
                            historical_code="Historical Code: %s " %
                            station.historical_code
                            if station.historical_code else "")

                        newStationEdit.setHtml(ret)
                        self.station_TabWidget.addTab(
                            newStationEdit, network.code + '.' + station.code)

        self.inventory_changed.emit(inventory)
        return

    def getStationCount(self):
        cnt = 0
        inventory = self.__parent.get_inventory()
        for network in inventory.networks:
            for station in network.stations:
                cnt += 1
        return cnt

    @QtCore.pyqtSlot(int)
    def closeMyTab(self, idx):
        self.station_TabWidget.removeTab(idx)

    def clear(self):
        for i in range(self.station_TabWidget.count()):
            self.station_TabWidget.removeTab(0)

        self.__parent.set_inventory(None)

        # now signal to the application that the inventory needs to be cleared
        self.inventory_cleared.emit()

    def saveStations(self):
        inventory = self.__parent.get_inventory()
        if inventory is None:
            self.errorPopup('Oops... There are no stations to save')
            return
        # if there is no current filename, prompt for one...
        # TODO: if there is an open project, default to that
        if self.savefile is None:
            self.saveStationsAs()
        else:
            inventory.write(self.savefile[0],
                            format='stationxml',
                            validate=True)
            path = os.path.dirname(self.savefile[0])
            self.__parent.settings.setValue("last_stationfile_directory", path)

    def saveStationsAs(self):
        inventory = self.__parent.get_inventory()
        if inventory is None:
            self.errorPopup('Oops... There are no stations to save')
            return

        if self.__parent.get_project() is None:
            # force a new filename...
            previousDirectory = self.__parent.settings.value(
                "last_stationfile_directory", QDir.homePath())
        else:
            # There is an open project, so make the default save location correspond to what the project wants
            previousDirectory = str(
                self.__parent.get_project().get_stationsPath())

        self.savefile = QFileDialog.getSaveFileName(self,
                                                    'Save StationXML File...',
                                                    previousDirectory)

        if self.savefile[0]:
            self.__parent._inv.write(self.savefile[0],
                                     format='stationxml',
                                     validate=True)
            path = os.path.dirname(self.savefile[0])
            self.__parent.settings.setValue("last_stationfile_directory", path)

    def loadStations(self):

        if self.__parent.get_project() is None:
            # force a new filename...
            previousDirectory = self.__parent.settings.value(
                "last_stationfile_directory", QDir.homePath())
        else:
            # There is an open project, so make the default save location correspond to what the project wants
            previousDirectory = str(
                self.__parent.get_project().get_stationsPath())

        self.__openfile = QFileDialog.getOpenFileName(self, 'Open File',
                                                      previousDirectory)

        if self.__openfile[0]:
            try:
                newinventory = read_inventory(self.__openfile[0],
                                              format='stationxml')
            except Exception:
                self.errorPopup("\nThis doesn't seem to be a valid XML file")
                return

            if self.__parent._inv is not None:
                self.__parent._inv += newinventory
            else:
                self.__parent._inv = newinventory

            self.setInventory(self.__parent._inv)

    def get_current_center(self):
        # this method will calculate the center of the current inventory and will return a [lat,lon]

        # TODO: This is not really setup right now to handle the (very rare) case where an array straddles the
        # international date line
        inventory = self.__parent.get_inventory()

        lat, lon, ele, cnt = 0, 0, 0, 0

        for network in inventory:
            for station in network:
                lat += station.latitude
                lon += station.longitude
                ele += station.elevation
                cnt += 1

        return [lat / cnt, lon / cnt, ele / cnt]

    def reconcileStations(self):
        needed_stations = []
        loaded_stations = []
        trace_stations = []

        streams = self.__parent.get_streams()
        inventory = self.__parent.get_inventory()

        if streams is None:
            return  # Nothing to reconcile

        # populate a list of all the stations in the current stream
        for trace in streams:
            trace_split = trace.id.split('.')
            name = ''
            if trace_split[2] == '':
                name = trace_split[0] + '.' + trace_split[1]
            else:
                # There is a location code, so deal with it
                name = trace_split[0] + '.' + trace_split[
                    1] + '.' + trace_split[2]

            if name not in trace_stations:
                trace_stations.append(name)

        if inventory is None:
            # No inventory loaded, so we need to get everything
            needed_stations = trace_stations
        else:
            # we already have inventory loaded, so we need to get the stations for waveforms that need it
            # First find all the loaded stations
            for network in inventory.networks:
                for station in network.stations:
                    name = ''
                    if len(station.channels) > 0:
                        for channel in station.channels:
                            name = network.code + '.' + station.code + '.' + channel.location_code
                    else:
                        name = network.code + '.' + station.code

                    if name not in loaded_stations:
                        loaded_stations.append(name)

            # now find all the stations that ARENT already loaded
            for sta in trace_stations:
                if sta not in loaded_stations:
                    needed_stations.append(sta)
        print("needed = {}".format(needed_stations))
        if needed_stations is not None:
            if self.matchDialog.exec_(
                    needed_stations,
                (self.__parent.get_earliest_start_time(),
                 self.__parent.get_earliest_start_time())):
                new_inventory = self.matchDialog.getInventory()
                if new_inventory is not None:
                    if inventory is None:
                        inventory = new_inventory
                    else:
                        inventory += new_inventory
                    self.__parent.set_inventory(inventory)
                    self.setInventory(inventory)

    def errorPopup(self, message):
        msgBox = QMessageBox()
        msgBox.setIcon(QMessageBox.Information)
        msgBox.setText(message)
        msgBox.setWindowTitle("Oops...")
        msgBox.exec_()
class dos_main(QWidget, tab_base):
    def __init__(self):
        QWidget.__init__(self)
        self.setMinimumSize(1000, 600)

        self.main_vbox = QVBoxLayout()

        self.setWindowIcon(icon_get("preferences-system"))

        self.setWindowTitle(
            _("Electrical parameter editor") + " (https://www.gpvdm.com)")

        toolbar = QToolBar()
        toolbar.setIconSize(QSize(48, 48))

        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        toolbar.addWidget(spacer)

        self.help = QAction(icon_get("help"), _("Help"), self)
        self.help.setStatusTip(_("Help"))
        self.help.triggered.connect(self.callback_help)
        toolbar.addAction(self.help)

        self.main_vbox.addWidget(toolbar)

        self.notebook = QTabWidget()

        css_apply(self, "tab_default.css")

        self.main_vbox.addWidget(self.notebook)
        self.setLayout(self.main_vbox)

        #self.notebook.setTabsClosable(True)
        #self.notebook.setMovable(True)
        #self.notebook.setTabBar(QHTabBar())
        #self.notebook.setTabPosition(QTabWidget.West)

        global_object_register("dos_update", self.update)
        self.update()

    def update(self):
        self.notebook.clear()
        fulle_sim = True
        sim_type = newton_solver_get_type()
        if sim_type == "newton_simple":
            fulle_sim = False
        epi = get_epi()
        for l in epi.layers:
            if fulle_sim == True:
                if l.dos_file.startswith("dos") == True:

                    name = "DoS of " + l.name
                    print(l.dos_file)
                    widget = tab_class(l.dos_file + ".inp")
                    self.notebook.addTab(widget, name)

                    for s in l.shapes:
                        if s.shape_dos != "none":
                            name = "DoS of " + s.name
                            widget = tab_class(s.shape_dos + ".inp")
                            self.notebook.addTab(widget, name)
                            #tab.append(s.shape_dos+".inp")
            else:
                name = "Electrical " + l.name

                widget = tab_class(l.shape_electrical + ".inp")

                self.notebook.addTab(widget, name)

    def help(self):
        help_window().help_set_help([
            "tab.png",
            "<big><b>Density of States</b></big>\nThis tab contains the electrical model parameters, such as mobility, tail slope energy, and band gap."
        ])

    def callback_help(self, widget):
        webbrowser.open('http://www.gpvdm.com/man/index.html')
Exemple #35
0
class MainWidget(QWidget):
    def __init__(self, parent=None):

        super().__init__()
        self.parent = parent

        self.template = None
        self.characters = []
        self.charIndex = 0

        self.init_UI()

    def connect_all(self):

        self.addChar.clicked.connect(self.add_character)
        self.removeChar.clicked.connect(self.remove_character)

        self.listWidget.currentItemChanged.connect(self.item_changed)
        self.listWidget.itemChanged.connect(self.item_text_changed)

        for r in range(self.tabs.count()):
            w = self.tabs.widget(r)
            w.connect_all()

    def disconnect_all(self):
        self.addChar.clicked.disconnect()
        self.removeChar.clicked.disconnect()

        self.listWidget.currentItemChanged.disconnect()
        self.listWidget.itemChanged.disconnect()

        for r in range(self.tabs.count()):
            w = self.tabs.widget(r)
            w.disconnect_all()

    def disable_stuff(self, disabled):

        self.addChar.setDisabled(disabled)
        self.removeChar.setDisabled(disabled)

    def init_UI(self):

        ##  Just to make life easier
        get_icon = Icons().get_icon

        ##  List of characters
        self.listWidget = QListWidget(self)

        ##  Add character button
        self.addChar = QPushButton(QIcon(get_icon("list-add")), "")
        self.addChar.setToolTip("Add a character")

        ##  Remove character button
        self.removeChar = QPushButton(QIcon(get_icon("list-remove")), "")
        self.removeChar.setToolTip("Remove selected character")

        ##  Bullshit to make qsplitter work
        self.list = MyListWidget(self)

        ##  Initial template
        self.template = self.parent.get_template()

        ##  Tab widget
        self.tabs = QTabWidget()

        self.load_tabs()

        ##  Tabs start out disabled
        self.tabs.setDisabled(True)

        ##  Connect it up
        self.connect_all()

        ##============= LAYOUTS

        splitter = QSplitter()
        splitter.addWidget(self.list)
        splitter.addWidget(self.tabs)

        splitter.setSizes([(WINDOW_WIDTH * .25), (WINDOW_WIDTH * .75)])

        ##  Set the layout
        hbox = QHBoxLayout()
        hbox.addWidget(splitter)
        self.setLayout(hbox)

    def load_tabs(self):
        if self.template != None:
            tabIndex = 0
            for tab in self.template.iter("tab"):
                widget = Tab(self, tabIndex, tab)
                tabIndex += 1
                self.tabs.addTab(widget, tab.find("title").text)

    def add_character(self):

        char = CharacterInfo(self.template)
        self.characters.append(char)

        li = QListWidgetItem(char.listName)
        li.setFlags(li.flags() | Qt.ItemIsEditable)
        self.listWidget.addItem(li)
        self.listWidget.setCurrentItem(li)

        ##  Enable the tabs widget
        self.tabs.setDisabled(False)

    def remove_character(self):

        idx = self.listWidget.currentRow()
        li = self.listWidget.item(idx)
        if li != None:
            ##  Kill whatever character at the index
            null = self.characters.pop(idx)

            ni = self.listWidget.item(idx + 1)
            pi = self.listWidget.item(idx - 1)

            #self.listWidget.currentItemChanged.disconnect()

            self.disconnect_all()

            sip.delete(li)

            self.listWidget.currentItemChanged.connect(self.item_changed)
            if ni != None:
                self.listWidget.setCurrentItem(ni)
                idx = self.listWidget.currentRow()
                self.charIndex = idx
                self.load_values(self.characters[idx])
            elif pi != None:
                self.listWidget.setCurrentItem(pi)
                idx = self.listWidget.currentRow()
                self.charIndex = idx
                self.load_values(self.characters[idx])
            else:
                self.charIndex = -1
                self.tabs.setDisabled(True)
                self.listWidget.currentItemChanged.connect(self.item_changed)
                self.clear_values()

            self.connect_all()

    def item_changed(self):

        li = self.listWidget.currentItem()
        if li != None:
            self.charIndex = self.listWidget.currentRow()
            self.load_values(self.characters[self.charIndex])

    def item_text_changed(self):

        li = self.listWidget.currentItem()
        if li != None:
            self.characters[self.charIndex].listName = li.text()

    def load_values(self, char):

        for t in range(len(self.characters[self.charIndex].tabs)):
            tab = self.tabs.widget(t)
            tab.load_values(char)

    def clear_values(self):
        for t in range(self.tabs.count()):
            w = self.tabs.widget(t)
            w.clear_values()
        #if len( self.characters ) > 0:
        #    for t in range( len(self.characters[ self.charIndex ].tabs )):
        #        tab = self.tabs.widget( t )
        #        tab.clear_values()

    def clear_all(self):

        ##  Kill characters and disable the tabs
        self.characters = []
        self.tabs.setDisabled(True)

        ##  Kill tabs
        self.tabs.clear()

        ##  Kill the list
        self.listWidget.clear()

    def load_list(self):

        for char in self.characters:

            li = QListWidgetItem(char.listName)
            li.setFlags(li.flags() | Qt.ItemIsEditable)
            self.listWidget.addItem(li)

    def reload(self, template, characters):
        self.disconnect_all()

        self.clear_all()
        self.template = template

        #c = characters[ 0 ]
        #print( c.tabs[ 0 ][0][ "name" ] )

        self.characters = characters
        self.load_list()
        self.load_tabs()

        self.connect_all()

        li = self.listWidget.item(0)
        if li != None:
            self.listWidget.setCurrentItem(li)
            self.tabs.setDisabled(False)
Exemple #36
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.settings = QSettings("Vial", "Vial")
        themes.set_theme(self.get_theme())

        self.current_device = None
        self.devices = []
        # create empty VIA definitions. Easier than setting it to none and handling a bunch of exceptions
        self.via_stack_json = {"definitions": {}}
        self.sideload_json = None
        self.sideload_vid = self.sideload_pid = -1

        self.combobox_devices = QComboBox()
        self.combobox_devices.currentIndexChanged.connect(
            self.on_device_selected)

        self.btn_refresh_devices = QToolButton()
        self.btn_refresh_devices.setToolButtonStyle(Qt.ToolButtonTextOnly)
        self.btn_refresh_devices.setText(tr("MainWindow", "Refresh"))
        self.btn_refresh_devices.clicked.connect(self.on_click_refresh)

        layout_combobox = QHBoxLayout()
        layout_combobox.addWidget(self.combobox_devices)
        layout_combobox.addWidget(self.btn_refresh_devices)

        self.layout_editor = LayoutEditor()
        self.keymap_editor = KeymapEditor(self.layout_editor)
        self.firmware_flasher = FirmwareFlasher(self)
        self.macro_recorder = MacroRecorder()
        self.matrix_tester = MatrixTest(self.layout_editor)

        self.editors = [(self.keymap_editor, "Keymap"),
                        (self.layout_editor, "Layout"),
                        (self.macro_recorder, "Macros"),
                        (self.matrix_tester, "Matrix tester"),
                        (self.firmware_flasher, "Firmware updater")]
        Unlocker.global_layout_editor = self.layout_editor

        self.tabs = QTabWidget()
        self.refresh_tabs()

        self.lbl_no_devices = QLabel(
            tr(
                "MainWindow",
                'No devices detected. Connect a Vial-compatible device and press '
                '"Refresh"\n'
                'or select "File" → "Download VIA definitions" in order to enable'
                ' support for VIA keyboards.'))
        self.lbl_no_devices.setAlignment(Qt.AlignCenter)

        layout = QVBoxLayout()
        layout.addLayout(layout_combobox)
        layout.addWidget(self.tabs)
        layout.addWidget(self.lbl_no_devices)
        layout.setAlignment(self.lbl_no_devices, Qt.AlignHCenter)
        w = QWidget()
        w.setLayout(layout)
        self.setCentralWidget(w)

        self.init_menu()

        # cache for via definition files
        self.cache_path = QStandardPaths.writableLocation(
            QStandardPaths.CacheLocation)
        if not os.path.exists(self.cache_path):
            os.makedirs(self.cache_path)
        # check if the via defitions already exist
        if os.path.isfile(os.path.join(self.cache_path, "via_keyboards.json")):
            with open(os.path.join(self.cache_path,
                                   "via_keyboards.json")) as vf:
                self.via_stack_json = json.load(vf)
                vf.close()

        # make sure initial state is valid
        self.on_click_refresh()

    def init_menu(self):
        layout_load_act = QAction(tr("MenuFile", "Load saved layout..."), self)
        layout_load_act.setShortcut("Ctrl+O")
        layout_load_act.triggered.connect(self.on_layout_load)

        layout_save_act = QAction(tr("MenuFile", "Save current layout..."),
                                  self)
        layout_save_act.setShortcut("Ctrl+S")
        layout_save_act.triggered.connect(self.on_layout_save)

        sideload_json_act = QAction(tr("MenuFile", "Sideload VIA JSON..."),
                                    self)
        sideload_json_act.triggered.connect(self.on_sideload_json)

        download_via_stack_act = QAction(
            tr("MenuFile", "Download VIA definitions"), self)
        download_via_stack_act.triggered.connect(self.load_via_stack_json)

        load_dummy_act = QAction(tr("MenuFile", "Load dummy JSON..."), self)
        load_dummy_act.triggered.connect(self.on_load_dummy)

        exit_act = QAction(tr("MenuFile", "Exit"), self)
        exit_act.setShortcut("Ctrl+Q")
        exit_act.triggered.connect(qApp.exit)

        file_menu = self.menuBar().addMenu(tr("Menu", "File"))
        file_menu.addAction(layout_load_act)
        file_menu.addAction(layout_save_act)
        file_menu.addSeparator()
        file_menu.addAction(sideload_json_act)
        file_menu.addAction(download_via_stack_act)
        file_menu.addAction(load_dummy_act)
        file_menu.addSeparator()
        file_menu.addAction(exit_act)

        keyboard_unlock_act = QAction(tr("MenuSecurity", "Unlock"), self)
        keyboard_unlock_act.triggered.connect(self.unlock_keyboard)

        keyboard_lock_act = QAction(tr("MenuSecurity", "Lock"), self)
        keyboard_lock_act.triggered.connect(self.lock_keyboard)

        keyboard_reset_act = QAction(
            tr("MenuSecurity", "Reboot to bootloader"), self)
        keyboard_reset_act.triggered.connect(self.reboot_to_bootloader)

        keyboard_layout_menu = self.menuBar().addMenu(
            tr("Menu", "Keyboard layout"))
        keymap_group = QActionGroup(self)
        selected_keymap = self.settings.value("keymap")
        for idx, keymap in enumerate(KEYMAPS):
            act = QAction(tr("KeyboardLayout", keymap[0]), self)
            act.triggered.connect(
                lambda checked, x=idx: self.change_keyboard_layout(x))
            act.setCheckable(True)
            if selected_keymap == keymap[0]:
                self.change_keyboard_layout(idx)
                act.setChecked(True)
            keymap_group.addAction(act)
            keyboard_layout_menu.addAction(act)
        # check "QWERTY" if nothing else is selected
        if keymap_group.checkedAction() is None:
            keymap_group.actions()[0].setChecked(True)

        self.security_menu = self.menuBar().addMenu(tr("Menu", "Security"))
        self.security_menu.addAction(keyboard_unlock_act)
        self.security_menu.addAction(keyboard_lock_act)
        self.security_menu.addSeparator()
        self.security_menu.addAction(keyboard_reset_act)

        self.theme_menu = self.menuBar().addMenu(tr("Menu", "Theme"))
        theme_group = QActionGroup(self)
        selected_theme = self.get_theme()
        for name, _ in [("System", None)] + themes.themes:
            act = QAction(tr("MenuTheme", name), self)
            act.triggered.connect(lambda x, name=name: self.set_theme(name))
            act.setCheckable(True)
            act.setChecked(selected_theme == name)
            theme_group.addAction(act)
            self.theme_menu.addAction(act)
        # check "System" if nothing else is selected
        if theme_group.checkedAction() is None:
            theme_group.actions()[0].setChecked(True)

    def on_layout_load(self):
        dialog = QFileDialog()
        dialog.setDefaultSuffix("vil")
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        dialog.setNameFilters(["Vial layout (*.vil)"])
        if dialog.exec_() == QDialog.Accepted:
            with open(dialog.selectedFiles()[0], "rb") as inf:
                data = inf.read()
            self.keymap_editor.restore_layout(data)
            self.rebuild()

    def on_layout_save(self):
        dialog = QFileDialog()
        dialog.setDefaultSuffix("vil")
        dialog.setAcceptMode(QFileDialog.AcceptSave)
        dialog.setNameFilters(["Vial layout (*.vil)"])
        if dialog.exec_() == QDialog.Accepted:
            with open(dialog.selectedFiles()[0], "wb") as outf:
                outf.write(self.keymap_editor.save_layout())

    def on_click_refresh(self):
        self.combobox_devices.clear()
        self.devices = find_vial_devices(self.via_stack_json,
                                         self.sideload_vid, self.sideload_pid)

        for dev in self.devices:
            self.combobox_devices.addItem(dev.title())

        if self.devices:
            self.lbl_no_devices.hide()
            self.tabs.show()
        else:
            self.lbl_no_devices.show()
            self.tabs.hide()

    def on_device_selected(self):
        if self.current_device is not None:
            self.current_device.close()
        self.current_device = None
        idx = self.combobox_devices.currentIndex()
        if idx >= 0:
            self.current_device = self.devices[idx]

        if self.current_device is not None:
            if self.current_device.sideload:
                self.current_device.open(self.sideload_json)
            elif self.current_device.via_stack:
                self.current_device.open(self.via_stack_json["definitions"][
                    self.current_device.via_id])
            else:
                self.current_device.open(None)

            if isinstance(self.current_device, VialKeyboard) \
                    and self.current_device.keyboard.keyboard_id in EXAMPLE_KEYBOARDS:
                QMessageBox.warning(
                    self, "", "An example keyboard UID was detected.\n"
                    "Please change your keyboard UID to be unique before you ship!"
                )

        self.rebuild()

        self.refresh_tabs()

    def rebuild(self):
        # don't show "Security" menu for bootloader mode, as the bootloader is inherently insecure
        self.security_menu.menuAction().setVisible(
            isinstance(self.current_device, VialKeyboard))

        # if unlock process was interrupted, we must finish it first
        if isinstance(
                self.current_device, VialKeyboard
        ) and self.current_device.keyboard.get_unlock_in_progress():
            Unlocker.unlock(self.current_device.keyboard)
            self.current_device.keyboard.reload()

        for e in [
                self.layout_editor, self.keymap_editor, self.firmware_flasher,
                self.macro_recorder, self.matrix_tester
        ]:
            e.rebuild(self.current_device)

    def refresh_tabs(self):
        self.tabs.clear()
        for container, lbl in self.editors:
            if not container.valid():
                continue

            w = QWidget()
            w.setLayout(container)
            self.tabs.addTab(w, tr("MainWindow", lbl))

    def load_via_stack_json(self):
        data = urlopen(
            "https://github.com/vial-kb/via-keymap-precompiled/raw/main/via_keyboard_stack.json"
        )
        self.via_stack_json = json.load(data)
        # write to cache
        with open(os.path.join(self.cache_path, "via_keyboards.json"),
                  "w") as cf:
            cf.write(json.dumps(self.via_stack_json, indent=2))
            cf.close()

    def on_sideload_json(self):
        dialog = QFileDialog()
        dialog.setDefaultSuffix("json")
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        dialog.setNameFilters(["VIA layout JSON (*.json)"])
        if dialog.exec_() == QDialog.Accepted:
            with open(dialog.selectedFiles()[0], "rb") as inf:
                data = inf.read()
            self.sideload_json = json.loads(data)
            self.sideload_vid = int(self.sideload_json["vendorId"], 16)
            self.sideload_pid = int(self.sideload_json["productId"], 16)
            self.on_click_refresh()

    def on_load_dummy(self):
        dialog = QFileDialog()
        dialog.setDefaultSuffix("json")
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        dialog.setNameFilters(["VIA layout JSON (*.json)"])
        if dialog.exec_() == QDialog.Accepted:
            with open(dialog.selectedFiles()[0], "rb") as inf:
                data = inf.read()
            self.sideload_json = json.loads(data)
            self.sideload_vid = self.sideload_pid = 0
            self.on_click_refresh()

    def lock_ui(self):
        self.tabs.setEnabled(False)
        self.combobox_devices.setEnabled(False)
        self.btn_refresh_devices.setEnabled(False)

    def unlock_ui(self):
        self.tabs.setEnabled(True)
        self.combobox_devices.setEnabled(True)
        self.btn_refresh_devices.setEnabled(True)

    def unlock_keyboard(self):
        if isinstance(self.current_device, VialKeyboard):
            Unlocker.unlock(self.current_device.keyboard)

    def lock_keyboard(self):
        if isinstance(self.current_device, VialKeyboard):
            self.current_device.keyboard.lock()

    def reboot_to_bootloader(self):
        if isinstance(self.current_device, VialKeyboard):
            Unlocker.unlock(self.current_device.keyboard)
            self.current_device.keyboard.reset()

    def change_keyboard_layout(self, index):
        self.settings.setValue("keymap", KEYMAPS[index][0])
        self.keymap_editor.set_keymap_override(KEYMAPS[index][1])

    def get_theme(self):
        return self.settings.value("theme", "Dark")

    def set_theme(self, theme):
        themes.set_theme(theme)
        self.settings.setValue("theme", theme)
        msg = QMessageBox()
        msg.setText(
            tr(
                "MainWindow",
                "In order to fully apply the theme you should restart the application."
            ))
        msg.exec_()
Exemple #37
0
class NewSessionPageExamAnswers(QWizardPage):

    def __init__(self):
        super().__init__()
        self.setTitle(_('Selection of correct answers'))
        self.setSubTitle(_('Select the correct answers for each exam model'))
        layout = QFormLayout()
        self.setLayout(layout)
        self.tabs = QTabWidget()
        layout.addRow(self.tabs)
        self.paramNAlts = None
        self.paramNCols = None
        self.paramNPerm = None

    def initializePage(self):
        new_paramNAlts = self.field("paramNAlts")
        new_paramNCols = self.field("paramNCols")
        new_paramNPerm = self.field("paramNPerm")
        if (new_paramNAlts != self.paramNAlts
            or new_paramNCols != self.paramNCols
            or new_paramNPerm != self.paramNPerm):
            self.paramNAlts = new_paramNAlts
            self.paramNCols = new_paramNCols
            self.paramNPerm = new_paramNPerm
            self._initialize()

    def _initialize(self):
        ## self.paramTPerm = self.field("paramTPerm")
        self.tabs.clear()
        self.total_answers = 0
        self.radioGroups = {}
        filas = self.paramNPerm
        for x in range(filas):
            mygroupbox = QScrollArea()
            mygroupbox.setWidget(QWidget())
            mygroupbox.setWidgetResizable(True)
            myform = QHBoxLayout(mygroupbox.widget())
            cols = self.paramNCols.split(',')
            ansID = 0
            radioGroupList = {}
            for col in cols:
                mygroupboxCol = QGroupBox()
                myformCol = QFormLayout()
                mygroupboxCol.setLayout(myformCol)
                for y in range(int(col)):
                    ansID += 1
                    radioGroupList[ansID] = QButtonGroup()
                    layoutRow = QHBoxLayout()
                    for j in range(self.paramNAlts):
                        myradio = QRadioButton(chr(97+j).upper())
                        layoutRow.addWidget(myradio)
                        radioGroupList[ansID].addButton(myradio)
                    self.total_answers  += 1
                    myformCol.addRow(str(ansID), layoutRow)
                myform.addWidget(mygroupboxCol)
            self.radioGroups[chr(97+x).upper()] = radioGroupList
            self.tabs.addTab(mygroupbox, _('Model ') + chr(97+x).upper())

    def _get_values(self, formated=False):
        response = dict()
        for k, v in self.radioGroups.items():
            answer = dict()
            for ak, av in v.items():
                answer[ak] = abs(int(av.checkedId())) - 1
            if formated:
                answer = list(answer.values())
            response[k] = answer
        return response

    def _check_count_answers(self):
        local_radioGroups = self._get_values(formated=True)
        local_total_answers = 0
        for v in local_radioGroups.values():
            for a in v:
                if a != 0:
                    local_total_answers += 1
        return (self.total_answers == local_total_answers)

    def validatePage(self):
        valid = True
        msg = ''
        if not self._check_count_answers():
            valid = False
            msg = _('You haven\'t entered the correct answer '
                    'for some questions.')
        else:
            try:
                self.wizard().exam_config = exams.ExamConfig()
                # dimentions generation:
                dimensions = []
                for c in self.paramNCols.split(','):
                    dimensions.append("%d,%s" % (self.paramNAlts, c))
                self.wizard().exam_config.set_dimensions(';'.join(dimensions))
                # solutions generation:
                current_solutions = self._get_values(formated=True)
                for k, v in current_solutions.items():
                    self.wizard().exam_config.set_solutions(k, v)
                # students ids generation:
                self.wizard().exam_config.id_num_digits = \
                    self.field("paramNEIDs")
            except IOError:
                valid = False
                msg = _('The exam configuration file cannot be read.')
            except Exception as e:
                valid = False
                msg = _('The exam configuration file contains errors')
                if str(e):
                    msg += ':<br><br>' + str(e)
                else:
                    msg += '.'
        if not valid:
            QMessageBox.critical(self, _('Error'), msg)
        return valid

    def nextId(self):
        return WizardNewSession.PageStudents