Python Forum
[PyQt] Creating a GUI
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyQt] Creating a GUI
#1
Hi,
as required, I started a new thread.

I'm very sorry for asking again, but unfortunately I can not handle this...

Could you please help me again?

This was an example without the created class "MyWindow()" that I managed (but that you probably won't like):
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayout
import sys

app = QApplication([])
window = QWidget()
window.setWindowTitle("Py App")
window.setGeometry(100, 100, 200, 200)

bA = QPushButton("A")
bB = QPushButton("B")
bC = QPushButton("C")
layout = QHBoxLayout()
layout.addWidget(bA)
layout.addWidget(bB)
layout.addWidget(bC)
bD = QPushButton("D")
bE = QPushButton("E")
bF = QPushButton("F")
layout2 = QVBoxLayout()
layout2.addWidget(bD)
layout2.addWidget(bE)
layout2.addWidget(bF)
layout.addLayout(layout2)
window.setLayout(layout)
window.show()
sys.exit(app.exec())
I tried to create the attributes layout and layout2 from the class "MyWindow()"
But I'm guessing...:
class MyWindow():
    def __init__(self, layout, layout2):
        self.layout = layout
        self.layout2 = layout2
        super().__init__()
        # make all the widgets
        labelThreshold = QLabel()
        labelThreshold.setText("erlaubter Unterschied für gleiche Bilder (threshold):")
        textboxThreshold = QLineEdit()
        textboxThreshold.setText(str(0))
        labelMinCount = QLabel()
        labelMinCount.setText("kleinster Hänger, der gefunden wird (Anzahl Bilder, min_count):")
        textboxMinCount = QLineEdit()
        textboxMinCount.setText(str(5))
        layout = QVBoxLayout()
        layout.addWidget(labelThreshold)
        layout.addWidget(textboxThreshold)
        layout.addWidget(labelMinCount)
        layout.addWidget(textboxMinCount)
        layout2 = QHBoxLayout()
        layout2.addWidget(button_HF_Start)
        layout2.addWidget(button_close)
        layout.addLayout(layout2)
        button_HF_Start = QPushButton("Hängerfinder ausführen")
        button_close = QPushButton("schließen")
        button_HF_Start.clicked.connect(self.start_processing)
        button_close.clicked.connect(self.close)

    def start_processing(self):
        # called when startbutton is clicked
        # check if film is marked with x in excel
        pH = "C:\hangerfinder\S8-Hanger_Positionen.xlsm"
        fileXLSX = openpyxl.load_workbook(pH, keep_vba=True)
        sheet = fileXLSX["Blatt"]
        r = 2
        xOcurrence = 0
        for c in range(2, 82, 4):
            if sheet.cell(r, c).value == "x":
                xOcurrence = 1
        if xOcurrence == 0:
            ctypes.windll.user32.MessageBoxW(0, "Please mark the film to be checked for hangers in excel with an ""X"".", "You forgot something...", 1)
            sys.exit()
        begin = time.time()
        all_frames = os.listdir(p)
        threshold = textboxThreshold.text()
        min_count = textboxMinCount.text()
        hangers = detect_hangers(phash_iter(all_frames), int(threshold), int(min_count))
        numberOfHangers = len(hangers)
        fill_hanger_information_in_excel(hangers, numberOfHangers)
        end = time.time()

    def close(self):
        quit()

def main():
    # Is not executed when file is imported
    app = QApplication([])
    window = MyWindow()
    app.exec()
    #window.close()
    #window.setWindowTitle("hangerfinder")
    #window.setGeometry(100, 100, 200, 200)

if __name__ == "__main__":
    main()
I'm sorry that I'm not so experienced in this...
Reply
#2
This is your first example written using a class.
from PyQt5.QtWidgets import (
    QApplication,
    QWidget,
    QPushButton,
    QHBoxLayout,
    QVBoxLayout,
)
import sys


class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.a2c = [QPushButton(x) for x in "ABC"]
        self.d2f = [QPushButton(x) for x in "DEF"]
        hlayout = QHBoxLayout(self)
        for button in self.a2c:
            hlayout.addWidget(button)
        vlayout = QVBoxLayout(self)
        for button in self.d2f:
            vlayout.addWidget(button)
        hlayout.addLayout(vlayout)


def main():
    app = QApplication()
    window = MyWindow()
    window.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()
And this the second.
from PyQt5.QtWidgets import (
    QWidget,
    QLabel,
    QLineEdit,
    QPushButton,
    QHBoxLayout,
    QVBoxLayout,
    QApplication,
)


class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        vbox = QVBoxLayout(self)
        vbox.addWidget(QLabel("erlaubter Unterschied für gleiche Bilder (threshold):"))
        self.threshold = QLineEdit("0")
        vbox.addWidget(self.threshold)

        vbox.addWidget(
            QLabel("kleinster Hänger, der gefunden wird (Anzahl Bilder, min_count):")
        )
        self.min_count = QLineEdit("5")
        vbox.addWidget(self.min_count)

        hbox = QHBoxLayout()
        vbox.addLayout(hbox)
        start_button = QPushButton("Hängerfinder ausführen")
        start_button.clicked.connect(self.start_processing)
        hbox.addWidget(start_button)
        close_button = QPushButton("schließen")
        close_button.clicked.connect(self.close)
        hbox.addWidget(close_button)

    def start_processing(self):
        print(
            f"Threshold = {int(self.threshold.text())}, Min Count = {int(self.min_count.text())}"
        )


def main():
    app = QApplication([])
    window = MyWindow()
    window.show()
    app.exec()


if __name__ == "__main__":
    main()
Note: I replaced the processing code with a simple demo showing how to get the threshold and min count values from the GUI.
Reply
#3
Dear deanhystad,
Thank you very much for your fantastic help!
As you may have noticed, writing custom classes is still completely new to me.
I'll probably get around to trying it out tomorrow due to appointments.
Thank you again!

Edit:
I did find some time:

Dear deanhystad,

I successfully implemented your class, thanks again...

Inside PyCharm everything works. There was another problem that I was able to fix by finding an entry on stackoverflow:

I created an exe of the hangerfinder with pyinstaller - when using it, a new window kept opening.

The stackoverflow entry read:
"If you want to use multiprocessing as a frozen executable, you need to call multiprocessing.freeze_support()
at the beginning of your main script. This will allow multiprocessing to "take over" when it spawns its worker processes."

I have implemented this - and now the hangerfinder also works as an exe!!

I think the project is now complete, thank you very much...

import multiprocessing

from PyQt6.QtWidgets import QWidget, QLabel, QLineEdit, QPushButton, QHBoxLayout, QVBoxLayout, QApplication
import ctypes
import os, sys
import time
import openpyxl
from itertools import zip_longest
from multiprocessing import Pool
from PIL import Image
import imagehash

p = "C:/hangerfinder/bild_analyse/"

def create_phash(frame):
    # load frames
    frame = Image.open(p + str(frame))
    # create pHash
    # Compare hashes to determine whether the frames are the same or not
    phash = str(imagehash.phash(frame))
    return phash

def difference_count(a: str, b: str) -> int:
    # count differences between a and b
    return sum(1 for a, b in zip_longest(a, b) if a != b)

def phash_iter(d):
    # frame phash iterator
    # use multiprocessing to hash frames in parallel
    pool = Pool(processes=6)
    for index, phash in enumerate(pool.imap(create_phash, d, chunksize=1)):
        yield index, phash

def detect_hangers(phash_iter, threshold, min_count):
    """Return list of "hangers" detected in frame_hash_list.
    A "hanger" is consecutive frames that are the same.

    frame_hash_list : list of frame hash strings.  Frames are considered
    same or different by counting the differences in their hash strings.

    threshold : Maximum number of diffences allowed for two frames to be
    considered "same".

    min_count : Minimum length of a hanger.  Short hangers aren't noticable
    and don't have to be removed.
    """
    hangers = []  # List of hanger start, stop frame indexes
    start_index, start_phash = next(phash_iter)
    for index, phash in phash_iter:
        print(index, phash)
        # Are frame and start_frame disimilar enough?
        if abs(difference_count(start_phash, phash)) > threshold:
            if index - start_index >= min_count:
                # Add hanger to list
                hangers.append((start_index, index - 1))
            start_phash = phash
            start_index = index
    # Check if we end with a hanger
    if index - start_index > 10:
        hangers.append((start_index, index))
    return hangers

def convert_frame_nr_in_time(d):
    # S8-Movie (avi-file) is checked of hangers
    #####################################################
    # 1 hour contains 72000 frames
    c1 = 72000
    # 1 minute contains 1200 frames
    c2 = 1200
    # 1 second contains 20 frames
    c3 = 20

    def find_even_frame_nr(a, b, c):
        while True:
            if a % c == 0:
                break
            else:
                a -= 1
                b += 1
        return a, b

    frame_nr_full_hour, rest_1 = find_even_frame_nr(d, 0, c1)
    number_of_hours = frame_nr_full_hour / c1
    ###########################################################
    frame_nr_full_minute, rest_2 = find_even_frame_nr(rest_1, 0, c2)
    number_of_minutes = frame_nr_full_minute / c2
    ###########################################################
    frame_nr_full_second, rest_3 = find_even_frame_nr(rest_2, 0, c3)
    number_of_seconds = frame_nr_full_second / c3

    return number_of_hours, number_of_minutes, number_of_seconds


def fill_hanger_information_in_excel(hangers, numberOfHangers):
    pH = "C:\hangerfinder\S8-Hanger_Positionen.xlsm"
    fileXLSX = openpyxl.load_workbook(pH, keep_vba=True)
    sheet = fileXLSX["Blatt"]

    # find film to check for hangers in excel
    # film was marked with "x" by me
    r = 2
    for c in range(2, 82, 4):
        if sheet.cell(r, c).value == "x":
            hanger_start_col = c
            hanger_end_col = c + 2

    # clear old hanger information
    # film doesn't have more than 100 hangers
    clear_row = 5
    clear_col = hanger_start_col
    for z in range(clear_row, clear_row + 100):
        for s in range(clear_col, clear_col + 3):
            sheet.cell(row=z, column=s).value = None

    # fill in hanger information
    r = 5
    for i in hangers:
        frame_nr_hanger_start = i[0]
        frame_nr_hanger_end = i[1]
        number_of_hours_start, number_of_minutes_start, number_of_seconds_start = convert_frame_nr_in_time(
            frame_nr_hanger_start)
        number_of_hours_end, number_of_minutes_end, number_of_seconds_end = convert_frame_nr_in_time(
            frame_nr_hanger_end)
        number_of_hours_start_int = int(number_of_hours_start)
        number_of_minutes_start_int = int(number_of_minutes_start)
        number_of_seconds_start_int = int(number_of_seconds_start)
        number_of_hours_end_int = int(number_of_hours_end)
        number_of_minutes_end_int = int(number_of_minutes_end)
        number_of_seconds_end_int = int(number_of_seconds_end)

        number_of_hours_start_str = str(number_of_hours_start_int)
        if len(number_of_hours_start_str) == 1:
            number_of_hours_start_str = "0" + number_of_hours_start_str
        number_of_minutes_start_str = str(number_of_minutes_start_int)
        if len(number_of_minutes_start_str) == 1:
            number_of_minutes_start_str = "0" + number_of_minutes_start_str
        number_of_seconds_start_str = str(number_of_seconds_start_int)
        if len(number_of_seconds_start_str) == 1:
            number_of_seconds_start_str = "0" + number_of_seconds_start_str
        number_of_hours_end_str = str(number_of_hours_end_int)
        if len(number_of_hours_end_str) == 1:
            number_of_hours_end_str = "0" + number_of_hours_end_str
        number_of_minutes_end_str = str(number_of_minutes_end_int)
        if len(number_of_minutes_end_str) == 1:
            number_of_minutes_end_str = "0" + number_of_minutes_end_str
        number_of_seconds_end_str = str(number_of_seconds_end_int)
        if len(number_of_seconds_end_str) == 1:
            number_of_seconds_end_str = "0" + number_of_seconds_end_str

        # create timestamp
        timestamp_start_str = number_of_hours_start_str + ":" + number_of_minutes_start_str + ":" + number_of_seconds_start_str
        timestamp_end_str = number_of_hours_end_str + ":" + number_of_minutes_end_str + ":" + number_of_seconds_end_str
        hangerNrs = "(" + str(frame_nr_hanger_start) + ", " + str(frame_nr_hanger_end) + ")"
        hangerNrs_col = hanger_start_col - 1
        sheet.cell(row=r, column=hangerNrs_col).value = hangerNrs
        sheet.cell(row=r, column=hanger_start_col).value = timestamp_start_str
        sheet.cell(row=r, column=hanger_end_col).value = timestamp_end_str
        r += 1
        hanger_text_col = hanger_start_col-1
    sheet.cell(row=r, column=hanger_text_col).value = "gefundene Hänger:"
    sheet.cell(row=r, column=hanger_start_col).value = numberOfHangers
    fileXLSX.save(pH)
    fileXLSX.close
    ctypes.windll.user32.MessageBoxW(0, "All hangers were written to excel.", "Cool...", 1)


class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        vbox = QVBoxLayout(self)
        vbox.addWidget(QLabel("erlaubter Unterschied für gleiche Bilder (threshold):"))
        self.threshold = QLineEdit("0")
        vbox.addWidget(self.threshold)
        vbox.addWidget(QLabel("kleinster Hänger, der gefunden wird (Anzahl Bilder, min_count):"))
        self.min_count = QLineEdit("5")
        vbox.addWidget(self.min_count)
        hbox = QHBoxLayout()
        vbox.addLayout(hbox)
        button_HF_Start = QPushButton("Hängerfinder ausführen")
        button_HF_Start.clicked.connect(self.start_processing)
        hbox.addWidget(button_HF_Start)
        button_close = QPushButton("schließen")
        button_close.clicked.connect(self.close)
        hbox.addWidget(button_close)

    def start_processing(self):
        # called when startbutton is clicked
        # check if film is marked with x in excel
        pH = "C:\hangerfinder\S8-Hanger_Positionen.xlsm"
        fileXLSX = openpyxl.load_workbook(pH, keep_vba=True)
        sheet = fileXLSX["Blatt"]
        r = 2
        xOcurrence = 0
        for c in range(2, 82, 4):
            if sheet.cell(r, c).value == "x":
                xOcurrence = 1
        if xOcurrence == 0:
            ctypes.windll.user32.MessageBoxW(0, "Please mark the film to be checked for hangers in excel with an ""X"".", "You forgot something...", 1)
            sys.exit()
        begin = time.time()
        all_frames = os.listdir(p)
        hangers = detect_hangers(phash_iter(all_frames), int(self.threshold.text()), int(self.min_count.text()))
        numberOfHangers = len(hangers)
        fill_hanger_information_in_excel(hangers, numberOfHangers)
        end = time.time()

    def close(self):
        quit()

def main():
    # Is not executed when file is imported
    multiprocessing.freeze_support()
    app = QApplication([])
    window = MyWindow()
    window.setWindowTitle("hangerfinder")
    window.setGeometry(700, 400, 200, 200)
    window.show()
    app.exec()


if __name__ == "__main__":
    main()
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020