example app now works with Qt4 and Python2 again (#1302)

* example app now workw with Qt4 and Python2 again

* Example App is now part of the test suite

* Add initExample

* No scary warning when launching examples

* Fix scary examples.__main__ warning

* Use courier new font as its on all platforms

* Remove commented out code

* Add newline

* Updated docs, example app now tested

* Non-relative import for example app

* Proper importing now
This commit is contained in:
Ogi Moore 2020-10-19 11:51:12 -07:00 committed by GitHub
parent 65e90faec5
commit 7e57e07068
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 660 additions and 254 deletions

View File

@ -45,12 +45,13 @@ Examples
-------- --------
PyQtGraph includes an extensive set of examples that can be accessed by PyQtGraph includes an extensive set of examples that can be accessed by
running:: running either ``python -m pyqtgraph.examples`` [#editableInstall]_ or ::
import pyqtgraph.examples import pyqtgraph.examples
pyqtgraph.examples.run() pyqtgraph.examples.run()
Or by running ``python examples/`` from the source root. Or, if the project repository is local, you can run``python examples/`` from
the source root.
This will start a launcher with a list of available examples. Select an item This will start a launcher with a list of available examples. Select an item
from the list to view its source code and double-click an item to run the from the list to view its source code and double-click an item to run the
@ -84,3 +85,8 @@ How does it compare to...
(My experience with these libraries is somewhat outdated; please correct me if (My experience with these libraries is somewhat outdated; please correct me if
I am wrong here) I am wrong here)
.. rubric:: Footnotes
.. [#editableInstall] This method does not work when pyqtgraph is installed in editable mode.

483
examples/ExampleApp.py Normal file
View File

@ -0,0 +1,483 @@
import os
import sys
import subprocess
import pyqtgraph as pg
from pyqtgraph.python2_3 import basestring
from pyqtgraph.Qt import QtGui, QtCore, QT_LIB
from pyqtgraph.pgcollections import OrderedDict
path = os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, path)
app = pg.mkQApp()
if QT_LIB == 'PySide':
from exampleLoaderTemplate_pyside import Ui_Form
elif QT_LIB == 'PySide2':
from exampleLoaderTemplate_pyside2 import Ui_Form
elif QT_LIB == 'PyQt5':
from exampleLoaderTemplate_pyqt5 import Ui_Form
else:
from exampleLoaderTemplate_pyqt import Ui_Form
examples = OrderedDict([
('Command-line usage', 'CLIexample.py'),
('Basic Plotting', 'Plotting.py'),
('ImageView', 'ImageView.py'),
('ParameterTree', 'parametertree.py'),
('Crosshair / Mouse interaction', 'crosshair.py'),
('Data Slicing', 'DataSlicing.py'),
('Plot Customization', 'customPlot.py'),
('Timestamps on x axis', 'DateAxisItem.py'),
('Image Analysis', 'imageAnalysis.py'),
('ViewBox Features', 'ViewBoxFeatures.py'),
('Dock widgets', 'dockarea.py'),
('Console', 'ConsoleWidget.py'),
('Histograms', 'histogram.py'),
('Beeswarm plot', 'beeswarm.py'),
('Symbols', 'Symbols.py'),
('Auto-range', 'PlotAutoRange.py'),
('Remote Plotting', 'RemoteSpeedTest.py'),
('Scrolling plots', 'scrollingPlots.py'),
('HDF5 big data', 'hdf5.py'),
('Demos', OrderedDict([
('Optics', 'optics_demos.py'),
('Special relativity', 'relativity_demo.py'),
('Verlet chain', 'verlet_chain_demo.py'),
('Koch Fractal', 'fractal.py'),
])),
('GraphicsItems', OrderedDict([
('Scatter Plot', 'ScatterPlot.py'),
#('PlotItem', 'PlotItem.py'),
('IsocurveItem', 'isocurve.py'),
('GraphItem', 'GraphItem.py'),
('ErrorBarItem', 'ErrorBarItem.py'),
('FillBetweenItem', 'FillBetweenItem.py'),
('ImageItem - video', 'ImageItem.py'),
('ImageItem - draw', 'Draw.py'),
('Region-of-Interest', 'ROIExamples.py'),
('Bar Graph', 'BarGraphItem.py'),
('GraphicsLayout', 'GraphicsLayout.py'),
('LegendItem', 'Legend.py'),
('Text Item', 'text.py'),
('Linked Views', 'linkedViews.py'),
('Arrow', 'Arrow.py'),
('ViewBox', 'ViewBoxFeatures.py'),
('Custom Graphics', 'customGraphicsItem.py'),
('Labeled Graph', 'CustomGraphItem.py'),
])),
('Benchmarks', OrderedDict([
('Video speed test', 'VideoSpeedTest.py'),
('Line Plot update', 'PlotSpeedTest.py'),
('Scatter Plot update', 'ScatterPlotSpeedTest.py'),
('Multiple plots', 'MultiPlotSpeedTest.py'),
])),
('3D Graphics', OrderedDict([
('Volumetric', 'GLVolumeItem.py'),
('Isosurface', 'GLIsosurface.py'),
('Surface Plot', 'GLSurfacePlot.py'),
('Scatter Plot', 'GLScatterPlotItem.py'),
('Shaders', 'GLshaders.py'),
('Line Plot', 'GLLinePlotItem.py'),
('Mesh', 'GLMeshItem.py'),
('Image', 'GLImageItem.py'),
])),
('Widgets', OrderedDict([
('PlotWidget', 'PlotWidget.py'),
('SpinBox', 'SpinBox.py'),
('ConsoleWidget', 'ConsoleWidget.py'),
('Histogram / lookup table', 'HistogramLUT.py'),
('TreeWidget', 'TreeWidget.py'),
('ScatterPlotWidget', 'ScatterPlotWidget.py'),
('DataTreeWidget', 'DataTreeWidget.py'),
('GradientWidget', 'GradientWidget.py'),
('TableWidget', 'TableWidget.py'),
('ColorButton', 'ColorButton.py'),
#('CheckTable', '../widgets/CheckTable.py'),
#('VerticalLabel', '../widgets/VerticalLabel.py'),
('JoystickButton', 'JoystickButton.py'),
])),
('Flowcharts', 'Flowchart.py'),
('Custom Flowchart Nodes', 'FlowchartCustomNode.py'),
])
# based on https://github.com/art1415926535/PyQt5-syntax-highlighting
QRegExp = QtCore.QRegExp
QFont = QtGui.QFont
QColor = QtGui.QColor
QTextCharFormat = QtGui.QTextCharFormat
QSyntaxHighlighter = QtGui.QSyntaxHighlighter
def format(color, style=''):
"""
Return a QTextCharFormat with the given attributes.
"""
_color = QColor()
if type(color) is not str:
_color.setRgb(color[0], color[1], color[2])
else:
_color.setNamedColor(color)
_format = QTextCharFormat()
_format.setForeground(_color)
if 'bold' in style:
_format.setFontWeight(QFont.Bold)
if 'italic' in style:
_format.setFontItalic(True)
return _format
class LightThemeColors:
Red = "#B71C1C"
Pink = "#FCE4EC"
Purple = "#4A148C"
DeepPurple = "#311B92"
Indigo = "#1A237E"
Blue = "#0D47A1"
LightBlue = "#01579B"
Cyan = "#006064"
Teal = "#004D40"
Green = "#1B5E20"
LightGreen = "#33691E"
Lime = "#827717"
Yellow = "#F57F17"
Amber = "#FF6F00"
Orange = "#E65100"
DeepOrange = "#BF360C"
Brown = "#3E2723"
Grey = "#212121"
BlueGrey = "#263238"
class DarkThemeColors:
Red = "#F44336"
Pink = "#F48FB1"
Purple = "#CE93D8"
DeepPurple = "#B39DDB"
Indigo = "#9FA8DA"
Blue = "#90CAF9"
LightBlue = "#81D4FA"
Cyan = "#80DEEA"
Teal = "#80CBC4"
Green = "#A5D6A7"
LightGreen = "#C5E1A5"
Lime = "#E6EE9C"
Yellow = "#FFF59D"
Amber = "#FFE082"
Orange = "#FFCC80"
DeepOrange = "#FFAB91"
Brown = "#BCAAA4"
Grey = "#EEEEEE"
BlueGrey = "#B0BEC5"
LIGHT_STYLES = {
'keyword': format(LightThemeColors.Blue, 'bold'),
'operator': format(LightThemeColors.Red, 'bold'),
'brace': format(LightThemeColors.Purple),
'defclass': format(LightThemeColors.Indigo, 'bold'),
'string': format(LightThemeColors.Amber),
'string2': format(LightThemeColors.DeepPurple),
'comment': format(LightThemeColors.Green, 'italic'),
'self': format(LightThemeColors.Blue, 'bold'),
'numbers': format(LightThemeColors.Teal),
}
DARK_STYLES = {
'keyword': format(DarkThemeColors.Blue, 'bold'),
'operator': format(DarkThemeColors.Red, 'bold'),
'brace': format(DarkThemeColors.Purple),
'defclass': format(DarkThemeColors.Indigo, 'bold'),
'string': format(DarkThemeColors.Amber),
'string2': format(DarkThemeColors.DeepPurple),
'comment': format(DarkThemeColors.Green, 'italic'),
'self': format(DarkThemeColors.Blue, 'bold'),
'numbers': format(DarkThemeColors.Teal),
}
class PythonHighlighter(QSyntaxHighlighter):
"""Syntax highlighter for the Python language.
"""
# Python keywords
keywords = [
'and', 'assert', 'break', 'class', 'continue', 'def',
'del', 'elif', 'else', 'except', 'exec', 'finally',
'for', 'from', 'global', 'if', 'import', 'in',
'is', 'lambda', 'not', 'or', 'pass', 'print',
'raise', 'return', 'try', 'while', 'yield',
'None', 'True', 'False', 'async', 'await',
]
# Python operators
operators = [
'=',
# Comparison
'==', '!=', '<', '<=', '>', '>=',
# Arithmetic
'\+', '-', '\*', '/', '//', '\%', '\*\*',
# In-place
'\+=', '-=', '\*=', '/=', '\%=',
# Bitwise
'\^', '\|', '\&', '\~', '>>', '<<',
]
# Python braces
braces = [
'\{', '\}', '\(', '\)', '\[', '\]',
]
def __init__(self, document):
QSyntaxHighlighter.__init__(self, document)
# Multi-line strings (expression, flag, style)
# FIXME: The triple-quotes in these two lines will mess up the
# syntax highlighting from this point onward
self.tri_single = (QRegExp("'''"), 1, 'string2')
self.tri_double = (QRegExp('"""'), 2, 'string2')
rules = []
# Keyword, operator, and brace rules
rules += [(r'\b%s\b' % w, 0, 'keyword')
for w in PythonHighlighter.keywords]
rules += [(r'%s' % o, 0, 'operator')
for o in PythonHighlighter.operators]
rules += [(r'%s' % b, 0, 'brace')
for b in PythonHighlighter.braces]
# All other rules
rules += [
# 'self'
(r'\bself\b', 0, 'self'),
# 'def' followed by an identifier
(r'\bdef\b\s*(\w+)', 1, 'defclass'),
# 'class' followed by an identifier
(r'\bclass\b\s*(\w+)', 1, 'defclass'),
# Numeric literals
(r'\b[+-]?[0-9]+[lL]?\b', 0, 'numbers'),
(r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, 'numbers'),
(r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, 'numbers'),
# Double-quoted string, possibly containing escape sequences
(r'"[^"\\]*(\\.[^"\\]*)*"', 0, 'string'),
# Single-quoted string, possibly containing escape sequences
(r"'[^'\\]*(\\.[^'\\]*)*'", 0, 'string'),
# From '#' until a newline
(r'#[^\n]*', 0, 'comment'),
]
# Build a QRegExp for each pattern
self.rules = [(QRegExp(pat), index, fmt)
for (pat, index, fmt) in rules]
@property
def styles(self):
app = QtGui.QApplication.instance()
return DARK_STYLES if app.dark_mode else LIGHT_STYLES
def highlightBlock(self, text):
"""Apply syntax highlighting to the given block of text.
"""
# Do other syntax formatting
for expression, nth, format in self.rules:
index = expression.indexIn(text, 0)
format = self.styles[format]
while index >= 0:
# We actually want the index of the nth match
index = expression.pos(nth)
length = len(expression.cap(nth))
self.setFormat(index, length, format)
index = expression.indexIn(text, index + length)
self.setCurrentBlockState(0)
# Do multi-line strings
in_multiline = self.match_multiline(text, *self.tri_single)
if not in_multiline:
in_multiline = self.match_multiline(text, *self.tri_double)
def match_multiline(self, text, delimiter, in_state, style):
"""Do highlighting of multi-line strings. ``delimiter`` should be a
``QRegExp`` for triple-single-quotes or triple-double-quotes, and
``in_state`` should be a unique integer to represent the corresponding
state changes when inside those strings. Returns True if we're still
inside a multi-line string when this function is finished.
"""
# If inside triple-single quotes, start at 0
if self.previousBlockState() == in_state:
start = 0
add = 0
# Otherwise, look for the delimiter on this line
else:
start = delimiter.indexIn(text)
# Move past this match
add = delimiter.matchedLength()
# As long as there's a delimiter match on this line...
while start >= 0:
# Look for the ending delimiter
end = delimiter.indexIn(text, start + add)
# Ending delimiter on this line?
if end >= add:
length = end - start + add + delimiter.matchedLength()
self.setCurrentBlockState(0)
# No; multi-line string
else:
self.setCurrentBlockState(in_state)
length = len(text) - start + add
# Apply formatting
self.setFormat(start, length, self.styles[style])
# Look for the next match
start = delimiter.indexIn(text, start + length)
# Return True if still inside a multi-line string, False otherwise
if self.currentBlockState() == in_state:
return True
else:
return False
class ExampleLoader(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_Form()
self.cw = QtGui.QWidget()
self.setCentralWidget(self.cw)
self.ui.setupUi(self.cw)
self.setWindowTitle("PyQtGraph Examples")
self.codeBtn = QtGui.QPushButton('Run Edited Code')
self.codeLayout = QtGui.QGridLayout()
self.ui.codeView.setLayout(self.codeLayout)
self.hl = PythonHighlighter(self.ui.codeView.document())
app = QtGui.QApplication.instance()
if QT_LIB in ['PyQt5', 'PySide2']:
# Qt4 does not have a paletteChanged signal
app.paletteChanged.connect(self.updateTheme)
self.codeLayout.addItem(QtGui.QSpacerItem(100,100,QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding), 0, 0)
self.codeLayout.addWidget(self.codeBtn, 1, 1)
self.codeBtn.hide()
global examples
self.itemCache = []
self.populateTree(self.ui.exampleTree.invisibleRootItem(), examples)
self.ui.exampleTree.expandAll()
self.resize(1000,500)
self.show()
self.ui.splitter.setSizes([250,750])
self.ui.loadBtn.clicked.connect(self.loadFile)
self.ui.exampleTree.currentItemChanged.connect(self.showFile)
self.ui.exampleTree.itemDoubleClicked.connect(self.loadFile)
self.ui.codeView.textChanged.connect(self.codeEdited)
self.codeBtn.clicked.connect(self.runEditedCode)
def simulate_black_mode(self):
"""
used to simulate MacOS "black mode" on other platforms
intended for debug only, as it manage only the QPlainTextEdit
"""
# first, a dark background
c = QtGui.QColor('#171717')
p = self.ui.codeView.palette()
p.setColor(QtGui.QPalette.Active, QtGui.QPalette.Base, c)
p.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Base, c)
self.ui.codeView.setPalette(p)
# then, a light font
f = QtGui.QTextCharFormat()
f.setForeground(QtGui.QColor('white'))
self.ui.codeView.setCurrentCharFormat(f)
# finally, override application automatic detection
app = QtGui.QApplication.instance()
app.dark_mode = True
def updateTheme(self):
self.hl = PythonHighlighter(self.ui.codeView.document())
def populateTree(self, root, examples):
for key, val in examples.items():
item = QtGui.QTreeWidgetItem([key])
self.itemCache.append(item) # PyQt 4.9.6 no longer keeps references to these wrappers,
# so we need to make an explicit reference or else the .file
# attribute will disappear.
if isinstance(val, basestring):
item.file = val
else:
self.populateTree(item, val)
root.addChild(item)
def currentFile(self):
item = self.ui.exampleTree.currentItem()
if hasattr(item, 'file'):
global path
return os.path.join(path, item.file)
return None
def loadFile(self, edited=False):
extra = []
qtLib = str(self.ui.qtLibCombo.currentText())
gfxSys = str(self.ui.graphicsSystemCombo.currentText())
if qtLib != 'default':
extra.append(qtLib.lower())
elif gfxSys != 'default':
extra.append(gfxSys)
if edited:
path = os.path.abspath(os.path.dirname(__file__))
proc = subprocess.Popen([sys.executable, '-'] + extra, stdin=subprocess.PIPE, cwd=path)
code = str(self.ui.codeView.toPlainText()).encode('UTF-8')
proc.stdin.write(code)
proc.stdin.close()
else:
fn = self.currentFile()
if fn is None:
return
if sys.platform.startswith('win'):
os.spawnl(os.P_NOWAIT, sys.executable, '"'+sys.executable+'"', '"' + fn + '"', *extra)
else:
os.spawnl(os.P_NOWAIT, sys.executable, sys.executable, fn, *extra)
def showFile(self):
fn = self.currentFile()
if fn is None:
self.ui.codeView.clear()
return
if os.path.isdir(fn):
fn = os.path.join(fn, '__main__.py')
text = open(fn).read()
self.ui.codeView.setPlainText(text)
self.ui.loadedFileLabel.setText(fn)
self.codeBtn.hide()
def codeEdited(self):
self.codeBtn.show()
def runEditedCode(self):
self.loadFile(edited=True)
def main():
app = pg.mkQApp()
loader = ExampleLoader()
app.exec_()
# or condition so pytest runs ExampleApp as part of test suite
if __name__ == '__main__':
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
main()

View File

@ -1 +1 @@
from .__main__ import run from .ExampleApp import main as run

View File

@ -1,159 +1,15 @@
import sys, os import sys, os
if __name__ == "__main__" and (__package__ is None or __package__==''):
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parent_dir)
import examples
__package__ = "examples"
import pyqtgraph as pg import pyqtgraph as pg
import subprocess from pyqtgraph.Qt import QtCore, QtGui
from pyqtgraph.python2_3 import basestring
from pyqtgraph.Qt import QtGui, QT_LIB
from .utils import buildFileList, path, examples
from .syntax import PythonHighlighter
if QT_LIB == 'PySide':
from .exampleLoaderTemplate_pyside import Ui_Form
elif QT_LIB == 'PySide2':
from .exampleLoaderTemplate_pyside2 import Ui_Form
elif QT_LIB == 'PyQt5':
from .exampleLoaderTemplate_pyqt5 import Ui_Form
else:
from .exampleLoaderTemplate_pyqt import Ui_Form
class App(QtGui.QApplication):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.paletteChanged.connect(self.onPaletteChange)
self.onPaletteChange(self.palette())
def onPaletteChange(self, palette):
self.dark_mode = palette.base().color().name().lower() != "#ffffff"
class ExampleLoader(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_Form()
self.cw = QtGui.QWidget()
self.setCentralWidget(self.cw)
self.ui.setupUi(self.cw)
self.setWindowTitle("PyQtGraph Examples")
self.codeBtn = QtGui.QPushButton('Run Edited Code')
self.codeLayout = QtGui.QGridLayout()
self.ui.codeView.setLayout(self.codeLayout)
self.hl = PythonHighlighter(self.ui.codeView.document())
app = QtGui.QApplication.instance()
app.paletteChanged.connect(self.updateTheme)
self.codeLayout.addItem(QtGui.QSpacerItem(100,100,QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding), 0, 0)
self.codeLayout.addWidget(self.codeBtn, 1, 1)
self.codeBtn.hide()
global examples
self.itemCache = []
self.populateTree(self.ui.exampleTree.invisibleRootItem(), examples)
self.ui.exampleTree.expandAll()
self.resize(1000,500)
self.show()
self.ui.splitter.setSizes([250,750])
self.ui.loadBtn.clicked.connect(self.loadFile)
self.ui.exampleTree.currentItemChanged.connect(self.showFile)
self.ui.exampleTree.itemDoubleClicked.connect(self.loadFile)
self.ui.codeView.textChanged.connect(self.codeEdited)
self.codeBtn.clicked.connect(self.runEditedCode)
def simulate_black_mode(self):
"""
used to simulate MacOS "black mode" on other platforms
intended for debug only, as it manage only the QPlainTextEdit
"""
# first, a dark background
c = QtGui.QColor('#171717')
p = self.ui.codeView.palette()
p.setColor(QtGui.QPalette.Active, QtGui.QPalette.Base, c)
p.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Base, c)
self.ui.codeView.setPalette(p)
# then, a light font
f = QtGui.QTextCharFormat()
f.setForeground(QtGui.QColor('white'))
self.ui.codeView.setCurrentCharFormat(f)
# finally, override application automatic detection
app = QtGui.QApplication.instance()
app.dark_mode = True
def updateTheme(self):
self.hl = PythonHighlighter(self.ui.codeView.document())
def populateTree(self, root, examples):
for key, val in examples.items():
item = QtGui.QTreeWidgetItem([key])
self.itemCache.append(item) # PyQt 4.9.6 no longer keeps references to these wrappers,
# so we need to make an explicit reference or else the .file
# attribute will disappear.
if isinstance(val, basestring):
item.file = val
else:
self.populateTree(item, val)
root.addChild(item)
def currentFile(self):
item = self.ui.exampleTree.currentItem()
if hasattr(item, 'file'):
global path
return os.path.join(path, item.file)
return None
def loadFile(self, edited=False):
extra = []
qtLib = str(self.ui.qtLibCombo.currentText())
gfxSys = str(self.ui.graphicsSystemCombo.currentText())
if qtLib != 'default':
extra.append(qtLib.lower())
elif gfxSys != 'default':
extra.append(gfxSys)
if edited:
path = os.path.abspath(os.path.dirname(__file__))
proc = subprocess.Popen([sys.executable, '-'] + extra, stdin=subprocess.PIPE, cwd=path)
code = str(self.ui.codeView.toPlainText()).encode('UTF-8')
proc.stdin.write(code)
proc.stdin.close()
else:
fn = self.currentFile()
if fn is None:
return
if sys.platform.startswith('win'):
os.spawnl(os.P_NOWAIT, sys.executable, '"'+sys.executable+'"', '"' + fn + '"', *extra)
else:
os.spawnl(os.P_NOWAIT, sys.executable, sys.executable, fn, *extra)
def showFile(self):
fn = self.currentFile()
if fn is None:
self.ui.codeView.clear()
return
if os.path.isdir(fn):
fn = os.path.join(fn, '__main__.py')
text = open(fn).read()
self.ui.codeView.setPlainText(text)
self.ui.loadedFileLabel.setText(fn)
self.codeBtn.hide()
def codeEdited(self):
self.codeBtn.show()
def runEditedCode(self):
self.loadFile(edited=True)
def run():
app = App([])
loader = ExampleLoader()
app.exec_()
if __name__ == '__main__': if __name__ == '__main__':
if __package__ is None or __package__ == "":
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parent_dir)
import examples
__package__ = "examples"
from .ExampleApp import main as run
run() run()

View File

@ -131,7 +131,7 @@
<widget class="QPlainTextEdit" name="codeView"> <widget class="QPlainTextEdit" name="codeView">
<property name="font"> <property name="font">
<font> <font>
<family>FreeMono</family> <family>Courier New</family>
</font> </font>
</property> </property>
</widget> </widget>

View File

@ -79,7 +79,7 @@ class Ui_Form(object):
self.verticalLayout.addWidget(self.loadedFileLabel) self.verticalLayout.addWidget(self.loadedFileLabel)
self.codeView = QtGui.QPlainTextEdit(self.widget1) self.codeView = QtGui.QPlainTextEdit(self.widget1)
font = QtGui.QFont() font = QtGui.QFont()
font.setFamily(_fromUtf8("FreeMono")) font.setFamily(_fromUtf8("Courier New"))
self.codeView.setFont(font) self.codeView.setFont(font)
self.codeView.setObjectName(_fromUtf8("codeView")) self.codeView.setObjectName(_fromUtf8("codeView"))
self.verticalLayout.addWidget(self.codeView) self.verticalLayout.addWidget(self.codeView)

View File

@ -1,13 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'examples/exampleLoaderTemplate.ui' # Form implementation generated from reading ui file 'exampleLoaderTemplate.ui'
# #
# Created by: PyQt5 UI code generator 5.6 # Created by: PyQt5 UI code generator 5.15.1
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object): class Ui_Form(object):
def setupUi(self, Form): def setupUi(self, Form):
Form.setObjectName("Form") Form.setObjectName("Form")
@ -67,7 +70,7 @@ class Ui_Form(object):
self.verticalLayout.addWidget(self.loadedFileLabel) self.verticalLayout.addWidget(self.loadedFileLabel)
self.codeView = QtWidgets.QPlainTextEdit(self.widget1) self.codeView = QtWidgets.QPlainTextEdit(self.widget1)
font = QtGui.QFont() font = QtGui.QFont()
font.setFamily("FreeMono") font.setFamily("Courier New")
self.codeView.setFont(font) self.codeView.setFont(font)
self.codeView.setObjectName("codeView") self.codeView.setObjectName("codeView")
self.verticalLayout.addWidget(self.codeView) self.verticalLayout.addWidget(self.codeView)
@ -91,4 +94,3 @@ class Ui_Form(object):
self.label_2.setText(_translate("Form", "Graphics System:")) self.label_2.setText(_translate("Form", "Graphics System:"))
self.label.setText(_translate("Form", "Qt Library:")) self.label.setText(_translate("Form", "Qt Library:"))
self.loadBtn.setText(_translate("Form", "Run Example")) self.loadBtn.setText(_translate("Form", "Run Example"))

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'examples/exampleLoaderTemplate.ui' # Form implementation generated from reading ui file 'exampleLoaderTemplate.ui'
# #
# Created: Fri Feb 16 20:29:46 2018 # Created: Sun Oct 18 21:53:22 2020
# by: pyside-uic 0.2.15 running on PySide 1.2.4 # by: pyside-uic 0.2.15 running on PySide 1.2.4
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@ -68,7 +68,7 @@ class Ui_Form(object):
self.verticalLayout.addWidget(self.loadedFileLabel) self.verticalLayout.addWidget(self.loadedFileLabel)
self.codeView = QtGui.QPlainTextEdit(self.widget1) self.codeView = QtGui.QPlainTextEdit(self.widget1)
font = QtGui.QFont() font = QtGui.QFont()
font.setFamily("FreeMono") font.setFamily("Courier New")
self.codeView.setFont(font) self.codeView.setFont(font)
self.codeView.setObjectName("codeView") self.codeView.setObjectName("codeView")
self.verticalLayout.addWidget(self.codeView) self.verticalLayout.addWidget(self.codeView)

View File

@ -1,94 +1,129 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'examples/exampleLoaderTemplate.ui' ################################################################################
# ## Form generated from reading UI file 'exampleLoaderTemplate.ui'
# Created: Fri Feb 16 20:30:37 2018 ##
# by: pyside2-uic 2.0.0 running on PySide2 2.0.0~alpha0 ## Created by: Qt User Interface Compiler version 5.15.0
# ##
# WARNING! All changes made in this file will be lost! ## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide2.QtCore import (QCoreApplication, QDate, QDateTime, QMetaObject,
QObject, QPoint, QRect, QSize, QTime, QUrl, Qt)
from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont,
QFontDatabase, QIcon, QKeySequence, QLinearGradient, QPalette, QPainter,
QPixmap, QRadialGradient)
from PySide2.QtWidgets import *
from PySide2 import QtCore, QtGui, QtWidgets
class Ui_Form(object): class Ui_Form(object):
def setupUi(self, Form): def setupUi(self, Form):
Form.setObjectName("Form") if not Form.objectName():
Form.setObjectName(u"Form")
Form.resize(846, 552) Form.resize(846, 552)
self.gridLayout_2 = QtWidgets.QGridLayout(Form) self.gridLayout_2 = QGridLayout(Form)
self.gridLayout_2.setObjectName("gridLayout_2") self.gridLayout_2.setObjectName(u"gridLayout_2")
self.splitter = QtWidgets.QSplitter(Form) self.splitter = QSplitter(Form)
self.splitter.setOrientation(QtCore.Qt.Horizontal) self.splitter.setObjectName(u"splitter")
self.splitter.setObjectName("splitter") self.splitter.setOrientation(Qt.Horizontal)
self.widget = QtWidgets.QWidget(self.splitter) self.widget = QWidget(self.splitter)
self.widget.setObjectName("widget") self.widget.setObjectName(u"widget")
self.gridLayout = QtWidgets.QGridLayout(self.widget) self.gridLayout = QGridLayout(self.widget)
self.gridLayout.setObjectName(u"gridLayout")
self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout") self.exampleTree = QTreeWidget(self.widget)
self.exampleTree = QtWidgets.QTreeWidget(self.widget) __qtreewidgetitem = QTreeWidgetItem()
self.exampleTree.setObjectName("exampleTree") __qtreewidgetitem.setText(0, u"1");
self.exampleTree.headerItem().setText(0, "1") self.exampleTree.setHeaderItem(__qtreewidgetitem)
self.exampleTree.setObjectName(u"exampleTree")
self.exampleTree.header().setVisible(False) self.exampleTree.header().setVisible(False)
self.gridLayout.addWidget(self.exampleTree, 0, 0, 1, 2) self.gridLayout.addWidget(self.exampleTree, 0, 0, 1, 2)
self.graphicsSystemCombo = QtWidgets.QComboBox(self.widget)
self.graphicsSystemCombo.setObjectName("graphicsSystemCombo") self.graphicsSystemCombo = QComboBox(self.widget)
self.graphicsSystemCombo.addItem("") self.graphicsSystemCombo.addItem("")
self.graphicsSystemCombo.addItem("") self.graphicsSystemCombo.addItem("")
self.graphicsSystemCombo.addItem("") self.graphicsSystemCombo.addItem("")
self.graphicsSystemCombo.addItem("") self.graphicsSystemCombo.addItem("")
self.graphicsSystemCombo.setObjectName(u"graphicsSystemCombo")
self.gridLayout.addWidget(self.graphicsSystemCombo, 2, 1, 1, 1) self.gridLayout.addWidget(self.graphicsSystemCombo, 2, 1, 1, 1)
self.qtLibCombo = QtWidgets.QComboBox(self.widget)
self.qtLibCombo.setObjectName("qtLibCombo") self.qtLibCombo = QComboBox(self.widget)
self.qtLibCombo.addItem("") self.qtLibCombo.addItem("")
self.qtLibCombo.addItem("") self.qtLibCombo.addItem("")
self.qtLibCombo.addItem("") self.qtLibCombo.addItem("")
self.qtLibCombo.addItem("") self.qtLibCombo.addItem("")
self.qtLibCombo.addItem("") self.qtLibCombo.addItem("")
self.qtLibCombo.setObjectName(u"qtLibCombo")
self.gridLayout.addWidget(self.qtLibCombo, 1, 1, 1, 1) self.gridLayout.addWidget(self.qtLibCombo, 1, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(self.widget)
self.label_2.setObjectName("label_2") self.label_2 = QLabel(self.widget)
self.label_2.setObjectName(u"label_2")
self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1) self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
self.label = QtWidgets.QLabel(self.widget)
self.label.setObjectName("label") self.label = QLabel(self.widget)
self.label.setObjectName(u"label")
self.gridLayout.addWidget(self.label, 1, 0, 1, 1) self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
self.loadBtn = QtWidgets.QPushButton(self.widget)
self.loadBtn.setObjectName("loadBtn") self.loadBtn = QPushButton(self.widget)
self.loadBtn.setObjectName(u"loadBtn")
self.gridLayout.addWidget(self.loadBtn, 3, 1, 1, 1) self.gridLayout.addWidget(self.loadBtn, 3, 1, 1, 1)
self.widget1 = QtWidgets.QWidget(self.splitter)
self.widget1.setObjectName("widget1") self.splitter.addWidget(self.widget)
self.verticalLayout = QtWidgets.QVBoxLayout(self.widget1) self.widget1 = QWidget(self.splitter)
self.widget1.setObjectName(u"widget1")
self.verticalLayout = QVBoxLayout(self.widget1)
self.verticalLayout.setObjectName(u"verticalLayout")
self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout") self.loadedFileLabel = QLabel(self.widget1)
self.loadedFileLabel = QtWidgets.QLabel(self.widget1) self.loadedFileLabel.setObjectName(u"loadedFileLabel")
font = QtGui.QFont() font = QFont()
font.setWeight(75)
font.setBold(True) font.setBold(True)
font.setWeight(75)
self.loadedFileLabel.setFont(font) self.loadedFileLabel.setFont(font)
self.loadedFileLabel.setText("") self.loadedFileLabel.setAlignment(Qt.AlignCenter)
self.loadedFileLabel.setAlignment(QtCore.Qt.AlignCenter)
self.loadedFileLabel.setObjectName("loadedFileLabel")
self.verticalLayout.addWidget(self.loadedFileLabel) self.verticalLayout.addWidget(self.loadedFileLabel)
self.codeView = QtWidgets.QPlainTextEdit(self.widget1)
font = QtGui.QFont() self.codeView = QPlainTextEdit(self.widget1)
font.setFamily("FreeMono") self.codeView.setObjectName(u"codeView")
self.codeView.setFont(font) font1 = QFont()
self.codeView.setObjectName("codeView") font1.setFamily(u"Courier New")
self.codeView.setFont(font1)
self.verticalLayout.addWidget(self.codeView) self.verticalLayout.addWidget(self.codeView)
self.splitter.addWidget(self.widget1)
self.gridLayout_2.addWidget(self.splitter, 0, 0, 1, 1) self.gridLayout_2.addWidget(self.splitter, 0, 0, 1, 1)
self.retranslateUi(Form) self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
QMetaObject.connectSlotsByName(Form)
# setupUi
def retranslateUi(self, Form): def retranslateUi(self, Form):
Form.setWindowTitle(QtWidgets.QApplication.translate("Form", "Form", None, -1)) Form.setWindowTitle(QCoreApplication.translate("Form", u"PyQtGraph", None))
self.graphicsSystemCombo.setItemText(0, QtWidgets.QApplication.translate("Form", "default", None, -1)) self.graphicsSystemCombo.setItemText(0, QCoreApplication.translate("Form", u"default", None))
self.graphicsSystemCombo.setItemText(1, QtWidgets.QApplication.translate("Form", "native", None, -1)) self.graphicsSystemCombo.setItemText(1, QCoreApplication.translate("Form", u"native", None))
self.graphicsSystemCombo.setItemText(2, QtWidgets.QApplication.translate("Form", "raster", None, -1)) self.graphicsSystemCombo.setItemText(2, QCoreApplication.translate("Form", u"raster", None))
self.graphicsSystemCombo.setItemText(3, QtWidgets.QApplication.translate("Form", "opengl", None, -1)) self.graphicsSystemCombo.setItemText(3, QCoreApplication.translate("Form", u"opengl", None))
self.qtLibCombo.setItemText(0, QtWidgets.QApplication.translate("Form", "default", None, -1))
self.qtLibCombo.setItemText(1, QtWidgets.QApplication.translate("Form", "PyQt4", None, -1)) self.qtLibCombo.setItemText(0, QCoreApplication.translate("Form", u"default", None))
self.qtLibCombo.setItemText(2, QtWidgets.QApplication.translate("Form", "PySide", None, -1)) self.qtLibCombo.setItemText(1, QCoreApplication.translate("Form", u"PyQt4", None))
self.qtLibCombo.setItemText(3, QtWidgets.QApplication.translate("Form", "PyQt5", None, -1)) self.qtLibCombo.setItemText(2, QCoreApplication.translate("Form", u"PySide", None))
self.qtLibCombo.setItemText(4, QtWidgets.QApplication.translate("Form", "PySide2", None, -1)) self.qtLibCombo.setItemText(3, QCoreApplication.translate("Form", u"PyQt5", None))
self.label_2.setText(QtWidgets.QApplication.translate("Form", "Graphics System:", None, -1)) self.qtLibCombo.setItemText(4, QCoreApplication.translate("Form", u"PySide2", None))
self.label.setText(QtWidgets.QApplication.translate("Form", "Qt Library:", None, -1))
self.loadBtn.setText(QtWidgets.QApplication.translate("Form", "Run Example", None, -1)) self.label_2.setText(QCoreApplication.translate("Form", u"Graphics System:", None))
self.label.setText(QCoreApplication.translate("Form", u"Qt Library:", None))
self.loadBtn.setText(QCoreApplication.translate("Form", u"Run Example", None))
self.loadedFileLabel.setText("")
# retranslateUi

View File

@ -0,0 +1,13 @@
import initExample ## Add path to library (just for examples; you do not need this)
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
from examples.ExampleApp import ExampleLoader
loader = ExampleLoader()
if __name__ == "__main__":
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,8 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import print_function, division, absolute_import from __future__ import print_function, division, absolute_import
from . import utils
from collections import namedtuple from collections import namedtuple
from pyqtgraph import Qt from pyqtgraph import Qt
from pyqtgraph.python2_3 import basestring
from .ExampleApp import examples
import errno import errno
import importlib import importlib
import itertools import itertools
@ -10,37 +12,27 @@ import pytest
import os, sys import os, sys
import subprocess import subprocess
import time import time
if __name__ == "__main__" and (__package__ is None or __package__==''):
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parent_dir)
import examples
__package__ = "examples"
def buildFileList(examples, files=None):
if files is None:
files = [("Example App", "test_ExampleApp.py")]
for key, val in examples.items():
if isinstance(val, basestring):
files.append((key,val))
else:
buildFileList(val, files)
return files
path = os.path.abspath(os.path.dirname(__file__)) path = os.path.abspath(os.path.dirname(__file__))
files = sorted(set(buildFileList(examples)))
# printing on travis ci frequently leads to "interrupted system call" errors.
# as a workaround, we overwrite the built-in print function (bleh)
if os.getenv('TRAVIS') is not None:
if sys.version_info[0] < 3:
import __builtin__ as builtins
else:
import builtins
def flaky_print(*args):
"""Wrapper for print that retries in case of IOError.
"""
count = 0
while count < 5:
count += 1
try:
orig_print(*args)
break
except IOError:
if count >= 5:
raise
pass
orig_print = builtins.print
builtins.print = flaky_print
print("Installed wrapper for flaky print.")
files = sorted(set(utils.buildFileList(utils.examples)))
frontends = { frontends = {
Qt.PYQT4: False, Qt.PYQT4: False,
Qt.PYQT5: False, Qt.PYQT5: False,

View File

@ -345,6 +345,24 @@ if m is not None and list(map(int, m.groups())) < versionReq:
print(list(map(int, m.groups()))) print(list(map(int, m.groups())))
raise Exception('pyqtgraph requires Qt version >= %d.%d (your version is %s)' % (versionReq[0], versionReq[1], QtVersion)) raise Exception('pyqtgraph requires Qt version >= %d.%d (your version is %s)' % (versionReq[0], versionReq[1], QtVersion))
class App(QtGui.QApplication):
def __init__(self, *args, **kwargs):
super(App, self).__init__(*args, **kwargs)
if QT_LIB in ['PyQt5', 'PySide2']:
# qt4 does not have paletteChanged signal!
self.paletteChanged.connect(self.onPaletteChange)
self.onPaletteChange(self.palette())
def onPaletteChange(self, palette):
if QT_LIB in ['PyQt4', 'PySide']:
# Qt4 this is a QString
color = str(palette.base().color().name())
else:
# Qt5 has this as a str
color = palette.base().color().name()
self.dark_mode = color.lower() != "#ffffff"
QAPP = None QAPP = None
def mkQApp(name=None): def mkQApp(name=None):
@ -359,7 +377,8 @@ def mkQApp(name=None):
global QAPP global QAPP
QAPP = QtGui.QApplication.instance() QAPP = QtGui.QApplication.instance()
if QAPP is None: if QAPP is None:
QAPP = QtGui.QApplication(sys.argv or ["pyqtgraph"]) QAPP = App(sys.argv or ["pyqtgraph"])
if name is not None: if name is not None:
QAPP.setApplicationName(name) QAPP.setApplicationName(name)
return QAPP return QAPP