Example #1
0
    def loadMonitors(self):
        self.active_monitors = {}
        resolution = options.GetOption("Resolution")
        data_folder = options.GetOption("Data_Folder")
        monitorsData = options.getMonitorsData()

        for mn in monitorsData:
            m = monitorsData[mn]

            track_type = ['DISTANCE', 'VBS',
                          'XY_COORDS'].index(m['track_type'])

            if type(m['source']) == int:
                source = int(m['source']) - 1
            else:
                source = m['source']

            output_file = m['outputfile'] or os.path.join(
                data_folder, 'Monitor%02d.txt' % mn)

            self.active_monitors[mn] = pysolovideo.Monitor()
            success = self.active_monitors[mn].setSource(source, resolution)
            if success:
                self.active_monitors[mn].setTracking(True, track_type,
                                                     m['mask_file'],
                                                     output_file)
                self.active_monitors[mn].SDserialPort = m['serial_port']
                self.active_monitors[mn].inactivity_threshold = m[
                    'inactivity_threshold'] or None
                #self.changeIcon(mn, 1)
            else:
                #self.changeIcon(mn, -1)
                pass

        pysolovideo.MONITORS = self.active_monitors
 def onRefresh(self):
     monitor_number = options.GetOption("Monitors")
     if self.mon_num != monitor_number:
         self.scrollThumbnails.updateMonitors(self.mon_num, monitor_number)
     tn_size = options.GetOption("ThumbnailSize")
     if self.thumb_size != tn_size:
         self.scrollThumbnails.updateThumbs(self.thumb_size, tn_size)
     self.PanelOneSizer.Layout()
     self.Layout()
    def onLoadMask(self, event):
        """
        Load Mask from file
        """
        print('Load Mask Function')  # temporary debug print
        wildcard = "PySolo Mask (*.msk) | *.msk | " \
                   "All files (*.*)|*.*"

        print('before filedialog')  # temporary debug print
        dlg = wx.FileDialog(self,
                            message="Choose a file",
                            defaultDir=options.GetOption("Mask_Folder"),
                            defaultFile="",
                            wildcard=wildcard,
                            style=wx.FD_OPEN | wx.FD_CHANGE_DIR)
        print('after filedialog')  # temporary debug print
        if dlg.ShowModal() == wx.ID_OK:
            print('showmodal is true')  # temporary debug print
            path = dlg.GetPath()
            print('Load Path = ' + path)  # temporary debug print
            self.fsPanel.mon.saveROIS(
                path)  # load set to save temporarily since load isn't working
            self.currentMaskTXT.SetValue(os.path.split(path)[1])

        print('done')  # temporary debug print
        dlg.Destroy()
    def onSaveMask(self, event):
        # temporary debug print
        print(
            'Current Mask Name = ' + self.currentMaskTXT.GetValue()
        )  # each switch between file types adds another .msk to the filename
        wildcard = "PySolo Mask (*.msk) | *.msk |" \
                   "All files (*.*)|*.*"

        print('wildcard = ' + wildcard)  # temporary debug print

        dlg = wx.FileDialog(self,
                            message="Save file as ...",
                            defaultDir=options.GetOption("Mask_Folder"),
                            defaultFile=self.currentMaskTXT.GetValue(),
                            wildcard=wildcard,
                            style=wx.SAVE | wx.FD_OVERWRITE_PROMPT)

        print('Current Mask Name = ' +
              self.currentMaskTXT.GetValue())  # temporary debug print
        #        dlg.SetFilterIndex(2)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            print('save path = ' + path)  # temporary debug print
            self.fsPanel.mon.saveROIS(path)
            self.currentMaskTXT.SetValue(os.path.split(path)[1])

        dlg.Destroy()
Example #5
0
    def onRefresh(self):
        """
        Called when the options dialog has been closed with an "OK"
        """
        # Determine what changes must be made
        old_mons = self.mon_num  # How many monitors we have right now
        old_cams = self.num_cams  # How many cameras we have right now
        self.mon_num = options.GetOption(
            "Monitors")  # Get desired number of monitors from config file
        self.num_cams = options.GetOption(
            "Webcams")  # Get desired number of webcams from config file

        if old_mons != self.mon_num:  # If current number of monitors doesn't match config file, update
            self.updateMonNum(old_mons)
        if old_cams != self.num_cams:  # If current number of cameras doesn't match config file, update
            self.updateNumCams(old_cams)
Example #6
0
    def __init__(self, parent):

        wx.Panel.__init__(self, parent)

        monitor_number = options.GetOption("Monitors")
        tn_size = options.GetOption("ThumbnailSize")

        self.temp_source = ''
        self.source = ''
        self.sourceType = -1

        self.scrollThumbnails = panelGridView(self,
                                              gridSize=monitor_number,
                                              thumbnailSize=tn_size)
        self.lowerPanel = panelConfigure(self)

        self.PanelOneSizer = wx.BoxSizer(wx.VERTICAL)
        self.PanelOneSizer.Add(self.scrollThumbnails, 1, wx.EXPAND, 0)
        self.PanelOneSizer.Add(self.lowerPanel, 0, wx.EXPAND, 0)
        self.SetSizer(self.PanelOneSizer)
Example #7
0
    def makePanel(self, parent, name):
        """
        """
        tp = wx.Panel(parent, -1, style=wx.TAB_TRAVERSAL)

        self.virtualw = wx.ScrolledWindow(tp)

        sz1 = wx.BoxSizer(wx.VERTICAL)

        titleFont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD)
        items = []

        for option in options.getOptionsNames(name):
            option_name = option
            option_value = str(options.GetOption(option_name))
            option_description = options.getOptionDescription(option_name)

            items.append(
                wx.StaticText(self.virtualw, -1,
                              '\nSet value of the variable: %s' % option_name))
            items[-1].SetFont(titleFont)
            items.append(wx.StaticText(self.virtualw, -1, option_description))

            if "FOLDER" in option_name.upper():
                items.append(
                    filebrowse.DirBrowseButton(self.virtualw,
                                               -1,
                                               size=(400, -1),
                                               changeCallback=partial(
                                                   self.__saveValue,
                                                   option_name),
                                               startDirectory="."))
                items.append((wx.StaticLine(self.virtualw), 0,
                              wx.EXPAND | wx.TOP | wx.BOTTOM, 5))

            else:  #for boolean first choice is always True, second choice always False

                items.append(wx.TextCtrl(self.virtualw, -1,
                                         value=option_value))
                items[-1].Bind(wx.EVT_TEXT,
                               partial(self.__saveValue, option_name))
                items.append((wx.StaticLine(self.virtualw), 0,
                              wx.EXPAND | wx.TOP | wx.BOTTOM, 5))

        sz1.AddMany(items)
        self.virtualw.SetSizer(sz1)
        sz1.Fit(self.virtualw)

        self.virtualw.SetScrollRate(20, 20)
        TreeBookPanelSizer = wx.BoxSizer()
        TreeBookPanelSizer.Add(self.virtualw, 1, wx.GROW | wx.EXPAND, 0)
        tp.SetSizer(TreeBookPanelSizer)
        return tp
Example #8
0
    def drawPanel(self):  # Draw the main interface
        for child in self.GetChildren():
            child.Destroy()

        self.mon_num = options.GetOption(
            "Monitors")  # Get number of monitors from config file
        self.num_cams = options.GetOption(
            "Webcams")  # Get number of cameras from config file
        monitorsData = options.getMonitorsData()
        self.monitorsData = monitorsData

        WebcamsList = [
            'Camera %02d' % (int(w) + 1) for w in range(self.num_cams)
        ]  # Generate a list of cameras
        colLabels = [
            'Status', 'Monitor', 'Source', 'Mask', 'Output', 'Track type',
            'Track', 'uptime', 'preview'
        ]
        tracktypes = ['DISTANCE', 'VBS', 'XY_COORDS']

        # create layouts and their items
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        self.drawMonitorGrid(self.mon_num, colLabels, WebcamsList, tracktypes)
        btnSizer = self.drawButtons(
        )  # The buttons that do not go with a particular monitor

        # Add monitor grid and buttons to main layout
        mainSizer.Add(self.gridSizer, 1, wx.EXPAND, 0)
        mainSizer.Add(btnSizer, 0, wx.ALL, 5)
        mainSizer.Layout()

        # Store sizers as global variables in order to update UI later
        self.mainSizer = mainSizer
        self.SetSizer(
            mainSizer)  # Make the panel display the mainSizer and its contents
        self.Refresh()
Example #9
0
    def makePanel(self, parent, name):
        # Make a new panel
        tp = wx.Panel(parent, -1,  style = wx.TAB_TRAVERSAL)

        # The panel contains a window, allowing us to scroll
        self.virtualw = wx.ScrolledWindow(tp)

        sz1 = wx.BoxSizer(wx.VERTICAL)

        titleFont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD)
        items = []

        # Put all of the options on the page
        for option in options.getOptionsNames(name):
            option_name = option
            option_value= str(options.GetOption(option_name))
            option_description = options.getOptionDescription(option_name)

            # Display option name and description
            items.append ( wx.StaticText(self.virtualw, -1, '\nSet value of the variable: %s' % option_name))
            items[-1].SetFont(titleFont)
            items.append (  wx.StaticText(self.virtualw, -1, option_description) )

            # If the option is regarding a folder, make a Browse button
            if "FOLDER" in option_name.upper():
                items.append ( filebrowse.DirBrowseButton(self.virtualw, -1, size=(400, -1), changeCallback = partial(self.__saveValue, option_name), startDirectory="."  ))
                items.append ( (wx.StaticLine(self.virtualw), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ))

            # Add a text field for the user to change the option
            else: #for boolean first choice is always True, second choice always False
                items.append ( wx.TextCtrl (self.virtualw, -1, value=option_value))
                items[-1].Bind (wx.EVT_TEXT, partial (self.__saveValue, option_name) )
                items.append ( (wx.StaticLine(self.virtualw), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ))


        # Put all of them in a boxsizer
        sz1.AddMany (items)
        self.virtualw.SetSizer(sz1)
        sz1.Fit(self.virtualw) # fit to window

        self.virtualw.SetScrollRate(20,20)
        TreeBookPanelSizer = wx.BoxSizer()
        TreeBookPanelSizer.Add(self.virtualw,  1, wx.GROW | wx.EXPAND, 0)
        tp.SetSizer(TreeBookPanelSizer)
        return tp
Example #10
0
    def onLoadMask(self, event):
        """
        Load Mask from file
        """

        wildcard = "pySolo mask file (*.msk)|*.msk"

        dlg = wx.FileDialog(self,
                            message="Choose a file",
                            defaultDir=options.GetOption("Mask_Folder"),
                            defaultFile="",
                            wildcard=wildcard,
                            style=wx.OPEN | wx.CHANGE_DIR)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.fsPanel.mon.loadROIS(path)
            self.currentMaskTXT.SetValue(os.path.split(path)[1])

        dlg.Destroy()
Example #11
0
    def onSaveMask(self, event):
        """
        Save ROIs to File
        """
        
        filename = '%s.msk' % self.monitor_name.replace(' ','_')
        wildcard = "pySolo mask file (*.msk)|*.msk"
        
        dlg = wx.FileDialog(
            self, message="Save file as ...", defaultDir=options.GetOption("Mask_Folder"), 
            defaultFile=filename, wildcard=wildcard, style=wx.SAVE
            )

        #dlg.SetFilterIndex(2)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.fsPanel.mon.saveROIS(path)
            self.currentMaskTXT.SetValue(os.path.split(path)[1])
        
        dlg.Destroy()
        return path
Example #12
0
    def updateNumCams(self, old):
        """
        Updates the camera combofilebrowser when the number of cameras changes
        """
        WebcamsList = [
            'Camera %02d' % (int(w) + 1) for w in range(self.num_cams)
        ]
        i = 11  # Skip the labels, start with first monitor
        for mn in range(1, self.mon_num + 1):  # For each monitor
            if not options.HasMonitor(mn):
                options.SetMonitor(
                    mn)  #If monitor does not exist in options we create it
            md = options.GetMonitor(mn)

            try:
                _, source = os.path.split(md['source'])
            except:
                source = 'Camera %02d' % (md['source'])
            self.gridSizer.Hide(i)
            self.gridSizer.Remove(i)  # Remove the outdated dropdown menu
            self.gridSizer.Insert(
                i,  # Insert a new dropdown menu in its place
                comboFileBrowser(
                    self,
                    wx.ID_ANY,
                    size=(-1, -1),
                    dialogTitle="Choose an Input video file",
                    startDirectory=options.GetOption("Data_Folder"),
                    value=source,
                    choices=WebcamsList,
                    fileMask="Video File (*.*)|*.*",
                    browsevalue="Browse for video...",
                    changeCallback=partial(self.onChangeDropDown,
                                           [mn, "source"])),
                0,
                wx.ALL | wx.ALIGN_CENTER,
                5)
            self.gridSizer.Layout()  # Show changes on GUI
            i += 9  # Move to next monitor (gridsizer rows are 9 items wide)
Example #13
0
    def __init__(self, parent):
        """
        """
        
        wx.Panel.__init__(self, parent, wx.ID_ANY)

        self.monitor_name = ''
        self.fsPanel = previewPanel(self, size=options.GetOption("Resolution"), showtime=True)
        
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_4 = wx.BoxSizer(wx.VERTICAL)
        
        #Static box1: monitor input
        sb_1 = wx.StaticBox(self, -1, "Select Monitor")#, size=(250,-1))
        sbSizer_1 = wx.StaticBoxSizer (sb_1, wx.VERTICAL)
        self.MonitorList = ['Monitor %s' % (int(m) + 1) for m in range(options.GetOption("Monitors"))]
        self.thumbnailNumber = wx.ComboBox(self, -1, size=(-1,-1) , choices=self.MonitorList, style=wx.CB_DROPDOWN | wx.CB_READONLY | wx.CB_SORT)
        self.Bind(wx.EVT_COMBOBOX, self.onChangeMonitor, self.thumbnailNumber)

        self.sourceTXTBOX =  wx.TextCtrl (self, -1, "No monitor selected", style=wx.TE_READONLY)

        sbSizer_1.Add ( self.thumbnailNumber, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT|wx.TOP, 5 )
        sbSizer_1.Add ( self.sourceTXTBOX, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT|wx.TOP|wx.EXPAND, 5 )

        #Static box2: mask parameters
        sb_2 = wx.StaticBox(self, -1, "Mask Editing")#, size=(250,-1))
        sbSizer_2 = wx.StaticBoxSizer (sb_2, wx.VERTICAL)
        fgSizer_1 = wx.FlexGridSizer( 0, 2, 0, 0 )
        
        self.btnClear = wx.Button( self, wx.ID_ANY, label="Clear All")
        self.Bind(wx.EVT_BUTTON, self.fsPanel.ClearAll, self.btnClear)

        self.btnClearLast = wx.Button( self, wx.ID_ANY, label="Clear selected")
        self.Bind(wx.EVT_BUTTON, self.fsPanel.ClearLast, self.btnClearLast)

        
        self.AFValue = wx.TextCtrl (self, -1, "32")
        self.btnAutoFill = wx.Button( self, wx.ID_ANY, label="Auto Fill")
        self.Bind(wx.EVT_BUTTON, self.onAutoMask, self.btnAutoFill)
        #self.btnAutoFill.Enable(False)

        fgSizer_1.Add (self.btnClear)
        fgSizer_1.Add (self.btnClearLast)
        fgSizer_1.Add (self.AFValue)
        fgSizer_1.Add (self.btnAutoFill)
        
        sbSizer_2.Add (fgSizer_1)


        #Static box3: mask I/O
        sb_3 = wx.StaticBox(self, -1, "Mask File")#, size=(250,-1))
        sbSizer_3 = wx.StaticBoxSizer (sb_3, wx.VERTICAL)

        self.currentMaskTXT = wx.TextCtrl (self, -1, "No Mask Loaded", style=wx.TE_READONLY)

        btnSizer_1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btnLoad = wx.Button( self, wx.ID_ANY, label="Load Mask")
        self.Bind(wx.EVT_BUTTON, self.onLoadMask, self.btnLoad)
        self.btnSave = wx.Button( self, wx.ID_ANY, label="Save Mask")
        self.Bind(wx.EVT_BUTTON, self.onSaveMask, self.btnSave)
        self.btnSaveApply = wx.Button( self, wx.ID_ANY, label="Save and Apply")
        self.Bind(wx.EVT_BUTTON, self.onSaveApply, self.btnSaveApply)
        
        btnSizer_1.Add(self.btnLoad)
        btnSizer_1.Add(self.btnSave)
        btnSizer_1.Add(self.btnSaveApply)

        sbSizer_3.Add ( self.currentMaskTXT, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT|wx.TOP|wx.EXPAND, 5 )
        sbSizer_3.Add (btnSizer_1, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT|wx.TOP, 5 )

        ##
        
        #Static box4: help
        sb_4 = wx.StaticBox(self, -1, "Help")
        sbSizer_4 = wx.StaticBoxSizer (sb_4, wx.VERTICAL)
        titleFont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD)
        instr = [ ('Left mouse button - single click outside ROI', 'Start dragging ROI. ROI will be a perfect rectangle'),
                  ('Left mouse button - single click inside ROI', 'Select ROI. ROI turns red.'),
                  ('Left mouse button - double click', 'Select corner of ROI.\nWill close ROI after fourth selection'),
                  ('Middle mouse button - single click', 'Add currently selected ROI. ROI turns white.'),
                  ('Right mouse button - click', 'Remove selected currently selected ROI'),
                  ('Auto Fill', 'Will fill 32 ROIS (16x2) to fit under the last two\nselected points. To use select first upper left corner,\n then the lower right corner, then use "Auto Fill".')
                  ]
                  
        for title, text in instr:
            t = wx.StaticText(self, -1, title); t.SetFont(titleFont)
            sbSizer_4.Add( t, 0, wx.ALL, 2 )
            sbSizer_4.Add(wx.StaticText(self, -1, text) , 0 , wx.ALL, 2 )
            sbSizer_4.Add ( (wx.StaticLine(self)), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 )
        
        sizer_4.Add(sbSizer_1, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT|wx.TOP|wx.EXPAND, 5 )
        sizer_4.Add(sbSizer_2, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT|wx.TOP|wx.EXPAND, 5 )
        sizer_4.Add(sbSizer_3, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT|wx.TOP|wx.EXPAND, 5 )
        sizer_4.Add(sbSizer_4, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT|wx.TOP|wx.EXPAND, 5 )

        
        sizer_3.Add(self.fsPanel, 0, wx.LEFT|wx.TOP, 20 )
        sizer_3.Add(sizer_4, 0, wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT|wx.TOP, 5 )

        sizer_1.Add(sizer_3, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT|wx.TOP, 5 )
        sizer_1.Add(sizer_2, 0, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT|wx.TOP, 5 )

        
        self.SetSizer(sizer_1)
        print wx.Window.FindFocus()
        
        self.Bind( wx.EVT_CHAR, self.fsPanel.onKeyPressed )
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

# %%
import wx  # GUI functions
import os
import numpy as np
import cv2  # camera / video control
import pysolovideo
import pvg_options
from sys import argv  # file I/O
from pvg_common import previewPanel, options

x, y = options.GetOption("Resolution")  # screen resolution

# %%


class panelLiveView(wx.Panel):
    """
    Panel Number 2
    Live view of selected camera
    """

    # %%
    def __init__(self, parent):

        self.panel = wx.Panel.__init__(self, parent)
        self.fsPanel = previewPanel(self,
    def __init__(self, parent):
        """
        """
        wx.Panel.__init__(self,
                          parent,
                          wx.ID_ANY,
                          size=(-1, 300),
                          style=wx.SUNKEN_BORDER | wx.TAB_TRAVERSAL)
        self.parent = parent

        self.thumbnail = None
        self.mask_file = None
        self.source = None
        self.sourceType = None
        self.track = None
        self.trackType = None

        lowerSizer = wx.BoxSizer(wx.HORIZONTAL)

        #Static box1 (LEFT)
        sb_1 = wx.StaticBox(self, -1, "Select Monitor")  #, size=(250,-1))
        sbSizer_1 = wx.StaticBoxSizer(sb_1, wx.VERTICAL)

        n_monitors = options.GetOption("Monitors")
        self.MonitorList = [
            'Monitor %s' % (int(m) + 1) for m in range(n_monitors)
        ]
        self.thumbnailNumber = wx.ComboBox(self,
                                           -1,
                                           size=(-1, -1),
                                           choices=self.MonitorList,
                                           style=wx.CB_DROPDOWN
                                           | wx.CB_READONLY | wx.CB_SORT)
        self.Bind(wx.EVT_COMBOBOX, self.onChangingMonitor,
                  self.thumbnailNumber)

        self.currentSource = wx.TextCtrl(self,
                                         -1,
                                         "No Source Selected",
                                         style=wx.TE_READONLY)

        btnSizer_1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btnPlay = wx.Button(self, wx.ID_FORWARD, label="Play")
        self.btnStop = wx.Button(self, wx.ID_STOP, label="Stop")
        self.Bind(wx.EVT_BUTTON, self.onPlay, self.btnPlay)
        self.Bind(wx.EVT_BUTTON, self.onStop, self.btnStop)
        self.btnPlay.Enable(False)
        self.btnStop.Enable(False)
        self.applyButton = wx.Button(self, wx.ID_APPLY)
        self.applyButton.SetToolTip(wx.ToolTip("Apply and Save to file"))
        self.Bind(wx.EVT_BUTTON, self.onApplySource, self.applyButton)

        btnSizer_1.Add(self.btnPlay, 0, wx.ALIGN_LEFT | wx.ALL, 5)
        btnSizer_1.Add(self.btnStop, 0,
                       wx.ALIGN_CENTER | wx.LEFT | wx.TOP | wx.DOWN, 5)
        btnSizer_1.Add(self.applyButton, 0,
                       wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.TOP, 5)

        sbSizer_1.Add(self.thumbnailNumber, 0,
                      wx.ALIGN_CENTRE | wx.LEFT | wx.RIGHT | wx.TOP, 5)
        sbSizer_1.Add(
            self.currentSource, 0,
            wx.EXPAND | wx.ALIGN_CENTRE | wx.LEFT | wx.RIGHT | wx.TOP, 5)
        sbSizer_1.Add(btnSizer_1, 0, wx.EXPAND | wx.ALIGN_BOTTOM | wx.TOP, 5)

        lowerSizer.Add(sbSizer_1, 0, wx.EXPAND | wx.ALL, 5)

        #Static box2 (CENTER)
        sb_2 = wx.StaticBox(self, -1, "Select Video input")
        sbSizer_2 = wx.StaticBoxSizer(sb_2, wx.VERTICAL)
        grid2 = wx.FlexGridSizer(0, 2, 0, 0)

        n_cams = options.GetOption("Webcams")
        self.WebcamsList = ['Webcam %s' % (int(w) + 1) for w in range(n_cams)]
        rb1 = wx.RadioButton(self, -1, 'Camera', style=wx.RB_GROUP)
        source1 = wx.ComboBox(self,
                              -1,
                              size=(285, -1),
                              choices=self.WebcamsList,
                              style=wx.CB_DROPDOWN | wx.CB_READONLY
                              | wx.CB_SORT)
        self.Bind(wx.EVT_COMBOBOX, self.sourceCallback, source1)

        rb2 = wx.RadioButton(self, -1, 'File')
        source2 = FileBrowseButton(self,
                                   -1,
                                   labelText='',
                                   size=(300, -1),
                                   changeCallback=self.sourceCallback)

        rb3 = wx.RadioButton(self, -1, 'Folder')
        source3 = DirBrowseButton(self,
                                  style=wx.DD_DIR_MUST_EXIST,
                                  labelText='',
                                  size=(300, -1),
                                  changeCallback=self.sourceCallback)

        self.controls = []
        self.controls.append((rb1, source1))
        self.controls.append((rb2, source2))
        self.controls.append((rb3, source3))

        for radio, source in self.controls:
            grid2.Add(radio, 0,
                      wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2)
            grid2.Add(source, 0,
                      wx.ALIGN_CENTRE | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2)
            self.Bind(wx.EVT_RADIOBUTTON, self.onChangeSource, radio)
            source.Enable(False)

        self.controls[0][1].Enable(True)

        #grid2.Add(wx.StaticText(self, -1, ""))

        sbSizer_2.Add(grid2)
        lowerSizer.Add(sbSizer_2, 0, wx.EXPAND | wx.ALL, 5)

        #Static box3 (RIGHT)
        sb_3 = wx.StaticBox(self, -1, "Set Tracking Parameters")
        sbSizer_3 = wx.StaticBoxSizer(sb_3, wx.VERTICAL)

        sbSizer_31 = wx.BoxSizer(wx.HORIZONTAL)

        self.activateTracking = wx.CheckBox(self, -1, "Activate Tracking")
        self.activateTracking.SetValue(False)
        self.activateTracking.Bind(wx.EVT_CHECKBOX, self.onActivateTracking)

        self.isSDMonitor = wx.CheckBox(self, -1, "Sleep Deprivation Monitor")
        self.isSDMonitor.SetValue(False)
        self.isSDMonitor.Bind(wx.EVT_CHECKBOX, self.onSDMonitor)
        self.isSDMonitor.Enable(False)

        sbSizer_31.Add(self.activateTracking, 0,
                       wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 5)
        sbSizer_31.Add(self.isSDMonitor, 0,
                       wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 5)

        self.pickMaskBrowser = FileBrowseButton(self,
                                                -1,
                                                labelText='Mask File')

        #sbSizer_3.Add ( self.activateTracking , 0, wx.ALIGN_LEFT|wx.LEFT|wx.RIGHT|wx.TOP, 5 )
        sbSizer_3.Add(sbSizer_31, 0,
                      wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 5)
        sbSizer_3.Add(self.pickMaskBrowser, 0,
                      wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND,
                      5)

        #trackingTypeSizer = wx.Sizer(wx.HORIZONTAL)
        self.trackDistanceRadio = wx.RadioButton(
            self, -1, "Activity as distance traveled", style=wx.RB_GROUP)
        self.trackVirtualBM = wx.RadioButton(
            self, -1, "Activity as midline crossings count")
        self.trackPosition = wx.RadioButton(self, -1, "Only position of flies")
        sbSizer_3.Add(wx.StaticText(self, -1, "Calculate fly activity as..."),
                      0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 5)
        sbSizer_3.Add(self.trackDistanceRadio, 0,
                      wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 2)
        sbSizer_3.Add(self.trackVirtualBM, 0,
                      wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 2)
        sbSizer_3.Add(self.trackPosition, 0,
                      wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 2)

        lowerSizer.Add(sbSizer_3, -1, wx.EXPAND | wx.ALL, 5)

        self.SetSizer(lowerSizer)
        self.Bind(EVT_THUMBNAIL_CLICKED, self.onThumbnailClicked)
    def __init__(self, parent):

        self.panel = wx.Panel.__init__(self, parent)
        self.fsPanel = previewPanel(self,
                                    size=options.GetOption("Resolution"),
                                    showtime=True)

        # %%%%%%%%%%%%%%%%%% sizer_Left: Monitor Display
        #  Monitor Selection on top
        # Give title to the monitor selection combobox
        title_1 = wx.StaticText(self, wx.ID_ANY, 'Select Monitor')
        # create select monitor combobox on top of left column
        MonitorList = [
            'Monitor %s' % (int(m) + 1)
            for m in range(options.GetOption("Monitors"))
        ]  # not reflecting number of monitors in configuration
        thumbnailNumber = wx.ComboBox(self,
                                      wx.ID_ANY,
                                      choices=MonitorList,
                                      style=wx.CB_DROPDOWN | wx.CB_READONLY
                                      | wx.CB_SORT)
        self.Bind(wx.EVT_COMBOBOX, self.onChangeMonitor, thumbnailNumber)

        #   sizer for left side, top row control buttons
        sizer_top_Left = wx.BoxSizer(wx.HORIZONTAL)
        sizer_top_Left.Add(title_1, 0, wx.ALL, 5)  # menu title
        sizer_top_Left.Add(thumbnailNumber, 0, wx.ALL, 5)  # menu

        # %%  Movie Display on bottom
        videoWarper = wx.StaticBox(self,
                                   label='Movie Label',
                                   size=(x / 2, y * 2 / 3))
        videoBoxSizer = wx.StaticBoxSizer(videoWarper, wx.VERTICAL)
        videoFrame = wx.Panel(self, wx.ID_ANY, size=(640, 480))
        #        cap = cv2.VideoCapture('fly_movie.avi')                                # not working on thinkpad computer
        #        showCap = ShowCapture(videoFrame, cap)                                 # not working on thinkpad computer
        videoBoxSizer.Add(videoFrame, 0)

        #  Sizer for left side of display
        sizer_Left = wx.BoxSizer(wx.VERTICAL)
        sizer_Left.Add(sizer_top_Left, 0, wx.ALL, 5)  # monitor combobox
        sizer_Left.Add(videoBoxSizer, 0, wx.ALL, 5)  # movie

        # %%%%%%%%%%%%%%%%%%  sizer_Right:  Mask Editing
        #     Section Title
        mask_editing = wx.StaticText(self, wx.ID_ANY, 'Mask Editing')

        #      top row is single item, no sizer needed
        #
        # %% Clear Buttons
        btnClearAll = wx.Button(self, wx.ID_ANY, label="Clear All")
        #        self.Bind(wx.EVT_BUTTON, self.fsPanel.ClearAll, self.btnClearAll)
        #
        btnClearSelected = wx.Button(self, wx.ID_ANY, label="Clear Selected")
        #        self.Bind(wx.EVT_BUTTON, self.fsPanel.ClearLast, self.btnClearSelected)
        #
        #  sizer for clear buttons on right side of display
        sizer_ClrBtns_Right = wx.BoxSizer(wx.HORIZONTAL)
        sizer_ClrBtns_Right.Add(btnClearSelected, 0, wx.ALL,
                                5)  # clear selected
        sizer_ClrBtns_Right.Add(btnClearAll, 0, wx.ALL, 5)  # clear all

        # %%   AutoFill Value
        AFValue = wx.TextCtrl(self, -1, "32")  #  make this variable?

        btnAutoFill = wx.Button(self, wx.ID_ANY, label="Auto Fill")
        #        self.Bind(wx.EVT_BUTTON, self.onAutoMask, self.btnAutoFill)
        #        self.btnAutoFill.Enable(False)

        #  sizer for AutoFill controls
        sizer_Autofill_Right = wx.BoxSizer(wx.HORIZONTAL)
        sizer_Autofill_Right.Add(btnAutoFill, 0, wx.ALL, 5)  # Autofill
        sizer_Autofill_Right.Add(AFValue, 0, wx.ALL, 5)  # AF Value

        # %%  Right Side, Mask selection controls
        #   Mask Title
        Mask_File_Title = wx.StaticText(self, wx.ID_ANY, "Curent Mask")
        self.currentMaskTXT = wx.TextCtrl(self,
                                          wx.ID_ANY,
                                          value='No Mask Selected',
                                          size=(x / 4, 20))
        #                                     style=wx.TE_READONLY)                     #  get the current mask text for this
        #  sizer for current mask
        sizer_currmsk_Right = wx.BoxSizer(wx.HORIZONTAL)  # button section
        sizer_currmsk_Right.Add(Mask_File_Title, 0, wx.ALL, 5)  # title
        sizer_currmsk_Right.Add(self.currentMaskTXT, 0, wx.ALL, 5)  # title

        # %%
        #   Mask Buttons
        btnLoad = wx.Button(self, wx.ID_ANY, label="Load Mask")
        self.Bind(wx.EVT_BUTTON, self.onLoadMask, btnLoad)

        btnSave = wx.Button(self, wx.ID_ANY, label="Save Mask")
        self.Bind(wx.EVT_BUTTON, self.onSaveMask, btnSave)

        btnSaveApply = wx.Button(self, wx.ID_ANY, label="Save + Apply")
        #        self.Bind(wx.EVT_BUTTON, self.onSaveApply, self.btnSaveApply)

        #  sizer for mask buttons
        sizer_maskIO_Right = wx.BoxSizer(wx.HORIZONTAL)  # button section
        sizer_maskIO_Right.Add(btnLoad, 0, wx.ALL, 5)  # load mask btn
        sizer_maskIO_Right.Add(btnSave, 0, wx.ALL, 5)  # Save mask btn
        sizer_maskIO_Right.Add(btnSaveApply, 0, wx.ALL, 5)  # Apply mask btn

        # %% Mouse Instructions
        #   Section Title
        Controls_Title = wx.StaticText(self, wx.ID_ANY, "Mouse Controls")
        #        titleFont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD)
        instr = [
            ('   Left mouse button - single click outside ROI'),
            ('        Start dragging ROI. ROI will be a perfect rectangle'),
            ('   Left mouse button - single click inside ROI'),
            ('        Select ROI. ROI turns red.'),
            ('   Left mouse button - double click'),
            ('        Select corner of ROI.  Will close ROI after fourth selection'
             ), ('   Middle mouse button - single click'),
            ('        Add currently selected ROI. ROI turns white.'),
            ('   Right mouse button - click'),
            ('        Remove selected currently selected ROI'),
            ('   Auto Fill'),
            ('        Will fill 32 ROIS (16x2) to fit under the last two'),
            ('        selected points. To use select first upper left corner,'
             ), ('        then the lower right corner, then use "Auto Fill".')
        ]

        #
        #   sizer for instructions
        sizer_instr_Right = wx.BoxSizer(wx.VERTICAL)  # Help Section
        sizer_instr_Right.Add(Controls_Title, 0, wx.ALL, 5)  # title
        for txtline in instr:
            instr_line = wx.StaticText(self, wx.ID_ANY,
                                       txtline)  #; t.SetFont(titleFont)
            sizer_instr_Right.Add(instr_line, 0, wx.ALL, 1)  # next instrctn

# %%  sizer for right side of display
        sizer_Right = wx.BoxSizer(wx.VERTICAL)
        sizer_Right.Add(mask_editing, 0, wx.ALL, 5)  # section title
        sizer_Right.Add(sizer_ClrBtns_Right, 0, wx.ALL, 5)  # Mask Buttons
        sizer_Right.Add(sizer_currmsk_Right, 0, wx.ALL, 5)  # Mask Selection
        sizer_Right.Add(sizer_maskIO_Right, 0, wx.ALL, 5)  # Mask Selection
        sizer_Right.Add(sizer_Autofill_Right, 0, wx.ALL, 5)  # AutoFill
        sizer_Right.Add(sizer_instr_Right, 0, wx.ALL, 5)  # Mouse Instruction

        # %%  Full Panel
        sizer_All = wx.BoxSizer(wx.HORIZONTAL)  # put everything together
        sizer_All.Add(sizer_Left, 0, wx.ALL, 5)
        sizer_All.Add(sizer_Right, 0, wx.ALL, 5)

        self.SetSizer(sizer_All)  # displays the grid
Example #17
0
 def __set_properties(self):
     # begin wxGlade: mainFrame.__set_properties
     self.SetTitle("pySoloVideo")
     x, y = options.GetOption("Resolution")
     self.SetSize((x * 1.8, y * 1.4))
Example #18
0
    def drawPanel(self):
        """
        """    
        ###################################################

        for child in self.GetChildren():
            child.Destroy()

        mon_num = options.GetOption("Monitors")
        num_cams = options.GetOption("Webcams")
        monitorsData = options.getMonitorsData()
 
        WebcamsList = [ 'Camera %02d' % (int(w) +1) for w in range( num_cams ) ]
        colLabels = ['Status', 'Monitor', 'Source', 'Mask', 'Output', 'Track type', 'Track', 'uptime', 'preview']
        tracktypes = ['DISTANCE','VBS','XY_COORDS']
 
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        gridSizer = wx.FlexGridSizer (cols=len(colLabels), vgap=5, hgap=5)  #wx.BoxSizer(wx.VERTICAL)

        #FIRST ROW
        for key in colLabels:
            text = wx.StaticText(self, -1, key )
            text.SetFont( wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD) )
            gridSizer.Add(text, 0, wx.ALL|wx.ALIGN_CENTER, 5)
    
        self.status = []
        self.recordBTNS = []
        self.uptimeTXT = []

        #LOOP THROUGH
        for mn in range(1, mon_num+1):
            
            if not options.HasMonitor(mn): 
                options.SetMonitor(mn) #If monitor does not exist in options we create it
            
            md = options.GetMonitor(mn)
            
            try:
                _, source = os.path.split( md['source'] )
            except:
                source = 'Camera %02d' % ( md['source'] )
            
            _, mf = os.path.split(md['mask_file'])
            df = 'Monitor%02d.txt' % (mn)

            #ICON
            self.status.append( wx.StaticBitmap(self, -1, wx.EmptyBitmap(16,16)) )
            gridSizer.Add(self.status[-1], 0, wx.ALL|wx.ALIGN_CENTER, 5)
            self.changeIcon(mn)
            
            #TEXT
            gridSizer.Add(wx.StaticText(self, -1, "Monitor %s" % mn ), 0, wx.ALL|wx.ALIGN_CENTER, 5)

            #INPUT SOURCE
            gridSizer.Add(comboFileBrowser(self, wx.ID_ANY, size=(-1,-1), dialogTitle = "Choose an Input video file", startDirectory = options.GetOption("Data_Folder"), value = source, choices=WebcamsList, fileMask = "Video File (*.*)|*.*", browsevalue="Browse for video...", changeCallback = partial(self.onChangeDropDown, [mn, "source"])), 0, wx.ALL|wx.ALIGN_CENTER, 5 )
            
            #MASK FILE
            gridSizer.Add(comboFileBrowser(self, wx.ID_ANY, size=(-1,-1), dialogTitle = "Choose a Mask file", startDirectory = options.GetOption("Mask_Folder"), value = mf, fileMask = "pySolo mask file (*.msk)|*.msk", browsevalue="Browse for mask...", changeCallback = partial(self.onChangeDropDown, [mn, "mask_file"])), 0, wx.ALL|wx.ALIGN_CENTER, 5 )
            
            #OUTPUT FILE
            gridSizer.Add(comboFileBrowser(self, wx.ID_ANY, size=(-1,-1), dialogTitle = "Choose the output file", startDirectory = options.GetOption("Data_Folder"), value = md['outputfile'], fileMask = "Output File (*.txt)|*.txt", browsevalue="Browse for output...", changeCallback = partial(self.onChangeDropDown, [mn, "outputfile"])), 0, wx.ALL|wx.ALIGN_CENTER, 5 )

            #TRACKTYPE
            ttcb = wx.ComboBox(self, -1, size=(-1,-1), value=md['track_type'], choices=tracktypes, style=wx.CB_DROPDOWN | wx.CB_READONLY | wx.CB_SORT)
            ttcb.Bind (wx.EVT_COMBOBOX, partial(self.onChangeDropDown, [mn, "track_type"]))
            gridSizer.Add(ttcb , 0, wx.ALL|wx.ALIGN_CENTER, 5)
            
            #RECORD BUTTON
            self.recordBTNS.append ( wx.ToggleButton(self, wx.ID_ANY, 'Start') )
            self.recordBTNS[-1].Bind (wx.EVT_TOGGLEBUTTON, partial( self.onToggleRecording, mn))
            gridSizer.Add(self.recordBTNS[-1], 0, wx.ALL|wx.ALIGN_CENTER, 5)

            #UPTIME
            self.uptimeTXT.append(wx.TextCtrl(self, value="00:00:00", size=(140,-1)))
            gridSizer.Add(self.uptimeTXT[-1], 0, wx.ALL|wx.ALIGN_CENTER, 5)

            #VIEW BUTTON
            vb = wx.Button(self, wx.ID_ANY, 'View')
            vb.Bind(wx.EVT_BUTTON, partial( self.onViewMonitor, mn))
            gridSizer.Add(vb, 0, wx.ALL|wx.ALIGN_CENTER, 5)

        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        conf_btnSizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, 'Configuration'), wx.HORIZONTAL)
        acq_btnSizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, 'Acquisition'), wx.HORIZONTAL)

        self.saveOptionsBtn = wx.Button(self, wx.ID_ANY, 'Save')
        self.saveOptionsBtn.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.onSave, self.saveOptionsBtn)
        conf_btnSizer.Add (self.saveOptionsBtn, 0, wx.ALL, 5) 
        
        self.startBtn = wx.Button(self, wx.ID_ANY, 'Start All')
        self.stopBtn = wx.Button(self, wx.ID_ANY, 'Stop All')
        self.startBtn.Enable(True)
        self.stopBtn.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.onStopAll, self.stopBtn)
        self.Bind(wx.EVT_BUTTON, self.onStartAll, self.startBtn)
        acq_btnSizer.Add (self.startBtn, 0, wx.ALL, 5) 
        acq_btnSizer.Add (self.stopBtn, 0, wx.ALL, 5)

        btnSizer.Add(conf_btnSizer, 0, wx.ALL, 5) 
        btnSizer.Add(acq_btnSizer, 0, wx.ALL, 5) 
        
        #mainSizer.Add(self.FBconfig, 0, wx.EXPAND|wx.ALL, 5) 
        mainSizer.Add(gridSizer, 1, wx.EXPAND, 0)
        mainSizer.Add(btnSizer, 0, wx.ALL, 5)
        mainSizer.Layout()
        self.SetSizer(mainSizer)
        self.Refresh()
Example #19
0
    def drawSingleMonitor(self, mn, WebcamsList, tracktypes):
        if not options.HasMonitor(mn):
            options.SetMonitor(mn)
        md = options.GetMonitor(mn)

        try:
            _, source = os.path.split(md['source'])
        except:
            source = 'Camera %02d' % (md['source'])

        _, mf = os.path.split(md['mask_file'])
        df = 'Monitor%02d.txt' % (mn)

        #ICON
        self.status.append(wx.StaticBitmap(self, -1, wx.EmptyBitmap(16, 16)))
        self.gridSizer.Add(self.status[-1], 0, wx.ALL | wx.ALIGN_CENTER, 5)
        self.changeIcon(mn)

        #TEXT
        self.gridSizer.Add(  # Monitor number label
            wx.StaticText(self, -1, "Monitor %s" % mn), 0,
            wx.ALL | wx.ALIGN_CENTER, 5)
        self.gridSizer.Add(  # Choose video input
            comboFileBrowser(self,
                             wx.ID_ANY,
                             size=(-1, -1),
                             dialogTitle="Choose an Input video file",
                             startDirectory=options.GetOption("Data_Folder"),
                             value=source,
                             choices=WebcamsList,
                             fileMask="Video File (*.*)|*.*",
                             browsevalue="Browse for video...",
                             changeCallback=partial(self.onChangeDropDown,
                                                    [mn, "source"])), 0,
            wx.ALL | wx.ALIGN_CENTER, 5)
        self.gridSizer.Add(  # Choose mask file
            comboFileBrowser(self,
                             wx.ID_ANY,
                             size=(-1, -1),
                             dialogTitle="Choose a Mask file",
                             startDirectory=options.GetOption("Mask_Folder"),
                             value=mf,
                             fileMask="pySolo mask file (*.msk)|*.msk",
                             browsevalue="Browse for mask...",
                             changeCallback=partial(self.onChangeDropDown,
                                                    [mn, "mask_file"])), 0,
            wx.ALL | wx.ALIGN_CENTER, 5)
        self.gridSizer.Add(  # Choose output file
            comboFileBrowser(self,
                             wx.ID_ANY,
                             size=(-1, -1),
                             dialogTitle="Choose the output file",
                             startDirectory=options.GetOption("Data_Folder"),
                             value=md['outputfile'],
                             fileMask="Output File (*.txt)|*.txt",
                             browsevalue="Browse for output...",
                             changeCallback=partial(self.onChangeDropDown,
                                                    [mn, "outputfile"])), 0,
            wx.ALL | wx.ALIGN_CENTER, 5)

        #TRACKTYPE
        ttcb = wx.ComboBox(self,
                           -1,
                           size=(-1, -1),
                           value=md['track_type'],
                           choices=tracktypes,
                           style=wx.CB_DROPDOWN | wx.CB_READONLY | wx.CB_SORT)
        ttcb.Bind(wx.EVT_COMBOBOX,
                  partial(self.onChangeDropDown, [mn, "track_type"]))
        self.gridSizer.Add(ttcb, 0, wx.ALL | wx.ALIGN_CENTER, 5)

        #RECORD BUTTON
        self.recordBTNS.append(wx.ToggleButton(self, wx.ID_ANY, 'Start'))
        self.recordBTNS[-1].Bind(wx.EVT_TOGGLEBUTTON,
                                 partial(self.onToggleRecording, mn))
        self.gridSizer.Add(self.recordBTNS[-1], 0, wx.ALL | wx.ALIGN_CENTER, 5)

        #UPTIME
        self.uptimeTXT.append(
            wx.TextCtrl(self, value="00:00:00", size=(140, -1)))
        self.gridSizer.Add(self.uptimeTXT[-1], 0, wx.ALL | wx.ALIGN_CENTER, 5)

        #VIEW BUTTON
        vb = wx.Button(self, wx.ID_ANY, 'View')
        vb.Bind(wx.EVT_BUTTON, partial(self.onViewMonitor, mn))
        self.gridSizer.Add(vb, 0, wx.ALL | wx.ALIGN_CENTER, 5)
Example #20
0
# %%

    """  refreshes screens  """
    def updateUI(self):
        self.panelOne.onRefresh()
        self.panelTwo.onRefresh()
        self.Layout()

# %%

    """  refreshes screens after configuration change  """
    def OnPageChanging(self, event):
        # self.panelOne.StopPlaying()                                           # old, unnecessary code?
        self.panelTwo.StopPlaying()


# %%
"""  Main Program  """

x, y = options.GetOption("Resolution")  # screen resolution global variables.

if __name__ == "__main__":

    app = wx.App(False)  # Create a new GUI,
    #                False => don't redirect stdout/stderr to a window.
    frame_1 = mainFrame(None, -1, "")  # create the GUI window
    frame_1.Show()
    app.SetTopWindow(frame_1)
    app.MainLoop()  # begin interaction with user
    print('done')  # temporary debug print