MVC with tkinter (tkinter နဲ့ MVC)

IA (Internal Assessment) code ကို စီစဉ်တဲ့ နည်းလမ်း တစ်ခုပါ၊ စာမေးပွဲ ခေါင်းစဉ် မဟုတ်ပါဘူး: ဒီစာမျက်နှာက ကိုယ့် IA (IB သင်တန်းမှာ ကျောင်းသားတစ်ယောက်ချင်း ကိုယ်တိုင် တည်ဆောက်ရပြီး ကျောင်းက ဆရာက အမှတ်ပေးတဲ့ အပိုင်း) product (ကိုယ်တိုင် တည်ဆောက်ရတဲ့ program) ကို Python နဲ့ tkinter (Python install လုပ်ပုံ အများစုမှာ ပါလာတဲ့ window တည်ဆောက်တဲ့ toolkit၊ အသင့်ရေးထားတဲ့ Python class အစု) သုံးပြီး တည်ဆောက်တဲ့ ကျောင်းသားတွေအတွက်ပါ။ MVC (Model-View-Controller၊ app ကို Model၊ View၊ Controller ဆိုပြီး အပိုင်းသုံးပိုင်း ခွဲရေးတဲ့ ပုံစံ) ကို syllabus (IB သင်ရိုး) ထဲမှာ နာမည် တပ်ပြီး မဖော်ပြထားပါဘူး။ IA ကလည်း သတ်မှတ်ထားတဲ့ ဖွဲ့စည်းပုံ တစ်ခုခုကို မတောင်းပါဘူး။ MVC က user interface (UI၊ user နဲ့ program ကြားက မြင်ကွင်း၊ ဆိုလိုတာက user မြင်ရပြီး သုံးရတဲ့ screen တွေ) ပါတဲ့ program တစ်ခုကို စီစဉ်ရေးတဲ့ အသုံးများတဲ့ နည်းလမ်း တစ်ခုပါ။ ဒေတာ၊ screen တွေနဲ့ user ကို တုံ့ပြန်တဲ့ code တွေကို သီးခြား class (object တွေ ဖန်တီးဖို့ ပုံစံခွက်) တွေထဲ ခွဲထားလို့ code ကို test လုပ်ရတာ၊ ရှင်းပြရတာ၊ ပြင်ရတာ ပိုလွယ်ပါတယ်။ Modularisation (program ကို အလုပ်တစ်ခုစီ ရှိတဲ့ သီးခြား အပိုင်းငယ်တွေ ခွဲရေးခြင်း၊ IB သင်ရိုးရဲ့ B2.3.4) ရဲ့ အကျိုးကျေးဇူးတွေကို လက်တွေ့ အသုံးချထားတာပါ။ Java နဲ့ JavaFX သုံးပြီး ရေးနေသလား။ ဒီစာမျက်နှာရဲ့ Java version က MVC Architecture ပါ။


ခြုံငုံ မိတ်ဆက်

ပထမဆုံး ရေးတဲ့ tkinter program တွေမှာ အရာအားလုံးကို file တစ်ခုတည်းထဲမှာ ထည့်ရေးလေ့ ရှိပါတယ်: ဒေတာ၊ window နဲ့ button ကို နှိပ်တဲ့အခါ run တဲ့ code ပါ။ App အသေးလေး တစ်ခုအတွက်တော့ အဆင်ပြေပါတယ်။ ဒါပေမယ့် application ကြီးလာတဲ့အခါ ဒါတွေကို ရောထားရင် တခြား အပိုင်းတွေကို မထိခိုက်စေဘဲ အပိုင်း တစ်ခုကို ပြင်ဖို့ ဒါမှမဟုတ် test လုပ်ဖို့ ပိုခက်လာပါတယ်။ အဲဒီ တာဝန်တွေကို ခွဲထားရင် code က စနစ်တကျ ရှိနေပါတယ်။

Model-View-Controller (MVC) က user interface ပါတဲ့ program တွေအတွက် architecture (ဖွဲ့စည်းပုံ) တစ်ခုပါ: program ကို ဘယ်အပိုင်းတွေ ခွဲမလဲ၊ အဲဒီ အပိုင်းတွေ ဘယ်လို ပူးပေါင်း အလုပ်လုပ်မလဲ ဆိုတဲ့ အစီအစဉ် တစ်ခုပါ။ MVC ကို design pattern (ထပ်ကြုံရတဲ့ ဒီဇိုင်း ပြဿနာအတွက် ဖြေရှင်းပုံစံ) လို့လည်း မကြာခဏ ခေါ်ကြပါတယ်။ MVC က application ကို ကိုယ်ပိုင် အလုပ်စီ ရှိတဲ့ အပိုင်း သုံးပိုင်း ခွဲပေးပါတယ်။ MVC ကို နားလည်ရင် ဒီအရာတွေမှာ အထောက်အကူ ဖြစ်ပါလိမ့်မယ်:

  • ဖတ်တဲ့သူ နားလည်လွယ်တဲ့ IA code ရေးဖို့
  • ရှိပြီးသား feature (program ရဲ့ လုပ်ဆောင်ချက်) တွေ ပျက်သွားနိုင်ခြေ နည်းနည်းနဲ့ feature အသစ်တွေ ထည့်ဖို့
  • ကိုယ့် IA product ရဲ့ ဖွဲ့စည်းပုံကို ရှင်းပြဖို့: Criterion C (System overview၊ IA အမှတ်ပေး စံနှုန်း C: system တစ်ခုလုံးကို ခြုံငုံ ဖော်ပြခြင်း) က အဓိက component (အစိတ်အပိုင်း) တွေနဲ့ သူတို့ အချင်းချင်း ဆက်သွယ် အလုပ်လုပ်ပုံကို ပြတဲ့ system model (system ရဲ့ ဖွဲ့စည်းပုံကို ပြတဲ့ ပုံ ဒါမှမဟုတ် ဖော်ပြချက်) တစ်ခုကို တောင်းပါတယ်။ Criterion D (Development၊ IA အမှတ်ပေး စံနှုန်း D: product ကို တည်ဆောက်ခြင်း) က product ရဲ့ ဖွဲ့စည်းပုံကို justify (ဘာကြောင့် ရွေးတာလဲ အကြောင်းပြချက်နဲ့ ပြ) လုပ်ဖို့ တောင်းပါတယ်

ဒီစာမျက်နှာမှာ Student Manager ဆိုတဲ့ app အသေးလေး တစ်ခုကို file လေးခုနဲ့ တည်ဆောက်ပါမယ်: models.py၊ views.py၊ controller.py နဲ့ main.py။ ပြီးရင် screen အများကြီး ပါတဲ့ ပိုကြီးတဲ့ app တစ်ခုက controller တစ်ခုတည်းကို ဘယ်လို မျှသုံးနိုင်လဲ ပြပါမယ်။

မစခင် ကိုယ့် Python မှာ tkinter ပါမပါ စစ်ပါ။ Python ထဲမှာ import tkinter ကို run ကြည့်ပါ: error မပေါ်ရင် ပါပါတယ်။ Windows နဲ့ macOS အတွက် python.org ရဲ့ installer (software ကို ကွန်ပျူတာပေါ် ထည့်သွင်းပေးတဲ့ program) တွေမှာ tkinter ပါပြီးသားပါ။ အဲဒီ system တွေပေါ်မှာ Thonny editor (code ရေးတဲ့ software) ရဲ့ ကိုယ်ပိုင် installer မှာလည်း ပါပါတယ်။ တချို့ install လုပ်ပုံတွေမှာတော့ မပါပါဘူး: Mac ပေါ်မှာ Homebrew (Mac ပေါ်မှာ software install လုပ်တဲ့ tool) ကနေ ထည့်ထားတဲ့ Python အတွက် python-tk package (ထပ်ပြီး install လုပ်ရတဲ့ software အစု) ကို သီးခြား ထည့်ဖို့ လိုပါတယ် (brew install python-tk)။ Linux ပေါ်မှာလည်း tkinter က များသောအားဖြင့် သီးခြား package တစ်ခုပါ (ဥပမာ python3-tk)။ “No module named ‘_tkinter’” (‘_tkinter’ ဆိုတဲ့ module မရှိပါ) ဆိုတဲ့ error ကို မြင်ရရင် အဲဒီ package ကို install လုပ်ပါ၊ ဒါမှမဟုတ် Python ကို python.org ကနေ install လုပ်ပါ။


အစိတ်အပိုင်း သုံးခု

Model: ဒေတာ

Model က application ရဲ့ ဒေတာနဲ့ business logic (ဒေတာ လိုက်နာရမယ့် စည်းမျဉ်းတွေနဲ့ ဒေတာကို သုံးပြီး လုပ်တဲ့ တွက်ချက်မှုတွေ) ကို သိမ်းထားပါတယ်။ Model က user interface အကြောင်း ဘာမှ မသိပါဘူး: button တွေ၊ text box တွေ ဒါမှမဟုတ် window တွေ ရှိမှန်းတောင် မသိပါဘူး။

တာဝန်များ:

  • ဒေတာ သိမ်းခြင်း: attribute (object ရဲ့ ဒေတာကို သိမ်းတဲ့ variable) တွေ၊ list တွေ
  • ဒေတာက စည်းမျဉ်းတွေနဲ့ ကိုက်ညီမညီ စစ်ခြင်း (ဥပမာ grade (အမှတ်) က 0 ကနေ 100 အထိ ဖြစ်ရမယ်)
  • တွက်ချက်မှုတွေနဲ့ operation (ဒေတာပေါ်မှာ လုပ်တဲ့ လုပ်ဆောင်ချက်) တွေ လုပ်ခြင်း
  • ဒေတာကို file တွေထဲ ရေးခြင်း၊ file တွေကနေ ဖတ်ခြင်း (File I/O in Apps ကို ကြည့်ပါ)

ဥပမာ: နာမည်၊ grade နဲ့ validation (စည်းမျဉ်းကို ချိုးဖောက်တဲ့ ဒေတာကို လက်မခံအောင် စစ်တာ) ပါတဲ့ Student class တစ်ခု။ ဒီ class က models.py ထဲမှာ ရှိပါတယ်။

class Student():
    def __init__(self, name, grade):
        if name == None or name.strip() == "":
            raise ValueError("Name cannot be empty")
        if grade < 0 or grade > 100:
            raise ValueError("Grade must be 0-100")
        self._name = name      # one underscore: private by agreement
        self._grade = grade

    def get_name(self):
        return self._name

    def get_grade(self):
        return self._grade

    def set_grade(self, grade):
        if grade < 0 or grade > 100:
            raise ValueError("Grade must be 0-100")
        self._grade = grade

    def __str__(self):    # __str__ is Python's toString()
        return self._name + " (" + str(self._grade) + ")"

raise ValueError(...) က constructor (object ဖန်တီးချိန် attribute တွေကို စတင် သတ်မှတ်ပေးတဲ့ method) ဒါမှမဟုတ် method (သီးခြား အလုပ်တစ်ခု လုပ်ပေးတဲ့ code အပိုင်း) ကို ရပ်လိုက်ပြီး exception (program run နေစဉ် ဖြစ်တဲ့ အမှား၊ ဘာမှားသွားလဲ ဖော်ပြတဲ့ object တစ်ခု) ကို ခေါ်ခဲ့တဲ့ code ဆီ ပြန်ပို့ပါတယ်။ အောက်က Controller က အဲဒီ exception ကို try/except နဲ့ ဖမ်းပြီး message ကို user ကို ပြပါတယ်။ Syllabus မှာ try၊ except နဲ့ finally ကို ဖော်ပြထားပေမယ့် raise ကို မဖော်ပြထားလို့ ကိုယ်ပိုင် exception တွေ raise (exception ဖြစ်ကြောင်း အချက်ပြ) လုပ်တာကို IA အတွက် tool တစ်ခုအဖြစ် သဘောထားပါ။

Collection Model (ဒေတာအစုကို စီမံတဲ့ Model)

IA project အများအပြားမှာ model object (class တစ်ခုကနေ ဖန်တီးထားတဲ့ instance) တွေရဲ့ collection (ဒေတာအစု) ကို စီမံတဲ့ class တစ်ခုလည်း လိုပါတယ်: ထည့်ခြင်း၊ ဖယ်ခြင်း၊ ရှာခြင်းနဲ့ စီခြင်း။ ဒီ class လည်း models.py ထဲမှာ ရှိပါတယ်။

class StudentList():
    def __init__(self):
        self._students = []

    def add_student(self, s):
        self._students.append(s)

    def remove_by_name(self, name):
        for i in range(len(self._students)):
            if self._students[i].get_name() == name:
                self._students.pop(i)
                return True
        return False

    def find_by_name(self, name):
        for i in range(len(self._students)):
            if self._students[i].get_name() == name:
                return self._students[i]
        return None

    def get_all(self):
        return self._students

    def size(self):
        return len(self._students)

ဒီ design မှာ models.py က tkinter ကို import (တခြားနေရာက code ကို ယူသုံးမယ်လို့ ကြေညာခြင်း) မလုပ်ပါဘူး။ Model class တစ်ခုက window၊ button ဒါမှမဟုတ် message box (message ပြတဲ့ pop-up window) ကို ဖန်တီးနေရင် တစ်ခုခု နေရာမှားနေပါပြီ။ Window မလိုတဲ့ Model ကို သီးခြား test လုပ်လို့ ရပါတယ်: StudentList တစ်ခု ဖန်တီး၊ ကျောင်းသားတွေ ထည့်၊ တစ်ယောက်ကို ရှာပြီး ရလဒ်တွေကို စစ်ပါ၊ ဒါအားလုံးကို window မဖွင့်ဘဲ လုပ်လို့ ရပါတယ်။

Underscore တစ်ခုလား၊ နှစ်ခုလား။ ဒီစာမျက်နှာက class တွေက attribute တွေကို private (class အပြင်ကနေ မသုံးသင့်တဲ့) အဖြစ် underscore (_) တစ်ခုနဲ့ မှတ်ထားပါတယ် (self._name)။ ဒါက ဒီ site ရဲ့ Python code တွေမှာ သုံးတဲ့ convention (အများ လိုက်နာတဲ့ ရေးထုံး) ပါ။ Python က တခြား code တွေ _name ကို သုံးတာကို မတားပါဘူး: underscore က programmer တွေကြားက သဘောတူညီချက် တစ်ခုပဲ ဖြစ်ပြီး တခြား class တွေက get method (private attribute ရဲ့ တန်ဖိုးကို ပြန်ပေးတဲ့ method) တွေကို သုံးသင့်ပါတယ်။ တချို့ starter project (စတင်ဖို့ အသင့်ပေးထားတဲ့ project) တွေမှာ အစမှာ underscore နှစ်ခု (self.__name) ကို သုံးကြပါတယ်။ Student class ထဲမှာ Python က အဲဒီ attribute ကို _Student__name လို့ နာမည်ပြောင်းပါတယ်။ ဒီ feature ကို name mangling (နာမည်ကို အလိုအလျောက် ပြောင်းပေးခြင်း) လို့ ခေါ်ပြီး subclass (အမွေဆက်ခံတဲ့ child class) ဒါမှမဟုတ် တခြား code က မတော်တဆ သုံးမိဖို့ နည်းသွားအောင် လုပ်ပေးပါတယ်။ ဒါပေမယ့် Java ရဲ့ private လို သုံးခွင့်ကို ကန့်သတ်တာ မဟုတ်ပါဘူး: နာမည်အသစ်ကို သုံးတဲ့ code က အဲဒီ attribute ကို ရောက်နိုင်ပါသေးတယ်။ ဘယ်လိုပဲ ဖြစ်ဖြစ် တခြား class တွေက get method တွေကိုပဲ သုံးသင့်ပါတယ်။ (__init__ နဲ့ __str__ လို အစနဲ့ အဆုံး နှစ်ဘက်လုံးမှာ underscore နှစ်ခုစီ ပါတဲ့ နာမည်တွေက Python ရဲ့ special method (အထူး method) တွေ ဖြစ်ပြီး နာမည် မပြောင်းပါဘူး။)

View: ပြသတဲ့ အပိုင်း

View က user မြင်ရပြီး ထိတွေ့ သုံးရတဲ့ အပိုင်းပါ။ tkinter မှာဆိုရင် widget (button တွေ၊ text box တွေ၊ label တွေ၊ list တွေလို control တွေ) ပါတဲ့ window နဲ့ အဲဒီ widget တွေကို စီစဉ်ပေးတဲ့ layout (screen ပေါ်မှာ စီစဉ်ထားပုံ) ပါ။ View ကို တတ်နိုင်သမျှ ရိုးရှင်းအောင် ထားပါ: View က ဒေတာကို ပြပြီး user ရဲ့ လုပ်ဆောင်ချက်တွေကို Controller ဆီ လွှဲပေးပါတယ်၊ ဒါပေမယ့် အဲဒီ လုပ်ဆောင်ချက်တွေနဲ့ ဘာလုပ်မလဲ ဆိုတာကို မဆုံးဖြတ်ပါဘူး။ ဒေတာနဲ့ ဆိုင်တဲ့ စည်းမျဉ်းတွေက Model ထဲမှာ ရှိရပါမယ်။

တာဝန်များ:

  • Widget တွေကို ဖန်တီးပြီး စီစဉ်ခြင်း
  • Controller ပေးတဲ့ ဒေတာကို ပြခြင်း
  • User ရဲ့ လုပ်ဆောင်ချက်တွေ (button နှိပ်တာ၊ စာရိုက်ထည့်တာ) ကို Controller ဆီ အသိပေးခြင်း

Controller: ကြားက ညှိပေးတဲ့ အပိုင်း

Controller က Model နဲ့ View ကြားက ညှိပေးသူ (coordinator) ပါ။ User ရဲ့ လုပ်ဆောင်ချက်တွေကို ကိုင်တွယ်ပြီး Model ကို update လုပ်ကာ View ကို ဘာပြရမလဲ ပြောပါတယ်။

တာဝန်များ:

  • View ကနေ လာတဲ့ user ရဲ့ လုပ်ဆောင်ချက်တွေကို တုံ့ပြန်ခြင်း
  • ဒေတာကို ထည့်ဖို့၊ ဖယ်ဖို့ ဒါမှမဟုတ် ပြင်ဖို့ Model ရဲ့ method တွေကို ခေါ်ခြင်း
  • View က Model ရဲ့ လက်ရှိ ဒေတာကို ပြအောင် View ကို update လုပ်ခြင်း
  • Error တွေကို ဖမ်းပြီး user ကို ရှင်းလင်းတဲ့ message ပြခြင်း

အစိတ်အပိုင်းတွေ ဆက်သွယ် အလုပ်လုပ်ပုံ

flowchart LR
    V["View<br>(UI)"] -- "user action" --> C["Controller<br>(coordinator)"]
    C -- "update data" --> M["Model<br>(data and rules)"]
    M -- "return data" --> C
    C -- "update display" --> V

ပုံထဲက စာတွေရဲ့ အဓိပ္ပာယ်: View (UI) က user action (user ရဲ့ လုပ်ဆောင်ချက်) ကို Controller (coordinator၊ ညှိပေးသူ) ဆီ ပို့ပါတယ်။ Controller က Model (data and rules၊ ဒေတာနဲ့ စည်းမျဉ်းများ) ထဲက ဒေတာကို update data (ဒေတာကို update လုပ်) ပြီး Model က return data (ဒေတာကို ပြန်ပေး) ပါတယ်။ ပြီးရင် Controller က View ကို update display (ပြတာကို update လုပ်) ပါတယ်။

  1. User က View ထဲက button တစ်ခုကို နှိပ်ပါတယ်
  2. Controller က event (ဖြစ်ရပ်) ကို ကိုင်တွယ်ပါတယ်: View ကနေ input (program ထဲ ထည့်ပေးတဲ့ ဒေတာ) ကို ဖတ်ပြီး သုံးလို့ ရမရ စစ်ပါတယ် (ဥပမာ grade က ကိန်းပြည့် ဟုတ်မဟုတ်)
  3. Controller က Model ကို update လုပ်ပါတယ်: ကျောင်းသား တစ်ယောက် ထည့်တာ၊ grade ပြောင်းတာ စသဖြင့် လုပ်ပါတယ်။ Model က ကိုယ့်စည်းမျဉ်းတွေကို ကိုယ်တိုင် စစ်ပါတယ် (grade က 0 ကနေ 100 အထိ)
  4. Controller က View ကို update လုပ်ပါတယ်: list ကို refresh (အသစ် ပြန်ပြ) လုပ်ပြီး message ပြပါတယ်

ဒီ design မှာ Model နဲ့ View က တိုက်ရိုက် ဆက်သွယ်ကြတာ မရှိပါဘူး။ ဆက်သွယ်မှု အားလုံးက Controller ကနေ ဖြတ်သွားပါတယ်။

MVC မှာ ပုံစံ အမျိုးမျိုး ရှိပါတယ်။ ဒီစာမျက်နှာက ပုံစံမှာ View က passive (ကိုယ်တိုင် ဘာမှ မလုပ်ဘဲ ပေးတာကိုပဲ ပြတဲ့) ဖြစ်ပါတယ်: View က Model ကို မဖတ်ပါဘူး၊ Controller က ပြရမယ့် စာသားကို View ကို ပေးပါတယ်။ တခြား ပုံစံတွေကို MVC Architecture: Versions of MVC (MVC ရဲ့ ပုံစံ အမျိုးမျိုး) မှာ ဖော်ပြထားပါတယ်။ အဲဒီထဲမှာ HL (Higher Level: IB သင်တန်းမှာ ပိုနက်နက်ရှိုင်းရှိုင်း လေ့လာရတဲ့ အဆင့်) syllabus ထဲက observer pattern (state ပြောင်းရင် အလိုအလျောက် အသိပေးတဲ့ ပုံစံ) လည်း ပါပါတယ်။


MVC ကို လက်တွေ့ သုံးခြင်း: Student Manager

Model class တွေကို အပေါ်မှာ ပြခဲ့ပြီးပါပြီ။ အောက်မှာ View၊ Controller နဲ့ app ကို စတင်ပြီး အပိုင်းတွေကို ချိတ်ဆက်ပေးတဲ့ file အတိုလေးကို ပြထားပါတယ်။

View

import tkinter as tk
from tkinter import messagebox


class StudentView():
    def __init__(self, window):
        # Create the widgets, so the Controller can connect to them straight away
        self._name_entry = tk.Entry(window, width=30)
        self._grade_entry = tk.Entry(window, width=30)
        self._add_button = tk.Button(window, text="Add")
        self._remove_button = tk.Button(window, text="Remove")
        self._search_button = tk.Button(window, text="Search")
        self._status = tk.Label(window, text="Ready")
        self._display = tk.Listbox(window, width=40, height=12)

        # Arrange them in a grid of rows and columns
        tk.Label(window, text="Name").grid(row=0, column=0, sticky="w", padx=8, pady=4)
        self._name_entry.grid(row=0, column=1, columnspan=3, sticky="we", padx=8, pady=4)
        tk.Label(window, text="Grade (0-100)").grid(row=1, column=0, sticky="w", padx=8, pady=4)
        self._grade_entry.grid(row=1, column=1, columnspan=3, sticky="we", padx=8, pady=4)
        self._add_button.grid(row=2, column=1, pady=4)
        self._remove_button.grid(row=2, column=2, pady=4)
        self._search_button.grid(row=2, column=3, pady=4)
        self._status.grid(row=3, column=0, columnspan=4, sticky="w", padx=8)
        self._display.grid(row=4, column=0, columnspan=4, sticky="nsew", padx=8, pady=8)

    # The Controller says which of its methods each button runs
    def set_handlers(self, add, remove, search):
        self._add_button.config(command=add)
        self._remove_button.config(command=remove)
        self._search_button.config(command=search)

    # What the user typed
    def get_name(self):
        return self._name_entry.get().strip()

    def get_grade_text(self):
        return self._grade_entry.get().strip()

    # What the Controller asks the View to show
    def clear_inputs(self):
        self._name_entry.delete(0, tk.END)
        self._grade_entry.delete(0, tk.END)

    def show_status(self, text):
        self._status.config(text=text)

    def show_students(self, lines):
        self._display.delete(0, tk.END)
        for i in range(len(lines)):
            self._display.insert(tk.END, lines[i])

    def confirm(self, question):
        return messagebox.askyesno("Confirm", question)

သတိပြုကြည့်ပါ: View က widget တွေကို ဖန်တီးပြီး ရိုးရှင်းတဲ့ method တွေ (ရိုက်ထည့်ထားတာကို ယူတာ၊ message ပြတာ၊ list ပြတာ၊ yes ဒါမှမဟုတ် no မေးခွန်း မေးတာ) ပေးထားပေမယ့် ဘာမှ မဆုံးဖြတ်ပါဘူး။ Button ကို နှိပ်တဲ့အခါ ဘာဖြစ်မလဲ ဆိုတာ View က မသိပါဘူး: set_handlers က Controller ကို ရွေးခွင့် ပေးပါတယ်။ Main window (အဓိက window) ကို ဖန်တီးပြီး mainloop() ကို စတင်ပေးတဲ့ main.py ကလွဲရင် app ထဲက tkinter ခေါ်မှု အားလုံးက ဒီ file ထဲမှာပဲ ရှိပါတယ်၊ ဒါကြောင့် Model နဲ့ Controller က tkinter ကနေ ဘာမှ import မလုပ်ပါဘူး။

command=add က နှိပ်တိုင်း run ဖို့ method ကိုယ်တိုင်ကို button ကို ပေးလိုက်တာပါ။ Method ရဲ့ နာမည်ကို ကွင်း မပါဘဲ ရေးပါ: command=self.handle_add() လို့ ရေးရင် window ကို တည်ဆောက်နေတုန်းမှာ method က တစ်ကြိမ် run သွားပြီး button ကို method ရဲ့ ရလဒ် (None) ကို ပေးလိုက်မိပါတယ်၊ ဒါကြောင့် နောက်ပိုင်း နှိပ်တဲ့အခါ ဘာမှ မဖြစ်တော့ပါဘူး။

Controller

from models import Student


class StudentController():
    def __init__(self, model, view):
        self._model = model
        self._view = view
        view.set_handlers(self.handle_add, self.handle_remove, self.handle_search)
        self.refresh_display()

    def handle_add(self):
        name = self._view.get_name()
        grade_text = self._view.get_grade_text()

        if name == "":
            self._view.show_status("Error: Name cannot be empty.")
            return

        try:
            grade = int(grade_text)
        except ValueError:
            self._view.show_status("Error: Grade must be a whole number.")
            return

        try:
            s = Student(name, grade)
        except ValueError as error:
            self._view.show_status("Error: " + str(error))
            return

        self._model.add_student(s)
        self._view.show_status("Added: " + str(s))
        self._view.clear_inputs()
        self.refresh_display()

    def handle_remove(self):
        name = self._view.get_name()
        if name == "":
            self._view.show_status("Error: Enter a name to remove.")
            return

        if self._view.confirm("Remove " + name + "?") == True:
            if self._model.remove_by_name(name) == True:
                self._view.show_status("Removed: " + name)
                self._view.clear_inputs()
                self.refresh_display()
            else:
                self._view.show_status("Not found: " + name)

    def handle_search(self):
        name = self._view.get_name()
        if name == "":
            self._view.show_status("Error: Enter a name to search.")
            return

        found = self._model.find_by_name(name)
        if found != None:
            self._view.show_status("Found: " + str(found))
        else:
            self._view.show_status("Not found: " + name)

    def refresh_display(self):
        students = self._model.get_all()
        lines = []
        for i in range(len(students)):
            lines.append(str(i + 1) + ". " + str(students[i]))
        self._view.show_students(lines)

Controller က input ကို အဆင့် နှစ်ဆင့်နဲ့ စစ်ပါတယ်။ ကိန်းပြည့် မဟုတ်တဲ့ စာသားကို int(grade_text) က ValueError raise လုပ်တဲ့အခါ ဖမ်းမိပါတယ်။ 0 ကနေ 100 အပြင်ဘက်က ကိန်းပြည့်ကိုတော့ Student constructor က လက်မခံဘဲ Controller က အဲဒီ message ကို ပြပါတယ်။ Controller က View ရဲ့ method တွေကိုပဲ သုံးလို့ window မဖွင့်ဘဲ Controller ကိုလည်း test လုပ်လို့ ရပါတယ်: View အစား method တူတွေ (get_name၊ show_status နဲ့ ကျန်တဲ့ method တွေ) ပါတဲ့ အစားထိုး object (stand-in object) တစ်ခုကို ပေးပြီး handle_add() ကို ခေါ်ကာ အဲဒီ object ကို ဘာပြခိုင်းခဲ့လဲ စစ်ပါ။

App ကို စတင်ခြင်း (အပိုင်းတွေကို ချိတ်ဆက်ခြင်း)

main.py က window၊ Model၊ View နဲ့ Controller ကို ဖန်တီးပြီး ချိတ်ဆက်ကာ tkinter ရဲ့ event loop (click တွေ၊ key နှိပ်တာတွေကို စောင့်ပြီး ကိုက်ညီတဲ့ handler တွေကို run ပေးတဲ့ loop) ကို စတင်ပါတယ်။

import tkinter as tk
from models import StudentList
from views import StudentView
from controller import StudentController

window = tk.Tk()
window.title("Student Manager")

model = StudentList()
view = StudentView(window)
controller = StudentController(model, view)

window.mainloop()

ဒီ file က သေးသေးလေးပဲ ရှိပါတယ်။ Application ရဲ့ ကျန်တဲ့ အလုပ်တွေက component သုံးခုထဲမှာ ဖြစ်ပါတယ်။ အစဉ်က အရေးကြီးပါတယ်: Controller က ဖန်တီးလိုက်တာနဲ့ Model ကော View ကော နှစ်ခုလုံးကို ချက်ချင်း သုံးလို့ Model နဲ့ View ကို အရင် ဖန်တီးပါတယ်။ window.mainloop() က နောက်ဆုံးမှာ လာပါတယ်: user က window ကို ပိတ်တဲ့အထိ window ကို ဖွင့်ထားပြီး တုံ့ပြန်နေအောင် လုပ်ပေးလို့ သူ့နောက်မှာ ရေးထားတဲ့ code က window ပိတ်မှပဲ run ပါတယ်။


Screen တိုင်းအတွက် Controller တစ်ခုတည်း

Student Manager မှာ window တစ်ခုပဲ ရှိလို့ main.py က Model နဲ့ View ကို Controller ဆီ တိုက်ရိုက် ပေးလို့ ရပါတယ်။ IA product တစ်ခုမှာ screen အများကြီး (login screen၊ main window၊ record (အချက်အလက် အများကြီး စုထားတဲ့ ဒေတာ တစ်ခု) အသစ် ထည့်တဲ့ window) ရှိလေ့ ရှိပြီး အဲဒီ screen အားလုံးက ဒေတာ အတူတူကို လိုပါတယ်။ အဲဒီ ဒေတာကို ပေးနိုင်တဲ့ နည်းလမ်း တစ်ခုက screen တိုင်း ရောက်နိုင်တဲ့ မျှသုံး controller တစ်ခုတည်း ထားတာပါ။ အဲဒီ controller ကို singleton (instance တစ်ခုတည်းပဲ ရှိတဲ့ class ပုံစံ) အဖြစ် တည်ဆောက်ပါတယ်: ကိုယ့်ရဲ့ object တစ်ခုတည်းကိုပဲ ဖန်တီးခွင့်ပြုတဲ့ class ပါ။

from models import StudentList


class AppController():
    _instance = None    # the one shared AppController, once it exists

    @staticmethod
    def get_instance():
        if AppController._instance == None:
            AppController()    # the constructor records itself as the one instance
        return AppController._instance

    def __init__(self):
        if AppController._instance != None:
            raise RuntimeError("Use AppController.get_instance(), not AppController()")
        AppController._instance = self
        self._students = StudentList()    # the data every screen shares
        self._current_user = None

    def get_students(self):
        return self._students

    def get_current_user(self):
        return self._current_user

    def set_current_user(self, name):
        self._current_user = name

ဘယ် screen မဆို AppController.get_instance() ကို ခေါ်ပြီး controller အတူတူကို ရပါတယ်:

controller = AppController.get_instance()
controller.set_current_user("Sam")                 # on the login screen
students = AppController.get_instance().get_students()   # on any other screen

အလုပ်လုပ်ပုံ:

  • _instance က class attribute (class တစ်ခုလုံး မျှသုံးတဲ့ တန်ဖိုး တစ်ခု၊ Java ရဲ့ static field လိုပါပဲ) တစ်ခုပါ။ None နဲ့ စပြီး နောက်မှ controller တစ်ခုတည်းကို သိမ်းထားပါတယ်။
  • get_instance() ကို ပထမဆုံး ခေါ်တဲ့အခါ controller ကို ဖန်တီးပါတယ်။ နောက်ပိုင်း ခေါ်တိုင်း အဲဒီ object အတူတူကိုပဲ ပြန်ပေးလို့ screen တိုင်းက ဒေတာ အတူတူနဲ့ အလုပ်လုပ်ပါတယ်။
  • Java မှာ private constructor နဲ့ ဒုတိယ object ဖန်တီးတာကို တားပါတယ်။ Python မှာ private constructor မရှိလို့ constructor က error raise လုပ်ပြီး ဒုတိယ object ကို လက်မခံပါဘူး၊ controller ကို ယူဖို့ get_instance() ကို သုံးရပါတယ်။
  • @staticmethod က get_instance() ကို object မလိုဘဲ၊ self မပါဘဲ class ကိုယ်တိုင်ပေါ်မှာ ခေါ်ရတဲ့ method အဖြစ် မှတ်ပေးပါတယ်။ @ line (decorator၊ method ကို အထူး အဓိပ္ပာယ် ထပ်ပေးတဲ့ အမှတ်အသား) က စာမေးပွဲ ပုံစံ မေးခွန်းတွေမှာ သုံးတဲ့ Python အစိတ်အပိုင်းထဲမှာ မပါလို့ IA အတွက် tool တစ်ခုအဖြစ် သဘောထားပါ။

Screen တိုင်း လိုတဲ့ ဒေတာအတွက် သုံးပါ၊ ဥပမာ login ဝင်ထားတဲ့ user ဒါမှမဟုတ် screen တိုင်း သုံးတဲ့ list ပါ။ Singleton က မျှသုံးတဲ့ global state (program တစ်ခုလုံးက ရောက်နိုင်တဲ့ ဒေတာ) ဖြစ်ပါတယ်: အရာအားလုံးကို အဲဒီထဲ ထည့်ရင် screen တွေကို နားလည်ရ ခက်လာပါတယ်။ ဒေတာ အများစုကို Student Manager မှာလိုပဲ Controller ကနေ ဆက်ပြီး ပို့လို့ ရပါတယ်။ Window တွေ ဘယ်လို ပွင့်လာလဲ၊ ဒေတာ ဘယ်လို ပေးလဲ၊ အဖြေတွေကို ဘယ်လို ပြန်ပို့လဲ ဆိုတာကို Multi-Window Apps with tkinter မှာ ပြထားပါတယ်။

IB CS (နိုင်ငံတကာ ကျောင်းတွေမှာ သင်တဲ့ ကွန်ပျူတာသိပ္ပံ သင်ရိုး) သင်ယူသူတွေအတွက် မှတ်ချက်: singleton က HL syllabus ထဲက design pattern တွေထဲက တစ်ခုပါ (B3.2.5)။ စာမေးပွဲမှာ singleton ကို ရေးခိုင်းတာ မဟုတ်ဘဲ ဘာလုပ်လဲ၊ ဘယ်အချိန်မှာ အသုံးဝင်လဲ ဆိုတာကို ရှင်းပြနိုင်ဖို့ မျှော်လင့်ပါတယ်။ SL (Standard Level: IB သင်တန်းရဲ့ အခြေခံ အဆင့်) ကျောင်းသားတွေလည်း ဒီမှာ IA ရဲ့ ဖွဲ့စည်းပုံ တစ်စိတ်တစ်ပိုင်းအဖြစ် သုံးလို့ ရပါတယ်။


MVC က IA အတွက် ဘာကြောင့် အရေးကြီးသလဲ

ကိုယ့် ဖွဲ့စည်းပုံကို ရှင်းပြခြင်း

MVC က ကိုယ်ရွေးလို့ရတဲ့ ဖွဲ့စည်းပုံ တစ်ခုပါ၊ IA က မဖြစ်မနေ မတောင်းပါဘူး။ သုံးမယ်ဆိုရင် criterion (အမှတ်ပေး စံနှုန်း) နှစ်ခုမှာ ပြောစရာ ရှင်းလင်းတဲ့ အချက်တွေ ရပါတယ်:

  • Criterion C (System overview) က “the key components, their relationships, the rules governing their interaction” (အဓိက component တွေ၊ သူတို့ကြားက ဆက်နွယ်မှုတွေ၊ သူတို့ ဆက်သွယ် အလုပ်လုပ်ပုံကို ထိန်းတဲ့ စည်းမျဉ်းတွေ) ပါတဲ့ system model တစ်ခုကို တောင်းပါတယ်။ ကိုယ့် Model၊ View နဲ့ Controller class တွေကို ဆွဲပြပြီး သူတို့ကြား ချိတ်ဆက်မှုတွေကို မြှားတွေနဲ့ ပြထားတဲ့ ပုံက အဲဒီ component တွေနဲ့ ဆက်နွယ်မှုတွေကို ပြပါတယ်။
  • Criterion D (Development) က “justify the structure of the product, why the structure is appropriate” (product ရဲ့ ဖွဲ့စည်းပုံကို အကြောင်းပြချက်နဲ့ ပြပါ၊ ဘာကြောင့် အဲဒီ ဖွဲ့စည်းပုံက သင့်တော်လဲ) ကို တောင်းပါတယ်။ MVC က ကိုယ့် code ကို modular design (အလုပ်တစ်ခုစီ ရှိတဲ့ သီးခြား အပိုင်းတွေ) ဖြစ်စေပါတယ်။ ဒါက ကိုယ့် product ကို ဘယ်လို အထောက်အကူ ဖြစ်လဲဆိုတာ ရှင်းပြနိုင်ပါတယ်၊ ဥပမာ:
    • Model class တွေကို GUI (graphical user interface၊ window တွေ၊ button တွေ၊ text box တွေ) မပါဘဲ test လုပ်လို့ ရပါတယ်
    • Model ကို မပြင်ဘဲ screen ရဲ့ ပုံစံကို ပြောင်းလို့ ရပါတယ်
    • ဒေတာ class တွေကို မပြင်ဘဲ Controller ကို ပြောင်းလို့ ရပါတယ်

Starter project ဒါမှမဟုတ် tutorial (အဆင့်ဆင့် သင်ပြတဲ့ လမ်းညွှန်) တစ်ခုကို အခြေခံပြီး တည်ဆောက်နေသလား။ အဲဒါကို ကိုးကားကြောင်း ဖော်ပြပါ၊ မူရင်း code ရဲ့ copy တစ်ခုကို သိမ်းထားပါ၊ ဘယ်အပိုင်းတွေကို ပြောင်းခဲ့လဲ၊ ထပ်ထည့်ခဲ့လဲ ဆိုတာ ရှင်းရှင်းလင်းလင်း ပြပါ။ ကိုယ့် ပြဿနာကို ဖြေရှင်းပေးတဲ့ logic (ဘာလုပ်မလဲ ဆုံးဖြတ်တဲ့ code) က ကိုယ့်ကိုယ်ပိုင် အလုပ်ပါ။

Feature အသစ် ထည့်ရတာ ပိုလွယ်ပါတယ်

MVC နဲ့ဆိုရင် feature အသစ် တစ်ခုက အပိုင်း သုံးပိုင်းကို အလှည့်ကျ ထိလေ့ ရှိပါတယ်။ ဥပမာ အတန်းရဲ့ ပျမ်းမျှ grade ကို ပြချင်ရင်:

  1. Model: method ကို ထည့်ပါ (ဥပမာ StudentList ထဲမှာ average_grade())
  2. View: widget တစ်ခုနဲ့ အဲဒါကို ပြဖို့ method တစ်ခု ထည့်ပါ (ဥပမာ label တစ်ခုနဲ့ show_average(text))
  3. Controller: Model ရဲ့ method ကို ခေါ်ပြီး ရလဒ်ကို View ဆီ ပေးပါ (ဥပမာ refresh_display() ရဲ့ အဆုံးမှာ)

Debugging (program ထဲက အမှားကို ရှာပြီး ပြင်ခြင်း) ပိုလွယ်ပါတယ်

တစ်ခုခု မှားသွားတဲ့အခါ:

  • ဒေတာ မှားပြီး သိမ်းမိနေသလား။ Model ကို စစ်ပါ
  • Window ရဲ့ layout မှားနေသလား။ View ကို စစ်ပါ
  • Button က မှားတာကို လုပ်နေသလား၊ ဒါမှမဟုတ် list က နောက်ဆုံး ဒေတာကို မပြဘူးလား။ Controller ကို စစ်ပါ

MVC မှာ အဖြစ်များတဲ့ အမှားများ

View ထဲမှာ logic ထည့်ခြင်း။ ကိုယ့် View ထဲမှာ user ရဲ့ input နဲ့ ဘာလုပ်မလဲ ဆုံးဖြတ်တဲ့ if statement (code ထဲက ညွှန်ကြားချက်) တွေ ရှိရင် ရွှေ့ပါ: ဒေတာနဲ့ ဆိုင်တဲ့ စည်းမျဉ်းတွေက Model ထဲ၊ နှိပ်ပြီးနောက် ဘာဖြစ်မလဲ ဆိုတဲ့ ဆုံးဖြတ်ချက်တွေက Controller ထဲမှာ ရှိရပါမယ်။

Model ထဲမှာ window code ထည့်ခြင်း။ models.py က tkinter ကို import လုပ်နေရင် ဒါမှမဟုတ် message box ပြနေရင် Model က View ရဲ့ အလုပ်ကို လုပ်နေတာပါ။ Model ကို သီးခြား test လုပ်နိုင်အောင် window တစ်ခုမှ မဖွင့်ဘဲ အလုပ်လုပ်ရပါမယ်။

Controller ကို ကျော်ခြင်း။ တချို့ ကျောင်းသားတွေက button တစ်ခုစီကို Model ကို တိုက်ရိုက် ပြောင်းတဲ့ function (သီးခြား အလုပ်တစ်ခု လုပ်ပေးတဲ့ code အပိုင်း) တစ်ခုစီ ပေးကြပါတယ်။ App အသေးလေးတွေမှာ အလုပ်လုပ်ပေမယ့် app ကြီးလာတာနဲ့အမျှ code ကို ပြောင်းရတာ၊ ပြင်ရတာ ခက်လာပါတယ်။ ကြားမှာ Controller ရှိရင် View နဲ့ Model က တစ်ခုနဲ့ တစ်ခု မမှီခိုတော့ပါဘူး။

ဒုတိယ window အတွက် ဒုတိယ tk.Tk() တစ်ခု ဖန်တီးခြင်း။ tkinter app တစ်ခုမှာ tk.Tk() နဲ့ ဖန်တီးတဲ့ main window တစ်ခုပဲ ရှိပါတယ်။ တခြား window တွေက main window ပိုင်တဲ့ tk.Toplevel window တွေပါ။ tk.Tk() တစ်ခုစီက သီးခြား Tcl interpreter (tkinter ရဲ့ window တွေကို run ပေးတဲ့ engine) တစ်ခုစီကို ဖန်တီးလို့ interpreter တစ်ခုအတွက် ဖန်တီးထားတဲ့ widget တွေ၊ tkinter variable တွေကို နောက်တစ်ခုနဲ့ သုံးလို့ မရပါဘူး။ Toplevel window တွေကတော့ main window ရဲ့ interpreter ကို မျှသုံးပါတယ်။ Multi-Window Apps with tkinter ကို ကြည့်ပါ။


MVC စစ်ဆေးစရာ စာရင်း

ဒီစာမျက်နှာက design ကို ကိုယ့် IA မှာ သုံးမယ်ဆိုရင် အပိုင်း သုံးပိုင်း ခွဲထားဆဲ ဟုတ်မဟုတ်ကို ဒီ စစ်ဆေးချက်တွေက ပြပါတယ်။

စည်းမျဉ်း စစ်ပြီးပြီ
models.py က tkinter ကနေ ဘာမှ import မလုပ်ပါဘူး  
View က widget တွေကို ဖန်တီးပြီး စီစဉ်ပေမယ့် ဒေတာနဲ့ ပတ်သက်ပြီး ဘာမှ မဆုံးဖြတ်ပါဘူး  
Controller က user ရဲ့ လုပ်ဆောင်ချက် တိုင်းကို ကိုင်တွယ်ပါတယ်  
Window မဖွင့်ဘဲ Model ကို test လုပ်လို့ ရပါတယ်  
Class တစ်ခုစီမှာ ရှင်းလင်းတဲ့ တာဝန် တစ်ခုတည်း ရှိပါတယ်  
main.py က အပိုင်းတွေကို ဖန်တီးပြီး ချိတ်ဆက်ကာ mainloop() ကို စတင်တာပဲ လုပ်ပါတယ်  

Quick Check (အမြန် စစ်ဆေးမေးခွန်း)

Q1. ဒီစာမျက်နှာက Student Manager မှာ widget တွေနဲ့ dialog (user ကို မေးခွန်း မေးတဲ့ pop-up window) တွေကို ဘယ် class က ဖန်တီးသလဲ။

Q2. ကျောင်းသား တစ်ယောက်က Add button ကို Controller ရဲ့ handler နဲ့ ချိတ်ပေမယ့် handler ရဲ့ နာမည်နောက်မှာ ကွင်း ရေးလိုက်ပါတယ်။ App ကို run တဲ့အခါ ဘာဖြစ်မလဲ။

Q3. Program တစ်ခုက AppController.get_instance() ကို ဒုတိယအကြိမ် ခေါ်ပါတယ်။ ဘာကို ပြန်ပေးသလဲ။

Q4. Student လို့ နာမည်ပေးထားတဲ့ class ထဲမှာ attribute ကို အစမှာ underscore နှစ်ခု ပါတဲ့ self.__name လို့ နာမည်ပေးရင် ဘာဖြစ်သလဲ။

Q5. App တစ်ခုက ဒီစာမျက်နှာက မျှသုံး AppController ကို သုံးပါတယ်။ ဒုတိယ window က ပထမ window နဲ့ ကျောင်းသား list အတူတူကို လိုပါတယ်။ ဒုတိယ window က ဘာလုပ်သင့်သလဲ။


Practice Exercises (လေ့ကျင့်ခန်းများ)

(4 marks) စတာတွေက မေးခွန်း တစ်ခုစီအတွက် ရနိုင်တဲ့ အမှတ်ပါ။ IB စာမေးပွဲ မေးခွန်းတွေမှာ အမှတ်ကို ဒီလို ပြလေ့ ရှိပါတယ်။

Core (အခြေခံ အဆင့်)

  1. Component တွေကို ခွဲခြားခြင်း (4 marks): ဂီတ ကျောင်းသား တစ်ယောက်ရဲ့ လေ့ကျင့်မှု မှတ်တမ်း app မှာ အောက်က class လေးခု ပါပါတယ်။ Class တစ်ခုစီက Model၊ View ဒါမှမဟုတ် Controller ထဲက ဘယ်အပိုင်းမှာ ပါလဲ Identify (ဖြစ်နိုင်တဲ့ အဖြေတွေထဲက ရွေးပြီး ဖော်ပြပါ)။
    • Session က တူရိယာ တစ်ခု၊ ရက်စွဲ တစ်ခုနဲ့ မိနစ် အရေအတွက်ကို သိမ်းပြီး အနုတ် မိနစ် အရေအတွက်ကို လက်မခံပါဘူး။
    • PracticeLog က Session object တွေရဲ့ list တစ်ခုကို သိမ်းထားပြီး လေ့ကျင့်ခဲ့တဲ့ မိနစ် စုစုပေါင်းကို တွက်ပါတယ်။
    • PracticeWindow က entry box တွေ၊ Save button တစ်ခုနဲ့ အရင် လေ့ကျင့်ခဲ့တဲ့ session တွေရဲ့ list ကို ဖန်တီးပြီး စာသားနဲ့ list ကို ပြတဲ့ method တွေ ပါပါတယ်။
    • PracticeManager က entry box တွေကို ဖတ်ပြီး PracticeLog ထဲ Session အသစ် တစ်ခု ထည့်ကာ update လုပ်ထားတဲ့ list ကို ပြဖို့ window ကို ခိုင်းတဲ့ method တစ်ခုကို Save button ကို ပေးပါတယ်။
  2. စည်းမျဉ်း ချိုးဖောက်မှုကို ပြင်ခြင်း (4 marks): ကျောင်းသား တစ်ယောက်ရဲ့ ProductView class က Save button ကို နှိပ်တဲ့အခါ ဒီ code ကို run ပါတယ်: ဈေးနှုန်းကို ဖတ်ပြီး ဈေးနှုန်းက 0 ထက် ငယ်ရင် “Invalid price” (ဈေးနှုန်း မမှန်ပါ) လို့ ပြပြီး ရပ်ပါတယ်။ ဈေးနှုန်း အနုတ် မဖြစ်ရဘူးလို့ စစ်တဲ့ အပိုင်းက ဘယ်မှာ ရှိသင့်လဲ၊ message ပြဖို့ ဆုံးဖြတ်ချက်က ဘယ်မှာ ရှိသင့်လဲ၊ တစ်ခုစီက ဘာကြောင့် အဲဒီမှာ ရှိသင့်လဲ ဆိုတာကို Explain (ရှင်းပြပါ)။ Code ရေးဖို့ မလိုပါဘူး။

Extension (တိုးချဲ့ အဆင့်)

  1. Calculator တစ်ခု တည်ဆောက်ခြင်း (6 marks): MVC ကို သုံးပြီး ရိုးရှင်းတဲ့ calculator (ဂဏန်း တွက်စက်) တစ်ခုကို Construct (ကိုယ်တိုင် တည်ဆောက်ပါ):
    • Model: ဂဏန်း နှစ်ခုစီကို လက်ခံပြီး အဖြေကို _result attribute ထဲ သိမ်းတဲ့ add()၊ subtract()၊ multiply() နဲ့ divide() method တွေနဲ့ get_result() method ပါတဲ့ Calculator class တစ်ခု။ divide() က ValueError ကို raise လုပ်ပြီး စားကိန်း 0 ကို လက်မခံပါဘူး။
    • View: ဂဏန်းတွေအတွက် entry box နှစ်ခု၊ operation button လေးခုနဲ့ ရလဒ် ပြတဲ့ label တစ်ခု၊ ဂဏန်းတွေကို ဖတ်ဖို့နဲ့ ရလဒ်ကို ပြဖို့ method တွေ ပါပါတယ်။
    • Controller: ဂဏန်း နှစ်ခုကို ဖတ်ပြီး ကိုက်ညီတဲ့ Model method ကို ခေါ်ကာ ရလဒ်ကို ပြပါတယ်။ ဂဏန်း မဟုတ်တဲ့ စာသား ဒါမှမဟုတ် 0 နဲ့ စားတာ ဖြစ်ရင် ရလဒ်အစား message တစ်ခု ပြပါ။
  2. Sorting (အစဉ်တကျ စီခြင်း) ထည့်ခြင်း (6 marks): Student Manager အတွက် sorting feature တစ်ခုကို Construct လုပ်ပါ: View ထဲမှာ “Sort by Grade” button တစ်ခု၊ StudentList model ထဲမှာ selection sort (အငယ်ဆုံးကို ရှာပြီး နေရာချ စီခြင်း) ကို သုံးပြီး ကျောင်းသားတွေကို grade အမြင့်ဆုံးကနေ အနိမ့်ဆုံးအထိ စီပေးတဲ့ sort_by_grade() method တစ်ခုနဲ့ Controller ထဲမှာ list ကို စီပြီး display ကို refresh လုပ်ပေးတဲ့ handler တစ်ခု။

Challenge (စိန်ခေါ်မှု အဆင့်)

  1. ကလပ် အစီအစဉ်များ App (8 marks): Member object တွေရဲ့ list တစ်ခုနဲ့ Event object (ကလပ် အစီအစဉ်) တွေရဲ့ list တစ်ခုကို သိမ်းထားတဲ့ ကျောင်း ကလပ် တစ်ခုအတွက် MVC application တစ်ခုကို Construct လုပ်ပါ။ Event တစ်ခုစီမှာ အဲဒီ event ကို တက်ရောက်မယ့် member တွေရဲ့ ကိုယ်ပိုင် list ရှိပါတယ်။ User က member တစ်ယောက် ထည့်တာ၊ event တစ်ခု ထည့်တာ၊ member တစ်ယောက်ကို event တစ်ခုအတွက် စာရင်းသွင်းတာ (တစ်ကြိမ်ပဲ) နဲ့ event တစ်ခုကို ဘယ်သူတွေ တက်ရောက်မလဲ ပြတာတွေ လုပ်နိုင်ရပါမယ်။ Model class တွေ၊ View ရဲ့ widget တွေနဲ့ method တွေ၊ Controller ရဲ့ method တွေကို စာရွက်ပေါ်မှာ အရင် စီစဉ်ပြီးမှ implement (တကယ် အလုပ်လုပ်အောင် code ရေး) လုပ်ပါ။

  2. မျှသုံး controller ကို အကြောင်းပြချက်နဲ့ ပြခြင်း (6 marks): အတန်းဖော် တစ်ယောက်ရဲ့ IA product မှာ login window၊ main window နဲ့ “add booking” (booking အသစ် ထည့်တဲ့) window ရှိပြီး window တိုင်းက AppController.get_instance() ကနေ booking (ကြိုတင် စာရင်းသွင်းမှု) တွေကို ရောက်ပါတယ်။ ဒီ product အတွက် မျှသုံး controller တစ်ခုတည်း သုံးတာရဲ့ အကျိုးကျေးဇူး နှစ်ခုနဲ့ အဲဒီထဲကို အရာတွေ အများကြီး ထည့်တာရဲ့ အန္တရာယ် တစ်ခုကို Explain လုပ်ပါ။ Code ရေးဖို့ မလိုပါဘူး။


ဆက်စပ် ခေါင်းစဉ်များ

  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: tkinter Basics (tkinter အခြေခံများ): main window၊ widget တွေနဲ့ event handling (user က နှိပ်တာ ဒါမှမဟုတ် စာရိုက်တာကို program က တုံ့ပြန်အောင် လုပ်ခြင်း)
  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: Encapsulation (ဒေတာနဲ့ method ကို တစ်စုတည်း ထုပ်ပိုးခြင်း): Model class တွေထဲက private attribute တွေနဲ့ get method တွေ
  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: Classes and Objects (class နဲ့ object များ): constructor နဲ့ method တွေ ပါတဲ့ class တစ်ခု ရေးခြင်း
  • အရင် လေ့လာထားရမယ့် ခေါင်းစဉ်: Exception Handling (exception တွေကို ကိုင်တွယ်ခြင်း): Controller က မှားတဲ့ input ကို ဖမ်းဖို့ သုံးတဲ့ try နဲ့ except
  • Java version: MVC Architecture (MVC ဖွဲ့စည်းပုံ): JavaFX (Java နဲ့ desktop app တည်ဆောက်တဲ့ library) နဲ့ တည်ဆောက်ထားတဲ့ Student Manager အတူတူ
  • နောက်ထပ် လေ့လာမယ့် ခေါင်းစဉ်: Multi-Window Apps with tkinter: window အသစ်တွေ ဖွင့်ခြင်းနဲ့ window တွေကြား ဒေတာ ပေးပို့ခြင်း
  • နောက်ထပ် လေ့လာမယ့် ခေါင်းစဉ်: File I/O in Apps: Model ရဲ့ ဒေတာကို file တွေထဲ သိမ်းပြီး ပြန် load လုပ်ခြင်း
  • ဆက်စပ် ခေါင်းစဉ်: Static Members (class ကိုယ်တိုင် ပိုင်တဲ့ member များ): class attribute တွေ၊ မျှသုံး controller ရဲ့ _instance က class attribute တစ်ခုပါ

© EduCS.me. ကွန်ပျူတာသိပ္ပံ ပညာရေးအတွက် အရင်းအမြစ်တွေ စုထားတဲ့ နေရာပါ။ EduCS is an independent educational resource. It is not affiliated with, or endorsed by, the International Baccalaureate Organization. (EduCS ဟာ သီးခြား ပညာရေး အရင်းအမြစ်တစ်ခုပါ။ International Baccalaureate Organization နဲ့ ဆက်စပ်မှု မရှိသလို သူတို့ရဲ့ ထောက်ခံချက်လည်း မရှိပါဘူး။) ဆက်သွယ်ရန်