{"id":2489,"date":"2021-10-11T12:05:52","date_gmt":"2021-10-11T12:05:52","guid":{"rendered":"https:\/\/www.pythontutorial.net\/?page_id=2489"},"modified":"2022-09-07T01:03:37","modified_gmt":"2022-09-07T01:03:37","slug":"tkinter-mvc","status":"publish","type":"page","link":"https:\/\/www.pythontutorial.net\/tkinter\/tkinter-mvc\/","title":{"rendered":"Tkinter MVC"},"content":{"rendered":"\n<p><strong>Summary<\/strong>: in this tutorial, you&#8217;ll learn how to structure a Tkinter application using the model-view-controller (MVC) pattern.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id='introduction-to-tkinter-mvc'>Introduction to Tkinter MVC <a href=\"#introduction-to-tkinter-mvc\" class=\"anchor\" id=\"introduction-to-tkinter-mvc\" title=\"Anchor for Introduction to Tkinter MVC\">#<\/a><\/h2>\n\n\n\n<p>As your application grows, its complexity also increases. To make the application more manageable, you can use the model-view-controller design pattern.<\/p>\n\n\n\n<p>The MVC design pattern allows you to divide the application into three main components: model, view, and controller. This structure helps you focus on the logic of each part and make it more maintainable, especially when the application grows.<\/p>\n\n\n\n<p>The following diagram illustrates the MVC design pattern:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"379\" height=\"320\" src=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/tkinter-mvc-design-pattern.png\" alt=\"\" class=\"wp-image-2495\" srcset=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/tkinter-mvc-design-pattern.png 379w, https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/tkinter-mvc-design-pattern-300x253.png 300w\" sizes=\"auto, (max-width: 379px) 100vw, 379px\" \/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id='model'>Model <a href=\"#model\" class=\"anchor\" id=\"model\" title=\"Anchor for Model\">#<\/a><\/h3>\n\n\n\n<p>A model in MVC represents the data. A model deals with getting data from or writing data into storage such as a database or file. The model may also contain the logic to validate the data to ensure data integrity.<\/p>\n\n\n\n<p>The model must not depend on the view and controller. In other words, you can reuse the model in other non-Tkinter applications such as web and mobile apps.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id='view'>View <a href=\"#view\" class=\"anchor\" id=\"view\" title=\"Anchor for View\">#<\/a><\/h3>\n\n\n\n<p>A view is the user interface that represents the data in the model. The view doesn&#8217;t directly communicate with the model. Ideally, a view should have very little logic to display data.  <\/p>\n\n\n\n<p>The view communicates with the controller directly. In Tinker applications, the view is the root <a href=\"https:\/\/www.pythontutorial.net\/tkinter\/tkinter-window\/\">window<\/a> that consists of widgets. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id='controller'>Controller <a href=\"#controller\" class=\"anchor\" id=\"controller\" title=\"Anchor for Controller\">#<\/a><\/h3>\n\n\n\n<p>A controller acts as the intermediary between the views and models. The controller routes data between the views and models. <\/p>\n\n\n\n<p>For example, if users click the save button on the view, the controller routes the &#8220;save&#8221; action to the model to save the data into a database and notify the view to display a message.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id='tkinter-mvc-example'>Tkinter MVC example <a href=\"#tkinter-mvc-example\" class=\"anchor\" id=\"tkinter-mvc-example\" title=\"Anchor for Tkinter MVC example\">#<\/a><\/h2>\n\n\n\n<p>We&#8217;ll take a simple example to illustrate how to apply the MVC design pattern in a Tkinter application:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"518\" height=\"130\" src=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/python-tkinter-mvc.png\" alt=\"python tkinter mvc\" class=\"wp-image-2493\" srcset=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/python-tkinter-mvc.png 518w, https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/python-tkinter-mvc-300x75.png 300w\" sizes=\"auto, (max-width: 518px) 100vw, 518px\" \/><\/figure>\n\n\n\n<p>The application that you&#8217;ll build contains an entry for entering an email. When you click the save button, the controller calls the model to validate the email.<\/p>\n\n\n\n<p>If the email is valid, the model saves the email into a text file and the view shows a success message:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"516\" height=\"128\" src=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/tkinter-mvc.png\" alt=\"\" class=\"wp-image-2491\" srcset=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/tkinter-mvc.png 516w, https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/tkinter-mvc-300x74.png 300w\" sizes=\"auto, (max-width: 516px) 100vw, 516px\" \/><\/figure>\n\n\n\n<p>If the email is not valid, the view shows an error message:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"516\" height=\"128\" src=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/tkinter-mvc-example.png\" alt=\"\" class=\"wp-image-2490\" srcset=\"https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/tkinter-mvc-example.png 516w, https:\/\/www.pythontutorial.net\/wp-content\/uploads\/2021\/10\/tkinter-mvc-example-300x74.png 300w\" sizes=\"auto, (max-width: 516px) 100vw, 516px\" \/><\/figure>\n\n\n\n<p>We&#8217;ll hide the message after 3 seconds.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id='model-class'>Model class <a href=\"#model-class\" class=\"anchor\" id=\"model-class\" title=\"Anchor for Model class\">#<\/a><\/h3>\n\n\n\n<p>The following defines the Model class that has an email property:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Model<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, email)<\/span>:<\/span>\n        self.email = email\n\n<span class=\"hljs-meta\">    @property<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">email<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> self.__email\n\n<span class=\"hljs-meta\">    @email.setter<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">email<\/span><span class=\"hljs-params\">(self, value)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Validate the email\n        :param value:\n        :return:\n        \"\"\"<\/span>\n        pattern = <span class=\"hljs-string\">r'\\b&#91;A-Za-z0-9._%+-]+@&#91;A-Za-z0-9.-]+\\.&#91;A-Z|a-z]{2,}\\b'<\/span>\n        <span class=\"hljs-keyword\">if<\/span> re.fullmatch(pattern, value):\n            self.__email = value\n        <span class=\"hljs-keyword\">else<\/span>:\n            <span class=\"hljs-keyword\">raise<\/span> ValueError(<span class=\"hljs-string\">f'Invalid email address: <span class=\"hljs-subst\">{value}<\/span>'<\/span>)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Save the email into a file\n        :return:\n        \"\"\"<\/span>\n        <span class=\"hljs-keyword\">with<\/span> open(<span class=\"hljs-string\">'emails.txt'<\/span>, <span class=\"hljs-string\">'a'<\/span>) <span class=\"hljs-keyword\">as<\/span> f:\n            f.write(self.email + <span class=\"hljs-string\">'\\n'<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>How it works:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The <code>email<\/code> setter validates the email before assigning it to the <code>__email<\/code> attribute. If the email is not valid, it&#8217;ll raise a <code>ValueError<\/code>. <\/li><li>The <code>save()<\/code> method writes the email into a simple text file. In real-world applications, you may want to save it into a database.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id='view'>View <a href=\"#view\" class=\"anchor\" id=\"view\" title=\"Anchor for View\">#<\/a><\/h3>\n\n\n\n<p>The following defines the view that shows a form to input an email:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">View<\/span><span class=\"hljs-params\">(ttk.Frame)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, parent)<\/span>:<\/span>\n        super().__init__(parent)\n\n        <span class=\"hljs-comment\"># create widgets<\/span>\n        <span class=\"hljs-comment\"># label<\/span>\n        self.label = ttk.Label(self, text=<span class=\"hljs-string\">'Email:'<\/span>)\n        self.label.grid(row=<span class=\"hljs-number\">1<\/span>, column=<span class=\"hljs-number\">0<\/span>)\n\n        <span class=\"hljs-comment\"># email entry<\/span>\n        self.email_var = tk.StringVar()\n        self.email_entry = ttk.Entry(self, textvariable=self.email_var, width=<span class=\"hljs-number\">30<\/span>)\n        self.email_entry.grid(row=<span class=\"hljs-number\">1<\/span>, column=<span class=\"hljs-number\">1<\/span>, sticky=tk.NSEW)\n\n        <span class=\"hljs-comment\"># save button<\/span>\n        self.save_button = ttk.Button(self, text=<span class=\"hljs-string\">'Save'<\/span>, command=self.save_button_clicked)\n        self.save_button.grid(row=<span class=\"hljs-number\">1<\/span>, column=<span class=\"hljs-number\">3<\/span>, padx=<span class=\"hljs-number\">10<\/span>)\n\n        <span class=\"hljs-comment\"># message<\/span>\n        self.message_label = ttk.Label(self, text=<span class=\"hljs-string\">''<\/span>, foreground=<span class=\"hljs-string\">'red'<\/span>)\n        self.message_label.grid(row=<span class=\"hljs-number\">2<\/span>, column=<span class=\"hljs-number\">1<\/span>, sticky=tk.W)\n\n        <span class=\"hljs-comment\"># set the controller<\/span>\n        self.controller = <span class=\"hljs-literal\">None<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">set_controller<\/span><span class=\"hljs-params\">(self, controller)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Set the controller\n        :param controller:\n        :return:\n        \"\"\"<\/span>\n        self.controller = controller\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">save_button_clicked<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Handle button click event\n        :return:\n        \"\"\"<\/span>\n        <span class=\"hljs-keyword\">if<\/span> self.controller:\n            self.controller.save(self.email_var.get())\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">show_error<\/span><span class=\"hljs-params\">(self, message)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Show an error message\n        :param message:\n        :return:\n        \"\"\"<\/span>\n        self.message_label&#91;<span class=\"hljs-string\">'text'<\/span>] = message\n        self.message_label&#91;<span class=\"hljs-string\">'foreground'<\/span>] = <span class=\"hljs-string\">'red'<\/span>\n        self.message_label.after(<span class=\"hljs-number\">3000<\/span>, self.hide_message)\n        self.email_entry&#91;<span class=\"hljs-string\">'foreground'<\/span>] = <span class=\"hljs-string\">'red'<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">show_success<\/span><span class=\"hljs-params\">(self, message)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Show a success message\n        :param message:\n        :return:\n        \"\"\"<\/span>\n        self.message_label&#91;<span class=\"hljs-string\">'text'<\/span>] = message\n        self.message_label&#91;<span class=\"hljs-string\">'foreground'<\/span>] = <span class=\"hljs-string\">'green'<\/span>\n        self.message_label.after(<span class=\"hljs-number\">3000<\/span>, self.hide_message)\n\n        <span class=\"hljs-comment\"># reset the form<\/span>\n        self.email_entry&#91;<span class=\"hljs-string\">'foreground'<\/span>] = <span class=\"hljs-string\">'black'<\/span>\n        self.email_var.set(<span class=\"hljs-string\">''<\/span>)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">hide_message<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Hide the message\n        :return:\n        \"\"\"<\/span>\n        self.message_label&#91;<span class=\"hljs-string\">'text'<\/span>] = <span class=\"hljs-string\">''<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>How it works.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>First, create the widgets in the <code>__init__()<\/code> method.<\/li><li>Second, define the <code>set_controller()<\/code> method to set a controller.<\/li><li>Third, call the <code>save()<\/code> method of the controller in the click event handler of the save button.<\/li><li>Finally, define the <code>show_error()<\/code>, <code>show_success()<\/code>, and <code>hide_message()<\/code> methods to show\/hide the message.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id='controller'>Controller <a href=\"#controller\" class=\"anchor\" id=\"controller\" title=\"Anchor for Controller\">#<\/a><\/h3>\n\n\n\n<p>The following defines a controller:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Controller<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, model, view)<\/span>:<\/span>\n        self.model = model\n        self.view = view\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(self, email)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Save the email\n        :param email:\n        :return:\n        \"\"\"<\/span>\n        <span class=\"hljs-keyword\">try<\/span>:\n\n            <span class=\"hljs-comment\"># save the model<\/span>\n            self.model.email = email\n            self.model.save()\n\n            <span class=\"hljs-comment\"># show a success message<\/span>\n            self.view.show_success(<span class=\"hljs-string\">f'The email <span class=\"hljs-subst\">{email}<\/span> saved!'<\/span>)\n\n        <span class=\"hljs-keyword\">except<\/span> ValueError <span class=\"hljs-keyword\">as<\/span> error:\n            <span class=\"hljs-comment\"># show an error message<\/span>\n            self.view.show_error(error)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>How the controller works.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>First, assign the model and view in the <code>__init__()<\/code> method<\/li><li>Second, define the <code>save()<\/code> method that saves the model into the text file. If the model is saved successfully, show a success message. Otherwise, display an error message.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id='application'>Application <a href=\"#application\" class=\"anchor\" id=\"application\" title=\"Anchor for Application\">#<\/a><\/h3>\n\n\n\n<p>The following defines the <code>App<\/code> class that uses the Model, View, and Controller classes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">App<\/span><span class=\"hljs-params\">(tk.Tk)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        super().__init__()\n\n        self.title(<span class=\"hljs-string\">'Tkinter MVC Demo'<\/span>)\n\n        <span class=\"hljs-comment\"># create a model<\/span>\n        model = Model(<span class=\"hljs-string\">'hello@pythontutorial.net'<\/span>)\n\n        <span class=\"hljs-comment\"># create a view and place it on the root window<\/span>\n        view = View(self)\n        view.grid(row=<span class=\"hljs-number\">0<\/span>, column=<span class=\"hljs-number\">0<\/span>, padx=<span class=\"hljs-number\">10<\/span>, pady=<span class=\"hljs-number\">10<\/span>)\n\n        <span class=\"hljs-comment\"># create a controller<\/span>\n        controller = Controller(model, view)\n\n        <span class=\"hljs-comment\"># set the controller to view<\/span>\n        view.set_controller(controller)\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">'__main__'<\/span>:\n    app = App()\n    app.mainloop()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>How it works.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>First, create a model.<\/li><li>Second, create a view and place it on the root window.<\/li><li>Third, create a controller and set it to the view.<\/li><\/ul>\n\n\n\n<p>Put it all together.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python\"><span class=\"hljs-keyword\">import<\/span> re\n<span class=\"hljs-keyword\">import<\/span> tkinter <span class=\"hljs-keyword\">as<\/span> tk\n<span class=\"hljs-keyword\">from<\/span> tkinter <span class=\"hljs-keyword\">import<\/span> ttk\n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Model<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, email)<\/span>:<\/span>\n        self.email = email\n\n<span class=\"hljs-meta\">    @property<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">email<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> self.__email\n\n<span class=\"hljs-meta\">    @email.setter<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">email<\/span><span class=\"hljs-params\">(self, value)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Validate the email\n        :param value:\n        :return:\n        \"\"\"<\/span>\n        pattern = <span class=\"hljs-string\">r'\\b&#91;A-Za-z0-9._%+-]+@&#91;A-Za-z0-9.-]+\\.&#91;A-Z|a-z]{2,}\\b'<\/span>\n        <span class=\"hljs-keyword\">if<\/span> re.fullmatch(pattern, value):\n            self.__email = value\n        <span class=\"hljs-keyword\">else<\/span>:\n            <span class=\"hljs-keyword\">raise<\/span> ValueError(<span class=\"hljs-string\">f'Invalid email address: <span class=\"hljs-subst\">{value}<\/span>'<\/span>)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Save the email into a file\n        :return:\n        \"\"\"<\/span>\n        <span class=\"hljs-keyword\">with<\/span> open(<span class=\"hljs-string\">'emails.txt'<\/span>, <span class=\"hljs-string\">'a'<\/span>) <span class=\"hljs-keyword\">as<\/span> f:\n            f.write(self.email + <span class=\"hljs-string\">'\\n'<\/span>)\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">View<\/span><span class=\"hljs-params\">(ttk.Frame)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, parent)<\/span>:<\/span>\n        super().__init__(parent)\n\n        <span class=\"hljs-comment\"># create widgets<\/span>\n        <span class=\"hljs-comment\"># label<\/span>\n        self.label = ttk.Label(self, text=<span class=\"hljs-string\">'Email:'<\/span>)\n        self.label.grid(row=<span class=\"hljs-number\">1<\/span>, column=<span class=\"hljs-number\">0<\/span>)\n\n        <span class=\"hljs-comment\"># email entry<\/span>\n        self.email_var = tk.StringVar()\n        self.email_entry = ttk.Entry(self, textvariable=self.email_var, width=<span class=\"hljs-number\">30<\/span>)\n        self.email_entry.grid(row=<span class=\"hljs-number\">1<\/span>, column=<span class=\"hljs-number\">1<\/span>, sticky=tk.NSEW)\n\n        <span class=\"hljs-comment\"># save button<\/span>\n        self.save_button = ttk.Button(self, text=<span class=\"hljs-string\">'Save'<\/span>, command=self.save_button_clicked)\n        self.save_button.grid(row=<span class=\"hljs-number\">1<\/span>, column=<span class=\"hljs-number\">3<\/span>, padx=<span class=\"hljs-number\">10<\/span>)\n\n        <span class=\"hljs-comment\"># message<\/span>\n        self.message_label = ttk.Label(self, text=<span class=\"hljs-string\">''<\/span>, foreground=<span class=\"hljs-string\">'red'<\/span>)\n        self.message_label.grid(row=<span class=\"hljs-number\">2<\/span>, column=<span class=\"hljs-number\">1<\/span>, sticky=tk.W)\n\n        <span class=\"hljs-comment\"># set the controller<\/span>\n        self.controller = <span class=\"hljs-literal\">None<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">set_controller<\/span><span class=\"hljs-params\">(self, controller)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Set the controller\n        :param controller:\n        :return:\n        \"\"\"<\/span>\n        self.controller = controller\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">save_button_clicked<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Handle button click event\n        :return:\n        \"\"\"<\/span>\n        <span class=\"hljs-keyword\">if<\/span> self.controller:\n            self.controller.save(self.email_var.get())\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">show_error<\/span><span class=\"hljs-params\">(self, message)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Show an error message\n        :param message:\n        :return:\n        \"\"\"<\/span>\n        self.message_label&#91;<span class=\"hljs-string\">'text'<\/span>] = message\n        self.message_label&#91;<span class=\"hljs-string\">'foreground'<\/span>] = <span class=\"hljs-string\">'red'<\/span>\n        self.message_label.after(<span class=\"hljs-number\">3000<\/span>, self.hide_message)\n        self.email_entry&#91;<span class=\"hljs-string\">'foreground'<\/span>] = <span class=\"hljs-string\">'red'<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">show_success<\/span><span class=\"hljs-params\">(self, message)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Show a success message\n        :param message:\n        :return:\n        \"\"\"<\/span>\n        self.message_label&#91;<span class=\"hljs-string\">'text'<\/span>] = message\n        self.message_label&#91;<span class=\"hljs-string\">'foreground'<\/span>] = <span class=\"hljs-string\">'green'<\/span>\n        self.message_label.after(<span class=\"hljs-number\">3000<\/span>, self.hide_message)\n\n        <span class=\"hljs-comment\"># reset the form<\/span>\n        self.email_entry&#91;<span class=\"hljs-string\">'foreground'<\/span>] = <span class=\"hljs-string\">'black'<\/span>\n        self.email_var.set(<span class=\"hljs-string\">''<\/span>)\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">hide_message<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Hide the message\n        :return:\n        \"\"\"<\/span>\n        self.message_label&#91;<span class=\"hljs-string\">'text'<\/span>] = <span class=\"hljs-string\">''<\/span>            \n\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Controller<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, model, view)<\/span>:<\/span>\n        self.model = model\n        self.view = view\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(self, email)<\/span>:<\/span>\n        <span class=\"hljs-string\">\"\"\"\n        Save the email\n        :param email:\n        :return:\n        \"\"\"<\/span>\n        <span class=\"hljs-keyword\">try<\/span>:\n\n            <span class=\"hljs-comment\"># save the model<\/span>\n            self.model.email = email\n            self.model.save()\n\n            <span class=\"hljs-comment\"># show a success message<\/span>\n            self.view.show_success(<span class=\"hljs-string\">f'The email <span class=\"hljs-subst\">{email}<\/span> saved!'<\/span>)\n\n        <span class=\"hljs-keyword\">except<\/span> ValueError <span class=\"hljs-keyword\">as<\/span> error:\n            <span class=\"hljs-comment\"># show an error message<\/span>\n            self.view.show_error(error)        \n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">App<\/span><span class=\"hljs-params\">(tk.Tk)<\/span>:<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        super().__init__()\n\n        self.title(<span class=\"hljs-string\">'Tkinter MVC Demo'<\/span>)\n\n        <span class=\"hljs-comment\"># create a model<\/span>\n        model = Model(<span class=\"hljs-string\">'hello@pythontutorial.net'<\/span>)\n\n        <span class=\"hljs-comment\"># create a view and place it on the root window<\/span>\n        view = View(self)\n        view.grid(row=<span class=\"hljs-number\">0<\/span>, column=<span class=\"hljs-number\">0<\/span>, padx=<span class=\"hljs-number\">10<\/span>, pady=<span class=\"hljs-number\">10<\/span>)\n\n        <span class=\"hljs-comment\"># create a controller<\/span>\n        controller = Controller(model, view)\n\n        <span class=\"hljs-comment\"># set the controller to view<\/span>\n        view.set_controller(controller)\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">'__main__'<\/span>:\n    app = App()\n    app.mainloop()            <\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id='summary'>Summary <a href=\"#summary\" class=\"anchor\" id=\"summary\" title=\"Anchor for Summary\">#<\/a><\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Use MVC to structure the Tkinter applications to make them more organized.<\/li><\/ul>\n<div class=\"helpful-block-content\" data-title=\"\">\n\t<header>\n\t\t<div class=\"wth-question\">Was this tutorial helpful ?<\/div>\n\t\t<div class=\"wth-thumbs\">\n\t\t\t<button\n\t\t\t\tdata-post=\"2489\"\n\t\t\t\tdata-post-url=\"https:\/\/www.pythontutorial.net\/tkinter\/tkinter-mvc\/\"\n\t\t\t\tdata-post-title=\"Tkinter MVC\"\n\t\t\t\tdata-response=\"1\"\n\t\t\t\tclass=\"wth-btn-rounded wth-yes-btn\"\n\t\t\t>\n\t\t\t\t<svg\n\t\t\t\t\txmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\tclass=\"feather feather-thumbs-up block w-full h-full\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3\"\n\t\t\t\t\t><\/path>\n\t\t\t\t<\/svg>\n\t\t\t\t<span class=\"sr-only\"> Yes <\/span>\n\t\t\t<\/button>\n\n\t\t\t<button\n\t\t\t\tdata-response=\"0\"\n\t\t\t\tdata-post=\"2489\"\n\t\t\t\tdata-post-url=\"https:\/\/www.pythontutorial.net\/tkinter\/tkinter-mvc\/\"\n\t\t\t\tdata-post-title=\"Tkinter MVC\"\n\t\t\t\tclass=\"wth-btn-rounded wth-no-btn\"\n\t\t\t>\n\t\t\t\t<svg\n\t\t\t\t\txmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17\"\n\t\t\t\t\t><\/path>\n\t\t\t\t<\/svg>\n\t\t\t\t<span class=\"sr-only\"> No <\/span>\n\t\t\t<\/button>\n\t\t<\/div>\n\t<\/header>\n\n\t<div class=\"wth-form hidden\">\n\t\t<div class=\"wth-form-wrapper\">\n\t\t\t<div class=\"wth-title\"><\/div>\n\t\t\t<textarea class=\"wth-message\"><\/textarea>\n\t\t\t<input type=\"button\" name=\"wth-submit\" class=\"wth-btn wth-btn-submit\" id=\"wth-submit\" \/>\n\t\t\t<input type=\"button\" class=\"wth-btn wth-btn-cancel\" value=\"Cancel\" \/>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Summary: in this tutorial, you&#8217;ll learn how to structure a Tkinter application using the model-view-controller (MVC) pattern. Introduction to Tkinter MVC # As your application grows, its complexity also increases. To make the application more manageable, you can use the model-view-controller design pattern. The MVC design pattern allows you to divide the application into three [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":1232,"menu_order":58,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2489","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages\/2489","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/comments?post=2489"}],"version-history":[{"count":0,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages\/2489\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/pages\/1232"}],"wp:attachment":[{"href":"https:\/\/www.pythontutorial.net\/wp-json\/wp\/v2\/media?parent=2489"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}