TkInter'da GUI'yi Ayrı Dosyaya Bölme

:information_source: Eğer aynı veya benzer bir konu, zaten açılmışsa söyleyin. Bu zamana kadar forumda böyle bir konuya denk gelmedim ve forumda/internette arama yaparak da bulamadım.


Merhaba arkadaşlar.

Merak ettiğim bir şey var. Aynı PyQt5’te ki gibi, TkInter’da da, bir programın işlevsel kodları ile grafik arayüzünü ayrı sınıflarda/dosyalarda tutup, bir bütün halinde çalışması mümkün mü?

Eğer mümkün ise nasıl?
Şimdiden teşekkür ederim. :slight_smile:

evet mümkününatı vardır tabi bahsettiğinizden anladığım şeyi yapmaya çalışıyorsanız

Şuna benzer bir yapıdan, bahsediyorum:

Project/
|
+ > __main__.py
|
+ > iu.py

olur reis niye olmasın. Ben yapıyorum oluyor sen yaparsın sende de olur. import edersin fonksiyonu çağırırsın yapıştır gitsin

2

bak böyle bir projemin içerisinde çalışacak kodları ayrıca çağırıyorum. her bir işlem için bir fonksiyon tanımlı ayrı ayrı

O kadarını bende akıl ettim :grinning_face_with_smiling_eyes:, ama mesela, ui.py'de ki bir butonun command parametresine vereceğim fonksiyon/metod diğer dosyada kalıyor, iş karışıyor.

1 Beğeni

Tamam, bunda bir sıkıntı yok. fonksiyonu import edersiniz. Yalnız döngüsel import oluşturmamaya çalışın.

Bu, bana pek mantıklı gelmedi, basit bir örnek verebilir misiniz?

__main__.py/hesapla -----> ui.py/buton
^                              |
|                              v
+---------------<--------------+

Ne mantıklı gelmedi size?

Bir fonksionu başka bir dosyadan import edip, sonra o dosyayı tekrar import etmek.
Bu, hep bu şekilde mi yapılır yoksa başka bir yolu var mı?

Ne demek istediğinizi anlamadım.

Bazı fonksiyonları birden çok dosyada kullanmak istediğimizde bu fonksiyonları tek bir dosyada tutup, ihtiyaç duyulan dosyalarda import edebiliriz.

Bazı sınıflar veya sabitler de özel olarak bazı dosyalarda tutulabilir. Örneğin özel işlevlere sahip düğmeler, entryler veya başka widgetler tasarlayıp, bu widgetleri farklı dosyalarda tutabiliriz, sonra da başka dosyalar içerisinde uygun görülen yerlerde bu widgetleri import ederek kullanmak isteyebiliriz.

Yani belli bir gruba ait kod parçalarının belli dosyalarda olması projeyi yönetmek açısından daha avantajlı.

Bu dediğinize katılıyorum zaten o sebepten bu konuyu açtım. :slight_smile:


Birde cevabınızı beklerken, aklıma şöyle bir yöntem geldi:

__main__.py:

from tkinter import tk
from gui import GUI


class Window:
    def __init__(self, root):
        self.root = root
        self.gui = GUI(self.root, self)
        
        self.root.mainloop()

    def fonk(self):
        self.gui.buton.config(text="OK")


if __name__ == "__main__":
    Window(tk.Tk())

gui.py:

from tkinter import *


class GUI:
  def __init__(self, root, meta_self):
    self.buton = Button(root, text="Bas", command=lambda: meta_self.fonk())
    self.buton.pack()

Bu şekilde olabilir mi? veya bir dezavantajı var mıdır sizce?

Bunu tam olarak nasıl yaptınız? Yani, gui ile asıl kodları ayırıp, ikisi arasındaki ileşimi nasıl sağladınız?

Burada bir hata var. gui.py'nin içinde gui'yi import ediyorsunuz.

Size basit bir örnek göstereyim.

Projenin ağaç görünümünün aşağıda gösterildiği gibi olduğunu varsayalım.

.
├── run.py
└── Scripts
    ├── button.py
    ├── entry.py
    ├── __init__.py
    ├── modules.py
    ├── toplevel.py
    └── utilities.py

Bu yukarıdaki py dosyalarının içinde de sırayla aşağıdaki kodlar yer alıyor ve program da run.py'yi çalıştırınca açılıyor olsun.

Dosya: run.py

#!/usr/bin/python3.8
# -*- coding: utf-8 -*-

if __name__ == "__main__":
    from Scripts import main
    main()

Dosya: Scripts/modules.py

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

import tkinter as tk

from tkinter.messagebox import showwarning

Dosya: Scripts/__init__.py

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

from .modules import tk
from .button import Button
from .entry import ManyEntries
from .utilities import open_toplevel


def main():
    root = tk.Tk()
    login = ManyEntries(
        master=root, 
        texts=["Username", "Password"], 
        max_chars=[20, 20]
    )
    apply_button = Button(
        master=root, 
        text="\u2714", 
        color="green", 
        command=lambda: open_toplevel(*login.entries.values())
    )
    root.mainloop()

Dosya: Scripts/utilities.py

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

from .toplevel import WelcomeScreen
from .modules import tk, showwarning


def get_entry_values(*entries):
    return [ent.get() for ent in entries if isinstance(ent, tk.Entry)]
    
    
def has_authentication(*entries):
    return get_entry_values(*entries) == ["Admin", "123456"]
    
    
def open_toplevel(*entries):
    if has_authentication(*entries):
        WelcomeScreen(entries[0].get())
    else:
        showwarning(
            title="Warning",
            message="Wrong username and password"
        )

Dosya: Scripts/toplevel.py

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

from .modules import tk


class WelcomeScreen(tk.Toplevel):
    def __init__(self, username: str, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.label = tk.Label(master=self, text=username)
        self.label.pack()

Dosya: Scripts/button.py

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

from .modules import tk


class Button(tk.Button):
    def __init__(self, color, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.configure(
            activebackground=self["bg"],
            activeforeground=color,
            borderwidth=0,
            highlightthickness=0,
            font="Default 20",
            fg=color
        )
        self.pack()

Dosya: Scripts/entry.py

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

from .modules import tk


class Entry(tk.Entry):
    def __init__(self, max_char: int, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.bind(
            sequence="<KeyRelease>",
            func=lambda event: self.__apply_max_char(max_char=max_char)
        )
        self.configure(width=max_char)
        if self._name == "password":
            self.configure(show="*")
        
    def __apply_max_char(self, max_char):
        if len(self.get()) > max_char:
            self.delete(max_char, "end")


class ManyEntries(tk.Frame):
    def __init__(self, texts: list, max_chars: list, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack()
        self.entries = self.__create_entries(
            texts=texts, 
            max_chars=max_chars
        )

    def __create_entries(self, texts, max_chars):
        entries = {}
        for index, (text, max_char) in enumerate(zip(texts, max_chars)):
            label = tk.Label(master=self, text=text)
            label.grid(row=index, column=0, sticky="w")
            entry = Entry(
                master=self, 
                max_char=max_char, 
                name=text.lower()
            )
            entry.grid(row=index, column=1, sticky="w")
            entries[text] = entry
        return entries
3 Beğeni

Burada anlatılanın üstüne artık bana laf düşmez burada anlatılanı öncelemek lazım.

ama yine de sorunu yanıtlayacağım.


antrenman.py dosyası (ana dosya) açıldığı zaman full modüler şekilde çalışıyor. Ne bu full modüler diye tabir ettiğim şey?

bu fonksiyonlar arasında değişken alışverişi, buton fonksiyon işlemleri nasıl oluyor? Çok basit. Örneğin bir kayıt yapacağın zaman 3 farklı entry girdisi aldığında, aldığın girdileri kayıt edecek fonksiyonun içine gönderiyorsun ve çalışıyor. Tanımlama önemli. Fonksiyonun yapacağı işlemlerde ihtiyaç duyacağı tüm verileri o modül fonksiyonunun içine aktarmamız gerekiyor.

from function import save_data (save_data.py modülü içinde start fonksiyonu var)

def start(entry_1,entry_2,entry_3) (değişkenleri yolladık içine)

.execute("INSERT INTO DATA VALUES (?,?,?)",(entry_1,entry_2,entry_3)) bu işlem fonksiyonu butona atandı ve butona tıklayınca kabaca işlemi yapmış olduk.

Şimdi önemli bir soru sormam lazım. Burada konuşmadan önce bahsettiğiniz şeyleri denediniz mi? Ya da konuştuklarımız ile beraber deneyerek ilerlediniz mi? İşin pratik boyutunda hata alarak çözüme varabilme imkanınız var.

:sweat_smile: Kusura bakmayın, yanlışlıkla eklemişim onu.


Öncelikle, bu kadar uğraştığınız için teşekkür ederim.

Tabi ki bu daha düzenli ama benim için biraz fazla. Ben, “gui’yi diğer kodlardan ayırmak” derken, Qt Designer ile yapılan GUI’yi içe aktarmak gibi bir şeyi, TkInter’da da yapabilir miyiz?, diye soruyorum.

Rica ederim, fazla uğraştırıcı bir şey değil.

Bilmem, GUI, bir tk.Frame, tk.Menu veya tk.Canvas üzerine kurulmuş olabilir. Son aşamada da bu saydığım widgetler, master widgetin tanımlandığı yerde içe aktarılıyor da olabilir. Ama PyQt'nin GUI tasarım şekli ile Tkinter'in tasarım şekli birbirinden farklı.

1 Beğeni