def __init__(self, master=None, article=None, **kw):
     self.article = article
     self.line = 0
     self.typeStart = time()
     self.typed = 0
     self.typos = TypoList()
     super().__init__(master, **kw)
class Practice(WFrame):

    ## 타자 연습에 쓸 기사
    # @var Article
    article = None
    ## 현재 타자 중인 줄 색인
    # @var int
    line = int
    LINES_PER_PAGE = 6
    ## 첫 타자 시각
    #
    # 타자 수 계산에 활용
    # @see keyDown()
    # @var time
    typeStart = float
    ## 타자 수
    #
    # 타자 수 계산에 활용
    # @see keyDown()
    # @var int
    typed = int
    ## 오타 저장을 위한 리스트
    # @var TypoList
    typos = None
    ## 줄의 보기 설정
    # @var DisplayMode
    displayMode = DisplayMode.default
    ## indicator
    # @var string
    @property
    def indicator(self):
        return '' if self.displayMode == DisplayMode.overlap else '_'

    ## 생성자
    def __init__(self, master=None, article=None, **kw):
        self.article = article
        self.line = 0
        self.typeStart = time()
        self.typed = 0
        self.typos = TypoList()
        super().__init__(master, **kw)

    def initializeWidget(self):
        labelOffset = -10 if self.displayMode == DisplayMode.default else 0
        textOffset = 10 if self.displayMode == DisplayMode.default else 0

        self.labels = []
        self.texts = []
        for i in range(self.LINES_PER_PAGE):
            label = Label(self)
            label['justify'] = LEFT
            label['font'] = 'Consolas'
            label['padx'] = 0
            label['pady'] = 0
            if self.displayMode == DisplayMode.overlap:
                label['fg'] = 'gray'
            if not self.displayMode == DisplayMode.hidden:
                label.place(x=40, y=30 + labelOffset + i * 60)
            self.labels.append(label)

            text = Label(self)
            text['justify'] = LEFT
            text['font'] = 'Consolas'
            text['padx'] = 0
            text['pady'] = 0
            text.place(x=40, y=30 + textOffset + i * 60)
            self.texts.append(text)

        self.meter = Label(self)
        self.meter['text'] = '현재 타속:'
        self.meter.pack(side=BOTTOM, fill=X, pady=(0, 40))

        self.text = '영어 타자 연습'
        self.width = 640
        self.height = 480
        self.startPosition = StartPosition.centerParent
        self.bind('<KeyPress>', self.keyDown)

    def onLoad(self):
        if self.line >= len(self.article.texts):
            MessageBox.showerror(self.text, '타자 끝')
            self.close()
            return

        line = self.line % self.LINES_PER_PAGE
        # 페이지 넘기기
        if line == 0:
            contexts = self.article.texts[self.line:][:self.LINES_PER_PAGE]
            for i in range(self.LINES_PER_PAGE):
                self.labels[i]['text'] = contexts[i] if i < len(contexts) else ''
                self.texts[i]['text'] = ''
        # 타자 위치 안내자
        self.texts[line]['text'] = self.indicator

    ## 키 입력 처리
    # @see http://www.tcl.tk/man/tcl8.4/TkCmd/keysyms.htm
    def keyDown(self, e):
        try:
            ch = chr(e.keysym_num)
            context = self.article.texts[self.line]
            text = self.texts[self.line % self.LINES_PER_PAGE]
            line = text['text'][:len(text['text']) - len(self.indicator)]
            # default behavior
            if e.keysym_num <= 0x7F and ch != '\0':
                # line incompleted
                expected = len(line)
                if expected < len(context):
                    text['text'] = line + ch + self.indicator
                    # 정타 처리
                    if ch == context[expected]:
                        self.typed += 1
                    # 오타 처리
                    else:
                        self.typos.append(context, expected, ch)
                # line completed
                else:
                    self.endLine()
            # erase
            elif e.keysym == 'BackSpace' and len(line):
                text['text'] = line[:-1] + self.indicator
                # penalty
                # self.typed -= 3
            # change line
            elif e.keysym == 'Return':
                self.endLine()
        except ValueError:
            pass
        # speed = typed / duration * 60
        self.meter['text'] = '현재 타속: %d' % int(self.typed / (time() - self.typeStart) * 60)

    ## 줄 넘기기
    def endLine(self):
        text = self.texts[self.line % self.LINES_PER_PAGE]
        line = text['text'][:len(text['text']) - len(self.indicator)]
        text['text'] = line
        self.typed += 1

        self.line += 1
        self.onLoad()

    def onClosing(self):
        Fixer.save(self.typos)