Python + Kivy GUI サンプルプログラム #001

Python


# コード
# -*- coding: utf-8 -*-
# - - - - - - - - - - - - - - (Kivy)
import os
from tkinter import Image
from turtle import color
import japanize_kivy                        # kvファイル内で日本語を扱えるようにする
from kivy.app import App
# from kivy.core.window import Window
from kivy.config import Config
from matplotlib.pyplot import box, cla
Config.set("graphics", "width", "1080")
Config.set("graphics", "height", "690") # 高さはタイトルバー(30px)分だけ引いておくと想像の大きさと一致する
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition, SlideTransition
from kivy.properties import StringProperty, NumericProperty, ObjectProperty 
from kivy.uix.widget import Widget          # 
from kivy.uix.scatter import Scatter          # 
from kivy.uix.image import Image          # 
from kivy.uix.label import Label            # #003
from kivy.uix.button import Button          # #004
from kivy.uix.textinput import TextInput    # #005
from kivy.uix.boxlayout import BoxLayout

from kivy.core.window import Window

# 下記の操作が必要
# pip install kivy-garden
# garden install matplotlib --kivy
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg


import numpy as np
import cv2
import matplotlib.pyplot as plt
import matplotlib


# グローバル変数の定義
text_window_title: str = "study_widget"     # window上部のタイトルバーに表示する文字列
list_menu_tag: list = ['MainScreen', 'sub'] # 生成する予定の画面の名前(プログラム上で使用する名前)
path_file_tmp = StringProperty(None)
object_sm = ObjectProperty()
flag_graph_01:bool = False
object_01 = ObjectProperty()
text_01 = StringProperty("0")
flag_drop_01:bool = False

# 
my_orenge: str  = "#e87f23ff"
my_green:str    = "#313f41ff"
my_blue:str     = "#193549ff"
my_yellow:str   = "#eeeedeff"
my_lightblue:str= "#cde3e5cc"   # 半透明
my_gray:str     = "#d9d9d9ff"
my_clear:str    = "#00000000"
size_font_graph: int = 5
size_spines_graph: int = 1.5

# ---自作関数の定義---------------------------------------------- #

#- - - - - - - - kivyオブジェクトの流用するための設定 - - - - - - - - -#

# - - Screen の設定を継承した設定を定義 - - -
class MyScreen(Screen):
    # python内で変数や関数の設定を追加変更しない。default状態。
    pass

# - - MyScreen の設定を継承した Screen の設定を定義 - - -
class MainScreen(MyScreen):
    pass

# - - 左側のレイアウト - - 
class LeftSide_Main(BoxLayout):
    pass
class Showing_Image(BoxLayout):
    pass
class Showing_Graph(BoxLayout):
    pass


# - - 右側のレイアウト - - 
class RightSide_Main(BoxLayout):
    pass
class MyTextInput(TextInput):
    # kivyスクリプト内で呼び出す変数
    global text_01
    path_image = StringProperty(f"{os.path.dirname(__file__)}"+r"\elements\background_active_image_1.png")

    def on_text_validate(self):
        global text_01
        text_01 = self.text
        print("C")
        print(text_01)


# GUIのクラス定義:スクリーンをGUI内部でインスタンス化
# ここのクラス名はkvファイルの名前と同一にすること: gui_analysis_image_l_value.kv
# App はアプリケーションそのもの
class gui_analysis_image_l_value(App):

    global text_window_title
    global list_menu_tag
    global path_file_tmp
    global flag_graph_01
    global text_01
    global flag_drop_01
    global my_orenge
    global my_green
    global my_lightblue
    global my_clear
    global size_font_graph
    global size_spines_graph
    
    # アプリの初期化設定
    def __init__(self, **kwargs):
        super(gui_analysis_image_l_value, self).__init__(**kwargs)
        self.title = text_window_title
        print("initiarized!")

    # アプリの内部構造設定: 今回はスクリーンマネージャーのみを組み込む
    def build(self):
        global object_sm

        self.sm = ScreenManager(transition=NoTransition())      # アニメーションなしで画面を瞬時に切り替える設定
        self.sm.add_widget(MainScreen(name=list_menu_tag[0]))   # 各スクリーンに名前をつけて管理する

        Window.bind(on_drop_file=self.on_dropped_file)           # ウィンドウ上でドロップイベントを使用できるようにする
        # # サイン波のデータを用意する
        # x = np.linspace(-np.pi, np.pi, 100)
        # y = np.sin(x)
        # # 描画する領域を用意する
        # fig, ax = plt.subplots()
        # # プロットする
        # ax.plot(x, y)
        # # Figure#canvas をウィジェットとして追加する
        # main_screen.add_widget(fig.canvas)

        print(f"今表示している画面は{self.sm.current_screen}")
        print("built!")

        return self.sm


    def test(self):
        print("self.test():クラスgui_analysis_image_l_value内のtest関数が呼び出されました")

    def clicked_button_01(self):
        self.test()
        print("test")
    
    def clicked_button_02(self):
        global path_file_tmp
        global flag_graph_01
        global object_01
        global text_01
        global flag_drop_01
        global my_orenge
        global my_green
        global my_lightblue
        global my_clear
        global size_font_graph
        global size_spines_graph
        
        if(flag_drop_01 == False):
            print(f"flag_drop_01={flag_drop_01}\nplease,drop a image file!")
            return
        else:
            pass

        if(flag_graph_01 == False):
            flag_graph_01 = True
            print("A")                              # デバッグ用
        elif(flag_graph_01 == True):
            object_01.parent.clear_widgets()        # もうすでにグラフがある場合は削除
            print("B")                              # デバッグ用
            
        box1 = BoxLayout()
        object_01 = box1
        
        print(path_file_tmp)                        # 評価画像のファイルパスの確認
        evafileName = path_file_tmp
        Img_eva=cv2.imread(evafileName,0)           # 評価画像を openCVを利用して読み込み
        e_numpy = np.array(Img_eva)                 # numpy配列に変換
        Mr = e_numpy.shape[0]                       # 行
        Mc = e_numpy.shape[1]                       # 列
        e_img_size = Mr * Mc                        # データサイズ(評価画像の縦横ピクセル数)
        
        pixel_pos_y:int = 0                         # データの pixel_pos_y行目ピクセルの数列を選択
        tmp = self.root.children[0].children[0].children[0].children[2].children[0].text
        print(tmp)
        pixel_pos_y = int(tmp)

        x = (list)(range(0, Mc))                    # 横軸: [px]
        y = e_numpy[pixel_pos_y]                    # 縦軸: L値

        fig, ax = plt.subplots()
        
        # グラフのデザイン設定
        ax.plot(x, y, label="L_value", color=my_orenge,linewidth=0.5)
        ax.set_facecolor( my_clear )                # グラフ内部の背景色を透明に設定
        ax.tick_params( direction = "inout",        
                        length = size_font_graph, 
                        colors = my_lightblue
                        )
        ax.spines['left'    ].set_color(my_lightblue)           # グラフの軸の色の設定
        ax.spines['bottom'  ].set_color(my_lightblue)
        ax.spines['right'   ].set_color(my_clear)
        ax.spines['top'     ].set_color(my_clear)
        ax.spines['left'    ].set_linewidth(size_spines_graph)  # グラフの軸の線幅の設定
        ax.spines['bottom'  ].set_linewidth(size_spines_graph)
        
        fig.set_facecolor( my_clear )               # グラフ外部余白の背景色を透明に設定

        # 目盛りの範囲
        xmin = 0
        xmax = Mc
        ymin = 0
        ymax = 255
        # plt.axis(xmin, xmax, ymin, ymax)
        plt.xlim(xmin, xmax)
        plt.ylim(ymin, ymax)

        ax.text(xmin-200, ymax+5, "L_value", color=my_lightblue)
        ax.text(xmax-700, ymin+5, "image_position[px]", color=my_lightblue)

        # plt.subplots_adjust(left=0, right=1, bottom=0, top=1)

        graph_01 = FigureCanvasKivyAgg(fig)         # kivyの要素としてアプリに組み込む

        
        self.root.children[0].children[0].children[1].children[0].add_widget(box1)

        box1.add_widget(graph_01)

        # print(f"fig={fig}")
        # print(x)
        # print(y)
        # print(f"{e_numpy[pixel_pos_y].size}")
        # print(f"size={e_img_size}[px],width={Mc}[px],height={Mr}[px]")

        print("clicked_button_02()_done!")

    def on_dropped_file(self, window, file_path, x, y):
        
        global path_file_tmp
        global flag_drop_01

        if(flag_drop_01 == False):
            flag_drop_01 = True

        path_file_tmp = file_path.decode('utf-8')   # on_drop_fileのリターン(file_path)はバイナリなのでutf-8への変換が必要
        print(self.root.children[0].children[0].children[1].children[1].children[0].source)
        self.root.children[0].children[0].children[1].children[1].children[0].source = path_file_tmp
        print("droped!")
        print(f"self.root.children[0].children[0].children[1].children[0].children[0]=\n{ self.root.children[0].children[0].children[1].children[1].children[0]}")
        # print(f"Showing_Graph.children[0]=\n{ self.root.children[0].children[0].children[1].children[0].children[0]}")
        print(path_file_tmp)                        # print(f"self.root.children[0]={self.root.children[0]}") # MainScreen


# ---機能上のメイン処理----------------------------------------------- #

def main():
    gui_analysis_image_l_value().run()

# ----------------------------------------- application_start

if __name__ == '__main__':
    main()

# ----------------------------------------- application_end__

# ------------------------------------------------------------------- #
print("done!")

# - - - - - - - - - - - - - - - - - - - - - - - - - -
# end_of_file: this line is

Kivy


# コード
#: kivy 2.1.0

#: import get_color_from_hex kivy.utils.get_color_from_hex

# AnchorLayout
# BoxLayout
# GridLayout
# ScrollView

<MyScreen>:
    # 画面の背景色設定
    canvas.before:
        Color:
            rgba: get_color_from_hex('#193549ff') #E6E4E0FF 
        Rectangle:
            size: self.size
            pos: self.pos

<MyTextInput>:

    # プロパティ
    text: "0"                    # プレーンテキスト用のボックス(テキストインプット)内にデフォルトので表示するテキスト
    
    halign:     'left'          # 水平(横)方向の位置揃え: horizontal align(整列)
    valign:     'center'        # 鉛直(縦)方向の位置揃え: vertical   align(整列)
    width:      "60px"         # テキストインプットの横幅
    height:     "30px"         # テキストインプットの縦幅
    size_hint:  (None,None)     # テキストインプットの縦横比: Noneを指定することで上記横幅・縦幅が適用される
    pandding:   ( "0px",  "0px")# border領域"の内側の余白を設定
    margin:     ( "0px",  "0px")# border領域"の外側の余白を設定
    # pos:        ("90px","400px")# テキストインプットの位置 (原点は左下)
    font_size:  "16px"           # テキストの大きさ 

    input_filter:"int"           # 入力可能な型を制限する

    multiline:  False            # デフォルト: True のとき Enterで改行される,  Falseのときでは入力を終了する(改行不可能でフォーカスが外れる)
        
    # 以上のプロパティ以外にもいくつも項目が存在する。

# windowの横幅: 1080[px]
<MainScreen>:
    BoxLayout:
        orientation:    "horizontal"
        LeftSide_Main:
        RightSide_Main:


<LeftSide_Main>:
    orientation:"vertical"
    Label:
        id: label_main
        font_size: "24px"
        text: "LeftSide_Main"
        height:     "24px"
        size_hint: (1, None)

    Showing_Image:
    Showing_Graph:

<Showing_Image>:
    height: "300px"
    Image:
        source: ".\elements\outline_post_add_white_48dp_2x.png"
        kepp_ratio: True
        width: "48px"

<Showing_Graph>:
    height: "324px"
    orientation:"vertical"
    # Label:
    #     id: Showing_Graph_Label
    #     font_size: "24px"
    #     text: "Showing_Graph"
    #     height:     "24px"
    #     size_hint: (1, None)
    # Image:
    #     source: ".\elements\outline_show_chart_white_48dp_2x.png"
    #     kepp_ratio: True
    #     width: "48px"


<RightSide_Main>:
    width:      "260px"#"1080px"
    size_hint: (None, 1)
    orientation: "vertical"

    BoxLayout:
        height: "30px"
        size_hint:  (1,None)     

        Label:
            id: label_main
            font_size: "24px"
            text: "RightSide_Main" 

    BoxLayout:
        orientation: "horizontal"
        height: "30px"
        size_hint:  (1,None)    
        Label:
            font_size: "16px"
            text: "高さ方向のピクセル指定:"
            height: "30px"

        MyTextInput:
            id:                 mti_01
            background_active:  self.path_image
            on_text_validate:   
                print("on_text_validate occurred!") # multiline=Flaseのときにのみ機能する(発生する)イベント

    BoxLayout:
        height: "30px"
        size_hint:  (1,None)
        

        BoxLayout:
            height: "30px"
            size_hint:  (0.05,None)

        Button:
            text:       "OK: creating_graph"
            width:      "240px"         # ボタンの横幅
            height:     "30px"          # ボタンの縦幅
            size_hint:  (0.9,None)           # ボタンの縦横比: Noneを指定することで上記横幅・縦幅が適用される
            on_press:   app.clicked_button_02() # appを継承した gui_analysis_image_l_value() クラスの中の関数を実行

        BoxLayout:
            height: "30px"
            size_hint:  (0.05,None)

    BoxLayout:

EOF

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

トップに戻る