While referring to various information on the net, I also studied GUI and worked on a GUI program that can display input / output + processing + graph.
Windows10 Python 3.7.7 Kivy 1.11.1 GUI seems to be popular in recent years, so I challenged Kivy.
The upper row is a button for file input / output and processing. Below the button is the graph display area. The graph is selected and displayed from the pull-down menu on the left.
Below is the code details. The code on the kivy side describes the contents related to the GUI (layout, file selection, function to be called when a button is pressed, etc.), and the code on the Python side describes the actual processing. First of all, Python
main.py
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import openpyxl
import os
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
class LoadDialog(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
Analysis = ObjectProperty(None)
current_dir = os.path.dirname(os.path.abspath(__file__))
class SaveDialog(FloatLayout):
save = ObjectProperty(None)
text_input = ObjectProperty(None)
cancel = ObjectProperty(None)
current_dir = os.path.dirname(os.path.abspath(__file__))
class MainBoard(BoxLayout):
file_name = ObjectProperty(None)
info = ObjectProperty(None)
bar_graph = ObjectProperty(None)
lbl4spinner = ObjectProperty([])
def __init__(self, **kwargs):
super(MainBoard, self).__init__(**kwargs)
self.master_flg = 0
self.lbl=[]
self.Data=[]
def dismiss_popup(self):
self._popup.dismiss()
def show_load(self):
content = LoadDialog(load=self.load, cancel=self.dismiss_popup, Analysis=self.Analysis)
self._popup = Popup(title="Load file", content=content,size_hint=(0.9, 0.9))
self._popup.open()
def show_save(self):
if self.master_flg==0:
self.info.text = 'Load masterfile first, please'
else:
content = SaveDialog(save=self.save, cancel=self.dismiss_popup)
self._popup = Popup(title="Save file", content=content, size_hint=(0.9, 0.9))
self._popup.open()
def load(self, path, filename, chkflg):
try:
if chkflg==1:
self.file_name.text=str(filename.pop(0))
else:
self.file_name.text=str(filename[0])
except Exception as e:
self.info.text=str(type(e))+' : '+str(e)
Matrix=pd.DataFrame([])
for i in range(len(filename)):
NewData = pd.read_excel(filename[i])
tmp_Data=NewData.iloc[0:,1:]
tmp_Data.index=NewData.iloc[:,0]
Matrix=pd.concat([Matrix,tmp_Data])
self.lbl=list(Matrix.index)
self.Data = Matrix
self.dismiss_popup()
def save(self, path, dirname):
with pd.ExcelWriter(os.path.join(path, dirname)) as writer:
self.Data.to_excel(writer)
self.dismiss_popup()
def Analysis(self):
try:
self.Data=self.Data*2
self.lbl4spinner=map(str,self.lbl)#List to pass to spinner
self.master_flg=1
self.info.text = 'Analysis completed!'
except Exception as e:
self.info.text=str(type(e))+' : '+str(e)
def on_spinner_select(self,text):
row_no=self.lbl.index(text)
Forgraph=self.Data.iloc[row_no,:]
plt.clf()
bar_plt=plt.bar(Forgraph.index, Forgraph)
self.ids.bar_graph.clear_widgets()
self.ids.bar_graph.add_widget(FigureCanvasKivyAgg(plt.gcf()))
class MainApp(App):
title = 'analysis'
def build(self):
return MainBoard()
if __name__ == '__main__':
MainApp().run()
Then Kivy
main.kv
<MainBoard>:
info: info
file_name: file_name
bar_graph: bar_graph
BoxLayout:
orientation: 'vertical'
pos: root.pos
size: root.size
canvas.before:
Color:
rgba: 0.9, 0.9, 0.9, 1
Rectangle:
pos: self.pos
size: self.size
Label:
id : info
text: 'load file first, please'
size_hint_y: 0.1
canvas.before:
Color:
rgba: 0, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'horizontal'
size_hint_y: 0.1
TextInput:
id: file_name
font_size: 12
size_hint_x: 0.7
BoxLayout:
orientation: 'horizontal'
size_hint_y: 0.1
Button:
text: 'Load & Analysis'
size_hint_x: 0.2
on_press: root.show_load()
Button:
text: 'Save data'
size_hint_x: 0.2
on_press: root.show_save()
Button:
text: "Exit"
id: btnExit
size_hint_x: 0.2
on_press: app.root_window.close()
BoxLayout:
orientation: 'horizontal'
size_hint_y: 0.7
Spinner:
text: "No."
values: root.lbl4spinner
size_hint: 0.1, 0.1
pos_hint: {'center_x': .5, 'center_y': .5}
on_text: root.on_spinner_select(self.text)
BoxLayout:
id: bar_graph
<LoadDialog>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooser:
id: filechooser
path:root.current_dir
multiselect: True
filters: ['*.xlsx']
FileChooserIconLayout
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Load selected file(s)"
on_press: root.load(filechooser.path, filechooser.selection,0)
on_release: root.Analysis()
Button:
text: "Load all files"
on_press: root.load(filechooser.path,filechooser.files,1)
on_release: root.Analysis()
Button:
text: "Cancel"
on_release: root.cancel()
<SaveDialog>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
BoxLayout:
size_hint_y: 0.1
Button:
text: 'ListView'
on_release: filechooser.view_mode = 'list'
Button:
text: 'IconView'
on_release: filechooser.view_mode = 'icon'
FileChooser:
id: filechooser
path:root.current_dir
on_selection: dir_name.text = self.selection and self.selection[0] or ''
filters: ['*.xls','*.xlsx']
FileChooserListLayout
TextInput:
id: dir_name
size_hint_y: None
height: 30
multiline: False
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Save"
on_release: root.save(filechooser.path, dir_name.text)
Button:
text: "Cancel"
on_release: root.cancel()
The base program uses Kivy's Filechooser. Filechooser itself has official documents and explanations of many ancestors, so it is omitted. I will explain only where I stumbled.
Due to the flow, the program does not flow from the top, but it goes back and forth between Kivy code and Py code, so I will explain along the flow.
--When you press the Load button on the GUI after starting the program, the Show Load function will be called from the Kivy side.
main.kv
Button:
text: 'Load & Analysis'
size_hint_x: 0.2
on_press: root.show_load()
--The Show load function launches LoadDialog in a separate window. At that time, the specified functions (Load function, Cancel function, Analysys function this time) in the MainBoard class are linked by content. (At first, I wondered why it was such a troublesome thing, but LoadDialog is a different class, so I can't call the associated function directly.)
main.py
def show_load(self):
content = LoadDialog(load=self.load, cancel=self.dismiss_popup, Analysis=self.Analysis)
self._popup = Popup(title="Load file", content=content,size_hint=(0.9, 0.9))
self._popup.open()
--Kivy's LoadDialog code. In FileChooser, select the file. This time, it is assumed that the original data is saved in xlsx format. It is filtered so that files in unnecessary formats are not displayed. Also, multiselect is specified as True so that multiple files can be selected. The id is a variable called filechooser that holds the address of the selected file.
main.kv
FileChooser:
id: filechooser
path:root.current_dir
multiselect: True
filters: ['*.xlsx']
FileChooserIconLayout
--Place 3 buttons following the above code. (1) To select and specify a file to be read by clicking (filechooser.selection) (2) To read all files in the current directory (filechooser.files) (3) Cancel button. When you press the buttons ① and ②, the load function is called first. The last number in the load function argument is the identification number (because it calls the same function).
main.kv
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Load selected file(s)"
on_press: root.load(filechooser.path, filechooser.selection,0)
on_release: root.Analysis()
Button:
text: "Load all files"
on_press: root.load(filechooser.path,filechooser.files,1)
on_release: root.Analysis()
Button:
text: "Cancel"
on_release: root.cancel()
--In the Load function on the Python side, use the file address (passed in List format) passed from Filechooser to extract data from xls. The following part is the code to display the address on the GUI. --At this time, if you read the files in the directory all at once, be careful because the directory address (files are not linked) is at the beginning of the list. For this, we have extracted the first section from the list. --```except Ececpiion as e: `` The part after `displays the contents when an error occurs.
main.pv
def load(self, path, filename, chkflg):
try:
if chkflg==1:
self.file_name.text=str(filename.pop(0))
else:
self.file_name.text=str(filename[0])
except Exception as e:
self.info.text=str(type(e))+' : '+str(e)
--Next, read the data. In this xlsx, the parameter name is entered in column A and the data number is entered in the first row.
--The for statement repeatedly reads the data of all the selected functions and finally stores them in the variables (matrix) `self.lbl``` and ``` self.Data```. --Finally, when you close
LoadDialog``` with ``
dismiss_popup ()` ``, the Load function is finished.
main.pv
Matrix=pd.DataFrame([])
for i in range(len(filename)):
NewData = pd.read_excel(filename[i])
tmp_Data=NewData.iloc[0:,1:]
tmp_Data.index=NewData.iloc[:,0]
Matrix=pd.concat([Matrix,tmp_Data])
self.lbl=list(Matrix.index)
self.Data = Matrix
self.dismiss_popup()
--In the LoadDialog on the kivy side, the Analysis function is called by on_release: root.Analysis () `` `following the Load function. --Here, describe the data processing you actually want to perform. This time, we simply double the data. --The following `` `self.lbl4spinner = map (str, self.lbl)
is the list to pass to the graph selection pull-down on the GUI.
-- self.master_flg
is a flag to see if the data has been read. It is used to judge whether or not to save.
main.pv
def Analysis(self):
try:
self.Data=self.Data*2
self.lbl4spinner=map(str,self.lbl)#List to pass to spinner
self.master_flg=1
self.info.text = 'Analysis completed!'
except Exception as e:
self.info.text=str(type(e))+' : '+str(e)
--First, the explanation on the kivy side. Use Spinner to display a pull-down menu.
-- text: "No." `` `specifies the characters to display before pulling down. --
values: root.lbl4spinner will display the list obtained in the analysis section when pulling down. --
on_text: root.on_spinner_select (self.text) `, call the on_spinner_select function on the python side with the item selected from the pull-down as an argument (
self.text```).
main.kv
Spinner:
text: "No."
values: root.lbl4spinner
size_hint: 0.1, 0.1
pos_hint: {'center_x': .5, 'center_y': .5}
on_text: root.on_spinner_select(self.text)
BoxLayout:
id: bar_graph
--The called on_spinner_select function creates a graph based on the argument information.
-- self.lbl.index (text) `` `Returns the order (number of lines) that matches the information selected in the pull-down. --
Forgraph = self.Data.iloc [row_no ,:] Reads the number of rows specified by --Create a graph with
bar_plt = plt.bar (Forgraph.index, Forgraph)``. This time I created a bar graph. --``` self.ids.bar_graph.add_widget (FigureCanvasKivyAgg (plt.gcf ())) ``
to graph to Kivy's
BoxLayout (
id: bar_graph) .. --
plt.clf ()
,
self.ids.bar_graph.clear_widgets ()` `` is for clearing the previous graph when switching the pull-down selection and redisplaying. is.
main.py
def on_spinner_select(self,text):
row_no=self.lbl.index(text)
Forgraph=self.Data.iloc[row_no,:]
plt.clf()
bar_plt=plt.bar(Forgraph.index, Forgraph)
self.ids.bar_graph.clear_widgets()
self.ids.bar_graph.add_widget(FigureCanvasKivyAgg(plt.gcf()))
5.Save --Save is omitted because it is processed in the same way as Load.
--As a preliminary preparation, in order to exchange between Kivy and Python, it is necessary to define Object at the beginning of the MainBoard class. In addition, on the Kivy side, it is necessary to associate Object and id.
main.py
class MainBoard(BoxLayout):
file_name = ObjectProperty(None)
info = ObjectProperty(None)
bar_graph = ObjectProperty(None)
lbl4spinner = ObjectProperty([])
main.kv
<MainBoard>:
info: info
file_name: file_name
bar_graph: bar_graph
--Also, the variables used between the functions are declared and summarized at the initialization stage to avoid duplication.
main.py
def __init__(self, **kwargs):
super(MainBoard, self).__init__(**kwargs)
self.master_flg = 0
self.lbl=[]
self.Data=[]
I especially referred to the Kivy-related information on the following site. kivy official document: Filechooser After all the first is the official document. Longing for freelance It was helpful as a starting point. The layout of this GUI is also referred to here. narito blog It was very helpful in understanding the relationship between Kivy and Python. stackoverflow : Python - Kivy framework - Spinner values list List display to Spinner, I think I would have been stuck without this post.
I use ids for graph drawing, but I want to describe this with Object Propaty.
Recommended Posts