Test Failed
Push — master ( b264b0...0ab505 )
by Yoshihiro
03:23
created

ProcessingMenuClass.yahooresult()   B

Complexity

Conditions 2

Size

Total Lines 72
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 50
dl 0
loc 72
rs 8.6363
c 0
b 0
f 0
cc 2
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
#!/usr/bin/env python3
2
import re
3
import textwrap
4
import threading
5
import itertools
6
import webbrowser
7
import tkinter as tk
8
import tkinter.ttk as ttk
9
import tkinter.messagebox as messagebox
10
import xml.etree.ElementTree as ET
11
import json
12
from urllib import request
13
14
import jaconv
15
import pyttsx4
16
17
from . import Definition
18
19
20
class ProcessingMenuClass(Definition.DefinitionClass):
21
    """処理メニューバーのクラス.
22
23
    ・処理メニューバーにあるプログラム群
24
25
    Args:
26
        app (instance): MainProcessingClass のインスタンス
27
        tokenizer (instance): Tokenizer のインスタンス
28
        wiki_wiki (instance): wikipediaapi.Wikipedia のインスタンス
29
        locale_var (str): ロケーション
30
        master (instance): toplevel のインスタンス
31
    """
32
    font_size = 0
33
    """フォントのサイズ."""
34
    yahoo_appid = ""
35
    """Yahoo! Client ID"""
36
37
    def __init__(self, app, tokenizer, wiki_wiki, locale_var, master=None):
38
        super().__init__(locale_var, master)
39
        # yahooの校正支援
40
        self.KOUSEI = "{urn:yahoo:jp:jlp:KouseiService}"
41
        self.app = app
42
        self.tokenizer = tokenizer
43
        self.wiki_wiki = wiki_wiki
44
45
    def ruby_huri(self):
46
        """ルビをふり.
47
48
        ・選択文字列に小説家になろうのルビを振る。
49
        """
50
        hon = ""
51
        # 選択文字列を切り取る
52
        set_ruby = self.app.NovelEditor.get('sel.first', 'sel.last')
53
        # 選択文字列を削除する
54
        self.app.NovelEditor.delete('sel.first', 'sel.last')
55
        # 形態素解析を行う
56
        for token in self.tokenizer.tokenize(set_ruby):
57
            # ルビの取得
58
            ruby = ""
59
            ruby = jaconv.kata2hira(token.reading)
60
            # 解析している文字のひらがなの部分を取得
61
            hira = ""
62
            for i in token.surface:
63
                if self.is_hiragana(i):
64
                    hira += i
65
            # ルビがないときと、記号の時の処理
66
            if ruby.replace(
67
                hira, ''
68
            ) == "" or token.part_of_speech.split(
69
                ","
70
            )[0] == self.dic.get_dict("symbol"):
71
                hon += token.surface
72
            else:
73
                # ルビ振りを行う
74
                hon += "|{0}≪{1}≫{2}".format(
75
                    token.surface.replace(hira, ''),
76
                    ruby.replace(hira, ''),
77
                    hira
78
                )
79
80
        # テキストを表示する
81
        self.app.NovelEditor.insert('insert', hon)
82
83
    @staticmethod
84
    def is_hiragana(char):
85
        """文字がひらがなか判断.
86
87
        ・与えられた文字がひらがなかどうか判断する。
88
89
        Args:
90
            char (str): 判断する文字
91
92
        Returns:
93
            bool: ひらがなならTrue、違うならFalse
94
        """
95
        return (0x3040 < ord(char) < 0x3097)
96
97
    def count_moji(self):
98
        """文字数と行数を表示.
99
100
        ・文字数と行数をカウントして表示する。
101
        """
102
        # 行数の取得
103
        new_line = int(self.app.NovelEditor.index('end-1c').split('.')[0])
104
        # 文字列の取得
105
        moji = self.app.NovelEditor.get('1.0', 'end')
106
        # 20文字で区切ったときの行数を数える
107
        gen_mai = 0
108
        for val in moji.splitlines():
109
            gen_mai += len(textwrap.wrap(val, 20))
110
        # メッセージボックスの表示
111
        messagebox.showinfo(
112
            self.app.dic.get_dict("Number of characters etc"),
113
            self.app.dic.get_dict(
114
                "Characters : {0} Lines : {1}\n Manuscript papers : {2}"
115
            )
116
            .format(
117
                len(moji)-new_line,
118
                new_line,
119
                -(-gen_mai//20)))
120
121
    def find_wikipedia(self):
122
        """意味を検索.
123
124
        ・Wikipedia-APIライブラリを使ってWikipediaから選択文字の意味を
125
        検索する。
126
        """
127
        # wikipediaから
128
        select_text = self.app.NovelEditor.selection_get()
129
        page_py = self.wiki_wiki.page(select_text)
130
        # ページがあるかどうか判断
131
        if page_py.exists():
132
            messagebox.showinfo(
133
                self.app.dic.get_dict(
134
                    "Meaning of [{0}]"
135
                ).format(select_text),
136
                page_py.summary
137
            )
138
        else:
139
            messagebox.showwarning(
140
                self.app.dic.get_dict(
141
                    "Meaning of [{0}]"
142
                ).format(select_text),
143
                self.app.dic.get_dict("Can't find.")
144
            )
145
146
    def open_becoming_novelist_page(self):
147
        """小説家になろうのユーザーページを開く.
148
149
        ・インターネットブラウザで小説家になろうのユーザーページを開く。
150
        """
151
        webbrowser.open("https://syosetu.com/user/top/")
152
153
    def read_text(self):
154
        """テキストを読み上げる.
155
156
        ・pyttsx4ライブラリを使ってテキストボックスに書かれているものを読み上げる。
157
        """
158
        self.app.engine = pyttsx4.init()
159
        self.speak = Speaking(self.app.NovelEditor.get(1.0, tk.END),self.app, daemon=True)
160
        self.speak.start()
161
162
    def pyttsx4_onend(self):
163
        """文章を読み終えた時の処理.
164
165
        ・文章を読み終えたら中止ウインドウを削除する。
166
167
        """
168
        if self.speak:
169
            self.speak.stop()
170
            self.speak = None
171
172
    def yahoo(self):
173
        """Yahoo! 校正支援.
174
175
        ・Yahoo! 校正支援を呼び出し表示する。
176
177
        Args:
178
            event (instance): tkinter.Event のインスタンス
179
        """
180
        html = self.yahoocall(
181
            self.app.NovelEditor.get('1.0', 'end -1c')
182
        )
183
        if not self.yahoo_appid == "":
184
            self.yahooresult(html)
185
            self.yahoo_tree.bind("<Double-1>", self.on_double_click_yahoo)
186
187
    def yahoocall(self, sentence=""):
188
        """yahooの校正支援を呼び出し.
189
190
        ・Yahoo! 校正支援をClient IDを使って呼び出す。
191
192
        Args:
193
            sentence (str): 校正をしたい文字列
194
195
        Returns:
196
            str: 校正結果
197
        """
198
        if self.yahoo_appid == "":
199
            messagebox.showerror(
200
                self.app.dic.get_dict("Yahoo! Client ID"),
201
                self.app.dic.get_dict(
202
                    "Yahoo! Client ID is not find."
203
                    "\nRead Readme.html and set it again."
204
                )
205
            )
206
            return
207
        APPID = self.yahoo_appid.rstrip('\n')
208
        URL = "https://jlp.yahooapis.jp/KouseiService/V2/kousei"
209
        headers = {
210
            "Content-Type": "application/json",
211
            "User-Agent": "Yahoo AppID: {}".format(APPID),
212
        }
213
        param_dic = {
214
            "id": "NovelEditor-Yahoo-Kousei",
215
            "jsonrpc" : "2.0",
216
            "method" : "jlp.kouseiservice.kousei",
217
            "params" : {
218
                "q" : sentence
219
            }
220
        }
221
        params = json.dumps(param_dic).encode()
222
        req = request.Request(URL, params, headers)
223
        with request.urlopen(req) as res:
224
            body = res.read()
225
        return body.decode()
226
227
    def yahooresult(self, html):
228
        """校正支援を表示する画面を制作.
229
230
        ・校正結果を表示するダイアログを作成する。
231
232
        Args:
233
            html (str): 校正結果
234
        """
235
        jsonData = json.loads(html)
236
        # サブウインドウの表示
237
        sub_win = tk.Toplevel(self.app)
238
        # ツリービューの表示
239
        self.yahoo_tree = ttk.Treeview(sub_win)
240
        self.yahoo_tree["columns"] = (1, 2, 3, 4, 5)
241
        # 表スタイルの設定(headingsはツリー形式ではない、通常の表形式)
242
        self.yahoo_tree["show"] = "headings"
243
        self.yahoo_tree.column(1, width=100)
244
        self.yahoo_tree.column(2, width=80)
245
        self.yahoo_tree.column(3, width=75)
246
        self.yahoo_tree.column(4, width=150)
247
        self.yahoo_tree.column(5, width=120)
248
        self.yahoo_tree.heading(
249
            1,
250
            text=self.dic.get_dict("Number of characters from the beginning")
251
        )
252
        self.yahoo_tree.heading(
253
            2,
254
            text=self.dic.get_dict("Number of target characters")
255
        )
256
        self.yahoo_tree.heading(
257
            3,
258
            text=self.dic.get_dict("Target notation")
259
        )
260
        self.yahoo_tree.heading(
261
            4,
262
            text=self.dic.get_dict("Paraphrase candidate string")
263
        )
264
        self.yahoo_tree.heading(
265
            5,
266
            text=self.dic.get_dict("Detailed information on the pointed out")
267
        )
268
        # 情報を取り出す
269
        for child in jsonData["result"]["suggestions"]:
270
            StartPos = (child["offset"])
271
            Length = (child["length"])
272
            Surface = (child["word"])
273
            ShitekiWord = (child["note"])
274
            ShitekiInfo = (child["rule"])
275
            self.yahoo_tree.insert(
276
                "",
277
                "end",
278
                values=(StartPos,
279
                        Length,
280
                        Surface,
281
                        ShitekiWord,
282
                        ShitekiInfo
283
                        )
284
                )
285
286
        self.yahoo_tree.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W))
287
        # スクロールバーを表示する
288
        SCRLBAR_Y = ttk.Scrollbar(
289
            sub_win,
290
            orient=tk.VERTICAL,
291
            command=self.yahoo_tree.yview
292
        )
293
        self.yahoo_tree.configure(yscroll=SCRLBAR_Y.set)
294
        SCRLBAR_Y.grid(row=0, column=1, sticky=(tk.N, tk.S))
295
        # 最前面に表示し続ける
296
        sub_win.attributes("-topmost", True)
297
        sub_win.title(
298
            self.app.dic.get_dict("Sentence structure")
299
        )
300
301
    def on_double_click_yahoo(self, event=None):
302
        """Yahoo! 校正支援リストをダブルクリック.
303
304
        ・Yahoo! 校正支援ダイアログのリストをダブルクリックすると
305
        その該当箇所を選択する。
306
307
        Args:
308
            event (instance): tkinter.Event のインスタンス
309
        """
310
        i = 0
311
        textlen = 0
312
        textforlen = 0
313
        curItem = self.yahoo_tree.focus()
314
        value = self.yahoo_tree.item(curItem)
315
        # 出てくる場所を取得
316
        val = int(value.get("values")[0])
317
        # 出てくる文字数を取得
318
        lenge = value.get("values")[1]
319
        # 何行目になるか確認する
320
        while True:
321
            if val > textlen:
322
                i += 1
323
                textforlen = textlen
324
                textlen += len(
325
                    self.app.NovelEditor.get(
326
                        '{0}.0'.format(i),
327
                        '{0}.0'.format(i+1)
328
                    )
329
                )
330
            else:
331
                break
332
        if i == 0:
333
            i = 1
334
        # 選択状態を一旦削除
335
        self.app.NovelEditor.tag_remove('sel', '1.0', 'end')
336
        # 選択状態にする
337
        self.app.NovelEditor.tag_add(
338
            'sel',
339
            "{0}.{1}".format(i, val-textforlen),
340
            "{0}.{1}".format(i, val-textforlen+lenge)
341
        )
342
        # カーソルの移動
343
        self.app.NovelEditor.mark_set('insert', '{0}.{1}'.format(i, val-textforlen))
344
        self.app.NovelEditor.see('insert')
345
        # フォーカスを合わせる
346
        self.app.NovelEditor.focus()
347
        return
348
349
    def font_dialog(self, event=None):
350
        """フォントサイズダイアログを作成.
351
352
        ・フォントサイズダイアログを作成し表示する。
353
354
        Args:
355
            event (instance): tkinter.Event のインスタンス
356
        """
357
        self.app.sub_wins = tk.Toplevel(self.app)
358
        self.app.intSpin = ttk.Spinbox(self.app.sub_wins, from_=12, to=72)
359
        self.app.intSpin.grid(
360
            row=0,
361
            column=0,
362
            columnspan=2,
363
            padx=5,
364
            pady=5,
365
            sticky=tk.W+tk.E,
366
            ipady=3
367
        )
368
        button = ttk.Button(
369
            self.app.sub_wins,
370
            text=self.app.dic.get_dict("Resize"),
371
            width=str(self.app.dic.get_dict("Resize")),
372
            padding=(10, 5),
373
            command=self.font_size_Change
374
        )
375
        button.grid(row=1, column=1)
376
        self.app.intSpin.set(ProcessingMenuClass.font_size)
377
        self.app.sub_wins.title(
378
            self.app.dic.get_dict("Font size")
379
        )
380
381
    def font_size_Change(self):
382
        """フォントのサイズの変更.
383
384
        ・サイズ変更を押されたときにサイズを変更する。
385
        上は72ptまで下は12ptまでにする。
386
        """
387
        # 比較のため数値列に変更
388
        font_size = int(self.app.intSpin.get())
389
        if font_size < 12:  # 12より下の値を入力した時、12にする
390
            font_size = 12
391
        elif 72 < font_size:  # 72より上の値を入力した時、72にする
392
            font_size = 72
393
        # 文字列にもどす
394
        self.font_size_input(str(font_size))
395
        self.app.sub_wins.destroy()
396
        # フォントサイズの変更
397
        self.app.NovelEditor.configure(font=(self.app.font, self.font_size))
398
        # ラインナンバーの変更
399
        self.app.spc.update_line_numbers()
400
        # ハイライトのやり直し
401
        self.app.hpc.all_highlight()
402
403
    @classmethod
404
    def font_size_input(cls, font_size):
405
        """フォントサイズを入力.
406
407
        ・フォントサイズをクラス変数に入力する。
408
409
        Args:
410
            font_size (str): フォントサイズ
411
        """
412
        cls.font_size = font_size
413
414
415
class Speaking(threading.Thread):
416
    """テキスト読み上げのクラス.
417
418
    ・テキスト読み上げのプログラム群
419
420
    Args:
421
        sentence (str): テキストデータ
422
        app (instance): MainProcessingClass のインスタンス
423
        **kwargs (dict): 複数のキーワード引数を辞書として受け取る
424
    """
425
    def __init__(self, sentence, app, **kwargs):
426
        super().__init__(**kwargs)
427
        self.app = app
428
        # 改行と点丸で分割(行ごとの二次元配列)
429
        self.words = [re.split("、|。", text) for text in sentence.splitlines()]
430
        # 二次元配列を一次元配列に変換
431
        self.words = list(itertools.chain.from_iterable(self.words))
432
        self.paused = False
433
434
    def run(self):
435
        """読み上げを実行.
436
437
        ・テキストの読み上げを始める。
438
439
        """
440
        self.running = True
441
        # 読むのを中止するウインドウを作成する
442
        self.sub_read_win = tk.Toplevel(self.app)
443
        button = ttk.Button(
444
            self.sub_read_win,
445
            text=self.app.dic.get_dict("Stop"),
446
            width=str(self.app.dic.get_dict("Stop")),
447
            padding=(100, 5),
448
            command=self.stop
449
        )
450
        button.grid(row=1, column=1)
451
        # 最前面に表示し続ける
452
        self.sub_read_win.attributes("-topmost", True)
453
        # サイズ変更禁止
454
        self.sub_read_win.resizable(False, False)
455
        self.sub_read_win.title(
456
            self.app.dic.get_dict("Read aloud")
457
        )
458
        # 頭から読み上げる
459
        pos = [0,0]
460
        while self.words and self.running:
461
            if not self.paused:
462
                # 読み上げ場所の選択
463
                word = self.words.pop(0)
464
                pos[1] = len(word) + 1 + pos[1]
465
                if pos[1]-pos[0]>1:
466
                    start = '0.0 + {0}c'.format(pos[0])
467
                    end = '0.0 + {0}c'.format(pos[1])
468
                    self.app.NovelEditor.tag_add('sel', start, end)
469
                    self.app.NovelEditor.mark_set('insert', end)
470
                    self.app.NovelEditor.see('insert')
471
                    self.app.NovelEditor.focus()
472
                    # 読み上げ開始
473
                    self.app.engine.say(word)
474
                    self.app.engine.runAndWait()
475
                    self.app.NovelEditor.tag_remove('sel', '1.0', 'end')
476
                    # 読み上げ終了
477
                pos[0] = pos[1]
478
        self.running = False
479
        self.sub_read_win.destroy()
480
        self.app.NovelEditor.tag_remove('sel', '1.0', 'end')
481
482
    def stop(self):
483
        """読み上げを終了する.
484
485
        ・テキストの読み上げを終わる。
486
487
        """
488
        self.running = False
489
        self.sub_read_win.destroy()
490
        self.app.NovelEditor.tag_remove('sel', '1.0', 'end')
491