À propos de la création de l'interface graphique à l'aide de TKinter de Python

Déclencheur

Je voulais faire une recherche Grep pour Excel en utilisant Python, et je faisais beaucoup d'essais et d'erreurs. C'est bien d'avoir créé une application Windows appelée "Excel Grepy" techniquement, mais je ne peux pas bien créer une interface utilisateur. .. .. ..

D'une certaine manière, la conception de la mise en page est gênante.

Il y avait plusieurs options pour configurer l'interface utilisateur.

Hormis TKinter adopté cette fois, Kivy, wxPython, PySimpleGUI, etc.

Il existe de nombreux sites qui comparent chacun, alors jetez un œil.

Image de mise en page Excel Grepy

L'image approximative ressemble à ceci:

image.png

Maintenant, comment exprimer cela en Python.

La configuration de la mise en page TKinter signifie

Eh bien, il existe différents moyens, mais reportez-vous au site suivant ... https://www.delftstack.com/ja/tutorial/tkinter-tutorial/tkinter-geometry-managers/

1.pack() On a l'impression que le formulaire est considéré comme un écran unique et organisé dans quatre directions. Je ne comprends pas vraiment.

2.grid() C'est comme diviser le formulaire verticalement et horizontalement et définir la grille, qui est similaire à la structure de mise en page du HTML? Non, c'est difficile pour les personnes atteintes de VB. Et c'est plus original que HTML. Je ne comprends pas vraiment.

3.place() C'était le plus simple à comprendre. Mais c'est ennuyeux. Pourquoi en 2020, je dois spécifier toute la hauteur de largeur X Y.

Résultat de l'examen (grille)

Pour le moment, Grid semblait être le plus simple, alors je l'ai essayé!

image.png

Oui, cliquetis.

Résultat d'examen (pack)

Alors essayez avec Pack. image.png

Eh bien, ça ne se passe pas comme prévu.

J'ai donc décidé de créer un Framework.

Si vous les essayez et les combinez de manière agréable, la composition de l'écran en Python aura fière allure, non? Le concept est comme ça.

・ Un écran est divisé à l'avance et la mise en page est composée d'images de lignes et de colonnes. ・ Divisez la mise en page en 10x10 afin que la mise en page puisse être placée de manière intuitive. -La mise en page peut être spécifiée comme X, Y, Col_SPAN, ROW_SPAN. ・ Pour prendre en charge la mise à l'échelle de l'écran

image.png

J'ai utilisé "3.place ()" pour implémenter cela. Après de nombreuses recherches, j'ai trouvé une propriété qui peut être réglée proportionnellement à la largeur de l'écran et au ressenti.

・ Relex: rapport de largeur d'écran en coordonnées X ・ Rely: rapport de la hauteur de l'écran des coordonnées Y -Relwidth: rapport de la largeur de l'objet à la largeur de l'écran ・ Relheight: rapport entre la hauteur de l'objet et la hauteur de l'écran

Chacun a un maximum de 1,0, et s'il est de 0,1, sa taille est de 1/10. -Taille de l'écran: largeur = 1000px hauteur = 600px Dans ce cas, 0,1 correspond à 100 pixels, 0,05 correspond à 50 pixels.

L'avantage de cette propriété est qu'elle prend en charge la mise à l'échelle. Si vous modifiez la largeur de l'écran, chaque objet changera également.

Résultat: cela ressemble à ceci. image.png

Plein écran: image.png

Comme c'est gentil!

La source ressemble à ceci.

Je me demande s'il peut être commercialisé s'il est un peu amélioré.

Tout d'abord, le côté FrameWork:

TKinterK.py



# -*- coding: utf-8 -*-

####importer
import tkinter as tk
import tkinter.ttk as ttk


class FormK(tk.Tk):
    pass

    def __init__(self, p_max_row, p_max_col, p_padding):
        super(FormK, self).__init__()
        
        ##Propriétés de mise en page
        self.MAX_ROW = p_max_row
        self.MAX_COL = p_max_col
        self.PAD_OUT = p_padding
        self.PAD_IN  = p_padding

        #Réglage constant
        self.CONST_MSG_ICON_INFO = 1
        self.CONST_MSG_ICON_ALERT = 2
        self.CONST_MSG_ICON_ERROR = 3
         
        self.CONST_MSG_QUES_YES_NO = 1
        self.CONST_MSG_QUES_OK_CANCEL = 2
        self.CONST_MSG_QUES_RETRY_CANCEL = 4

    ##Réglage de la taille de l'écran à la définition
    def geometry(self,newGeometry=None):
        super(FormK, self).geometry(newGeometry)
        sp = newGeometry.split("x")
        self.WIDTH  = int(sp[0])
        self.HEIGHT = int(sp[1])


    ##messagerie
    def MsgBox(self,p_msg,p_title,p_icon,p_ques):

        #Valeur de retour valeur initiale
        o_res = None

        if (p_ques == None):
            if (p_icon == self.CONST_MSG_ICON_INFO):
                messagebox.showinfo(p_title,p_msg)
            if (p_icon == self.CONST_MSG_ICON_ALERT):
                messagebox.showwarning(p_title,p_msg)
            if (p_icon == self.CONST_MSG_ICON_ERROR):
                messagebox.showerror(p_title,p_msg)
        if (p_ques == self.CONST_MSG_QUES_YES_NO):
            if (p_icon == self.CONST_MSG_ICON_INFO):
                o_res = messagebox.askyesno(p_title,p_msg)
            if (p_icon == self.CONST_MSG_ICON_ALERT):
                o_res = messagebox.askyesno(p_title,p_msg)
            if (p_icon == self.CONST_MSG_ICON_ERROR):
                o_res = messagebox.askyesno(p_title,p_msg)
        if (p_ques == self.CONST_MSG_QUES_OK_CANCEL):
            if (p_icon == self.CONST_MSG_ICON_INFO):
                o_res = messagebox.askokcancel(p_title,p_msg)
            if (p_icon == self.CONST_MSG_ICON_ALERT):
                o_res = messagebox.askokcancel(p_title,p_msg)
            if (p_icon == self.CONST_MSG_ICON_ERROR):
                o_res = messagebox.askokcancel(p_title,p_msg)
        if (p_ques == self.CONST_MSG_QUES_RETRY_CANCEL):
            if (p_icon == self.CONST_MSG_ICON_INFO):
                o_res = messagebox.askretrycancel(p_title,p_msg)
            if (p_icon == self.CONST_MSG_ICON_ALERT):
                o_res = messagebox.askretrycancel(p_title,p_msg)
            if (p_icon == self.CONST_MSG_ICON_ERROR):
                o_res = messagebox.askretrycancel(p_title,p_msg)
        return o_res

    ##Placer un objet
    def set_layout(self):

        n_height_in = self.HEIGHT - (self.PAD_OUT * 2)
        n_height_one = (n_height_in - ((self.MAX_ROW - 1) * self.PAD_IN)) / self.MAX_ROW

        n_width_in = self.WIDTH - (self.PAD_OUT * 2)
        n_width_one = (n_width_in  - ((self.MAX_COL - 1) * self.PAD_IN)) / self.MAX_COL
        for v in self.children:
            try:
                if self.children[v].layout != None:
                    sp = self.children[v].layout.split(",")

                    self.children[v].place_configure(
                        relx     =round((float(self.PAD_OUT) + ((int(sp[0])-1) * n_width_one)  + ((int(sp[0]) - 1) * self.PAD_IN)) / self.WIDTH ,4)
                       ,rely     =round((float(self.PAD_OUT) + ((int(sp[1])-1) * n_height_one) + ((int(sp[1]) - 1) * self.PAD_IN)) / self.HEIGHT ,4)
                       ,relwidth =round(((int(sp[2]) * n_width_one)  + ((int(sp[2]) - 1) * self.PAD_IN)) / self.WIDTH ,4)
                       ,relheight=round(((int(sp[3]) * n_height_one) + ((int(sp[3]) - 1) * self.PAD_IN)) / self.HEIGHT ,4)
                    )
            except:
                print("No TkinterK Object(" + v +").")
                pass


        pass

class ButtonK(tk.Button):
    pass

    def __init__(self):
        super(ButtonK, self).__init__()
        self.layout = None


class EntryK(tk.Entry):
    pass

    def __init__(self):
        super(EntryK, self).__init__()
        self.layout = None
        self["highlightthickness"] = 1
        self.config(highlightcolor= "red")

class ProgressbarK(ttk.Progressbar):
    pass

    def __init__(self):
        super(ProgressbarK, self).__init__()
        self.layout = None

class LabelK(tk.Label):
    pass

    def __init__(self):
        super(LabelK, self).__init__()
        self.layout = None

class TreeviewK(ttk.Treeview):
    pass

    def __init__(self):
        super(TreeviewK, self).__init__()
        self.layout = None

Puis Excel Grepy:

ExcelGrepy.py



# -*- coding: utf-8 -*-

####importer
import os
import tkinter as tk
import tkinter.ttk as ttk
import openpyxl as px
import subprocess

import TKinterK as tkk


from tkinter import messagebox
from tkinter import filedialog
from pathlib import Path





####paramètres du cadre racine
root = tkk.FormK(20,10,10)
root.title("Excel Grepy")
root.geometry("1000x800")

#Couleur de l'arrière plan
root.bg = '#B7E899'

root.configure(background=root.bg)

root.result = tk.StringVar()


#Paramètres de style
style = ttk.Style() 
style.configure('TButton', font = 
               ('calibri', 20, 'bold'),
                    borderwidth = '4') 
  
# Changes will be reflected 
# by the movement of mouse. 
style.map('Button'
         , foreground = [('active', '!disabled', 'green')]
         , background = [('active', 'black')]
         )





####Fonction d'événement d'écran

#Boîte de dialogue de sélection de dossier
def btnFolderDir_click():
    root = tk.Tk()
    root.withdraw()
    iDir = ""
    file = filedialog.askdirectory(initialdir = iDir)
    
    #Sortie du nom du fichier de traitement
    if file != "":
        txtPath.delete(0, tk.END)
        txtPath.insert(0, file)

#Bouton Effacer
def btnClear_click():

    #Initialisation du message
    root.result.set("")

    #Initialisation de divers chemins
    txtPath.delete(0, tk.END)
    txtStr.delete(0, tk.END)

    #initialisation de la grille
    x = tree.get_children()
    for item in x:
        tree.delete(item)

    #Mise à jour de la barre de progression (initialisation)
    progress.configure(value=0, maximum=100)
    progress.update()


#Bouton d'examen
def btnCheck_click():

    #Initialisation du message
    root.result.set("")

    #initialisation de la grille
    x = tree.get_children()
    for item in x:
        tree.delete(item)

    #Acquisition de paramètres
    p_temp = Path(txtPath.get())

    #Vérification des erreurs
    if txtPath.get() == "":
        messagebox.showerror("Erreur", "Sélectionnez le dossier que vous souhaitez rechercher.")
        return

    cnt = 0
    for i in p_temp.glob('**/*.xlsx'):
        row_data =[i.name, '-', i]
        tree.insert("","end",tags=cnt,values=row_data)
        cnt += 1

    if cnt == 0:
        root.result.set(str(cnt) + "Le fichier xlsx n'existait pas dans ce dossier.")
    else:
        root.result.set(str(cnt) + "Trouvé 1 fichiers!")


#Bouton Grep
def btnGrep_click():

    #Initialisation du message
    root.result.set("")

    #initialisation de la grille
    x = tree.get_children()
    for item in x:
        tree.delete(item)

    #Acquisition de paramètres
    p_temp = Path(txtPath.get())
    s_str = txtStr.get()

    #Vérification des erreurs
    if txtPath.get() == "":
        messagebox.showerror("Erreur", "Sélectionnez le dossier que vous souhaitez rechercher.")
        return
    if s_str == "":
        messagebox.showerror("Erreur", "Veuillez saisir la chaîne de caractères à rechercher.")
        return

    cnt = 0
    prg_cnt = 0
    max_cnt = 0

    #Comptez le nombre de résultats de recherche
    for i in p_temp.glob('**/*.xlsx'):
        max_cnt += 1

    #Définir la barre de progression
    progress.configure(value=prg_cnt, maximum=max_cnt)

    for i in p_temp.glob('**/*.xlsx'):
        #Ouvrez le fichier Excel d'argument
        wb = px.load_workbook(i, data_only=True)
        for nm in wb.get_sheet_names():
            ws = wb[nm]
            value_matrix = str(list(ws.values))
            value_matrix = value_matrix.replace('(None','')
            value_matrix = value_matrix.replace('None), ','')
            value_matrix = value_matrix.replace(', None','')
            if (s_str in str(value_matrix)):
                row_data =[i.name, nm, i]
                tree.insert("","end",tags=cnt,values=row_data)
                cnt += 1
        
        #Mise à jour de la barre de progression
        prg_cnt += 1
        progress.configure(value=prg_cnt)
        progress.update()

    #Mise à jour de la barre de progression (FIN)
    progress.configure(value=max_cnt, maximum=max_cnt)
    progress.update()

    if cnt == 0:
        root.result.set("La chaîne de recherche n’existait pas dans le dossier.")
    else:
        root.result.set(str(cnt) + "Trouvé 1 fichiers!")

#arborescence double-cliquez
def tree_row_dclick(self):
    #Obtenir des données de ligne
    selected_items = tree.selection()
    row_data = tree.item(selected_items[0])
    #Obtenez le chemin
    row_value = row_data['values']
    file_path = row_value[2]
    #Fichier ouvert
    #print (file_path)
    subprocess.Popen(['start', file_path], shell=True)




####Création d'objets d'écran

# 1.Paramètres du menu
btnQuit = tkk.ButtonK()
btnQuit["text"] = "Fin"
btnQuit["command"] = root.destroy
btnQuit.layout = "10,1,1,1"


#Génération d'étiquettes
lblProg = tkk.LabelK()
lblProg["text"] = "le progrès"
lblProg["bg"] = root.bg
lblProg["anchor"] = "e"
lblProg.layout = "1,1,1,1"

progress = tkk.ProgressbarK()
progress.configure( value=0
                  , mode='determinate'
                  , maximum=1000
                  , length=600)
progress.layout = "2,1,8,1"


## 2 row ################################################
#Génération d'étiquettes
lblFilePath = tkk.LabelK()
lblFilePath["text"] = "Chemin du dossier"
lblFilePath["bg"] = root.bg
lblFilePath["anchor"] = "e"
lblFilePath.layout = "1,2,1,1"

#Zone de saisie(FilePath)
txtPath = tkk.EntryK()
txtPath.layout = "2,2,8,1"

#Bouton Parcourir
btnFolderDir = tkk.ButtonK()
btnFolderDir["text"] = "référence"
btnFolderDir["command"] = btnFolderDir_click
btnFolderDir.layout = "10,2,1,1"


## 3 row ################################################
#Génération d'étiquettes
lblFilePath = tkk.LabelK()
lblFilePath["text"] = "Chaîne de recherche"
lblFilePath["bg"] = root.bg
lblFilePath["anchor"] = "e"
lblFilePath.layout = "1,3,1,1"

#Caractère de recherche
txtStr = tkk.EntryK()
txtStr.layout = "2,3,8,1"

## 4 row ################################################
#Génération d'étiquettes
lblCond = tkk.LabelK()
lblCond["text"] = "Résultats de recherche"
lblCond["bg"] = root.bg
lblCond["anchor"] = "e"
lblCond.layout = "1,4,1,1"

lblCondResult = tkk.LabelK()
lblCondResult["textvariable"] = root.result
lblCondResult["anchor"] = "w"
lblCondResult.layout = "2,4,8,1"


## 5 row ################################################

#Processus de recherche
btnGrep = tkk.ButtonK()
btnGrep["text"] = "Grep"
btnGrep["command"] = btnGrep_click
btnGrep.layout = "10,5,1,1"

btnCheck = tkk.ButtonK()
btnCheck["text"] = "Examen minutieux"
btnCheck["command"] = btnCheck_click
btnCheck.layout = "9,5,1,1"

btnCheck = tkk.ButtonK()
btnCheck["text"] = "clair"
btnCheck["command"] = btnClear_click
btnCheck.layout = "1,5,1,1"


## 6-20 row ################################################
#Créer une arborescence
tree = tkk.TreeviewK()

tree["columns"] = (1,2,3)
tree["show"] = "headings"
tree.column(1,width=100)
tree.column(2,width=75)
tree.column(3,width=100)
tree.heading(1,text="nom de fichier")
tree.heading(2,text="Nom de la feuille")
tree.heading(3,text="Chemin du fichier")
tree.bind('<Double-1>', tree_row_dclick)
tree.layout = "1,6,10,15"

#Organiser les objets selon la disposition
root.set_layout()

#Boucle principale
root.mainloop()

Commentaire de la source

Ce que vous faites est simple:

1. Définissez le formulaire (racine)

root = tkk.FormK(20,10,10) Cet argument est (MAX_ROW, MAX_COL, PADDING).

2. Définissez la taille d'écran du formulaire

root.geometry("1000x800") Je l'utilise tel quel, mais le Framework conserve la largeur et la hauteur comme tailles d'écran.

3. Créer + définir la propriété "layout" pour chaque objet

'# 1. Paramètres du menu btnQuit = tkk.ButtonK() btnQuit ["text"] = "fin" btnQuit["command"] = root.destroy

Le bouton de fin est organisé. Lors de la définition originale du Button, il est possible de définir des propriétés dans l'argument au moment de la définition, Je ne peux pas bien le faire avec ce cadre. Veuillez définir chacun après avoir créé l'objet.

btnQuit.layout = "10,1,1,1"

Cela crée des boutons pour la 10e colonne, la 1ère ligne, 1/10 de largeur et 1/10 de hauteur.

4. Après avoir défini chaque objet, appelez la fonction root.set_layout pour définir la disposition

root.set_layout()

Si vous n'écrivez pas ceci, l'écran sera vide.

Contenu de 5.4

Eh bien, il est plus rapide de regarder le code, donc je ne vais pas l'expliquer en détail. -Calculer la taille d'une colonne et la taille d'une ligne à partir de la taille de l'écran et du nombre de divisions lorsque FormK est défini. -Split la propriété layout de chaque objet par ',' et calculez la largeur que vous souhaitez afficher -En fonction du résultat du calcul, redéfinissez OK avec Object.place_configure (...)

Seulement ça. J'ai lutté avec la formule pendant un moment, mais c'est tout.

À propos du développement futur

Je pense développer un framework pour ceux qui créent actuellement des applications Windows et ceux qui créent des applications Python avec diverses fonctions.

Je pense qu'il existe un moyen de l'élever vers Git et de le développer, mais combien de personnes ont la même opinion?

Il n'y a aucune motivation pour créer quelque chose qui n'a pas de besoins w Il y a des choses qui n'ont pas encore atteint le plein potentiel de Python ... Eh bien, la mise en page est intuitivement facile à comprendre, donc si elle devient un jour courante, j'aurai une plus large gamme de technologies. .. .. En premier lieu, est-il acceptable de transformer l'outil GUI en un framework et de le licencier? ?? w

Eh bien, restez à l'écoute w

Recommended Posts

À propos de la création de l'interface graphique à l'aide de TKinter de Python
Créer une interface graphique python à l'aide de tkinter
Création d'interface graphique en python avec tkinter 2
Création d'interface graphique en python à l'aide de tkinter partie 1
Bases de l'écran d'entrée / sortie en utilisant tkinter en python3
[Question] À propos de la conversion API du chat bot à l'aide de Python
À propos de divers encodages de Python 3
python: principes de base de l'utilisation de scikit-learn ①
À propos des fonctionnalités de Python
[Python GUI] Réglage du contraste DICOM et conversion BMP à l'aide de Tkinter
Programmation GUI en Python avec Appjar
Remarque Python: à propos de la comparaison en utilisant is
Capture d'image de Firefox en utilisant Python
Suppression de la brume à l'aide de Python detailEnhanceFilter
Implémentation des notifications de bureau à l'aide de Python
À propos de la liste de base des bases de Python
Python: principes de base de la reconnaissance d'image à l'aide de CNN
Collecte automatique des cours boursiers à l'aide de python
Traitement d'exécution périodique lors de l'utilisation de tkinter [Python3]
Pratique d'utilisation de ceci en Python (mauvais)
À propos de l'environnement virtuel de Python version 3.7
Mémorandum des débutants en python
Python: Application de la reconnaissance d'image à l'aide de CNN
Affichage externe des diagrammes matplotlib à l'aide de tkinter
Étude sur Tokyo Rent en utilisant Python (3-1 sur 3)
Note de problèmes sur la coexistence du système Python 2/3
[Python] Chapitre 02-04 Bases du programme Python (À propos des commentaires)
GUI affiche les informations de retard de train à l'aide de python
À propos des tranches Python
Essayez d'utiliser Tkinter
Analyse de la variation temporelle des trous noirs en utilisant Python
Reconnaissance d'accords à l'aide du chromagramme de la bibliothèque de python librosa
À propos de Python tqdm.
À propos du rendement Python
À propos de python, classe
Outil de rognage d'image GUI réalisé avec Python + Tkinter
Commencez à utiliser Python
Introduction de la bibliothèque d'imagerie Python (PIL) à l'aide de HomeBrew
À propos de l'héritage Python
Encodage de caractères lors de l'utilisation du module csv de python 2.7.3
Les bases de Python ①
Bases de python ①
Écrire une note sur la version python de python virtualenv
À propos de python, range ()
Copie de python
Pensez à créer un environnement Python 3 dans un environnement Mac
À propos de Python Decorator
[Note] À propos du rôle du trait de soulignement "_" en Python
Essayez d'utiliser le module de collections (ChainMap) de python3
Téléchargement anonyme d'images à l'aide de l'API Imgur (à l'aide de Python)
Télécharger des images sur S3 avec une interface graphique à l'aide de tkinter
À propos de la référence Python
Étude introductive sur Python-Sortie des données de vente à l'aide de tapple-
À propos des décorateurs Python
[Python] À propos du multi-processus