OzbekPython / Bob 23

PySide6 bilan grafik interfeys

PySide6 yordamida oynalar, tugmalar, matn maydonlari, menyular, kiçik bloknot va kiriş formasi bilan iş stoli ilovalarini yaratamiz.

Örta daraja18 daqiqa14 kod misoli23 / 24

Grafik interfeys nima uçun kerak?

Hozirgaça yozgan dasturlarimiz terminalda işladi: matn çiqardi va input() orqali javob kutdi. Dasturçi uçun bu qulay, lekin oddiy foydalanuvçi oynalar, tugmalar va menyularga örgangan. Bunday interfeys GUI (Graphical User Interface — grafik foydalanuvçi interfeysi) deb ataladi.

Python da GUI yoziş uçun bir neçta kutubxona bor:

KutubxonaXususiyati
tkinterPython bilan birga keladi, oddiy, lekin körinişi biroz eskiça
PySide6Qt 6 ning rasmiy Python versiyasi, zamonaviy va kuçli, LGPL litsenziyasi
PyQt6Xuddi şu Qt 6 uçun boşqa kompaniya tayyorlagan versiya, GPL yoki tijoriy litsenziya
Kivy, FletMobil va sensorli ekranlarga möljallangan

Bu bobda PySide6 ni tanlaymiz. Qt — dunyodagi eng maşhur GUI freymvorklaridan biri, unda yozilgan ilovalar Windows, macOS va Linux da bir xil işlaydi. PySide6 — Qt for Python loyihasining kutubxonasi, uni Qt ni işlab çiquvçi kompaniyaning özi qöllab-quvvatlaydi. Avvalgi avlod kutubxonasi PyQt5 edi; endi yangi loyihalar uçun PySide6 yoki PyQt6 tavsiya etiladi.

Bu bobdagi misollar brauzerda işlamaydi (ular haqiqiy oyna oçadi), şuning uçun ularni öz kompyuteringizda işga tuşiring.

Örnatiş

PySide6 ni virtual muhitga örnatiş tavsiya etiladi:

Terminal
python -m venv .venv
source .venv/bin/activate      # Windows: .venv\Scripts\activate
pip install PySide6

Tekşirib köriş uçun:

Terminal
python -c "import PySide6; print(PySide6.__version__)"

Birinçi oyna

Har qanday Qt ilovasi uçta qismdan iborat: bitta QApplication obyekti, kamida bitta oyna (vidjet) va hodisalar sikli (event loop). Hodisalar sikli — app.exec() çaqirilganda boşlanadigan çeksiz aylaniş: u siçqonça bosilişi, klaviatura, oynani yopiş kabi hodisalarni kutadi va kerakli funksiyani çaqiradi.

Python
import sys
from PySide6.QtWidgets import QApplication, QWidget

app = QApplication(sys.argv)

oyna = QWidget()
oyna.setWindowTitle("Mening birinçi oynam")
oyna.resize(400, 250)
oyna.show()

sys.exit(app.exec())

sys.argv ni QApplication ga beriş anʼanaviy usul: Qt buyruq qatoridagi ayrim parametrlarni özi tuşunadi. sys.exit(app.exec()) esa oyna yopilganda dastur töğri çiqiş kodi bilan tugaşini taʼminlaydi.

Vidjetlar va QLabel

Qt da ekrandagi har bir element — vidjet (QWidget dan meros olgan klass). Yorliq QLabel, tugma QPushButton, matn maydoni QLineEdit va hokazo. Oynani odatda QWidget dan meros olgan klass sifatida yozamiz — şunda barça vidjetlar va funksiyalar bir joyda turadi:

Python
import sys
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget


class SalomOyna(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Salom")

        sarlavha = QLabel("Assalomu alaykum!")
        sarlavha.setAlignment(Qt.AlignmentFlag.AlignCenter)
        shrift = sarlavha.font()
        shrift.setPointSize(20)
        sarlavha.setFont(shrift)

        izoh = QLabel("Bu oyna PySide6 yordamida yaratildi.")

        joylashuv = QVBoxLayout(self)
        joylashuv.addWidget(sarlavha)
        joylashuv.addWidget(izoh)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = SalomOyna()
    oyna.show()
    sys.exit(app.exec())

QVBoxLayout (vertikal joylaşuv) haqida quyida batafsil gaplaşamiz — hozirça u vidjetlarni ustma-ust tizişini bilsangiz yetarli.

Tugmalar, signallar va slotlar

Qt ning eng muhim ğoyasi — signal va slot. Vidjet biror hodisa yuz berganda signal çiqaradi (masalan, tugma bosilganda clicked). Siz bu signalni istalgan funksiyaga — slot ga — ulaysiz. Signal çiqqanda Qt öşa funksiyani avtomatik çaqiradi.

Bu xuddi eşik qönğiroğiga öxşaydi: qönğiroq (signal) kim eşitişini bilmaydi, siz esa unga «jiringlasa, eşikni oç» degan vazifani (slot) boğlab qöyasiz.

Python
import sys
from PySide6.QtCore import Slot
from PySide6.QtWidgets import QApplication, QLabel, QPushButton, QVBoxLayout, QWidget


class Hisoblagich(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Hisoblagiç")
        self.son = 0

        self.yorliq = QLabel("Bosişlar soni: 0")
        tugma = QPushButton("Bosing!")
        nol_tugma = QPushButton("Nolga qaytariş")

        # Signalni slotga ulaymiz
        tugma.clicked.connect(self.oshir)
        nol_tugma.clicked.connect(self.nolla)

        joylashuv = QVBoxLayout(self)
        joylashuv.addWidget(self.yorliq)
        joylashuv.addWidget(tugma)
        joylashuv.addWidget(nol_tugma)

    @Slot()
    def oshir(self) -> None:
        self.son += 1
        self.yorliq.setText(f"Bosişlar soni: {self.son}")

    @Slot()
    def nolla(self) -> None:
        self.son = 0
        self.yorliq.setText("Bosişlar soni: 0")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = Hisoblagich()
    oyna.show()
    sys.exit(app.exec())

Eʼtibor bering: connect(self.oshir) — funksiyaning özini beramiz, self.oshir() deb çaqirmaymiz. @Slot() dekoratori majburiy emas, lekin u kodni aniqroq qiladi va biroz tezlik beradi.

Matn kiritiş: QLineEdit

QLineEdit — bir qatorli matn maydoni. Undagi matnni text() bilan öqiymiz, setText() bilan yozamiz. Foydali signallari: textChanged (har bir harf özgarganda) va returnPressed (Enter bosilganda). Quyida valyuta kalkulyatori — dollarni sömga aylantiradi:

Python
import sys
from PySide6.QtWidgets import (
    QApplication, QLabel, QLineEdit, QPushButton, QVBoxLayout, QWidget,
)

KURS = 12_800  # 1 dollar neça söm (misol uçun)


class Valyuta(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Valyuta kalkulyatori")

        self.maydon = QLineEdit()
        self.maydon.setPlaceholderText("Dollar miqdorini kiriting")
        self.natija = QLabel("Natija şu yerda çiqadi")
        tugma = QPushButton("Hisoblaş")

        tugma.clicked.connect(self.hisobla)
        self.maydon.returnPressed.connect(self.hisobla)  # Enter ham işlaydi

        joylashuv = QVBoxLayout(self)
        joylashuv.addWidget(QLabel(f"Kurs: 1 $ = {KURS:,} söm"))
        joylashuv.addWidget(self.maydon)
        joylashuv.addWidget(tugma)
        joylashuv.addWidget(self.natija)

    def hisobla(self) -> None:
        matn = self.maydon.text().replace(",", ".").strip()
        try:
            dollar = float(matn)
        except ValueError:
            self.natija.setText("Iltimos, son kiriting")
            return
        self.natija.setText(f"{dollar:,.2f} $ = {dollar * KURS:,.0f} söm")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = Valyuta()
    oyna.show()
    sys.exit(app.exec())

Foydalanuvçi har doim ham töğri qiymat kiritmaydi, şuning uçun try/except bilan himoyalaniş GUI dasturlarida ayniqsa muhim — aks holda xato butun ilovani yopib qöyişi mumkin.

Köp qatorli matn: QTextEdit

QTextEdit — köp qatorli matn muharriri. Oddiy matn uçun toPlainText() va setPlainText() metodlaridan foydalanamiz. Quyidagi misol yozilgan matndagi söz va belgilar sonini jonli hisoblaydi:

Python
import sys
from PySide6.QtWidgets import QApplication, QLabel, QTextEdit, QVBoxLayout, QWidget


class SozSanagich(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Söz sanagiç")
        self.resize(450, 300)

        self.muharrir = QTextEdit()
        self.muharrir.setPlaceholderText("Matningizni şu yerga yozing...")
        self.holat = QLabel("Sözlar: 0 | Belgilar: 0")

        self.muharrir.textChanged.connect(self.yangila)

        joylashuv = QVBoxLayout(self)
        joylashuv.addWidget(self.muharrir)
        joylashuv.addWidget(self.holat)

    def yangila(self) -> None:
        matn = self.muharrir.toPlainText()
        self.holat.setText(f"Sözlar: {len(matn.split())} | Belgilar: {len(matn)}")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = SozSanagich()
    oyna.show()
    sys.exit(app.exec())

Tanlov vidjetlari: QCheckBox va QRadioButton

  • QCheckBox — belgilaş katakçasi: bir neçtasini bir vaqtda tanlaş mumkin. Holati isChecked() bilan tekşiriladi.
  • QRadioButton — bir guruh içida faqat bittasi tanlanadi. Bir xil ota-vidjet içidagi radiotugmalar avtomatik guruhlanadi; aniqroq boşqariş uçun QButtonGroup işlatiladi.

Kiçik kafe buyurtma oynasini yasaymiz:

Python
import sys
from PySide6.QtWidgets import (
    QApplication, QButtonGroup, QCheckBox, QLabel, QPushButton,
    QRadioButton, QVBoxLayout, QWidget,
)

ICHIMLIKLAR = {"Çoy": 5_000, "Qahva": 15_000, "Kompot": 8_000}
QOSHIMCHALAR = {"Şakar": 0, "Limon": 2_000, "Pişiriq": 12_000}


class Buyurtma(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Kafe buyurtmasi")
        joylashuv = QVBoxLayout(self)

        joylashuv.addWidget(QLabel("Içimlik tanlang:"))
        self.guruh = QButtonGroup(self)
        for i, nom in enumerate(ICHIMLIKLAR):
            radio = QRadioButton(nom)
            if i == 0:
                radio.setChecked(True)
            self.guruh.addButton(radio)
            joylashuv.addWidget(radio)

        joylashuv.addWidget(QLabel("Qöşimçalar:"))
        self.katakchalar: list[QCheckBox] = []
        for nom in QOSHIMCHALAR:
            katak = QCheckBox(nom)
            self.katakchalar.append(katak)
            joylashuv.addWidget(katak)

        tugma = QPushButton("Hisoblaş")
        tugma.clicked.connect(self.hisobla)
        self.natija = QLabel()
        joylashuv.addWidget(tugma)
        joylashuv.addWidget(self.natija)

    def hisobla(self) -> None:
        ichimlik = self.guruh.checkedButton().text()
        jami = ICHIMLIKLAR[ichimlik]
        tanlangan = [k.text() for k in self.katakchalar if k.isChecked()]
        jami += sum(QOSHIMCHALAR[nom] for nom in tanlangan)
        royxat = ", ".join([ichimlik, *tanlangan])
        self.natija.setText(f"{royxat}\nJami: {jami:,} söm")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = Buyurtma()
    oyna.show()
    sys.exit(app.exec())

Joylaşuvlar: QVBoxLayout va QHBoxLayout

Vidjetlarni piksel koordinatalari bilan (move(10, 20)) joylaştiriş mumkin, lekin bu yomon odat: oyna kattalaşganda yoki boşqa tilga tarjima qilinganda hamma narsa buziladi. Buning örniga joylaşuv menejerlari (layout) işlatiladi — ular vidjetlarni özi tartiblaydi va ölçamini moslaydi.

JoylaşuvVazifasi
QVBoxLayoutVidjetlarni yuqoridan pastga tizadi
QHBoxLayoutVidjetlarni çapdan öngga tizadi
QGridLayoutJadval (qator va ustun) körinişida joylaydi
QFormLayout«Yorliq — maydon» juftliklari uçun qulay

Joylaşuvlarni bir-birining içiga joylaş mumkin: masalan, vertikal joylaşuv içida tugmalar qatori uçun gorizontal joylaşuv. addStretch() esa böş «prujina» qöşadi — u elementlarni bir tomonga surib turadi.

Python
import sys
from PySide6.QtWidgets import (
    QApplication, QHBoxLayout, QLabel, QLineEdit, QPushButton, QVBoxLayout, QWidget,
)


class Anketa(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Joylaşuvlar")

        asosiy = QVBoxLayout(self)

        for yorliq in ("Ism:", "Familiya:", "Şahar:"):
            qator = QHBoxLayout()
            nom = QLabel(yorliq)
            nom.setFixedWidth(70)
            qator.addWidget(nom)
            qator.addWidget(QLineEdit())
            asosiy.addLayout(qator)

        tugmalar = QHBoxLayout()
        tugmalar.addStretch()  # tugmalarni öng tomonga suradi
        tugmalar.addWidget(QPushButton("Bekor qiliş"))
        tugmalar.addWidget(QPushButton("Saqlaş"))
        asosiy.addStretch()
        asosiy.addLayout(tugmalar)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = Anketa()
    oyna.resize(380, 200)
    oyna.show()
    sys.exit(app.exec())

Oynani siçqonça bilan kattalaştirib köring: matn maydonlari kengayadi, tugmalar esa öng pastki burçakda qoladi.

Menyular va QMainWindow

Haqiqiy ilovalarda odatda menyu satri, asboblar paneli va holat satri böladi. Bularning hammasi QMainWindow klassida tayyor. Menyu bandlari QAction obyektlari orqali yaratiladi: har bir QAction ning matni, tezkor tugmasi va triggered signali bor.

Python
import sys
from PySide6.QtGui import QAction, QKeySequence
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QMessageBox


class AsosiyOyna(QMainWindow):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Menyu namunasi")
        self.setCentralWidget(QLabel("Menyudan biror bandni tanlang"))

        fayl_menyu = self.menuBar().addMenu("&Fayl")
        chiqish = QAction("&Çiqiş", self)
        chiqish.setShortcut(QKeySequence.StandardKey.Quit)
        chiqish.triggered.connect(self.close)
        fayl_menyu.addAction(chiqish)

        yordam_menyu = self.menuBar().addMenu("&Yordam")
        haqida = QAction("Dastur &haqida", self)
        haqida.triggered.connect(self.haqida_oyna)
        yordam_menyu.addAction(haqida)

        self.statusBar().showMessage("Tayyor")

    def haqida_oyna(self) -> None:
        QMessageBox.information(self, "Dastur haqida", "PySide6 menyu namunasi, 1.0-versiya")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = AsosiyOyna()
    oyna.resize(500, 300)
    oyna.show()
    sys.exit(app.exec())

Menyu matnidagi & belgisi keyingi harfni Alt bilan işlaydigan tezkor harfga aylantiradi (masalan, Alt+F — «Fayl»). QKeySequence.StandardKey.Quit esa har bir operatsion tizimdagi odatiy tugmalar birikmasini özi tanlaydi.

Amaliy loyiha: mini bloknot

Endi örganganlarimizni birlaştirib, fayllarni oçib-saqlaydigan kiçik bloknot yasaymiz. Yangi narsalar:

  • QFileDialog.getOpenFileName() va getSaveFileName() — tizimning standart fayl tanlaş oynalari. Ular (yoʻl, filtr) juftligini qaytaradi; foydalanuvçi bekor qilsa, yöl böş satr böladi.
  • pathlib.Path bilan faylni utf-8 kodlaşda öqiş va yoziş.
  • document().isModified() — matn saqlanmaganini biliş va oyna yopilayotganda (closeEvent) ogohlantiriş.
Python
import sys
from pathlib import Path

from PySide6.QtGui import QAction, QCloseEvent, QKeySequence
from PySide6.QtWidgets import (
    QApplication, QFileDialog, QMainWindow, QMessageBox, QTextEdit,
)

FILTR = "Matn fayllari (*.txt);;Barça fayllar (*)"


class Bloknot(QMainWindow):
    def __init__(self) -> None:
        super().__init__()
        self.joriy_fayl: Path | None = None
        self.muharrir = QTextEdit()
        self.muharrir.setAcceptRichText(False)  # faqat oddiy matn
        self.setCentralWidget(self.muharrir)
        self.menyu_yarat()
        self.sarlavhani_yangila()
        self.muharrir.document().modificationChanged.connect(self.sarlavhani_yangila)

    def menyu_yarat(self) -> None:
        menyu = self.menuBar().addMenu("&Fayl")
        bandlar = [
            ("&Yangi", QKeySequence.StandardKey.New, self.yangi),
            ("&Oçiş...", QKeySequence.StandardKey.Open, self.ochish),
            ("&Saqlaş", QKeySequence.StandardKey.Save, self.saqlash),
            ("&Boşqa nom bilan saqlaş...", QKeySequence.StandardKey.SaveAs, self.boshqa_nomda),
        ]
        for matn, tugma, funksiya in bandlar:
            amal = QAction(matn, self)
            amal.setShortcut(tugma)
            amal.triggered.connect(funksiya)
            menyu.addAction(amal)
        menyu.addSeparator()
        chiqish = QAction("&Çiqiş", self)
        chiqish.triggered.connect(self.close)
        menyu.addAction(chiqish)

    def sarlavhani_yangila(self) -> None:
        nom = self.joriy_fayl.name if self.joriy_fayl else "Nomsiz"
        belgi = "*" if self.muharrir.document().isModified() else ""
        self.setWindowTitle(f"{belgi}{nom} — Bloknot")

    def davom_etsinmi(self) -> bool:
        """Saqlanmagan özgariş bölsa, foydalanuvçidan söraydi."""
        if not self.muharrir.document().isModified():
            return True
        javob = QMessageBox.question(
            self, "Bloknot", "Özgarişlar saqlanmagan. Saqlansinmi?",
            QMessageBox.StandardButton.Save
            | QMessageBox.StandardButton.Discard
            | QMessageBox.StandardButton.Cancel,
        )
        if javob == QMessageBox.StandardButton.Save:
            return self.saqlash()
        return javob == QMessageBox.StandardButton.Discard

    def yangi(self) -> None:
        if self.davom_etsinmi():
            self.muharrir.clear()
            self.joriy_fayl = None
            self.muharrir.document().setModified(False)
            self.sarlavhani_yangila()

    def ochish(self) -> None:
        if not self.davom_etsinmi():
            return
        yol, _ = QFileDialog.getOpenFileName(self, "Faylni oçiş", "", FILTR)
        if not yol:
            return  # foydalanuvçi bekor qildi
        try:
            matn = Path(yol).read_text(encoding="utf-8")
        except (OSError, UnicodeDecodeError) as xato:
            QMessageBox.warning(self, "Xato", f"Faylni öqib bölmadi:\n{xato}")
            return
        self.muharrir.setPlainText(matn)
        self.joriy_fayl = Path(yol)
        self.muharrir.document().setModified(False)
        self.sarlavhani_yangila()

    def saqlash(self) -> bool:
        if self.joriy_fayl is None:
            return self.boshqa_nomda()
        return self.faylga_yoz(self.joriy_fayl)

    def boshqa_nomda(self) -> bool:
        yol, _ = QFileDialog.getSaveFileName(self, "Saqlaş", "matn.txt", FILTR)
        if not yol:
            return False
        return self.faylga_yoz(Path(yol))

    def faylga_yoz(self, yol: Path) -> bool:
        try:
            yol.write_text(self.muharrir.toPlainText(), encoding="utf-8")
        except OSError as xato:
            QMessageBox.warning(self, "Xato", f"Saqlab bölmadi:\n{xato}")
            return False
        self.joriy_fayl = yol
        self.muharrir.document().setModified(False)
        self.statusBar().showMessage(f"Saqlandi: {yol}", 3000)
        self.sarlavhani_yangila()
        return True

    def closeEvent(self, event: QCloseEvent) -> None:
        if self.davom_etsinmi():
            event.accept()
        else:
            event.ignore()  # oyna yopilmaydi


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = Bloknot()
    oyna.resize(700, 500)
    oyna.show()
    sys.exit(app.exec())

Bu kodda bir neçta yaxşi odatlar bor: fayl bilan işlaş try/except içida, foydalanuvçi «Bekor qiliş» tugmasini bossa dastur jim qaytadi, saqlanmagan matn esa tasodifan yöqolmaydi. Sarlavhadagi * belgisi ham köplab muharrirlarda qabul qilingan anʼana.

Amaliy loyiha: kiriş formasi

Oxirgi misolda login va parol söraydigan kiriş oynasini yasaymiz. Unda QFormLayout, parolni yulduzçalar bilan yaşiradigan QLineEdit.EchoMode.Password rejimi, «parolni körsatiş» katakçasi va urinişlar sonini çeklaş bor.

Python
import hashlib
import sys

from PySide6.QtWidgets import (
    QApplication, QCheckBox, QFormLayout, QLabel, QLineEdit,
    QMessageBox, QPushButton, QVBoxLayout, QWidget,
)

# Haqiqiy dasturda parollar oçiq holda emas, xeş körinişida saqlanadi
FOYDALANUVCHILAR = {
    "aziza": hashlib.sha256("parol123".encode()).hexdigest(),
}
MAKS_URINISH = 3


class KirishOynasi(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Tizimga kiriş")
        self.urinishlar = 0

        self.login = QLineEdit()
        self.login.setPlaceholderText("masalan: aziza")
        self.parol = QLineEdit()
        self.parol.setEchoMode(QLineEdit.EchoMode.Password)
        self.korsat = QCheckBox("Parolni körsatiş")
        self.korsat.toggled.connect(self.parol_rejimi)

        forma = QFormLayout()
        forma.addRow("Login:", self.login)
        forma.addRow("Parol:", self.parol)
        forma.addRow("", self.korsat)

        self.xabar = QLabel()
        self.tugma = QPushButton("Kiriş")
        self.tugma.setDefault(True)
        self.tugma.clicked.connect(self.tekshir)
        self.parol.returnPressed.connect(self.tekshir)

        asosiy = QVBoxLayout(self)
        asosiy.addLayout(forma)
        asosiy.addWidget(self.xabar)
        asosiy.addWidget(self.tugma)

    def parol_rejimi(self, belgilangan: bool) -> None:
        rejim = QLineEdit.EchoMode.Normal if belgilangan else QLineEdit.EchoMode.Password
        self.parol.setEchoMode(rejim)

    def tekshir(self) -> None:
        login = self.login.text().strip().lower()
        xesh = hashlib.sha256(self.parol.text().encode()).hexdigest()

        if not login or not self.parol.text():
            self.xabar.setText("Login va parolni kiriting")
            return

        if FOYDALANUVCHILAR.get(login) == xesh:
            QMessageBox.information(self, "Xuş kelibsiz", f"Salom, {login}!")
            self.close()
            return

        self.urinishlar += 1
        qoldi = MAKS_URINISH - self.urinishlar
        self.parol.clear()
        if qoldi == 0:
            self.xabar.setText("Urinişlar tugadi. Keyinroq qayta urinib köring.")
            self.tugma.setEnabled(False)
        else:
            self.xabar.setText(f"Login yoki parol notöğri. Yana {qoldi} ta uriniş qoldi.")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = KirishOynasi()
    oyna.show()
    sys.exit(app.exec())

Xato xabarida «login yoki parol notöğri» deb umumiy yozilgani ham bejiz emas: qaysi biri xato ekanini aytsangiz, buzğunçiga mavjud loginlarni topişda yordam bergan bölasiz.

Köp uçraydigan xatolar

GUI dasturlaşni endi boşlaganlar odatda bir xil tuzoqlarga tuşadi. Ularni oldindan bilsangiz, köp vaqtingiz tejaladi:

  • Funksiyani çaqirib yuboriş. tugma.clicked.connect(self.oshir()) deb yozsangiz, funksiya darhol bir marta bajariladi va uning natijasi (None) ulanadi. Töğrisi — qavslarsiz: connect(self.oshir).
  • Vidjetni özgaruvçida saqlamaslik. Oynani funksiya içida yaratib, self ga yoki taşqi özgaruvçiga saqlamasangiz, Python uni «keraksiz» deb öçirib yuborişi mumkin — oyna bir lahza körinib, yöqoladi.
  • app.exec() ni unutiş. Hodisalar sikli boşlanmasa, oyna oçilib darhol yopiladi.
  • Uzoq işni asosiy oqimda bajariş. Tugma bosilganda katta faylni yuklaş yoki internetdan maʼlumot oliş kabi uzoq amal bajarilsa, şu vaqt davomida oyna «qotib» qoladi. Bunday işlar uçun QThread yoki QThreadPool işlatiladi — bu keyingi bosqiç mavzusi.
  • Koordinatalar bilan joylaştiriş. Turli ekran ölçamlari va şriftlarda interfeys buzilmasligi uçun doim joylaşuv menejerlaridan foydalaning.

Xulosa

  • PySide6 — Qt 6 ning rasmiy, LGPL litsenziyali Python kutubxonasi; PyQt6 — GPL/tijoriy muqobili.
  • Har bir ilova: QApplication + oyna + app.exec() (hodisalar sikli).
  • Signal va slot: tugma.clicked.connect(funksiya) — funksiyani çaqirmasdan uzatamiz.
  • QLabel, QPushButton, QLineEdit, QTextEdit, QCheckBox, QRadioButton — asosiy vidjetlar.
  • Vidjetlarni koordinata bilan emas, QVBoxLayout, QHBoxLayout, QFormLayout bilan joylaştiring.
  • QMainWindow menyu va holat satrini beradi; menyu bandlari — QAction.
  • QFileDialog va QMessageBox standart dialog oynalarini tayyor holda taqdim etadi.

Maşqlar

  1. Ikki QLineEdit va törtta tugma (+, -, *, /) dan iborat oddiy kalkulyator yozing. Natija QLabel da çiqsin, nolga bölişda xabar bersin.
Yeçim
Python
import sys
from PySide6.QtWidgets import (
    QApplication, QHBoxLayout, QLabel, QLineEdit, QPushButton, QVBoxLayout, QWidget,
)


class Kalkulyator(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Kalkulyator")
        self.a = QLineEdit()
        self.b = QLineEdit()
        self.natija = QLabel("Natija: -")

        tugmalar = QHBoxLayout()
        for belgi in "+-*/":
            tugma = QPushButton(belgi)
            tugma.clicked.connect(lambda _=False, b=belgi: self.hisobla(b))
            tugmalar.addWidget(tugma)

        joylashuv = QVBoxLayout(self)
        joylashuv.addWidget(self.a)
        joylashuv.addWidget(self.b)
        joylashuv.addLayout(tugmalar)
        joylashuv.addWidget(self.natija)

    def hisobla(self, amal: str) -> None:
        try:
            x, y = float(self.a.text()), float(self.b.text())
        except ValueError:
            self.natija.setText("Iltimos, son kiriting")
            return
        match amal:
            case "+": javob = x + y
            case "-": javob = x - y
            case "*": javob = x * y
            case "/":
                if y == 0:
                    self.natija.setText("Nolga bölib bölmaydi")
                    return
                javob = x / y
        self.natija.setText(f"Natija: {javob:g}")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = Kalkulyator()
    oyna.show()
    sys.exit(app.exec())
  1. Hisoblagiç misolini özgartiring: «Kamaytiriş» tugmasini qöşing va son 0 dan pastga tuşmasin (0 bölganda tugma setEnabled(False) bilan öçirilsin).
Yeçim
Python
import sys
from PySide6.QtWidgets import QApplication, QLabel, QPushButton, QVBoxLayout, QWidget


class Hisoblagich(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.son = 0
        self.yorliq = QLabel()
        oshir = QPushButton("Oşiriş")
        self.kamaytir_tugma = QPushButton("Kamaytiriş")
        oshir.clicked.connect(lambda: self.ozgartir(1))
        self.kamaytir_tugma.clicked.connect(lambda: self.ozgartir(-1))

        joylashuv = QVBoxLayout(self)
        joylashuv.addWidget(self.yorliq)
        joylashuv.addWidget(oshir)
        joylashuv.addWidget(self.kamaytir_tugma)
        self.ozgartir(0)

    def ozgartir(self, qadam: int) -> None:
        self.son = max(0, self.son + qadam)
        self.yorliq.setText(f"Son: {self.son}")
        self.kamaytir_tugma.setEnabled(self.son > 0)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = Hisoblagich()
    oyna.show()
    sys.exit(app.exec())
  1. Mini bloknotga «Tahrirlaş» menyusini qöşing: «Bekor qiliş» (undo), «Qaytariş» (redo) va «Hammasini tanlaş» (selectAll). QTextEdit da bu metodlar tayyor mavjud.
Yeçim
Python
import sys
from PySide6.QtGui import QAction, QKeySequence
from PySide6.QtWidgets import QApplication, QMainWindow, QTextEdit


class Bloknot(QMainWindow):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Bloknot")
        self.muharrir = QTextEdit()
        self.setCentralWidget(self.muharrir)

        menyu = self.menuBar().addMenu("&Tahrirlaş")
        bandlar = [
            ("Bekor qiliş", QKeySequence.StandardKey.Undo, self.muharrir.undo),
            ("Qaytariş", QKeySequence.StandardKey.Redo, self.muharrir.redo),
            ("Hammasini tanlaş", QKeySequence.StandardKey.SelectAll, self.muharrir.selectAll),
        ]
        for matn, tugma, funksiya in bandlar:
            amal = QAction(matn, self)
            amal.setShortcut(tugma)
            amal.triggered.connect(funksiya)
            menyu.addAction(amal)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = Bloknot()
    oyna.show()
    sys.exit(app.exec())
  1. Ob-havo tanlaş oynasini yozing: uçta QRadioButton (Toşkent, Samarqand, Buxoro) va «Batafsil» QCheckBox. Radiotugma özgarişi bilan (toggled signali) QLabel da tanlangan şahar va (katakça belgilangan bölsa) «batafsil maʼlumot» yozuvi çiqsin.
Yeçim
Python
import sys
from PySide6.QtWidgets import (
    QApplication, QCheckBox, QLabel, QRadioButton, QVBoxLayout, QWidget,
)

SHAHARLAR = {"Toşkent": "+24°", "Samarqand": "+21°", "Buxoro": "+27°"}


class ObHavo(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("Ob-havo")
        joylashuv = QVBoxLayout(self)
        self.radiolar: list[QRadioButton] = []
        for nom in SHAHARLAR:
            radio = QRadioButton(nom)
            radio.toggled.connect(self.yangila)
            self.radiolar.append(radio)
            joylashuv.addWidget(radio)
        self.batafsil = QCheckBox("Batafsil")
        self.batafsil.toggled.connect(self.yangila)
        self.yorliq = QLabel("Şaharni tanlang")
        joylashuv.addWidget(self.batafsil)
        joylashuv.addWidget(self.yorliq)

    def yangila(self) -> None:
        for radio in self.radiolar:
            if radio.isChecked():
                matn = f"{radio.text()}: {SHAHARLAR[radio.text()]}"
                if self.batafsil.isChecked():
                    matn += "\nBatafsil maʼlumot: şamol sust, yoğingarçilik kutilmaydi"
                self.yorliq.setText(matn)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    oyna = ObHavo()
    oyna.show()
    sys.exit(app.exec())