質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.91%

PYTHONのファイル分割時エラー

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 397

akiteru

score 11

既に同じ質問を3回目となる投稿です、投稿ルール、「編集」、「削除」等の処理方法が今一つ理解不足でご迷惑をかけています。(これからは言い訳です)投稿のコード部分のインデントが質問入力画面では有るにもかかわらず新着の質問で表示させると全て削除され左側揃えとなっています。2回目投稿ではご指摘で<code>で挿入したのですが頭尾にある「'''」を文面作成フォームで削除しました(入力画面ではインデント有)。この為と思われますが投稿の様にすべて左揃えになっていました。
下記文面はコード部分が入力フォームとプレビュー画面ともにインデント有で表示されています。
正常に表示されるようでしたら、よろしくお願いします。
【追記】ご指摘によりPyPostTblの定義を追加してあります

(1) ////////  初心者です。PYTHONでファイルを分割(親子関係で)したいのですがエラーが発生。対策を教えて下さい。
又、修正した方が良い部分があればご指摘ください。

(2) /////// 下記ソースコードは正常に動作します。これをメインプログラム(parent.py)と画面作成部分(child1.py)とボタンクリック動作部分(child2.py)にファイルに分割したい。

(3) 正常に動作する元コード及びエラーが出る三分割後コードは下記の通りです

////////////元コード(original.py):正常に動作する

#*** original.py
import tkinter as tk
import tkinter.ttk as ttk
import sqlite3

class MainWindow(ttk.Frame):
    def __init__(self, parent):
        super(MainWindow, self).__init__(parent)
        self.parent = parent

        #********画面作成
        self.create_notebook()       
        self.pack()

    def create_notebook(self):
        # ノートブック
        self.nb = ttk.Notebook(self)

        # タブの作成
        self.tab1 = tk.Frame(self.nb)
        self.nb.add(self.tab1, text='入力', padding=3)
        self.nb.pack(expand=1, fill='both')

        #*******検索条件ページ画面(タブ1)
        # ページ表題ラベルの設定
        self.label1_1 = tk.Label(self.tab1,text="【検索条件】",font=("",16),height=2)
        self.label1_1.pack(fill="x")

         # ふりがなのラベルとエントリーの設定
        self.frame1_1 = tk.Frame(self.tab1,pady=10)
        self.frame1_1.pack()
        self.label1_1 = tk.Label(self.frame1_1,font=("",14),text="フリガナ")
        self.label1_1.pack(side="left")
        self.entry1_1 = tk.Entry(self.frame1_1,font=("",14),justify="center",width=15)
        self.entry1_1.pack(side="left")

        # 検索ボタンの設定
        self.button1_4 = tk.Button(self.tab1,text="検 索",font("",14),width=5,bg="gray",command=self.show_datalist)
        self.button1_4.pack()

        #**********データベース接続
        self.conn = sqlite3.connect("PyPostDb.db")
        self.cur = self.conn.cursor()

    # ****検索実行ボタンが押された処理
    def show_datalist(self):
        huri = str.strip(self.entry1_1.get())

        #*********検索条件文の作成
        sql = "SELECT * FROM PyPostTbl WHERE PyPostTbl.Huri LIKE '%" + huri + "%'"

        # SELECT文で取得した各レコードを繰り返し取得
        self.cur.execute(sql)
        r = self.cur.fetchall()
        if r==None:
            self.lblStatus.configure(text="登録データはありません。")
        else:
            for row in r:
                print(row[2],row[4])

    def quit(self, event=None):
        # 終了時の処理

        #self.conn.close()
        self.master.destroy()

application = tk.Tk()
application.geometry("1000x600")        
application.title('PyPost')
window = MainWindow(application)
application.protocol('WM_DELETE_WINDOW', window.quit)
application.mainloop()

///////////  エラーとなる三分割後コード(parent.py  child1.py  child2.py)
///親コード(parent.py)

# ******* parent.py
import tkinter as tk
import tkinter.ttk as ttk
import sqlite3

import child1 as ch1
import child2 as ch2

class MainWindow(ttk.Frame):
    def __init__(self, parent):
        super(MainWindow, self).__init__(parent)
        self.parent = parent

        #**********データベース接続
        self.conn = sqlite3.connect("PyPostDb.db")
        self.cur = self.conn.cursor()

# *********** ここにインポートしたモジュールを置きました *******************
        #********画面作成
        ch1.create_notebook(self)       
        self.pack()

        # ****検索実行ボタンが押された処理
        ch2.show_datalist(self)

    def quit(self, event=None):
        # 終了時の処理
        self.conn.close()
        self.master.destroy()

application = tk.Tk()
application.geometry("1000x600")        
application.title('PyPost')
window = MainWindow(application)
application.protocol('WM_DELETE_WINDOW', window.quit)
application.mainloop()


//////子コード1(child1.py)

#**** child1.py
import tkinter as tk
import tkinter.ttk as ttk
import sqlite3

import child2 as ch2

# **********画面作成
def create_notebook(self):
        # ノートブック
        self.nb = ttk.Notebook(self)

        # タブの作成
        self.tab1 = tk.Frame(self.nb)
        self.nb.add(self.tab1, text='入力', padding=3)
        self.nb.pack(expand=1, fill='both')

        #*******検索条件ページ画面(タブ1)
        # ページ表題ラベルの設定
        self.label1_1 = tk.Label(self.tab1,text="【検索条件】",font=("",16),height=2)
        self.label1_1.pack(fill="x")

         # ふりがなのラベルとエントリーの設定
        self.frame1_1 = tk.Frame(self.tab1,pady=10)
        self.frame1_1.pack()
        self.label1_1 = tk.Label(self.frame1_1,font=("",14),text="フリガナ")
        self.label1_1.pack(side="left")
        self.entry1_1 = tk.Entry(self.frame1_1,font=("",14),justify="center",width=15)
        self.entry1_1.pack(side="left")

        # 検索ボタンの設定
        self.button1_4 = tk.Button(self.tab1,text="検索",font=("",14),width=5,bg="gray",command=ch2.show_datalist)
        self.button1_4.pack()


//// 子コード2(child2.py)

# **** child2.py
import tkinter as tk
import tkinter.ttk as ttk
import sqlite3

import child1 as ch1

# ******各種アクション集

def show_datalist(self):
        huri = str.strip(self.entry1_1.get())

        #*********検索条件文の作成
        sql = "SELECT * FROM PyPostTbl WHERE PyPostTbl.Huri LIKE '%" + huri + "%'"

        # SELECT文で取得した各レコードを繰り返し取得
        self.cur.execute(sql)
        r = self.cur.fetchall()
        if r==None:
            self.lblStatus.configure(text="登録データはありません。")
        else:
            for row in r:
                print(row[2],row[4])

(4) //////////// 分割前はフォームが表示された後、「フリガナ」を入力し「検索」ボタンをクリックすると結果が表示されます。
これに対し分割後はフォームが表示された後、「フリガナ」を入力する以前に全登録データが表示されます。その後「フリガナ」を入力し「検索」ボタンをクリックすると下記エラーが表示され検索結果は表示されません
raceback (most recent call last):
File "C:\Users\akiteru\AppData\Local\Programs\Python\Python38\lib\tkinter\init.py", line 1883, in call
return self.func(*args)
TypeError: show_datalist() missing 1 required positional argument: 'self'

(5) /////// PyPostTblの定義
DB Browser for SQLiteで表示されるテーブルの定義は下記の通りです。

CREATE TABLE "PyPostTbl" (
"Id"    INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"KuwakeId"    INTEGER NOT NULL,
"Name"    TEXT NOT NULL,
"Huri"    TEXT NOT NULL UNIQUE,
"Add1"    TEXT,
"Add2"    TEXT,
"Add3"    TEXT,
"PostNo"    TEXT,
"Tel1"    TEXT,
"Tel2"    TEXT,
"Email"    TEXT,
"Nennga"    TEXT,
"Seibo"    TEXT,
"Bikou1"    TEXT,
"Bikou2"    TEXT
);

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • hoshi-takanori

    2020/03/14 18:08 編集

    質問は編集できますので、ソースコードを code 機能を使ってちゃんと表示されるようにしてください。このままだとどこからどこまでがどのファイルかわからないし、python ではインデントが重要なのですが、インデントも失われているので。やり方は、以下の記事を参考にしてください。
    https://apuridasuo.hatenablog.com/entry/2019/10/20/220308

    キャンセル

  • y_waiwai

    2020/03/14 18:12

    このままではコードが読めないので、質問を編集し、<code>ボタンを押し、出てくる’’’の枠の中にコードを貼り付けてください

    キャンセル

  • akiteru

    2020/03/16 10:57

    やっと「編集」ボタンを発見し、その後もトラブりましたがほぼ理解いただける状態かな?と思われる投稿になりました

    キャンセル

  • akiteru

    2020/03/16 16:59

    PyPostTblの定義を追加しました

    キャンセル

回答 1

checkベストアンサー

+1

エラーの該当箇所

# child1.py
# 検索ボタンの設定 ... のボタンが押されたときに実行されるcommand
command=ch2.show_datalist        

原因: "インスタンス・メソッド"と"関数"の違い

ボタンが押されたときに実行されるのは  ch2.show_datalist() (関数呼び出し)なので
引数 self がなく呼び出される為に、エラーが出ています。

解決策:
手短な解決策としては、ボタンが押されたときに呼び出されるコードに
引数を渡すようにすることで解消出来ます。

command=lambda:ch2.show_datalist(self)

original.py の元のコード command=self.show_datalist は、インスタンスメソッドの呼び出しで 
メソッド呼び出しでは、引数の self は省略可能になる為、引数を渡す必要はありません。


改善案:
メソッドの分割について

別ファイルの関数を呼び出す方法で分割されていましたが、
これをMainWindowクラスのメソッドとして呼び出す方法を紹介します。
(オブジェクト指向の用語について学習されているのを前提として)

※これはおすすめの分割方法という訳ではなく、Pythonのクラスの実装でのみ使えるTipsです。


import child1 as ch1
import child2 as ch2

class MainWindow(ttk.Frame):

    # 別ファイルで定義した関数を、インスタンスメソッドにする。
    create_notebook = ch1.create_notebook  # ここで from child1 import create_notebook でも良い
    show_datalist = ch2.show_datalist

    def __init__(self, parent):
        super(MainWindow, self).__init__(parent)
        self.parent = parent

        #**********データベース接続
        self.conn = sqlite3.connect("PyPostDb.db")
        self.cur = self.conn.cursor()

# *********** ここにインポートしたモジュールを置きました *******************
        #********画面作成
        self.create_notebook()  # <-- ch1.create_notebook(self)
        self.pack()

        # ****検索実行ボタンが押された処理
        self.show_datalist()  # <-- ch2.show_datalist(self)

    def quit(self, event=None):
        # 終了時の処理
        self.conn.close()
        self.master.destroy()

同じクラスのインスタンスメソッドとすることで、
self を通じて別ファイルで定義した関数を、メソッドとして呼び出す事が出来ます。
>メソッド呼び出し→selfを渡す関数呼び出し といった変更作業が不要になります。


補足: 上記の分割方法について注意点
IDEやドキュメント生成ツールではメソッドと認識されない可能性があります。
あまり多用する分割方法ではなく、飽くまで「メソッドを関数にして (インスタンス)self を渡す」の代用です。
オブジェクト指向に於いては、役割毎に別クラスに分割するのが一般的です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/03/17 17:07

    ありがとうございました。「解決策」にて希望の動作しました。「lambda」は知らなかったので、これから勉強します。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る