{"id":13410637,"url":"https://github.com/R0land013/easy-mvp","last_synced_at":"2025-03-14T16:32:40.408Z","repository":{"id":60009175,"uuid":"502071147","full_name":"R0land013/easy-mvp","owner":"R0land013","description":"Build PyQt5 apps easily using MVP pattern","archived":false,"fork":false,"pushed_at":"2023-04-21T20:35:16.000Z","size":164,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-09-17T23:53:26.880Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/R0land013.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-10T14:21:55.000Z","updated_at":"2024-05-20T00:10:24.000Z","dependencies_parsed_at":"2024-10-26T05:51:29.358Z","dependency_job_id":"b9f89a94-b1cf-4edb-bf62-ea7304f82ce9","html_url":"https://github.com/R0land013/easy-mvp","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/R0land013%2Feasy-mvp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/R0land013%2Feasy-mvp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/R0land013%2Feasy-mvp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/R0land013%2Feasy-mvp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/R0land013","download_url":"https://codeload.github.com/R0land013/easy-mvp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243610319,"owners_count":20318941,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:01:08.130Z","updated_at":"2025-03-14T16:32:40.090Z","avatar_url":"https://github.com/R0land013.png","language":"Python","funding_links":[],"categories":["Hacks"],"sub_categories":["Telegram"],"readme":"# easy_mvp\n\n## Description\n\n**easy_mvp** is a package that makes it easy to build desktop applications\nusing **MVP** pattern, **Python** and **PyQt5**. This is achieved by using\na set of classes and practices similar to those used in the **Android framework**.\n\nThe basic idea behind this package is to open a presenter from\nanother presenter and that the new presenter fills the window for\ncomplete. This is similar to **Android applications**, which\nwhen opening a new **Activity**, they replace the old one with the new one.\nIf the Back button is pressed, then the new **Activity** is removed\nand the old one reappears on the screen. This gives the impression\nthat there is a **stack of Activity**.\n\nIn addition, **easy_mvp** offers the possibility of doing the above **in more than\na window**, since desktop applications are very different to **Android apps**.\n\n## Installation\n\nTo install this package you need to have **Python** installed in your system.\nThen you can run this command to install this package:\n\n````shell script\npip install easy_mvp\n````\n\n## Explaining the mechanism\n\nThe following explanations are reflected in ``demo.py`` program.\n\n### The application model created by **easy_mvp** is like this:\n\n![App Manager and windows](https://github.com/R0land013/easy-mvp/blob/master/readme_img/app_manager_and_windows.png?raw=true)\n\nThe **ApplicationManager** class is the **entry point** of the program. This\nclass is the one that knows the windows, creates them and destroys them. Each\nwindow holds a **stack of presenters**. In this way it is implemented\nthe same behavior that Android applications have. Class\n**Intent** is used to **transition** from one presenter to another,\nregardless of whether the new presenter is started in a new window.\n\n\n### The behavior of the windows is represented as follows:\n\n![Window with its internal stack presenter](https://github.com/R0land013/easy-mvp/blob/b2c8ba51e5315679848925967611e1e5931871dd/readme_img/window_with_its_internal_stack.png?raw=true)\n\nAs you can see the **window has a stack of presenters**, the\nwhich are attached to their views, therefore **all the\npresenters must possess a view**. Again we can see that the class\n**Intent** is the one that allows to add a new **pair of presenter-view**\nto the window stack. For this mechanism to work, the presenter\nclasses must **inherit from the AbstractPresenter** class.\n\n**AbstractPresenter** has several methods to open new presenters,\nclose the current presenter and to handle certain flows within the\napplication, such as when a presenter is going to be initialized, hidden,\nor closed.\n\n### The flow of events in a class that inherits from AbstractPresenter is like this:\n\n![flow_of_calls_on_presenter](https://github.com/R0land013/easy-mvp/blob/b2c8ba51e5315679848925967611e1e5931871dd/readme_img/flow_of_calls_on_presenter.png?raw=true)\n\nThe **_on_initialize** method is the first method called in the **AbstractPresenter** class.\nHere the instance variables of the presenter must be initialized. Also, you must\ncreate the instance of the view that corresponds to the presenter. Once created,\nthe view instance must be set with the **_set_view(view)** method, from\nthis way the view is made accessible to the rest of the classes in **easy_mvp environment**.\nThe view can be obtained with the **view** property or **get_view** method of **AbstractPresenter**, if it was set\nby **_set_view(view)** first.\n\nThe **on_view_shown** method is called just after the presenter view\nis displayed in the graphical interface. In this method the data should be loaded,\nand the tables and forms filled.\n\nIf on the presenter whose view is visible, **_open_other_presenter** is called\nthen another presenter will be created, and this presenter will go to the top of the\n**stack of presenters**, and its view will be the only one visible in the graphical interface.\nThe view of presenter that called **_open_other_presenter** will no longer be visible.\nBefore this last occurs, the calling presenter will receive control of the program\nwith the method **on_view_covered**.\n\nIf now the top presenter calls _**close_this_presenter**, then the\npresenter view just below it will be visible again, and the\npresenter will receive control of the main thread via **on_view_discovered**.\nIn this method the view should be updated due to some change in the model.\n\nWhen the presenter at the top of the stack calls **_close_this_presenter**\nreceives the control with the **on_closing_presenter** method. In this method\nresources should be released and observers removed. After the call to\n**on_closing_presenter** the presenter and the view of the presenter will be\nremoved, and it will happen what it has been explained previously.\nIt should be noted that if the presenter who closes is the\nonly one that exists in the entire application, that is, there is only one\nwindow, and it owns only a presenter then the application will close with\nthe **status code 0**. To close the program with different code, use the **exit_app**\nmethod of AbstractPresenter.\n\n### Receiving data from presenters\n\nA presenter can receive data from another presenter that it has created,\nif the second one is closed using the **_close_this_presenter_with_result(result_data: dict, result: str = Intent.NO_RESULT)**\nmethod. In this way, the presenter that is below in the stack receives control of the\nprogram via **on_view_discovered_with_result(action: str, data: dict, result: str)** instead of\n**on_view_discovered**. The action parameter is the action that was passed to the **Intent** when\nit was ordered to open the new presenter. The result_data is a **Python dictionary** that the\npresenter who was on top can pass through\n**_close_this_presenter_with_result(result_data: dict, result: str = Intent.NO_RESULT)**. The result string can be used\nto tell the below presenter what happened on top presenter. In this way the below presenter can take different decisions\ndepending on the result returned by top presenter.\n\n### Managing global data\n\nData can be created to be accessible throughout the entire application. This is\naccomplished with the **set_global_data(key: str, data)** and **get_global_data(key: str)**\nmethods of **AbstractPresenter**. You can also check if a global data exists with the method\n**has_global_data(key: str)**, which returns True if it exists and False otherwise.\n\n## Knowing Intent in depth\n\nAs explained **Intent** is what makes it possible to open new presenters. You can specify\nthat the new presenter must be opened on a new window using\n**use_new_window(use_new_window: bool = False)**. You can also specify that\nif a new window is to be opened, then make it modal or not with the method\n**use_modal(self, modal: bool = False)**.\n\nAn intent can be specified with an action with **set_action(action: str)**, like so\nthe new presenter may behave differently depending on the action received.\n\n**Intent** also allows data to be passed to a new presenter via **set_data(data: dict)**.\nTo see examples of how **Intent** is used, check out the ``demo.py`` program.\n\n## Acting when user closes window\n\nIf the user clicks the close window button then all presenters in the\npresenter stack of the window will receive the **on_window_closing(self)**\ncall. The first presenter to receive the call will be the one which is\non the stack top, the next to receive the call will be the below presenter.\nThis flow of calls will occur all the way down through the window stack.\nWhen all presenters have executed **on_window_closing(self)** then the\nwindow will be closed. If the window that is being closing has child\nwindows, then all its children will receive the **on_window_closing(self)**\ncalls in the same way the parent window received it.\n\nThis method is useful to close database connections, close files and\nstop threads. All you must close goes here.\n\n## Customize window title\n\nEvery presenter can change the window title. This is useful because each presenter will do\na different task, and therefore you would like to change the title of the window depending\non it.\n\nYou can reimplement the method **get_default_window_title(self)** to set a custom\nwindow title. You only have to set **return 'My custom window title'** in the method's\nbody. This method will be called before the presenter receives the\n***on_view_show***, ***on_view_discovered*** and ***on_view_discovered_with_result*** calls.\nBy default, this method returns the *'No Title, reimplement get_default_window_title'* string.\n\nBut if you want more power on changing the window title you can use the\n**_set_window_title(self, window_title: str)** to immediately change it. After calling\nthis method the window title can be replaced automatically if the presenter who set it is popped,\nor if a new presenter is added on top. This will happen because of the calls to\n**get_default_window_title(self)** on below presenter or on the new presenter respectively,\nduring the **easy-mvp** event flow.\n\n## Setting application name and window Icon\n\nYou can set the application name and Window Icon by passing this information to\nApplicationManager constructor:\n\n````python\nmanager = ApplicationManager(\n    app_name='easy_mvp demo',\n    window_icon_path='./demo/view/ui/assets/icon.png'\n)\n````\n\nThis way the operating system will know the name of your app and its window icon.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FR0land013%2Feasy-mvp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FR0land013%2Feasy-mvp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FR0land013%2Feasy-mvp/lists"}