Source code for bci_framework.framework.environments.development

"""
===========
Development
===========

This environment use the integrated development environt to code a test new
extensions.
"""

import os
from typing import TypeVar

from PySide6.QtCore import QTimer, Qt
from PySide6.QtGui import QTextCursor, QKeySequence, QShortcut

from ..extensions_handler import ExtensionWidget
from ..editor import Autocompleter
from ..widgets import projects

# LINE_DELIVERY = 'bci_framework.extensions.stimuli_delivery'
# LINE_VISUALIZATION = 'bci_framework.extensions.visualizations'
# LINE_ANALYSIS = 'bci_framework.extensions.data_analysis'
# LINE_LOCKTIME = 'bci_framework.extensions.timelock_analysis'

PATH = TypeVar('Path')


########################################################################
[docs]class Development: """Main Action with an integrated development environ.""" # ----------------------------------------------------------------------
[docs] def __init__(self, core): """Constructor""" self.parent_frame = core.main self.core = core self.parent_frame.pushButton_stop_preview.hide() shortcut_docs = QShortcut(QKeySequence('F5'), self.parent_frame) shortcut_docs.activated.connect(self.start_preview) shortcut_docs = QShortcut(QKeySequence('Ctrl+F5'), self.parent_frame) shortcut_docs.activated.connect(self.stop_preview) shortcut_docs = QShortcut(QKeySequence('Ctrl+S'), self.parent_frame) shortcut_docs.activated.connect(self.save_all_files) self.log_timer = QTimer() self.log_timer.timeout.connect(self.update_log) self.log_timer.setInterval(10) self.timer_autosave = QTimer() self.timer_autosave.timeout.connect(self.save_all_files) self.timer_autosave.setInterval(5000) self.timer_autosave.start() self.connect() self.hide_preview()
# self.build_linenumber() # # ---------------------------------------------------------------------- # def build_linenumber(self) -> None: # """The same linenumber is used for all editors.""" # self.parent_frame.textEdit_linenumber.setStyleSheet(f""" # QTextEdit {{ # background-color: {os.environ.get('QTMATERIAL_SECONDARYDARKCOLOR')}; # color: {os.environ.get('QTMATERIAL_SECONDARYTEXTCOLOR')}; # font-weight: normal; # font-family: 'DejaVu Sans Mono'; # font-size: 15px; # line-height: 15px; # border: 0px solid {os.environ.get('QTMATERIAL_SECONDARYLIGHTCOLOR')}; # border-right: 10px solid {os.environ.get('QTMATERIAL_SECONDARYLIGHTCOLOR')}; # border-radius: 0px; # padding: 8px 0px ; # padding-top: 41px; # }} # """) # ----------------------------------------------------------------------
[docs] def connect(self) -> None: """Connect events.""" self.parent_frame.pushButton_script_preview.clicked.connect( self.start_preview) self.parent_frame.pushButton_stop_preview.clicked.connect( self.stop_preview)
# ----------------------------------------------------------------------
[docs] def hide_preview(self) -> None: """Expand editor.""" if self.mode != 'analysis': self.parent_frame.mdiArea_development.hide() self.parent_frame.splitter_preview.moveSplitter( self.parent_frame.splitter_preview.getRange(1)[1], 1)
# self.handle_width = self.parent_frame.splitter_preview.handleWidth() # self.parent_frame.splitter_preview.setHandleWidth(0) # ----------------------------------------------------------------------
[docs] def show_preview(self) -> None: """Show previsualization.""" if self.mode != 'analysis': self.parent_frame.mdiArea_development.show() self.parent_frame.splitter_preview.moveSplitter( self.parent_frame.splitter_preview.getRange(1)[1] // 2, 1)
# self.parent_frame.splitter_preview.setHandleWidth(30) # ----------------------------------------------------------------------
[docs] def get_project(self) -> PATH: """Return the path of the current project.""" return os.path.split(self.parent_frame.treeWidget_project.path)[1]
# ----------------------------------------------------------------------
[docs] def start_preview(self) -> None: """Initialize the previsualization and the debugger.""" if not self.parent_frame.pushButton_script_preview.isVisible(): return self.log_timer.start() self.show_preview() self.parent_frame.mdiArea_development.tileSubWindows() self.save_all_files() self.parent_frame.plainTextEdit_preview_log.setPlainText('') self.sub.open_subwindow = self.open_subwindow self.sub.load_extension(self.get_project(), debugger=True) self.parent_frame.pushButton_stop_preview.show() self.parent_frame.pushButton_script_preview.hide()
# ----------------------------------------------------------------------
[docs] def open_subwindow(self) -> None: """Open an auxiliar window for stimuli delivery.""" url = self.sub.stream_subprocess.url self.core.stimuli_delivery.open_subwindow( url.replace('/dashboard', '/'))
# ----------------------------------------------------------------------
[docs] def on_focus(self) -> None: """Set the correct projects widget.""" self.parent_frame.mdiArea_development.tileSubWindows() self.parent_frame.tabWidget_widgets.setCurrentWidget( self.parent_frame.tab_projects) self.parent_frame.actionDevelopment.setEnabled( self.parent_frame.tabWidget_project.count())
# ----------------------------------------------------------------------
[docs] def build_preview(self) -> None: """Update mdiAreas.""" self.stop_preview() for sub in self.parent_frame.mdiArea_development.subWindowList(): sub.remove() if sub := getattr(self, 'sub', False): sub.deleteLater() self.sub = ExtensionWidget( self.parent_frame.mdiArea_development, extensions_list=[], mode=self.mode) self.parent_frame.mdiArea_development.addSubWindow(self.sub) self.sub.show() self.parent_frame.mdiArea_development.tileSubWindows()
# ----------------------------------------------------------------------
[docs] def stop_preview(self) -> None: """Stop subprocess.""" if not self.parent_frame.pushButton_stop_preview.isVisible(): return self.log_timer.stop() if hasattr(self, 'sub'): self.sub.stop_preview() self.parent_frame.pushButton_stop_preview.hide() self.parent_frame.pushButton_script_preview.show() self.hide_preview()
# ----------------------------------------------------------------------
[docs] def update_log(self) -> None: """Write logs into debugger. Logging messages could be from Python or JavaScript (Bryhon). """ # if not hasattr(self.sub, 'stream_subprocess'): # # self.timer.singleShot(50, self.update_log) # return if not hasattr(self.sub.stream_subprocess, 'stdout'): self.sub.stream_subprocess.start_debug() # return loglevel = self.parent_frame.comboBox_log_level.currentText() levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] lines = [] try: if hasattr(self.sub.stream_subprocess, 'subprocess_script'): if line := self.sub.stream_subprocess.subprocess_script.nb_stdout.readline(timeout=0.01): if hasattr(line, 'decode'): lines.append(line.decode()) else: lines.append(line) if self.mode == 'stimuli': if line := self.sub.stream_subprocess.stdout.readline(timeout=0.01): if hasattr(line, 'decode'): lines.append(line.decode()) else: lines.append(line) except: pass # if hasattr(self.sub.stream_subprocess, 'subprocess_script'): # if line := self.sub.stream_subprocess.stdout.readline(timeout=0.01): # if hasattr(line, 'decode'): # lines.append(line.decode()) # else: # lines.append(line) filter_ = [ 'using indexedDB', 'Synchronous XMLHttpRequest', 'Error 404 means', 'The AudioContext', 'WARNING:tornado.access:404', 'INFO:tornado.access:200', 'upgrade needed', 'INFO:werkzeug', '* Serving', '* Environment:', '* Debug mode:', 'This is a development server', 'selenium', ] for line in lines: if not line.strip(): continue trace = 'Traceback (most recent call last):' if trace in line: line = line[line.find(trace):] c = False for f in filter_: if f in line: c = True break if c: continue self.parent_frame.plainTextEdit_preview_log.moveCursor( QTextCursor.End) if line[:line.find(':')] in levels: s = [level == loglevel for level in levels] if line[:line.find(':')] in levels[s.index(True):]: self.parent_frame.plainTextEdit_preview_log.insertPlainText( line) else: self.parent_frame.plainTextEdit_preview_log.insertPlainText( line) if not line.endswith('\n'): self.parent_frame.plainTextEdit_preview_log.insertPlainText( '\n')
# if lines: # self.log_timer.setInterval(5) # else: # self.log_timer.setInterval(500) # self.log_timer.setInterval(5) # ----------------------------------------------------------------------
[docs] def save_all_files(self) -> None: """Save all opened files.""" for i, tab_text in enumerate(map(self.parent_frame.tabWidget_project.tabText, range(self.parent_frame.tabWidget_project.count()))): if tab_text.endswith('*'): editor = self.parent_frame.tabWidget_project.widget(i) with open(editor.path, 'w') as file: content = editor.toPlainText() file.write(content) # if not editor.completer: # if projects.LINE_DELIVERY in content: # completer = Autocompleter(mode='stimuli') # editor.set_completer(completer) # elif projects.LINE_ANALYSIS in content: # completer = Autocompleter(mode='analysis') # editor.set_completer(completer) # elif projects.LINE_VISUALIZATION in content: # completer = Autocompleter(mode='visualization') # editor.set_completer(completer) # elif projects.LINE_LOCKTIME in content: # completer = Autocompleter(mode='locktime') # editor.set_completer(completer) self.parent_frame.tabWidget_project.setTabText( i, tab_text.strip('*'))
# ---------------------------------------------------------------------- @property def mode(self): """""" return getattr(self.core.projects, 'mode', None)