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

NovelEditor.ListMenuClass   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 568
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 52
eloc 334
dl 0
loc 568
rs 7.44
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A ListMenuClass.select_list_item_input() 0 10 1
A ListMenuClass.text_text_input() 0 10 1
A ListMenuClass.message_window() 0 28 4
B ListMenuClass.on_double_click() 0 59 6
A MyDialogClass.__init__() 0 40 3
A ListMenuClass.character_rename() 0 14 2
B ListMenuClass.click_child_item() 0 45 7
A ListMenuClass.rename_gif() 0 15 2
B ListMenuClass.path_read_text() 0 47 5
B ListMenuClass.on_name_click() 0 57 6
B ListMenuClass.check_image_false() 0 72 7
A MyDialogClass.sub_name_ok() 0 15 1
A ListMenuClass.check_image_true() 0 44 3
A ListMenuClass.path_read_image() 0 46 3
A ListMenuClass.__init__() 0 4 1

How to fix   Complexity   

Complexity

Complex classes like NovelEditor.ListMenuClass often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#!/usr/bin/env python3
2
import os
3
import shutil
4
import tkinter as tk
5
import tkinter.ttk as ttk
6
import tkinter.filedialog as filedialog
7
import tkinter.messagebox as messagebox
8
import xml.etree.ElementTree as ET
9
10
from PIL import Image, ImageTk
11
12
from . import FileMenu
13
from . import Definition
14
15
16
class ListMenuClass(Definition.DefinitionClass):
17
    """リストメニューバーのクラス.
18
19
    ・リストメニューバーにあるプログラム群
20
21
    Args:
22
        app (instance): MainProcessingClass のインスタンス
23
        locale_var (str): ロケーション
24
        master (instance): toplevel のインスタンス
25
    """
26
    text_text = ""
27
    """現在入力中の初期テキスト."""
28
    select_list_item = ""
29
    """選択中のリストボックスアイテム名."""
30
31
    def __init__(self, app, locale_var, master=None):
32
        super().__init__(locale_var, master)
33
        self.app = app
34
        self.master = master
35
36
    def message_window(self, event=None):
37
        """ツリービューを右クリックしたときの処理.
38
39
        ・子アイテムならば削除ダイアログを表示する。
40
        親アイテムならば追加を行う。
41
42
        Args:
43
            event (instance): tkinter.Event のインスタンス
44
        """
45
        # 選択アイテムの認識番号取得
46
        curItem = self.app.tree.focus()
47
        # 親アイテムの認識番号取得
48
        parentItem = self.app.tree.parent(curItem)
49
        # 親アイテムをクリックしたとき
50
        if self.app.tree.item(curItem)["text"] == self.TREE_FOLDER[4][1]:
51
            # イメージアイテムの親アイテムを選択したとき
52
            self.check_image_true()
53
        else:
54
            if (
55
                        str(self.app.tree.item(curItem)["text"])
56
                    ) and (
57
                        not str(self.app.tree.item(parentItem)["text"])
58
                    ):
59
                # イメージアイテム以外の親アイテムを選択したとき
60
                self.check_image_false(curItem)
61
            else:
62
                # 子アイテムを右クリックしたとき
63
                self.click_child_item(curItem, parentItem)
64
65
    def on_name_click(self, event=None):
66
        """名前の変更.
67
68
        ・リストボックスの名前を変更する。
69
70
        Args:
71
            event (instance): tkinter.Event のインスタンス
72
        """
73
        # 開いているファイルを保存
74
        self.app.fmc.open_file_save(FileMenu.FileMenuClass.now_path)
75
        # 選択アイテムの認識番号取得
76
        curItem = self.app.tree.focus()
77
        # 親アイテムの認識番号取得
78
        parentItem = self.app.tree.parent(curItem)
79
        text = self.app.tree.item(parentItem)["text"]
80
        if not text == "":
81
            old_file = self.app.tree.item(curItem)["text"]
82
            title = self.app.dic.get_dict("Rename {0}").format(old_file)
83
            dialog2 = MyDialogClass(
84
                self.app,
85
                self.app.dic.get_dict("Change"),
86
                True,
87
                title,
88
                old_file
89
            )
90
            self.master.wait_window(dialog2.sub_name_win)
91
            # テキストを読み取り専用を解除する
92
            self.app.NovelEditor.configure(state='normal')
93
            new_file = dialog2.txt
94
            del dialog2
95
            for val in self.TREE_FOLDER:
96
                if text == val[1]:
97
                    path1 = "./{0}/{1}.txt".format(val[0], old_file)
98
                    path2 = "./{0}/{1}.txt".format(val[0], new_file)
99
                    FileMenu.FileMenuClass.now_path = path2
100
                    # テキストの名前を変更する
101
                    os.rename(path1, path2)
102
                    self.app.tree.delete(curItem)
103
                    Item = self.app.tree.insert(
104
                        parentItem,
105
                        'end',
106
                        text=new_file
107
                    )
108
                    if val == self.TREE_FOLDER[0]:
109
                        self.character_rename(
110
                            FileMenu.FileMenuClass.now_path,
111
                            val[0],
112
                            old_file,
113
                            new_file
114
                        )
115
116
                    if val == self.TREE_FOLDER[4]:
117
                        self.rename_gif(val[0], old_file, new_file)
118
119
                    self.app.tree.selection_set(Item)
120
                    self.path_read_text(val[0], new_file)
121
                    return
122
123
    def character_rename(self, now_path, folder, old_file, new_file):
124
        """キャラクターの名前変更.
125
126
        ・キャラクターの名前を変更する。
127
128
        Args:
129
            now_path (str): 今の処理ししているファイルのパス
130
            folder (str): 今処理しているフォルダ
131
            old_file (str): 変更前のファイル名
132
            new_file (str): 変更後のファイル名
133
        """
134
        self.rename_gif(folder, old_file, new_file)
135
        with open(now_path, mode='w', encoding='utf-8') as f:
136
            f.write(self.app.fmc.save_charactor_file(new_file))
137
138
    @staticmethod
139
    def rename_gif(folder, old_file, new_file):
140
        """gifの名前変更.
141
142
        ・gifの名前を変更する。
143
144
        Args:
145
            folder (str): 今処理しているフォルダ
146
            old_file (str): 変更前のファイル名
147
            new_file (str): 変更後のファイル名
148
        """
149
        path1 = "./{0}/{1}.gif".format(folder, old_file)
150
        path2 = "./{0}/{1}.gif".format(folder, new_file)
151
        if os.path.isfile(path1):
152
            os.rename(path1, path2)
153
154
    def path_read_image(self, image_path, image_name, scale):
155
        """イメージを読み込んで表示.
156
157
        ・パスが存在すればイメージファイルを読み込んで表示する。
158
159
        Args:
160
            image_path (str): イメージファイルの相対パス
161
            image_name (str): イメージファイルの名前
162
            scale (int): 拡大率(%)
163
        """
164
        if not FileMenu.FileMenuClass.now_path == "":
165
            title = "{0}/{1}.gif".format(
166
                image_path,
167
                image_name
168
            )
169
            giffile = Image.open(title)
170
            if scale > 0:
171
                giffile = giffile.resize(
172
                    (
173
                        int(giffile.width / 100*scale),
174
                        int(giffile.height / 100*scale)
175
                    ),
176
                    resample=Image.LANCZOS
177
                )
178
179
            self.app.CanvasImage.photo = ImageTk.PhotoImage(giffile)
180
            self.app.CanvasImage.itemconfig(
181
                self.app.ImageOnImage,
182
                image=self.app.CanvasImage.photo
183
            )
184
            # イメージサイズにキャンバスサイズを合わす
185
            self.app.CanvasImage.config(
186
                scrollregion=(
187
                    0,
188
                    0,
189
                    giffile.size[0],
190
                    giffile.size[1]
191
                )
192
            )
193
            giffile.close()
194
195
        self.app.winfo_toplevel().title(
196
                "{0}/{1}/{2}".format(
197
                    self.app.dic.get_dict("Novel Editor"),
198
                    self.TREE_FOLDER[4][1],
199
                    image_name
200
                )
201
            )
202
203
    def path_read_text(self, text_path, text_name):
204
        """テキストを読み込んで表示.
205
206
        ・パスが存在すればテキストを読み込んで表示する。
207
208
        Args:
209
            text_path (str): テキストファイルの相対パス
210
            text_name (str): テキストファイルの名前
211
        """
212
        if not FileMenu.FileMenuClass.now_path == "":
213
            if not FileMenu.FileMenuClass.now_path.find(
214
                        self.TREE_FOLDER[0][0]
215
                    ) == -1:
216
                self.app.EntryCallName.configure(state='normal')
217
                self.app.EntryCallName.delete('0', tk.END)
218
                self.app.EntryName.delete('0', tk.END)
219
                self.app.EntryBirthday.delete('0', tk.END)
220
                self.app.TextboxBiography.delete('1.0', tk.END)
221
                tree = ET.parse(FileMenu.FileMenuClass.now_path)
222
                elem = tree.getroot()
223
                self.app.EntryCallName.insert(tk.END, elem.findtext("call"))
224
                self.app.EntryCallName.configure(state='readonly')
225
                self.app.EntryName.insert(tk.END, elem.findtext("name"))
226
                self.app.var.set(elem.findtext("sex"))
227
                self.app.EntryBirthday.insert(tk.END, elem.findtext("birthday"))
228
                self.app.TextboxBiography.insert(tk.END, elem.findtext("body"))
229
                title = "{0}/{1}.gif".format(
230
                    self.TREE_FOLDER[0][0],
231
                    elem.findtext("call")
232
                )
233
                if os.path.isfile(title):
234
                    self.app.spc.print_gif(title)
235
            else:
236
                self.app.NovelEditor.delete('1.0', tk.END)
237
                with open(FileMenu.FileMenuClass.now_path, encoding='utf-8') as f:
238
                    self.text_text_input(f.read())
239
                    self.app.NovelEditor.insert(tk.END, self.text_text)
240
241
            self.app.winfo_toplevel().title(
242
                "{0}/{1}/{2}".format(
243
                    self.app.dic.get_dict("Novel Editor"),
244
                    text_path,
245
                    text_name
246
                )
247
            )
248
            # シンタックスハイライトをする
249
            self.app.hpc.all_highlight()
250
251
    def on_double_click(self, event=None):
252
        """ツリービューをダブルクリック.
253
254
        ・ファイルを保存して閉じて、選択されたアイテムを表示する。
255
256
        Args:
257
            event (instance): tkinter.Event のインスタンス
258
        """
259
        # 選択アイテムの認識番号取得
260
        curItem = self.app.tree.focus()
261
        # 親アイテムの認識番号取得
262
        parentItem = self.app.tree.parent(curItem)
263
        text = self.app.tree.item(parentItem)["text"]
264
        # 開いているファイルを保存
265
        self.app.fmc.open_file_save(FileMenu.FileMenuClass.now_path)
266
        # テキストを読み取り専用を解除する
267
        self.app.cwc.frame()
268
        self.app.NovelEditor.configure(state='disabled')
269
        # 条件によって分離
270
        self.select_list_item_input(self.app.tree.item(curItem)["text"])
271
        path = ""
272
        for val in self.TREE_FOLDER:
273
            if text == val[1]:
274
                if val[0] == self.TREE_FOLDER[4][0]:
275
                    path = "./{0}/{1}.txt".format(
276
                        val[0],
277
                        self.select_list_item
278
                    )
279
                    with open(path, encoding='utf-8') as f:
280
                        zoom = f.read()
281
282
                    self.app.spc.zoom = int(zoom)
283
                    FileMenu.FileMenuClass.now_path = path
284
                    self.app.cwc.frame_image()
285
                    self.path_read_image(
286
                        self.TREE_FOLDER[4][0],
287
                        self.select_list_item,
288
                        self.app.spc.zoom
289
                    )
290
                else:
291
                    path = "./{0}/{1}.txt".format(
292
                        val[0],
293
                        self.select_list_item
294
                    )
295
                    FileMenu.FileMenuClass.now_path = path
296
                    if val[0] == self.TREE_FOLDER[0][0]:
297
                        self.app.cwc.frame_character()
298
                    else:
299
                        # テキストを読み取り専用を解除する
300
                        self.app.NovelEditor.configure(state='normal')
301
                        self.app.NovelEditor.focus()
302
303
                    self.path_read_text(text, self.select_list_item)
304
305
                return
306
307
        FileMenu.FileMenuClass.now_path = ""
308
        self.app.winfo_toplevel().title(
309
            self.app.dic.get_dict("Novel Editor")
310
        )
311
312
    def check_image_true(self):
313
        """イメージアイテムを右クリックしたとき.
314
315
        ・イメージアイテムの親アイテムを右クリックしたときの処理。
316
        """
317
        # イメージアイテムを選択したとき
318
        fTyp = [(self.app.dic.get_dict("Novel Editor"), '*.gif')]
319
        iDir = os.path.abspath(os.path.dirname(__file__))
320
        filepath = filedialog.askopenfilename(
321
            filetypes=fTyp,
322
            initialdir=iDir
323
        )
324
        # ファイル名があるとき
325
        if not filepath == "":
326
            file_name = os.path.splitext(os.path.basename(filepath))[0]
327
            path = "./{0}/{1}.gif".format(
328
                self.TREE_FOLDER[4][0],
329
                file_name
330
            )
331
            shutil.copy2(filepath, path)
332
            self.app.cwc.frame_image()
333
            path = "./{0}/{1}.txt".format(
334
                self.TREE_FOLDER[4][0],
335
                file_name
336
            )
337
            tree = self.app.tree.insert(
338
                self.TREE_FOLDER[4][0],
339
                'end',
340
                text=file_name
341
            )
342
            self.app.tree.see(tree)
343
            self.app.tree.selection_set(tree)
344
            self.app.tree.focus(tree)
345
            self.select_list_item_input(file_name)
346
            FileMenu.FileMenuClass.now_path = path
347
            with open(path, mode='w', encoding='utf-8') as f:
348
                self.app.spc.zoom = 100
349
                f.write(str(self.app.spc.zoom))
350
351
            self.app.cwc.frame_image()
352
            self.path_read_image(
353
                self.TREE_FOLDER[4][0],
354
                file_name,
355
                0
356
            )
357
358
    def check_image_false(self, curItem):
359
        """イメージアイテム以外を右クリックしたとき.
360
361
        ・イメージアイテム以外の親アイテムを右クリックしたときの処理。
362
363
        Args:
364
            curItem (int): 選択アイテムの認識番号
365
        """
366
        # サブダイヤログを表示する
367
        title = self.app.dic.get_dict(
368
            "Insert in {0}"
369
        ).format(self.app.tree.item(curItem)["text"])
370
        dialog = MyDialogClass(
371
            self.app,
372
            self.app.dic.get_dict("Insert"),
373
            True,
374
            title,
375
            False
376
        )
377
        self.master.wait_window(dialog.sub_name_win)
378
        file_name = dialog.txt
379
        del dialog
380
        if not file_name == "":
381
            self.app.fmc.open_file_save(FileMenu.FileMenuClass.now_path)
382
            curItem = self.app.tree.focus()
383
            text = self.app.tree.item(curItem)["text"]
384
            path = ""
385
            tree = ""
386
            # 選択されているフォルダを見つける
387
            for val in self.TREE_FOLDER:
388
                if text == val[1]:
389
                    if val[0] == self.TREE_FOLDER[0][0]:
390
                        self.app.cwc.frame_character()
391
                        self.app.EntryCallName.configure(state='normal')
392
                        self.app.EntryCallName.insert(
393
                            tk.END,
394
                            file_name
395
                        )
396
                        self.app.EntryCallName.configure(state='readonly')
397
                    else:
398
                        self.app.cwc.frame()
399
400
                    path = "./{0}/{1}.txt".format(val[0], file_name)
401
                    tree = self.app.tree.insert(
402
                        val[0],
403
                        'end',
404
                        text=file_name
405
                    )
406
                    FileMenu.FileMenuClass.now_path = path
407
                    break
408
409
            # パスが存在すれば新規作成する
410
            if not path == "":
411
                with open(path, mode='w', encoding='utf-8') as f:
412
                    f.write("")
413
414
                # ツリービューを選択状態にする
415
                self.app.tree.see(tree)
416
                self.app.tree.selection_set(tree)
417
                self.app.tree.focus(tree)
418
                self.select_list_item_input(file_name)
419
                self.app.winfo_toplevel().title(
420
                    "{0}/{1}/{2}".format(
421
                        self.app.dic.get_dict("Novel Editor"),
422
                        text,
423
                        file_name
424
                    )
425
                )
426
                self.app.NovelEditor.focus()
427
                # テキストを読み取り専用を解除する
428
                self.app.NovelEditor.configure(state='normal')
429
                self.app.hpc.create_tags()
430
431
    def click_child_item(self, curItem, parentItem):
432
        """子アイテムを右クリックしたとき.
433
434
        ・子アイテムを右クリックしたときの処理。
435
436
        Args:
437
            curItem (int): 選択アイテムの認識番号
438
            parentItem (int): 親アイテムの認識番号
439
        """
440
        if str(self.app.tree.item(curItem)["text"]):
441
            # 項目を削除する
442
            file_name = self.app.tree.item(curItem)["text"]
443
            text = self.app.tree.item(parentItem)["text"]
444
            # OK、キャンセルダイアログを表示し、OKを押したとき
445
            if messagebox.askokcancel(
446
                    self.app.dic.get_dict("Delete item"),
447
                    self.app.dic.get_dict(
448
                        "Delete {0} item?"
449
                    ).format(file_name)
450
            ):
451
                image_path = ""
452
                path = ""
453
                # パスを取得する
454
                for val in self.TREE_FOLDER:
455
                    if text == val[1]:
456
                        path = "./{0}/{1}.txt".format(
457
                            val[0],
458
                            file_name
459
                        )
460
                        image_path = "./{0}/{1}.gif".format(
461
                            val[0],
462
                            file_name
463
                        )
464
                        self.app.tree.delete(curItem)
465
                        FileMenu.FileMenuClass.now_path = ""
466
                        break
467
                # imageパスが存在したとき
468
                if os.path.isfile(image_path):
469
                    os.remove(image_path)
470
471
                # パスが存在したとき
472
                if not path == "":
473
                    os.remove(path)
474
                    self.app.cwc.frame()
475
                    self.app.NovelEditor.focus()
476
477
    @classmethod
478
    def text_text_input(cls, text_text):
479
        """現在入力中の初期テキストを入力.
480
481
        ・現在入力中の初期テキストをクラス変数に入力する。
482
483
        Args:
484
            text_text (str): 現在入力中の初期テキスト
485
        """
486
        cls.text_text = text_text
487
488
    @classmethod
489
    def select_list_item_input(cls, select_list_item):
490
        """選択中のリストボックスアイテム名を入力.
491
492
        ・選択中のリストボックスアイテム名をクラス変数に入力する。
493
494
        Args:
495
            select_list_item (str): 選択中のリストボックスアイテム名
496
        """
497
        cls.select_list_item = select_list_item
498
499
500
class MyDialogClass():
501
    """ダイアログ作成クラス.
502
503
    ・自作ダイアログを呼び出し表示する。
504
505
    Args:
506
        app (instance): 親ウインドウインスタンス
507
        caption (str): ボタンのメッセージ
508
        cancel (bool): キャンセルボタンを表示する(True)
509
        title (str): タイトル
510
        text (bool): 選択状態にする(True)
511
    """
512
    def __init__(self, app, caption, cancel, title, text):
513
        self.txt = ""
514
        self.sub_name_win = tk.Toplevel(app)
515
        self.EntryName = ttk.Entry(self.sub_name_win, width=40)
516
        self.EntryName.grid(
517
            row=0,
518
            column=0,
519
            columnspan=2,
520
            padx=5,
521
            pady=5,
522
            sticky=tk.W+tk.E,
523
            ipady=3
524
        )
525
        button = ttk.Button(
526
            self.sub_name_win,
527
            text=caption,
528
            width=str(caption),
529
            padding=(10, 5),
530
            command=self.sub_name_ok
531
        )
532
        button.grid(row=1, column=0)
533
        if cancel:
534
            button2 = ttk.Button(
535
                self.sub_name_win,
536
                text=app.dic.get_dict("Cancel"),
537
                width=str(app.dic.get_dict("Cancel")),
538
                padding=(10, 5),
539
                command=self.sub_name_win.destroy
540
            )
541
542
            button2.grid(row=1, column=1)
543
            self.EntryName.focus()
544
            if text is not False:
545
                self.EntryName.insert(tk.END, text)
546
                self.EntryName.select_range(0, 'end')
547
548
        self.sub_name_win.title(title)
549
        self.sub_name_win.attributes("-topmost", True)
550
        self.sub_name_win.resizable(False, False)
551
        self.EntryName.focus()
552
553
    def sub_name_ok(self, event=None):
554
        """ダイアログボタンクリック時の処理.
555
556
        ・自作ダイアログのボタンをクリックしたときにインプットボックスに
557
        入力されている値を取得する。
558
559
        Args:
560
            event (instance): tkinter.Event のインスタンス
561
562
        Returns:
563
            str: インプットボックスの値
564
        """
565
        self.txt = self.EntryName.get()
566
        self.sub_name_win.destroy()
567
        return self.txt
568