Пример #1
0
    def create_buttonbox(self):
        # add standard button box. override if you don't want the
        # standard buttons

        box = Frame(self)

        w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
        w.pack(side=LEFT, padx=5, pady=5)

        self.bind("&<Return>", self.ok)
        self.bind("&<Escape>", self.ok)

        box.pack()
Пример #2
0
class ConcordanceSearchView(object):
    _BACKGROUND_COLOUR = '#FFF'  #white

    #Colour of highlighted results
    _HIGHLIGHT_WORD_COLOUR = '#F00'  #red
    _HIGHLIGHT_WORD_TAG = 'HL_WRD_TAG'

    _HIGHLIGHT_LABEL_COLOUR = '#C0C0C0'  # dark grey
    _HIGHLIGHT_LABEL_TAG = 'HL_LBL_TAG'

    #Percentage of text left of the scrollbar position
    _FRACTION_LEFT_TEXT = 0.30

    def __init__(self):
        self.queue = q.Queue()
        self.model = ConcordanceSearchModel(self.queue)
        self.top = Tk()
        self._init_top(self.top)
        self._init_menubar()
        self._init_widgets(self.top)
        self.load_corpus(self.model.DEFAULT_CORPUS)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def _init_top(self, top):
        top.geometry('950x680+50+50')
        top.title('NLTK Concordance Search')
        top.bind('<Control-q>', self.destroy)
        top.protocol('WM_DELETE_WINDOW', self.destroy)
        top.minsize(950, 680)

    def _init_widgets(self, parent):
        self.main_frame = Frame(
            parent,
            dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1))
        self._init_corpus_select(self.main_frame)
        self._init_query_box(self.main_frame)
        self._init_results_box(self.main_frame)
        self._init_paging(self.main_frame)
        self._init_status(self.main_frame)
        self.main_frame.pack(fill='both', expand=True)

    def _init_menubar(self):
        self._result_size = IntVar(self.top)
        self._cntx_bf_len = IntVar(self.top)
        self._cntx_af_len = IntVar(self.top)
        menubar = Menu(self.top)

        filemenu = Menu(menubar, tearoff=0, borderwidth=0)
        filemenu.add_command(label='Exit',
                             underline=1,
                             command=self.destroy,
                             accelerator='Ctrl-q')
        menubar.add_cascade(label='File', underline=0, menu=filemenu)

        editmenu = Menu(menubar, tearoff=0)
        rescntmenu = Menu(editmenu, tearoff=0)
        rescntmenu.add_radiobutton(label='20',
                                   variable=self._result_size,
                                   underline=0,
                                   value=20,
                                   command=self.set_result_size)
        rescntmenu.add_radiobutton(label='50',
                                   variable=self._result_size,
                                   underline=0,
                                   value=50,
                                   command=self.set_result_size)
        rescntmenu.add_radiobutton(label='100',
                                   variable=self._result_size,
                                   underline=0,
                                   value=100,
                                   command=self.set_result_size)
        rescntmenu.invoke(1)
        editmenu.add_cascade(label='Result Count',
                             underline=0,
                             menu=rescntmenu)

        cntxmenu = Menu(editmenu, tearoff=0)
        cntxbfmenu = Menu(cntxmenu, tearoff=0)
        cntxbfmenu.add_radiobutton(label='60 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0,
                                   value=60,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.add_radiobutton(label='80 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0,
                                   value=80,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.add_radiobutton(label='100 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0,
                                   value=100,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.invoke(1)
        cntxmenu.add_cascade(label='Before', underline=0, menu=cntxbfmenu)

        cntxafmenu = Menu(cntxmenu, tearoff=0)
        cntxafmenu.add_radiobutton(label='70 characters',
                                   variable=self._cntx_af_len,
                                   underline=0,
                                   value=70,
                                   command=self.set_cntx_af_len)
        cntxafmenu.add_radiobutton(label='90 characters',
                                   variable=self._cntx_af_len,
                                   underline=0,
                                   value=90,
                                   command=self.set_cntx_af_len)
        cntxafmenu.add_radiobutton(label='110 characters',
                                   variable=self._cntx_af_len,
                                   underline=0,
                                   value=110,
                                   command=self.set_cntx_af_len)
        cntxafmenu.invoke(1)
        cntxmenu.add_cascade(label='After', underline=0, menu=cntxafmenu)

        editmenu.add_cascade(label='Context', underline=0, menu=cntxmenu)

        menubar.add_cascade(label='Edit', underline=0, menu=editmenu)

        self.top.config(menu=menubar)

    def set_result_size(self, **kwargs):
        self.model.result_count = self._result_size.get()

    def set_cntx_af_len(self, **kwargs):
        self._char_after = self._cntx_af_len.get()

    def set_cntx_bf_len(self, **kwargs):
        self._char_before = self._cntx_bf_len.get()

    def _init_corpus_select(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.var = StringVar(innerframe)
        self.var.set(self.model.DEFAULT_CORPUS)
        Label(innerframe,
              justify=LEFT,
              text=' Corpus: ',
              background=self._BACKGROUND_COLOUR,
              padx=2,
              pady=1,
              border=0).pack(side='left')

        other_corpora = list(self.model.CORPORA.keys()).remove(
            self.model.DEFAULT_CORPUS)
        om = OptionMenu(innerframe,
                        self.var,
                        self.model.DEFAULT_CORPUS,
                        command=self.corpus_selected,
                        *self.model.non_default_corpora())
        om['borderwidth'] = 0
        om['highlightthickness'] = 1
        om.pack(side='left')
        innerframe.pack(side='top', fill='x', anchor='n')

    def _init_status(self, parent):
        self.status = Label(parent,
                            justify=LEFT,
                            relief=SUNKEN,
                            background=self._BACKGROUND_COLOUR,
                            border=0,
                            padx=1,
                            pady=0)
        self.status.pack(side='top', anchor='sw')

    def _init_query_box(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        another = Frame(innerframe, background=self._BACKGROUND_COLOUR)
        self.query_box = Entry(another, width=60)
        self.query_box.pack(side='left', fill='x', pady=25, anchor='center')
        self.search_button = Button(another,
                                    text='Search',
                                    command=self.search,
                                    borderwidth=1,
                                    highlightthickness=1)
        self.search_button.pack(side='left',
                                fill='x',
                                pady=25,
                                anchor='center')
        self.query_box.bind('<KeyPress-Return>',
                            self.search_enter_keypress_handler)
        another.pack()
        innerframe.pack(side='top', fill='x', anchor='n')

    def search_enter_keypress_handler(self, *event):
        self.search()

    def _init_results_box(self, parent):
        innerframe = Frame(parent)
        i1 = Frame(innerframe)
        i2 = Frame(innerframe)
        vscrollbar = Scrollbar(i1, borderwidth=1)
        hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz')
        self.results_box = Text(i1,
                                font=Font(family='courier', size='16'),
                                state='disabled',
                                borderwidth=1,
                                yscrollcommand=vscrollbar.set,
                                xscrollcommand=hscrollbar.set,
                                wrap='none',
                                width='40',
                                height='20',
                                exportselection=1)
        self.results_box.pack(side='left', fill='both', expand=True)
        self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG,
                                    foreground=self._HIGHLIGHT_WORD_COLOUR)
        self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG,
                                    foreground=self._HIGHLIGHT_LABEL_COLOUR)
        vscrollbar.pack(side='left', fill='y', anchor='e')
        vscrollbar.config(command=self.results_box.yview)
        hscrollbar.pack(side='left', fill='x', expand=True, anchor='w')
        hscrollbar.config(command=self.results_box.xview)
        #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!!
        Label(i2, text='   ',
              background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e')
        i1.pack(side='top', fill='both', expand=True, anchor='n')
        i2.pack(side='bottom', fill='x', anchor='s')
        innerframe.pack(side='top', fill='both', expand=True)

    def _init_paging(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.prev = prev = Button(innerframe,
                                  text='Previous',
                                  command=self.previous,
                                  width='10',
                                  borderwidth=1,
                                  highlightthickness=1,
                                  state='disabled')
        prev.pack(side='left', anchor='center')
        self.next = next = Button(innerframe,
                                  text='Next',
                                  command=self.__next__,
                                  width='10',
                                  borderwidth=1,
                                  highlightthickness=1,
                                  state='disabled')
        next.pack(side='right', anchor='center')
        innerframe.pack(side='top', fill='y')
        self.current_page = 0

    def previous(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.prev(self.current_page - 1)

    def __next__(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.next(self.current_page + 1)

    def about(self, *e):
        ABOUT = ("NLTK Concordance Search Demo\n")
        TITLE = 'About: NLTK Concordance Search Demo'
        try:
            from six.moves.tkinter_messagebox import Message
            Message(message=ABOUT, title=TITLE, parent=self.main_frame).show()
        except:
            ShowText(self.top, TITLE, ABOUT)

    def _bind_event_handlers(self):
        self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded)
        self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated)
        self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error)
        self.top.bind(ERROR_LOADING_CORPUS_EVENT,
                      self.handle_error_loading_corpus)

    def _poll(self):
        try:
            event = self.queue.get(block=False)
        except q.Empty:
            pass
        else:
            if event == CORPUS_LOADED_EVENT:
                self.handle_corpus_loaded(event)
            elif event == SEARCH_TERMINATED_EVENT:
                self.handle_search_terminated(event)
            elif event == SEARCH_ERROR_EVENT:
                self.handle_search_error(event)
            elif event == ERROR_LOADING_CORPUS_EVENT:
                self.handle_error_loading_corpus(event)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def handle_error_loading_corpus(self, event):
        self.status['text'] = 'Error in loading ' + self.var.get()
        self.unfreeze_editable()
        self.clear_all()
        self.freeze_editable()

    def handle_corpus_loaded(self, event):
        self.status['text'] = self.var.get() + ' is loaded'
        self.unfreeze_editable()
        self.clear_all()
        self.query_box.focus_set()

    def handle_search_terminated(self, event):
        #todo: refactor the model such that it is less state sensitive
        results = self.model.get_results()
        self.write_results(results)
        self.status['text'] = ''
        if len(results) == 0:
            self.status['text'] = 'No results found for ' + self.model.query
        else:
            self.current_page = self.model.last_requested_page
        self.unfreeze_editable()
        self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT)

    def handle_search_error(self, event):
        self.status['text'] = 'Error in query ' + self.model.query
        self.unfreeze_editable()

    def corpus_selected(self, *args):
        new_selection = self.var.get()
        self.load_corpus(new_selection)

    def load_corpus(self, selection):
        if self.model.selected_corpus != selection:
            self.status['text'] = 'Loading ' + selection + '...'
            self.freeze_editable()
            self.model.load_corpus(selection)

    def search(self):
        self.current_page = 0
        self.clear_results_box()
        self.model.reset_results()
        query = self.query_box.get()
        if (len(query.strip()) == 0): return
        self.status['text'] = 'Searching for ' + query
        self.freeze_editable()
        self.model.search(
            query,
            self.current_page + 1,
        )

    def write_results(self, results):
        self.results_box['state'] = 'normal'
        row = 1
        for each in results:
            sent, pos1, pos2 = each[0].strip(), each[1], each[2]
            if len(sent) != 0:
                if (pos1 < self._char_before):
                    sent, pos1, pos2 = self.pad(sent, pos1, pos2)
                sentence = sent[pos1 - self._char_before:pos1 +
                                self._char_after]
                if not row == len(results):
                    sentence += '\n'
                self.results_box.insert(str(row) + '.0', sentence)
                word_markers, label_markers = self.words_and_labels(
                    sent, pos1, pos2)
                for marker in word_markers:
                    self.results_box.tag_add(self._HIGHLIGHT_WORD_TAG,
                                             str(row) + '.' + str(marker[0]),
                                             str(row) + '.' + str(marker[1]))
                for marker in label_markers:
                    self.results_box.tag_add(self._HIGHLIGHT_LABEL_TAG,
                                             str(row) + '.' + str(marker[0]),
                                             str(row) + '.' + str(marker[1]))
                row += 1
        self.results_box['state'] = 'disabled'

    def words_and_labels(self, sentence, pos1, pos2):
        search_exp = sentence[pos1:pos2]
        words, labels = [], []
        labeled_words = search_exp.split(' ')
        index = 0
        for each in labeled_words:
            if each == '':
                index += 1
            else:
                word, label = each.split('/')
                words.append((self._char_before + index,
                              self._char_before + index + len(word)))
                index += len(word) + 1
                labels.append((self._char_before + index,
                               self._char_before + index + len(label)))
                index += len(label)
            index += 1
        return words, labels

    def pad(self, sent, hstart, hend):
        if hstart >= self._char_before:
            return sent, hstart, hend
        d = self._char_before - hstart
        sent = ''.join([' '] * d) + sent
        return sent, hstart + d, hend + d

    def destroy(self, *e):
        if self.top is None: return
        self.top.after_cancel(self.after)
        self.top.destroy()
        self.top = None

    def clear_all(self):
        self.query_box.delete(0, END)
        self.model.reset_query()
        self.clear_results_box()

    def clear_results_box(self):
        self.results_box['state'] = 'normal'
        self.results_box.delete("1.0", END)
        self.results_box['state'] = 'disabled'

    def freeze_editable(self):
        self.query_box['state'] = 'disabled'
        self.search_button['state'] = 'disabled'
        self.prev['state'] = 'disabled'
        self.next['state'] = 'disabled'

    def unfreeze_editable(self):
        self.query_box['state'] = 'normal'
        self.search_button['state'] = 'normal'
        self.set_paging_button_states()

    def set_paging_button_states(self):
        if self.current_page == 0 or self.current_page == 1:
            self.prev['state'] = 'disabled'
        else:
            self.prev['state'] = 'normal'
        if self.model.has_more_pages(self.current_page):
            self.next['state'] = 'normal'
        else:
            self.next['state'] = 'disabled'

    def fire_event(self, event):
        #Firing an event so that rendering of widgets happen in the mainloop thread
        self.top.event_generate(event, when='tail')

    def mainloop(self, *args, **kwargs):
        if in_idle(): return
        self.top.mainloop(*args, **kwargs)
Пример #3
0
class LintGui(object):
    """Build and control a window to interact with pylint"""

    def __init__(self, root=None):
        """init"""
        self.root = root or Tk()
        self.root.title('Pylint')
        #reporter
        self.reporter = None
        #message queue for output from reporter
        self.msg_queue = six.moves.queue.Queue()
        self.msgs = []
        self.visible_msgs = []
        self.filenames = []
        self.rating = StringVar()
        self.tabs = {}
        self.report_stream = BasicStream(self)
        #gui objects
        self.lb_messages = None
        self.showhistory = None
        self.results = None
        self.btnRun = None
        self.information_box = None
        self.convention_box = None
        self.refactor_box = None
        self.warning_box = None
        self.error_box = None
        self.fatal_box = None
        self.txtModule = None
        self.status = None
        self.msg_type_dict = None
        self.init_gui()

    def init_gui(self):
        """init helper"""

        window = PanedWindow(self.root, orient="vertical")
        window.pack(side=TOP, fill=BOTH, expand=True)

        top_pane = Frame(window)
        window.add(top_pane)
        mid_pane = Frame(window)
        window.add(mid_pane)
        bottom_pane = Frame(window)
        window.add(bottom_pane)

        #setting up frames
        top_frame = Frame(top_pane)
        mid_frame = Frame(top_pane)
        history_frame = Frame(top_pane)
        radio_frame = Frame(mid_pane)
        rating_frame = Frame(mid_pane)
        res_frame = Frame(mid_pane)
        check_frame = Frame(bottom_pane)
        msg_frame = Frame(bottom_pane)
        btn_frame = Frame(bottom_pane)
        top_frame.pack(side=TOP, fill=X)
        mid_frame.pack(side=TOP, fill=X)
        history_frame.pack(side=TOP, fill=BOTH, expand=True)
        radio_frame.pack(side=TOP, fill=X)
        rating_frame.pack(side=TOP, fill=X)
        res_frame.pack(side=TOP, fill=BOTH, expand=True)
        check_frame.pack(side=TOP, fill=X)
        msg_frame.pack(side=TOP, fill=BOTH, expand=True)
        btn_frame.pack(side=TOP, fill=X)

        # Binding F5 application-wide to run lint
        self.root.bind('<F5>', self.run_lint)

        #Message ListBox
        rightscrollbar = Scrollbar(msg_frame)
        rightscrollbar.pack(side=RIGHT, fill=Y)
        bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL)
        bottomscrollbar.pack(side=BOTTOM, fill=X)
        self.lb_messages = Listbox(
            msg_frame,
            yscrollcommand=rightscrollbar.set,
            xscrollcommand=bottomscrollbar.set,
            bg="white")
        self.lb_messages.bind("<Double-Button-1>", self.show_sourcefile)
        self.lb_messages.pack(expand=True, fill=BOTH)
        rightscrollbar.config(command=self.lb_messages.yview)
        bottomscrollbar.config(command=self.lb_messages.xview)

        #History ListBoxes
        rightscrollbar2 = Scrollbar(history_frame)
        rightscrollbar2.pack(side=RIGHT, fill=Y)
        bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL)
        bottomscrollbar2.pack(side=BOTTOM, fill=X)
        self.showhistory = Listbox(
            history_frame,
            yscrollcommand=rightscrollbar2.set,
            xscrollcommand=bottomscrollbar2.set,
            bg="white")
        self.showhistory.pack(expand=True, fill=BOTH)
        rightscrollbar2.config(command=self.showhistory.yview)
        bottomscrollbar2.config(command=self.showhistory.xview)
        self.showhistory.bind('<Double-Button-1>', self.select_recent_file)
        self.set_history_window()

        #status bar
        self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W)
        self.status.pack(side=BOTTOM, fill=X)

        #labelbl_ratingls
        lbl_rating_label = Label(rating_frame, text='Rating:')
        lbl_rating_label.pack(side=LEFT)
        lbl_rating = Label(rating_frame, textvariable=self.rating)
        lbl_rating.pack(side=LEFT)
        Label(mid_frame, text='Recently Used:').pack(side=LEFT)
        Label(top_frame, text='Module or package').pack(side=LEFT)

        #file textbox
        self.txt_module = Entry(top_frame, background='white')
        self.txt_module.bind('<Return>', self.run_lint)
        self.txt_module.pack(side=LEFT, expand=True, fill=X)

        #results box
        rightscrollbar = Scrollbar(res_frame)
        rightscrollbar.pack(side=RIGHT, fill=Y)
        bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL)
        bottomscrollbar.pack(side=BOTTOM, fill=X)
        self.results = Listbox(
            res_frame,
            yscrollcommand=rightscrollbar.set,
            xscrollcommand=bottomscrollbar.set,
            bg="white", font="Courier")
        self.results.pack(expand=True, fill=BOTH, side=BOTTOM)
        rightscrollbar.config(command=self.results.yview)
        bottomscrollbar.config(command=self.results.xview)

        #buttons
        Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT)
        Button(top_frame, text='Open Package',
               command=(lambda: self.file_open(package=True))).pack(side=LEFT)

        self.btnRun = Button(top_frame, text='Run', command=self.run_lint)
        self.btnRun.pack(side=LEFT)
        Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM)

        #radio buttons
        self.information_box = IntVar()
        self.convention_box = IntVar()
        self.refactor_box = IntVar()
        self.warning_box = IntVar()
        self.error_box = IntVar()
        self.fatal_box = IntVar()
        i = Checkbutton(check_frame, text="Information", fg=COLORS['(I)'],
                        variable=self.information_box, command=self.refresh_msg_window)
        c = Checkbutton(check_frame, text="Convention", fg=COLORS['(C)'],
                        variable=self.convention_box, command=self.refresh_msg_window)
        r = Checkbutton(check_frame, text="Refactor", fg=COLORS['(R)'],
                        variable=self.refactor_box, command=self.refresh_msg_window)
        w = Checkbutton(check_frame, text="Warning", fg=COLORS['(W)'],
                        variable=self.warning_box, command=self.refresh_msg_window)
        e = Checkbutton(check_frame, text="Error", fg=COLORS['(E)'],
                        variable=self.error_box, command=self.refresh_msg_window)
        f = Checkbutton(check_frame, text="Fatal", fg=COLORS['(F)'],
                        variable=self.fatal_box, command=self.refresh_msg_window)
        i.select()
        c.select()
        r.select()
        w.select()
        e.select()
        f.select()
        i.pack(side=LEFT)
        c.pack(side=LEFT)
        r.pack(side=LEFT)
        w.pack(side=LEFT)
        e.pack(side=LEFT)
        f.pack(side=LEFT)

        #check boxes
        self.box = StringVar()
        # XXX should be generated
        report = Radiobutton(
            radio_frame, text="Report", variable=self.box,
            value="Report", command=self.refresh_results_window)
        raw_met = Radiobutton(
            radio_frame, text="Raw metrics", variable=self.box,
            value="Raw metrics", command=self.refresh_results_window)
        dup = Radiobutton(
            radio_frame, text="Duplication", variable=self.box,
            value="Duplication", command=self.refresh_results_window)
        ext = Radiobutton(
            radio_frame, text="External dependencies",
            variable=self.box, value="External dependencies",
            command=self.refresh_results_window)
        stat = Radiobutton(
            radio_frame, text="Statistics by type",
            variable=self.box, value="Statistics by type",
            command=self.refresh_results_window)
        msg_cat = Radiobutton(
            radio_frame, text="Messages by category",
            variable=self.box, value="Messages by category",
            command=self.refresh_results_window)
        msg = Radiobutton(
            radio_frame, text="Messages", variable=self.box,
            value="Messages", command=self.refresh_results_window)
        source_file = Radiobutton(
            radio_frame, text="Source File", variable=self.box,
            value="Source File", command=self.refresh_results_window)
        report.select()
        report.grid(column=0, row=0, sticky=W)
        raw_met.grid(column=1, row=0, sticky=W)
        dup.grid(column=2, row=0, sticky=W)
        msg.grid(column=3, row=0, sticky=W)
        stat.grid(column=0, row=1, sticky=W)
        msg_cat.grid(column=1, row=1, sticky=W)
        ext.grid(column=2, row=1, sticky=W)
        source_file.grid(column=3, row=1, sticky=W)

        #dictionary for check boxes and associated error term
        self.msg_type_dict = {
            'I': lambda: self.information_box.get() == 1,
            'C': lambda: self.convention_box.get() == 1,
            'R': lambda: self.refactor_box.get() == 1,
            'E': lambda: self.error_box.get() == 1,
            'W': lambda: self.warning_box.get() == 1,
            'F': lambda: self.fatal_box.get() == 1
        }
        self.txt_module.focus_set()


    def select_recent_file(self, event): # pylint: disable=unused-argument
        """adds the selected file in the history listbox to the Module box"""
        if not self.showhistory.size():
            return

        selected = self.showhistory.curselection()
        item = self.showhistory.get(selected)
        #update module
        self.txt_module.delete(0, END)
        self.txt_module.insert(0, item)

    def refresh_msg_window(self):
        """refresh the message window with current output"""
        #clear the window
        self.lb_messages.delete(0, END)
        self.visible_msgs = []
        for msg in self.msgs:
            if self.msg_type_dict.get(msg.C)():
                self.visible_msgs.append(msg)
                msg_str = convert_to_string(msg)
                self.lb_messages.insert(END, msg_str)
                fg_color = COLORS.get(msg_str[:3], 'black')
                self.lb_messages.itemconfigure(END, fg=fg_color)

    def refresh_results_window(self):
        """refresh the results window with current output"""
        #clear the window
        self.results.delete(0, END)
        try:
            for res in self.tabs[self.box.get()]:
                self.results.insert(END, res)
        except KeyError:
            pass

    def process_incoming(self):
        """process the incoming messages from running pylint"""
        while self.msg_queue.qsize():
            try:
                msg = self.msg_queue.get(0)
                if msg == "DONE":
                    self.report_stream.output_contents()
                    return False

                #adding message to list of msgs
                self.msgs.append(msg)

                #displaying msg if message type is selected in check box
                if self.msg_type_dict.get(msg.C)():
                    self.visible_msgs.append(msg)
                    msg_str = convert_to_string(msg)
                    self.lb_messages.insert(END, msg_str)
                    fg_color = COLORS.get(msg_str[:3], 'black')
                    self.lb_messages.itemconfigure(END, fg=fg_color)

            except six.moves.queue.Empty:
                pass
        return True

    def periodic_call(self):
        """determine when to unlock the run button"""
        if self.process_incoming():
            self.root.after(100, self.periodic_call)
        else:
            #enabling button so it can be run again
            self.btnRun.config(state=NORMAL)

    def mainloop(self):
        """launch the mainloop of the application"""
        self.root.mainloop()

    def quit(self, _=None):
        """quit the application"""
        self.root.quit()

    def halt(self): # pylint: disable=no-self-use
        """program halt placeholder"""
        return

    def file_open(self, package=False, _=None):
        """launch a file browser"""
        if not package:
            filename = askopenfilename(parent=self.root,
                                       filetypes=[('pythonfiles', '*.py'),
                                                  ('allfiles', '*')],
                                       title='Select Module')
        else:
            filename = askdirectory(title="Select A Folder", mustexist=1)

        if filename == ():
            return

        self.txt_module.delete(0, END)
        self.txt_module.insert(0, filename)

    def update_filenames(self):
        """update the list of recent filenames"""
        filename = self.txt_module.get()
        if not filename:
            filename = os.getcwd()
        if filename+'\n' in self.filenames:
            index = self.filenames.index(filename+'\n')
            self.filenames.pop(index)

        #ensure only 10 most recent are stored
        if len(self.filenames) == 10:
            self.filenames.pop()
        self.filenames.insert(0, filename+'\n')

    def set_history_window(self):
        """update the history window with info from the history file"""
        #clear the window
        self.showhistory.delete(0, END)
        # keep the last 10 most recent files
        try:
            view_history = open(HOME+HISTORY, 'r')
            for hist in view_history.readlines():
                if not hist in self.filenames:
                    self.filenames.append(hist)
                self.showhistory.insert(END, hist.split('\n')[0])
            view_history.close()
        except IOError:
            # do nothing since history file will be created later
            return

    def run_lint(self, _=None):
        """launches pylint"""
        self.update_filenames()
        self.root.configure(cursor='watch')
        self.reporter = GUIReporter(self, output=self.report_stream)
        module = self.txt_module.get()
        if not module:
            module = os.getcwd()

        #cleaning up msgs and windows
        self.msgs = []
        self.visible_msgs = []
        self.lb_messages.delete(0, END)
        self.tabs = {}
        self.results.delete(0, END)
        self.btnRun.config(state=DISABLED)

        #setting up a worker thread to run pylint
        worker = Thread(target=lint_thread, args=(module, self.reporter, self,))
        self.periodic_call()
        worker.start()

        # Overwrite the .pylint-gui-history file with all the new recently added files
        # in order from filenames but only save last 10 files
        write_history = open(HOME+HISTORY, 'w')
        write_history.writelines(self.filenames)
        write_history.close()
        self.set_history_window()

        self.root.configure(cursor='')

    def show_sourcefile(self, event=None):  # pylint: disable=unused-argument
        selected = self.lb_messages.curselection()
        if not selected:
            return

        msg = self.visible_msgs[int(selected[0])]
        scroll = msg.line - 3
        if scroll < 0:
            scroll = 0

        self.tabs["Source File"] = open(msg.path, "r").readlines()
        self.box.set("Source File")
        self.refresh_results_window()
        self.results.yview(scroll)
        self.results.select_set(msg.line - 1)
Пример #4
0
def main():
    root = Tk()

    frame = add_scrollbars(root, Frame, wheel=True)

    if False:

        def event_break(e):
            print("breaking %r" % e)
            return "break"

        frame.bind_all("<Button-4>", event_break, "+")

    lb = Label(frame, text="Label")
    lb.pack(fill=BOTH, side=TOP)

    cb = Combobox(frame, values=("1", "2", "3"))
    cb.pack(fill=BOTH, side=TOP)

    text = Text(frame)
    text.pack(fill=BOTH, expand=True, side=TOP)
    text.insert(END, "A\nMultiline\nMessage")

    for i in range(3, 100):
        text.insert(END, "line %d\n" % i)

    text2 = Text(frame)
    text2.pack(fill=BOTH, expand=True, side=TOP)

    for i in range(1, 200):
        text2.insert(END, "line %d\n" % i)

    bt1 = Button(frame, text="Bt#1")
    bt1.pack(side=LEFT)

    bt2 = Button(frame, text="Bt#2")
    bt2.pack(side=RIGHT)

    root.rowconfigure(2, weight=0)
    Label(root, text="Outer label").grid(row=2,
                                         column=0,
                                         columnspan=2,
                                         sticky="EW")

    if False:

        def event(e):
            print("event %r" % e)

        frame.bind("<Button-4>", event, "+")

    scrollable = set(["TCombobox", "Scrollbar", "Text"])

    def event_all(e):
        w = e.widget

        m = e.widget
        while m is not None:
            if m is frame:
                break
            m = m.master
        else:
            print("Outer widget")
            return

        cls = w.winfo_class()
        print("cls = " + cls)
        if cls in scrollable:
            return
        # scroll here

    bind_all_mouse_wheel(frame, event_all, "+")

    root.mainloop()
Пример #5
0
class LintGui(object):
    """Build and control a window to interact with pylint"""
    def __init__(self, root=None):
        """init"""
        self.root = root or Tk()
        self.root.title('Pylint')
        #reporter
        self.reporter = None
        #message queue for output from reporter
        self.msg_queue = six.moves.queue.Queue()
        self.msgs = []
        self.visible_msgs = []
        self.filenames = []
        self.rating = StringVar()
        self.tabs = {}
        self.report_stream = BasicStream(self)
        #gui objects
        self.lb_messages = None
        self.showhistory = None
        self.results = None
        self.btnRun = None
        self.information_box = None
        self.convention_box = None
        self.refactor_box = None
        self.warning_box = None
        self.error_box = None
        self.fatal_box = None
        self.txtModule = None
        self.status = None
        self.msg_type_dict = None
        self.init_gui()

    def init_gui(self):
        """init helper"""

        window = PanedWindow(self.root, orient="vertical")
        window.pack(side=TOP, fill=BOTH, expand=True)

        top_pane = Frame(window)
        window.add(top_pane)
        mid_pane = Frame(window)
        window.add(mid_pane)
        bottom_pane = Frame(window)
        window.add(bottom_pane)

        #setting up frames
        top_frame = Frame(top_pane)
        mid_frame = Frame(top_pane)
        history_frame = Frame(top_pane)
        radio_frame = Frame(mid_pane)
        rating_frame = Frame(mid_pane)
        res_frame = Frame(mid_pane)
        check_frame = Frame(bottom_pane)
        msg_frame = Frame(bottom_pane)
        btn_frame = Frame(bottom_pane)
        top_frame.pack(side=TOP, fill=X)
        mid_frame.pack(side=TOP, fill=X)
        history_frame.pack(side=TOP, fill=BOTH, expand=True)
        radio_frame.pack(side=TOP, fill=X)
        rating_frame.pack(side=TOP, fill=X)
        res_frame.pack(side=TOP, fill=BOTH, expand=True)
        check_frame.pack(side=TOP, fill=X)
        msg_frame.pack(side=TOP, fill=BOTH, expand=True)
        btn_frame.pack(side=TOP, fill=X)

        # Binding F5 application-wide to run lint
        self.root.bind('<F5>', self.run_lint)

        #Message ListBox
        rightscrollbar = Scrollbar(msg_frame)
        rightscrollbar.pack(side=RIGHT, fill=Y)
        bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL)
        bottomscrollbar.pack(side=BOTTOM, fill=X)
        self.lb_messages = Listbox(msg_frame,
                                   yscrollcommand=rightscrollbar.set,
                                   xscrollcommand=bottomscrollbar.set,
                                   bg="white")
        self.lb_messages.bind("<Double-Button-1>", self.show_sourcefile)
        self.lb_messages.pack(expand=True, fill=BOTH)
        rightscrollbar.config(command=self.lb_messages.yview)
        bottomscrollbar.config(command=self.lb_messages.xview)

        #History ListBoxes
        rightscrollbar2 = Scrollbar(history_frame)
        rightscrollbar2.pack(side=RIGHT, fill=Y)
        bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL)
        bottomscrollbar2.pack(side=BOTTOM, fill=X)
        self.showhistory = Listbox(history_frame,
                                   yscrollcommand=rightscrollbar2.set,
                                   xscrollcommand=bottomscrollbar2.set,
                                   bg="white")
        self.showhistory.pack(expand=True, fill=BOTH)
        rightscrollbar2.config(command=self.showhistory.yview)
        bottomscrollbar2.config(command=self.showhistory.xview)
        self.showhistory.bind('<Double-Button-1>', self.select_recent_file)
        self.set_history_window()

        #status bar
        self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W)
        self.status.pack(side=BOTTOM, fill=X)

        #labelbl_ratingls
        lbl_rating_label = Label(rating_frame, text='Rating:')
        lbl_rating_label.pack(side=LEFT)
        lbl_rating = Label(rating_frame, textvariable=self.rating)
        lbl_rating.pack(side=LEFT)
        Label(mid_frame, text='Recently Used:').pack(side=LEFT)
        Label(top_frame, text='Module or package').pack(side=LEFT)

        #file textbox
        self.txt_module = Entry(top_frame, background='white')
        self.txt_module.bind('<Return>', self.run_lint)
        self.txt_module.pack(side=LEFT, expand=True, fill=X)

        #results box
        rightscrollbar = Scrollbar(res_frame)
        rightscrollbar.pack(side=RIGHT, fill=Y)
        bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL)
        bottomscrollbar.pack(side=BOTTOM, fill=X)
        self.results = Listbox(res_frame,
                               yscrollcommand=rightscrollbar.set,
                               xscrollcommand=bottomscrollbar.set,
                               bg="white",
                               font="Courier")
        self.results.pack(expand=True, fill=BOTH, side=BOTTOM)
        rightscrollbar.config(command=self.results.yview)
        bottomscrollbar.config(command=self.results.xview)

        #buttons
        Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT)
        Button(top_frame,
               text='Open Package',
               command=(lambda: self.file_open(package=True))).pack(side=LEFT)

        self.btnRun = Button(top_frame, text='Run', command=self.run_lint)
        self.btnRun.pack(side=LEFT)
        Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM)

        #radio buttons
        self.information_box = IntVar()
        self.convention_box = IntVar()
        self.refactor_box = IntVar()
        self.warning_box = IntVar()
        self.error_box = IntVar()
        self.fatal_box = IntVar()
        i = Checkbutton(check_frame,
                        text="Information",
                        fg=COLORS['(I)'],
                        variable=self.information_box,
                        command=self.refresh_msg_window)
        c = Checkbutton(check_frame,
                        text="Convention",
                        fg=COLORS['(C)'],
                        variable=self.convention_box,
                        command=self.refresh_msg_window)
        r = Checkbutton(check_frame,
                        text="Refactor",
                        fg=COLORS['(R)'],
                        variable=self.refactor_box,
                        command=self.refresh_msg_window)
        w = Checkbutton(check_frame,
                        text="Warning",
                        fg=COLORS['(W)'],
                        variable=self.warning_box,
                        command=self.refresh_msg_window)
        e = Checkbutton(check_frame,
                        text="Error",
                        fg=COLORS['(E)'],
                        variable=self.error_box,
                        command=self.refresh_msg_window)
        f = Checkbutton(check_frame,
                        text="Fatal",
                        fg=COLORS['(F)'],
                        variable=self.fatal_box,
                        command=self.refresh_msg_window)
        i.select()
        c.select()
        r.select()
        w.select()
        e.select()
        f.select()
        i.pack(side=LEFT)
        c.pack(side=LEFT)
        r.pack(side=LEFT)
        w.pack(side=LEFT)
        e.pack(side=LEFT)
        f.pack(side=LEFT)

        #check boxes
        self.box = StringVar()
        # XXX should be generated
        report = Radiobutton(radio_frame,
                             text="Report",
                             variable=self.box,
                             value="Report",
                             command=self.refresh_results_window)
        raw_met = Radiobutton(radio_frame,
                              text="Raw metrics",
                              variable=self.box,
                              value="Raw metrics",
                              command=self.refresh_results_window)
        dup = Radiobutton(radio_frame,
                          text="Duplication",
                          variable=self.box,
                          value="Duplication",
                          command=self.refresh_results_window)
        ext = Radiobutton(radio_frame,
                          text="External dependencies",
                          variable=self.box,
                          value="External dependencies",
                          command=self.refresh_results_window)
        stat = Radiobutton(radio_frame,
                           text="Statistics by type",
                           variable=self.box,
                           value="Statistics by type",
                           command=self.refresh_results_window)
        msg_cat = Radiobutton(radio_frame,
                              text="Messages by category",
                              variable=self.box,
                              value="Messages by category",
                              command=self.refresh_results_window)
        msg = Radiobutton(radio_frame,
                          text="Messages",
                          variable=self.box,
                          value="Messages",
                          command=self.refresh_results_window)
        source_file = Radiobutton(radio_frame,
                                  text="Source File",
                                  variable=self.box,
                                  value="Source File",
                                  command=self.refresh_results_window)
        report.select()
        report.grid(column=0, row=0, sticky=W)
        raw_met.grid(column=1, row=0, sticky=W)
        dup.grid(column=2, row=0, sticky=W)
        msg.grid(column=3, row=0, sticky=W)
        stat.grid(column=0, row=1, sticky=W)
        msg_cat.grid(column=1, row=1, sticky=W)
        ext.grid(column=2, row=1, sticky=W)
        source_file.grid(column=3, row=1, sticky=W)

        #dictionary for check boxes and associated error term
        self.msg_type_dict = {
            'I': lambda: self.information_box.get() == 1,
            'C': lambda: self.convention_box.get() == 1,
            'R': lambda: self.refactor_box.get() == 1,
            'E': lambda: self.error_box.get() == 1,
            'W': lambda: self.warning_box.get() == 1,
            'F': lambda: self.fatal_box.get() == 1
        }
        self.txt_module.focus_set()

    def select_recent_file(self, event):  # pylint: disable=unused-argument
        """adds the selected file in the history listbox to the Module box"""
        if not self.showhistory.size():
            return

        selected = self.showhistory.curselection()
        item = self.showhistory.get(selected)
        #update module
        self.txt_module.delete(0, END)
        self.txt_module.insert(0, item)

    def refresh_msg_window(self):
        """refresh the message window with current output"""
        #clear the window
        self.lb_messages.delete(0, END)
        self.visible_msgs = []
        for msg in self.msgs:
            if self.msg_type_dict.get(msg.C)():
                self.visible_msgs.append(msg)
                msg_str = convert_to_string(msg)
                self.lb_messages.insert(END, msg_str)
                fg_color = COLORS.get(msg_str[:3], 'black')
                self.lb_messages.itemconfigure(END, fg=fg_color)

    def refresh_results_window(self):
        """refresh the results window with current output"""
        #clear the window
        self.results.delete(0, END)
        try:
            for res in self.tabs[self.box.get()]:
                self.results.insert(END, res)
        except KeyError:
            pass

    def process_incoming(self):
        """process the incoming messages from running pylint"""
        while self.msg_queue.qsize():
            try:
                msg = self.msg_queue.get(0)
                if msg == "DONE":
                    self.report_stream.output_contents()
                    return False

                #adding message to list of msgs
                self.msgs.append(msg)

                #displaying msg if message type is selected in check box
                if self.msg_type_dict.get(msg.C)():
                    self.visible_msgs.append(msg)
                    msg_str = convert_to_string(msg)
                    self.lb_messages.insert(END, msg_str)
                    fg_color = COLORS.get(msg_str[:3], 'black')
                    self.lb_messages.itemconfigure(END, fg=fg_color)

            except six.moves.queue.Empty:
                pass
        return True

    def periodic_call(self):
        """determine when to unlock the run button"""
        if self.process_incoming():
            self.root.after(100, self.periodic_call)
        else:
            #enabling button so it can be run again
            self.btnRun.config(state=NORMAL)

    def mainloop(self):
        """launch the mainloop of the application"""
        self.root.mainloop()

    def quit(self, _=None):
        """quit the application"""
        self.root.quit()

    def halt(self):  # pylint: disable=no-self-use
        """program halt placeholder"""
        return

    def file_open(self, package=False, _=None):
        """launch a file browser"""
        if not package:
            filename = askopenfilename(parent=self.root,
                                       filetypes=[('pythonfiles', '*.py'),
                                                  ('allfiles', '*')],
                                       title='Select Module')
        else:
            filename = askdirectory(title="Select A Folder", mustexist=1)

        if filename == ():
            return

        self.txt_module.delete(0, END)
        self.txt_module.insert(0, filename)

    def update_filenames(self):
        """update the list of recent filenames"""
        filename = self.txt_module.get()
        if not filename:
            filename = os.getcwd()
        if filename + '\n' in self.filenames:
            index = self.filenames.index(filename + '\n')
            self.filenames.pop(index)

        #ensure only 10 most recent are stored
        if len(self.filenames) == 10:
            self.filenames.pop()
        self.filenames.insert(0, filename + '\n')

    def set_history_window(self):
        """update the history window with info from the history file"""
        #clear the window
        self.showhistory.delete(0, END)
        # keep the last 10 most recent files
        try:
            view_history = open(HOME + HISTORY, 'r')
            for hist in view_history.readlines():
                if hist not in self.filenames:
                    self.filenames.append(hist)
                self.showhistory.insert(END, hist.split('\n')[0])
            view_history.close()
        except IOError:
            # do nothing since history file will be created later
            return

    def run_lint(self, _=None):
        """launches pylint"""
        self.update_filenames()
        self.root.configure(cursor='watch')
        self.reporter = GUIReporter(self, output=self.report_stream)
        module = self.txt_module.get()
        if not module:
            module = os.getcwd()

        #cleaning up msgs and windows
        self.msgs = []
        self.visible_msgs = []
        self.lb_messages.delete(0, END)
        self.tabs = {}
        self.results.delete(0, END)
        self.btnRun.config(state=DISABLED)

        #setting up a worker thread to run pylint
        worker = Thread(target=lint_thread,
                        args=(
                            module,
                            self.reporter,
                            self,
                        ))
        self.periodic_call()
        worker.start()

        # Overwrite the .pylint-gui-history file with all the new recently added files
        # in order from filenames but only save last 10 files
        write_history = open(HOME + HISTORY, 'w')
        write_history.writelines(self.filenames)
        write_history.close()
        self.set_history_window()

        self.root.configure(cursor='')

    def show_sourcefile(self, event=None):  # pylint: disable=unused-argument
        selected = self.lb_messages.curselection()
        if not selected:
            return

        msg = self.visible_msgs[int(selected[0])]
        scroll = msg.line - 3
        if scroll < 0:
            scroll = 0

        self.tabs["Source File"] = open(msg.path, "r").readlines()
        self.box.set("Source File")
        self.refresh_results_window()
        self.results.yview(scroll)
        self.results.select_set(msg.line - 1)
 def _create_button(self, parent, label, callback):
     if label:
         button = Button(parent, text=label, width=10, command=callback)
         button.pack(side=LEFT, padx=5, pady=5)
Пример #7
0
class Game(Frame):
    '''Top-level Frame managing top-level events. Interact directly with user.'''

    ############ geometry ###############
    X_PADDING = 25
    Y_PADDING = 25
    SHIP_PANEL_WIDTH = 150
    BUTTON_PANEL_HEIGHT = 50
    BUTTON_PADDING = 5
    WARNING_BAR_HEIGHT = 40
    #####################################

    ########### states ##################
    PLACING = 0
    PLAYING = 1
    GAME_OVER = 2
    #####################################

    ############ colors #################
    BACKGROUND_COLOR = "white"
    WARNING_BACKGROUND = "khaki1"
    #####################################

    ###### window titles and messages ################
    GAME_OVER_POPUP_TITLE = "Game Over"
    GAME_OVER_WIN_MSG = "You win!"
    GAME_OVER_LOSE_MSG = "Game over. You lose."
    WINDOW_TITLE_GAME_OVER = "Battleship (Game Over)"
    WINDOW_TITLE_NORMAL = "Battleship"

    ##################################################

    def __init__(self, master):
        '''Create the UI for a game of battleship.'''

        Frame.__init__(self, master)

        self._create_ui()

        # these are 'controller' elements that should really be in another class
        self.ai = ShipAI(self.their_grid._model, self.my_grid._model)
        self.reset()

    def show_warning(self, msg, title=None):
        '''Show a warning msg that a certain action is illegal.
        Allows user to understand what's going on (i.e. why their action failed.
        '''

        tkMessageBox.showwarning(title, msg)

    def _create_ui(self):
        '''Create all UI elements for the game.'''

        self._create_menu()
        self._add_grids()
        self._add_staging_panel()
        self._addship_panels()
        self._make_buttons()

        self.config(height=self.Y_PADDING * 3 + self.my_grid.size +
                    self.BUTTON_PANEL_HEIGHT + self.WARNING_BAR_HEIGHT)
        self.set_all_bgs(self.BACKGROUND_COLOR, self)

    def _destroy_popup(self, event=None):
        '''Process removal of the popup.'''

        self.master.focus_set()
        self._popup.grab_release()
        self._popup.destroy()

    def _show_rules(self):
        '''Show the help dialog in a new window.'''

        # load the help page
        help_page_location = "help/rules.txt"
        f = open(help_page_location, "r")
        lines = f.read()
        f.close()
        tkMessageBox.showinfo("Rules", lines)

    def show_keyboard_shortcuts(self):
        '''Show a dialog box with the keyboard shortcuts.'''

        # load the keyboard shortcuts page
        ks_page_location = "help/keyboard_shortcuts.txt"
        f = open(ks_page_location, "r")
        lines = f.read()
        f.close()
        tkMessageBox.showinfo("Keyboard Shortcuts", lines)

    def _create_menu(self):
        '''Create the menu in the GUI.'''

        menubar = Menu(self)
        self.menus = {}

        count = 0
        self.file_menu = Menu(menubar, tearoff=0)
        self.file_menu.add_command(label="New Game")  #, command=self.reset)
        self.menus["file_new_game"] = count
        count += 1
        self.file_menu.add_command(label="Save")
        self.menus["file_save"] = count
        count += 1
        self.file_menu.add_command(label="Open")
        self.menus["file_open"] = count
        count += 1
        self.file_menu.add_command(
            label="Exit")  #, command=self.master.destroy)
        self.menus["file_exit"] = count
        count += 1
        menubar.add_cascade(label="File", menu=self.file_menu)

        if battleship.GameController.DEV_FLAG:
            count = 0
            self.dev_menu = Menu(menubar, tearoff=0)
            self.dev_menu.add_command(label="Auto Place")
            self.menus["dev_auto_place"] = count
            count += 1
            self.dev_menu.add_command(label="Random Shot")
            self.menus["dev_random_shot"] = count
            count += 1
            self.dev_menu.add_command(label="Auto Load")
            self.menus["dev_auto_load"] = count
            count += 1
            menubar.add_cascade(label="Dev", menu=self.dev_menu)

        help_menu = Menu(menubar, tearoff=0)
        help_menu.add_command(label="Rules", command=self._show_rules)
        help_menu.add_command(label="Keyboard Shortcuts",
                              command=self.show_keyboard_shortcuts)
        menubar.add_cascade(label="Help", menu=help_menu)

        self.master.config(menu=menubar)

    def _add_staging_panel(self):
        '''Create the placement/ship staging panel.'''

        self.my_grid_frame.staging_panel = ShipPlacementPanel(self)
        self.my_grid_frame.staging_panel.place(
            x=self.X_PADDING * 2 + self.SHIP_PANEL_WIDTH + self.my_grid.size,
            y=self.Y_PADDING)

    def set_all_bgs(self, color, parent):
        '''Set all the backgrounds of the child widgets to a certain color.'''

        parent.config(background=color)

        for child in parent.winfo_children():
            self.set_all_bgs(color, child)

    def _addship_panels(self):
        '''Add a list of ships to select from, for adding.
        Note that staging area must be added FIRST'''

        ############################## ShipPanel ########################
        self.my_grid_frame.ship_panel = ShipPanel(self)
        self.my_grid_frame.ship_panel.place(x=self.X_PADDING,
                                            y=self.Y_PADDING * 4)

        self.unselect_ship()
        ##################################################################

        ###################### ShipWarPanel ##############################
        self.my_grid_frame._ship_war_panel = ShipWarPanel(self)
        self.my_grid_frame._ship_war_panel.config(
            height=self.my_grid_frame.winfo_height())
        self.my_grid_frame._ship_war_panel.place(x=self.X_PADDING,
                                                 y=self.Y_PADDING * 2)
        ##################################################################

        ###################### EnemyShipPanel ############################
        self.their_grid_frame.ship_panel = EnemyShipPanel(self)
        self.their_grid_frame.ship_panel.place(x=self.my_grid.size * 2 +
                                               self.X_PADDING * 3 +
                                               self.SHIP_PANEL_WIDTH,
                                               y=self.Y_PADDING * 4)
        ##################################################################

    def unselect_ship(self):
        '''Deselect all ships in the placement and staging GUIs.'''

        self.my_grid_frame.ship_panel._ship_var.set(10)
        self.my_grid_frame.staging_panel.reset()

    def _hide_frame(self, frame):
        '''Since you can't hide a frame per se, 'unpack' the frame's child widgets.
        WARNING: this removes all packing directions for children'''

        frame.lower()

        for child in frame.winfo_children():
            child.pack_forget()

    def show_game_over_popup(self, winner):
        '''Show a popup with a dialog saying the game is over, and showing the winning player.'''

        msg = {
            battleship.GameController.HUMAN_PLAYER: self.GAME_OVER_WIN_MSG,
            battleship.GameController.AI_PLAYER: self.GAME_OVER_LOSE_MSG
        }[winner]

        tkMessageBox.showinfo(self.GAME_OVER_POPUP_TITLE, msg)

    def process_placing_state(self):
        '''Basic stuff to do during placing state.'''

        self.config(width=self.X_PADDING * 3 + self.my_grid.size +
                    self.SHIP_PANEL_WIDTH +
                    self.my_grid_frame.staging_panel.CANVAS_WIDTH)

        # show staging panel
        self.my_grid_frame.staging_panel.pack_ui()
        self.my_grid_frame.staging_panel.lift(aboveThis=self.their_grid_frame)
        self.my_grid_frame.staging_panel.reset()

        #re-pack
        self.play_game_button.pack(side=LEFT,
                                   padx=self.BUTTON_PADDING,
                                   pady=self.BUTTON_PADDING)
        self.play_game_button.config(state=DISABLED)
        self._hide_frame(self.their_grid_frame)

        self._hide_frame(self.my_grid_frame._ship_war_panel)
        self.my_grid_frame.ship_panel.lift(
            aboveThis=self.my_grid_frame._ship_war_panel)

        # allow the AI to place ships
        self.ai.place_ships()

    def process_playing_state(self):
        '''Basic stuff to do during playing state.'''

        self.config(width=self.X_PADDING * 4 + self.my_grid.size * 2 +
                    self.SHIP_PANEL_WIDTH * 2)
        self.my_grid._model.finalize()
        self.their_grid._model.finalize()
        self._hide_frame(self.my_grid_frame.staging_panel)

        self.their_grid.config(state=NORMAL)
        self.their_grid.enable()

        # hide while playing
        self.play_game_button.pack_forget()

        self.unselect_ship()

        self.my_grid_frame._ship_war_panel.pack_ui()
        self.my_grid_frame._ship_war_panel.lift(
            aboveThis=self.my_grid_frame.ship_panel)

        # show opponent's grid
        self.their_grid_frame.lift(aboveThis=self.my_grid_frame.staging_panel)
        self.their_grid_label.pack()
        self.their_grid.pack(side=LEFT, pady=20)

    def process_game_over_state(self):
        '''Change the view to reflect the game is over.'''

        self.their_grid.disable()
        self.master.title(self.WINDOW_TITLE_GAME_OVER)

    def process_state(self):
        '''Simple state controller to enable and disable certain widgets depending on the state.'''

        if self._state == self.PLACING:
            self.process_placing_state()
        elif self._state == self.PLAYING:
            self.process_playing_state()
        elif self._state == self.GAME_OVER:
            self.process_game_over_state()

    def _add_grid_events(self):
        '''Add events to the grids.'''

        #self.their_grid.tag_bind("tile", "<Button-1>", self._shot)
        pass

    def _add_grids(self):
        '''Create UI containers for the player grids.'''

        self.my_grid_frame = PlayerGridFrame(self)
        self.my_grid_frame.place(x=self.X_PADDING + self.SHIP_PANEL_WIDTH,
                                 y=self.Y_PADDING)
        l1 = Label(self.my_grid_frame, text="Your Grid")
        l1.pack()
        self.my_grid = ShipGrid(self.my_grid_frame, True)
        self.my_grid.pack(side=LEFT, pady=20)

        self.their_grid_frame = PlayerGridFrame(self)
        self.their_grid_frame.place(x=self.my_grid.size + self.X_PADDING * 2 +
                                    self.SHIP_PANEL_WIDTH,
                                    y=self.Y_PADDING)
        self.their_grid_label = Label(self.their_grid_frame,
                                      text="Opponent's Grid")
        self.their_grid_label.pack()
        self.their_grid = ShipGrid(self.their_grid_frame, False)
        self.their_grid.pack(side=LEFT, pady=20)

        self._add_grid_events()

    def reset(self):
        '''New game!'''

        self.master.title(self.WINDOW_TITLE_NORMAL)

        # reset selected ship
        self.unselect_ship()

        # reset staging area
        self.my_grid_frame.staging_panel.reset()

        # reset indicators on ships in panels
        self.my_grid_frame._ship_war_panel.reset()
        for ship, button in self.my_grid_frame.ship_panel.ship_buttons.items():
            button.config(foreground="black")

        for x, y in self.my_grid.get_tiles():
            self.reset_closure(x, y)

        self._state = self.PLACING
        self.process_state()

    def reset_closure(self, x, y):
        '''Add a placement event to the given tile.
        TODO this is badly named'''

        tag_id = self.my_grid._get_tile_name(x, y)
        c = self.get_add_ship_callback()
        f = lambda event: self.add_staged_ship(x, y, c)
        self.my_grid.tag_bind(tag_id, "<Button-1>", f)

    def add_staged_ship(self, x, y, callback):
        '''Take the stage from the staging area, and place it on the board at position (x, y).
        After ship has been placed, execute the function <callback>.'''

        s = self.my_grid_frame.staging_panel.get_staged_ship()

        if s is not None:
            self.my_grid.add_ship(x, y, s.get_short_name(), s.is_vertical(),
                                  callback)

    def get_add_ship_callback(self):
        '''Return the callback function for adding a ship.'''

        return lambda: self.ship_set(self.my_grid_frame.ship_panel.
                                     get_current_ship())

    def _set_ship_sunk(self, ship):
        '''This is a callback, to be called when a ship has been sunk.
        TODO for now only called when one of MY ships is sunk.
        UI shows that the given ship has been sunk.'''

        self.my_grid_frame.ship_panel.set_sunk(ship)

    def _set_ship_hit(self, ship):
        '''This is a callback, to be called when a ship has been hit.
        TODO for now only called when one of MY ships is hit.
        UI shows that the given ship has been hit.'''

        assert ship is not None
        self.my_grid_frame._ship_war_panel.update(ship)

    def ship_set(self, ship):
        '''This is a callback, to be called when a ship has been placed.
        UI shows that the given ship has been placed.'''

        self._set_ships[ship] = True
        self.my_grid_frame.ship_panel.set_placed(ship)

        if all(self._set_ships.values()):
            self.play_game_button.config(state=NORMAL)

    def _make_buttons(self):
        '''Create action buttons at the bottom.'''

        button_row = self.my_grid.size + self.Y_PADDING + (
            self.BUTTON_PANEL_HEIGHT - 2 * self.BUTTON_PADDING)
        button_frame = Frame(self)
        button_frame.place(x=self.my_grid.size - self.X_PADDING, y=button_row)

        self.play_game_button = Button(button_frame, text="Play")
        self.play_game_button.pack(side=LEFT,
                                   padx=self.BUTTON_PADDING,
                                   pady=self.BUTTON_PADDING)

    def redraw(self):
        '''Redraw the GUI, reloading all info from the model.
        TODO this is a work-in-progress'''

        # first, figure out the state
        # are the ships placed? are they sunk?
        grids = [self.my_grid._model, self.their_grid._model]

        # set panels to the correct state
        self.my_grid_frame.ship_panel.redraw(grids[0])
        self.my_grid_frame._ship_war_panel.redraw(grids[0])
        self.their_grid_frame.ship_panel.redraw(grids[1])

        # set the grid to the correct state
        self.my_grid.redraw(grids[0])
        self.their_grid.redraw(grids[1])

        if all([g.has_all_ships()
                for g in grids]) and all([g.all_sunk() for g in grids]):
            #TODO
            # state is game over.
            pass
        elif all([g.has_all_ships() for g in grids]):
            self.process_playing_state()
        else:
            #TODO
            # state is placing
            pass
Пример #8
0
class Task(Frame):
    def __init__(self,
                 master=None,
                 work=WORK_TM,
                 rest=REST_TM,
                 server=HOST,
                 port=PORT,
                 mode='fascist'):
        """create the task widget and get things started"""

        self.lid_state = "open"
        self.lid_time = time.time()
        self.interrupt_count = 0
        self.then = 0.0
        self.old_work = 0.0
        self.state = "working"
        self.cancel_rest = 0

        self.log = logging.getLogger(__name__)

        Frame.__init__(*(self, master))

        self.mode = StringVar()
        self.mode.set(mode)  # "fascist" or "friendly"

        # create main interactor window
        self.workmeter = Meter(self, background="grey50")
        self.workmeter.pack()

        self.work_frame = Frame(self)
        self.work_frame.pack()
        self.work_label = Label(self.work_frame, text="Work time:")
        self.work_label.pack(side=LEFT)
        self.work_scl = Scale(self.work_frame,
                              orient=HORIZONTAL,
                              from_=1,
                              to=max(45, work),
                              command=self.reset_duration)
        self.work_scl.set(work)
        self.work_scl.pack(side=LEFT)

        self.rest_frame = Frame(self)
        self.rest_frame.pack()
        self.rest_label = Label(self.rest_frame, text="Rest time:")
        self.rest_label.pack(side=LEFT)
        self.rest_scl = Scale(self.rest_frame,
                              orient=HORIZONTAL,
                              from_=1,
                              to=max(15, rest),
                              command=self.reset_duration)
        self.rest_scl.set(rest)
        self.rest_scl.pack(side=LEFT)

        self.radio_frame = Frame(self)
        self.radio_frame.pack()
        self.dictator = Radiobutton(self.radio_frame,
                                    text="Fascist",
                                    variable=self.mode,
                                    value="fascist")
        self.friend = Radiobutton(self.radio_frame,
                                  text="Friendly",
                                  variable=self.mode,
                                  value="friendly")
        self.dictator.pack(side=LEFT)
        self.friend.pack(side=LEFT)

        self.button_frame = Frame(self)
        self.button_frame.pack()
        self.restb = Button(self.button_frame, text="Rest", command=self.rest)
        self.restb.pack(side=LEFT)
        self.stopb = Button(self.button_frame, text="Quit", command=self.quit)
        self.stopb.pack(side=LEFT)
        self.helpb = Button(self.button_frame, text="Help", command=self.help)
        self.helpb.pack(side=LEFT)

        # create the cover window
        self.cover = Toplevel(background="black")
        self.cover.withdraw()
        # user can't resize it
        self.cover.resizable(0, 0)
        # when displayed, it should cover all displays
        (w, h) = (self.winfo_vrootwidth(), self.winfo_vrootheight())
        if self.log.getEffectiveLevel() <= logging.DEBUG:
            # but we reduce it while debugging
            w, h = (w / 5, h / 5)
        self.cover.geometry("%dx%d+0+0" % (w, h))

        # and it's undecorated
        self.cover.overrideredirect(1)

        # cover contains a frame with rest message and meter
        f = Frame(self.cover, background="black")
        self.restnote = Label(f, background="black", foreground="white")
        self.restmeter = Meter(f, background="grey50", height=10, width=200)
        self.restnote.pack(pady=2)
        self.restmeter.pack(fill="x", expand=1, pady=2)
        self.cancel_button = Button(f, text="Cancel Rest", command=self.cancel)
        f.pack()

        # initialize interrupt information
        self.linux_interrupts = 0
        # used by the default activity checker
        self.mouse = None

        self.setup_server(server, port)

        # self.last_int is the last time the server was alerted to activity
        # self.now is the server's notion of the current time
        # idle time is therefore max(0, self.now-self.last_int)
        (self.last_int, _w_time, _r_time, self.now) = self.server.get()
        self.idle = max(0, self.now - self.last_int)

        self.bgcolor = self["background"]

        # start the ball rolling
        self.after(CHK_INT, self.tick)
        self.work()

    def setup_server(self, server, port):
        if server is None:
            self.server = collector.Collector()
            self.log.debug("using private Collector()")
            return

        self.server = xmlrpc_client.ServerProxy("http://%s:%d" %
                                                (server, port))
        try:
            self.server.get()
            self.log.debug("found existing server")
        except socket.error:
            if server in ["", "localhost", "127.0.0.1"]:
                cmd = "watch-server.py"
                args = ["-p", "%d" % port]
                pid = os.spawnvp(os.P_NOWAIT, cmd, args)
                # wait briefly for server to start
                time.sleep(0.2)
                self.server = xmlrpc_client.ServerProxy("http://%s:%d" %
                                                        (server, port),
                                                        allow_none=True)
                # try connecting
                for _i in range(10):
                    try:
                        self.server.get()
                        atexit.register(os.kill, pid, signal.SIGHUP)
                        self.log.debug("spawned server")
                        return
                    except socket.error:
                        time.sleep(0.1)
            # nothing else worked, so fall back to an embedded collector
            self.server = collector.Collector()
            self.log.debug("using private Collector()")

    def reset_duration(self, _dummy=None):
        """reset work/rest interval lengths to current scale values"""
        wtime = self.work_scl.get()
        self.workmeter.set_range(self.workmeter.min_val,
                                 self.workmeter.min_val + wtime * 60)
        self.restmeter.set_range(
            self.restmeter.min_val,
            self.restmeter.min_val + self.rest_scl.get() * 60)
        # only time the user can fiddle the work/rest meters is during
        # the work phase, so the only scale change that matters for extending
        # or contracting the end of the interval is the work scale
        try:
            delta = wtime - self.old_work
        except AttributeError:
            delta = 0
        self.log.debug(__("then: {} delta: {}m", hhmm(self.then), delta))
        self.then = self.then + delta * 60
        self.old_work = wtime

        self.server.put(self.work_scl.get(), self.rest_scl.get())

    def work(self):
        """start the work period"""
        self.reset_warning()
        self.restmeter.reset()
        self.state = "working"
        self.then = self.now + self.work_scl.get() * 60
        self.log.debug(__("work: then: {}", hhmm(self.then)))
        self.workmeter.set_range(self.now, self.then)
        self.workmeter.reset()
        self.cover.withdraw()

    def warn_work_end(self):
        """alert user that work period is almost up"""
        self.set_background("yellow")

    def reset_warning(self):
        """back to plain old grey bg"""
        self.set_background(self.bgcolor)

    def set_background(self, color):
        for w in (self, self.work_scl, self.rest_scl, self.dictator,
                  self.friend, self.button_frame, self.stopb, self.restb,
                  self.helpb, self.work_frame, self.rest_frame,
                  self.radio_frame, self.work_label, self.rest_label):
            w["background"] = color

    def rest(self):
        """overlay the screen with a window, preventing typing"""
        self.cancel_rest = 0
        self.workmeter.reset()
        self.state = "resting"
        # the user may not have been typing or mousing right up to the
        # work/rest threshold - give credit for whatever rest time has
        # already been accumulated
        resttime = self.rest_scl.get() * 60 - (self.now - self.last_int)
        if resttime < 0:
            self.work()
            return
        mins, secs = divmod(resttime, 60)
        self.then = self.now + resttime
        self.cover.deiconify()
        self.cover.tkraise()
        resttext = ("Rest for %dm%02ds please..." % (mins, secs))
        self.restnote.configure(text=resttext)
        self.restmeter.set_range(self.now, self.then)
        self.restmeter.reset()
        if self.mode.get() == "friendly":
            self.cancel_button.pack(pady=2)
        else:
            self.cancel_button.pack_forget()

        self.log.debug(
            __("rest: state: {} now: {} then: {} active? {}", self.state,
               hhmm(self.now), hhmm(self.then), self.check_activity()))

    def help(self):
        Dialog(self.master, title="Help", content=usageText())

    ### ---- define activity checkers here ---- ###
    # keyed by sys.platform or "default" to return a method that checks
    # for mouse/keyboard activity
    _dispatch = {}

    def check_activity(self):
        """check mouse/keyboard activity info

        where possible, call platform-dependent routine to get mouse and
        keyboard info, otherwise, just return mouse info

        in all cases, the value returned should evaluate to False if no
        activity was detected.
        """
        active = False
        if self.lid_state == "open":
            dflt = self._dispatch["default"]
            checker = self._dispatch.get(sys.platform, dflt)
            active = checker(self)
            if active:
                self.server.tick()
        return active

    def check_mouse(self):
        """default checker, just compares current w/ saved mouse pos"""
        mouse = self.winfo_pointerxy()
        try:
            return mouse != self.mouse
        finally:
            self.mouse = mouse

    _dispatch["default"] = check_mouse

    def get_linux_interrupts(self):
        count = 0
        # Can't seem to find mouse interrupts, so for now, just watch
        # keyboard and mix add get_mouseinfo() output as a substitute for
        # mouse interrupts.
        last_count = self.interrupt_count
        for line in open("/proc/interrupts"):
            fields = line.split()
            if fields[0] == "1:":
                count = sum(int(fields[n]) for n in range(1, 8))
                self.interrupt_count = count
                break
        return self.check_mouse() or self.interrupt_count > last_count

    _dispatch["linux"] = get_linux_interrupts

    def check_lid_state(self):
        if os.path.exists(LID_STATE):
            for line in open(LID_STATE):
                fields = line.strip().split()
                if fields[0] == "state:":
                    return self.maybe_change_lid_state(fields[1])
        else:
            self.lid_state = "open"
        return 0

    def maybe_change_lid_state(self, state):
        """Take necessary action when lid state changes.

        Return True if lid state changed.
        """
        idle = 0
        if state != self.lid_state:
            self.log.info(__("lid state changed: {}", state))
            lid_time = time.time()
            if state == "open":
                idle = lid_time - self.lid_time
                self.log.info(__("idle for {}s", idle))
            self.lid_state = state
            self.lid_time = lid_time
        return idle

    def tick(self):
        """perform periodic checks for activity or state switch"""
        try:
            self._tick()
        finally:
            self.after(CHK_INT, self.tick)

    def _tick(self):
        (self.last_int, work_time, rest_time, self.now) = self.server.get()
        # check for mouse or keyboard activity
        idle_time = self.check_lid_state()
        if idle_time > rest_time * 60:
            self.log.info(
                __("The lid is up! Back to work: {}", hhmm(idle_time)))
            self.work()
            return

        active = self.check_activity()
        idle = max(0, self.now - self.last_int)

        # check to see if the work/rest scales got adjusted by another
        # watch instance
        if (self.work_scl.get() != work_time
                or self.rest_scl.get() != rest_time):
            self.work_scl.set(work_time)
            self.rest_scl.set(rest_time)

        self.log.debug(
            __("work: state: {} now: {} then: {} active? {}", self.state,
               hhmm(self.now), hhmm(self.then), active))
        if self.state == "resting":
            # user explicitly cancelled the rest or the idle period
            # exceeded the desired rest time
            if self.cancel_rest or idle > rest_time * 60:
                self.work()
                return

            # make sure the rest window is as far up the window stack as
            # possible
            self.cover.tkraise()

            if idle <= self.idle:
                # user moved something - extend rest by a second
                self.then += 1
                self.restmeter.set_range(self.restmeter.min_val, self.then)
                self.restmeter.set(self.now)
                self.idle = idle

            # update message to reflect rest time remaining
            timeleft = int(round(self.then - self.now))
            minleft, secleft = divmod(timeleft, 60)
            resttext = ("Rest for %dm%02ds please..." % (minleft, secleft))
            self.restnote.configure(text=resttext)

        else:
            # if it's been at least the length of the rest interval
            # since last interrupt, reset the work interval
            if idle >= rest_time * 60:
                self.work()
                return

            self.idle = idle

            if self.now > self.then:
                # work period expired
                self.rest()
                return

            if self.now + 60 > self.then:
                # work period nearly expired - warn user
                self.warn_work_end()
            else:
                self.reset_warning()

        self.restmeter.set(self.now)
        self.workmeter.set(self.now)

    def cancel(self):
        self.cancel_rest = 1
class ShipPlacementPanel(Frame):
    '''A frame which contains visualizations for placing ships.'''

    # the size of a single tile
    SHIP_TILE_SIZE = 20
    SHIP_TILE_COLOR = "steel blue"
    TAG = "staging_ship"
    CANVAS_WIDTH = 150

    def __init__(self, master):
        '''Create a new panel with the given parent.'''

        Frame.__init__(self, master)
        self._ship_name = StringVar()
        self._create_ui()
        self.reset()

    def reset(self):
        '''Alias for unstage_all'''

        self.unstage_all()

    def _create_ui(self):
        '''Create all UI objects.'''

        #self._tl = Label(self, text="Staged Ship", f)
        self._sl = Label(self, textvariable=self._ship_name)
        self._c = Canvas(self)
        self._c.config(width=self.CANVAS_WIDTH)
        self._rb = Button(self,
                          text="Rotate",
                          command=self.rotate_current_ship)

        self.pack_ui()

    def pack_ui(self):
        '''(re) pack the UI.
        Created mostly to counter hiding by unpacking.'''

        #self._tl.pack()
        #self._tl.grid(row=0)
        self._sl.pack()
        self._sl.grid(row=1, pady=10)
        self._c.pack()
        self._c.grid(row=2, pady=15)
        self._rb.pack()
        self._rb.grid(row=3)

    def unstage_all(self):
        '''Remove all ships from staging area.
        Clear all staging preferences.'''

        self._staged_ship = None
        self._clear_staging_grid()
        self._ship_name.set("")
        self._disable_rotate_button()

    def _clear_staging_grid(self):
        '''Remove previously staged ships from staging grid.'''

        for item in self._c.find_withtag(self.TAG):
            self._c.delete(item)

    def _draw_staged_ship(self):
        '''Draw the currently staged ship.'''

        # remove prior drawings
        self._clear_staging_grid()

        if self._staged_ship.is_vertical():
            x = 0
            x_pad = (self._c.winfo_width() - self.SHIP_TILE_SIZE) / 2.0
            y_pad = (self._c.winfo_height() -
                     self.SHIP_TILE_SIZE * self._staged_ship.get_size()) / 2.0

            for y in range(self._staged_ship.get_size()):
                self._draw_ship_tile(x_pad + x * self.SHIP_TILE_SIZE,
                                     y_pad + y * self.SHIP_TILE_SIZE)
        else:
            y = 0
            x_pad = (self._c.winfo_width() -
                     self.SHIP_TILE_SIZE * self._staged_ship.get_size()) / 2.0
            y_pad = (self._c.winfo_height() - self.SHIP_TILE_SIZE) / 2.0

            for x in range(self._staged_ship.get_size()):
                self._draw_ship_tile(x_pad + x * self.SHIP_TILE_SIZE,
                                     y_pad + y * self.SHIP_TILE_SIZE)

    def add_ship(self, s):
        '''Alias for stage ship.'''

        self.stage_ship(s)

    def _disable_rotate_button(self):
        '''Disable / hide the rotate button.'''

        self._rb.grid_forget()

    def _enable_rotate_button(self):
        '''Enable / show the rotate button.'''

        self._rb.grid(row=3)

    def stage_ship(self, s):
        '''Add a ship to the staging area. 
        Display what it would look like on the grid.
        Create support for accidentally adding ship that isn't ready'''

        if s is not None:
            self._staged_ship = s
            self._ship_name.set(s.get_full_name().title())
            self._draw_staged_ship()

            self._enable_rotate_button()
        else:
            self._disable_rotate_button()

    def _draw_ship_tile(self, x, y):
        '''Draw a single tile for the ship at given coordinates.'''

        self._c.create_rectangle(x,
                                 y,
                                 x + self.SHIP_TILE_SIZE,
                                 y + self.SHIP_TILE_SIZE,
                                 fill=self.SHIP_TILE_COLOR,
                                 outline="black",
                                 tag=self.TAG)

    def get_staged_ship(self):
        '''Return the currently staged ship.'''

        return self._staged_ship

    def rotate_current_ship(self):
        '''Rotate the currently staged ship.'''

        if self._staged_ship is not None:
            self._staged_ship.rotate()
            self._draw_staged_ship()
Пример #10
0
 def _create_button(self, parent, label, callback):
     if label:
         button = Button(parent, text=label, width=10, command=callback)
         button.pack(side=LEFT, padx=5, pady=5)
class ConcordanceSearchView(object):
    _BACKGROUND_COLOUR='#FFF' #white

    #Colour of highlighted results
    _HIGHLIGHT_WORD_COLOUR='#F00' #red
    _HIGHLIGHT_WORD_TAG='HL_WRD_TAG'

    _HIGHLIGHT_LABEL_COLOUR='#C0C0C0' # dark grey
    _HIGHLIGHT_LABEL_TAG='HL_LBL_TAG'


    #Percentage of text left of the scrollbar position
    _FRACTION_LEFT_TEXT=0.30

    def __init__(self):
        self.queue = q.Queue()
        self.model = ConcordanceSearchModel(self.queue)
        self.top = Tk()
        self._init_top(self.top)
        self._init_menubar()
        self._init_widgets(self.top)
        self.load_corpus(self.model.DEFAULT_CORPUS)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def _init_top(self, top):
        top.geometry('950x680+50+50')
        top.title('NLTK Concordance Search')
        top.bind('<Control-q>', self.destroy)
        top.protocol('WM_DELETE_WINDOW', self.destroy)
        top.minsize(950,680)

    def _init_widgets(self, parent):
        self.main_frame = Frame(parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1))
        self._init_corpus_select(self.main_frame)
        self._init_query_box(self.main_frame)
        self._init_results_box(self.main_frame)
        self._init_paging(self.main_frame)
        self._init_status(self.main_frame)
        self.main_frame.pack(fill='both', expand=True)

    def _init_menubar(self):
        self._result_size = IntVar(self.top)
        self._cntx_bf_len = IntVar(self.top)
        self._cntx_af_len = IntVar(self.top)
        menubar = Menu(self.top)

        filemenu = Menu(menubar, tearoff=0, borderwidth=0)
        filemenu.add_command(label='Exit', underline=1,
                             command=self.destroy, accelerator='Ctrl-q')
        menubar.add_cascade(label='File', underline=0, menu=filemenu)

        editmenu = Menu(menubar, tearoff=0)
        rescntmenu = Menu(editmenu, tearoff=0)
        rescntmenu.add_radiobutton(label='20', variable=self._result_size,
                                   underline=0, value=20,
                                   command=self.set_result_size)
        rescntmenu.add_radiobutton(label='50', variable=self._result_size,
                                   underline=0, value=50,
                                   command=self.set_result_size)
        rescntmenu.add_radiobutton(label='100', variable=self._result_size,
                                   underline=0, value=100,
                                   command=self.set_result_size)
        rescntmenu.invoke(1)
        editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu)

        cntxmenu = Menu(editmenu, tearoff=0)
        cntxbfmenu = Menu(cntxmenu, tearoff=0)
        cntxbfmenu.add_radiobutton(label='60 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0, value=60,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.add_radiobutton(label='80 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0, value=80,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.add_radiobutton(label='100 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0, value=100,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.invoke(1)
        cntxmenu.add_cascade(label='Before', underline=0, menu=cntxbfmenu)

        cntxafmenu = Menu(cntxmenu, tearoff=0)
        cntxafmenu.add_radiobutton(label='70 characters',
                                   variable=self._cntx_af_len,
                                   underline=0, value=70,
                                   command=self.set_cntx_af_len)
        cntxafmenu.add_radiobutton(label='90 characters',
                                   variable=self._cntx_af_len,
                                   underline=0, value=90,
                                   command=self.set_cntx_af_len)
        cntxafmenu.add_radiobutton(label='110 characters',
                                   variable=self._cntx_af_len,
                                   underline=0, value=110,
                                   command=self.set_cntx_af_len)
        cntxafmenu.invoke(1)
        cntxmenu.add_cascade(label='After', underline=0, menu=cntxafmenu)

        editmenu.add_cascade(label='Context', underline=0, menu=cntxmenu)

        menubar.add_cascade(label='Edit', underline=0, menu=editmenu)

        self.top.config(menu=menubar)

    def set_result_size(self, **kwargs):
        self.model.result_count = self._result_size.get()

    def set_cntx_af_len(self, **kwargs):
        self._char_after = self._cntx_af_len.get()

    def set_cntx_bf_len(self, **kwargs):
        self._char_before = self._cntx_bf_len.get()

    def _init_corpus_select(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.var = StringVar(innerframe)
        self.var.set(self.model.DEFAULT_CORPUS)
        Label(innerframe, justify=LEFT, text=' Corpus: ',
              background=self._BACKGROUND_COLOUR, padx = 2, pady = 1, border = 0).pack(side='left')

        other_corpora = list(self.model.CORPORA.keys()).remove(self.model.DEFAULT_CORPUS)
        om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora())
        om['borderwidth'] = 0
        om['highlightthickness'] = 1
        om.pack(side='left')
        innerframe.pack(side='top', fill='x', anchor='n')

    def _init_status(self, parent):
        self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx = 1, pady = 0)
        self.status.pack(side='top', anchor='sw')

    def _init_query_box(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        another = Frame(innerframe, background=self._BACKGROUND_COLOUR)
        self.query_box = Entry(another, width=60)
        self.query_box.pack(side='left', fill='x', pady=25, anchor='center')
        self.search_button = Button(another, text='Search', command=self.search, borderwidth=1, highlightthickness=1)
        self.search_button.pack(side='left', fill='x', pady=25, anchor='center')
        self.query_box.bind('<KeyPress-Return>', self.search_enter_keypress_handler)
        another.pack()
        innerframe.pack(side='top', fill='x', anchor='n')

    def search_enter_keypress_handler(self, *event):
        self.search()

    def _init_results_box(self, parent):
        innerframe = Frame(parent)
        i1 = Frame(innerframe)
        i2 = Frame(innerframe)
        vscrollbar = Scrollbar(i1, borderwidth=1)
        hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz')
        self.results_box = Text(i1,
                                font=Font(family='courier', size='16'),
                                state='disabled', borderwidth=1,
                                                            yscrollcommand=vscrollbar.set,
                                xscrollcommand=hscrollbar.set, wrap='none', width='40', height = '20', exportselection=1)
        self.results_box.pack(side='left', fill='both', expand=True)
        self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG, foreground=self._HIGHLIGHT_WORD_COLOUR)
        self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG, foreground=self._HIGHLIGHT_LABEL_COLOUR)
        vscrollbar.pack(side='left', fill='y', anchor='e')
        vscrollbar.config(command=self.results_box.yview)
        hscrollbar.pack(side='left', fill='x', expand=True, anchor='w')
        hscrollbar.config(command=self.results_box.xview)
        #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!!
        Label(i2, text='   ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e')
        i1.pack(side='top', fill='both', expand=True, anchor='n')
        i2.pack(side='bottom', fill='x', anchor='s')
        innerframe.pack(side='top', fill='both', expand=True)

    def _init_paging(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled')
        prev.pack(side='left', anchor='center')
        self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled')
        next.pack(side='right', anchor='center')
        innerframe.pack(side='top', fill='y')
        self.current_page = 0

    def previous(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.prev(self.current_page - 1)

    def __next__(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.next(self.current_page + 1)

    def about(self, *e):
        ABOUT = ("NLTK Concordance Search Demo\n")
        TITLE = 'About: NLTK Concordance Search Demo'
        try:
            from six.moves.tkinter_messagebox import Message
            Message(message=ABOUT, title=TITLE, parent=self.main_frame).show()
        except:
            ShowText(self.top, TITLE, ABOUT)

    def _bind_event_handlers(self):
        self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded)
        self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated)
        self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error)
        self.top.bind(ERROR_LOADING_CORPUS_EVENT, self.handle_error_loading_corpus)

    def _poll(self):
        try:
            event = self.queue.get(block=False)
        except q.Empty:
            pass
        else:
            if event == CORPUS_LOADED_EVENT:
                self.handle_corpus_loaded(event)
            elif event == SEARCH_TERMINATED_EVENT:
                self.handle_search_terminated(event)
            elif event == SEARCH_ERROR_EVENT:
                self.handle_search_error(event)
            elif event == ERROR_LOADING_CORPUS_EVENT:
                self.handle_error_loading_corpus(event)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def handle_error_loading_corpus(self, event):
        self.status['text'] = 'Error in loading ' + self.var.get()
        self.unfreeze_editable()
        self.clear_all()
        self.freeze_editable()

    def handle_corpus_loaded(self, event):
        self.status['text'] = self.var.get() + ' is loaded'
        self.unfreeze_editable()
        self.clear_all()
        self.query_box.focus_set()

    def handle_search_terminated(self, event):
        #todo: refactor the model such that it is less state sensitive
        results = self.model.get_results()
        self.write_results(results)
        self.status['text'] = ''
        if len(results) == 0:
            self.status['text'] = 'No results found for ' + self.model.query
        else:
                self.current_page = self.model.last_requested_page
        self.unfreeze_editable()
        self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT)

    def handle_search_error(self, event):
        self.status['text'] = 'Error in query ' + self.model.query
        self.unfreeze_editable()

    def corpus_selected(self, *args):
        new_selection = self.var.get()
        self.load_corpus(new_selection)

    def load_corpus(self, selection):
        if self.model.selected_corpus != selection:
            self.status['text'] = 'Loading ' + selection + '...'
            self.freeze_editable()
            self.model.load_corpus(selection)

    def search(self):
        self.current_page = 0
        self.clear_results_box()
        self.model.reset_results()
        query = self.query_box.get()
        if (len(query.strip()) == 0): return
        self.status['text']  = 'Searching for ' + query
        self.freeze_editable()
        self.model.search(query, self.current_page + 1, )


    def write_results(self, results):
        self.results_box['state'] = 'normal'
        row = 1
        for each in results:
            sent, pos1, pos2 = each[0].strip(), each[1], each[2]
            if len(sent) != 0:
                if (pos1 < self._char_before):
                    sent, pos1, pos2 = self.pad(sent, pos1, pos2)
                sentence = sent[pos1-self._char_before:pos1+self._char_after]
                if not row == len(results):
                    sentence += '\n'
                self.results_box.insert(str(row) + '.0', sentence)
                word_markers, label_markers = self.words_and_labels(sent, pos1, pos2)
                for marker in word_markers: self.results_box.tag_add(self._HIGHLIGHT_WORD_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1]))
                for marker in label_markers: self.results_box.tag_add(self._HIGHLIGHT_LABEL_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1]))
                row += 1
        self.results_box['state'] = 'disabled'

    def words_and_labels(self, sentence, pos1, pos2):
        search_exp = sentence[pos1:pos2]
        words, labels = [], []
        labeled_words = search_exp.split(' ')
        index = 0
        for each in labeled_words:
            if each == '':
                index += 1
            else:
                word, label = each.split('/')
                words.append((self._char_before + index, self._char_before + index + len(word)))
                index += len(word) + 1
                labels.append((self._char_before + index, self._char_before + index + len(label)))
                index += len(label)
            index += 1
        return words, labels

    def pad(self, sent, hstart, hend):
        if hstart >= self._char_before:
            return sent, hstart, hend
        d = self._char_before - hstart
        sent = ''.join([' '] * d) + sent
        return sent, hstart + d, hend + d

    def destroy(self, *e):
        if self.top is None: return
        self.top.after_cancel(self.after)
        self.top.destroy()
        self.top = None

    def clear_all(self):
        self.query_box.delete(0, END)
        self.model.reset_query()
        self.clear_results_box()

    def clear_results_box(self):
        self.results_box['state'] = 'normal'
        self.results_box.delete("1.0", END)
        self.results_box['state'] = 'disabled'

    def freeze_editable(self):
        self.query_box['state'] = 'disabled'
        self.search_button['state'] = 'disabled'
        self.prev['state'] = 'disabled'
        self.next['state'] = 'disabled'

    def unfreeze_editable(self):
        self.query_box['state'] = 'normal'
        self.search_button['state'] = 'normal'
        self.set_paging_button_states()

    def set_paging_button_states(self):
        if self.current_page == 0 or self.current_page == 1:
            self.prev['state'] = 'disabled'
        else:
            self.prev['state'] = 'normal'
        if self.model.has_more_pages(self.current_page):
            self.next['state'] = 'normal'
        else:
            self.next['state'] = 'disabled'

    def fire_event(self, event):
        #Firing an event so that rendering of widgets happen in the mainloop thread
        self.top.event_generate(event, when='tail')

    def mainloop(self, *args, **kwargs):
        if in_idle(): return
        self.top.mainloop(*args, **kwargs)
Пример #12
0
class Game(Frame):
    '''Top-level Frame managing top-level events. Interact directly with user.'''

    ############ geometry ###############
    X_PADDING = 25
    Y_PADDING = 25
    SHIP_PANEL_WIDTH = 150
    BUTTON_PANEL_HEIGHT = 50
    BUTTON_PADDING = 5
    WARNING_BAR_HEIGHT = 40
    #####################################

    ########### states ##################
    PLACING = 0
    PLAYING = 1
    GAME_OVER = 2
    #####################################    

    ############ colors #################
    BACKGROUND_COLOR = "white"
    WARNING_BACKGROUND = "khaki1"
    #####################################

    ###### window titles and messages ################
    GAME_OVER_POPUP_TITLE = "Game Over"
    GAME_OVER_WIN_MSG = "You win!"
    GAME_OVER_LOSE_MSG = "Game over. You lose."
    WINDOW_TITLE_GAME_OVER = "Battleship (Game Over)"
    WINDOW_TITLE_NORMAL = "Battleship"
    ##################################################

    def __init__(self, master):
        '''Create the UI for a game of battleship.'''
    
        Frame.__init__(self, master)
        
        self._create_ui()
        
        # these are 'controller' elements that should really be in another class
        self.ai = ShipAI(self.their_grid._model, self.my_grid._model)
        self.reset()
        
    def show_warning(self, msg, title=None):
        '''Show a warning msg that a certain action is illegal.
        Allows user to understand what's going on (i.e. why their action failed.
        '''
        
        tkMessageBox.showwarning(title, msg)
        
    def _create_ui(self):
        '''Create all UI elements for the game.'''
        
        self._create_menu()
        self._add_grids()
        self._add_staging_panel()
        self._addship_panels()
        self._make_buttons()
        
        self.config(height=self.Y_PADDING * 3 + self.my_grid.size + self.BUTTON_PANEL_HEIGHT + self.WARNING_BAR_HEIGHT)
        self.set_all_bgs(self.BACKGROUND_COLOR, self)
        
    def _destroy_popup(self, event=None):
        '''Process removal of the popup.'''
    
        self.master.focus_set()
        self._popup.grab_release()
        self._popup.destroy()
        
    def _show_rules(self):
        '''Show the help dialog in a new window.'''
        
        # load the help page
        help_page_location = "help/rules.txt"
        f = open(help_page_location, "r")
        lines = f.read()
        f.close()
        tkMessageBox.showinfo("Rules", lines)
        
    def show_keyboard_shortcuts(self):
        '''Show a dialog box with the keyboard shortcuts.'''
        
        # load the keyboard shortcuts page
        ks_page_location = "help/keyboard_shortcuts.txt"
        f = open(ks_page_location, "r")
        lines = f.read()
        f.close()
        tkMessageBox.showinfo("Keyboard Shortcuts", lines)
        
    def _create_menu(self):
        '''Create the menu in the GUI.'''
    
        menubar = Menu(self)
        self.menus = {}
        
        count = 0
        self.file_menu = Menu(menubar, tearoff=0)
        self.file_menu.add_command(label="New Game")#, command=self.reset)
        self.menus["file_new_game"] = count
        count += 1
        self.file_menu.add_command(label="Save")
        self.menus["file_save"] = count
        count += 1
        self.file_menu.add_command(label="Open")
        self.menus["file_open"] = count
        count += 1
        self.file_menu.add_command(label="Exit")#, command=self.master.destroy)
        self.menus["file_exit"] = count
        count += 1
        menubar.add_cascade(label="File", menu=self.file_menu)
        
        if battleship.GameController.DEV_FLAG:
            count = 0
            self.dev_menu = Menu(menubar, tearoff=0)
            self.dev_menu.add_command(label="Auto Place")
            self.menus["dev_auto_place"] = count
            count += 1
            self.dev_menu.add_command(label="Random Shot")
            self.menus["dev_random_shot"] = count
            count += 1
            self.dev_menu.add_command(label="Auto Load")
            self.menus["dev_auto_load"] = count
            count += 1
            menubar.add_cascade(label="Dev", menu=self.dev_menu)
        
        help_menu = Menu(menubar, tearoff=0)
        help_menu.add_command(label="Rules", command=self._show_rules)
        help_menu.add_command(label="Keyboard Shortcuts", command=self.show_keyboard_shortcuts)
        menubar.add_cascade(label="Help", menu=help_menu)
        
        self.master.config(menu=menubar)

    def _add_staging_panel(self):
        '''Create the placement/ship staging panel.'''
    
        self.my_grid_frame.staging_panel = ShipPlacementPanel(self)
        self.my_grid_frame.staging_panel.place(
            x=self.X_PADDING * 2 + self.SHIP_PANEL_WIDTH + self.my_grid.size,
            y=self.Y_PADDING
        )
            
    def set_all_bgs(self, color, parent):
        '''Set all the backgrounds of the child widgets to a certain color.'''
    
        parent.config(background=color)
    
        for child in parent.winfo_children():
            self.set_all_bgs(color, child)
        
    def _addship_panels(self):
        '''Add a list of ships to select from, for adding.
        Note that staging area must be added FIRST'''
        
        ############################## ShipPanel ########################
        self.my_grid_frame.ship_panel = ShipPanel(self)
        self.my_grid_frame.ship_panel.place(x=self.X_PADDING, y=self.Y_PADDING * 4)
        
        self.unselect_ship()
        ##################################################################
        
        ###################### ShipWarPanel ##############################
        self.my_grid_frame._ship_war_panel = ShipWarPanel(self)
        self.my_grid_frame._ship_war_panel.config(height=self.my_grid_frame.winfo_height())
        self.my_grid_frame._ship_war_panel.place(x=self.X_PADDING, y=self.Y_PADDING * 2)
        ##################################################################
        
        ###################### EnemyShipPanel ############################
        self.their_grid_frame.ship_panel = EnemyShipPanel(self)
        self.their_grid_frame.ship_panel.place(x=self.my_grid.size * 2 + self.X_PADDING * 3 + self.SHIP_PANEL_WIDTH, y=self.Y_PADDING * 4)
        ##################################################################
            
    def unselect_ship(self):
        '''Deselect all ships in the placement and staging GUIs.'''
    
        self.my_grid_frame.ship_panel._ship_var.set(10)
        self.my_grid_frame.staging_panel.reset()
        
    def _hide_frame(self, frame):
        '''Since you can't hide a frame per se, 'unpack' the frame's child widgets.
        WARNING: this removes all packing directions for children'''

        frame.lower()
        
        for child in frame.winfo_children():
            child.pack_forget()
            
    def show_game_over_popup(self, winner):
        '''Show a popup with a dialog saying the game is over, and showing the winning player.'''
        
        msg = {
            battleship.GameController. HUMAN_PLAYER : self.GAME_OVER_WIN_MSG,
            battleship.GameController.AI_PLAYER : self.GAME_OVER_LOSE_MSG
        } [winner]
            
        tkMessageBox.showinfo(self.GAME_OVER_POPUP_TITLE, msg)
        
    def process_placing_state(self):
        '''Basic stuff to do during placing state.'''
        
        self.config(width=self.X_PADDING * 3 + self.my_grid.size + self.SHIP_PANEL_WIDTH + self.my_grid_frame.staging_panel.CANVAS_WIDTH)
        
        # show staging panel
        self.my_grid_frame.staging_panel.pack_ui()
        self.my_grid_frame.staging_panel.lift(aboveThis=self.their_grid_frame)
        self.my_grid_frame.staging_panel.reset()
    
        #re-pack
        self.play_game_button.pack(side=LEFT, padx=self.BUTTON_PADDING, pady=self.BUTTON_PADDING)
        self.play_game_button.config(state=DISABLED)
        self._hide_frame(self.their_grid_frame)
        
        self._hide_frame(self.my_grid_frame._ship_war_panel)
        self.my_grid_frame.ship_panel.lift(aboveThis=self.my_grid_frame._ship_war_panel)
        
        # allow the AI to place ships
        self.ai.place_ships()
        
    def process_playing_state(self):
        '''Basic stuff to do during playing state.'''
        
        self.config(width=self.X_PADDING * 4 + self.my_grid.size * 2 + self.SHIP_PANEL_WIDTH * 2)
        self.my_grid._model.finalize()
        self.their_grid._model.finalize()
        self._hide_frame(self.my_grid_frame.staging_panel)
        
        self.their_grid.config(state=NORMAL)
        self.their_grid.enable()
        
        # hide while playing
        self.play_game_button.pack_forget()
        
        self.unselect_ship()
        
        self.my_grid_frame._ship_war_panel.pack_ui()
        self.my_grid_frame._ship_war_panel.lift(aboveThis=self.my_grid_frame.ship_panel)
        
        # show opponent's grid
        self.their_grid_frame.lift(aboveThis=self.my_grid_frame.staging_panel)
        self.their_grid_label.pack()
        self.their_grid.pack(side=LEFT, pady=20)
        
    def process_game_over_state(self):
        '''Change the view to reflect the game is over.'''
        
        self.their_grid.disable()
        self.master.title(self.WINDOW_TITLE_GAME_OVER)
        
    def process_state(self):
        '''Simple state controller to enable and disable certain widgets depending on the state.'''
    
        if self._state == self.PLACING:
            self.process_placing_state()
        elif self._state == self.PLAYING:
            self.process_playing_state()
        elif self._state == self.GAME_OVER:
            self.process_game_over_state()
                
    def _add_grid_events(self):
        '''Add events to the grids.'''
        
        #self.their_grid.tag_bind("tile", "<Button-1>", self._shot)
        pass
            
    def _add_grids(self):
        '''Create UI containers for the player grids.'''
    
        self.my_grid_frame = PlayerGridFrame(self)
        self.my_grid_frame.place(x=self.X_PADDING + self.SHIP_PANEL_WIDTH, y=self.Y_PADDING)
        l1 = Label(self.my_grid_frame, text="Your Grid")
        l1.pack()
        self.my_grid = ShipGrid(self.my_grid_frame, True)
        self.my_grid.pack(side=LEFT, pady=20)
        
        self.their_grid_frame = PlayerGridFrame(self)
        self.their_grid_frame.place(x=self.my_grid.size + self.X_PADDING * 2 + self.SHIP_PANEL_WIDTH, y=self.Y_PADDING)
        self.their_grid_label = Label(self.their_grid_frame, text="Opponent's Grid")
        self.their_grid_label.pack()
        self.their_grid = ShipGrid(self.their_grid_frame, False)
        self.their_grid.pack(side=LEFT, pady=20)
        
        self._add_grid_events()
        
    def reset(self):
        '''New game!'''
        
        self.master.title(self.WINDOW_TITLE_NORMAL)
        
        # reset selected ship
        self.unselect_ship()
        
        # reset staging area
        self.my_grid_frame.staging_panel.reset()
        
        # reset indicators on ships in panels
        self.my_grid_frame._ship_war_panel.reset()
        for ship, button in self.my_grid_frame.ship_panel.ship_buttons.items():
            button.config(foreground="black")
        
        for x, y in self.my_grid.get_tiles():
            self.reset_closure(x, y)
            
        self._state = self.PLACING
        self.process_state()
            
    def reset_closure(self, x, y):
        '''Add a placement event to the given tile.
        TODO this is badly named'''
    
        tag_id = self.my_grid._get_tile_name(x, y)
        c = self.get_add_ship_callback()
        f = lambda event: self.add_staged_ship(x, y, c)
        self.my_grid.tag_bind(tag_id, "<Button-1>", f)
        
    def add_staged_ship(self, x, y, callback):
        '''Take the stage from the staging area, and place it on the board at position (x, y).
        After ship has been placed, execute the function <callback>.'''
    
        s = self.my_grid_frame.staging_panel.get_staged_ship()
        
        if s is not None:
            self.my_grid.add_ship(x, y, s.get_short_name(), s.is_vertical(), callback)
        
    def get_add_ship_callback(self):
        '''Return the callback function for adding a ship.'''
    
        return lambda: self.ship_set(self.my_grid_frame.ship_panel.get_current_ship())
        
    def _set_ship_sunk(self, ship):
        '''This is a callback, to be called when a ship has been sunk.
        TODO for now only called when one of MY ships is sunk.
        UI shows that the given ship has been sunk.'''
        
        self.my_grid_frame.ship_panel.set_sunk(ship)
        
    def _set_ship_hit(self, ship):
        '''This is a callback, to be called when a ship has been hit.
        TODO for now only called when one of MY ships is hit.
        UI shows that the given ship has been hit.'''
        
        assert ship is not None
        self.my_grid_frame._ship_war_panel.update(ship)
        
    def ship_set(self, ship):
        '''This is a callback, to be called when a ship has been placed.
        UI shows that the given ship has been placed.'''
    
        self._set_ships[ship] = True
        self.my_grid_frame.ship_panel.set_placed(ship)
        
        if all(self._set_ships.values()):
            self.play_game_button.config(state=NORMAL)
        
    def _make_buttons(self):
        '''Create action buttons at the bottom.'''
    
        button_row = self.my_grid.size + self.Y_PADDING + (self.BUTTON_PANEL_HEIGHT - 2 * self.BUTTON_PADDING)
        button_frame = Frame(self)
        button_frame.place(x=self.my_grid.size - self.X_PADDING, y=button_row)
        
        self.play_game_button = Button(button_frame, text="Play")
        self.play_game_button.pack(side=LEFT, padx=self.BUTTON_PADDING, pady=self.BUTTON_PADDING)
        
    def redraw(self):
        '''Redraw the GUI, reloading all info from the model.
        TODO this is a work-in-progress'''
        
        # first, figure out the state
        # are the ships placed? are they sunk?
        grids = [self.my_grid._model, self.their_grid._model]
        
        # set panels to the correct state
        self.my_grid_frame.ship_panel.redraw(grids[0])
        self.my_grid_frame._ship_war_panel.redraw(grids[0])
        self.their_grid_frame.ship_panel.redraw(grids[1])
        
        # set the grid to the correct state
        self.my_grid.redraw(grids[0])
        self.their_grid.redraw(grids[1])
        
        if all([g.has_all_ships() for g in grids]) and all([g.all_sunk() for g in grids]):
            #TODO
            # state is game over.
            pass
        elif all([g.has_all_ships() for g in grids]):
            self.process_playing_state()
        else:
            #TODO
            # state is placing
            pass
Пример #13
0
class ConcordanceSearchView(object):
    _BACKGROUND_COLOUR = "#FFF"  # white

    # Colour of highlighted results
    _HIGHLIGHT_WORD_COLOUR = "#F00"  # red
    _HIGHLIGHT_WORD_TAG = "HL_WRD_TAG"

    _HIGHLIGHT_LABEL_COLOUR = "#C0C0C0"  # dark grey
    _HIGHLIGHT_LABEL_TAG = "HL_LBL_TAG"

    # Percentage of text left of the scrollbar position
    _FRACTION_LEFT_TEXT = 0.30

    def __init__(self):
        self.queue = q.Queue()
        self.model = ConcordanceSearchModel(self.queue)
        self.top = Tk()
        self._init_top(self.top)
        self._init_menubar()
        self._init_widgets(self.top)
        self.load_corpus(self.model.DEFAULT_CORPUS)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def _init_top(self, top):
        top.geometry("950x680+50+50")
        top.title("NLTK Concordance Search")
        top.bind("<Control-q>", self.destroy)
        top.protocol("WM_DELETE_WINDOW", self.destroy)
        top.minsize(950, 680)

    def _init_widgets(self, parent):
        self.main_frame = Frame(
            parent,
            dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1))
        self._init_corpus_select(self.main_frame)
        self._init_query_box(self.main_frame)
        self._init_results_box(self.main_frame)
        self._init_paging(self.main_frame)
        self._init_status(self.main_frame)
        self.main_frame.pack(fill="both", expand=True)

    def _init_menubar(self):
        self._result_size = IntVar(self.top)
        self._cntx_bf_len = IntVar(self.top)
        self._cntx_af_len = IntVar(self.top)
        menubar = Menu(self.top)

        filemenu = Menu(menubar, tearoff=0, borderwidth=0)
        filemenu.add_command(label="Exit",
                             underline=1,
                             command=self.destroy,
                             accelerator="Ctrl-q")
        menubar.add_cascade(label="File", underline=0, menu=filemenu)

        editmenu = Menu(menubar, tearoff=0)
        rescntmenu = Menu(editmenu, tearoff=0)
        rescntmenu.add_radiobutton(
            label="20",
            variable=self._result_size,
            underline=0,
            value=20,
            command=self.set_result_size,
        )
        rescntmenu.add_radiobutton(
            label="50",
            variable=self._result_size,
            underline=0,
            value=50,
            command=self.set_result_size,
        )
        rescntmenu.add_radiobutton(
            label="100",
            variable=self._result_size,
            underline=0,
            value=100,
            command=self.set_result_size,
        )
        rescntmenu.invoke(1)
        editmenu.add_cascade(label="Result Count",
                             underline=0,
                             menu=rescntmenu)

        cntxmenu = Menu(editmenu, tearoff=0)
        cntxbfmenu = Menu(cntxmenu, tearoff=0)
        cntxbfmenu.add_radiobutton(
            label="60 characters",
            variable=self._cntx_bf_len,
            underline=0,
            value=60,
            command=self.set_cntx_bf_len,
        )
        cntxbfmenu.add_radiobutton(
            label="80 characters",
            variable=self._cntx_bf_len,
            underline=0,
            value=80,
            command=self.set_cntx_bf_len,
        )
        cntxbfmenu.add_radiobutton(
            label="100 characters",
            variable=self._cntx_bf_len,
            underline=0,
            value=100,
            command=self.set_cntx_bf_len,
        )
        cntxbfmenu.invoke(1)
        cntxmenu.add_cascade(label="Before", underline=0, menu=cntxbfmenu)

        cntxafmenu = Menu(cntxmenu, tearoff=0)
        cntxafmenu.add_radiobutton(
            label="70 characters",
            variable=self._cntx_af_len,
            underline=0,
            value=70,
            command=self.set_cntx_af_len,
        )
        cntxafmenu.add_radiobutton(
            label="90 characters",
            variable=self._cntx_af_len,
            underline=0,
            value=90,
            command=self.set_cntx_af_len,
        )
        cntxafmenu.add_radiobutton(
            label="110 characters",
            variable=self._cntx_af_len,
            underline=0,
            value=110,
            command=self.set_cntx_af_len,
        )
        cntxafmenu.invoke(1)
        cntxmenu.add_cascade(label="After", underline=0, menu=cntxafmenu)

        editmenu.add_cascade(label="Context", underline=0, menu=cntxmenu)

        menubar.add_cascade(label="Edit", underline=0, menu=editmenu)

        self.top.config(menu=menubar)

    def set_result_size(self, **kwargs):
        self.model.result_count = self._result_size.get()

    def set_cntx_af_len(self, **kwargs):
        self._char_after = self._cntx_af_len.get()

    def set_cntx_bf_len(self, **kwargs):
        self._char_before = self._cntx_bf_len.get()

    def _init_corpus_select(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.var = StringVar(innerframe)
        self.var.set(self.model.DEFAULT_CORPUS)
        Label(
            innerframe,
            justify=LEFT,
            text=" Corpus: ",
            background=self._BACKGROUND_COLOUR,
            padx=2,
            pady=1,
            border=0,
        ).pack(side="left")

        other_corpora = list(self.model.CORPORA.keys()).remove(
            self.model.DEFAULT_CORPUS)
        om = OptionMenu(innerframe,
                        self.var,
                        self.model.DEFAULT_CORPUS,
                        command=self.corpus_selected,
                        *self.model.non_default_corpora())
        om["borderwidth"] = 0
        om["highlightthickness"] = 1
        om.pack(side="left")
        innerframe.pack(side="top", fill="x", anchor="n")

    def _init_status(self, parent):
        self.status = Label(
            parent,
            justify=LEFT,
            relief=SUNKEN,
            background=self._BACKGROUND_COLOUR,
            border=0,
            padx=1,
            pady=0,
        )
        self.status.pack(side="top", anchor="sw")

    def _init_query_box(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        another = Frame(innerframe, background=self._BACKGROUND_COLOUR)
        self.query_box = Entry(another, width=60)
        self.query_box.pack(side="left", fill="x", pady=25, anchor="center")
        self.search_button = Button(
            another,
            text="Search",
            command=self.search,
            borderwidth=1,
            highlightthickness=1,
        )
        self.search_button.pack(side="left",
                                fill="x",
                                pady=25,
                                anchor="center")
        self.query_box.bind("<KeyPress-Return>",
                            self.search_enter_keypress_handler)
        another.pack()
        innerframe.pack(side="top", fill="x", anchor="n")

    def search_enter_keypress_handler(self, *event):
        self.search()

    def _init_results_box(self, parent):
        innerframe = Frame(parent)
        i1 = Frame(innerframe)
        i2 = Frame(innerframe)
        vscrollbar = Scrollbar(i1, borderwidth=1)
        hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz")
        self.results_box = Text(
            i1,
            font=Font(family="courier", size="16"),
            state="disabled",
            borderwidth=1,
            yscrollcommand=vscrollbar.set,
            xscrollcommand=hscrollbar.set,
            wrap="none",
            width="40",
            height="20",
            exportselection=1,
        )
        self.results_box.pack(side="left", fill="both", expand=True)
        self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG,
                                    foreground=self._HIGHLIGHT_WORD_COLOUR)
        self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG,
                                    foreground=self._HIGHLIGHT_LABEL_COLOUR)
        vscrollbar.pack(side="left", fill="y", anchor="e")
        vscrollbar.config(command=self.results_box.yview)
        hscrollbar.pack(side="left", fill="x", expand=True, anchor="w")
        hscrollbar.config(command=self.results_box.xview)
        # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!!
        Label(i2, text="   ",
              background=self._BACKGROUND_COLOUR).pack(side="left", anchor="e")
        i1.pack(side="top", fill="both", expand=True, anchor="n")
        i2.pack(side="bottom", fill="x", anchor="s")
        innerframe.pack(side="top", fill="both", expand=True)

    def _init_paging(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.prev = prev = Button(
            innerframe,
            text="Previous",
            command=self.previous,
            width="10",
            borderwidth=1,
            highlightthickness=1,
            state="disabled",
        )
        prev.pack(side="left", anchor="center")
        self.next = next = Button(
            innerframe,
            text="Next",
            command=self.__next__,
            width="10",
            borderwidth=1,
            highlightthickness=1,
            state="disabled",
        )
        next.pack(side="right", anchor="center")
        innerframe.pack(side="top", fill="y")
        self.current_page = 0

    def previous(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.prev(self.current_page - 1)

    def __next__(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.next(self.current_page + 1)

    def about(self, *e):
        ABOUT = "NLTK Concordance Search Demo\n"
        TITLE = "About: NLTK Concordance Search Demo"
        try:
            from six.moves.tkinter_messagebox import Message

            Message(message=ABOUT, title=TITLE, parent=self.main_frame).show()
        except:
            ShowText(self.top, TITLE, ABOUT)

    def _bind_event_handlers(self):
        self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded)
        self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated)
        self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error)
        self.top.bind(ERROR_LOADING_CORPUS_EVENT,
                      self.handle_error_loading_corpus)

    def _poll(self):
        try:
            event = self.queue.get(block=False)
        except q.Empty:
            pass
        else:
            if event == CORPUS_LOADED_EVENT:
                self.handle_corpus_loaded(event)
            elif event == SEARCH_TERMINATED_EVENT:
                self.handle_search_terminated(event)
            elif event == SEARCH_ERROR_EVENT:
                self.handle_search_error(event)
            elif event == ERROR_LOADING_CORPUS_EVENT:
                self.handle_error_loading_corpus(event)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def handle_error_loading_corpus(self, event):
        self.status["text"] = "Error in loading " + self.var.get()
        self.unfreeze_editable()
        self.clear_all()
        self.freeze_editable()

    def handle_corpus_loaded(self, event):
        self.status["text"] = self.var.get() + " is loaded"
        self.unfreeze_editable()
        self.clear_all()
        self.query_box.focus_set()

    def handle_search_terminated(self, event):
        # todo: refactor the model such that it is less state sensitive
        results = self.model.get_results()
        self.write_results(results)
        self.status["text"] = ""
        if len(results) == 0:
            self.status["text"] = "No results found for " + self.model.query
        else:
            self.current_page = self.model.last_requested_page
        self.unfreeze_editable()
        self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT)

    def handle_search_error(self, event):
        self.status["text"] = "Error in query " + self.model.query
        self.unfreeze_editable()

    def corpus_selected(self, *args):
        new_selection = self.var.get()
        self.load_corpus(new_selection)

    def load_corpus(self, selection):
        if self.model.selected_corpus != selection:
            self.status["text"] = "Loading " + selection + "..."
            self.freeze_editable()
            self.model.load_corpus(selection)

    def search(self):
        self.current_page = 0
        self.clear_results_box()
        self.model.reset_results()
        query = self.query_box.get()
        if len(query.strip()) == 0:
            return
        self.status["text"] = "Searching for " + query
        self.freeze_editable()
        self.model.search(query, self.current_page + 1)

    def write_results(self, results):
        self.results_box["state"] = "normal"
        row = 1
        for each in results:
            sent, pos1, pos2 = each[0].strip(), each[1], each[2]
            if len(sent) != 0:
                if pos1 < self._char_before:
                    sent, pos1, pos2 = self.pad(sent, pos1, pos2)
                sentence = sent[pos1 - self._char_before:pos1 +
                                self._char_after]
                if not row == len(results):
                    sentence += "\n"
                self.results_box.insert(str(row) + ".0", sentence)
                word_markers, label_markers = self.words_and_labels(
                    sent, pos1, pos2)
                for marker in word_markers:
                    self.results_box.tag_add(
                        self._HIGHLIGHT_WORD_TAG,
                        str(row) + "." + str(marker[0]),
                        str(row) + "." + str(marker[1]),
                    )
                for marker in label_markers:
                    self.results_box.tag_add(
                        self._HIGHLIGHT_LABEL_TAG,
                        str(row) + "." + str(marker[0]),
                        str(row) + "." + str(marker[1]),
                    )
                row += 1
        self.results_box["state"] = "disabled"

    def words_and_labels(self, sentence, pos1, pos2):
        search_exp = sentence[pos1:pos2]
        words, labels = [], []
        labeled_words = search_exp.split(" ")
        index = 0
        for each in labeled_words:
            if each == "":
                index += 1
            else:
                word, label = each.split("/")
                words.append((self._char_before + index,
                              self._char_before + index + len(word)))
                index += len(word) + 1
                labels.append((self._char_before + index,
                               self._char_before + index + len(label)))
                index += len(label)
            index += 1
        return words, labels

    def pad(self, sent, hstart, hend):
        if hstart >= self._char_before:
            return sent, hstart, hend
        d = self._char_before - hstart
        sent = "".join([" "] * d) + sent
        return sent, hstart + d, hend + d

    def destroy(self, *e):
        if self.top is None:
            return
        self.top.after_cancel(self.after)
        self.top.destroy()
        self.top = None

    def clear_all(self):
        self.query_box.delete(0, END)
        self.model.reset_query()
        self.clear_results_box()

    def clear_results_box(self):
        self.results_box["state"] = "normal"
        self.results_box.delete("1.0", END)
        self.results_box["state"] = "disabled"

    def freeze_editable(self):
        self.query_box["state"] = "disabled"
        self.search_button["state"] = "disabled"
        self.prev["state"] = "disabled"
        self.next["state"] = "disabled"

    def unfreeze_editable(self):
        self.query_box["state"] = "normal"
        self.search_button["state"] = "normal"
        self.set_paging_button_states()

    def set_paging_button_states(self):
        if self.current_page == 0 or self.current_page == 1:
            self.prev["state"] = "disabled"
        else:
            self.prev["state"] = "normal"
        if self.model.has_more_pages(self.current_page):
            self.next["state"] = "normal"
        else:
            self.next["state"] = "disabled"

    def fire_event(self, event):
        # Firing an event so that rendering of widgets happen in the mainloop thread
        self.top.event_generate(event, when="tail")

    def mainloop(self, *args, **kwargs):
        if in_idle():
            return
        self.top.mainloop(*args, **kwargs)
Пример #14
0
    def initialize(self):
        """
            Initialize the tKinter app (GUI)
            by creating frames and graphical components,
            setting the window size etc.
        """
        # self.bind("<Escape>", lambda x: self.destroy())
        self.bind("<Escape>", on_close)

        listFrame0 = Frame(self)
        listFrame0.pack(side=TOP, fill=BOTH, expand=True)
        listFrame = Frame(listFrame0)
        listFrame.pack(side=LEFT, fill=BOTH, expand=True)

        scrollbary = Scrollbar(listFrame, orient=VERTICAL)
        scrollbary.pack(side=RIGHT, fill=Y)
        scrollbarx = Scrollbar(listFrame, orient=HORIZONTAL)
        scrollbarx.pack(side=BOTTOM, fill=X)
        # bd --> border
        self.listbox = Listbox(listFrame, bd=1, selectmode=SINGLE, yscrollcommand=scrollbary.set, xscrollcommand=scrollbarx.set)
        self.listbox.bind('<<ListboxSelect>>', self.on_select_click)
        scrollbary.config(command=self.listbox.yview)
        scrollbarx.config(command=self.listbox.xview)
        self.listbox.config(width=self.listbox.winfo_reqwidth() // 3)  # width=self.listbox.winfo_reqwidth()
        self.listbox.pack(side=LEFT, fill=BOTH, expand=True)

        listFrame1 = Frame(listFrame0)
        listFrame1.pack(side=LEFT, fill=BOTH, expand=True)
        scrollbary1 = Scrollbar(listFrame1, orient=VERTICAL)
        scrollbary1.pack(side=RIGHT, fill=Y)
        scrollbarx1 = Scrollbar(listFrame1, orient=HORIZONTAL)
        scrollbarx1.pack(side=BOTTOM, fill=X)
        # bd --> border
        self.listbox1 = Listbox(listFrame1, bd=0, selectmode=SINGLE, yscrollcommand=scrollbary1.set, xscrollcommand=scrollbarx1.set)
        self.listbox1.bind('<<ListboxSelect>>', self.on_update_select_click)
        scrollbary1.config(command=self.listbox1.yview)
        scrollbarx1.config(command=self.listbox1.xview)
        self.listbox1.config(width=self.listbox1.winfo_reqwidth() // 2)  # width=self.listbox.winfo_reqwidth()
        self.listbox1.pack(side=LEFT, fill=BOTH, expand=True)

        text_frame = Frame(self)
        text_frame.pack(fill=X)  # ,expand=True)
        self.created_str = tk.StringVar()
        created = tk.Entry(text_frame, textvariable=self.created_str)
        self.created_months = ttk.Combobox(text_frame)
        self.created_months['values'] = ('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12')
        self.created_months.set(datetime.date.today().strftime('%m'))
        self.created_months.state(['readonly'])
        # self.created_months.bind('<<ComboboxSelected>>', self.On_table_select)
        self.value_str = tk.StringVar()
        value = tk.Entry(text_frame, textvariable=self.value_str)
        self.category_str = tk.StringVar()
        category = tk.Entry(text_frame, textvariable=self.category_str)  # , state='readonly')
        self.description_str = tk.StringVar()
        description = tk.Entry(text_frame, textvariable=self.description_str)
        fill_from_csv_button = Button(text_frame, text="fill from csv")
        fill_from_csv_button.bind('<Button-1>', self.on_fill_from_csv_click)
        self.db_tables = ttk.Combobox(text_frame)
        self.db_tables.state(['readonly'])
        self.db_tables.bind('<<ComboboxSelected>>', self.on_table_select)
        created.pack(side=LEFT)
        self.created_months.pack(side=LEFT)
        value.pack(side=LEFT)
        category.pack(side=LEFT)
        description.pack(side=LEFT)
        # check.pack(side=LEFT)
        fill_from_csv_button.pack(side=LEFT)
        self.db_tables.pack(side=LEFT)

        button_frame = Frame(self)
        button_frame.pack(fill=X)  # ,expand=True)

        save_button = Button(button_frame, text="save entry")
        save_button.bind('<Button-1>', self.on_save_entry_click)

        database_button = Button(button_frame, text="select database")
        database_button.bind('<Button-1>', self.on_database_click)
        self.database_path_str = tk.StringVar()
        self.database_path = tk.Label(button_frame, textvariable=self.database_path_str, bg="white", anchor=tk.W)

        save_button.pack(side=LEFT)
        database_button.pack(side=RIGHT)
        self.database_path.pack(side=RIGHT)   # , expand=True, fill=BOTH fill=BOTH,

        debugFrame = Frame(self)
        debugFrame.pack(side=BOTTOM, fill=X)  # ,expand=True)
        self.last_entry = tk.StringVar()
        self.path_label = tk.Label(debugFrame, textvariable=self.last_entry, bg="white", anchor=tk.W)

        self.path_label.pack(side=LEFT, fill=BOTH, expand=True)

        # print self.geometry()
        self.update()
        # self.geometry(self.geometry())
        # self.update()
        # print self.geometry()
        # fix the size of the window by setting the window size to its own size
        w = self.winfo_screenwidth()
        # print w
        # print 'reqwidth',self.winfo_reqwidth()
        # h = self.winfo_screenheight()
        self.geometry('{0}x{1}+{2}+{3}'.format(self.winfo_reqwidth(), self.winfo_reqheight() * 3, w - self.winfo_reqwidth() - 20, 0))  # self.geometry()
        self.update()
        self.minsize(self.winfo_reqwidth(), self.winfo_reqheight())