【问题标题】:How to use Python oops concepts to develop dual pane file browser? [closed]如何使用 Python oops 概念开发双窗格文件浏览器? [关闭]
【发布时间】:2020-04-24 21:01:17
【问题描述】:

我正在尝试使用 python 和 Tkinter GUI 创建一个双窗格文件浏览器,我将在其中添加'比较目录'、'等功能点击按钮即可比较文件。

(最终的 GUI 看起来有点像这个名为 Meld https://en.wikipedia.org/wiki/Meld_(software) 的工具)

作为第一步,我正在尝试创建一个 Tree() 类(其脚本是用 Tree_browser.py 编写的)。然后我正在导入这个 在 dircmp_utility.py 中使用 import Tree_browser(我还将编写一个名为 dircmp.py 的目录/文件比较脚本)

现在我的问题是我对课程和 oops 概念不熟悉,并且仍在努力学习和理解。因此,在互联网上花了一些时间(YouTube、StackOverflow、geeks for geeks 等)之后,我尝试了下面的代码,但我得到了下面显示的错误。

谁能告诉我为什么会出现这个错误以及我的代码中的错误是什么

即使定义了 self、autoscroll()、populate_roots(),它也会抛出一个错误,说它们没有定义。这是为什么呢??

Tree_browser.py

​​>
import os
import glob
import tkinter as tk
from tkinter import ttk

class Tree():
    def __init__(self,root):
        self.frame = tk.Frame(root)
        self.frame.pack()

    def populate_tree(self, tree, node):
        if tree.set(node, "type") != 'directory':
            return

        path = tree.set(node, "fullpath")
        tree.delete(*tree.get_children(node))

        parent = tree.parent(node)
        special_dirs = [] if parent else glob.glob('.') + glob.glob('..')

        for p in special_dirs + os.listdir(path):
            ptype = None
            p = os.path.join(path, p).replace('\\', '/')
            if os.path.isdir(p): ptype = "directory"
            elif os.path.isfile(p): ptype = "file"

            fname = os.path.split(p)[1]
            id = tree.insert(node, "end", text=fname, values=[p, ptype])

            if ptype == 'directory':
                if fname not in ('.', '..'):
                    tree.insert(id, 0, text="dummy")
                    tree.item(id, text=fname)
            elif ptype == 'file':
                size = os.stat(p).st_size
                tree.set(id, "size", "%d bytes" % size)


    def populate_roots(self, tree):
        self.dir = os.path.abspath('.').replace('\\', '/')
        self.node = tree.insert('', 'end', text=dir, values=[dir, "directory"])
        populate_tree(tree, node)

    def update_tree(self, event):
        tree = event.widget
        populate_tree(tree, tree.focus())

    def change_dir(self,event):
        tree = event.widget
        node = tree.focus()
        if tree.parent(node):
            path = os.path.abspath(tree.set(node, "fullpath"))
            if os.path.isdir(path):
                os.chdir(path)
                tree.delete(tree.get_children(''))
                populate_roots(tree)

    def autoscroll(self, sbar, first, last):
        """Hide and show scrollbar as needed."""
        first, last = float(first), float(last)
        if first <= 0 and last >= 1:
            sbar.pack_forget()
        else:
            sbar.pack()
        sbar.set(first, last)

    vsb = ttk.Scrollbar(orient="vertical")
    hsb = ttk.Scrollbar(orient="horizontal")

    tree = ttk.Treeview(self.frame, columns=("fullpath", "type", "size"),
        displaycolumns="size", yscrollcommand=lambda f, l: autoscroll(vsb, f, l),
        xscrollcommand=lambda f, l:autoscroll(hsb, f, l))
    tree.pack()
    vsb['command'] = tree.yview
    hsb['command'] = tree.xview

    tree.heading("#0", text="Directory Structure", anchor='w')
    tree.heading("size", text="File Size", anchor='w')
    tree.column("size", stretch=0, width=100)

    populate_roots(tree)
    tree.bind('<<TreeviewOpen>>', update_tree)
    tree.bind('<Double-Button-1>', change_dir)

    # Arrange the tree and its scrollbars in the toplevel
    tree.pack(expand=1, fill=BOTH)
    vsb.pack(expand=1, fill=Y)
    hsb.pack(expand=1, fill=X)

    
if __name__ == '__main__':
    pass

dircmp_utility.py

​​>
import Tree_browser
import dircmp
import tkinter

master = tk()

left_frame = Frame(master)
right_frame = Frame(master)

left_frame.pack()
right_frame.pack()

left_tree = Tree(left_frame)
right_tree = Tree(right_frame)

master.mainloop()

运行dircmp.py后的错误

Traceback (most recent call last):
  File "E:\Python_code_to_compare_two_folders\dircmp_using class\dircmp_utility.py", line 3, in <module>
    import Tree_browser
  File "E:\Python_code_to_compare_two_folders\dircmp_using class\Tree_browser.py", line 7, in <module>
    class Tree():
  File "E:\Python_code_to_compare_two_folders\dircmp_using class\Tree_browser.py", line 82, in Tree
    populate_roots(tree)
TypeError: populate_roots() missing 1 required positional argument: 'tree'
>>> Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Program Files\Python38\lib\tkinter\__init__.py", line 1883, in __call__
    return self.func(*args)
  File "E:\Python_code_to_compare_two_folders\dircmp_using class\Tree_browser.py", line 73, in <lambda>
    xscrollcommand=lambda f, l:autoscroll(hsb, f, l))
NameError: name 'autoscroll' is not defined
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Program Files\Python38\lib\tkinter\__init__.py", line 1883, in __call__
    return self.func(*args)
  File "E:\Python_code_to_compare_two_folders\dircmp_using class\Tree_browser.py", line 72, in <lambda>
    displaycolumns="size", yscrollcommand=lambda f, l: autoscroll(vsb, f, l),
NameError: name 'autoscroll' is not defined
 File "E:\Python_code_to_compare_two_folders\dircmp_using class\Tree_browser.py", line 71, in Tree
    tree = ttk.Treeview(self.frame, columns=("fullpath", "type", "size"),
NameError: name 'self' is not defined

更新:发布工作脚本

保留我之前发布的错误代码,以便其他人可以从我的错误中吸取教训。感谢所有帮助我编写正确脚本的 cmets。

Tree_browser.py

​​>
import os
import glob
import tkinter as tk
from tkinter import ttk

class Tree:
    def __init__(self,root):
        self.pane = tk.Frame(root, bg="red")
        self.frame = tk.Frame(self.pane, bg="blue")

        self.vsb = ttk.Scrollbar(self.frame, orient="vertical")
        self.hsb = ttk.Scrollbar(self.pane, orient="horizontal")

        self.tree = ttk.Treeview(self.frame, columns=("fullpath", "type", "size"),
                            displaycolumns="size", yscrollcommand=lambda f, l: self.yautoscroll(self.vsb, f, l),
                            xscrollcommand=lambda f, l: self.xautoscroll(self.hsb, f, l))
        self.vsb['command'] = self.tree.yview
        self.hsb['command'] = self.tree.xview

        self.tree.heading("#0", text="Directory Structure", anchor='w')
        self.tree.heading("size", text="File Size", anchor='w')
        self.tree.column("size", stretch=0, width=100)

        self.populate_roots(self.tree)
        self.tree.bind('<<TreeviewOpen>>', self.update_tree)
        self.tree.bind('<Double-Button-1>', self.change_dir)

        # Arrange the tree and its scrollbars in the toplevel
        self.pane.pack(side=tk.BOTTOM, expand=1, fill=tk.BOTH)
        self.frame.pack(side=tk.TOP, expand=1, fill=tk.BOTH)
        self.tree.pack(side=tk.LEFT, expand=1, fill=tk.BOTH)
        self.vsb.pack(expand=1, fill=tk.Y)
        self.hsb.pack(expand=1, fill=tk.X)

    def populate_tree(self, tree, node):
        if self.tree.set(self.node, "type") != 'directory':
            return
        self.path = self.tree.set(self.node, "fullpath")
        self.tree.delete(*self.tree.get_children(self.node))

        self.parent = self.tree.parent(self.node)
        self.special_dirs = [] if self.parent else glob.glob('.') + glob.glob('..')

        for p in self.special_dirs + os.listdir(self.path):
            ptype = None
            p = os.path.join(self.path, p).replace('\\', '/')
            if os.path.isdir(p): ptype = "directory"
            elif os.path.isfile(p): ptype = "file"

            self.fname = os.path.split(p)[1]
            id = tree.insert(self.node, "end", text=self.fname, values=[p, ptype])

            if ptype == 'directory':
                if self.fname not in ('.', '..'):
                    self.tree.insert(id, 0, text="dummy", open=True)
                    self.tree.item(id, text=self.fname)
            elif ptype == 'file':
                size = os.stat(p).st_size
                self.tree.set(id, "size", "%d bytes" % size)

    def populate_roots(self, tree):
        self.dir = os.path.abspath('.').replace('\\', '/')
        self.node = tree.insert('', 'end', text=self.dir, values=[self.dir, "directory"], open=True)
        self.populate_tree(self.tree, self.node)

    def update_tree(self, event):
        self.tree = event.widget
        self.node = self.tree.focus()
        self.populate_tree(self.tree, self.node)

    def change_dir(self,event):
        self.tree = event.widget
        self.node = self.tree.focus()
        if self.tree.parent(self.node):
            self.path = os.path.abspath(self.tree.set(self.node, "fullpath"))
            if os.path.isdir(self.path):
                os.chdir(self.path)
                self.tree.delete(self.tree.get_children(''))
                self.populate_roots(self.tree)

    def yautoscroll(self, sbar, first, last):
        """Hide and show scrollbar as needed."""
        self.first, self.last = float(first), float(last)
        if self.first <= 0 and self.last >= 1:
            sbar.pack_forget()
        else:
            sbar.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)
        sbar.set(self.first, self.last)

    def xautoscroll(self, sbar, first, last):
        """Hide and show scrollbar as needed."""
        self.first, self.last = float(first), float(last)
        if self.first <= 0 and self.last >= 1:
            sbar.pack_forget()
        else:
            sbar.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
        sbar.set(self.first, self.last)

if __name__ == '__main__':
    pass

dircmp_utility.py

​​>
import tkinter as tk

master = tk.Tk()

left_frame = tk.Frame(master)
right_frame = tk.Frame(master)

left_frame.pack(side=tk.LEFT, expand=1, fill=tk.BOTH)
right_frame.pack(side=tk.LEFT, expand=1, fill=tk.BOTH)

left_tree = Tree(left_frame)
right_tree = Tree(right_frame)

master.mainloop()

【问题讨论】:

  • 尽量不要一次写这么多代码。从仅使用一种方法的类开始,对其进行测试和试验,直到您确定并挑战了所有假设并且可能学到了很多东西。然后添加另一个类方法并再次测试和实验。拥有像 PyCharm 这样的良好 IDE 会有所帮助,这样您就可以在调试器中逐行执行代码并查看变量值。一点一点地增加。在你已经拥有的东西没有错误之前,不要开始下一件事。

标签: python python-3.x tkinter django-filebrowser


【解决方案1】:

让我们提取您的类概要文件、一个函数概要文件和错误语句:

class Tree():
    ...
    ...

    def autoscroll(self, sbar, first, last):
        ...
        ...

    tree = ttk.Treeview(self.frame, columns=("fullpath", "type", "size"),
        displaycolumns="size", yscrollcommand=lambda f, l: autoscroll(vsb, f, l),
        xscrollcommand=lambda f, l:autoscroll(hsb, f, l))

您似乎对类的结构有些困惑。上面的 3 行语句位于类的 body 中。它不是在实例方法中——所以没有共生体实例,也没有定义self变量。

稍后在同一语句中,您尝试调用“打开”函数autoscroll。没有这样的功能。您将此与同名的实例方法混淆了。查看如何调用实例方法——这将是一些持续的事情

seedling = Tree()
...
seedling.autoscroll(vsb, f, l)

后退几步。当您还没有学会如何调用类方法时,不要编写所有这些代码,而是从类和方法头重新开始。为标头制作存根——正文将仅为pass,或返回一个常量供调用者保存。使用这些来使您的层次结构清晰。 然后恢复真正的方法体。没有必要贴上 100 多行的代码来说明一个 10 行的问题;我也学会了按照这个原则进行编码。

【讨论】:

  • 此外,许多方法调用直接只是方法名称,而不是“绑定到对象的方法”。前任。 populate_rootsupdate_tree 中的调用 populate_tree(tree, node) 应该是 self.populate_tree(tree, node) - self 丢失。 change_dir() 有一个populate_roots(tree),同样没有self
  • 对。由于 Stack Overflow 不是代码审查或教程资源,因此我尝试仅将 OP 推入一般轨道以解决他们自己的问题。
猜你喜欢
  • 2020-02-05
  • 1970-01-01
  • 2020-02-05
  • 1970-01-01
  • 1970-01-01
  • 2011-02-01
  • 1970-01-01
  • 2016-04-23
  • 2021-09-29
相关资源
最近更新 更多