Skip to content

dialog

mtp_dialog Documentation

A module with wpd relevant dialogs for tkinter

Author: Heribert Füchtenhans

Version: 1.0.1

OS: Windows

Requirements: OS: Windows 10 Python: tkinter mtp_access

The module contains the following classes:

  • 'AskDirectory' - Class to select a directory on an MTP device

Examples:

>>> from win_mtp import mtp_dialog
>>> root = tkinter.Tk()
>>> root.title("mtp_dialogs")
>>> adir = mtp_dialog.AskDirectory(root, "Test ask_directory", ("Alls well", "Don't do it"))

AskDirectory

Bases: Dialog

Select a wpd device and directory.

Public methods:

Public attributes

answer: The MTP path to the selected directory wpd_device: The PortableDevice that was selected

Raises:

Type Description
COMError

exceptions if something went wrong

Source code in win_mtp\dialog.py
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
class AskDirectory(
    tkinter.simpledialog.Dialog
):  # pylint: disable=too-many-instance-attributes
    """Select a wpd device and directory.

    Public methods:

    Public attributes:
        answer: The MTP path to the selected directory
        wpd_device: The PortableDevice that was selected

    Exceptions:
        comtypes.COMError: exceptions if something went wrong
    """

    def __init__(
        self,
        parent: tkinter.Tk,
        title: str,
        buttons: tuple[str, str] = ("OK", "Cancel"),
    ) -> None:
        """Initializes the class and shows the directory dialog.

        Args:
            parent: The tkinter parent
            title: The windows title
            buttons: A tuple with the text for the OK and cancel button to chnage them
                     for other languages.
        """
        self._parent = parent
        self._dialog_title = title
        self._tree: ttk.Treeview
        self._smartphone_icon = tkinter.PhotoImage(data=SMARTPHONE_ICON)
        self._devicelist: dict[str, access.PortableDevice] = {}
        self._buttons = buttons
        # external variables
        self.answer = ""
        self.wpd_device: Optional[access.PortableDevice] = None
        tkinter.simpledialog.Dialog.__init__(self, parent, title=title)

    def buttonbox(self) -> None:
        """Create own buttons"""
        box = ttk.Frame(self)
        box.pack(side=tkinter.TOP, fill=tkinter.BOTH)
        but = ttk.Button(box, text=self._buttons[1], command=self.cancel)
        but.pack(side=tkinter.RIGHT, padx=5, pady=5)
        but = ttk.Button(
            box, text=self._buttons[0], command=self._on_ok, default=tkinter.ACTIVE
        )
        but.pack(side=tkinter.RIGHT, padx=5, pady=5)
        but.focus_set()
        but.bind("<Return>", self.ok)

    def body(self, _: Any) -> None:
        """Create body"""
        box = ttk.Frame(self)
        box.pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True, padx=5, pady=5)
        self._tree = ttk.Treeview(box, height=20, show="tree")
        self._tree.column("#0", width=500)
        self._tree.bind("<<TreeviewSelect>>", self._on_treeselect)
        # adding data, get devices
        for dev in access.get_portable_devices():
            if device_desc := dev.get_description():
                name = device_desc[0]
                self._devicelist[name] = dev
                self._tree.insert(
                    "",
                    tkinter.END,
                    text=name,
                    iid=name,
                    open=False,
                    image=self._smartphone_icon,
                    tags=[name],
                )
                self._start_process_directory(dev, name, name)
        # place the Treeview widget on the root window
        self._tree.pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True)

    def _start_process_directory(
        self, dev: access.PortableDevice, name: str, devicename: str
    ) -> None:
        """Start reading directories"""
        self.config(cursor="wait")
        self._parent.after(500, self._process_directory, dev, name, devicename)

    def _process_directory(
        self, dev: access.PortableDevice, name: str, devicename: str, depth: int = 3
    ) -> None:
        """Insert directory listing until depth is 0"""
        if depth == 0:
            return
        selection = name
        cont = access.get_content_from_device_path(
            self._devicelist[devicename], selection
        )
        if not cont:  # no children
            return
        for child in cont.get_children():
            contenttype = child.content_type
            if contenttype not in (
                access.WPD_CONTENT_TYPE_STORAGE,
                access.WPD_CONTENT_TYPE_DIRECTORY,
            ):
                continue
            cont_name = child.name
            fullpath = f"{selection}/{cont_name}" if cont_name else selection
            with contextlib.suppress(tkinter.TclError):
                self._tree.insert(
                    selection,
                    tkinter.END,
                    text=cont_name,
                    iid=fullpath,
                    open=False,
                    tags=[devicename],
                )
            self._process_directory(dev, fullpath, devicename, depth - 1)
        if depth == 3:
            # When depth is 3, we are in the top level of calls
            self.config(cursor="")

    def _on_treeselect(self, _: Any) -> None:
        """Will be called on very selection"""
        selection = self._tree.focus()
        devicename = self._tree.item(selection, "tags")[0]
        self._start_process_directory(
            self._devicelist[devicename], selection, devicename
        )
        self._tree.item(selection, open=True)

    def _on_ok(self) -> None:
        """OK Button"""
        self.withdraw()
        self.update_idletasks()
        self.answer = self._tree.focus()
        if self.answer == "":
            self.cancel()
        else:
            devicename = self._tree.item(self.answer, "tags")[0]
            self.wpd_device = self._devicelist[devicename]
            try:
                self.apply()
            finally:
                self.cancel()

__init__(parent, title, buttons=('OK', 'Cancel'))

Initializes the class and shows the directory dialog.

Parameters:

Name Type Description Default
parent Tk

The tkinter parent

required
title str

The windows title

required
buttons tuple[str, str]

A tuple with the text for the OK and cancel button to chnage them for other languages.

('OK', 'Cancel')
Source code in win_mtp\dialog.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def __init__(
    self,
    parent: tkinter.Tk,
    title: str,
    buttons: tuple[str, str] = ("OK", "Cancel"),
) -> None:
    """Initializes the class and shows the directory dialog.

    Args:
        parent: The tkinter parent
        title: The windows title
        buttons: A tuple with the text for the OK and cancel button to chnage them
                 for other languages.
    """
    self._parent = parent
    self._dialog_title = title
    self._tree: ttk.Treeview
    self._smartphone_icon = tkinter.PhotoImage(data=SMARTPHONE_ICON)
    self._devicelist: dict[str, access.PortableDevice] = {}
    self._buttons = buttons
    # external variables
    self.answer = ""
    self.wpd_device: Optional[access.PortableDevice] = None
    tkinter.simpledialog.Dialog.__init__(self, parent, title=title)

body(_)

Create body

Source code in win_mtp\dialog.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
def body(self, _: Any) -> None:
    """Create body"""
    box = ttk.Frame(self)
    box.pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True, padx=5, pady=5)
    self._tree = ttk.Treeview(box, height=20, show="tree")
    self._tree.column("#0", width=500)
    self._tree.bind("<<TreeviewSelect>>", self._on_treeselect)
    # adding data, get devices
    for dev in access.get_portable_devices():
        if device_desc := dev.get_description():
            name = device_desc[0]
            self._devicelist[name] = dev
            self._tree.insert(
                "",
                tkinter.END,
                text=name,
                iid=name,
                open=False,
                image=self._smartphone_icon,
                tags=[name],
            )
            self._start_process_directory(dev, name, name)
    # place the Treeview widget on the root window
    self._tree.pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True)

buttonbox()

Create own buttons

Source code in win_mtp\dialog.py
100
101
102
103
104
105
106
107
108
109
110
111
def buttonbox(self) -> None:
    """Create own buttons"""
    box = ttk.Frame(self)
    box.pack(side=tkinter.TOP, fill=tkinter.BOTH)
    but = ttk.Button(box, text=self._buttons[1], command=self.cancel)
    but.pack(side=tkinter.RIGHT, padx=5, pady=5)
    but = ttk.Button(
        box, text=self._buttons[0], command=self._on_ok, default=tkinter.ACTIVE
    )
    but.pack(side=tkinter.RIGHT, padx=5, pady=5)
    but.focus_set()
    but.bind("<Return>", self.ok)