Ejemplo n.º 1
0
 def add(self, itemType, cnf={}, **kw):
     end = self.index('end')
     if end is None:
         end = 1
     else:
         end += 2  # end is the index of the last item starting from 0 so the
         # nb of items after the addition is end + 2
     if not end % self.max_height:
         kw['columnbreak'] = True
     Menu.add(self, itemType, cnf, **kw)
Ejemplo n.º 2
0
    def popup_menu(event):
        right_click_menu = Menu(None, tearoff=0, takefocus=0)

        right_click_menu.add_command(
            label='Select all', command=lambda: on_click(event, 'SelectAll'))
        right_click_menu.add_command(label='Copy',
                                     command=lambda: on_click(event, 'Copy'))
        right_click_menu.add_command(label='Paste',
                                     command=lambda: on_click(event, 'Paste'))
        right_click_menu.add_command(label='Cut',
                                     command=lambda: on_click(event, 'Cut'))
        right_click_menu.add(constants.SEPARATOR)
        right_click_menu.add_command(
            label='Close window',
            command=lambda: utils.get_toplevel('winpath', mainwin).destroy())

        right_click_menu.tk_popup(event.x_root + 10, event.y_root + 15)
Ejemplo n.º 3
0
    def init_gui(self):
        """
        Initializes all widgets for the main screen and calls grid_widgets()
        to place them
        """
        self.title("exp_check")
        self.config(bg='black')

        style = Style()
        style.configure("TMenubutton", foreground="white", background="black")
        # setup widgets
        self.add_button = Button(self,
                                 text="Add Food",
                                 **widget_options,
                                 command=self.add_food)
        self.delete_button = Button(self,
                                    text="Delete Food",
                                    **widget_options,
                                    command=self.delete_food)

        self.search_box = Entry(self,
                                **widget_options,
                                insertbackground='white')
        self.search_box.bind("<Key>", self.search)

        self.data_display = DataDisplay(widget_options)
        vals = self.data_manager.get_keylist()
        vals.insert(0, 'name')
        menu1 = Menu(tearoff=0)
        menu1.add('radiobutton',
                  label=vals[0],
                  command=lambda: self.update_data_display(sort_key=vals[0]))
        menu1.add('radiobutton',
                  label=vals[1],
                  command=lambda: self.update_data_display(sort_key=vals[1]))
        menu1.add('radiobutton',
                  label=vals[2],
                  command=lambda: self.update_data_display(sort_key=vals[2]))

        self.sort_menu = Menubutton(self,
                                    text='sort by',
                                    menu=menu1,
                                    style="TMenubutton")

        search_icon = self.generate_icon_object("./data/search_icon.png",
                                                (20, 20))
        self.search_label = Label(image=search_icon, **widget_options)
        self.search_label.image = search_icon
        # setup message box dialog
        self.message = Label(self, text='', **widget_options)
        self.okay_button = Button(
            self,
            text="   OK   ",
            **widget_options,
            command=lambda: self.okay_button_press(self.cur_instance))

        self.grid_widgets()
Ejemplo n.º 4
0
class RandomSelector(Frame):
    def __init__(self, master):
        super().__init__(master)
        self.set_vars()
        self.set_frames()
        self.set_widgets()
        self.set_menu()
        self._items = []

    def set_vars(self):
        # tkinter variable
        self.item = StringVar()
        self.item.set('Item Name')
        self.hide = BooleanVar()
        self.hide.set(False)

    def set_frames(self):
        self.seladd_frame = Frame(self)
        self.itemframe = LabelFrame(self, text='Items')
        self.seladd_frame.pack()
        self.itemframe.pack(fill='both', expand=1)

    def set_widgets(self):
        self.entry = Entry(self.seladd_frame, width=30, textvariable=self.item)
        self.entry.pack(side='left')
        self.sel_button = Button(self.seladd_frame,
                                 text="Select",
                                 command=self.sel,
                                 width=8)
        self.add_button = Button(self.seladd_frame,
                                 text="Add",
                                 command=self.add,
                                 width=8)
        self.sel_button.pack(side='left')
        self.add_button.pack(side='left')
        self.listbox = Listbox(self.itemframe)
        self.listbox.pack(fill='both', expand=1)

    def set_menu(self):
        self.top_menu = Menu(self)
        self.cmd_menu = Menu(self.top_menu)
        self.cmd_menu.add(itemType='command', command=self.sel, label='Select')
        self.cmd_menu.add(itemType='command', command=self.add, label='Add')
        self.cmd_menu.add(itemType='separator')
        self.cmd_menu.add(itemType='command', command=self.quit, label='Quit')
        self.view_menu = Menu(self.top_menu)
        self.view_menu.add(itemType='checkbutton',
                           label='Hide items',
                           onvalue=1,
                           offvalue=0,
                           variable=self.hide,
                           command=self.show)
        self.top_menu.add_cascade(menu=self.cmd_menu,
                                  label='Command',
                                  underline=0)
        self.top_menu.add_cascade(menu=self.view_menu,
                                  label='View',
                                  underline=0)
        self.master['menu'] = self.top_menu

    def sel(self):
        if self._items:
            self.item.set(random.choice(self._items))

    def add(self):
        self._items.append(self.item.get())
        self.listbox.insert('end', self.item.get())

    def show(self):
        if self.hide.get():
            self.itemframe.pack_forget()
        else:
            self.itemframe.pack(fill="both", expand="1")

    def quit(self):
        self.master.destroy()
Ejemplo n.º 5
0
    def __init__(self, master):
        self.dirname = os.path.abspath( os.curdir )

        self.initComplete = 0
        self.master = master
        self.x, self.y, self.w, self.h = -1,-1,-1,-1

        # bind master to <Configure> in order to handle any resizing, etc.
        # postpone self.master.bind("<Configure>", self.Master_Configure)
        self.master.bind('<Enter>', self.bindConfigure)

        self.menuBar = Menu(master, relief = "raised", bd=2)


        top_Directory = Menu(self.menuBar, tearoff=0)
        top_Directory.add("command", label = "Change Dir", command = self.menu_Directory_Change_Dir)
        self.menuBar.add("cascade", label="Directory", menu=top_Directory)


        #top_Snippet = Menu(self.menuBar, tearoff=0)

        self.menuBar.add("command", label = "Run", command = self.menu_Run)

        master.config(menu=self.menuBar)

        # make a Status Bar
        self.statusMessage = StringVar()
        self.statusMessage.set(self.dirname)
        self.statusbar = Label(self.master, textvariable=self.statusMessage, bd=1, relief=SUNKEN)
        self.statusbar.pack(anchor=SW, fill=X, side=BOTTOM)

        self.statusbar_bg = self.statusbar.cget('bg') # save bg for restore

        myFont = tkinter.font.Font(family="Arial", size=12, weight=tkinter.font.BOLD)
        self.statusbar.config( font=myFont )


        frame = Frame(master)
        frame.pack(anchor=NE, fill=BOTH, side=TOP)

        self.Pass_Fail_Button = Button(frame,text="Pass/Fail Will Be Shown Here",
                                       image="", width="15", background="green",
                                       anchor=W, justify=LEFT, padx=2)
        self.Pass_Fail_Button.pack(anchor=NE, fill=X, side=TOP)
        self.Pass_Fail_Button.bind("<ButtonRelease-1>", self.Pass_Fail_Button_Click)

        #self.master.title("tk_nosy")
        self.master.title('Python %s.%s.%s '%sys.version_info[:3])
        self.oscillator = 1 # animates character on title
        self.oscillator_B = 0 # used to return statusbar to statusbar_bg

        self.lbframe = Frame( frame )
        self.lbframe.pack(anchor=SE, side=LEFT, fill=BOTH, expand=1)

        scrollbar = Scrollbar(self.lbframe, orient=VERTICAL)
        self.Text_1 = Text(self.lbframe, width="80", height="24", yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.Text_1.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.Text_1.pack(side=LEFT, fill=BOTH, expand=1)

        self.master.resizable(1,1) # Linux may not respect this

        self.numNosyCalls = 0
        self.need_to_pick_dir = 1

        if len(sys.argv)>1:
            #  I don't care what the exception is, if there's a problem, bail
            # pylint: disable=W0702
            try:
                dirname = os.path.abspath( sys.argv[1] )
                self.try_change_to_new_dir( dirname )
            except:
                pass # let Alarm force dir selection
        else:
            try:
                if os.path.isdir(os.path.join( self.dirname, 'tests' )):
                    self.try_change_to_new_dir( self.dirname )
            except:
                pass # let Alarm force dir selection
                

        print(LICENSE)
        self.Alarm()
Ejemplo n.º 6
0
class _Tk_Nosy(object):
    """This class is the tkinter GUI object"""
    def __init__(self, master):
        self.dirname = os.path.abspath( os.curdir )

        self.initComplete = 0
        self.master = master
        self.x, self.y, self.w, self.h = -1,-1,-1,-1

        # bind master to <Configure> in order to handle any resizing, etc.
        # postpone self.master.bind("<Configure>", self.Master_Configure)
        self.master.bind('<Enter>', self.bindConfigure)

        self.menuBar = Menu(master, relief = "raised", bd=2)


        top_Directory = Menu(self.menuBar, tearoff=0)
        top_Directory.add("command", label = "Change Dir", command = self.menu_Directory_Change_Dir)
        self.menuBar.add("cascade", label="Directory", menu=top_Directory)


        #top_Snippet = Menu(self.menuBar, tearoff=0)

        self.menuBar.add("command", label = "Run", command = self.menu_Run)

        master.config(menu=self.menuBar)

        # make a Status Bar
        self.statusMessage = StringVar()
        self.statusMessage.set(self.dirname)
        self.statusbar = Label(self.master, textvariable=self.statusMessage, bd=1, relief=SUNKEN)
        self.statusbar.pack(anchor=SW, fill=X, side=BOTTOM)

        self.statusbar_bg = self.statusbar.cget('bg') # save bg for restore

        myFont = tkinter.font.Font(family="Arial", size=12, weight=tkinter.font.BOLD)
        self.statusbar.config( font=myFont )


        frame = Frame(master)
        frame.pack(anchor=NE, fill=BOTH, side=TOP)

        self.Pass_Fail_Button = Button(frame,text="Pass/Fail Will Be Shown Here",
                                       image="", width="15", background="green",
                                       anchor=W, justify=LEFT, padx=2)
        self.Pass_Fail_Button.pack(anchor=NE, fill=X, side=TOP)
        self.Pass_Fail_Button.bind("<ButtonRelease-1>", self.Pass_Fail_Button_Click)

        #self.master.title("tk_nosy")
        self.master.title('Python %s.%s.%s '%sys.version_info[:3])
        self.oscillator = 1 # animates character on title
        self.oscillator_B = 0 # used to return statusbar to statusbar_bg

        self.lbframe = Frame( frame )
        self.lbframe.pack(anchor=SE, side=LEFT, fill=BOTH, expand=1)

        scrollbar = Scrollbar(self.lbframe, orient=VERTICAL)
        self.Text_1 = Text(self.lbframe, width="80", height="24", yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.Text_1.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.Text_1.pack(side=LEFT, fill=BOTH, expand=1)

        self.master.resizable(1,1) # Linux may not respect this

        self.numNosyCalls = 0
        self.need_to_pick_dir = 1

        if len(sys.argv)>1:
            #  I don't care what the exception is, if there's a problem, bail
            # pylint: disable=W0702
            try:
                dirname = os.path.abspath( sys.argv[1] )
                self.try_change_to_new_dir( dirname )
            except:
                pass # let Alarm force dir selection
        else:
            try:
                if os.path.isdir(os.path.join( self.dirname, 'tests' )):
                    self.try_change_to_new_dir( self.dirname )
            except:
                pass # let Alarm force dir selection
                

        print(LICENSE)
        self.Alarm()


    def try_change_to_new_dir(self, dirname):
        """A legal abspath will switch to dirname."""
        #  I don't care what the exception is, if there's a problem, bail
        # pylint: disable=W0702
        if dirname:
            try:
                dirname = os.path.abspath( dirname )
            except:
                return # let Alarm force dir selection
        else:
            return

        self.dirname = dirname
        print('Selected dirname    =',dirname)
        fileD.clear()
        os.chdir( self.dirname )
        self.reset_statusbar_bg()
        self.need_to_pick_dir = 0

        #with open(NOSY_USER_DATA_FILE, 'w') as text_file:
        #    text_file.write( self.dirname )

        self.numNosyCalls = 0


    def reset_statusbar_bg(self):
        """Return status bar to default state"""
        self.statusbar.config(bg=self.statusbar_bg)
        self.statusMessage.set(self.dirname)

    def set_statusbar_bg(self, c):
        """Set status bar to show new color and message"""
        self.statusbar.config(bg=c)
        self.oscillator_B = 1 # will return to initial color after a few cycles

    def menu_Directory_Change_Dir(self):
        """Menu selection to set directory in which to run nosetests"""
        dirname = self.AskDirectory( title='Choose Directory For Nose Tests', initialdir=".")
        if dirname:
            self.try_change_to_new_dir( dirname )
        # >>>>>>insert any user code below this comment for section "menu_Directory_Change_Dir"
        # replace, delete, or comment-out the following
        print("called menu_Directory_Change_Dir")


    def menu_Run(self):
        """User initiates a nosetests run, not file change detection."""
        print("called menu_Run")
        self.callNosy()

    def callNosy(self):
        """Run nosetests and display results"""
        self.numNosyCalls += 1
        self.Text_1.delete(1.0, END)

        # turn indicator button gray while running the tests
        myFont = tkinter.font.Font(family="Arial", size=12, weight=tkinter.font.BOLD)
        self.Pass_Fail_Button.config(background="#999999", text='TESTING...', font=myFont)
        self.master.update()
        self.master.update_idletasks()

        # pylint: disable=W0201
        self.passedAllTests, numPassed, numFailed, numErrors, numSkipped, outputTextL = \
            run_nosetests(self.numNosyCalls)

        max_len_s = 42
        num_lines = 1
        for s in outputTextL:
            self.Text_1.insert(END, s)
            sL = s.split('\n')
            for ss in sL:
                max_len_s = max(max_len_s, len(ss))
                num_lines += 1


        if self.numNosyCalls % 2:
            myFont = tkinter.font.Font(family="Arial", size=12, weight=tkinter.font.BOLD)
        else:
            myFont = tkinter.font.Font(family="Arial", size=12)

        if self.passedAllTests:
            s = 'PASSED'
            if numPassed > 1:
                s = 'PASSED ALL %i TESTS'%numPassed
            elif numPassed == 1:
                s = 'PASSED ONE TEST'


            bg="#00ff00"
            if numSkipped==1:
                s = 'passed with 1 SKIP'
                bg = "#00cc00"
            elif numSkipped > 1:
                s = 'passed with %i SKIPS'%numSkipped
                bg = "#00cc00"
            elif numPassed==0:
                s = 'No Tests Found'
                bg="#ff8000"
            self.Pass_Fail_Button.config(background=bg, text=s, font=myFont)

            #self.master.geometry('200x50')
        else:
            s = 'FAILED %i, ERRORS %i, SKIP %i, PASSED %i'%(numFailed,
                                                            numErrors, numSkipped, numPassed)
            self.Pass_Fail_Button.config(background="#ff0000", text=s, font=myFont)
            #self.master.geometry('516x385')


        # Show list of files being watched.
        #self.Text_1.insert(END, '_'*40+'\n')
        self.Text_1.insert(END, 'WATCHED *.py FILES'.center(40,'_') + '\n' )
        self.Text_1.insert(END, '%s%s..\n\n'%(self.dirname,os.path.sep) )
        num_lines += 3

        len_dirname = len( self.dirname )

        keyL = list(fileD.keys())
        keyL.sort()
        lastdir = ''
        for key in keyL:
            dn = os.path.dirname( key )
            if dn != lastdir:
                self.Text_1.insert(END, '..'+dn[len_dirname:] + '\n')
                max_len_s = max(max_len_s, len(dn)+1)
                lastdir = dn
                num_lines += 1
            s = '    ' +os.path.basename( key )
            self.Text_1.insert(END, s + '\n')
            max_len_s = max(max_len_s, len(s)+1)
            num_lines += 1

        self.Text_1.config(width=max_len_s)
        self.Text_1.config(height=min(40, num_lines))
        self.master.winfo_toplevel().wm_geometry("")


    def bindConfigure(self, event):
        """Part of goofy main window setup in tkinter."""
        #  tkinter requires arguments, but I don't use them
        # pylint: disable=W0613
        if not self.initComplete:
            self.master.bind("<Configure>", self.Master_Configure)
            self.initComplete = 1



    # return a string containing directory name
    def AskDirectory(self, title='Choose Directory', initialdir="."):
        """Run pop-up menu for user to select directory."""
    #    This is not an error
    # pylint: disable=E1101

        if sys.version_info < (3,):
            dirname = tkFileDialog.askdirectory(parent=self.master,
                                                initialdir=initialdir,title=title)
        else:
            dirname = tkinter.filedialog.askdirectory(parent=self.master,
                                                      initialdir=initialdir,title=title)
        return dirname # <-- string


    def Master_Configure(self, event):
        """Part of tkinter main window initialization"""

        if event.widget != self.master:
            if self.w != -1:
                return
        x = int(self.master.winfo_x())
        y = int(self.master.winfo_y())
        w = int(self.master.winfo_width())
        h = int(self.master.winfo_height())
        if (self.x, self.y, self.w, self.h) == (-1,-1,-1,-1):
            self.x, self.y, self.w, self.h = x,y,w,h


        if self.w!=w or self.h!=h:
            #print "Master reconfigured... make resize adjustments"
            self.w=w
            self.h=h

    def Pass_Fail_Button_Click(self, event):
        """Place-holder routine for user clicking Pass/Fail Button"""
        pass

    # alarm function is called after specified number of milliseconds
    def SetAlarm(self, milliseconds=1000):
        """Reinitialize tkinter alarm mechanism as well as update seconds
           counter in main window title bar.
        """
        self.master.after( milliseconds, self.Alarm )

        self.oscillator += 1
        if self.oscillator > 5:
            self.oscillator = 0

        if self.oscillator_B>0:
            self.oscillator_B += 1
        if self.oscillator_B>5:
            self.oscillator_B = 0
            self.reset_statusbar_bg()

        pad = '|'*self.oscillator

        #self.master.title("%i) tk_nosy "%self.numNosyCalls + pad )
        s = '%s.%s.%s '%sys.version_info[:3]
        self.master.title('%i) Python %s '%(self.numNosyCalls , s + pad ))


    def Alarm(self):
        """Look for changed files every second, then reset alarm"""
        if self.need_to_pick_dir:
            dirname = self.AskDirectory( title='Choose Directory For Nose Tests', initialdir=".")
            self.try_change_to_new_dir( dirname )

        #first call to numberOfChangedFiles will be > 0 if any .py files are found
        elif numberOfChangedFiles( self.dirname ) > 0: # or self.numNosyCalls==0
            self.callNosy()

        self.SetAlarm()
Ejemplo n.º 7
0
    def __init__(self, master):
        self.dirname = os.path.abspath( os.curdir )

        self.initComplete = 0
        self.master = master
        self.x, self.y, self.w, self.h = -1,-1,-1,-1

        # bind master to <Configure> in order to handle any resizing, etc.
        # postpone self.master.bind("<Configure>", self.Master_Configure)
        self.master.bind('<Enter>', self.bindConfigure)

        self.menuBar = Menu(master, relief = "raised", bd=2)

        self.menuBar.add("command", label = "Change_Dir", command = self.menu_Directory_Change_Dir)

        disp_Choices = Menu(self.menuBar, tearoff=0)
        self.display_test_details = StringVar()
        self.display_test_details.set('N')
        disp_Choices.add_checkbutton(label='Display Test Details', variable=self.display_test_details, onvalue='Y', offvalue='N')

        self.display_watched_files = StringVar()
        self.display_watched_files.set('N')
        disp_Choices.add_checkbutton(label='Show Watched Files', variable=self.display_watched_files, onvalue='Y', offvalue='N')
        self.menuBar.add("cascade", label="Display", menu=disp_Choices)


        py_choices = Menu(self.menuBar, tearoff=0)
        py_choices.add("command", label = "Change Python Version",
                          command = self.changePythonVersion)
        py_choices.add("command", label = "Find New Python Interpreter",
                          command = self.findNewPythonInterpreter)
        py_choices.add("command", label = "Launch Another Python Interpreter",
                          command = self.launchAnotherPythonInterpreter)
        self.menuBar.add("cascade", label="Python", menu=py_choices)


        #top_Snippet = Menu(self.menuBar, tearoff=0)

        self.menuBar.add("command", label = "Run", command = self.menu_Run)

        self.display_test_details.trace("w", self.rerun_tests)
        self.display_watched_files.trace("w", self.rerun_tests)

        master.config(menu=self.menuBar)

        # make a Status Bar
        self.statusMessage = StringVar()
        self.statusMessage.set(self.dirname)
        self.statusbar = Label(self.master, textvariable=self.statusMessage,
                               bd=1, relief=SUNKEN)
        self.statusbar.pack(anchor=SW, fill=X, side=BOTTOM)

        self.statusbar_bg = self.statusbar.cget('bg') # save bg for restore

        self.arial_12_bold_font = tkinter.font.Font(family="Arial", size=12,
                                                    weight=tkinter.font.BOLD)
        self.arial_12_font      = tkinter.font.Font(family="Arial", size=12)


        self.statusbar.config( font=self.arial_12_bold_font )

        frame = Frame(master)
        frame.pack(anchor=NE, fill=BOTH, side=TOP)

        self.Pass_Fail_Button = Button(frame,text="Pass/Fail Will Be Shown Here",
                                       image="", width="15", background="green",
                                       anchor=W, justify=LEFT, padx=2)
        self.Pass_Fail_Button.pack(anchor=NE, fill=X, side=TOP)
        self.Pass_Fail_Button.bind("<ButtonRelease-1>", self.Pass_Fail_Button_Click)

        self.master.title("tk_nosy")

        self.oscillator = 1 # animates character on title
        self.oscillator_B = 0 # used to return statusbar to statusbar_bg

        self.lbframe = Frame( frame )
        self.lbframe.pack(anchor=SE, side=LEFT, fill=BOTH, expand=1)

        scrollbar = Scrollbar(self.lbframe, orient=VERTICAL)
        self.Text_1 = Text(self.lbframe, width="80", height="24",
                           yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.Text_1.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.Text_1.pack(side=LEFT, fill=BOTH, expand=1)

        self.master.resizable(1,1) # Linux may not respect this

        self.numNosyCalls = 0
        self.need_to_pick_dir = 1

        print('sys.argv =',sys.argv)
        if len(sys.argv)>1:
            #  I don't care what the exception is, if there's a problem, bail
            # pylint: disable=W0702
            print( "Try Dir =",sys.argv[1] )
            try:
                dirname = os.path.abspath( sys.argv[1] )
                self.try_change_to_new_dir( dirname )
            except Exception:
                pass # let Alarm force dir selection
        else:
            try:
                if os.path.isdir(os.path.join( self.dirname, 'tests' )):
                    self.try_change_to_new_dir( self.dirname )
            except Exception:
                pass # let Alarm force dir selection


        print(LICENSE)

        self.defaultPyInterp = None # need to identify default python interpreter
        if Tk_Nosy.pythonInterpreterCollection == None:
            Tk_Nosy.pythonInterpreterCollection = PyInterpsOnSys()
            self.defaultPyInterp = Tk_Nosy.pythonInterpreterCollection.get_PI_obj_by_py_path( sys.executable )
            #print( Tk_Nosy.pythonInterpreterCollection )

        self.Alarm()
Ejemplo n.º 8
0
class Tk_Nosy(object):
    """This class is the tkinter GUI object"""

    # make a collection of python interpreters to choose from
    pythonInterpreterCollection = None # will be PyInterpsOnSys object

    # extra python interpreters can run nosetests concurrently to main window
    #  concurrent_versionL contains tuples = (PI, Popup)
    concurrent_versionL = [] # additional running python interpreters


    def __init__(self, master):
        self.dirname = os.path.abspath( os.curdir )

        self.initComplete = 0
        self.master = master
        self.x, self.y, self.w, self.h = -1,-1,-1,-1

        # bind master to <Configure> in order to handle any resizing, etc.
        # postpone self.master.bind("<Configure>", self.Master_Configure)
        self.master.bind('<Enter>', self.bindConfigure)

        self.menuBar = Menu(master, relief = "raised", bd=2)

        self.menuBar.add("command", label = "Change_Dir", command = self.menu_Directory_Change_Dir)

        disp_Choices = Menu(self.menuBar, tearoff=0)
        self.display_test_details = StringVar()
        self.display_test_details.set('N')
        disp_Choices.add_checkbutton(label='Display Test Details', variable=self.display_test_details, onvalue='Y', offvalue='N')

        self.display_watched_files = StringVar()
        self.display_watched_files.set('N')
        disp_Choices.add_checkbutton(label='Show Watched Files', variable=self.display_watched_files, onvalue='Y', offvalue='N')
        self.menuBar.add("cascade", label="Display", menu=disp_Choices)


        py_choices = Menu(self.menuBar, tearoff=0)
        py_choices.add("command", label = "Change Python Version",
                          command = self.changePythonVersion)
        py_choices.add("command", label = "Find New Python Interpreter",
                          command = self.findNewPythonInterpreter)
        py_choices.add("command", label = "Launch Another Python Interpreter",
                          command = self.launchAnotherPythonInterpreter)
        self.menuBar.add("cascade", label="Python", menu=py_choices)


        #top_Snippet = Menu(self.menuBar, tearoff=0)

        self.menuBar.add("command", label = "Run", command = self.menu_Run)

        self.display_test_details.trace("w", self.rerun_tests)
        self.display_watched_files.trace("w", self.rerun_tests)

        master.config(menu=self.menuBar)

        # make a Status Bar
        self.statusMessage = StringVar()
        self.statusMessage.set(self.dirname)
        self.statusbar = Label(self.master, textvariable=self.statusMessage,
                               bd=1, relief=SUNKEN)
        self.statusbar.pack(anchor=SW, fill=X, side=BOTTOM)

        self.statusbar_bg = self.statusbar.cget('bg') # save bg for restore

        self.arial_12_bold_font = tkinter.font.Font(family="Arial", size=12,
                                                    weight=tkinter.font.BOLD)
        self.arial_12_font      = tkinter.font.Font(family="Arial", size=12)


        self.statusbar.config( font=self.arial_12_bold_font )

        frame = Frame(master)
        frame.pack(anchor=NE, fill=BOTH, side=TOP)

        self.Pass_Fail_Button = Button(frame,text="Pass/Fail Will Be Shown Here",
                                       image="", width="15", background="green",
                                       anchor=W, justify=LEFT, padx=2)
        self.Pass_Fail_Button.pack(anchor=NE, fill=X, side=TOP)
        self.Pass_Fail_Button.bind("<ButtonRelease-1>", self.Pass_Fail_Button_Click)

        self.master.title("tk_nosy")

        self.oscillator = 1 # animates character on title
        self.oscillator_B = 0 # used to return statusbar to statusbar_bg

        self.lbframe = Frame( frame )
        self.lbframe.pack(anchor=SE, side=LEFT, fill=BOTH, expand=1)

        scrollbar = Scrollbar(self.lbframe, orient=VERTICAL)
        self.Text_1 = Text(self.lbframe, width="80", height="24",
                           yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.Text_1.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.Text_1.pack(side=LEFT, fill=BOTH, expand=1)

        self.master.resizable(1,1) # Linux may not respect this

        self.numNosyCalls = 0
        self.need_to_pick_dir = 1

        print('sys.argv =',sys.argv)
        if len(sys.argv)>1:
            #  I don't care what the exception is, if there's a problem, bail
            # pylint: disable=W0702
            print( "Try Dir =",sys.argv[1] )
            try:
                dirname = os.path.abspath( sys.argv[1] )
                self.try_change_to_new_dir( dirname )
            except Exception:
                pass # let Alarm force dir selection
        else:
            try:
                if os.path.isdir(os.path.join( self.dirname, 'tests' )):
                    self.try_change_to_new_dir( self.dirname )
            except Exception:
                pass # let Alarm force dir selection


        print(LICENSE)

        self.defaultPyInterp = None # need to identify default python interpreter
        if Tk_Nosy.pythonInterpreterCollection == None:
            Tk_Nosy.pythonInterpreterCollection = PyInterpsOnSys()
            self.defaultPyInterp = Tk_Nosy.pythonInterpreterCollection.get_PI_obj_by_py_path( sys.executable )
            #print( Tk_Nosy.pythonInterpreterCollection )

        self.Alarm()


    def try_change_to_new_dir(self, dirname):
        """A legal abspath will switch to dirname."""
        #  I don't care what the exception is, if there's a problem, bail
        # pylint: disable=W0702
        if dirname:
            try:
                dirname = os.path.abspath( dirname )
            except:
                return # let Alarm force dir selection
        else:
            return

        self.dirname = dirname
        print('Selected dirname    =',dirname)
        fileD.clear()
        os.chdir( self.dirname )
        self.reset_statusbar_bg()
        self.need_to_pick_dir = 0

        #with open(NOSY_USER_DATA_FILE, 'w') as text_file:
        #    text_file.write( self.dirname )

        self.numNosyCalls = 0


    def reset_statusbar_bg(self):
        """Return status bar to default state"""
        self.statusbar.config(bg=self.statusbar_bg)
        self.statusMessage.set(self.dirname)

    def set_statusbar_bg(self, c):
        """Set status bar to show new color and message"""
        self.statusbar.config(bg=c)
        self.oscillator_B = 1 # will return to initial color after a few cycles

    def menu_Directory_Change_Dir(self):
        """Menu selection to set directory in which to run nosetests"""
        dirname = self.AskDirectory( title='Choose Directory For Nose Tests', initialdir=".")
        if dirname:
            self.try_change_to_new_dir( dirname )
        # >>>>>>insert any user code below this comment for section "menu_Directory_Change_Dir"
        # replace, delete, or comment-out the following
        print("called menu_Directory_Change_Dir")


    def menu_Run(self):
        """User initiates a nosetests run, not file change detection."""
        print("called menu_Run")
        self.callNosy()
    def rerun_tests(self,*args):
        self.menu_Run()


    def callNosy(self):


        """Run nosetests and display results"""
        self.numNosyCalls += 1

        runL = [(self.defaultPyInterp, self)]
        for PI,Popup in Tk_Nosy.concurrent_versionL:
            runL.append( (PI, Popup) )

        for PI, tkwindow in runL:
            tkwindow.Text_1.delete(1.0, END)

            # turn indicator button gray while running the tests
            tkwindow.Pass_Fail_Button.config(background="#999999",
                                             text='TESTING...',
                                             font=self.arial_12_bold_font)
        self.master.update()
        self.master.update_idletasks()

        for PI, tkwindow in runL:
            self.run_tkwin_nosetests( PI, tkwindow)

        self.master.winfo_toplevel().wm_geometry("")



    def run_tkwin_nosetests(self, PI, tkwindow):
        """Run nosetests for main python interpreter and any concurrent
             python interpreters.

           Update GUI to show results.
        """

        if PI.nose_version == None:
            # if nose was not installed last time we checked, check again
            PI.nose_version, err_msg = get_nose_version_info( PI.full_path )
            if PI.nose_version == None:
                print( "\a" )  # make beep
                s = 'Can not verify nose for:\nPython ' + PI.name()
                tkwindow.Pass_Fail_Button.config(background='orange',
                                                  text=s,
                                                  font=self.arial_12_bold_font)
                s = 'Please verify nose installed for:\n'+str(PI) +\
                    '\n\n' + err_msg+\
                    '\n\nFor install instructions see:\n'+\
                    'https://nose.readthedocs.org/en/latest/'
                tkwindow.Text_1.insert(END, s )
                ShowError(title='Can not verify nose', message=s)
                return

        # pylint: disable=W0201
        passedAllTests, numPassed, numFailed, numErrors, numSkipped, outputTextL = \
            run_nosetests(self.numNosyCalls, PI, display_test_details=self.display_test_details.get())

        max_len_s = 42
        num_lines = 1
        for s in outputTextL:
            tkwindow.Text_1.insert(END, s)
            sL = s.split('\n')
            for ss in sL:
                max_len_s = max(max_len_s, len(ss))
                num_lines += 1


        if self.numNosyCalls % 2:
            myFont = self.arial_12_bold_font
        else:
            myFont = self.arial_12_font

        if passedAllTests:
            s = 'PASSED'
            if numPassed > 1:
                s = 'PASSED ALL %i TESTS'%numPassed
            elif numPassed == 1:
                s = 'PASSED ONE TEST'


            bg="#00ff00"
            if numSkipped==1:
                s = 'passed with 1 SKIP'
                bg = "#00cc00"
            elif numSkipped > 1:
                s = 'passed with %i SKIPS'%numSkipped
                bg = "#00cc00"
            elif numPassed==0:
                s = 'No Tests Found'
                bg="#ff8000"
            tkwindow.Pass_Fail_Button.config(background=bg, text=s, font=myFont)

            #self.master.geometry('200x50')
        else:
            s = 'FAILED %i, ERRORS %i, SKIP %i, PASSED %i'%(numFailed,
                                                            numErrors, numSkipped, numPassed)
            tkwindow.Pass_Fail_Button.config(background="#ff0000", text=s, font=myFont)
            #self.master.geometry('516x385')


        # Show list of files being watched.
        #self.Text_1.insert(END, '_'*40+'\n')
        tkwindow.Text_1.insert(END, 'WATCHED *.py FILES'.center(40,'_') + '\n' )
        tkwindow.Text_1.insert(END, '%s%s..\n\n'%(self.dirname,os.path.sep) )
        num_lines += 3

        len_dirname = len( self.dirname )

        if self.display_watched_files.get()=='Y':
            keyL = list(fileD.keys())
            keyL.sort()
            lastdir = ''
            for key in keyL:
                dn = os.path.dirname( key )
                if dn != lastdir:
                    tkwindow.Text_1.insert(END, '..'+dn[len_dirname:] + '\n')
                    max_len_s = max(max_len_s, len(dn)+1)
                    lastdir = dn
                    num_lines += 1
                s = '    ' +os.path.basename( key )
                tkwindow.Text_1.insert(END, s + '\n')
                max_len_s = max(max_len_s, len(s)+1)
                num_lines += 1
        else:
            num_lines += 1
            tkwindow.Text_1.insert(END, '     %i files watched.\n'%len(fileD))

        tkwindow.Text_1.config(width=max_len_s)
        tkwindow.Text_1.config(height=min(40, num_lines))


    def bindConfigure(self, event):
        """Part of goofy main window setup in tkinter."""
        #  tkinter requires arguments, but I don't use them
        # pylint: disable=W0613
        if not self.initComplete:
            self.master.bind("<Configure>", self.Master_Configure)
            self.initComplete = 1

    def change_python_exe(self, full_path ):
        """Allow nosetests to be run under any available python version """
        PI = Tk_Nosy.pythonInterpreterCollection.add_interp( full_path )
        if PI:
            self.defaultPyInterp = PI


    def findNewPythonInterpreter(self):
        """Find a new python interpreter, one that is not already in
             the PyInterpsOnSys object (pythonInterpreterCollection).
        """
        if Tk_Nosy.pythonInterpreterCollection == None:
            print( 'pythonInterpreterCollection NOT yet initialized' )
            self.statusMessage.set('Interpreter Collection NOT initialized')
            self.set_statusbar_bg( '#FF9999' )
            return

        print('Open File')
        filetypes = [
            ('python executable','py*'),
            ('Any File','*.*')]
        pathopen = tkFileDialog.askopenfilename(parent=self.master,
                       title='Select Python Executable',
                       filetypes=filetypes,
                       initialdir=self.defaultPyInterp.full_path)

        if pathopen:
            self.change_python_exe( pathopen )
            self.menu_Run()

    def kill_popup_window(self, popup_name):
        """Close a popup window running another verions of python interpreter"""
        for itup, tup in enumerate(Tk_Nosy.concurrent_versionL):
            PI, Popup = tup
            s = '%s %s' % (PI.exe_name, PI.version_str)
            if popup_name == s:
                Tk_Nosy.concurrent_versionL.pop( itup )
                return True # removed popup from list
        return False # no popup found


    def launchAnotherPythonInterpreter(self):
        """Launch a pop-up window that concurrently runs another python version"""

        removeNameL=[self.defaultPyInterp.name()]
        for PI,Popup in Tk_Nosy.concurrent_versionL:
            removeNameL.append( PI.name() )

        piL = Tk_Nosy.pythonInterpreterCollection.get_PI_list( removeNameL=removeNameL )
        if len(piL)==0:
            print( 'All identified python interpreters in use.' )
        else:
            print( [pi.name() for pi in piL] )
            rbL = [PI.name() for PI in piL]
            dialog = Select_Py_Version(self.master, "Launch Another Python Version",
                                       dialogOptions={'rbL':rbL})
            if dialog.result:
                PI = Tk_Nosy.pythonInterpreterCollection.get_PI_obj_by_name(
                                                    dialog.result['selection'])

                s = '%s %s' % (PI.exe_name, PI.version_str)
                Popup = SatelliteWindow(self, self.master, s)
                Tk_Nosy.concurrent_versionL.append( (PI, Popup) )
                self.menu_Run()

    def changePythonVersion(self):
        """Change to a different python version.
           If the PyInterpsOnSys object (pythonInterpreterCollection) has been
           initialized, select from its list.
           Otherwise find the python interpreter executable
           (ex. python.exe or python)
        """
        if (Tk_Nosy.pythonInterpreterCollection == None) or \
           (Tk_Nosy.pythonInterpreterCollection.num_terps() == 0):
            # If there is no list of available python interpreters, look for python file
            print('Open File')
            filetypes = [
                ('python executable','py*'),
                ('Any File','*.*')]
            pathopen = tkFileDialog.askopenfilename(parent=self.master,
                           title='Select Python Executable',
                           filetypes=filetypes,
                           initialdir=self.defaultPyInterp.full_path)

            if pathopen:
                self.change_python_exe( pathopen )
                self.menu_Run()
        else:
            rbL = [PI.name() for PI in Tk_Nosy.pythonInterpreterCollection.interpL]
            dialog = Select_Py_Version(self.master, "Select Python Version",
                                       dialogOptions={'rbL':rbL})
            if dialog.result:
                PI = Tk_Nosy.pythonInterpreterCollection.get_PI_obj_by_name(
                                                   dialog.result['selection'] )
                pathopen = PI.full_path

                self.change_python_exe( pathopen )
                self.menu_Run()

    # return a string containing directory name
    def AskDirectory(self, title='Choose Directory', initialdir="."):
        """Run pop-up menu for user to select directory."""
    #    This is not an error
    # pylint: disable=E1101

        if sys.version_info < (3,):
            dirname = tkFileDialog.askdirectory(parent=self.master,
                                             initialdir=initialdir,title=title)
        else:
            dirname = tkFileDialog.askdirectory(parent=self.master,
                                             initialdir=initialdir,title=title)
        return dirname # <-- string


    def Master_Configure(self, event):
        """Part of tkinter main window initialization"""

        if event.widget != self.master:
            if self.w != -1:
                return
        x = int(self.master.winfo_x())
        y = int(self.master.winfo_y())
        w = int(self.master.winfo_width())
        h = int(self.master.winfo_height())
        if (self.x, self.y, self.w, self.h) == (-1,-1,-1,-1):
            self.x, self.y, self.w, self.h = x,y,w,h


        if self.w!=w or self.h!=h:
            #print "Master reconfigured... make resize adjustments"
            self.w=w
            self.h=h

    # pylint: disable=W0613
    def Pass_Fail_Button_Click(self, event):
        """Routine for user clicking Pass/Fail Button"""
        print('Arranging Windows by User Request')
        num_popups = len(Tk_Nosy.concurrent_versionL)
        DX = 50
        DY = 70
        x = 10
        y = 10 + num_popups * DY
        self.master.geometry( '+%i+%i'%(x,y))

        for PI,Popup in Tk_Nosy.concurrent_versionL:
            x += DX
            y -= DY
            Popup.geometry( '+%i+%i'%(x,y))



    # alarm function is called after specified number of milliseconds
    def SetAlarm(self, milliseconds=1000):
        """Reinitialize tkinter alarm mechanism as well as update seconds
           counter in main window title bar.
        """
        self.master.after( milliseconds, self.Alarm )

        self.oscillator += 1
        if self.oscillator > 5:
            self.oscillator = 0

        if self.oscillator_B>0:
            self.oscillator_B += 1
        if self.oscillator_B>5:
            self.oscillator_B = 0
            self.reset_statusbar_bg()

        pad = '|'*self.oscillator

        s = '%s (v%s)'%(self.defaultPyInterp.exe_name, self.defaultPyInterp.version_str)

        self.master.title('%i) %s '%(self.numNosyCalls , s + pad ))

        for PI,Popup in Tk_Nosy.concurrent_versionL:
            s = '%s (v%s)'%(PI.exe_name, PI.version_str)
            Popup.title( '%i) %s '%(self.numNosyCalls , s + pad ) )



    def Alarm(self):
        """Look for changed files every second, then reset alarm"""
        if self.need_to_pick_dir:
            dirname = self.AskDirectory( title='Choose Directory For Nose Tests', initialdir=".")
            self.try_change_to_new_dir( dirname )

        #first call to numberOfChangedFiles will be > 0 if any .py files are found
        elif numberOfChangedFiles( self.dirname ) > 0: # or self.numNosyCalls==0
            self.callNosy()

        self.SetAlarm()
Ejemplo n.º 9
0
    def __init__(self, master):
        self.dirname = os.path.abspath(os.curdir)

        self.initComplete = 0
        self.master = master
        self.x, self.y, self.w, self.h = -1, -1, -1, -1

        # bind master to <Configure> in order to handle any resizing, etc.
        # postpone self.master.bind("<Configure>", self.Master_Configure)
        self.master.bind('<Enter>', self.bindConfigure)

        self.menuBar = Menu(master, relief="raised", bd=2)

        self.menuBar.add("command",
                         label="Change_Dir",
                         command=self.menu_Directory_Change_Dir)

        disp_Choices = Menu(self.menuBar, tearoff=0)
        self.display_test_details = StringVar()
        self.display_test_details.set('N')
        disp_Choices.add_checkbutton(label='Display Test Details',
                                     variable=self.display_test_details,
                                     onvalue='Y',
                                     offvalue='N')

        self.display_watched_files = StringVar()
        self.display_watched_files.set('N')
        disp_Choices.add_checkbutton(label='Show Watched Files',
                                     variable=self.display_watched_files,
                                     onvalue='Y',
                                     offvalue='N')
        self.menuBar.add("cascade", label="Display", menu=disp_Choices)

        py_choices = Menu(self.menuBar, tearoff=0)
        py_choices.add("command",
                       label="Change Python Version",
                       command=self.changePythonVersion)
        py_choices.add("command",
                       label="Find New Python Interpreter",
                       command=self.findNewPythonInterpreter)
        py_choices.add("command",
                       label="Launch Another Python Interpreter",
                       command=self.launchAnotherPythonInterpreter)
        self.menuBar.add("cascade", label="Python", menu=py_choices)

        #top_Snippet = Menu(self.menuBar, tearoff=0)

        self.menuBar.add("command", label="Run", command=self.menu_Run)

        self.display_test_details.trace("w", self.rerun_tests)
        self.display_watched_files.trace("w", self.rerun_tests)

        master.config(menu=self.menuBar)

        # make a Status Bar
        self.statusMessage = StringVar()
        self.statusMessage.set(self.dirname)
        self.statusbar = Label(self.master,
                               textvariable=self.statusMessage,
                               bd=1,
                               relief=SUNKEN)
        self.statusbar.pack(anchor=SW, fill=X, side=BOTTOM)

        self.statusbar_bg = self.statusbar.cget('bg')  # save bg for restore

        self.arial_12_bold_font = tkinter.font.Font(family="Arial",
                                                    size=12,
                                                    weight=tkinter.font.BOLD)
        self.arial_12_font = tkinter.font.Font(family="Arial", size=12)

        self.statusbar.config(font=self.arial_12_bold_font)

        frame = Frame(master)
        frame.pack(anchor=NE, fill=BOTH, side=TOP)

        self.Pass_Fail_Button = Button(frame,
                                       text="Pass/Fail Will Be Shown Here",
                                       image="",
                                       width="15",
                                       background="green",
                                       anchor=W,
                                       justify=LEFT,
                                       padx=2)
        self.Pass_Fail_Button.pack(anchor=NE, fill=X, side=TOP)
        self.Pass_Fail_Button.bind("<ButtonRelease-1>",
                                   self.Pass_Fail_Button_Click)

        self.master.title("tk_nosy")

        self.oscillator = 1  # animates character on title
        self.oscillator_B = 0  # used to return statusbar to statusbar_bg

        self.lbframe = Frame(frame)
        self.lbframe.pack(anchor=SE, side=LEFT, fill=BOTH, expand=1)

        scrollbar = Scrollbar(self.lbframe, orient=VERTICAL)
        self.Text_1 = Text(self.lbframe,
                           width="80",
                           height="24",
                           yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.Text_1.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.Text_1.pack(side=LEFT, fill=BOTH, expand=1)

        self.master.resizable(1, 1)  # Linux may not respect this

        self.numNosyCalls = 0
        self.need_to_pick_dir = 1

        print('sys.argv =', sys.argv)
        if len(sys.argv) > 1:
            #  I don't care what the exception is, if there's a problem, bail
            # pylint: disable=W0702
            print("Try Dir =", sys.argv[1])
            try:
                dirname = os.path.abspath(sys.argv[1])
                self.try_change_to_new_dir(dirname)
            except Exception:
                pass  # let Alarm force dir selection
        else:
            try:
                if os.path.isdir(os.path.join(self.dirname, 'tests')):
                    self.try_change_to_new_dir(self.dirname)
            except Exception:
                pass  # let Alarm force dir selection

        print(LICENSE)

        self.defaultPyInterp = None  # need to identify default python interpreter
        if Tk_Nosy.pythonInterpreterCollection == None:
            Tk_Nosy.pythonInterpreterCollection = PyInterpsOnSys()
            self.defaultPyInterp = Tk_Nosy.pythonInterpreterCollection.get_PI_obj_by_py_path(
                sys.executable)
            #print( Tk_Nosy.pythonInterpreterCollection )

        self.Alarm()
Ejemplo n.º 10
0
class Tk_Nosy(object):
    """This class is the tkinter GUI object"""

    # make a collection of python interpreters to choose from
    pythonInterpreterCollection = None  # will be PyInterpsOnSys object

    # extra python interpreters can run nosetests concurrently to main window
    #  concurrent_versionL contains tuples = (PI, Popup)
    concurrent_versionL = []  # additional running python interpreters

    def __init__(self, master):
        self.dirname = os.path.abspath(os.curdir)

        self.initComplete = 0
        self.master = master
        self.x, self.y, self.w, self.h = -1, -1, -1, -1

        # bind master to <Configure> in order to handle any resizing, etc.
        # postpone self.master.bind("<Configure>", self.Master_Configure)
        self.master.bind('<Enter>', self.bindConfigure)

        self.menuBar = Menu(master, relief="raised", bd=2)

        self.menuBar.add("command",
                         label="Change_Dir",
                         command=self.menu_Directory_Change_Dir)

        disp_Choices = Menu(self.menuBar, tearoff=0)
        self.display_test_details = StringVar()
        self.display_test_details.set('N')
        disp_Choices.add_checkbutton(label='Display Test Details',
                                     variable=self.display_test_details,
                                     onvalue='Y',
                                     offvalue='N')

        self.display_watched_files = StringVar()
        self.display_watched_files.set('N')
        disp_Choices.add_checkbutton(label='Show Watched Files',
                                     variable=self.display_watched_files,
                                     onvalue='Y',
                                     offvalue='N')
        self.menuBar.add("cascade", label="Display", menu=disp_Choices)

        py_choices = Menu(self.menuBar, tearoff=0)
        py_choices.add("command",
                       label="Change Python Version",
                       command=self.changePythonVersion)
        py_choices.add("command",
                       label="Find New Python Interpreter",
                       command=self.findNewPythonInterpreter)
        py_choices.add("command",
                       label="Launch Another Python Interpreter",
                       command=self.launchAnotherPythonInterpreter)
        self.menuBar.add("cascade", label="Python", menu=py_choices)

        #top_Snippet = Menu(self.menuBar, tearoff=0)

        self.menuBar.add("command", label="Run", command=self.menu_Run)

        self.display_test_details.trace("w", self.rerun_tests)
        self.display_watched_files.trace("w", self.rerun_tests)

        master.config(menu=self.menuBar)

        # make a Status Bar
        self.statusMessage = StringVar()
        self.statusMessage.set(self.dirname)
        self.statusbar = Label(self.master,
                               textvariable=self.statusMessage,
                               bd=1,
                               relief=SUNKEN)
        self.statusbar.pack(anchor=SW, fill=X, side=BOTTOM)

        self.statusbar_bg = self.statusbar.cget('bg')  # save bg for restore

        self.arial_12_bold_font = tkinter.font.Font(family="Arial",
                                                    size=12,
                                                    weight=tkinter.font.BOLD)
        self.arial_12_font = tkinter.font.Font(family="Arial", size=12)

        self.statusbar.config(font=self.arial_12_bold_font)

        frame = Frame(master)
        frame.pack(anchor=NE, fill=BOTH, side=TOP)

        self.Pass_Fail_Button = Button(frame,
                                       text="Pass/Fail Will Be Shown Here",
                                       image="",
                                       width="15",
                                       background="green",
                                       anchor=W,
                                       justify=LEFT,
                                       padx=2)
        self.Pass_Fail_Button.pack(anchor=NE, fill=X, side=TOP)
        self.Pass_Fail_Button.bind("<ButtonRelease-1>",
                                   self.Pass_Fail_Button_Click)

        self.master.title("tk_nosy")

        self.oscillator = 1  # animates character on title
        self.oscillator_B = 0  # used to return statusbar to statusbar_bg

        self.lbframe = Frame(frame)
        self.lbframe.pack(anchor=SE, side=LEFT, fill=BOTH, expand=1)

        scrollbar = Scrollbar(self.lbframe, orient=VERTICAL)
        self.Text_1 = Text(self.lbframe,
                           width="80",
                           height="24",
                           yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.Text_1.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.Text_1.pack(side=LEFT, fill=BOTH, expand=1)

        self.master.resizable(1, 1)  # Linux may not respect this

        self.numNosyCalls = 0
        self.need_to_pick_dir = 1

        print('sys.argv =', sys.argv)
        if len(sys.argv) > 1:
            #  I don't care what the exception is, if there's a problem, bail
            # pylint: disable=W0702
            print("Try Dir =", sys.argv[1])
            try:
                dirname = os.path.abspath(sys.argv[1])
                self.try_change_to_new_dir(dirname)
            except Exception:
                pass  # let Alarm force dir selection
        else:
            try:
                if os.path.isdir(os.path.join(self.dirname, 'tests')):
                    self.try_change_to_new_dir(self.dirname)
            except Exception:
                pass  # let Alarm force dir selection

        print(LICENSE)

        self.defaultPyInterp = None  # need to identify default python interpreter
        if Tk_Nosy.pythonInterpreterCollection == None:
            Tk_Nosy.pythonInterpreterCollection = PyInterpsOnSys()
            self.defaultPyInterp = Tk_Nosy.pythonInterpreterCollection.get_PI_obj_by_py_path(
                sys.executable)
            #print( Tk_Nosy.pythonInterpreterCollection )

        self.Alarm()

    def try_change_to_new_dir(self, dirname):
        """A legal abspath will switch to dirname."""
        #  I don't care what the exception is, if there's a problem, bail
        # pylint: disable=W0702
        if dirname:
            try:
                dirname = os.path.abspath(dirname)
            except:
                return  # let Alarm force dir selection
        else:
            return

        self.dirname = dirname
        print('Selected dirname    =', dirname)
        fileD.clear()
        os.chdir(self.dirname)
        self.reset_statusbar_bg()
        self.need_to_pick_dir = 0

        #with open(NOSY_USER_DATA_FILE, 'w') as text_file:
        #    text_file.write( self.dirname )

        self.numNosyCalls = 0

    def reset_statusbar_bg(self):
        """Return status bar to default state"""
        self.statusbar.config(bg=self.statusbar_bg)
        self.statusMessage.set(self.dirname)

    def set_statusbar_bg(self, c):
        """Set status bar to show new color and message"""
        self.statusbar.config(bg=c)
        self.oscillator_B = 1  # will return to initial color after a few cycles

    def menu_Directory_Change_Dir(self):
        """Menu selection to set directory in which to run nosetests"""
        dirname = self.AskDirectory(title='Choose Directory For Nose Tests',
                                    initialdir=".")
        if dirname:
            self.try_change_to_new_dir(dirname)
        # >>>>>>insert any user code below this comment for section "menu_Directory_Change_Dir"
        # replace, delete, or comment-out the following
        print("called menu_Directory_Change_Dir")

    def menu_Run(self):
        """User initiates a nosetests run, not file change detection."""
        print("called menu_Run")
        self.callNosy()

    def rerun_tests(self, *args):
        self.menu_Run()

    def callNosy(self):
        """Run nosetests and display results"""
        self.numNosyCalls += 1

        runL = [(self.defaultPyInterp, self)]
        for PI, Popup in Tk_Nosy.concurrent_versionL:
            runL.append((PI, Popup))

        for PI, tkwindow in runL:
            tkwindow.Text_1.delete(1.0, END)

            # turn indicator button gray while running the tests
            tkwindow.Pass_Fail_Button.config(background="#999999",
                                             text='TESTING...',
                                             font=self.arial_12_bold_font)
        self.master.update()
        self.master.update_idletasks()

        for PI, tkwindow in runL:
            self.run_tkwin_nosetests(PI, tkwindow)

        self.master.winfo_toplevel().wm_geometry("")

    def run_tkwin_nosetests(self, PI, tkwindow):
        """Run nosetests for main python interpreter and any concurrent
             python interpreters.

           Update GUI to show results.
        """

        if PI.nose_version == None:
            # if nose was not installed last time we checked, check again
            PI.nose_version, err_msg = get_nose_version_info(PI.full_path)
            if PI.nose_version == None:
                print("\a")  # make beep
                s = 'Can not verify nose for:\nPython ' + PI.name()
                tkwindow.Pass_Fail_Button.config(background='orange',
                                                 text=s,
                                                 font=self.arial_12_bold_font)
                s = 'Please verify nose installed for:\n'+str(PI) +\
                    '\n\n' + err_msg+\
                    '\n\nFor install instructions see:\n'+\
                    'https://nose.readthedocs.org/en/latest/'
                tkwindow.Text_1.insert(END, s)
                ShowError(title='Can not verify nose', message=s)
                return

        # pylint: disable=W0201
        passedAllTests, numPassed, numFailed, numErrors, numSkipped, outputTextL = \
            run_nosetests(self.numNosyCalls, PI, display_test_details=self.display_test_details.get())

        max_len_s = 42
        num_lines = 1
        for s in outputTextL:
            tkwindow.Text_1.insert(END, s)
            sL = s.split('\n')
            for ss in sL:
                max_len_s = max(max_len_s, len(ss))
                num_lines += 1

        if self.numNosyCalls % 2:
            myFont = self.arial_12_bold_font
        else:
            myFont = self.arial_12_font

        if passedAllTests:
            s = 'PASSED'
            if numPassed > 1:
                s = 'PASSED ALL %i TESTS' % numPassed
            elif numPassed == 1:
                s = 'PASSED ONE TEST'

            bg = "#00ff00"
            if numSkipped == 1:
                s = 'passed with 1 SKIP'
                bg = "#00cc00"
            elif numSkipped > 1:
                s = 'passed with %i SKIPS' % numSkipped
                bg = "#00cc00"
            elif numPassed == 0:
                s = 'No Tests Found'
                bg = "#ff8000"
            tkwindow.Pass_Fail_Button.config(background=bg,
                                             text=s,
                                             font=myFont)

            #self.master.geometry('200x50')
        else:
            s = 'FAILED %i, ERRORS %i, SKIP %i, PASSED %i' % (
                numFailed, numErrors, numSkipped, numPassed)
            tkwindow.Pass_Fail_Button.config(background="#ff0000",
                                             text=s,
                                             font=myFont)
            #self.master.geometry('516x385')

        # Show list of files being watched.
        #self.Text_1.insert(END, '_'*40+'\n')
        tkwindow.Text_1.insert(END,
                               'WATCHED *.py FILES'.center(40, '_') + '\n')
        tkwindow.Text_1.insert(END, '%s%s..\n\n' % (self.dirname, os.path.sep))
        num_lines += 3

        len_dirname = len(self.dirname)

        if self.display_watched_files.get() == 'Y':
            keyL = list(fileD.keys())
            keyL.sort()
            lastdir = ''
            for key in keyL:
                dn = os.path.dirname(key)
                if dn != lastdir:
                    tkwindow.Text_1.insert(END, '..' + dn[len_dirname:] + '\n')
                    max_len_s = max(max_len_s, len(dn) + 1)
                    lastdir = dn
                    num_lines += 1
                s = '    ' + os.path.basename(key)
                tkwindow.Text_1.insert(END, s + '\n')
                max_len_s = max(max_len_s, len(s) + 1)
                num_lines += 1
        else:
            num_lines += 1
            tkwindow.Text_1.insert(END,
                                   '     %i files watched.\n' % len(fileD))

        tkwindow.Text_1.config(width=max_len_s)
        tkwindow.Text_1.config(height=min(40, num_lines))

    def bindConfigure(self, event):
        """Part of goofy main window setup in tkinter."""
        #  tkinter requires arguments, but I don't use them
        # pylint: disable=W0613
        if not self.initComplete:
            self.master.bind("<Configure>", self.Master_Configure)
            self.initComplete = 1

    def change_python_exe(self, full_path):
        """Allow nosetests to be run under any available python version """
        PI = Tk_Nosy.pythonInterpreterCollection.add_interp(full_path)
        if PI:
            self.defaultPyInterp = PI

    def findNewPythonInterpreter(self):
        """Find a new python interpreter, one that is not already in
             the PyInterpsOnSys object (pythonInterpreterCollection).
        """
        if Tk_Nosy.pythonInterpreterCollection == None:
            print('pythonInterpreterCollection NOT yet initialized')
            self.statusMessage.set('Interpreter Collection NOT initialized')
            self.set_statusbar_bg('#FF9999')
            return

        print('Open File')
        filetypes = [('python executable', 'py*'), ('Any File', '*.*')]
        pathopen = tkFileDialog.askopenfilename(
            parent=self.master,
            title='Select Python Executable',
            filetypes=filetypes,
            initialdir=self.defaultPyInterp.full_path)

        if pathopen:
            self.change_python_exe(pathopen)
            self.menu_Run()

    def kill_popup_window(self, popup_name):
        """Close a popup window running another versions of python interpreter"""
        for itup, tup in enumerate(Tk_Nosy.concurrent_versionL):
            PI, Popup = tup
            s = '%s %s' % (PI.exe_name, PI.version_str)
            if popup_name == s:
                Tk_Nosy.concurrent_versionL.pop(itup)
                return True  # removed popup from list
        return False  # no popup found

    def launchAnotherPythonInterpreter(self):
        """Launch a pop-up window that concurrently runs another python version"""

        removeNameL = [self.defaultPyInterp.name()]
        for PI, Popup in Tk_Nosy.concurrent_versionL:
            removeNameL.append(PI.name())

        piL = Tk_Nosy.pythonInterpreterCollection.get_PI_list(
            removeNameL=removeNameL)
        if len(piL) == 0:
            print('All identified python interpreters in use.')
        else:
            print([pi.name() for pi in piL])
            rbL = [PI.name() for PI in piL]
            dialog = Select_Py_Version(self.master,
                                       "Launch Another Python Version",
                                       dialogOptions={'rbL': rbL})
            if dialog.result:
                PI = Tk_Nosy.pythonInterpreterCollection.get_PI_obj_by_name(
                    dialog.result['selection'])

                s = '%s %s' % (PI.exe_name, PI.version_str)
                Popup = SatelliteWindow(self, self.master, s)
                Tk_Nosy.concurrent_versionL.append((PI, Popup))
                self.menu_Run()

    def changePythonVersion(self):
        """Change to a different python version.
           If the PyInterpsOnSys object (pythonInterpreterCollection) has been
           initialized, select from its list.
           Otherwise find the python interpreter executable
           (ex. python.exe or python)
        """
        if (Tk_Nosy.pythonInterpreterCollection == None) or \
           (Tk_Nosy.pythonInterpreterCollection.num_terps() == 0):
            # If there is no list of available python interpreters, look for python file
            print('Open File')
            filetypes = [('python executable', 'py*'), ('Any File', '*.*')]
            pathopen = tkFileDialog.askopenfilename(
                parent=self.master,
                title='Select Python Executable',
                filetypes=filetypes,
                initialdir=self.defaultPyInterp.full_path)

            if pathopen:
                self.change_python_exe(pathopen)
                self.menu_Run()
        else:
            rbL = [
                PI.name() for PI in Tk_Nosy.pythonInterpreterCollection.interpL
            ]
            dialog = Select_Py_Version(self.master,
                                       "Select Python Version",
                                       dialogOptions={'rbL': rbL})
            if dialog.result:
                PI = Tk_Nosy.pythonInterpreterCollection.get_PI_obj_by_name(
                    dialog.result['selection'])
                pathopen = PI.full_path

                self.change_python_exe(pathopen)
                self.menu_Run()

    # return a string containing directory name
    def AskDirectory(self, title='Choose Directory', initialdir="."):
        """Run pop-up menu for user to select directory."""
        #    This is not an error
        # pylint: disable=E1101

        if sys.version_info < (3, ):
            dirname = tkFileDialog.askdirectory(parent=self.master,
                                                initialdir=initialdir,
                                                title=title)
        else:
            dirname = tkFileDialog.askdirectory(parent=self.master,
                                                initialdir=initialdir,
                                                title=title)
        return dirname  # <-- string

    def Master_Configure(self, event):
        """Part of tkinter main window initialization"""

        if event.widget != self.master:
            if self.w != -1:
                return
        x = int(self.master.winfo_x())
        y = int(self.master.winfo_y())
        w = int(self.master.winfo_width())
        h = int(self.master.winfo_height())
        if (self.x, self.y, self.w, self.h) == (-1, -1, -1, -1):
            self.x, self.y, self.w, self.h = x, y, w, h

        if self.w != w or self.h != h:
            #print "Master reconfigured... make resize adjustments"
            self.w = w
            self.h = h

    # pylint: disable=W0613
    def Pass_Fail_Button_Click(self, event):
        """Routine for user clicking Pass/Fail Button"""
        print('Arranging Windows by User Request')
        num_popups = len(Tk_Nosy.concurrent_versionL)
        DX = 50
        DY = 70
        x = 10
        y = 10 + num_popups * DY
        self.master.geometry('+%i+%i' % (x, y))

        for PI, Popup in Tk_Nosy.concurrent_versionL:
            x += DX
            y -= DY
            Popup.geometry('+%i+%i' % (x, y))

    # alarm function is called after specified number of milliseconds
    def SetAlarm(self, milliseconds=1000):
        """Reinitialize tkinter alarm mechanism as well as update seconds
           counter in main window title bar.
        """
        self.master.after(milliseconds, self.Alarm)

        self.oscillator += 1
        if self.oscillator > 5:
            self.oscillator = 0

        if self.oscillator_B > 0:
            self.oscillator_B += 1
        if self.oscillator_B > 5:
            self.oscillator_B = 0
            self.reset_statusbar_bg()

        pad = '|' * self.oscillator

        s = '%s (v%s)' % (self.defaultPyInterp.exe_name,
                          self.defaultPyInterp.version_str)

        self.master.title('%i) %s ' % (self.numNosyCalls, s + pad))

        for PI, Popup in Tk_Nosy.concurrent_versionL:
            s = '%s (v%s)' % (PI.exe_name, PI.version_str)
            Popup.title('%i) %s ' % (self.numNosyCalls, s + pad))

    def Alarm(self):
        """Look for changed files every second, then reset alarm"""
        if self.need_to_pick_dir:
            dirname = self.AskDirectory(
                title='Choose Directory For Nose Tests', initialdir=".")
            self.try_change_to_new_dir(dirname)

        #first call to numberOfChangedFiles will be > 0 if any .py files are found
        elif numberOfChangedFiles(self.dirname) > 0:  # or self.numNosyCalls==0
            self.callNosy()

        self.SetAlarm()
class mgmainmenu:
    # init application menubar, the Dump Options menu and the Database Select options + another Info/help/about
    # the callbacks are in a dictionary like "newfilefn":MGU_newfilefn ...
    # the statevars are in a dictionary like "dumpfileopt":MGU_dumpfileoptval
    # caller provides them... menu state changes update them/ call them
    def __init__(self, topwin, keyvalcallbacks, keyvalstatevars):
        self.parent = topwin
        self.menubar = Menu(self.parent)
        self.keyvalstatevars = keyvalstatevars
        #???self.view_menu = Menu(self.menubar)
        # the file menu
        Command_button = Menubutton(self.menubar,
                                    text='Simple Button Commands',
                                    underline=0)
        Command_button.menu = Menu(Command_button)
        #Command_button.menu = Menu(self.menubar)
        Command_button.menu.add_command(label="Undo")
        # undo is the 0th entry...
        Command_button.menu.entryconfig(0, state=DISABLED)
        Command_button.menu.add_command(label='New...',
                                        underline=0,
                                        command=keyvalcallbacks["newfilefn"])
        Command_button.menu.add_command(label='Open...',
                                        underline=0,
                                        command=keyvalcallbacks["openfilefn"])
        Command_button.menu.add_command(
            label='ReScan...',
            underline=0,
            command=keyvalcallbacks["rescanmenufn"])
        # alternate font example
        Command_button.menu.add_command(
            label='Print',
            underline=0,
            font='-*-helvetica-*-r-*-*-*-180-*-*-*-*-*-*',
            command=keyvalcallbacks["printmenufn"])
        # separator example
        Command_button.menu.add('separator')
        Command_button.menu.add_command(
            label='Options',
            underline=0,
            font='-*-helvetica-*-r-*-*-*-180-*-*-*-*-*-*',
            command=keyvalcallbacks["optionmenufn"])

        # aLternate color example
        Command_button.menu.add_command(label='Quit',
                                        underline=0,
                                        background='red',
                                        activebackground='green',
                                        command=keyvalcallbacks["quitmenufn"])
        self.menubar.add_cascade(label='File', menu=Command_button.menu)

        #self.menubar.add_cascade(label='View', menu=self.view_menu)
        # add a menubar menu to select option for dumping all elements, or only Dups or only Unique
        self.dumpselect_menu = Menu(self.menubar)
        self.dumpradioval = IntVar()
        self.dumpradioval.set(1)
        self.dumpselect_menu.add_radiobutton(
            label="Dump All", value=1, variable=keyvalstatevars['DumpOptions'])
        self.dumpselect_menu.add_radiobutton(
            label="Dump only unique",
            value=2,
            variable=keyvalstatevars['DumpOptions'])
        self.dumpselect_menu.add_radiobutton(
            label="Dump only Dups",
            value=3,
            variable=keyvalstatevars['DumpOptions'])
        self.dumpselect_menu.add('separator')
        self.dumpselect_menu.add_command(
            label='Default header log path',
            underline=0,
            background='red',
            activebackground='green',
            command=keyvalcallbacks["dumppath1menufn"])
        self.menubar.add_cascade(label='Dump File options',
                                 menu=self.dumpselect_menu)

        self.dumpdbselect_menu1 = Menu(self.menubar)
        self.menubar.add_cascade(label='Dump DB selection',
                                 menu=self.dumpdbselect_menu1)
        # add a placeholder for this menu with zero databases added in memory
        # this DUMP DB menu will be dynamically extended as new databases are scanned.
        #  the parent owner of the class instance must call the add method of this class to extend the menu
        self.dumpdbselect_menu1.add_command(label="<None>")
        # <None> is the 0th entry...
        self.dumpdbselect_menu1.entryconfig(0, state=DISABLED)
        # set a flag just so we can delete this <None> placeholder during first menu add operation
        self.newmenu = True

        self.dumpvolselect_menu1 = Menu(self.menubar)
        self.menubar.add_cascade(label='Dump Volume selection',
                                 menu=self.dumpvolselect_menu1)
        # add a placeholder for this menu with zero databases added in memory
        # this DUMP DB menu will be dynamically extended as new databases are scanned.
        #  the parent owner of the class instance must call the add method of this class to extend the menu
        self.dumpvolselect_menu1.add_command(label="<None>")
        # <None> is the 0th entry...
        self.dumpvolselect_menu1.entryconfig(0, state=DISABLED)
        # set a flag just so we can delete this <None> placeholder during first menu add operation
        self.newvolmenu = True

        # menu begins with only one  option- dump all
        #self.dumpdbselect_menu1.add_checkbutton(label="<None>", onvalue=False, offvalue=False, variable=self.var01)#variable=self.show_all)

        #
        # add a menubar menu for the app info Help/ about/ etc
        self.show_all = IntVar()
        self.show_all.set(True)
        self.vendorhelp_menu = Menu(self.menubar)
        self.help001 = {"Help": self.show_all}
        # menu begins with only one  option- dump all
        self.vendorhelp_menu.add_checkbutton(
            label="Show _Hints",
            onvalue=True,
            offvalue=False,
            variable=keyvalstatevars['HintsFlag'])
        self.vendorhelp_menu.add_checkbutton(
            label="Monitor volumes (10second timer activity)",
            onvalue=True,
            offvalue=False,
            variable=keyvalstatevars['TimerCheck'])
        self.vendorhelp_menu.add_command(label='Help...',
                                         underline=0,
                                         command=keyvalcallbacks["helpmenufn"])
        # separator example
        self.vendorhelp_menu.add('separator')
        self.vendorhelp_menu.add_command(label='Dbg info dump...',
                                         underline=0,
                                         command=self.MGmenudumpdbg)
        self.vendorhelp_menu.add_checkbutton(
            label='Dbg runtime info@HIGH',
            onvalue=True,
            offvalue=False,
            variable=keyvalstatevars['DebuginfohighFlag'])
        self.vendorhelp_menu.add_command(
            label='About...',
            underline=0,
            command=keyvalcallbacks["aboutmenufn"])
        self.menubar.add_cascade(label='Help', menu=self.vendorhelp_menu)

        # register all this with the callers parent window frame
        self.parent.config(menu=self.menubar)

    # add a menuitem to the check options in the database selection menu list
    # this option allows the databases to be added as they are specified & scanned by user
    # later used as criteria for which source data to include in the dump of memory database
    # NOTE that the caller MUST extend the @keyvalstatevars[] list to include the newlabel Key-Value pair for this new item with an associated BooleanVar()
    #   and init the associated variable (I default to False i.e. unchecked)
    def mgmenuitem_adddb(self, newlabel, addseparator=False):
        # if this is the first database added, lets remove the <None> menu option!
        if self.newmenu:
            self.newmenu = False
            self.dumpdbselect_menu1.delete(0, END)
        self.dumpdbselect_menu1.add_checkbutton(
            label=newlabel,
            onvalue=True,
            offvalue=False,
            variable=self.keyvalstatevars[newlabel])
        if addseparator:
            self.dumpdbselect_menu1.add('separator')

    def mgmenuitem_addvol(self, newlabel, addseparator=False):
        # if this is the first database added, lets remove the <None> menu option!
        if self.newmenu:
            self.newmenu = False
            self.dumpdbselect_menu1.delete(0, END)
        self.dumpvolselect_menu1.add_checkbutton(
            label=newlabel,
            onvalue=True,
            offvalue=False,
            variable=self.keyvalstatevars[newlabel])
        if addseparator:
            self.dumpvolselect_menu1.add(
                'separator'
            )  # get the menu radio selection for including All/Unique/Duplicate files in the dump

    #def mgmenuselection_dumpoptions_get(self):
    #    return self.dumpradioval.get()  ALL OF THE MENU STATES SHOULD LIE IN PARENT OF THIS INSTANCE! see keyvalstatevars{}
    # def MGmenugroups_get(self):
    #   return self.mgmenuselection_dumpoptions_get(), self.keyvalstatevars.it

    # tutorial dump of the state of the menu vars dictionary
    def MGmenudumpdbg(self):
        for i, (k, v) in enumerate(self.keyvalstatevars.items()):
            print(i, k, v.get())