Прежде всего, я новичок в Pyside и приветствую конструктивные отзывы.Я также ценю любого, кто смотрит на это и пытается запустить мой код.Заранее спасибо!
Я должен также упомянуть, что я использую https://github.com/mottosso/Qt.py в качестве импорта Qt в моем коде.
Я создаю графический интерфейс для выполнения некоторых аппаратных модульных тестов.Этот графический интерфейс имеет встроенный график FigureCanvas для отображения данных, полученных с оборудования, когда пользователь нажимает кнопку.Я создаю подкласс unittest.TestCase для вставки PlotCanvas (пользовательский класс FigureCanvas с некоторыми простыми методами).Затем я создаю мастер с несколькими страницами, каждая из которых имеет независимый график.
Теперь это работает, если я не делаю подкласс unittest.TestCase и просто создаю статический plot_canvas.См. DEBUG_STATE = 1 в моем главном блоке.
# Uses statically defined plot canvas, no subclassing
class TestPage1(unittest.TestCase):
PLOT_COLS = 1
PLOT_SIZE = (12, 6)
plot_canvas = PlotCanvas(size=PLOT_SIZE, cols=PLOT_COLS)
def test_01(self):
x = np.linspace(0,10,50)
self.plot_canvas.plot(x, 2*x)
Но если я использую StaticPlottingTestCase, код практически идентичен, я не могу вставить несколько страниц.Холст не отображается на первой странице.Я думаю, что это как-то связано с QWizard, но я не уверен.См. DEBUG_STATE = 3.
class StaticPlottingTestCase(unittest.TestCase):
# Static definition
PLOT_COLS = 1
PLOT_SIZE = (12, 6)
plot_canvas = PlotCanvas(size=PLOT_SIZE, cols=PLOT_COLS)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
В попытке исправить это я создал другой класс InstancePlottingTestCase, для которого plot_canvas определен как переменная класса.Теперь сюжет появляется на обеих страницах, но черчение ничего не дает.См. DEBUG_STATE = 4.
class InstancePlottingTestCase(unittest.TestCase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Instance definition
self.plot_canvas = PlotCanvas()
Я потратил много часов на исследования и на данный момент близок к тому, чтобы использовать мой оригинальный подход, хотя код не такой чистый.Я внимательно изучил документацию Pyside.
Это мой минимальный рабочий пример.Переменная DEBUG_STATE в моем главном блоке может иметь разные значения для имитации проблем, с которыми я сталкиваюсь.
DEBUG_STATE = 1: Мой базовый подход.Это работает, как и ожидалось.
DEBUG_STATE = 2: одна страница StaticPlottingTestCase.Это работает.
DEBUG_STATE = 3: две страницы StaticPlottingTestCase.На первой странице нет сюжета.Построение на первой странице, похоже, влияет на вторую.
DEBUG_STATE = 4: две страницы InstancePlottingTestCase.Графики отображаются, но они не могут быть нарисованы.
ОБНОВЛЕНИЕ: я обнаружил, что если я добавлю две страницы с одинаковыми именами (DEBUG_STATE = 6), результат будет таким же, как DEBUG_STATE = 3. Интересно...
Полный код:
import io
import sys
import unittest
from unittest.runner import TextTestResult
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_qt5agg import \
FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from Qt import QtCore, QtGui, QtWidgets
from Qt.QtCore import QCoreApplication
from Qt.QtWidgets import (QFormLayout, QPushButton, QVBoxLayout, QWizard,
QWizardPage)
def discover_tests(test_case):
"""Compile a list of tests for the given test case.
Parameters
----------
test_case : TestCase
Returns
-------
names : list
Test names
"""
return [a for a in dir(test_case) if a.startswith('test_')]
class PlotCanvas(FigureCanvas):
def __init__(self, figsize=(8,6), *args, **kwargs):
# Initialize the figure first
self.fig = Figure(figsize=figsize)
self.fig.set_facecolor('white')
# Create the canvas with the created figure
super(PlotCanvas, self).__init__(self.fig)
# Add axes to the figure
self.fig.add_subplot(111)
self.ax = self.fig.get_axes()[0]
def plot(self, x, y, *args, **kwargs):
self.ax.clear()
self.ax.plot(x, y)
self.draw()
class StaticPlottingTestCase(unittest.TestCase):
# Static definition
PLOT_COLS = 1
PLOT_SIZE = (12, 6)
plot_canvas = PlotCanvas(size=PLOT_SIZE, cols=PLOT_COLS)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class InstancePlottingTestCase(unittest.TestCase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Instance definition
self.plot_canvas = PlotCanvas()
class TestPage(QWizardPage):
def __init__(self, unittest_class, static_canvas=True, *args, **kwargs):
super(TestPage, self).__init__(*args, **kwargs)
# Assign the unit test class
self.unittest_class = unittest_class
# Define overall layout as an hbox
top_hbox = QtWidgets.QHBoxLayout()
# Populate left side with unittest buttons
unittest_vbox = QVBoxLayout()
formLayout = QFormLayout()
self.buttons = {}
unittest_names = discover_tests(unittest_class)
for name in unittest_names:
button = QPushButton('Push to Start', self)
button.setCheckable(True)
button.unittest_name = name
button.setMinimumWidth(100)
button.clicked.connect(self.onButton)
self.buttons[name] = button
formLayout.addRow(name + ':', button)
unittest_vbox.addLayout(formLayout)
# Central plot
plot_vbox = QVBoxLayout()
# Static plot
if static_canvas:
self.plot_canvas = self.unittest_class.plot_canvas
else:
self.plot_canvas = self.unittest_class().plot_canvas
plot_vbox.addWidget(self.plot_canvas)
# Set overall layout
top_hbox.addLayout(unittest_vbox)
top_hbox.addLayout(plot_vbox)
self.setLayout(top_hbox)
def onButton(self):
""" Create a TestSuite with just the clicked test.
"""
name = self.sender().unittest_name
self.runTests({name : self.buttons[name]})
# print(self.plot_canvas.figure.)
def runTests(self, buttons):
""" Run the unit tests corresponding to the dict buttons.
Buttons are used instead of names because the isChecked() value provides
pass/fail information about the test, whereas a name is just...a name.
"""
suite = unittest.TestSuite()
stream = io.StringIO()
for name, button in buttons.items():
suite.addTest(self.unittest_class(name))
runner = unittest.TextTestRunner(
stream=stream,
verbosity=2,
failfast=True,
resultclass=TextTestResult,
)
runner.run(suite)
if __name__ == '__main__':
# Uses statically defined plot canvas, no subclassing
class TestPage1(unittest.TestCase):
PLOT_COLS = 1
PLOT_SIZE = (12, 6)
plot_canvas = PlotCanvas(size=PLOT_SIZE, cols=PLOT_COLS)
def test_01(self):
x = np.linspace(0,10,50)
self.plot_canvas.plot(x, 2*x)
class TestPage1b(unittest.TestCase):
PLOT_COLS = 1
PLOT_SIZE = (12, 6)
plot_canvas = PlotCanvas(size=PLOT_SIZE, cols=PLOT_COLS)
def test_01(self):
x = np.linspace(0,10,50)
self.plot_canvas.plot(x, 2*x)
# Uses statically defined canvas in subclass. Should act the same way as TestPage1.
class TestPage2(StaticPlottingTestCase):
def test_01(self):
x = np.linspace(0,10,50)
self.plot_canvas.plot(x, 3*x)
class TestPage2b(StaticPlottingTestCase):
def test_01(self):
x = np.linspace(0,10,50)
self.plot_canvas.plot(x, 3*x)
# Uses instance defined canvas. This doesn't plot anything.
class TestPage3(InstancePlottingTestCase):
def test_01(self):
x = np.linspace(0,10,50)
y = x ^ 2
self.plot_canvas.plot(x, y)
class TestPage3b(InstancePlottingTestCase):
def test_01(self):
x = np.linspace(0,10,50)
y = x ^ 2
self.plot_canvas.plot(x, y)
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
print('Conjuring test wizard...')
wizard = QWizard()
wizard.setWizardStyle(QtWidgets.QWizard.ModernStyle)
# Debugging
DEBUG_STATE = 6
# Adding TestPage1 works as expected. The plots are independent.
if DEBUG_STATE == 1:
wizard.addPage(TestPage(TestPage1, static_canvas=True))
wizard.addPage(TestPage(TestPage1b, static_canvas=True))
# A single TestPage2 works...
elif DEBUG_STATE == 2:
wizard.addPage(TestPage(TestPage2, static_canvas=True))
# ...but adding two of them does not work...
# The canvas doesn't show up on the first page.
# I think this has something to do with the widgets layout.
elif DEBUG_STATE == 3:
wizard.addPage(TestPage(TestPage2, static_canvas=True))
wizard.addPage(TestPage(TestPage2b, static_canvas=True))
# Making self.canvas a class variable generates the plot, but now plotting doesn't do anything
elif DEBUG_STATE == 4:
wizard.addPage(TestPage(TestPage3, static_canvas=False))
wizard.addPage(TestPage(TestPage3b, static_canvas=False))
# Plotting works on the statically defined canvas, but not on the class-defined one.
elif DEBUG_STATE == 5:
wizard.addPage(TestPage(TestPage2, static_canvas=True))
wizard.addPage(TestPage(TestPage3, static_canvas=False))
# A clue! Adding a page with the same name mimics DEBUG_STATE == 3...
elif DEBUG_STATE == 6:
wizard.addPage(TestPage(TestPage1, static_canvas=True))
wizard.addPage(TestPage(TestPage1, static_canvas=True))
wizard.setFixedSize(1200, 800)
wizard.show()
app.exec_()