ソースを参照

add tipog tester, gridviewer can now read tipog results [all of that needs to be cleaned]

olinox 8 年 前
コミット
c1082c5396

+ 84 - 14
gridviewer/GridViewer.py

@@ -7,8 +7,10 @@ if __name__ == "__main__":
     pypog_path = (os.path.abspath(".."))
     sys.path.append(pypog_path)
 
+import json
 import time
 
+from PyQt5.Qt import QFileDialog, Qt, QRectF
 from PyQt5.QtCore import QPointF, QMimeData
 from PyQt5.QtWidgets import QMainWindow, \
     QApplication, QGraphicsScene, QGraphicsView, QMessageBox
@@ -18,7 +20,6 @@ from gridviewer.GridViewerCell import GridViewerCell
 from gridviewer.viewer import Ui_window
 from pypog import geometry
 
-
 class GridViewer(QMainWindow):
 
     def __init__(self):
@@ -44,38 +45,58 @@ class GridViewer(QMainWindow):
         self.ui.btn_toClipboard.clicked.connect(self.to_clipboard)
         self.ui.btn_zoom_plus.clicked.connect(self.zoom_plus)
         self.ui.btn_zoom_minus.clicked.connect(self.zoom_minus)
+        self.ui.chk_displayCoords.toggled.connect(self.update_cell_labels)
+
         self.ui.txt_stdin.returnPressed.connect(self.run_stdin)
 
-        self.ui.chk_displayCoords.toggled.connect(self.update_cell_labels)
+        self.ui.btn_load_tipog.clicked.connect(self.tipog_load)
+        self.ui.btn_tipog_previous.clicked.connect(self.tipog_previous)
+        self.ui.btn_tipog_next.clicked.connect(self.tipog_next)
 
         self.make_grid()
 
-    def make_grid(self):
+    def current_shape(self):
+        return geometry.FLAT_HEX if self.ui.opt_hex.isChecked() else geometry.SQUARE
 
-        self.selection = []
-        shape = geometry.FLAT_HEX if self.ui.opt_hex.isChecked() else geometry.SQUARE
+    def make_grid(self):
+        shape = self.current_shape()
         width = self.ui.spb_width.value()
         height = self.ui.spb_height.value()
 
-        kx = 1 if shape == geometry.SQUARE else 0.866
+        self._update_grid(shape, 0, width, 0, height)
+
+    def _update_grid(self, cell_shape, min_x, max_x, min_y, max_y):
+        QApplication.setOverrideCursor(Qt.WaitCursor)
 
-        margin = 240
-        cell_height = 120
+        self.cells = {}
+        self.selection = []
 
         self._scene.clear()
 
-        self._scene.setSceneRect(0 - margin, 0 - margin, (kx * cell_height * (width + 2)) + margin, (cell_height * (height + 2)) + margin)
+        if (max_x - min_x) * (max_y - min_y) > 10000:
+            self.ui.chk_displayCoords.setChecked(False)
 
-        for x in range(width):
-            for y in range(height):
+        for x in range(min_x, max_x):
+            for y in range(min_y, max_y):
 
                 cell = GridViewerCell(self, x, y)
-                cell.generate(shape)
+                cell.generate(cell_shape, show_label=self.ui.chk_displayCoords.isChecked())
 
                 self._scene.addItem(cell)
 
                 self.cells[(x, y)] = cell
 
+        self.ui.txt_stdout.setText(str(self.cells))
+
+#         rect = self._scene.sceneRect()
+#         margin = 240
+#         new_rect = QRectF(rect.x() - margin, rect.y() - margin, rect.width() + (2 * margin), rect.height() + (2 * margin))
+#         self._scene.setSceneRect(new_rect)
+
+        self.ui.view.centerOn(QPointF(0, 0))
+#         self.ui.view.centerOn(QPointF(rect.x() - margin, rect.y() - margin))
+
+        QApplication.restoreOverrideCursor()
 
     def add_to_selection(self, x, y):
         self.selection.append((x, y))
@@ -87,6 +108,7 @@ class GridViewer(QMainWindow):
         self.ui.txt_coords.setText(str(self.selection))
 
     def update_selected_cells(self):
+        QApplication.setOverrideCursor(Qt.WaitCursor)
         try:
             new_selection = list(eval(self.ui.txt_coords.toPlainText()))
         except SyntaxError:
@@ -98,6 +120,7 @@ class GridViewer(QMainWindow):
 
         for x, y in new_selection:
             self.cells[(x, y)].select()
+        QApplication.restoreOverrideCursor()
 
     def to_clipboard(self):
         data = QMimeData()
@@ -118,13 +141,60 @@ class GridViewer(QMainWindow):
         stdin = self.ui.txt_stdin.text()
         try:
             t0 = time.time()
-            result = eval(stdin)
+            result = eval("geometry." + stdin)
             self.ui.txt_coords.setText(str(result))
             self.update_selected_cells()
-            self.ui.txt_stdout.setText("{} ms.".format(1000 * (time.time() - t0)))
+            self.ui.txt_stdout.setText("{} ms.".format(int(1000 * (time.time() - t0))))
         except Exception as e:
             self.ui.txt_stdout.setText("{} : {}".format(e.__class__name__, e))
 
+    def tipog_load(self):
+        filepath = QFileDialog.getOpenFileName(self, "Select the Tipog results file", ".")[0]
+        try:
+            with open(filepath, "r") as f:
+                results = json.load(f)
+        except:
+            print("unreadable file: {}".format(filepath))
+
+        if not results:
+            print("empty results file")
+            return
+
+        self.tipog_results = results
+        self.tipog_current = 0
+
+        self.ui.btn_tipog_next.setEnabled(True)
+        self.ui.btn_tipog_previous.setEnabled(True)
+        self.tipog_update()
+
+    def tipog_next(self):
+        if self.tipog_current > (len(self.tipog_results) - 1):
+            return
+        self.tipog_current += 1
+        self.tipog_update()
+
+    def tipog_previous(self):
+        if self.tipog_current < 1:
+            return
+        self.tipog_current -= 1
+        self.tipog_update()
+
+    def tipog_update(self):
+        res = self.tipog_results[self.tipog_current]
+        xs = [x for x, _ in res["result"]]
+        ys = [y for _, y in res["result"]]
+        min_x = min(xs) - 5
+        max_x = max(xs) + 5
+        min_y = min(ys) - 5
+        max_y = max(ys) + 5
+        self._update_grid(int(res["cell_shape"]), min_x, max_x, min_y, max_y)
+        self.ui.lbl_tipog_counter.setText("{} on {}".format(self.tipog_current + 1, len(self.tipog_results)))
+        formatted_result = [(x, y) for x, y in res["result"]]
+        self.ui.txt_coords.setText(str(formatted_result))
+
+        self.update_selected_cells()
+        self.ui.txt_stdout.setText("{}\nExecuted in {} ms.".format(res["call"], res["exectime"]))
+
 if __name__ == "__main__":
 
     app = QApplication(sys.argv)

+ 3 - 1
gridviewer/GridViewerCell.py

@@ -19,7 +19,7 @@ class GridViewerCell(QGraphicsPolygonItem):
         self.y = y
         self.selected = False
 
-    def generate(self, shape, scale=120):
+    def generate(self, shape, scale=120, show_label=False):
 
         points = [QPointF(xp, yp) for xp, yp in graphic.g_cell(shape, self.x, self.y, scale)]
         qpolygon = QPolygonF(points)
@@ -33,6 +33,8 @@ class GridViewerCell(QGraphicsPolygonItem):
         self.setFlag(QGraphicsItem.ItemIsFocusable)
 
         self.label = QGraphicsSimpleTextItem("{}-{}".format(self.x, self.y), parent=self)
+        self.label.setVisible(show_label)
+
         k = 0
         if (self.x % 2) != 0:
             k = 0.5

+ 120 - 0
gridviewer/qt_viewer.ui

@@ -60,6 +60,126 @@
         </property>
        </widget>
       </item>
+      <item>
+       <widget class="QFrame" name="frame_tipog">
+        <property name="minimumSize">
+         <size>
+          <width>0</width>
+          <height>30</height>
+         </size>
+        </property>
+        <property name="maximumSize">
+         <size>
+          <width>16777215</width>
+          <height>30</height>
+         </size>
+        </property>
+        <property name="frameShape">
+         <enum>QFrame::StyledPanel</enum>
+        </property>
+        <property name="frameShadow">
+         <enum>QFrame::Raised</enum>
+        </property>
+        <layout class="QHBoxLayout" name="horizontalLayout_6">
+         <property name="spacing">
+          <number>0</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout_5">
+           <property name="spacing">
+            <number>0</number>
+           </property>
+           <item>
+            <widget class="QPushButton" name="btn_load_tipog">
+             <property name="minimumSize">
+              <size>
+               <width>140</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="maximumSize">
+              <size>
+               <width>140</width>
+               <height>16777215</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>Load tipog results</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <spacer name="horizontalSpacer_3">
+             <property name="orientation">
+              <enum>Qt::Horizontal</enum>
+             </property>
+             <property name="sizeHint" stdset="0">
+              <size>
+               <width>40</width>
+               <height>20</height>
+              </size>
+             </property>
+            </spacer>
+           </item>
+           <item>
+            <widget class="QToolButton" name="btn_tipog_previous">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>&lt;</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLabel" name="lbl_tipog_counter">
+             <property name="minimumSize">
+              <size>
+               <width>100</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="maximumSize">
+              <size>
+               <width>100</width>
+               <height>16777215</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>Test 0 on 0 </string>
+             </property>
+             <property name="alignment">
+              <set>Qt::AlignCenter</set>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QToolButton" name="btn_tipog_next">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>&gt;</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </widget>
+      </item>
       <item>
        <widget class="QGraphicsView" name="view"/>
       </item>

+ 45 - 5
gridviewer/viewer.py

@@ -34,6 +34,42 @@ class Ui_window(object):
         self.lbl_title.setAlignment(QtCore.Qt.AlignCenter)
         self.lbl_title.setObjectName("lbl_title")
         self.verticalLayout.addWidget(self.lbl_title)
+        self.frame_tipog = QtWidgets.QFrame(self.centralwidget)
+        self.frame_tipog.setMinimumSize(QtCore.QSize(0, 30))
+        self.frame_tipog.setMaximumSize(QtCore.QSize(16777215, 30))
+        self.frame_tipog.setFrameShape(QtWidgets.QFrame.StyledPanel)
+        self.frame_tipog.setFrameShadow(QtWidgets.QFrame.Raised)
+        self.frame_tipog.setObjectName("frame_tipog")
+        self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.frame_tipog)
+        self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0)
+        self.horizontalLayout_6.setSpacing(0)
+        self.horizontalLayout_6.setObjectName("horizontalLayout_6")
+        self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
+        self.horizontalLayout_5.setSpacing(0)
+        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
+        self.btn_load_tipog = QtWidgets.QPushButton(self.frame_tipog)
+        self.btn_load_tipog.setMinimumSize(QtCore.QSize(140, 0))
+        self.btn_load_tipog.setMaximumSize(QtCore.QSize(140, 16777215))
+        self.btn_load_tipog.setObjectName("btn_load_tipog")
+        self.horizontalLayout_5.addWidget(self.btn_load_tipog)
+        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout_5.addItem(spacerItem)
+        self.btn_tipog_previous = QtWidgets.QToolButton(self.frame_tipog)
+        self.btn_tipog_previous.setEnabled(False)
+        self.btn_tipog_previous.setObjectName("btn_tipog_previous")
+        self.horizontalLayout_5.addWidget(self.btn_tipog_previous)
+        self.lbl_tipog_counter = QtWidgets.QLabel(self.frame_tipog)
+        self.lbl_tipog_counter.setMinimumSize(QtCore.QSize(100, 0))
+        self.lbl_tipog_counter.setMaximumSize(QtCore.QSize(100, 16777215))
+        self.lbl_tipog_counter.setAlignment(QtCore.Qt.AlignCenter)
+        self.lbl_tipog_counter.setObjectName("lbl_tipog_counter")
+        self.horizontalLayout_5.addWidget(self.lbl_tipog_counter)
+        self.btn_tipog_next = QtWidgets.QToolButton(self.frame_tipog)
+        self.btn_tipog_next.setEnabled(False)
+        self.btn_tipog_next.setObjectName("btn_tipog_next")
+        self.horizontalLayout_5.addWidget(self.btn_tipog_next)
+        self.horizontalLayout_6.addLayout(self.horizontalLayout_5)
+        self.verticalLayout.addWidget(self.frame_tipog)
         self.view = QtWidgets.QGraphicsView(self.centralwidget)
         self.view.setObjectName("view")
         self.verticalLayout.addWidget(self.view)
@@ -93,8 +129,8 @@ class Ui_window(object):
         self.btn_zoom_minus = QtWidgets.QToolButton(self.centralwidget)
         self.btn_zoom_minus.setObjectName("btn_zoom_minus")
         self.horizontalLayout_4.addWidget(self.btn_zoom_minus)
-        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
-        self.horizontalLayout_4.addItem(spacerItem)
+        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout_4.addItem(spacerItem1)
         self.chk_displayCoords = QtWidgets.QCheckBox(self.centralwidget)
         self.chk_displayCoords.setChecked(True)
         self.chk_displayCoords.setObjectName("chk_displayCoords")
@@ -113,8 +149,8 @@ class Ui_window(object):
         self.btn_toClipboard.setMaximumSize(QtCore.QSize(84, 1000))
         self.btn_toClipboard.setObjectName("btn_toClipboard")
         self.horizontalLayout_3.addWidget(self.btn_toClipboard)
-        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
-        self.horizontalLayout_3.addItem(spacerItem1)
+        spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout_3.addItem(spacerItem2)
         self.btn_updateSelection = QtWidgets.QToolButton(self.centralwidget)
         self.btn_updateSelection.setMinimumSize(QtCore.QSize(50, 35))
         self.btn_updateSelection.setMaximumSize(QtCore.QSize(50, 1000))
@@ -132,7 +168,7 @@ class Ui_window(object):
         self.txt_stdin.setObjectName("txt_stdin")
         self.verticalLayout_2.addWidget(self.txt_stdin)
         self.txt_stdout = QtWidgets.QTextEdit(self.centralwidget)
-        self.txt_stdout.setMinimumSize(QtCore.QSize(0, 80))
+        self.txt_stdout.setMinimumSize(QtCore.QSize(0, 120))
         self.txt_stdout.setMaximumSize(QtCore.QSize(16777215, 120))
         self.txt_stdout.setObjectName("txt_stdout")
         self.verticalLayout_2.addWidget(self.txt_stdout)
@@ -152,6 +188,10 @@ class Ui_window(object):
         _translate = QtCore.QCoreApplication.translate
         window.setWindowTitle(_translate("window", "GridViewer"))
         self.lbl_title.setText(_translate("window", "Grid Viewer"))
+        self.btn_load_tipog.setText(_translate("window", "Load tipog results"))
+        self.btn_tipog_previous.setText(_translate("window", "<"))
+        self.lbl_tipog_counter.setText(_translate("window", "Test 0 on 0 "))
+        self.btn_tipog_next.setText(_translate("window", ">"))
         self.label.setText(_translate("window", "GRID"))
         self.opt_hex.setText(_translate("window", "Flat Hex"))
         self.opt_square.setText(_translate("window", "Square"))

+ 1 - 0
requirements.txt

@@ -0,0 +1 @@
+pyyaml

+ 153 - 0
tests/tipog.py

@@ -0,0 +1,153 @@
+'''
+Tipog tests
+
+Usage:
+  tipog [-v] <filename> [-o <output>]
+
+Options:
+  -v --verbose       Verbose result
+  -o --output        Register the results in <output> file (json)
+  -h --help          Show this screen.
+  --version          Show version.
+'''
+
+import cProfile
+import json
+import os
+import sys
+import timeit
+
+from docopt import docopt
+import yaml
+
+
+__version__ = "0.1"
+
+sysargs = docopt(__doc__, version=__version__)
+
+yaml_file = sysargs["<filename>"]
+
+try:
+    with open(yaml_file, "r") as f:
+        data = yaml.load(f)
+except FileNotFoundError:
+    print("ERR: no file named '{}'".format(yaml_file))
+    sys.exit(1)
+except yaml.scanner.ScannerError:
+    print("ERR: unreadable yaml file '{}'".format(yaml_file))
+    sys.exit(1)
+
+if not data:
+    print("ERR: empty yaml file '{}'".format(yaml_file))
+    sys.exit(1)
+
+for to_import in data["imports"]:
+    try:
+        # TODO: avoid the 'exec'
+        exec(to_import)
+    except (ModuleNotFoundError, ImportError):
+        print("ERR: unable to import '{}'".format(to_import))
+        sys.exit(1)
+
+try:
+    jobs = data["jobs"]
+except KeyError:
+    print("ERR: missing 'jobs' entry")
+    sys.exit(1)
+
+
+
+
+def profile(_call, verbose=False):
+
+    print(">> {}".format(_call))
+
+    if verbose:
+        cProfile.run(_call, sort='nfl')
+
+    number = 1
+    t = 0
+    while 1:
+        t = timeit.timeit(lambda: eval(_call), number=number)
+        if t >= 0.1:
+            break
+        elif number > 10000000:
+            print("ERR: unable to compute the execution time")
+            number = "err"
+            t = "?"
+            break
+        number *= 10
+
+    return t / 1000
+
+
+
+def validate(fct, validator, args, verbose=False):
+    result = fct(*args)
+    attended = validator(*args)
+    return result == attended
+
+to_register = []
+
+for function_name, job in jobs.items():
+
+    try:
+        function = eval(function_name)
+    except NameError:
+        print("ERR: unknown function ('{}')".format(function_name))
+        continue
+
+    if sysargs["--verbose"]:
+        print("** Test function '{}'".format(function_name))
+
+    try:
+        args_lst = job["args"]
+    except KeyError:
+        args_lst = [[]]
+
+    try:
+        validator_str = job["validator"]
+        try:
+            validator = eval(validator_str)
+            if sysargs["--verbose"]:
+                print("> validator: '{}'".format(validator_str))
+        except NameError:
+            print("ERR: unknown function as validator ('{}')".format(validator_str))
+    except (TypeError, KeyError):
+        validator = None
+
+    for args in args_lst:
+        call_str = "{}(*{})".format(function_name, args)
+
+        exectime = profile(call_str, sysargs["--verbose"])
+        print("\t> Run in {} ms.".format(exectime))
+
+        if validator:
+            valid = validate(function, validator, args, sysargs["--verbose"])
+            print("\t> Validated: {}".format(valid))
+
+        if sysargs["--output"]:
+            last_result = {"call": call_str, "result": function(*args), "exectime": exectime}
+            try:
+                last_result.update(job["infos"])
+            except KeyError:
+                pass
+            to_register.append(last_result)
+
+    if sysargs["--verbose"]:
+        print("------------------------------")
+
+if sysargs["--output"]:
+    output_name = sysargs["<output>"]
+    if not output_name:
+        output_name = "{}_result".format(os.path.splitext(sysargs["<filename>"])[0])
+
+    reg_file_path = os.path.join(".", output_name)
+    try:
+        os.remove(reg_file_path)
+    except FileNotFoundError:
+        pass
+    with open(reg_file_path, "w+") as f:
+        json.dump(to_register, f)
+
+print("** End of the tests")

+ 18 - 0
tests/tipog_model.yml

@@ -0,0 +1,18 @@
+imports:
+- from pypog import geometry
+  
+jobs:
+  geometry.fhex2_line:
+    infos: {'cell_shape': 61}
+    args:
+    - [0, 0, 1, 1]
+    - [0, 0, 10, 10]
+    - [0, 0, 100, 100]
+    validator:
+  geometry.squ2_line:
+    infos: {'cell_shape': 4}
+    args:
+    - [0, 0, 1, 1]
+    - [0, 0, 10, 10]
+    - [0, 0, 100, 100]
+    validator: geometry.squ2_line

ファイルの差分が大きいため隠しています
+ 0 - 0
tests/tipog_model_result


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません