1 Eylül 2018 Cumartesi

PyQt5 ile Grafiksel Kullanıcı Arayüzü (GUI) Geliştirme

PyQt5 ile Grafiksel Kullanıcı Arayüzü (GUI) Geliştirme


PyQt5 ile Grafiksel Kullanıcı Arayüzü (GKA; Graphical User Interface, GUI) tasarımı yapmak için birçok seçeneğimiz var. Bunları 3 başlık altında toplamak mümkün. GUI tasarımını aşağıdaki yöntemlerle gerçekleştirebiliriz.
  1. GUI tasarımını Kod yazarak gerçekleştirme
  2. Qt Designer ile tasarım yapıp tasarımı Python kodlarına çevirme
  3. Qt Designer ile tasarım yapıp bu tasarımı loadUi modülü yardımıyla Python kodlarında kullanmak

Burada PyQt5 kurulumu yaptığınızı varsayıyorum eğer yapmadıysanız PyQt5 kurulumu için önceki yazımı takip edebilirsiniz.
Windows'a PyQt5 Kurulumu


Tasarıma geçmeden önce belirtmem gereken diğer bir husus da PyQt5 ile GUI tasarımı yaparken kullanacağımız GUI araçları hakkında ilgili dökümanlara nasıl ulaşacağımızı bilmemiz konusudur.
PyQt5 sınıf, metod ve fonksiyonlarına aşağıdaki yolu izleyerek ulaşabiliriz:

Başlat > Tüm Programlar > PyQt GPL v5.6 for Python v3.5 (x64) > Documentation > PyQt Reference Guide

bu yolda belirtilen klasör ismi sizin kurmuş olduğunuz PyQt5 versiyonuna göre farklı olabilir.


Kurulum ve PyQt5 dökmümanlarına ulaşma konusundan bahsettikten sonra arayüz geliştirme yöntemlerine geçebiliriz.

1. Kod Yazarak Arayüz Geliştirme

Tüm 3 yöntem için de aynı arayüzü tasarlayacağız. Bunun için hem basit hem de sinyal-slot yapısını kullanacağımız bir tasarıma karar verdim. Qt'nin en önemli özelliklerinden biri de sinyal-slot(yuva) yapısıdır. Bu yapıyı bir örnekle açıklamaya çalışalım. Arayüzde bir tuşa bastığımızda tıklandı (clicked) sinyali yayınlanır ve bu tuşa basıldığında yapılacak işleri (dosya seçme, kamera açma, yazıyı silme, aklınıza gelebilecek herşey) yazdığımız slot'da belirtmemiz gerekir. Slot burada bir fonksiyondur. Bizim tek yapmamız gereken sinyal ile slot bağlantısını "connect" işlevi ile tanımlamak. Geliştireceğimiz arayüz aşağıdaki ekran görüntüsünde gösterilmiştir. Bu arayüz bir etiket (QLabel) iki tane düğmeden (QPushButton) oluşmaktadır. Etikette "Merhaba PyQt5" yazmaktadır. Temizle düğmesi ile etikette yazan yazı silinmektedir. Göster düğmesi ile etikete yeniden "Merhaba PyQt5" yazılmaktadır.


Bu arayüzü elde etmek için yazacağımız kod aşağıda belirtilmiştir.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#from PyQt5.QtWidgets import QWidget, QApplication, QLabel, QPushButton, QVBoxLayout
from PyQt5.QtWidgets import*
from PyQt5.QtCore import*


class first_GUI(QWidget):
    def __init__(self):
        #super(first_GUI, self).__init__()
        #super().__init__()
        QWidget.__init__(self)

        self.label_text = QLabel("Merhaba PyQt5")
        self.label_text.setAlignment(Qt.AlignCenter)
        self.label_text.setStyleSheet("color: rgb(0,0,255);font-weight: bold; font-size: 16pt")

        pushButton_clear = QPushButton("Temizle")
        pushButton_clear.setMinimumHeight(40)
        pushButton_clear.setStyleSheet("font-weight: bold; font-size: 16pt")
        pushButton_show = QPushButton("Göster")
        pushButton_show.setMinimumHeight(40)
        pushButton_show.setStyleSheet("font-weight: bold; font-size: 16pt")
        
        pushButton_clear.clicked.connect(self.clear_text)
        pushButton_show.clicked.connect(self.show_text)

        vertical_layout = QVBoxLayout()
        vertical_layout.addWidget(self.label_text)
        vertical_layout.addWidget(pushButton_clear)
        vertical_layout.addWidget(pushButton_show)

        self.setLayout(vertical_layout)
        self.setWindowTitle("PyQt5 first GUI")
        self.resize(400,300)


    def clear_text(self):
        self.label_text.clear()

    def show_text(self):
        self.label_text.setText("Merhaba PyQt5")
        
app = QApplication([])
widget = first_GUI()
widget.show()
app.exec_()        

Şimdi adım adım bu kodları açıklayalım.

1
2
3
#from PyQt5.QtWidgets import QWidget, QApplication, QLabel, QPushButton, QVBoxLayout
from PyQt5.QtWidgets import*
from PyQt5.QtCore import*

1 nolu satır yorum satırıdır. Burada isterseniz yorum satırında belirtildiği gibi kullanacağınız arayüz bileşenlerini tek tek ekleyebilirsiniz ya da 2 nolu satırda belirtildiği gibi QtWidgets sınıfına ait tüm bileşenler eklenir. 3 nolu satırda QtCore sınıfına ait metodlar eklenir.

6
7
class first_GUI(QWidget):
    def __init__(self):

6 nolu satırda QWidget sınıfından ürettiğimiz "first_GUI" adında sınıfı tanımlıyoruz. Burada QWidget yerine QDialog ya da QMainWindow tipinde bir sınıf oluşturabilirdik. Her sınıfın __init__() fonksiyonu vardır ve sınıf çağrıldığında ilk olarak bu fonksiyon çalıştırılır. self ifadesi bir sınıfta tanımlanan değişkenlerin, fonksiyonların sınıf dışından çağrılabilmesi için o sınıfa ait bir fonksiyon, değişken olduğunu belirtmek için kullanılır. C++ programlama dilinde kullanılan this ifadesi ile aynı kullanıma sahiptir.

8
9
10
        #super(first_GUI, self).__init__()
        #super().__init__()
        QWidget.__init__(self)

8 ve 9 nolu satırlar yorum satırıdır. Bu satırları yazmamın sebebi 10 nolu satıra alternatif olarak kullanılabiliyor olmalarıdır. super() fonksiyonu miras alma (inheritance) durumunda kullanılan miras aldığımız bir üst (parent, base) sınıfın nitelik ve metodlarını kullanmamızı sağlar. 8 nolu satır eski tip super() fonksiyonudur. 9 nolu satır ise Python 3 ile yeni kullanılmaya başlayan super() fonksiyonudur. 10 nolu satır ile üst sınıfın __init__() fonksiyonu çağrılır. 10 nolu satırı ya da yerine 9 nolu satırı yazmaksak programımızı çalıştırdığımızda hata alırız.

12
13
14
        self.label_text = QLabel("Merhaba PyQt5")
        self.label_text.setAlignment(Qt.AlignCenter)
        self.label_text.setStyleSheet("color: rgb(0,0,255);font-weight: bold; font-size: 16pt")

12 nolu satırda QLabel etiket parçacığı (widget) "Merhaba PyQt5" yazısını içerecek şekilde oluşturulur. Nesne adı (object name) "label_text" olarak tanımlanmıştır ve sınıf dışında (slot tanımlamalarında) bu etikete ulaşmamız gerekeceğinden bu parçacık self ifadesiyle tanımlanmıştır.
13 nolu satırda etiket içerisinde yazan yazının hizalaması ortalanmıştır. Bu hizalamayı setAlignment fonksiyonu sağlamaktadır.
14 nolu satırda etikette yazan yazının rengini, font büyüklüğünü ve font kalınlığını setStyleSheet fonksiyonunu CSS tanımlamaları kullanarak ayarlıyoruz.

16
17
18
19
20
21
        pushButton_clear = QPushButton("Temizle")
        pushButton_clear.setMinimumHeight(40)
        pushButton_clear.setStyleSheet("font-weight: bold; font-size: 16pt")
        pushButton_show = QPushButton("Göster")
        pushButton_show.setMinimumHeight(40)
        pushButton_show.setStyleSheet("font-weight: bold; font-size: 16pt")

16 nolu satırda üzerinde "Temizle" yazan QPushButton (buton, düğme, tuş) parçacığı tanımlıyoruz. Bu düğmenin minimum yüksekliğini 40 piksel olarak 17 nolu satırda setMinimumHeight fonksiyonunu kullanarak tanımlıyoruz. 18 nolu satırda daha önce etiket için yaptığımız gibi setStyleSheet fonksiyonunu kullanarak düğme üzerinde yazan yazının font özelliklerinde daha güzel görünmesini sağlayacak değişiklikler yapıyoruz. Bu düğmemizin adını "pushButton_clear" olarak tanımlıyoruz. Ben hangi parçacık olduğunu belirten (pushButton) ve ne işe yarayacağını belirten kelimeleri birleştirerek isimlendirme yapıyorum. Böylece kod içerisinde hangi tipte bir düğme olduğunu ve ne işe yaradığını anlamak kolay oluyor ve kodun anlaşılabilirliğini arttırıyor. Burada etiketten farklı olarak oluşturduğumuz düğmeyi self'e atamadık çünkü sınıf dışında ihtiyacımız olmayacak eğer ihtiyaç duyarsak self olarak tanımlamalıyız. 19, 20 ve 21 nolu satırlar önceki satırların 2. düğme olan "Göster" düğmesini oluşturmamıza ve aynı işlemleri onun üzerinde yapmamızı sağlamaktadır.

23
24
        pushButton_clear.clicked.connect(self.clear_text)
        pushButton_show.clicked.connect(self.show_text)

23 ve 24 nolu satırlar sinyaller ile fonksiyonları (slotları) birbirine connect metodunu kullanarak bağladığımız satırlar. "Temizle" düğmesinin clicked sinyalini "clear_text" fonksiyonuyla birbirine bağladık ve "Temizle" düğmesine tıklandığında "clear_text" fonksiyonu çağrılacaktır. "Göster" düğmesinin clicked sinyalini de "show_text" fonksiyonuna bağladık. Kod yazarken türkçe karakter kullanamadığımızdan anlaşılabilir olması için ingilizce değişken ve fonksiyon adı kullanmayı tercih ediyorum.

26
27
28
29
        vertical_layout = QVBoxLayout()
        vertical_layout.addWidget(self.label_text)
        vertical_layout.addWidget(pushButton_clear)
        vertical_layout.addWidget(pushButton_show)

26 nolu satırda dikey yerleşim düzeni ( QVBoxLayout ) tanımlıyoruz ve 27, 28, 29 nolu satırlarda addWidget fonksiyonunu kullanarak sırasıyla oluşturduğumuz etiket ve düğmeleri bu dikey yerleşim planı içerisine ekliyoruz. Tasarımınıza göre Yatay yerleşim düzeni ( QHBoxLayout ) veya ızgara yerleşim düzeni ( QGridLayout ) kullanabilirsiniz.

31
32
33
        self.setLayout(vertical_layout)
        self.setWindowTitle("PyQt5 first GUI")
        self.resize(400,300)

31 nolu satırda oluşturduğumuz dikey yerleşim düzenini sınıfımızın (oluşturacağımız arayüzün) yerleşim düzeni olmasını sağlamak için setLayout fonksiyonuyla tanımlıyoruz. 32 nolu satırda arayüzün gösterileceği pencerenin adının "PyQt5 first GUI" olmasını setWindowTitle fonksiyonunu kullanarak sağlıyoruz. 33 nolu satırda açılacak pencerenin boyutlarını yüksekliği 300 piksel, genişliği 400 piksel olacak şekilde resize fonksiyonunu kullanarak ayarlıyoruz. Bu satır ile "first_GUI" sınıf tanımlaması bitmiştir.

36
37
    def clear_text(self):
        self.label_text.clear()

36 nolu satırda "Temizle" düğmesinin tıklama sinyaline bağlanan "clear_text()" slotunu (fonksiyonunu) tanımlıyoruz. Değişken olarak self değişkenini almaktadır. İşlem olarak QLabel sınıfının clear() fonksiyonu ile etiket içerisinde yazan yazı temizlenir.

39
40
    def show_text(self):
        self.label_text.setText("Merhaba PyQt5")

39 ve 40 nolu satırlarda "Göster" düğmesinin "show_text()" slotu tanımlanmaktadır. Bir etikete yazı yazmak için kullanılan setText() fonksiyonu ile bu düğmeye tıklandığında "Merhaba PyQt5" yazısı yazacaktır. Bu düğmenin yaptığı işi gözlemlemek için öncelikle etikette yazan yazı silinmelidir.

42
43
44
45
app = QApplication([])
widget = first_GUI()
widget.show()
app.exec_()

Sınıf ve slot tanımlamalarından sonra 42 nolu satırda QApplication sınıfından "app" adında bir nesne tanımlıyoruz. 43 nolu satırda "first_GUI" adında tanımladığımız sınıftan "widget" adında bir nesne tanımlıyoruz. Bu nesnenin show() fonksiyonunu kullanarak arayüz için oluşturduğumuz pencereyi gösteriyoruz (44 nolu satır). 45 nolu satırda QApplication sınıfının exec_() fonksiyonu ile sonsuz bir döngü oluşturulur. Bu ifade ile kullanıcının arayüzü kullanarak fare ve klavye ile yapmış olduğu tüm işlemler yakalanarak komutlar yerine getirilir. Pencere kapatılana kadar döngü devam eder.
Böylece kod yazarak kullanıcı arayüzü geliştirmiş olduk. Python kodlarından farklı olarak yazdığımız kodu (Windows için) .pyw uzantılı olarak kaydediyoruz.

2. Qt Designer Tasarımını Python Kodlarına Çevirme

İlk yöntemde her şeyi kod yazarak tanımladık. Bu çok uzun bir işlem olmasa da Qt Designer ile tasarım yapmak sizin için uygun olabilir. Bunun için PyQt5 kurulumu ile birlikte gelen Qt Designer açılır.


Kod yazarak yaptığımız tasarımın aynısını Qt Designer ile yaptık. Aşağıdaki videoyu izleyerek tasarımın nasıl yapıldığını izleyebilirsiniz.


Qt Designer ile yapılan tasarımı .ui uzantılı olarak kaydediyoruz. Daha sonra komut satır (cmd) programı açılır. Tasarımı kaydettiğimiz klasöre geçiyoruz ve Python kurulumunun gerçekleştiği klasörde yer alan (“C:\Program Files\Python35\Lib\site-packages\PyQt5\") pyuic5.bat designer ile (.ui uzantılı) yaptığımız tasarım ve bunun Python kodlarına çevrileceğinde hangi isimle kaydedileceğini tanımlayarak çalıştırılır. Örnek kodu aşağıda bulabilirsiniz.


“C:\Program Files\Python35\Lib\site-packages\PyQt5\pyuic5.bat” designer_example.ui -o designer_example.py

Bu kodu çalıştırarak tasarımı Python kodlarına çevirdik. Tasarımda yapacağımız her işlemden sonra kodu çalıştırarak bu dönüşümü gerçekleştirmemiz gerektiğinden yazacağımız kodları otomatik oluşturulan bu Python koduna yazmıyoruz bunun yerine bu kodu kullanacak başka Python kodu (wrapper) yazacağız. Qt Designer ile tasarım yaptığımızda sinyal-slot bağlantısı için 3 farklı yöntem mevcuttur.
  1. Kod yazarak sinyal-slot bağlantısı yapma
  2. Qt Designer ile sinyal-slot bağlantısı yapma
  3. @pyqtSlot() ile sinyal-slot bağlantısı yapma

2.1. Kod Yazarak Sinyal-Slot Bağlantısı Yapma

Python koduna dönüştürülen tasarımı kullanmamızı sağlayan ve sinyal-slot bağlantısının kod yazılarak yapıldığı Python kodunu (wrapper) aşağıda bulabilirsiniz.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from PyQt5.QtWidgets import*
from designer_example import Ui_MainWindow

class Pyuic5_example(QMainWindow):
    def __init__(self):
        super().__init__()

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.pushButton_clear.clicked.connect(self.clear_text)
        self.ui.pushButton_show.clicked.connect(self.show_text)

    def clear_text(self):
        self.ui.label.clear()

    def show_text(self):
        self.ui.label.setText("Merhaba PyQt5")

app = QApplication([])
window = Pyuic5_example()
window.show()
app.exec_()

Burada satır satır açıkladığımız ilk yöntemden farklı olarak 2 nolu satırda .py uzantılı tasarımın Python kodlarına dönüştürüldüğü dosya adını kullanarak (designer_example) Ui_MainWindow() modülü eklenmiştir.
8 ve 9 nolu satırlarda tasarım kodumuz mevcut Python koduna eklenmiştir. Bu işlem için setupUi() fonksiyonu kullanılmıştır ve ilk yöntemden farklı olarak atama self değişkenine değil de self.ui değişkenine yapılmıştır.
11 ve 12 nolu satırlarda kod yazarak sinyal-slot bağlantıları yapılmıştır. Diğer satırlarda yer alan kodlar ilk yöntemde açıklandığından burada tekrar açıklanmayacaktır.

2.2. Qt Designer ile Sinyal-Slot Bağlantısı Yapma

Sinyal-Slot bağlantısını kod yazarak değil de Qt Designer ile de yapabilirsiniz. Yazı ile bu işlemi anlatmak zor olduğundan dolayı aşağıdaki videoyu izleyerek bağlantının nasıl yapıldığını öğrenebilirsiniz.


Burada 2.1 yönteminde yazmış olduğumuz Python kodlarında yer alan sinyal-slot bağlantılarının yapıldığı 11 ve 12 nolu satırları siliyoruz çünkü bu satırlara ihtiyacımız yok. Bu satırları sildikten sonra kalan kısımları tasarımı yükleme ve tasarlanan arayüzü çalıştırmak için kullanabilirsiniz.

2.3. @pyqtSlot() ile Sinyal-Slot Bağlantısı Yapma

pyqtSlot dekoratörü ile sinyal-slot bağlantısının nasıl yapıldığını aşağıdaki video'yu izleyerek öğrenebilirsiniz.


Qt Designer ile yapmış olduğumuz tasarımı kullanabilmek için yazmış olduğumuz Python kodlarında sinyal-slot bağlantısı için pyqtSlot dekoratörü kullandığımızda slot tanımlamalarından bir satır önce @pyqtSlot() ifadesi eklenir. Burada Slot ismini belirlerken istediğimiz ismi yazamıyoruz belirli bir kurala göre isimlendirme yapmamız gerekiyor. İsimlendirmede kullanacağımız parçacığın nesne adı (object name, bunu designer'dan parçacığa tıkladığımızda sağ tarafta görebilirsiniz) ve kullanılacak sinyal aşağıdaki formatta yazılır:

on_ nesne_adi_ sinyal_adi()

Örnek slot tanımlamalarını aşağıdaki Python kodunun 14 ve 18. satırlarında görebilirsiniz.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from PyQt5.QtWidgets import*
from PyQt5.QtCore import pyqtSlot

from designer_example import Ui_MainWindow

class Pyuic5_example(QMainWindow):
    def __init__(self):
        super().__init__()

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

    @pyqtSlot()
    def on_pushButton_clear_clicked(self):
        self.ui.label.clear()

    @pyqtSlot()
    def on_pushButton_show_clicked(self):
        self.ui.label.setText("Merhaba PyQt5")

app = QApplication([])
window = Pyuic5_example()
window.show()
app.exec_()

Burada önceki kodlardan farklı olarak QtCore sınıfından pyqtSlot 2 nolu satırda gösterildiği gibi eklenir.

3. Qt Designer ile Tasarım + loadUi modülü ile bu Tasarımı Kullanma

İkinci yöntemde tasarımda yaptığınız her değişiklik sonrasında tasarımı Python kodlarına çevirmek için pyuic.bat çalıştırılır. Bu kod çalıştırma işlemi size zahmetli bir iş olarak gelebilir. Tüm bunlarla uğraşmadan Qt Designer ile yapmış olduğunuz tasarımı kolay bir şekilde dönüşüm yapmadan kullanabileceğiniz bir yöntem mevcut. Qt Designer ile yapmış olduğumuz tasarımı kullandığımız Python kodu aşağıda verilmiştir.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from PyQt5.QtWidgets import*
from PyQt5.QtCore import pyqtSlot

from PyQt5.uic import loadUi

class loadUi_example(QMainWindow):
    
    def __init__(self):
        super().__init__()
        loadUi("designer_example.ui",self)

    @pyqtSlot()
    def on_pushButton_clear_clicked(self):
        self.label.clear()

    @pyqtSlot()
    def on_pushButton_show_clicked(self):
        self.label.setText("Merhaba PyQt5")

app = QApplication([])
window = loadUi_example()
window.show()
app.exec_()

Burada diğer yazdığımız kodlardan farklı olarak 4 nolu satırda uic modülünde yer alan loadUi fonksiyonu eklenmiştir ve bu fonksiyon 10 nolu satırda .ui uzantılı tasarımı yüklemek için kullanılmıştır. Burada sinyal-slot bağlantısı 2.3 bölümünde anlatıldığı gibi pyqtSlot() dekorlayıcısı kullanılarak yapılmıştır. Diğer kodlar daha önce açıklandığından tekrar açıklanmayacaktır. Sanırım PyQt5 ile GUI geliştirmek için en ideal yöntem son olarak bahsettiğimiz üçüncü yöntem gibi gözüküyor.

10 yorum:

  1. Ui uzantısı py cmdden donuştururken yol bulunamadı diyor. Sizin dönüştürücüyüde bulamadım. Elle yazmak

    YanıtlaSil
    Yanıtlar
    1. Program için yapayzekalabs@gmail.com adresine mail atabilirsiniz.

      Sil
  2. Emeğinize sağlık, çok teşekkürler. Mail attım ve programı aldım. Hem bu faydalı dersleri yazdığınız için hem de program için ayrı ayrı teşekkür ederim. İyi çalışmalar.

    YanıtlaSil
    Yanıtlar
    1. İlginiz ve güzel yorumlarınız için teşekkürler.

      Sil
  3. Merhaba
    Qt Designer tasarımı python kodlarına çevirmeden pyuic.bat dosyasını kontrol ettiğimde sadece pyuic dosyasının bulunduğunu fark ettim. Bundan dolayı Windows PowerShell i çalıştıramadım. Yardımcı olur musunuz? Teşekkür ederim. İyi çalışmlar.

    YanıtlaSil
    Yanıtlar
    1. Merhaba
      pyuic5.bat PyQt 5.6 kurulumu yaptığınızda oluyor. PyQt 5.11 kurulumu yapınca pyuic5.exe oluyor. Ayrıca windows da .bat .exe gibi uzantılar bir ayara göre gösteriliyor ya da gösterilmiyor. O ayarı aktif hale getirirseniz uzantısını da görebilirsiniz. Bulduğunuz pyuic yi yine aynı amaç için sorun olmadan kullanabilirsiniz.

      Sil
  4. Hoca selam,
    Pyqt ile plot grafik tasarımnda x ekseninde DateTime olarak şekilde greafik çizimleri özerine küçük bir eğitim yapabilir misiniz?

    YanıtlaSil
    Yanıtlar
    1. Merhaba
      Yakın bir zamanda öyle bir eğitim yapmam zor gözüküyor. Matplotlib in PyQt5 arayüzünde nasıl kullanılcağını anlattım. Daha sonra matplotlib in dökümanlarından yararlanarak istediğiniz şeyi yapabilirsiniz.

      Sil
  5. Merhaba Hocam
    Öncelikle hazırlamış olduğunuz bu güzel eğitim için size teşekkür ederim. Bir sorum olacaktı size, Pyqt5 ile
    beraber data base kullanımı ile ilgili video çekme şansınız var mı?

    YanıtlaSil
    Yanıtlar
    1. Merhaba
      İlginiz ve yorumunuz için teşekkürler. Yakın zamanda pek mümkün gözükmüyor.

      Sil