1616import sys
1717import subprocess
1818import platform
19- from qtpy .QtWidgets import (
20- QApplication ,
21- QWidget ,
22- QMainWindow ,
23- QVBoxLayout ,
24- QFormLayout ,
25- QCheckBox ,
26- QGroupBox ,
27- QGridLayout ,
28- QToolButton ,
29- QStyle ,
30- QToolBar ,
31- QAction ,
32- QMessageBox ,
33- )
34- from qtpy .QtGui import QIcon , QPixmap
35- from qtpy .QtCore import Qt , QSize , QTimer
36- from qtpy import PYQT5
19+ import argparse
20+ import inspect
21+
22+ from qtpy import QtWidgets as QW
23+ from qtpy import QtGui as QG
24+ from qtpy import QtCore as QC
25+ from qtpy import PYQT5 , PYSIDE2
26+
3727from qwt import QwtPlot
3828
29+ if PYSIDE2 :
30+ import PySide2
3931
40- TEST_PATH = osp .abspath (osp .dirname (__file__ ))
32+ PYTHON_QT_API = "PySide2 v" + PySide2 .__version__
33+ elif PYQT5 :
34+ from PyQt5 .QtCore import PYQT_VERSION_STR
4135
36+ PYTHON_QT_API = "PyQt5 v" + PYQT_VERSION_STR
37+ else :
38+ from PyQt4 .QtCore import PYQT_VERSION_STR
4239
43- def run_test (fname , wait = False ):
44- """Run test"""
45- os .environ ["PYTHONPATH" ] = os .pathsep .join (sys .path )
46- args = " " .join ([sys .executable , '"' + fname + '"' ])
47- if os .environ .get ("TEST_UNATTENDED" ) is not None :
48- print (args )
49- if wait :
50- subprocess .call (args , shell = True )
51- else :
52- subprocess .Popen (args , shell = True )
40+ PYTHON_QT_API = "PyQt4 v" + PYQT_VERSION_STR
41+
42+
43+ TEST_PATH = osp .abspath (osp .dirname (__file__ ))
5344
5445
5546def get_tests (package ):
@@ -74,15 +65,24 @@ def get_tests(package):
7465 return tests
7566
7667
77- def run_all_tests (wait ):
68+ def run_test (fname , wait = True ):
69+ """Run test"""
70+ os .environ ["PYTHONPATH" ] = os .pathsep .join (sys .path )
71+ args = " " .join ([sys .executable , '"' + fname + '"' ])
72+ if TestEnvironment ().unattended :
73+ print (" " + args )
74+ (subprocess .call if wait else subprocess .Popen )(args , shell = True )
75+
76+
77+ def run_all_tests (wait = True ):
7878 """Run all PythonQwt tests"""
7979 import qwt
8080
8181 for fname in get_tests (qwt ):
8282 run_test (fname , wait = wait )
8383
8484
85- class TestLauncher (QMainWindow ):
85+ class TestLauncher (QW . QMainWindow ):
8686 """PythonQwt Test Launcher main window"""
8787
8888 ROWS = 5
@@ -94,8 +94,8 @@ def __init__(self, parent=None):
9494 self .setObjectName ("TestLauncher" )
9595 self .setWindowIcon (self .get_std_icon ("FileDialogListView" ))
9696 self .setWindowTitle ("PythonQwt %s - Test Launcher" % __version__ )
97- self .setCentralWidget (QWidget ())
98- self .grid_layout = QGridLayout ()
97+ self .setCentralWidget (QW . QWidget ())
98+ self .grid_layout = QW . QGridLayout ()
9999 self .centralWidget ().setLayout (self .grid_layout )
100100 self .test_nb = None
101101 self .fill_layout ()
@@ -104,31 +104,31 @@ def __init__(self, parent=None):
104104
105105 def get_std_icon (self , name ):
106106 """Return Qt standard icon"""
107- return self .style ().standardIcon (getattr (QStyle , "SP_" + name ))
107+ return self .style ().standardIcon (getattr (QW . QStyle , "SP_" + name ))
108108
109109 def fill_layout (self ):
110110 """Fill grid layout"""
111111 import qwt
112112
113113 for fname in get_tests (qwt ):
114114 self .add_test (fname )
115- toolbar = QToolBar (self )
116- all_act = QAction (self .get_std_icon ("DialogYesButton" ), "" , self )
115+ toolbar = QW . QToolBar (self )
116+ all_act = QW . QAction (self .get_std_icon ("DialogYesButton" ), "" , self )
117117 all_act .setIconText ("Run all tests" )
118118 all_act .triggered .connect (lambda checked : run_all_tests (wait = False ))
119- folder_act = QAction (self .get_std_icon ("DirOpenIcon" ), "" , self )
119+ folder_act = QW . QAction (self .get_std_icon ("DirOpenIcon" ), "" , self )
120120 folder_act .setIconText ("Open tests folder" )
121121 open_test_folder = lambda checked : os .startfile (TEST_PATH )
122122 folder_act .triggered .connect (open_test_folder )
123- about_act = QAction (self .get_std_icon ("FileDialogInfoView" ), "" , self )
123+ about_act = QW . QAction (self .get_std_icon ("FileDialogInfoView" ), "" , self )
124124 about_act .setIconText ("About" )
125125 about_act .triggered .connect (self .about )
126126 for action in (all_act , folder_act , None , about_act ):
127127 if action is None :
128128 toolbar .addSeparator ()
129129 else :
130130 toolbar .addAction (action )
131- toolbar .setToolButtonStyle (Qt .ToolButtonTextBesideIcon )
131+ toolbar .setToolButtonStyle (QC . Qt .ToolButtonTextBesideIcon )
132132 self .addToolBar (toolbar )
133133
134134 def add_test (self , fname ):
@@ -139,78 +139,65 @@ def add_test(self, fname):
139139 row = (self .test_nb - 1 ) % self .ROWS
140140 column = (self .test_nb - 1 ) // self .ROWS
141141 bname = osp .basename (fname )
142- button = QToolButton (self )
143- button .setToolButtonStyle (Qt .ToolButtonTextUnderIcon )
142+ button = QW . QToolButton (self )
143+ button .setToolButtonStyle (QC . Qt .ToolButtonTextUnderIcon )
144144 shot = osp .join (TEST_PATH , "data" , bname .replace (".py" , ".png" ))
145145 if osp .isfile (shot ):
146- button .setIcon (QIcon (shot ))
146+ button .setIcon (QG . QIcon (shot ))
147147 else :
148148 button .setIcon (self .get_std_icon ("DialogYesButton" ))
149149 button .setText (bname )
150150 button .setToolTip (fname )
151- button .setIconSize (QSize (130 , 80 ))
151+ button .setIconSize (QC . QSize (130 , 80 ))
152152 button .clicked .connect (lambda checked = None , fname = fname : run_test (fname ))
153153 self .grid_layout .addWidget (button , row , column )
154154
155155 def about (self ):
156156 """About test launcher"""
157157 from qtpy .QtCore import __version__ as qt_version
158158
159- QMessageBox .about (
159+ QW . QMessageBox .about (
160160 self ,
161161 "About " + self .windowTitle (),
162162 """<b>%s</b><p>Developped by Pierre Raybaut
163163 <br>Copyright © 2020 Pierre Raybaut
164- <p>Python %s, Qt %s on %s"""
164+ <p>Python %s, Qt %s, %s on %s"""
165165 % (
166166 self .windowTitle (),
167167 platform .python_version (),
168168 qt_version ,
169+ PYTHON_QT_API ,
169170 platform .system (),
170171 ),
171172 )
172173
173174
174- def run (wait = True ):
175- """Run PythonQwt tests or test launcher (requires `guidata`)"""
176- app = QApplication ([])
177- launcher = TestLauncher ()
178- launcher .show ()
179- unattended = os .environ .get ("TEST_UNATTENDED" ) is not None
180- if unattended :
181- QTimer .singleShot (100 , lambda : take_screenshot (launcher ))
182- app .exec_ ()
183- launcher .close ()
184- if unattended :
185- run_all_tests (wait = wait )
186-
187-
188- class TestOptions (QGroupBox ):
175+ class TestOptions (QW .QGroupBox ):
189176 """Test options groupbox"""
190177
191178 def __init__ (self , parent = None ):
192179 super (TestOptions , self ).__init__ ("Test options" , parent )
193- self .setLayout (QFormLayout ())
180+ self .setLayout (QW . QFormLayout ())
194181 self .hide ()
195182
196183 def add_checkbox (self , title , label , slot ):
197184 """Add new checkbox to option panel"""
198- widget = QCheckBox (label , self )
185+ widget = QW . QCheckBox (label , self )
199186 widget .stateChanged .connect (slot )
200187 self .layout ().addRow (title , widget )
201188 self .show ()
202189 return widget
203190
204191
205- class TestCentralWidget (QWidget ):
192+ class TestCentralWidget (QW . QWidget ):
206193 """Test central widget"""
207194
208195 def __init__ (self , widget_name , parent = None ):
209196 super (TestCentralWidget , self ).__init__ (parent )
210197 self .widget_name = widget_name
211198 self .plots = None
212199 self .widget_of_interest = self .parent ()
213- self .setLayout (QVBoxLayout ())
200+ self .setLayout (QW . QVBoxLayout ())
214201 self .options = TestOptions (self )
215202 self .add_widget (self .options )
216203
@@ -238,24 +225,29 @@ def take_screenshot(widget):
238225 if PYQT5 :
239226 pixmap = widget .grab ()
240227 else :
241- pixmap = QPixmap .grabWidget (widget )
228+ pixmap = QG . QPixmap .grabWidget (widget )
242229 bname = (widget .objectName ().lower () + ".png" ).replace ("window" , "" )
243230 bname = bname .replace ("plot" , "" ).replace ("widget" , "" )
244231 pixmap .save (osp .join (TEST_PATH , "data" , bname ))
245- QTimer .singleShot (0 , QApplication .instance ().quit )
232+ QC . QTimer .singleShot (0 , QW . QApplication .instance ().quit )
246233
247234
248- def test_widget (widget_class , size = None , title = None , options = True , timeout = 1000 ):
235+ def test_widget (widget_class , size = None , title = None , options = True ):
249236 """Test widget"""
250237 widget_name = widget_class .__name__
251- app = QApplication ([])
252- window = widget = widget_class ()
238+ app = QW .QApplication ([])
239+ test_env = TestEnvironment ()
240+ if inspect .signature (widget_class ).parameters .get ("unattended" ) is None :
241+ widget = widget_class ()
242+ else :
243+ widget = widget_class (unattended = test_env .unattended )
244+ window = widget
253245 if options :
254- if isinstance (widget , QMainWindow ):
246+ if isinstance (widget , QW . QMainWindow ):
255247 widget = window .centralWidget ()
256248 widget .setParent (None )
257249 else :
258- window = QMainWindow ()
250+ window = QW . QMainWindow ()
259251 central_widget = TestCentralWidget (widget_name , parent = window )
260252 central_widget .add_widget (widget )
261253 window .setCentralWidget (central_widget )
@@ -273,11 +265,73 @@ def test_widget(widget_class, size=None, title=None, options=True, timeout=1000)
273265 window .resize (width , height )
274266
275267 window .show ()
276- if os .environ .get ("TEST_UNATTENDED" ) is not None :
277- QTimer .singleShot (timeout , lambda : take_screenshot (widget_of_interest ))
268+ if test_env .screenshots :
269+ QC .QTimer .singleShot (1000 , lambda : take_screenshot (widget_of_interest ))
270+ elif test_env .unattended :
271+ QC .QTimer .singleShot (0 , QW .QApplication .instance ().quit )
278272 app .exec_ ()
279273 return app
280274
281275
276+ class TestEnvironment (object ):
277+ UNATTENDED_ARG = "unattended"
278+ SCREENSHOTS_ARG = "screenshots"
279+ UNATTENDED_ENV = "PYTHONQWT_UNATTENDED_TESTS"
280+ SCREENSHOTS_ENV = "PYTHONQWT_TAKE_SCREENSHOTS"
281+
282+ def __init__ (self ):
283+ self .parse_args ()
284+
285+ @property
286+ def unattended (self ):
287+ return os .environ .get (self .UNATTENDED_ENV ) is not None
288+
289+ @property
290+ def screenshots (self ):
291+ return os .environ .get (self .SCREENSHOTS_ENV ) is not None
292+
293+ def parse_args (self ):
294+ """Parse command line arguments"""
295+ parser = argparse .ArgumentParser (description = "Run PythonQwt tests" )
296+ parser .add_argument (
297+ "--mode" ,
298+ choices = [self .UNATTENDED_ARG , self .SCREENSHOTS_ARG ],
299+ required = False ,
300+ )
301+ args = parser .parse_args ()
302+ if args .mode is not None :
303+ self .set_env_from_args (args )
304+
305+ def set_env_from_args (self , args ):
306+ """Set appropriate environment variables"""
307+ for name in (self .UNATTENDED_ENV , self .SCREENSHOTS_ENV ):
308+ if name in os .environ :
309+ os .environ .pop (name )
310+ if args .mode == self .UNATTENDED_ARG :
311+ os .environ [self .UNATTENDED_ENV ] = "1"
312+ if args .mode == self .SCREENSHOTS_ARG :
313+ os .environ [self .SCREENSHOTS_ENV ] = os .environ [self .UNATTENDED_ENV ] = "1"
314+
315+
316+ def run (wait = True ):
317+ """Run PythonQwt tests or test launcher"""
318+ app = QW .QApplication ([])
319+ launcher = TestLauncher ()
320+ launcher .show ()
321+ test_env = TestEnvironment ()
322+ if test_env .screenshots :
323+ print ("Running PythonQwt tests and taking screenshots automatically:" )
324+ print ("-------------------------------------------------------------" )
325+ QC .QTimer .singleShot (100 , lambda : take_screenshot (launcher ))
326+ elif test_env .unattended :
327+ print ("Running PythonQwt tests in unattended mode:" )
328+ print ("-------------------------------------------" )
329+ QC .QTimer .singleShot (0 , QW .QApplication .instance ().quit )
330+ app .exec_ ()
331+ launcher .close ()
332+ if test_env .unattended :
333+ run_all_tests (wait = wait )
334+
335+
282336if __name__ == "__main__" :
283337 run ()
0 commit comments