Diff of /app/LeapGui.py [000000] .. [4de1c7]

Switch to side-by-side view

--- a
+++ b/app/LeapGui.py
@@ -0,0 +1,515 @@
+import json
+from multiprocessing import Process
+import os
+
+import LeapRecord
+from AnyPy import AnyPy
+from config.Configuration import env
+from BVHAnimation import bvh_animation
+from resources.pymo.pymo.parsers import BVHParser as Pymo_BVHParser
+from gooey.gui import application
+from gooey.gui import processor
+from gooey.gui.containers import application as containers_application
+from gooey.python_bindings import gooey_decorator, gooey_parser
+
+# strings for the actions in the Gooey side-menu
+ACTION_RECORD = 'Record'
+ACTION_ANYBODY = 'AnyBody'
+ACTION_CONVERTER = 'Converter'
+ACTION_ANIMATION = 'Animation'
+EXECUTED_COMMAND = 'command'
+
+
+class GooeyModification:
+    # overwrite the Gooey default stop method to cancel Leap Motion recording
+    def on_stop(self):
+        """Overload the stop method to allow stopping of Leap Motion Recording"""
+        executed_command = LeapGui.StoredArgs().load().stored_args[EXECUTED_COMMAND]
+        if executed_command == ACTION_RECORD:
+            return self.clientRunner.stop_leap()
+        self.clientRunner.stop()
+
+    def stop_leap(self):
+        if not self._process.stdin.closed:
+            """Send Keyboard Interrupt to stop Leap Motion recording and parse bvh / interpolation"""
+            self._process.stdin.write(b"\n")
+            self._process.stdin.close()
+            return
+        self.stop()
+
+    # overwrite classes and methods of the stop button
+    containers_application.GooeyApplication.onStopExecution = on_stop
+    processor.ProcessController.stop_leap = stop_leap
+
+    containers_application.ProcessController = processor.ProcessController
+    application.GooeyApplication = containers_application.GooeyApplication
+    gooey_decorator.application = application
+
+    # create alias
+    Gooey = gooey_decorator.Gooey
+    GooeyParser = gooey_parser.GooeyParser
+
+
+class LeapGui:
+    """Implementation of the Gooey including some adaptions"""
+
+    Gooey = GooeyModification.Gooey
+    GooeyParser = GooeyModification.GooeyParser
+
+    @staticmethod
+    @Gooey(program_name="Leap Motion Recorder (c) Robin, Sean",
+           sidebar_title='Actions',
+           # return_to_config=True,
+           image_dir='config/gooey',
+           tabbed_groups=True,
+           default_size=(1000, 700))
+    def parse_args():
+        """ Use GooeyParser to build up the arguments we will use in our script
+        Save the arguments in a default json file so that we can retrieve them
+        every time we run the script.
+        """
+
+        # load default values from json (saved from last run)
+        stored_args = LeapGui.StoredArgs().load()
+
+        parser = LeapGui.GooeyParser(description='Record Leap Motion data and export to BVH or AnyBody')
+        env.add_parser(parser)
+        subs = parser.add_subparsers(help='Tools', dest='command')
+
+        # === record === #
+        record_parser = subs.add_parser(ACTION_RECORD, help='Leap Recording')
+
+        # settings Group
+        settings_group = record_parser.add_argument_group(
+            "Settings",
+            gooey_options={
+                'show_border': True,
+                'columns': 1
+            }
+        )
+
+        basis_group = settings_group.add_mutually_exclusive_group(
+            required=True,
+            gooey_options={
+                'initial_selection': 0
+            })
+
+        settings_group.add_argument('frames_per_second',
+                                    metavar='Frames per second',
+                                    action='store',
+                                    default=stored_args.get(ACTION_RECORD, 'frames_per_second', '30'),
+                                    gooey_options={
+                                        'validator': {
+                                            'test': '1 <= int(user_input) <= 150',
+                                            'message': 'Must be between 1 and 150'
+                                        }
+                                    }
+                                    )
+
+        settings_group.add_argument('-show_animation',
+                                    metavar='Animate',
+                                    help='Show motion animation after recording',
+                                    action='store_true')
+
+        basis_group.add_argument('-anybody_basis',
+                                 metavar='Calculate joint angles to AnyBody basis',
+                                 action='store_true')
+
+        basis_group.add_argument('-firstframe_basis',
+                                 metavar='Calculate joint angles to initial recorded basis (first frame)',
+                                 action='store_true')
+
+        # bvh Group
+        bvh_group = record_parser.add_argument_group(
+            "BVH Export",
+            gooey_options={
+                'show_border': True,
+                'columns': 1
+            }
+        )
+
+        bvh_group.add_argument('-bvh',
+                               metavar='Write BVH-File',
+                               action='store_true')
+
+        bvh_group.add_argument('-bvh_path',
+                               metavar='BVH File',
+                               action='store',
+                               default=stored_args.get(
+                                   ACTION_RECORD, 'bvh_path', LeapGui.StoredArgs.path('../output/BVH/RightHand.bvh')),
+                               widget='FileSaver',
+                               help='Choose location, where to save the BVH File')
+
+        bvh_group.add_argument('channels',
+                               metavar='BVH Channels',
+                               action='store',
+                               default=stored_args.get(ACTION_RECORD, 'channels', 'rotation'),
+                               widget='Dropdown',
+                               help='Rotation: (X,Y,Z) rotation only (default)\n'
+                                    'Position: (X,Y,Z) rotation and position for all channels',
+                               choices=['rotation', 'position'],
+                               gooey_options={
+                                   'validator': {
+                                       'test': 'user_input != "Select Option"',
+                                       'message': 'Choose a channel setting'
+                                   }
+                               })
+
+        # interpol Group
+        interpol_group = record_parser.add_argument_group(
+            "Interpolation Vector",
+            gooey_options={
+                'show_border': True,
+                'columns': 1
+            }
+        )
+        interpol_group.add_argument('-anybody',
+                                    metavar='Write interpolation files for AnyBody',
+                                    action='store_true')
+
+        interpol_group.add_argument('-anybody_template_path',
+                                    metavar='AnyBody templates',
+                                    action='store',
+                                    default=stored_args.get(
+                                        ACTION_RECORD, 'anybody_template_path',
+                                        LeapGui.StoredArgs.path('config/anybody_templates')),
+                                    widget='DirChooser',
+                                    help='Source directory that contains *.template files for AnyBody')
+
+        interpol_group.add_argument('-anybody_output_path',
+                                    metavar='Output directory',
+                                    action='store',
+                                    default=stored_args.get(
+                                        ACTION_RECORD, 'anybody_output_path',
+                                        LeapGui.StoredArgs.path('../output/Anybody')),
+                                    widget='DirChooser',
+                                    help='Output directory for interpolation files')
+
+        # # c3d Group
+        # c3d_group = record_parser.add_argument_group(
+        #     "C3D",
+        #     gooey_options={
+        #         'show_border': True,
+        #         'columns': 2
+        #     }
+        # )
+        # c3d_group.add_argument('-c3d',
+        #                        metavar='Write C3D-File',
+        #                        action='store_true')
+        #
+        # c3d_group.add_argument('-c3d_filename',
+        #                        metavar=' ',
+        #                        action='store',
+        #                        default=stored_args.get(ACTION_RECORD, 'c3d_filename', 'RightHand'),
+        #                        help='Filename')
+        #
+        # c3d_group.add_argument('-c3d_path',
+        #                        metavar=' ',
+        #                        action='store',
+        #                        default=stored_args.get(
+        #                            ACTION_RECORD, 'c3d_path', LeapGui.StoredArgs.path('../output/C3D')),
+        #                        widget='DirChooser',
+        #                        help='Output directory for c3d file')
+
+        # === anybody === #
+        anybody_parser = subs.add_parser(ACTION_ANYBODY, help='Anybody Simulation')
+        anybody_group = anybody_parser.add_argument_group(
+            "Source files",
+            "Make a selection for the source files used for the AnyBody analysis",
+            gooey_options={
+                'show_border': True,
+                'columns': 1
+            }
+        )
+        anybody_file_group = anybody_group.add_mutually_exclusive_group(
+            required=True,
+            gooey_options={
+                'initial_selection': 0
+            })
+
+        anybody_file_group.add_argument('-any_interpol_files',
+                                        metavar='Use existing vector files (in {})'.format(AnyPy.INTERPOL_DIR),
+                                        help='Use interpolation vector files from AnyBody project default directory',
+                                        action='store_true')
+
+        anybody_file_group.add_argument('-any_bvh_file',
+                                        metavar='Source of the *.bvh file',
+                                        action='store',
+                                        default=stored_args.get(
+                                            ACTION_ANYBODY, 'any_bvh_file',
+                                            LeapGui.StoredArgs.path('../output/BVH/RightHand.bvh')),
+                                        widget='FileChooser',
+                                        help='Choose a bvh file to be converted to the interpolation vector files')
+
+        anybody_file_group.add_argument('-any_files_dir',
+                                        metavar='Source (.any)',
+                                        action='store',
+                                        default=stored_args.get(
+                                            ACTION_ANYBODY, 'any_files_dir',
+                                            LeapGui.StoredArgs.path('../output/Anybody')),
+                                        widget='DirChooser',
+                                        help='Source directory that contains interpolation *.any files for AnyBody')
+
+        anybody_group.add_argument('any_main_file',
+                                   metavar='Source of HAND.Main.any',
+                                   action='store',
+                                   default=stored_args.get(ACTION_ANYBODY, 'any_main_file', ''),
+                                   widget='FileChooser',
+                                   help='Choose the main AnyBody file for the calculation')
+
+        anybody_group.add_argument('-start_frame',
+                                   metavar='Start Frame',
+                                   help='default: 1',
+                                   action='store',
+                                   # default=stored_args.get(ACTION_ANYBODY, 'start_frame', '1'),
+                                   gooey_options={
+                                       'validator': {
+                                           'test': '1 <= int(user_input)',
+                                           'message': 'Must be greater or equal than 1'
+                                       }
+                                   },
+                                   type=int)
+
+        anybody_group.add_argument('-end_frame',
+                                   metavar='End Frame',
+                                   help='default: end',
+                                   action='store',
+                                   # default=stored_args.get(ACTION_ANYBODY, 'end_frame', 'end'),
+                                   gooey_options={
+                                       'validator': {
+                                           'test': '("end" in user_input.lower()) or (1 <= int(user_input))',
+                                           'message': 'Must be a positive value and greater than the start frame'
+                                       }
+                                   })
+
+        operation_group = anybody_parser.add_argument_group(
+            "Operations",
+            "Select which operations should be executed by AnyBody",
+            gooey_options={
+                'show_border': True,
+                'columns': 1
+            }
+        )
+
+        operation_group.add_argument('-load',
+                                     metavar='Load AnyBody model',
+                                     action='store_true')
+
+        operation_group.add_argument('-initial_conditions',
+                                     metavar='Calc initial conditions',
+                                     action='store_true')
+
+        operation_group.add_argument('-kinematic',
+                                     metavar='Calc kinematic analysis',
+                                     action='store_true')
+
+        operation_group.add_argument('-inverse_dynamics',
+                                     metavar='Calc inverse dynamics',
+                                     action='store_true')
+
+        operation_group.add_argument('-nstep',
+                                     metavar='Time steps',
+                                     help='Number of equally spaced time steps\n'
+                                          '(leave empty for not changing the setting)',
+                                     action='store',
+                                     # default=stored_args.get(ACTION_ANYBODY, 'nstep', '50'),
+                                     gooey_options={
+                                         'validator': {
+                                             'test': '1 <= int(user_input)',
+                                             'message': 'Must be greater or equal than 1'
+                                         }
+                                     },
+                                     type=int)
+
+        # operation_group.add_argument('-order',
+        #                              metavar='Order of B-spline interpolation',
+        #                              help='Interpolates between the data points with a B-spline using this order'
+        #                                   '\n(leave empty for using the default value)',
+        #                              action='store',
+        #                              # default=stored_args.get(ACTION_ANYBODY, 'order', '4'),
+        #                              gooey_options={
+        #                                  'validator': {
+        #                                      'test': '1 <= int(user_input)',
+        #                                      'message': 'Must be greater or equal than 1'
+        #                                  }
+        #                              },
+        #                              type=int)
+
+        result_group = anybody_parser.add_argument_group(
+            "Results",
+            gooey_options={
+                'show_border': True,
+                'columns': 1
+            }
+        )
+
+        result_group.add_argument('-plot',
+                                  metavar='Open the plot after the analysis',
+                                  action='store_true')
+
+        result_group.add_argument('-output_file_path',
+                                  metavar='Save .anydata.h5 file',
+                                  action='store',
+                                  default=stored_args.get(
+                                      ACTION_ANYBODY, 'output_file_path',
+                                      LeapGui.StoredArgs.path('../output/Anybody/FreeHand.anydata.h5')),
+                                  widget='FileSaver',
+                                  help='Save .anydata.h5 file to save the analysis results')
+
+        result_group.add_argument('-replay_output',
+                                  metavar='Open AnyBody and load the results',
+                                  action='store_true')
+
+        # === converter === #
+        converter_parser = subs.add_parser(ACTION_CONVERTER, help='Convert a BVH-File in .any-Files')
+        converter_group = converter_parser.add_argument_group(
+            "Converter",
+            "Convert a BVH-File in .any-Files",
+            gooey_options={
+                'show_border': True,
+                'columns': 1
+            }
+        )
+
+        converter_group.add_argument('bvh_file',
+                                     metavar='Source: *.bvh',
+                                     action='store',
+                                     default=stored_args.get(
+                                         ACTION_CONVERTER, 'bvh_file',
+                                         LeapGui.StoredArgs.path('../output/BVH/RightHand.bvh')),
+                                     widget='FileChooser',
+                                     help='Source bvh-file to convert')
+
+        # converter_group.add_argument('-any_file',
+        #                              metavar='Convert to .any files',
+        #                              action='store_true')
+
+        # converter_group.add_argument('-c3d',
+        #                              metavar='Convert to .c3d files',
+        #                              action='store_true')
+
+        converter_group.add_argument('file_dir',
+                                     metavar='Target: store files',
+                                     action='store',
+                                     default=stored_args.get(
+                                         ACTION_CONVERTER, 'file_dir', LeapGui.StoredArgs.path('../output')),
+                                     widget='DirChooser',
+                                     help='Directory to store the converted files')
+
+        # === bvh animation === #
+        animation_parser = subs.add_parser(ACTION_ANIMATION, help='Show an animation for a BVH file')
+        animation_group = animation_parser.add_argument_group(
+            "Animation",
+            "Select a BVH file to be animated",
+            gooey_options={
+                'show_border': True,
+                'columns': 1
+            }
+        )
+
+        animation_group.add_argument('bvh_animation',
+                                     metavar='BVH file path',
+                                     action='store',
+                                     default=stored_args.get(
+                                         ACTION_ANIMATION, 'bvh_animation',
+                                         LeapGui.StoredArgs.path('../output/BVH/RightHand.bvh')),
+                                     widget='FileChooser')
+
+        # start the UI and save arguments to json for next run
+        stored_args.save(parser.parse_args())
+
+    class StoredArgs:
+        """class for loading and saving arguments from/to json, also to handle default values"""
+
+        def __init__(self):
+            self.stored_args = {}
+            self.loaded_actions = {}
+            # get the script name without the extension & use it to build up the json filename
+            script_name = os.path.splitext(os.path.basename(__file__))[0]
+            self.args_file = "{}-args.json".format(script_name)
+
+        def load(self):
+            # Read in the prior arguments as a dictionary
+            if os.path.isfile(self.args_file):
+                with open(self.args_file) as data_file:
+                    self.stored_args = json.load(data_file)
+                    self.loaded_actions = self.stored_args.keys()
+            return self
+
+        def get(self, action, arg, default):
+            return self.stored_args[action].get(arg) if action in self.loaded_actions else default
+
+        @staticmethod
+        def path(relative_path):
+            return os.path.normpath(os.path.join(os.getcwd(), relative_path))
+
+        def save(self, args):
+            # Store the values of the arguments to the environment to access them in the code
+            env.save_arguments(args)
+            # Store the values of the arguments so we have them next time we run
+            with open(self.args_file, 'w') as data_file:
+                # Using vars(args) returns the data as a dictionary
+                self.stored_args[EXECUTED_COMMAND] = args.command
+                self.stored_args[args.command] = vars(args)
+                json.dump(self.stored_args, data_file)
+
+    @staticmethod
+    def run():
+        """Open the Gooey GUI and then run the selected action with the chosen arguments"""
+        LeapGui.parse_args()
+
+        # Record, Anybody, Converter
+        if env.config.command == ACTION_RECORD:
+            from GuiControl import GuiControl
+            gui = GuiControl()
+            gui.set_windows_record()
+
+            import time
+            countdown = 5
+            for ii in range(countdown):
+                print("Record starting in {} seconds ...".format(countdown-ii))
+                time.sleep(1)
+
+            LeapRecord.start_recording()
+
+            gui.end_record()
+            print("End of recording\n")
+
+            if env.config.show_animation:
+                print("Loading the animation ...")
+                p = Process(target=bvh_animation.animate)
+                p.start()
+                # wait for bvh_animation to be closed
+                p.join()
+            return True
+
+        if env.config.command == ACTION_ANYBODY:
+            from LogWatcher import log_watcher
+            anypy = AnyPy(env.config.any_main_file, env.config.any_files_dir)
+            log_watcher.start(os.path.join(anypy.any_path, anypy.LOG_FILE))
+            run_status = anypy.run()
+            log_watcher.stop()
+            if not run_status:
+                # AnyBody operations were not successful
+                return False
+            if env.config.replay_output:
+                anypy.post_operations()
+            if env.config.plot:
+                anypy.plot()
+            return True
+
+        if env.config.command == ACTION_CONVERTER:
+            from AnyWriter import AnyWriter
+            any_writer = AnyWriter(template_directory='config/anybody_templates/',
+                                   output_directory=env.config.file_dir + '/')
+            any_writer.write(Pymo_BVHParser().parse(env.config.bvh_file))
+            return True
+
+        if env.config.command == ACTION_ANIMATION:
+            print("Loading the animation ...")
+            bvh_animation.bvh_data = Pymo_BVHParser().parse(env.config.bvh_animation)
+            bvh_animation.animate()
+
+
+if __name__ == "__main__":
+    LeapGui.run()