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
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()
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()
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
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."])
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)
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)
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." ) ])
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)
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." ])
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
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()
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()
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)
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 )
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)
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
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]
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
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()
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')
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)
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)
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))
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')
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)
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_()
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