1#!/usr/bin/env python3
2# -*- Coding: UTF-8 -*-
3
4# ---------------------------------------------------------------------------
5# Open Asset Import Library (ASSIMP)
6# ---------------------------------------------------------------------------
7#
8# Copyright (c) 2006-2020, ASSIMP Development Team
9#
10# All rights reserved.
11#
12# Redistribution and use of this software in source and binary forms,
13# with or without modification, are permitted provided that the following
14# conditions are met:
15#
16# * Redistributions of source code must retain the above
17#   copyright notice, this list of conditions and the
18#   following disclaimer.
19#
20# * Redistributions in binary form must reproduce the above
21#   copyright notice, this list of conditions and the
22#   following disclaimer in the documentation and/or other
23#   materials provided with the distribution.
24#
25# * Neither the name of the ASSIMP team, nor the names of its
26#   contributors may be used to endorse or promote products
27#   derived from this software without specific prior
28#   written permission of the ASSIMP Development Team.
29#
30# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41# ---------------------------------------------------------------------------
42from tkinter import *
43import sys
44import os
45import platform
46import run
47import subprocess
48import result_checker as rc
49
50INFO  = 0
51WARN  = 1
52ERROR = 2
53
54# -------------------------------------------------------------------------------
55def log( sev, msg ):
56    """
57    This function is used to log info, warnings and errors.
58    """
59    logEntry = ""
60    if sev == 0:
61        logEntry = logEntry + "[INFO]: "
62    elif sev == 1:
63        logEntry = logEntry + "[WARN]: "
64    elif sev == 2:
65        logEntry = logEntry + "[ERR] : "
66    logEntry = logEntry + str( msg )
67    print( logEntry )
68
69# -------------------------------------------------------------------------------
70class BaseDialog( Toplevel ):
71    """
72    Helper base class for dialogs used in the UI.
73    """
74
75    def __init__(self, parent, title = None, buttons=""):
76        """
77        Constructor
78        """
79        Toplevel.__init__( self, parent )
80        self.transient(parent)
81
82        if title:
83            self.title(title)
84
85        self.parent = parent
86        self.result = None
87        body = Frame(self)
88        self.initial_focus = self.body(body)
89        body.pack(padx=5, pady=5)
90        self.buttonbox(buttons)
91        self.grab_set()
92        if not self.initial_focus:
93            self.initial_focus = self
94        self.protocol("WM_DELETE_WINDOW", self.cancel)
95        self.geometry("+%d+%d" % (parent.winfo_rootx() + 50,
96                                  parent.winfo_rooty() + 50))
97        self.initial_focus.focus_set()
98        self.wait_window(self)
99
100    def body(self, master):
101        # create dialog body.  return widget that should have
102        # initial focus.  this method should be overridden
103        pass
104
105    def buttonbox(self, buttons):
106        # add standard button box. override if you don't want the
107        # standard buttons
108        box = Frame(self)
109        w = Button(box, text="OK", width=40, command=self.ok, default=ACTIVE)
110        w.pack(side=LEFT, padx=5, pady=5)
111        self.bind("<Return>", self.ok)
112        box.pack()
113
114    def ok(self, event=None):
115        if not self.validate():
116            self.initial_focus.focus_set()  # put focus back
117            return
118
119        self.withdraw()
120        self.update_idletasks()
121        self.apply()
122        self.cancel()
123
124    def cancel(self, event=None):
125        # put focus back to the parent window
126        self.parent.focus_set()
127        self.destroy()
128
129    def validate(self):
130        return 1  # override
131
132    def apply(self):
133        pass  # override
134
135# -------------------------------------------------------------------------------
136class VersionDialog( BaseDialog ):
137    """
138    This class is used to create the info dialog.
139    """
140    def body(self, master):
141        # info will be read from assimp command line tool
142        version = "Asset importer lib version unknown"
143        exe = run.getEnvVar( "assimp_path" )
144        if len( exe ) != 0:
145            command = [exe, "version" ]
146            log( INFO, "command = " + str(command))
147            stdout = subprocess.check_output(command)
148            for line in stdout.splitlines():
149                pos = str(line).find( "Version" )
150                if -1 != pos:
151                    version = line
152        Label(master, text=version).pack()
153
154    def apply(self):
155        pass
156
157# -------------------------------------------------------------------------------
158class SetupDialog( BaseDialog ):
159    """
160    This class is used to create the setup dialog.
161    """
162    def body(self, master):
163        Label(master, justify=LEFT, text="Assimp: " ).grid(row=0, column=0)
164        Label(master, justify=LEFT, text=run.getEnvVar("assimp_path")).grid(row=0, column=1)
165        Label(master, text="New executable:").grid(row=1)
166        self.e1 = Entry(master)
167        self.e1.grid(row=1, column=1)
168        return self.e1  # initial focus
169
170    def apply(self):
171        exe = str( self.e1.get() )
172        if len( exe )  == 0:
173            return 0
174        if os.path.isfile( exe ):
175            log( INFO, "Set executable at " + exe)
176            self.assimp_bin_path = exe
177            run.setEnvVar("assimp_path", self.assimp_bin_path)
178        else:
179            log( ERROR, "Executable not found at "+exe )
180        return 0
181
182# -------------------------------------------------------------------------------
183class RegDialog( object ):
184    """
185    This class is used to create a simplified user interface for running the regression test suite.
186    """
187
188    def __init__(self, bin_path ):
189        """
190        Constructs the dialog, you can define which executable shal be used.
191        @param  bin_path    [in] Path to assimp binary.
192        """
193        run.setEnvVar( "assimp_path", bin_path )
194        self.b_run_ = None
195        self.b_update_ = None
196        self.b_res_checker_ = None
197        self.b_quit_ = None
198        if platform.system() == "Windows":
199            self.editor = "notepad"
200        elif platform.system() == "Linux":
201            self.editor = "vim"
202        self.root = None
203        self.width=40
204
205    def run_reg(self):
206        log(INFO, "Starting regression test suite.")
207        run.run_test()
208        rc.run()
209        self.b_update_.config( state=ACTIVE  )
210        return 0
211
212    def reg_update(self):
213        assimp_exe = run.getEnvVar( "assimp_path" )
214        if len( assimp_exe ) == 0:
215            return 1
216        exe = "python"
217        command = [ exe, "gen_db.py", assimp_exe ]
218        log(INFO, "command = " + str(command))
219        stdout = subprocess.call(command)
220
221        log(INFO, stdout)
222        return 0
223
224    def shop_diff( self ):
225        log(WARN, "ToDo!")
226        return 0
227
228    def open_log(self):
229        command = [ self.editor, "../results/run_regression_suite_output.txt", ]
230        log(INFO, "command = " + str( command ) )
231        r = subprocess.call(command)
232        return 0
233
234    def show_version( self ):
235        d = VersionDialog( self.root )
236        return 0
237
238    def setup(self):
239        d = SetupDialog( self.root )
240        return 0
241
242    def quit(self):
243        log( INFO, "quit" )
244        sys.exit( 0 )
245
246    def initUi(self):
247        # create the frame with buttons
248        self.root = Tk()
249        self.root.title( "Assimp-Regression UI")
250        self.b_run_       = Button( self.root, text="Run regression ", command=self.run_reg,    width = self.width )
251        self.b_update_    = Button( self.root, text="Update database", command=self.reg_update, width = self.width )
252        self.b_show_diff_ = Button( self.root, text="Show diff", command=self.shop_diff, width = self.width )
253        self.b_log_       = Button( self.root, text="Open log", command=self.open_log, width = self.width )
254        self.b_setup_     = Button( self.root, text="Setup", command=self.setup, width = self.width )
255        self.b_version_   = Button( self.root, text="Show version", command=self.show_version, width = self.width )
256        self.b_quit_      = Button( self.root, text="Quit", command=self.quit,       width = self.width )
257
258        # define the used grid
259        self.b_run_.grid(       row=0, column=0, sticky=W+E )
260        self.b_update_.grid(    row=1, column=0, sticky=W+E )
261        self.b_show_diff_.grid( row=2, column=0, sticky=W+E )
262        self.b_log_.grid(       row=3, column=0, sticky=W+E )
263        self.b_setup_.grid(     row=4, column=0, sticky=W+E )
264        self.b_version_.grid(   row=5, column=0, sticky=W+E )
265        self.b_quit_.grid(      row=6, column=0, sticky=W+E )
266
267        #self.b_update_.config( state=DISABLED )
268        self.b_show_diff_.config( state=DISABLED )
269
270        # run mainloop
271        self.root.mainloop()
272
273# -------------------------------------------------------------------------------
274def getDefaultExecutable():
275    assimp_bin_path = ""
276    if platform.system() == "Windows":
277        assimp_bin_path = '..\\..\\bin\\debug\\assimpd.exe'
278    elif platform.system() == "Linux":
279        assimp_bin_path = '../../bin/assimp'
280
281    return assimp_bin_path
282
283# -------------------------------------------------------------------------------
284if __name__ == "__main__":
285    if len(sys.argv) > 1:
286        assimp_bin_path = sys.argv[1]
287    else:
288        assimp_bin_path = getDefaultExecutable()
289    log( INFO, 'Using assimp binary: ' + assimp_bin_path )
290    dlg = RegDialog(assimp_bin_path)
291    dlg.initUi()
292
293# vim: ai ts=4 sts=4 et sw=4
294