Code überprüfen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Benutzeravatar
mp1337
User
Beiträge: 23
Registriert: Dienstag 5. Mai 2020, 16:33
Kontaktdaten:

Hallo liebes Forum,

ich heiße Peter, bin 33 Jahre jung und beschäftige mich hobbymäßig mit dem Programmieren.
Python hat es mir angetan =) Steige so langsa von MonoDevelop (C#) unter Ubuntu auf Python um und fange an mich mit GUI's zu beschäftigen.

Zu Übungszwecken schreibe ich einen kleinen Yourube Downloader.
Es funktioniert alles, ich möchte aber wissen ob ich das ganze Konstrukt richtig verstanden habe.

Vielleicht wäre Jemand bereit drüber zu schauen =).

Hier der Code:

Code: Alles auswählen

from PyQt5.QtGui import QIcon, QPixmap
from PyQt5 import uic
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QAction, QPushButton, QLabel,QLineEdit, \
                            QProgressBar,QFileDialog, QStyleFactory, QListWidget
import clipboard
import pytube
from urllib import request
import sys
from os import getcwd

class AppWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        uic.loadUi('app.ui', self)
        self.show()
        #Buttons
        buttonBrowse = self.findChild(QPushButton, 'buttonBrowse')
        buttonBrowse.clicked.connect(self.buttonBrowseHandler)

        buttonPaste = self.findChild(QPushButton, 'buttonPaste')
        buttonPaste.clicked.connect(self.buttonPasteHandler)

        buttonDownload = self.findChild(QPushButton, 'buttonDownload')
        buttonDownload.clicked.connect(self.buttonDownloadHandler)

        actionInfo = self.findChild(QAction, 'actionInfo')
        actionInfo.triggered.connect(self.actionInfoHandler)

        actionClose = self.findChild(QAction, 'actionClose')
        actionClose.triggered.connect(self.actionCloseHandler)

        #list Widget
        self.listWidget = self.findChild(QListWidget, 'listWidget')

        #Labels
        self.labelVideo = self.findChild(QLabel, 'labelVideo')
        self.labelImage = self.findChild(QLabel, 'labelImage')
        img = QPixmap("default_thumb.jpg")
        self.labelImage.setPixmap(img)

        #QlineEdits
        self.editTarget  = self.findChild(QLineEdit, 'editTarget')
        self.editTarget.setText(getcwd())

        self.editLink = self.findChild(QLineEdit, 'editLink')

        #Progressbar
        self.pbar  = self.findChild(QProgressBar)
        self.pbar.setValue(0)

################################################################################################

    def buttonBrowseHandler(self):
        filename = str(QFileDialog.getExistingDirectory(None))
        self.editTarget.setText(filename)

    def buttonPasteHandler(self):
        clip = str(clipboard.paste())
        self.editLink.setText(clip)
        clip = self.editLink.text()
        
        if 'youtube' in clip and 'watch' in clip:
            yttitle = pytube.YouTube(clip)
            self.labelVideo.setText(yttitle.title)
            url = yttitle.thumbnail_url #url for thumbnail
            request.urlretrieve(url,'thumb.jpg')
            pixmap = QPixmap("thumb.jpg").scaled(650,400)
            self.labelImage.setPixmap(pixmap)
        else:
            QMessageBox.about(self,"Warning", "No YouTube link!")
    
    def buttonDownloadHandler(self):
        try:
            ytdown = pytube.YouTube(str(self.editLink.text()),on_progress_callback=self.progress_function)
            ytdown.streams.first().download(self.editTarget.text())
            self.listWidget.addItem(ytdown.title)
            self.download_finished()
        except:
            print(ex)
        
    
    def progress_function(self, stream, chunk, bytes_remaining):
        percent = round((1-bytes_remaining/stream.filesize)*100)
        if( percent%1 == 0):
            self.pbar.setValue(percent)            

    def download_finished(self):
        QMessageBox.information(self, "Download Completed", "YouTube download completed!")

    def actionInfoHandler(self):
        QMessageBox.information(self, "Info", "Created by Peter")

    def actionCloseHandler(self):
        QApplication.instance().quit()

################################################################################################

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = AppWindow()
    sys.exit(app.exec_())
Vielen Dank im Voraus!
IT-News ohne Werbung
https://www.it-feedo.de
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Fuer ein Erstlingswerk durchaus gelungen. Aber Anmerkungen gibt es natuerlich trotzdem:

- man macht keine nackten try/excepts. Das verschleiert ggf. Programmierfehler. Exceptions immer so praezise wie moeglich abfangen, also zB

Code: Alles auswählen

try:
     int(string_a) / int(string_b)
except (ValueError, DivisionByZeroError):
    ...
- das show im Konstruktor *muss* nicht als letztes, sollte aber.
- ich bin mir ziemlich sicher, dass das laden der UI-Datei die Widgets die du benannt hast auch als Attribute anlegt. Du also sowas in der Art von

Code: Alles auswählen

        self.buttonPaste.clicked.connect(self.buttonPasteHandler)
ohne das find davor machen koennen solltest.

- percent%1 == 0 ist sinnlos, alles ist immer ohne Rest durch 1 teilbar.
- der if __name__ guard ist schon ein guter Anfang. Naechster Schritt: darin nur noch eine main-Funktion aufrufen, die dann den Code enthaelt der da jetzt schon steht. Denn so legst du boese(tm) globale Variablen an. Und verlaesst dich ggf. aus versehen darauf.
Benutzeravatar
mp1337
User
Beiträge: 23
Registriert: Dienstag 5. Mai 2020, 16:33
Kontaktdaten:

Edit: Hab den Code bei gituhub inkl. GUI Layout hochgeladen. Vielleicht kann ihn ja Jemand gebrauchen =) https://github.com/MP1337/SimpleYoutubeDownloaderQT

Danke für deine schnelle Antwort und die Tipps. Schön, dass ich nicht so viele Böcke geschossen habe.

- Wenn ich "findChild" entferne kommt dann 'buttonBrowse' is not defined. Vielleicht würde das mit PySide2 funktionieren da ich schon ähnliches Verhalten an anderen Stellen bemerkt habe.

- Das "percent%1 == 0" habe ich nur drin weil ich ursprünglich die ProgressBar in 10er Schritten beschrieben habe, werde ich gleich korrigieren

- Die nackten Exceptions habe ich vor meinem ersten Post hier im Forum hinzugefügt damit keiner sagt ich hätte nicht daran gedacht. Hab erst jetzt geschaut was es überhaupt für Exceptions gibt. Korrigiert =)
- Ich habe jetzt alles in eine main() Funktion gepackt. Könntest du mir das bitte erläutern:
"Denn so legst du boese(tm) globale Variablen an. Und verlaesst dich ggf. aus versehen darauf."

Hier der korrigierte Code:

Code: Alles auswählen

from PyQt5.QtGui import QIcon, QPixmap
from PyQt5 import uic
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QAction, QPushButton, QLabel,QLineEdit, \
                            QProgressBar,QFileDialog, QStyleFactory, QListWidget
import clipboard
import pytube
from urllib import request
import sys
from os import getcwd

class AppWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        uic.loadUi('app.ui', self)
        
        #Buttons
        buttonBrowse = self.findChild(QPushButton, 'buttonBrowse')
        buttonBrowse.clicked.connect(self.buttonBrowseHandler)

        buttonPaste = self.findChild(QPushButton, 'buttonPaste')
        buttonPaste.clicked.connect(self.buttonPasteHandler)

        buttonDownload = self.findChild(QPushButton, 'buttonDownload')
        buttonDownload.clicked.connect(self.buttonDownloadHandler)

        actionInfo = self.findChild(QAction, 'actionInfo')
        actionInfo.triggered.connect(self.actionInfoHandler)

        actionClose = self.findChild(QAction, 'actionClose')
        actionClose.triggered.connect(self.actionCloseHandler)

        #list Widget
        self.listWidget = self.findChild(QListWidget, 'listWidget')

        #Labels
        self.labelVideo = self.findChild(QLabel, 'labelVideo')
        self.labelImage = self.findChild(QLabel, 'labelImage')
        img = QPixmap("default_thumb.jpg")
        self.labelImage.setPixmap(img)

        #QlineEdits
        self.editTarget  = self.findChild(QLineEdit, 'editTarget')
        self.editTarget.setText(getcwd())

        self.editLink = self.findChild(QLineEdit, 'editLink')

        #Progressbar
        self.pbar  = self.findChild(QProgressBar)
        self.pbar.setValue(0)

        self.show()

################################################################################################

    def buttonBrowseHandler(self):
        filename = str(QFileDialog.getExistingDirectory(None))
        self.editTarget.setText(filename)

    def buttonPasteHandler(self):
        clip = str(clipboard.paste())
        self.editLink.setText(clip)
        clip = self.editLink.text()
        
        if 'youtube' in clip and 'watch' in clip:
            yttitle = pytube.YouTube(clip)
            self.labelVideo.setText(yttitle.title)
            url = yttitle.thumbnail_url #url for thumbnail
            request.urlretrieve(url,'thumb.jpg')
            pixmap = QPixmap("thumb.jpg").scaled(650,400)
            self.labelImage.setPixmap(pixmap)
        else:
            QMessageBox.about(self,"Warning", "No YouTube link!")
    
    def buttonDownloadHandler(self):
        try:
            ytdown = pytube.YouTube(str(self.editLink.text()),on_progress_callback=self.progress_function)
            ytdown.streams.first().download(self.editTarget.text())
            self.listWidget.addItem(ytdown.title)
            self.download_finished()
        except pytube.exceptions.PytubeError as err:
            print(err)
        
    
    def progress_function(self, stream, chunk, bytes_remaining):
        percent = round((1-bytes_remaining/stream.filesize)*100)
        self.pbar.setValue(percent)            

    def download_finished(self):
        QMessageBox.information(self, "Download Completed", "YouTube download completed!")

    def actionInfoHandler(self):
        QMessageBox.information(self, "Info", "Created by Peter Mazela")

    def actionCloseHandler(self):
        QApplication.instance().quit()

def main():
    app = QApplication(sys.argv)
    ex = AppWindow()
    sys.exit(app.exec_())

################################################################################################

if __name__ == '__main__':
    main()

IT-News ohne Werbung
https://www.it-feedo.de
Benutzeravatar
__blackjack__
User
Beiträge: 13533
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mp1337: Die `findChild()`-Aufrufe *sind* unnötig, denn genau deswegen übergibt man ja `uic.LoadUi()` das Objekt selbst als Argument, damit die Funktion diese Attribute darauf setzen kann.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Um rauszufinden, was das Objekt fuer Attribute hat, kannst du zB mal nach dem loadUI folgenden Code packen, und ueber das Ergebnis nachdenken:

Code: Alles auswählen

import pprint
pprint.pprint(self.__dict__)
Was die Frage zum Main angeht - das hier illustriert das Problem:

Code: Alles auswählen

def funktion():
      print(unabsichlich_global)

# A - geht, aber sollte man eben nicht machen.
if __name__ == "__main__":
      unabsichtlich_global = 1000
      funktion()

# B knallt, und mit den Worten eines ehemaligen Berliner OB: und das ist auch gut so
def main():
      unabsichtlich_global = 1000
      funktion()
if __name__ == "__main__":
     main()
Benutzeravatar
mp1337
User
Beiträge: 23
Registriert: Dienstag 5. Mai 2020, 16:33
Kontaktdaten:

__deets__ hat geschrieben: Mittwoch 6. Mai 2020, 14:41 Um rauszufinden, was das Objekt fuer Attribute hat, kannst du zB mal nach dem loadUI folgenden Code packen, und ueber das Ergebnis nachdenken:

Code: Alles auswählen

import pprint
pprint.pprint(self.__dict__)
Was die Frage zum Main angeht - das hier illustriert das Problem:
Vielen Dank! Das macht die Sache mit der GUI Programmierung ja noch einfacher =)
Hat funktioniert, musste noch ein self. vor die Widgets schreiben.

pprint ist ja mega, danke für den Tipp!!
IT-News ohne Werbung
https://www.it-feedo.de
Antworten