プログラムでおかえしできるかな

定年を過ぎて何かの役に立てないかなと始めた元SEのブログです

SQLクライアントアプリの作り方(Tkinterで表)【Python】

このエントリーをはてなブックマークに追加

PythonGUIアプリの勉強を兼ねてSQL Client(SQLクライアント)を作成しました。
GUIにはTkinterを、データベースにはPostgreSQLをライブラリにはpsycopg2を使用しました。
Tkinterでは良く使用するウィジェットの他に、表表示にTreeviewを使用しました。
これらの使い方をサンプルコードで説明します。
また、サンプルはアプリとして実行できます。

目次

■アプリのサンプル画面

SQL Client treeview

▼エラーがある時
SQL Client treeview

■できること

SQLを実行しその結果を画面に表示します。

おまけ的に CSV 出力もできます。

□画面表示の特徴

画面表示には次の特徴があります。

Tkinterの使用ウィジェット

Tkinter がサポートする GUI の部品をウィジェット (widget) と呼びます。

ここでは次のウィジェットを使用しています。
これらの使い方も後程説明します。

■必要なもの

Pythonの必要なパッケージ

  • psycopg2

TkinterPythonの標準パッケージです。

□データベース - PostgreSQL

データベースにはPostgreSQLを使用しています。
無料なのと使用経験があったので選びました。

外部サイト「日本PostgreSQLユーザ会 | 日本PostgreSQLユーザ会」から日本語マニュアル、インストーラなどを入手できます。

外部サイト「PostgreSQL Tutorial - Learn PostgreSQL from Scratch」のチュートリアルにサンプルデータベースが提供されています。
データベースは外部サイト「PostgreSQL Sample Database」から取得できます。

PostgreSQLの学習にはこのチュートリアルとサンプルデータベースが大変役に立ちます。お勧めです‼
英語ですが翻訳サイトで翻訳しながらでもなんとか進められると思います。

PostgreSQLの導入などについての解説は諸先輩に譲ります。

Tkinterの使い方

Tkinter(ティーキンター)は Python が標準で提供している GUI パッケージです。
他のプログラムの開発ツールで提供されているようなルック&フィールな GUI ツールではありません。
従って、画面を構成するには、すべてコードを書く必要があります。

Tkinter を使用するには import が必要です。

   import tkinter as tk

慣例的に tk と別名を付けるようです。

□初歩的な使い方

ウィンドウを出すだけのサンプルです。
Python を起動して次の命令を入れるとウィンドウが出てきてアプリらしさをちょっとだけ体験できます。

import tkinter as tk     #tkと省略することが慣例のようです  
win = tk.Tk()               #ウィンドウの作成と表示  
win.title("Hello, World")    #タイトル設定  
win.geometry("400x300")      #ウィンドウサイズ設定  
lbl = tk.Label(win, text="ラベル")    #ラベルウィジェットの作成  
lbl.pack()                  #ラベルウィジェットの配置  
win.mainloop()              #イベント待ち  

□使用ウィジェット

ウィジェットは基本的にインスタンスを作成し、表示テキストや表示方法などを指定して使用します。

今回使用しているウィジェットです。

パッケージ ウィジェット 用途 見た目
tkinter Frame
フレーム
ウィジェットの受け皿
tkinter Label
ラベル
文字の表示
tkinter Entry
エントリー
テキストを入力するボックス
tkinter Button
ボタン
押すと処理が動くボタン
tkinter Checkbutton
チェックボックス
チェックでオン/オフを設定
tkinter Scrollbar
スクロールバー
画面をスクロール
tkinter.ttk Treeview
ツリービュー
ツリー表示やリスト表示
ここではリスト表示で使用

ウィジェットに変数を関連付ける

ウィジェットの値と連動して変更される変数をウィジェット変数と呼びます。
今回は、SQL 入力用エントリー、CSV 出力チェック用チェックボックス、メッセージ表示用ラベルにウィジェット変数を関連付けます。

ウィジェット変数とウィジェットを関連付けるには、ウィジェットの属性 variable, textvariable にウィジェット変数を指定します。
ウィジェット変数のコンストラクタに value= 引数で初期値を与えられます。
ウィジェット変数の値の読み取りは get() メソッドで、値の書き込みは set() メソッドで行います。

他に、DoubleVar, BooleanVar があります。

【コード】
        self.sql = tk.StringVar(value="select * from customer limit 5")
        self.ety_sql = tk.Entry(parent, textvariable=self.sql)
        
        self.var_csv = tk.IntVar(value=0)
        self.ckb_csv = tk.Checkbutton(parent, text="CSV出力", variable=self.var_csv)

        self.msg = tk.StringVar(value="msg")
        self.lbl_msg = tk.Label(parent
                                , textvariable=self.msg
                                , justify=tk.LEFT
                                , font=("Fixedsys", 11)
                                , relief=tk.RIDGE
                                , anchor=tk.W)

□画面構成 - フレーム構造

プログラムを作るにあたって、どのような画面にするか決めなくてはなりません。画面構成を考えるということですね。
画面を構成するというのは、GUI 部品(ウィジェット)を画面に配置することです。
ウィジェットは設定したテキストに合わせてサイズが決まるので、そのまま使用しても画面構成上問題ないものと、広げないと見栄えが良くないものが出てきます。
ウィジェットを広げる場合、他のウィジェットとの位置関係で思ったように配置できないことがあります。
そのようなときに Frame ウィジェットを使用して区分けをして希望する位置に配置します。
引き出しの整理に使うトレイのようなイメージでしょうか。

今回の画面構成です。

  • u_frame(frame)
    • lbl_sql(Label)
    • ety_sql(Entry)
    • btn_exe(Button)
    • ckb_csv(Checkbutton)
    • lbl_msg(Label)
  • b_frame(frame)
    • tree(Treeview)
    • hscrlbar(Scrollbar)

※「u_frame」などはプログラムで使用した変数名です。
※図形の中の矢印はこの後説明するオプション fill の方向です。

入力をする部分と出力をする部分で大きくフレームを分けています。
結果を表示する部分は余った領域をすべて使用してできるだけ大きく表示させるためです。

ウィジェットの配置 - pack

ウィジェットインスタンスを作成した後に配置 (pack) をしないと表示されません。
配置には他に grid と place がありますが、基本的な pack を使用します。
pack とは、スーツケースに荷物をパッキングするようなイメージなんじゃないかなと勝手に思っています。

ウィジェットを pack すると side オプションで指定された方向から配置します。
その時に配置した方向と直行する方向には可能な限りの領域を割り当てます。
例えば、デフォルトの TOP で配置すると直行する横方向に可能なだけ領域を確保します。横一杯になるということですね。
ただし、これは領域の話で、実際のウィジェットの表示は他のオプションに依ります。

  • 【構文】 ウィジェット.pack(オプション1 = 設定値, オプション2 = 設定値,・・・)

  • 【主なオプション】

    オプション 説明 設定値
    fill 割り当て領域内での引き伸ばし NONE:なし、X:水平方向、Y:垂直方向、BOTH:両方向
    expand 割り当て領域の詰め込み方向への拡大 True:する、False:しない
    side 次の方向から詰め込む TOP:上(デフォルト)、BOTTOM:下、LEFT:左、RIGHT:右


その他のオプション

オプション 説明 設定値
after 指定ウィジェットの後に配置 他のウィジェット
before 指定ウィジェットの前に配置 他のウィジェット
anchor 割り当て領域内の配置位置 NW,N,NE,W,CENTER,E,SW,S,SE(方角)
in 配置する親
ipadx,ipady 内部に空ける間隔 間隔(単位無しはピクセル)
padx,pady 外側に空ける間隔 間隔(単位無しはピクセル)

pack で「よく表示されない」という問題が発生しますが、私もそうでした、pack の順番を変えてあげることで解決する場合があるようです。

必ずではないようですが、配置の時に次のことを考慮しておくと良いみたいです。

  • expand オプションを True に指定して pack するウィジェットは、後から pack する。

ウィジェットの pack についてはこちらのサイトが詳しいです。

SQL の実行をウィジェットに割り当てる

ウィジェットで何か操作した場合にそれに連動して動作するメソッドを割り当てることができます。
この動作(イベント)とメソッドを割り当てることをバインドと呼びます。
イベントには、キー入力可能なウィジェットでキー入力した場合やマウス操作した場合、ボタンをクリックした場合などがあります。

キーバインド

入力ボックスのエントリーウィジェットで Enter を押した時に SQL が実行されるようにイベント登録をします。

  • 【構文】 ウィジェット.bind(シーケンス, コールバック関数, 追加フラグ)

  • 引数

    • シーケンス:イベント名(例:<Return>, <Button-1>)
    • コールバック関数:関数には event 引数が必要
      ウィジェットの command オプションと共通の場合は、関数の引数定義を event=None とする
    • 追加フラグウィジェットに対してイベントを置き換えるか追加するか
      デフォルトは置き換え(引数を省略した場合)
       self.ety_sql.bind("<Return>", self.execute_sql)
    
    def execute_sql(self, event=None):

◎ボタンウィジェット

ボタンウィジェットでは、ボタンを押した時に動作する処理を割り当てられます。
オプションの command で引数なしの関数を指定します。

   tk.Button(parent, text="実行", command=self.execute_sql)  

※引数が必要な場合はラムダ式を使用します。(詳細は省略)

移動:2021-11-06

■画面の作成

先ほどの画面構成を踏まえて画面を作成します。

【処理】

  1. Frame の作成
    上側用:入力用テキストボックスや実行用ボタン用
    下側用:Treeview とスクロールバー用

  2. Frame の配置 (pack)

  3. 上フレームの中身の作成 ( create_input_frame メソッド)

    1. SQL:」文字用ラベル作成
    2. SQL 入力用ウィジェット変数の作成
    3. 入力用エントリー作成
    4. 実行ボタン作成
    5. チェックボックスウィジェット変数作成
    6. CSV 出力用チェックボックス作成
    7. メッセージ用ウィジェット変数作成
    8. メッセージ用ラベル作成
    9. ウィジェットの配置
    10. キーバインドの作成
  4. 下フレームの中身の作成 ( create_tree_frame メソッド)

    1. Treeviewの作成
    2. スクロールバーの作成
    3. ウィジェットの配置

【コード】

▼フレームの作成

        self.u_frame = tk.Frame(master, bg="blue")     # 背景色を付けて配置を見る
        self.b_frame = tk.Frame(master, bg="green")    # 背景色を付けて配置を見る
        self.u_frame.pack(fill=tk.X)
        self.b_frame.pack(fill=tk.BOTH, expand=True)
        self.create_input_frame(self.u_frame)
        self.create_tree_frame(self.b_frame)

▼上フレームの中身の作成

    def create_input_frame(self, parent):
        """
        入力項目の画面の作成
        上段:SQL入力ボックス、実行ボタン、CSV出力チェックボックス
        下段:メッセージ
        """
        self.lbl_sql = tk.Label(parent, text="SQL:")
        self.sql = tk.StringVar(value="select * from customer limit 5")
        self.ety_sql = tk.Entry(parent, textvariable=self.sql)
        self.btn_exe = tk.Button(parent, text="実行", command=self.execute_sql)
        self.var_csv = tk.IntVar(value=0)
        self.ckb_csv = tk.Checkbutton(parent, text="CSV出力", variable=self.var_csv)
        self.msg = tk.StringVar(value="msg")
        self.lbl_msg = tk.Label(parent
                                , textvariable=self.msg
                                , justify=tk.LEFT
                                , font=("Fixedsys", 11)
                                , relief=tk.RIDGE
                                , anchor=tk.W)
        self.lbl_msg.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)    #先にpackしないと下に配置されない
        self.lbl_sql.pack(side=tk.LEFT, fill=tk.BOTH)
        self.ety_sql.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.ckb_csv.pack(side=tk.RIGHT, fill=tk.Y)
        self.btn_exe.pack(side=tk.RIGHT)
        self.ety_sql.bind("<Return>", self.execute_sql)     #Enterキーを押しても動作するように

▼下フレームの中身の作成

    def create_tree_frame(self, parent):
        """
        Treeviewとスクロールバーをparentに作成する。
        Treeviewは、listview形式、行は縞模様
        Args:
            tk.Frame:   親frame
        """
        # tagを有効にするためstyleを更新 tkinter8.6?以降必要みたい
        # 表の文字色、背景色の設定に必要
        self.style = ttk.Style()
        self.style.map('Treeview', foreground=self.fixed_map('foreground')
                                 , background=self.fixed_map('background'))
        # Treeviewの作成
        self.tree = ttk.Treeview(parent)
        self.tree["show"] = "headings"      # listview形式の指定
        self.tree.tag_configure("odd", background="ivory2")
        # スクロールバーの作成
        self.hscrlbar = tk.Scrollbar(parent, orient=tk.HORIZONTAL, command=self.tree.xview)
        self.tree.configure(xscrollcommand=self.hscrlbar.set)
        # pack 横一杯にする方を先にpackしないと見えなくなる
        self.hscrlbar.pack(side=tk.BOTTOM, fill=tk.X)
        self.tree.pack(fill=tk.BOTH, expand=True)

■表の実装(Treeviewウィジェットの使い方)

Treeview ウィジェットはツリー形式や表形式でデータを表示するウィジェットです。
Treeview ウィジェットtkinter.ttk パッケージに入っているので使用には import が必要です。

   import tkinter.ttk as ttk

慣例的に ttk と別名を付けるようです。

表形式で表示する時の基本的な使い方です。

  1. インスタンス作成
  2. 配置 (pack)
  3. 表示形式の指定 (treeview["show"])
  4. 列の定義 (treeview.column)
  5. 見出しの設定 (treeview.heading)
  6. データ挿入 (treeview.insert)

この章では次の内容について説明します。

この章での説明

追加:2021-10-04

□表(Listview)形式の指定

Treeview はツリー形式で表示する部分と、表形式で表示する部分を持っています。
両方、どちらか片方だけの表示を選択できます。
デフォルトは両方を表示するので表形式だけを指定します。
指定は、インスタンスの show オプションか、Treeview のコンストラクタの show= 引数で行います。

        self.tree["show"] = "headings"      # listview形式の指定
        # or
        ttk.Treeview(parent, show="headings")
オプション名 説明 設定値
show 表示対象を指定 ・"tree":ツリー表示部分の表示
・"headings":表形式部分の表示
・["tree","headings"]:すべて(デフォルト)

□1行おきに背景色を設定する

行修飾を行うにはタグ (tag) を使用します。
タグはタグ名で区別して属性の異なるタグを複数設定できます。
行に対しても複数のタグを設定できます。

タグで使用できる主な属性 (オプション)

オプション名 説明
background 背景色
foreground 前景色
font フォント

◎タグの使い方

タグ設定とデータ追加はどちらが先でも動作します。

  • treeviewにタグ (tag) を設定
    treeview.tab_configure(タグ名, オプション)
  • タグを指定してデータ追加
    データ追加時:treeview.insert(…, tags=タグ名)

◎背景色指定時のバグ

リンクのサイトにあるように Treeview の色付けにはバグがあるようです。
fixed_map メソッドを再定義して style の map メソッドを実行する必要があるようです。

【コード】

▼Treeview の色設定のバグフィクス

    def fixed_map(self, option):
        # Fix for setting text colour for Tkinter 8.6.9
        # From: https://core.tcl.tk/tk/info/509cafafae
        #
        # Returns the style map for 'option' with any styles starting with
        # ('!disabled', '!selected', ...) filtered out.

        # style.map() returns an empty list for missing options, so this
        # should be future-safe.
        return [elm for elm in self.style.map('Treeview', query_opt=option) if
            elm[:2] != ('!disabled', '!selected')]
        # tagを有効にするためstyleを更新 tkinter8.6?以降必要みたい
        # 表の文字色、背景色の設定に必要
        self.style = ttk.Style()
        self.style.map('Treeview', foreground=self.fixed_map('foreground')
                                 , background=self.fixed_map('background'))

▼タグの設定

        # Treeviewの作成
        self.tree = ttk.Treeview(parent)
        self.tree["show"] = "headings"      # listview形式の指定
        self.tree.tag_configure("odd", background="ivory2")

▼タグを指定してデータ追加

        # treeviewに要素追加。背景はtagを切り替えて設定
        self.tree.delete(*self.tree.get_children())    # Treeviewをクリア
        for i, row in enumerate(rows):
            _tags = []              # tag設定値の初期化
            if i & 1:               # 奇数か? i % 2 == 1:
                _tags.append("odd") # 奇数番目(treeviewは0始まりなので偶数行)だけ背景色を変える(oddタグを設定)
            self.tree.insert("", tk.END, values=row, tags=_tags)

□列の定義

Treeview で列を操作するには、列の定義が必要です。
これは見出し名とは別で列操作のための識別子(カラム識別子)です。
指定は、インスタンスの columns オプションか、Treeview のコンストラクタの colums= 引数で行います。

   treeview = Treeview(parent)
    treeview["columns"] = ["列1", "列2", "列3"]
    # or
    ttk.Treevew(parent, columns=["列1", "列2", "列3"])

実際のコードでは、SQL の結果から列名を取り出して設定します。

【コード】

        columns = [desc.name for desc in cur.description]   # descriptionのname要素がカラム名

        self.tree["columns"] = columns                  # treeviewの列定義を設定

□見出しの設定

見出しの設定は、heading メソッドで行います。

  • 【構文】 treeview.heading("列1", text="列名")

列定義で指定した名前で列を指定して1列ずつ設定します。

合せて列の幅も見出しの文字の長さで設定します。
Font クラスの measure メソッドで文字列からピクセル値を取得し、
column メソッドで width= 引数を指定して設定します。

【コード】

        font1 = tkFont.Font()
        for col_name in columns:
            self.tree.heading(col_name, text=col_name)  # 見出しの設定
            width1 = font1.measure(col_name) + 10       # 見出しの文字幅をピクセルで取得
            self.tree.column(col_name, width=width1)    # 見出し幅の設定

□データの挿入

データの挿入は、insert メソッドで行います。

  • 【構文】 treeview.insert(parent, index, iid=None, オプション)
  • よく使う使い方
    【構文】 treeview.insert("", tk.END, value=データ列の値 (リスト))
  • 引数
    • parent:挿入するデータの親。"" で最上位
    • index:挿入位置。tk.END で最後尾に追加
    • iidNone(省略も同じ)で iid の作成をお任せ
      "I001" から順に割り当てる
      ※iid はアイテムごとのユニークな識別子
  • オプション
    • values:カラム順のカラムデータ(リスト)
    • image:画像(PhotoImage)オブジェクトを指定
    • text:ツリーカラムのテキストを指定
    • tags:タグを指定
    • open:子アイテムの展開を指定
更新:2022-03-06

【コード】

    tree.insert("", tk.END, values=row, tags=tags1)     # Treeviewに1行分のデータを設定
追加:2021-12-10

□列の幅の自動調整

Treeview の列の幅は、列名で設定します。

  • 【構文】 treeview.column(列名, width=幅)

列の幅は設定する文字の長さを計測します。

   import tkinter.font as tkFont
    tkFont.Font().measure(文字列)  

【処理】

  1. 見出しの文字幅で設定

    1. 見出しの個数分、以下を繰り返す
      1. 見出しの文字列の幅を計算
      2. 列幅として設定
  2. 結果セットの文字幅で再設定

    1. 列の個数分、以下を繰り返す
      1. 同じ列のあるデータを文字に変換しその長さが最も長いデータを求めます
        max([x[i] for x in rows], key=lambda x:len(str(x)))
        同じ列のデータをリストにして max 関数で長さを調べる
      2. 求めたデータの文字幅を計算
        font1.measure(max_str)
      3. 現在の列幅の設定値を取得
        tree.column(self.tree['columns'][i], width=None)
        width=None で呼び出す
      4. 求めた文字幅が現在の列幅より大きい時は列幅を再設定
        tree.column(self.tree['columns'][i], width=width1)

【コード】

        font1 = tkFont.Font()
        for col_name in columns:
            self.tree.heading(col_name, text=col_name)  # 見出しの設定
            width1 = font1.measure(col_name) + 10       # 見出しの文字幅をピクセルで取得
            self.tree.column(col_name, width=width1)    # 見出し幅の設定
        font1 = tkFont.Font()
        for i, _ in enumerate(rows[0]):
            # 同じ列のデータをリストにし列の値の長さを求め、最大となる列のデータを求める。
            # 値は数字もあるので文字に変換し長さを求める。
            max_str = max([x[i] for x in rows], key=lambda x:len(str(x)))
            width1 = font1.measure(max_str) + 10   # 見出しの文字幅をピクセルで取得
            header1 = self.tree.column(self.tree['columns'][i], width=None) # 現在の幅
            if width1 > header1:
                self.tree.column(self.tree['columns'][i], width=width1)    # 見出し幅の再設定

■スクロールバーの設置

スクロールバーはスクロールバーウィジェットを作成してスクロールさせたいウィジェットと関連付けします。

□コンストラク

  • 【構文】 tk.Scrollbar(親ウィジェット, オプション)
  • よく使うオプションを使った時の構文
    【構文】 tk.Scrollbar(parent, orient=tk.HORIZONTAL, command=対象ウィジェット.xview)
    【構文】 tk.Scrollbar(parent, orient=tk.VERTICAL, command=対象ウィジェット.yview)

  • 【主なオプション】

    オプション 説明 設定値
    orient スクロールの方向 HORIZONTAL:水平、VERTICAL:垂直
    command スクロール対象ウィジェットの移動メソッド メソッド名
    width スクロールバーの幅
    (水平の場合は高さ、垂直の場合は幅)
    デフォルトは16
更新:2022-03-06

□対象ウィジェットの割り付け

  • 【構文】 対象ウィジェット.configure(xscrollcommand=横Scrollbarウィジェット.set)
  • 【構文】 対象ウィジェット.configure(yscrollcommand=縦Scrollbarウィジェット.set)
追加:2021-09-28
更新:2022-03-06

【処理】

  1. インスタンス作成
    Scrollbar(parent, orient=tk.HORIZONTAL, command=self.tree.xview)

    • 第一引数で対象のウィジェットを指定
    • orient で垂直 (VERTICAL) 、水平 (HORIZONTAL) を指定
    • command でウィジェットの方向を指定 ※垂直方向の場合は x が y
  2. スクロールバーを対象ウィジェットに関連付け
    treeview.configure(xscrollcommand=self.hscrlbar.set)

    • xscrollcommand は水平スクロール発生時の呼び出しメソッドを指定
      ここにスクロールバーの set メソッドを指定
      ※垂直方向の場合は x が y 、h が v

【コード】

        # スクロールバーの作成
        self.hscrlbar = tk.Scrollbar(parent, orient=tk.HORIZONTAL, command=self.tree.xview)
        self.tree.configure(xscrollcommand=self.hscrlbar.set)

PostgreSQL(psycopg2)への接続

□データベース接続

データベースの接続情報は、環境変数に別途登録しておきます。

  • 環境変数に登録する接続情報
    • py_sqlc_dbname=データベース名
    • py_sqlc_user=ユーザー名
    • py_sqlc_pass=パスワード
    • py_sqlc_port=ポート

【処理】

  1. 環境変数を取得(get_auth_info メソッド)

  2. コネクションオブジェクトを作成(psycopg2.connect メソッド)
    引数で接続情報を指定

【コード】

    KEY_DBNAME = "py_sqlc_dbname"
    KEY_USER = "py_sqlc_user"
    KEY_PASS = "py_sqlc_pass"
    KEY_PORT = "py_sqlc_port"

    def get_auth_info(self) -> Tuple[str, str, str, str]:
        """
        環境変数を読んで値を返す
        Returns:
            str:    データベース名
            str:    ユーザー名
            str:    パスワード
            str:    ポート
        """
        dbname1 = os.getenv(self.KEY_DBNAME)
        user1 = os.getenv(self.KEY_USER)
        pass1 = os.getenv(self.KEY_PASS)
        port1 = os.getenv(self.KEY_PORT)
        return dbname1, user1, pass1, port1

    def get_connection(self):
        """
        PostgreSQLに接続
        Returns:
            psycopg2.connection:    コネクション
        """
        dbname1, user1, pass1, port1 = self.get_auth_info()
        conn = psycopg2.connect(
        dbname=dbname1
        , user=user1
        , password=pass1
        , port=port1)
        return conn

SQLを実行

exe_sqlメソッドを作成して対応しています。
SQLを引数で受け取ります。
戻り値として、結果セット、カラム名リスト、エラーメッセージを返します。

【処理】

  1. データベースコネクションの取得(get_connectionメソッド)
    自動的にクローズするようにwith文にします。

  2. カーソルオブジェクトの作成(cursorメソッド)
    自動的にクローズするようにwith文にします。

  3. SQLの実行(excuteメソッド)

  4. 結果セットの取得(fetchallメソッド)
    すべての結果の行が取得できます。
    1行分がタプルになったリストで返ります。

  5. カラム名の取得
    [desc.name for desc in cur.description]
    カーソルオブジェクトのdescriptionのname要素がカラム名
    内包表記を使用してリストにします。

  6. 例外処理
    例外が発生した場合は、エラーメッセージを返します。

【コード】

    def exe_sql(self, sql:str, w_csv:bool) -> Tuple[list, list, str]:
        """sqlを実行し、結果を返す
        Args:
            str:    SQL文字列
            bool:   CSV出力する(true:出力、false:未出力)
        Returns:
            list:   カラム定義
            list:   結果セット
            str:    エラーメッセージ(空文はエラーなし)
        """
        try:
            with self.get_connection() as conn:
                with conn.cursor() as cur:
                    _msg = ""
                    cur.execute(sql)        # SQLの実行
                    rows = cur.fetchall()   # 結果セットの取得。タプルのリストで返る
                    columns = [desc.name for desc in cur.description]   # descriptionのname要素がカラム名
                    if w_csv:
                        CsvManage().write_csv(columns, rows)    # CSV作成
                    return columns, rows, _msg
        except psycopg2.DatabaseError as e:
            print("DB error", e)
            _msg = e
        except Exception as e:
            print("例外エラー", e)
            _msg = e
        return [], [], _msg

■全体のソース

全体のソースはこちらから取得できます。
取得先:GitHub juu7g/Python-SQL-Client

■バイナリ

pyinstaller でバイナリ( exe ) ファイルを作成しました。

  • 作成コマンド: pyinstaller -F --noconsole ファイル名

□バイナリ取得先

バイナリも公開します。
こちらから取得してください。Githubからダウンロード

□使用時の設定

環境変数PostgreSQL に接続する際の接続情報を次のように設定してください。

  • 環境変数に登録する接続情報
    • py_sqlc_dbname=データベース名
    • py_sqlc_user=ユーザー名
    • py_sqlc_pass=パスワード
    • py_sqlc_port=ポート

■さいごに

SQL クライアントプログラムの作成方法を紹介しました。
GUITkinter を使い、Treeview で表形式のデータ出力を実現しました。
動作するアプリの作り方を説明しながら機能の説明をしています。
どっちつかずで分かりにくいようでしたらご指摘いただけると幸いです。

Phthon の勉強は使えるアプリを作りながら進めています。
その方が、具体的に解決したい問題が見つかって勉強になるからです。
有ったら使いそうなアプリを見つけるのが結構大変です。
今のところは自分で不便に感じているところで探しています。
今後は、想像力を働かせて、誰かの不便を助けられるものを見つけていきたいと思います。

そういえば、「Python」って直訳すると「ニシキヘビ」だと最近知りました。
Pythonのアイコンを考えた人は日本語を知っている人かな?
だってアイコンは「ニヒキヘビ」でしょ。

あわせて読みたい - Tkinter関連記事

更新:2021-11-06

□ご注意

本記事は次のバージョンの下で動作した内容を基に記述しています。

ご利用に際しては、『免責事項』をご確認ください。
お気づきの点がございましたら『お問い合わせ』からお問い合わせください。
更新:2022-07-22

■参考

投稿: 、更新: