更新进度条——MVP模式
Update progress bar - MVP pattern
我正在研究MVP模式,但很难遵循这些原则来实时更新进度条。据我了解,Presenter 检查模型中是否有任何更新,然后输出结果,因此模型中没有 Presenter 的实例化,只有 Presenter 应该实例化模型和视图。
我的问题是:如何按照MVP原则更新进度条?
我当然可以从 Model 中调用 presenter.update_progress_bar(i, total),但这会违反 MVP 原则。
这是一个最小的工作示例:
PS:目前,我正在使用 CLI。
/main.py
import modules
def main():
modules.View(modules.Presenter).run()
if __name__ == "__main__":
main()
/modules/__init__.py
from modules.Model.Model import Model
from modules.Model.progressbar import ProgressBar
from modules.View.View import View
from modules.Presenter.Presenter import Presenter
/modules/Model/Model.py
class Model:
def __init__(self):
pass
def long_process(self):
import time
for i in range(10):
time.sleep(0.1)
print("Update the progress bar.")
return True
/modules/Model/progressbar.py
# MIT license: https://gist.github.com/vladignatyev/06860ec2040cb497f0f3
import sys
class ProgressBar:
def progress(count, total, status=''):
bar_len = 60
filled_len = int(round(bar_len * count / float(total)))
percents = round(100.0 * count / float(total), 1)
bar = '=' * filled_len + '-' * (bar_len - filled_len)
sys.stdout.write('[%s] %s%s ...%s\r' % (bar, percents, '%', status))
sys.stdout.flush()
/modules/View/View.py
import sys
class View:
def __init__(self, presenter):
self.presenter = presenter(self)
def run(self):
self.presenter.long_process()
def update_progress_bar(self, msg):
sys.stdout.write(msg)
def hide_progress_bar(self, msg):
sys.stdout.write(msg)
def update_status(self, msg):
print(msg)
/modules/Presenter/Presenter.py
class Presenter:
def __init__(self, view):
import modules
self.model = modules.Model()
self.view = view
def long_process(self):
if self.model.long_process():
self.view.update_status('Long process finished correctly')
else:
self.view.update_status('error')
def update_progress_bar(self, i, total):
from modules import ProgressBar
ProgressBar.progress(i, total)
self.view.update_progress_bar(ProgressBar.progress(i, total))
def end_progress_bar(self):
self.view.end_progress_bar('\n')
我能做到:
class Model:
def __init__(self, presenter):
self.presenter = presenter # Violation of MVP
def long_process(self):
import time
for i in range(10):
time.sleep(0.1)
self.presenter.update_progress_bar(i, 10) # Violation of MVP
print("Update the progress bar.")
return True
但这是错误的,因为模型现在实例化了演示者。有什么建议吗?
使用回调:
import time
class Model:
def long_process(self, notify=lambda current, total: None):
for i in range(10):
time.sleep(0.1)
notify(i, 10)
return True
class Presenter:
def long_process(self):
result = self.model.long_process(lambda c, t: self.update_progress_bar(c, t)):
if result:
self.view.update_status('Long process finished correctly')
else:
self.view.update_status('error')
这使您的模型独立于客户端代码,同时仍然允许它(我指的是模型)通知它的调用者。
附带说明一下,您的代码中有很多东西完全不是 pythonic 的:
1/ 你不必将每个 class 放在一个不同的模块中(它实际上被认为是 Python 中的反模式),甚至更少的嵌套子模块(Python禅:"flat is better than nested").
2/ 当普通函数就足够时,你不必使用 classes(提示:Python 函数是对象......实际上,Python 中的所有内容都是一个对象) - 你的 ProgressBar
class 没有状态,只有一个方法,所以它可能只是一个普通函数 (Python Zen: "simple is better than complex")。
3/ imports should be at the top of the module,而不是在函数中(如果你必须将它们放在函数中以解决循环依赖问题,那么正确的解决方案是重新考虑你的设计以避免循环依赖)。
我正在研究MVP模式,但很难遵循这些原则来实时更新进度条。据我了解,Presenter 检查模型中是否有任何更新,然后输出结果,因此模型中没有 Presenter 的实例化,只有 Presenter 应该实例化模型和视图。
我的问题是:如何按照MVP原则更新进度条? 我当然可以从 Model 中调用 presenter.update_progress_bar(i, total),但这会违反 MVP 原则。
这是一个最小的工作示例:
PS:目前,我正在使用 CLI。
/main.py
import modules
def main():
modules.View(modules.Presenter).run()
if __name__ == "__main__":
main()
/modules/__init__.py
from modules.Model.Model import Model
from modules.Model.progressbar import ProgressBar
from modules.View.View import View
from modules.Presenter.Presenter import Presenter
/modules/Model/Model.py
class Model:
def __init__(self):
pass
def long_process(self):
import time
for i in range(10):
time.sleep(0.1)
print("Update the progress bar.")
return True
/modules/Model/progressbar.py
# MIT license: https://gist.github.com/vladignatyev/06860ec2040cb497f0f3
import sys
class ProgressBar:
def progress(count, total, status=''):
bar_len = 60
filled_len = int(round(bar_len * count / float(total)))
percents = round(100.0 * count / float(total), 1)
bar = '=' * filled_len + '-' * (bar_len - filled_len)
sys.stdout.write('[%s] %s%s ...%s\r' % (bar, percents, '%', status))
sys.stdout.flush()
/modules/View/View.py
import sys
class View:
def __init__(self, presenter):
self.presenter = presenter(self)
def run(self):
self.presenter.long_process()
def update_progress_bar(self, msg):
sys.stdout.write(msg)
def hide_progress_bar(self, msg):
sys.stdout.write(msg)
def update_status(self, msg):
print(msg)
/modules/Presenter/Presenter.py
class Presenter:
def __init__(self, view):
import modules
self.model = modules.Model()
self.view = view
def long_process(self):
if self.model.long_process():
self.view.update_status('Long process finished correctly')
else:
self.view.update_status('error')
def update_progress_bar(self, i, total):
from modules import ProgressBar
ProgressBar.progress(i, total)
self.view.update_progress_bar(ProgressBar.progress(i, total))
def end_progress_bar(self):
self.view.end_progress_bar('\n')
我能做到:
class Model:
def __init__(self, presenter):
self.presenter = presenter # Violation of MVP
def long_process(self):
import time
for i in range(10):
time.sleep(0.1)
self.presenter.update_progress_bar(i, 10) # Violation of MVP
print("Update the progress bar.")
return True
但这是错误的,因为模型现在实例化了演示者。有什么建议吗?
使用回调:
import time
class Model:
def long_process(self, notify=lambda current, total: None):
for i in range(10):
time.sleep(0.1)
notify(i, 10)
return True
class Presenter:
def long_process(self):
result = self.model.long_process(lambda c, t: self.update_progress_bar(c, t)):
if result:
self.view.update_status('Long process finished correctly')
else:
self.view.update_status('error')
这使您的模型独立于客户端代码,同时仍然允许它(我指的是模型)通知它的调用者。
附带说明一下,您的代码中有很多东西完全不是 pythonic 的:
1/ 你不必将每个 class 放在一个不同的模块中(它实际上被认为是 Python 中的反模式),甚至更少的嵌套子模块(Python禅:"flat is better than nested").
2/ 当普通函数就足够时,你不必使用 classes(提示:Python 函数是对象......实际上,Python 中的所有内容都是一个对象) - 你的 ProgressBar
class 没有状态,只有一个方法,所以它可能只是一个普通函数 (Python Zen: "simple is better than complex")。
3/ imports should be at the top of the module,而不是在函数中(如果你必须将它们放在函数中以解决循环依赖问题,那么正确的解决方案是重新考虑你的设计以避免循环依赖)。