def paintEvent(self, e): """ Paints this editor. """ # Paint the editor super(Editor, self).paintEvent(e) # Paint a line between the editor and the line number area x = self.contentOffset().x() + self.document().documentMargin() \ + self._line_offset p = QtGui.QPainter(self.viewport()) p.setPen(QtGui.QPen(QtGui.QColor('#ddd'))) rect = e.rect() p.drawLine(x, rect.top(), x, rect.bottom())
def __init__(self, document): super(ModelHighlighter, self).__init__(document) # Highlighting rules self._rules = [] # Numbers style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(255, 0, 255)) pattern = QtCore.QRegExp(r'\b[+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?\b') self._rules.append((pattern, style)) # Keywords style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(0, 96, 0)) style.setFontWeight(QtGui.QFont.Bold) for keyword in self.KEYWORDS: pattern = QtCore.QRegExp(r'\b' + keyword + r'\b') self._rules.append((pattern, style)) # Meta-data coloring (overrules previous formatting) self._meta_style = QtGui.QTextCharFormat() self._meta_style.setForeground(QtGui.QColor(128, 128, 192)) pattern = QtCore.QRegExp(r':.*') self._rules.append((pattern, self._meta_style)) # Strings (overrule previous formatting, except when commented) self._string_start = QtCore.QRegExp(r'"""') self._string_stop = QtCore.QRegExp(r'"""') self._comment_start = QtCore.QRegExp(r'#[^\n]*') # Comments (overrule all other formatting) style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(20, 20, 255)) pattern = QtCore.QRegExp(r'#[^\n]*') self._rules.append((pattern, style))
def _line_number_area_paint(self, area, event): """ Repaints the line number area. """ # Area to repaint rect = event.rect() etop = rect.top() ebot = rect.bottom() # Font metrics metrics = self.fontMetrics() height = metrics.height() width = area.width() # Create painter, set font color painter = QtGui.QPainter(area) painter.fillRect(rect, self._palette.button()) painter.setPen(self._palette.buttonText().color()) # Get top and bottom of first visible block block = self.firstVisibleBlock() geom = self.blockBoundingGeometry(block) btop = int(geom.translated(self.contentOffset()).top()) bbot = int(btop + geom.height()) # Iterate over visible blocks count = block.blockNumber() while block.isValid() and btop <= ebot: count += 1 if block.isVisible() and bbot >= etop: painter.drawText( 0, btop, width - 4, height, Qt.AlignRight, str(count)) block = block.next() btop = bbot bbot += int(self.blockBoundingRect(block).height())
def action_set_frame(self, frame): """ Move to a specific frame. """ # Check frame index nt, ny, nx = self._data.shape() frame = int(frame) if frame < 0 or frame >= nt: frame = 0 # Update if self._video_iframe != frame or True: self._video_iframe = frame # Update slider if self._slider.value() != frame: self._slider.setValue(self._video_iframe) # Update frame/time information self._frame_field.setText(str(frame)) self._time_field.setText(str(self._data.time()[frame])) # Update scene image = self._video_frames[self._video_iframe] image = QtGui.QImage(image, nx, ny, QtGui.QImage.Format_ARGB32) self._video_pixmap.convertFromImage(image) self._video_item.setPixmap(self._video_pixmap) # qt5 # Update graph area self._graph_area.set_position(self._video_iframe)
def __init__(self, *args): super(VideoScene, self).__init__(*args) self.setBackgroundBrush(QtGui.QColor(192, 192, 192)) self._w = None self._h = None self._p = None self.resize(1, 1, 0)
def _line_number_area_paint(self, area, event): """ Repaints the line number area. """ # Repaint area rect = event.rect() etop = rect.top() ebot = rect.bottom() # Repaint metrics metrics = self.fontMetrics() height = metrics.height() width = area.width() # Create painter, get font metrics painter = QtGui.QPainter(area) painter.fillRect(rect, COLOR_BG_LINE_NUMBER) # Get top and bottom of first block block = self.firstVisibleBlock() geom = self.blockBoundingGeometry(block) btop = geom.translated(self.contentOffset()).top() bbot = btop + geom.height() # Iterate over blocks count = block.blockNumber() while block.isValid() and btop <= ebot: count += 1 if block.isVisible() and bbot >= etop: painter.drawText(0, btop, width, height, Qt.AlignRight, str(count)) block = block.next() btop = bbot bbot += self.blockBoundingRect(block).height()
def paintEvent(self, event): """ Draws all the graphs. """ if self._data is None: return # Create painter painter = QtGui.QPainter() painter.begin(self) # Fill background painter.fillRect(self.rect(), QtGui.QBrush(Qt.white)) # Create coordinate system for graphs painter.scale(self.width(), self.height()) painter.setRenderHint(QtGui.QPainter.Antialiasing) # Create pen pen = QtGui.QPen() pen.setWidth(0) # Draw frozen graphs colors = iter(self._color_cycle) for path in self._frozen.values(): try: pen.setColor(next(colors)) except StopIteration: colors = iter(self._color_cycle) pen.setColor(next(colors)) painter.setPen(pen) painter.drawPath(path) # Draw temp graph if self._temp_path: pen.setColor(self._color_temp) painter.setPen(pen) painter.drawPath(self._temp_path) # Show time indicator pen.setColor(Qt.red) painter.setPen(pen) t = (self._position - self._tmin) / self._trange painter.drawLine(QtCore.QLineF(t, 0, t, 1)) # Finish painter.end()
def action_extract_colormap_image(self): """ Extracts the current colormap to an image file. """ if not self._data: QtWidgets.QMessageBox.warning( self, TITLE, '<h1>No data to export.</h1>' '<p>Please open a data file first.</p>') return fname = QtWidgets.QFileDialog.getSaveFileName( self, 'Extract colormap to image file', self._path, filter=FILTER_IMG)[0] if fname: fname = str(fname) ext = os.path.splitext(fname)[1][1:].upper() if ext not in IMAGE_TYPES: QtWidgets.QMessageBox.warning( self, TITLE, '<h1>Image type not recognized.</h1>' '<p>Unknown image type "' + str(ext) + '".</p>') return # Get min and max of data data = self._data.get2d(self._variable) lower = np.min(data) upper = np.max(data) # Create image nx = 200 ny = 800 image = myokit.ColorMap.image(self._colormap, nx, ny) image = QtGui.QImage(image, nx, ny, QtGui.QImage.Format_ARGB32) painter = QtGui.QPainter(image) painter.drawText(10, 15, str(upper)) painter.drawText(10, ny - 5, str(lower)) painter.end() # Save image.save(fname, ext)
def graph(self, variable, x, y): """ Adds temporary graph to this widget. """ if self._data is None: return # Create index, check for duplicates variable = self._last_variable = str(variable) x, y = int(x), int(y) index = (x, y, variable) if index == self._temp_index: return if index in self._frozen: self._temp_index = self._temp_path = None self.update() return # Get scaling info try: ymin, ymax = self._scaling[variable] except KeyError: data = self._data.get2d(variable) ymin = np.min(data) ymax = np.max(data) d = ymax - ymin ymin -= 0.05 * d ymax += 0.05 * d if ymin == ymax: ymin -= 1 ymax += 1 self._scaling[variable] = (ymin, ymax) # Create path, using real time and scaled y data xx = iter(self._time_scaled) yy = (self._data.trace(variable, x, y) - ymin) / (ymax - ymin) yy = iter(1 - yy) path = QtGui.QPainterPath() x, y = next(xx), next(yy) path.moveTo(x, y) for i in range(1, len(self._time)): x, y = next(xx), next(yy) path.lineTo(x, y) self._temp_index = index self._temp_path = path # Update! self.update()
def icon(): icons = [ 'icon-datablock-viewer.ico', 'icon-datablock-viewer-16.xpm', 'icon-datablock-viewer-24.xpm', 'icon-datablock-viewer-32.xpm', 'icon-datablock-viewer-48.xpm', 'icon-datablock-viewer-64.xpm', 'icon-datablock-viewer-96.xpm', 'icon-datablock-viewer-128.xpm', 'icon-datablock-viewer-256.xpm', ] icon = QtGui.QIcon() for i in icons: icon.addFile(os.path.join(myokit.DIR_DATA, 'gui', i)) return icon
def action_set_colormap(self, name): """ Loads the ColorMap specified by ``name``. """ name = str(name) if not myokit.ColorMap.exists(name): return # Silent return? # Set colormap self._colormap = name # Update colormap controls self._colormap_select.setCurrentIndex( self._colormap_select.findText(self._colormap)) if self._data: # Update variable display nt, ny, nx = self._data.shape() nx = self._colormap_size image = myokit.ColorMap.image(self._colormap, nx, ny) image = QtGui.QImage(image, nx, ny, QtGui.QImage.Format_ARGB32) self._colormap_pixmap.convertFromImage(image) self._colormap_item.setPixmap(self._colormap_pixmap) # qt5 self.action_set_variable(self._variable)
def action_extract_frame_image(self): """ Extracts the current frame to an image file. """ if not self._data: QtWidgets.QMessageBox.warning( self, TITLE, '<h1>No data to export.</h1>' '<p>Please open a data file first.</p>') return fname = QtWidgets.QFileDialog.getSaveFileName( self, 'Extract frame to image file', self._path, filter=FILTER_IMG)[0] if fname: fname = str(fname) ext = os.path.splitext(fname)[1][1:].upper() if ext not in IMAGE_TYPES: QtWidgets.QMessageBox.warning( self, TITLE, '<h1>Image type not recognized.</h1>' '<p>Unknown image type "' + str(ext) + '".</p>') return nt, ny, nx = self._data.shape() image = self._video_frames[self._video_iframe] image = QtGui.QImage(image, nx, ny, QtGui.QImage.Format_ARGB32) image.save(fname, ext)
def __init__(self, document): super(ProtocolHighlighter, self).__init__(document) # Highlighting rules self._rules = [] # Numbers style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(255, 0, 255)) pattern = QtCore.QRegExp(r'\b[+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?\b') self._rules.append((pattern, style)) # Keyword "next" style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(255, 0, 255)) pattern = QtCore.QRegExp(r'\bnext\b') self._rules.append((pattern, style)) # Comments style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(20, 20, 255)) pattern = QtCore.QRegExp(r'#[^\n]*') self._rules.append((pattern, style))
def keyPressEvent(self, event): """ Qt event: A key was pressed. """ # Get key and modifiers key = event.key() mod = event.modifiers() # Possible modifiers: # NoModifier # ShiftModifier, ControlModifier, AltModifiier # MetaModifier (i.e. super key) # KeyPadModifier (button is part of keypad) # GroupSwitchModifier (x11 thing) # Ignore the keypad modifier, we don't care! if mod & Qt.KeypadModifier: mod = mod ^ Qt.KeypadModifier # xor! # Actions per key/modifier combination if key == Qt.Key_Tab and mod == Qt.NoModifier: # Indent cursor = self.textCursor() start, end = cursor.selectionStart(), cursor.selectionEnd() if cursor.hasSelection(): # Add single tab to all lines in selection cursor.beginEditBlock() # Undo grouping doc = self.document() b = doc.findBlock(start) e = doc.findBlock(end).next() while b != e: cursor.setPosition(b.position()) cursor.insertText(TABS * SPACE) b = b.next() cursor.endEditBlock() else: # Insert spaces until next tab stop pos = cursor.positionInBlock() cursor.insertText((TABS - pos % TABS) * SPACE) elif key == Qt.Key_Backtab and mod == Qt.ShiftModifier: # Dedent all lines in selection (or single line if no selection) ''' cursor = self.textCursor() start, end = cursor.selectionStart(), cursor.selectionEnd() cursor.beginEditBlock() # Undo grouping doc = self.document() # Get blocks in selection blocks = [] b = doc.findBlock(start) while b.isValid() and b.position() <= end: blocks.append(b) b = b.next() # Dedent for b in blocks: t = b.text() p1 = b.position() p2 = p1 + min(4, len(t) - len(t.lstrip())) c = self.textCursor() c.setPosition(p1) c.setPosition(p2, QtGui.QTextCursor.KeepAnchor) c.removeSelectedText() cursor.endEditBlock() ''' # This silly method is required because of a bug in qt4/qt5 cursor = self.textCursor() start, end = cursor.selectionStart(), cursor.selectionEnd() first = self.document().findBlock(start) q = 0 new_text = [] new_start, new_end = start, end b = QtGui.QTextBlock(first) while b.isValid() and b.position() <= end: t = b.text() p = min(4, len(t) - len(t.lstrip())) new_text.append(t[p:]) if b == first: new_start -= p new_end -= p q += p b = b.next() last = b.previous() new_start = max(new_start, first.position()) new_end = max(new_end, new_start) if q > 0: # Cut text, replace with new cursor.beginEditBlock() cursor.setPosition(first.position()) cursor.setPosition(last.position() + last.length() - 1, QtGui.QTextCursor.KeepAnchor) cursor.removeSelectedText() cursor.insertText('\n'.join(new_text)) cursor.endEditBlock() # Set new cursor cursor.setPosition(new_start) cursor.setPosition(new_end, QtGui.QTextCursor.KeepAnchor) self.setTextCursor(cursor) elif key == Qt.Key_Enter or key == Qt.Key_Return: # Enter/Return with modifier is overruled here to mean nothing # This is very important as the default for shift-enter is to # start a new line within the same block (this can't happen with # copy-pasting, so it's safe to just catch it here). if mod == Qt.NoModifier: # "Smart" enter: # - If selection, selection is deleted # - Else, autoindenting is performed cursor = self.textCursor() cursor.beginEditBlock() if cursor.hasSelection(): # Replace selection with newline, cursor.removeSelectedText() cursor.insertBlock() else: # Insert new line with correct indenting b = self.document().findBlock(cursor.position()) t = b.text() i = t[:len(t) - len(t.lstrip())] i = i[:cursor.positionInBlock()] cursor.insertBlock() cursor.insertText(i) cursor.endEditBlock() # Scroll if necessary self.ensureCursorVisible() elif key == Qt.Key_Home and (mod == Qt.NoModifier or mod == Qt.ShiftModifier): # Plain home button: move to start of line # If Control is used: Jump to start of document # Ordinary home button: Jump to first column or first # non-whitespace character cursor = self.textCursor() block = cursor.block() cp = cursor.position() bp = block.position() if cp != bp: # Jump to first column newpos = bp # Smart up/down: self._last_column = 0 else: # Already at first column: Jump to first non-whitespace or # end of line if all whitespace t = block.text() indent = len(t) - len(t.lstrip()) newpos = bp + indent # Smart up/down: self._last_column = indent # If Shift is used: only move position (keep anchor, i.e. select) anchor = (QtGui.QTextCursor.KeepAnchor if mod == Qt.ShiftModifier else QtGui.QTextCursor.MoveAnchor) cursor.setPosition(newpos, anchor) self.setTextCursor(cursor) elif key == Qt.Key_Home and (mod == Qt.ControlModifier or mod == Qt.ControlModifier & Qt.ShiftModifier): # Move to start of document # If Shift is used: only move position (keep anchor, i.e. select) anchor = (QtGui.QTextCursor.KeepAnchor if mod == Qt.ShiftModifier else QtGui.QTextCursor.MoveAnchor) cursor = self.textCursor() cursor.setPosition(0, anchor) self.setTextCursor(cursor) elif key in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown) \ and (mod == Qt.NoModifier or mod == Qt.ShiftModifier): # Move cursor up/down # Maintain the column position, even when the current row doesn't # have as many characters. Reset this behavior as soon as a # left/right home/end action is made or whenever the text is # changed. # Set up operation anchor = (QtGui.QTextCursor.KeepAnchor if mod == Qt.ShiftModifier else QtGui.QTextCursor.MoveAnchor) operation = \ QtGui.QTextCursor.PreviousBlock if key in ( Qt.Key_Up, Qt.Key_PageUp) else QtGui.QTextCursor.NextBlock n = 1 if key in (Qt.Key_Up, Qt.Key_Down) else (self._blocks_per_page - 3) # Move cursor = self.textCursor() if self._last_column is None: # Update "smart" column self._last_column = cursor.positionInBlock() if cursor.movePosition(operation, anchor, n): column = min(cursor.block().length() - 1, self._last_column) cursor.setPosition(cursor.position() + column, anchor) else: # Up/Down beyond document start/end? Move cursor to document # start/end and update last column if operation == QtGui.QTextCursor.NextBlock: cursor.movePosition(QtGui.QTextCursor.EndOfBlock, anchor) else: cursor.movePosition(QtGui.QTextCursor.StartOfBlock, anchor) self._last_column = cursor.positionInBlock() self.setTextCursor(cursor) elif key in (Qt.Key_Left, Qt.Key_Right, Qt.Key_End) and not (mod & Qt.AltModifier): # Allow all modifiers except alt # Reset smart up/down behavior self._last_column = None # Pass to parent class super(Editor, self).keyPressEvent(event) elif key == Qt.Key_Insert and mod == Qt.NoModifier: # Insert/replace self.setOverwriteMode(not self.overwriteMode()) else: # Default keyboard shortcuts / functions: # Backspace OK # Delete OK # Control+C OK # Control+V OK # Control+X OK # Control+Insert OK # Shift+Insert OK # Shift+Delete OK # Control+Z OK # Control+Y OK # LeftArrow Overwritten (maintained) # RightArrow Overwritten (maintained) # UpArrow Overwritten (maintained) # DownArrow Overwritten (maintained) # Control+RightArrow OK (Jump to next word) # Control+LeftArrow OK (Jump to previous word) # Control+UpArrow Removed # Control+Down Arrow Removed # PageUp Overwritten (maintained) # PageDown Overwritten (maintained) # Home Overwritten (maintained) # End Overwritten (maintained) # Control+Home Overwritten (maintained) # Control+End Overwritten (maintained) # Alt+Wheel OK (Horizontal scrolling) # Control+Wheel OK (Fast scrolling) # Control+K Removed # Not listed, but very important: # Shift-Enter Starts new line within the same block! # Definitely removed # Ctrl-i Undocumented, but inserts tab... ctrl_ignore = (Qt.Key_K, Qt.Key_I) if mod == Qt.ControlModifier and key in ctrl_ignore: # Control-K: ignore pass elif key == Qt.Key_Up or key == Qt.Key_Down: # Up/down with modifiers: ignore pass else: # Let parent class handle it super(Editor, self).keyPressEvent(event)
def __init__(self, document): super(ScriptHighlighter, self).__init__(document) # Highlighting rules self._rules = [] # Numbers style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(255, 0, 255)) pattern = QtCore.QRegExp(r'\b[+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?\b') self._rules.append((pattern, style)) # True/False/None style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(255, 0, 255)) pattern = QtCore.QRegExp(r'\bTrue\b') self._rules.append((pattern, style)) pattern = QtCore.QRegExp(r'\bFalse\b') self._rules.append((pattern, style)) pattern = QtCore.QRegExp(r'\bNone\b') self._rules.append((pattern, style)) # Built-in essential functions style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(0, 128, 128)) for func in _PYFUNC: pattern = QtCore.QRegExp(r'\b' + str(func) + r'\b') self._rules.append((pattern, style)) # Keywords import keyword style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(0, 96, 0)) style.setFontWeight(QtGui.QFont.Bold) for kw in keyword.kwlist: pattern = QtCore.QRegExp(r'\b' + kw + r'\b') self._rules.append((pattern, style)) # Strings self._string_style = QtGui.QTextCharFormat() self._string_style.setForeground(QtGui.QColor(255, 0, 255)) pattern = QtCore.QRegExp(r'"([^"\\]|\\")*"') self._rules.append((pattern, self._string_style)) pattern = QtCore.QRegExp(r"'([^'\\]|\\')*'") self._rules.append((pattern, self._string_style)) # Multi-line strings self._string1 = QtCore.QRegExp(r"'''") self._string2 = QtCore.QRegExp(r'"""') # Comments style = QtGui.QTextCharFormat() style.setForeground(QtGui.QColor(20, 20, 255)) pattern = QtCore.QRegExp(r'#[^\n]*') self._rules.append((pattern, style))
from __future__ import absolute_import, division from __future__ import print_function, unicode_literals import myokit from myokit.gui import Qt, QtCore, QtGui, QtWidgets # GUI components # Constants SPACE = ' ' TABS = 4 INDENT = SPACE * TABS BRACKETS = {'(': ')', ')': '(', '[': ']', ']': '['} BRACKETS_CLOSE = (')', ']') FONT = myokit.gui.qtMonospaceFont() FONT.setPointSize(11) COLOR_CURRENT_LINE = QtGui.QColor(230, 230, 240) COLOR_BG_LINE_NUMBER = QtGui.QColor(230, 230, 230) COLOR_BG_BRACKET = QtGui.QColor(210, 210, 210) # Classes & methods class Editor(QtWidgets.QPlainTextEdit): """ Source code editor used in Myokit. Provides the signal ``find_action(str)`` which is fired everything a find action occurred with a description that can be used in an application's status bar. """ # Signal: Find action happened, update with text # Attributes: (description)
def _check_for_dark_mode(palette): """ Checks the default editor background color, and adjusts the colour scheme if it looks like dark-mode is enabled. """ c = palette.base().color() c = (c.blueF() + c.greenF() + c.redF()) / 3 dark = c < 0.5 # Don't mess with these directly: Use the SVG in myokit-docs if not dark: STYLE_HEADER.setForeground(QtGui.QColor(0, 31, 231)) STYLE_COMMENT.setForeground(QtGui.QColor(103, 161, 107)) STYLE_ANNOT_KEY.setForeground(QtGui.QColor(0, 31, 231)) STYLE_ANNOT_VAL.setForeground(QtGui.QColor(57, 115, 214)) STYLE_KEYWORD_1.setForeground(QtGui.QColor(0, 128, 0)) STYLE_KEYWORD_1.setFontWeight(QtGui.QFont.Bold) STYLE_KEYWORD_2.setForeground(QtGui.QColor(0, 128, 128)) STYLE_LITERAL.setForeground(QtGui.QColor(255, 20, 215)) STYLE_INLINE_UNIT.setForeground(QtGui.QColor(128, 0, 128)) else: STYLE_HEADER.setForeground(QtGui.QColor(98, 178, 255)) STYLE_COMMENT.setForeground(QtGui.QColor(153, 153, 153)) STYLE_ANNOT_KEY.setForeground(QtGui.QColor(179, 179, 179)) STYLE_ANNOT_VAL.setForeground(QtGui.QColor(171, 177, 205)) STYLE_KEYWORD_1.setForeground(QtGui.QColor(10, 195, 87)) STYLE_KEYWORD_1.setFontWeight(QtGui.QFont.Bold) STYLE_KEYWORD_2.setForeground(QtGui.QColor(10, 195, 87)) STYLE_LITERAL.setForeground(QtGui.QColor(255, 223, 12)) STYLE_INLINE_UNIT.setForeground(QtGui.QColor(168, 152, 33)) global COLOR_SELECTED_LINE COLOR_SELECTED_LINE = QtGui.QColor(70, 70, 70)
def __init__(self, parent, title, var, func, args): super(VarGrapher, self).__init__(parent) self.setFixedSize(700, 600) self.setWindowTitle(title) # Figure panel self._figure = matplotlib.figure.Figure() self._canvas = backend.FigureCanvasQTAgg(self._figure) self._toolbar = backend.NavigationToolbar2QT(self._canvas, self) # Variable panel self._variable_widget = QtWidgets.QWidget() # Button panel self._button_widget = QtWidgets.QWidget() # Central widget layout = QtWidgets.QVBoxLayout() layout.addWidget(self._canvas) layout.addWidget(self._toolbar) layout.addWidget(self._variable_widget) layout.addWidget(self._button_widget) self.setLayout(layout) # Get function handle, information object self._func = func self._args = args self._var = var # Variable ranges grid = QtWidgets.QGridLayout() self._bounds = {} for k, lhs in enumerate(self._args): var = lhs.var() # Guess appropriate bounds if var.label() == 'membrane_potential' or \ var.name().lower() in ['v', 'voltage', 'potential']: if var.unit() == myokit.units.volt: lohi = (-0.1, 0.1) else: lohi = (-100.0, 100.0) else: v = lhs.eval() if v >= 0 and v <= 1: lohi = (0.0, 1.0) elif v < 0: lohi = (-50, 50) else: lohi = (0, 100) # Row and column of first widget in grid row = k // 2 col = (k % 2) * 3 # Add label label = QtWidgets.QLabel(var.qname()) grid.addWidget(label, row, col) # Add lower and upper bound or single value if k < 2: # Lower editlo = QtWidgets.QLineEdit() editlo.setValidator(QtGui.QDoubleValidator()) editlo.setText(str(lohi[0])) grid.addWidget(editlo, row, col + 1) # Upper edithi = QtWidgets.QLineEdit() edithi.setValidator(QtGui.QDoubleValidator()) edithi.setText(str(lohi[1])) grid.addWidget(edithi, row, col + 2) self._bounds[lhs] = (editlo, edithi) else: # Single, fixed value v = 0.5 * (lohi[0] + lohi[1]) edit = QtWidgets.QLineEdit(str(v)) edit.setReadOnly(True) grid.addWidget(edit, row, col + 1) self._bounds[lhs] = (edit, edit) self._variable_widget.setLayout(grid) # Buttons layout = QtWidgets.QHBoxLayout() # Graph button button = QtWidgets.QPushButton('Refresh') button.clicked.connect(self.action_draw) layout.addWidget(button) # Close button button = QtWidgets.QPushButton('Close') button.clicked.connect(self.close) layout.addWidget(button) self._button_widget.setLayout(layout) # Draw! self.action_draw()
def load_data_file(self, fname): """ Attempts to load the given data block 2d file. """ self._timer.stop() # Fix path fname = os.path.abspath(str(fname)) self._path = os.path.dirname(fname) # Try loading file. self.statusBar().showMessage('Loading ' + str(fname)) n = 1000000 pd = QtWidgets.QProgressDialog('Loading data file...', 'Cancel', 0, n) pd.setWindowModality(Qt.WindowModal) pd.setValue(0) class Reporter(myokit.ProgressReporter): def __init__(self, pd): self._pd = pd def enter(self, msg=None): pass def exit(self): pass def update(self, f): self._pd.setValue((int)(n * f)) return not self._pd.wasCanceled() reporter = Reporter(pd) try: data = myokit.DataBlock2d.load(fname, progress=reporter) del (reporter) except myokit.DataBlockReadError: pd.reset() self.statusBar().showMessage('Load failed.') QtWidgets.QMessageBox.warning( self, TITLE, '<h1>Unable to read file.</h1>' '<p>The given filename <code>' + str(fname) + '</code>' ' could not be read as a <code>myokit.DataBlock2d</code>.</p>') return except Exception as e: pd.reset() self.statusBar().showMessage('Load failed.') self.display_exception() return finally: if pd.wasCanceled(): self.statusBar().showMessage('Load canceled.') return # Don't load empty files if data.len2d() < 1: self.statusBar().showMessage('Load failed: empty file.') QtWidgets.QMessageBox.warning( self, TITLE, '<h1>Unable to read file.</h1>' '<p>The given filename <code>' + str(fname) + '</code>' ' does not contain any 2d data.</p>') return # File loaded okay self.statusBar().showMessage('File loaded succesfully.') self._file = fname self._data = data self.update_window_title() # Update recent file list try: # Remove fname from recent files list i = self._recent_files.index(fname) self._recent_files = self._recent_files[:i] \ + self._recent_files[i+1:] except ValueError: pass self._recent_files.insert(0, fname) self._recent_files = self._recent_files[:N_RECENT_FILES] self.update_recent_files_menu() # Update video scene nt, ny, nx = self._data.shape() self._colormap_size = max(int(0.05 * nx), 1) self._video_scene.clear() self._video_scene.resize(nx, ny, 2 * self._colormap_size) self._video_view.resizeEvent() self._video_iframe = 0 # Add empty video item to video scene self._video_pixmap = QtGui.QPixmap(nx, ny) self._video_item = QtWidgets.QGraphicsPixmapItem(self._video_pixmap) self._video_scene.addItem(self._video_item) # Add empty colormap item to video scene self._colormap_pixmap = QtGui.QPixmap(self._colormap_size, ny) self._colormap_item = QtWidgets.QGraphicsPixmapItem( self._colormap_pixmap) self._colormap_item.setPos(nx + self._colormap_size, 0) self._video_scene.addItem(self._colormap_item) # Move slider to correct position self._slider.setMaximum(nt) self._slider.setPageStep(int(nt / 20)) # Update controls for i in range(self._variable_select.count(), 0, -1): self._variable_select.removeItem(i - 1) variable = None for name in data.keys2d(): self._variable_select.addItem(name) if variable is None or name in ('membrane.V', 'membrane.v'): variable = name self.action_set_variable(variable) self.action_set_colormap(self._colormap) # Update graph area self._graph_area.set_data(self._data)
def __init__(self, filename=None): super(DataBlockViewer, self).__init__() # Set application icon self.setWindowIcon(icon()) # Set size, center self.resize(800, 600) self.setMinimumSize(600, 400) qr = self.frameGeometry() cp = QtWidgets.QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) # Status bar self._label_cursor = QtWidgets.QLabel() self.statusBar().addPermanentWidget(self._label_cursor) self.statusBar().showMessage('Ready') # Menu bar self.create_menu() # Timer self._timer_interval = 50 self._timer = QtCore.QTimer(self) self._timer.setInterval(self._timer_interval) self._timer.setSingleShot(False) self._timer.timeout.connect(self.action_next_frame) self._timer_paused = False try: self._timer.setTimerType(Qt.PreciseTimer) except AttributeError: pass # Qt4 uses precise timer by default # Video widget self._video_scene = VideoScene() self._video_scene.mouse_moved.connect(self.event_mouse_move) self._video_scene.single_click.connect(self.event_single_click) self._video_scene.double_click.connect(self.event_double_click) self._video_view = VideoView(self._video_scene) #self._video_view.setViewport(QtOpenGL.QGLWidget()) self._video_pixmap = None self._video_item = None self._video_frames = None self._video_iframe = None self._colormap_pixmap = None self._colormap_item = None # Video slider self._slider = QtWidgets.QSlider(Qt.Horizontal) self._slider.setTickPosition(QtWidgets.QSlider.NoTicks) #self._slider.setTickPosition(QtWidgets.QSlider.TicksBothSides) self._slider.setSingleStep(1) self._slider.setMinimum(0) self._slider.setMaximum(0) self._slider.sliderPressed.connect(self.action_pause_timer) self._slider.sliderReleased.connect(self.action_depause_timer) self._slider.valueChanged.connect(self.action_set_frame) # Controls style = QtWidgets.QApplication.style() # Play button self._play_icon_play = style.standardIcon(style.SP_MediaPlay) self._play_icon_pause = style.standardIcon(style.SP_MediaPause) self._play_button = QtWidgets.QPushButton() self._play_button.setIcon(self._play_icon_play) self._play_button.pressed.connect(self.action_start_stop) # Frame indicator self._frame_label = QtWidgets.QLabel('Frame') self._frame_field = QtWidgets.QLineEdit('0') self._frame_field.setReadOnly(True) self._frame_field.setMaxLength(6) self._frame_field.setMaximumWidth(100) self._frame_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) # Time indicator self._time_label = QtWidgets.QLabel('Time') self._time_field = QtWidgets.QLineEdit('0') self._time_field.setReadOnly(True) self._time_field.setMaxLength(6) self._time_field.setMaximumWidth(100) self._time_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) # Speed indicator self._rate_label = QtWidgets.QLabel('Delay') self._rate_field = QtWidgets.QLineEdit(str(self._timer_interval)) self._rate_field.setValidator(QtGui.QIntValidator(1, 2**20, self)) self._rate_field.editingFinished.connect(self.event_rate_changed) self._rate_field.setMaximumWidth(100) self._rate_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) # Graph controls self._graph_clear_button = QtWidgets.QPushButton('Clear graphs') self._graph_clear_button.pressed.connect(self.action_clear_graphs) # Variable selection self._variable_select = QtWidgets.QComboBox() self._variable_select.activated.connect(self.event_variable_selected) self._variable_select.setMinimumWidth(120) # Colormap selection self._colormap = myokit.ColorMap.names().next() self._colormap_select = QtWidgets.QComboBox() for cmap in myokit.ColorMap.names(): self._colormap_select.addItem(cmap) self._colormap_select.activated.connect(self.event_colormap_selected) self._colormap_select.setMinimumWidth(120) # Control layout self._control_layout = QtWidgets.QHBoxLayout() self._control_layout.addWidget(self._play_button) self._control_layout.addWidget(self._frame_label) self._control_layout.addWidget(self._frame_field) self._control_layout.addWidget(self._time_label) self._control_layout.addWidget(self._time_field) self._control_layout.addWidget(self._rate_label) self._control_layout.addWidget(self._rate_field) self._control_layout.addWidget(self._graph_clear_button) self._control_layout.addWidget(self._variable_select) self._control_layout.addWidget(self._colormap_select) # Graph area self._graph_area = GraphArea() self._graph_area.mouse_moved.connect(self.event_mouse_move) # Video Layout self._video_layout = QtWidgets.QVBoxLayout() self._video_layout.addWidget(self._video_view) self._video_layout.addWidget(self._slider) self._video_layout.addLayout(self._control_layout) self._video_widget = QtWidgets.QWidget() self._video_widget.setLayout(self._video_layout) # Central layout self._central_widget = QtWidgets.QSplitter(Qt.Vertical) self._central_widget.addWidget(self._video_widget) self._central_widget.addWidget(self._graph_area) self.setCentralWidget(self._central_widget) # Current path, current file, recent files self._path = QtCore.QDir.currentPath() self._file = None self._recent_files = [] # Current data block, display variable self._data = None self._variable = None # Load settings from ini file self.load_config() self.update_window_title() # Set controls to correct values self._colormap_select.setCurrentIndex( self._colormap_select.findText(self._colormap)) self._rate_field.setText(str(self._timer_interval)) self._timer.setInterval(self._timer_interval) # Pause video playback during resize self._resize_timer = QtCore.QTimer() self._resize_timer.timeout.connect(self._resize_timeout) self._video_view.resize_event.connect(self._resize_started) # Attempt to load selected file if filename and os.path.isfile(filename): self.load_data_file(filename)
def __init__(self, parent, sim_method, output_stream, duration=1000): super(Explorer, self).__init__(parent) self.setWindowTitle('Myokit Explorer') self._sim_method = sim_method self._stream = output_stream # Set guess for run times guess_pre = 0 guess_run = duration # Explorer data self._data = None self._keys = None # Fix background color of line edits self.setStyleSheet('QLineEdit{background: white;}') # Create top widgets label1 = QtWidgets.QLabel('Run unlogged for ') label2 = QtWidgets.QLabel(' and then log for ') self._pre_field = QtWidgets.QLineEdit(str(guess_pre)) self._pre_valid = QtGui.QDoubleValidator() self._pre_valid.setBottom(0) self._pre_field.setValidator(self._pre_valid) self._run_field = QtWidgets.QLineEdit(str(guess_run)) self._run_valid = QtGui.QDoubleValidator() self._run_valid.setBottom(0) self._run_field.setValidator(self._run_valid) self._clear_button = QtWidgets.QPushButton('Clear graphs') self._clear_button.clicked.connect(self.action_clear) self._run_button = QtWidgets.QPushButton('Run') self._run_button.clicked.connect(self.action_run) # Create graph widgets self._axes = None self._figure = matplotlib.figure.Figure() self._canvas = backend.FigureCanvasQTAgg(self._figure) self._toolbar = backend.NavigationToolbar2QT(self._canvas, self) self._select_x = QtWidgets.QComboBox() self._select_x.currentIndexChanged.connect(self.combo_changed) self._select_y = QtWidgets.QComboBox() self._select_y.currentIndexChanged.connect(self.combo_changed) # Create bottom widgets self._close_button = QtWidgets.QPushButton('Close') self._close_button.clicked.connect(self.action_close) # Create button layout button_layout = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.LeftToRight) button_layout.addWidget(label1) button_layout.addWidget(self._pre_field) button_layout.addWidget(label2) button_layout.addWidget(self._run_field) button_layout.addWidget(self._clear_button) button_layout.addWidget(self._run_button) # Create graph options layout graph_option_layout = QtWidgets.QBoxLayout( QtWidgets.QBoxLayout.LeftToRight) graph_option_layout.addWidget(self._select_x) graph_option_layout.addWidget(self._select_y) # Create bottom layout bottom_layout = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.LeftToRight) bottom_layout.addWidget(self._close_button) # Create central layout layout = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.TopToBottom) layout.addLayout(button_layout) layout.addLayout(graph_option_layout) layout.addWidget(self._canvas) layout.addWidget(self._toolbar) layout.addLayout(bottom_layout) self.setLayout(layout)
# Constants SPACE = ' ' TABS = 4 INDENT = SPACE * TABS BRACKETS = { '(': ')', ')': '(', '[': ']', ']': '[' } BRACKETS_CLOSE = (')', ']') FONT = myokit.gui.qtMonospaceFont() FONT.setPointSize(11) # Component and model headers STYLE_HEADER = QtGui.QTextCharFormat() # Comments STYLE_COMMENT = QtGui.QTextCharFormat() # Model annotations (including meta, labels, and units) STYLE_ANNOT_KEY = QtGui.QTextCharFormat() STYLE_ANNOT_VAL = QtGui.QTextCharFormat() # Language keywords STYLE_KEYWORD_1 = QtGui.QTextCharFormat() STYLE_KEYWORD_2 = QtGui.QTextCharFormat() # Literals: Numbers in model/protocol, Also booleans and strings in script STYLE_LITERAL = QtGui.QTextCharFormat() STYLE_INLINE_UNIT = QtGui.QTextCharFormat()