0

Is there anyway I can run this program like this.

At the start of the program GUI shows and background process starts running. And the the GUI can be closed and opened anytime the user wants. But the background process keeps running uninterrupted.

Here is my current code. 4 classes.

  1. FileHandler
  2. Scanner <- Inherits from QThread
  3. UserInterface <- Takes QMainWindow as an argument
  4. Main <- Inherits from UserInterface
import math
import sys
import time
import psutil
import win32gui
import win32process
import threading
import atexit
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtChart import QChart, QChartView, QPieSeries
from PyQt5.QtCore import QThread, pyqtSignal

RECORDED_PROGRAMS = {}

class FileHandler():
    def __init__ (self):
        print("[+] Driver File Initiated...")
        self.logFile = open("log.txt", "a+")
        self.dataFile = open("data.txt", "a+")

    def readFile(self):
        pass

    def writeFile(self):
        print("[+] {} : Writing Data to the File...".format(time.strftime("%H:%M:%S".format(time.localtime()))))
        self.dataFile.write("\nOn Write : {} : ".format(time.strftime("%H:%M:%S".format(time.localtime()))) + str(RECORDED_PROGRAMS))

    def closeFile(self):
        print("[+] File Handler Exiting...")
        print("[+] Closing File...")
        self.dataFile.write("\nOn Exit  : {} : ".format(time.strftime("%H:%M:%S".format(time.localtime()))) + str(RECORDED_PROGRAMS))
        self.dataFile.write("\n=================================================================================")
        self.dataFile.write("\n[!] Program Exited. TimeStamp: {}".format(time.strftime("%H:%M:%S", time.localtime())))
        self.dataFile.write("\n=================================================================================")
        self.dataFile.close()
        self.logFile.close()
#==============================================================================================================
class Scanner(QThread):

    signal = pyqtSignal(bool)
    
    def run(self):
        print("[+] Scanner Initialized...")
        count = 0
        while True:
            count += 1
            try:
                activeWindowId = win32gui.GetForegroundWindow()
                threadList = win32process.GetWindowThreadProcessId(activeWindowId)
                mainThreadName = psutil.Process(threadList[-1]).name()

                if(mainThreadName in RECORDED_PROGRAMS.keys()):
                    RECORDED_PROGRAMS[mainThreadName] += 1
                else:
                    RECORDED_PROGRAMS[mainThreadName] = 1
            except Exception as E:
                print("[-] Error in Scanner...")
                print("======================================================")
                print(E)
                print("======================================================")

            if count == 60:
                self.signal.emit(True)
                count = 0
            time.sleep(1)

#==============================================================================================================
class UserInterface():
    def __init__(self, MainWindow):
        self.setupUi(MainWindow)

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(640, 480)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
        MainWindow.setSizePolicy(sizePolicy)
        MainWindow.setMinimumSize(QtCore.QSize(640, 480))
        MainWindow.setMaximumSize(QtCore.QSize(640, 480))
        font = QtGui.QFont()
        font.setFamily("Bahnschrift")
        font.setPointSize(14)
        MainWindow.setFont(font)

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.centralWidgetHLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.centralWidgetHLayout.setObjectName("centralWidgetHLayout")

        self.leftGroupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.leftGroupBox.setMinimumSize(QtCore.QSize(300, 462))
        self.leftGroupBox.setMaximumSize(QtCore.QSize(300, 462))
        self.leftGroupBox.setObjectName("leftGroupBox")

        self.leftGroupVLayout = QtWidgets.QVBoxLayout(self.leftGroupBox)
        self.leftGroupVLayout.setContentsMargins(5, 5, 5, 5)
        self.leftGroupVLayout.setSpacing(5)
        self.leftGroupVLayout.setObjectName("leftGroupVLayout")

        self.widget = QtWidgets.QWidget(self.leftGroupBox)
        self.widget.setMinimumSize(QtCore.QSize(288, 427))
        self.widget.setMaximumSize(QtCore.QSize(288, 427))
        self.widget.setObjectName("widget")
        self.widgetLayout = QtWidgets.QVBoxLayout(self.widget)
        self.widgetLayout.setContentsMargins(0, 0, 0, 0)
        self.widgetLayout.setSpacing(0)
        self.widgetLayout.setObjectName("widgetLayout")

        self.series = QPieSeries()
        self.chart = QChart()
        self.chart.addSeries(self.series)
        self.chartView = QChartView(self.chart)
        self.chart.setAnimationOptions(QChart.SeriesAnimations)
        self.chart.legend().hide()

        self.widgetLayout.addWidget(self.chartView)
        self.leftGroupVLayout.addWidget(self.widget)
        self.centralWidgetHLayout.addWidget(self.leftGroupBox)

        self.rightGroupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.rightGroupBox.setMinimumSize(QtCore.QSize(316, 462))
        self.rightGroupBox.setMaximumSize(QtCore.QSize(316, 462))
        self.rightGroupBox.setObjectName("rightGroupBox")

        self.rightGroupVLayout = QtWidgets.QVBoxLayout(self.rightGroupBox)
        self.rightGroupVLayout.setContentsMargins(5, 5, 5, 5)
        self.rightGroupVLayout.setSpacing(5)
        self.rightGroupVLayout.setObjectName("rightGroupVLayout")

        self.tableView = QtWidgets.QTableWidget(self.rightGroupBox)
        self.tableView.setMinimumSize(QtCore.QSize(304, 427))
        self.tableView.setMaximumSize(QtCore.QSize(304, 427))
        self.tableView.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.tableView.setFrameShadow(QtWidgets.QFrame.Plain)
        self.tableView.setLineWidth(1)
        self.tableView.setMidLineWidth(0)
        self.tableView.setObjectName("tableView")
        self.tableView.setColumnCount(2)
        self.tableView.setRowCount(0)

        item = QtWidgets.QTableWidgetItem()
        self.tableView.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()

        self.tableView.setHorizontalHeaderItem(1, item)
        self.tableView.horizontalHeader().setVisible(True)
        self.tableView.horizontalHeader().setDefaultSectionSize(150)
        self.tableView.horizontalHeader().setHighlightSections(False)
        self.tableView.horizontalHeader().setMinimumSectionSize(150)
        self.tableView.verticalHeader().setVisible(True)
        self.tableView.verticalHeader().setDefaultSectionSize(31)

        self.rightGroupVLayout.addWidget(self.tableView)
        self.centralWidgetHLayout.addWidget(self.rightGroupBox)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.leftGroupBox.setTitle(_translate("MainWindow", "Top 10 Overview"))
        self.rightGroupBox.setTitle(_translate("MainWindow", "Recorded Programs"))
        self.tableView.setSortingEnabled(True)
        item = self.tableView.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "Program Name"))
        item = self.tableView.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "Time (mins)"))

        MainWindow.show()

    
#===========================================================================================================================
class Main(UserInterface):
    def __init__(self, MainWindow):
        self.scanner = Scanner()
        self.fileHandler = FileHandler()
        self.runScanner()
        super().__init__(MainWindow)

    def runScanner(self):
        self.scanner.signal.connect(self.update)
        self.scanner.start()

    def update(self):
        self.updateChart()
        self.updateTable()
        self.updateLog()

    def updateChart(self):
        print("[+] Updating Chart...")
        self.chart.removeSeries(self.series)
        self.series.clear()
        for key, val in RECORDED_PROGRAMS.items():
            self.series.append(key, math.ceil(val/60))

        self.chart.addSeries(self.series)

    def updateTable(self):
        print("[+] Updating Table...")
        rowCount = len(RECORDED_PROGRAMS)
        self.tableView.setRowCount(rowCount)
        programNames = list(RECORDED_PROGRAMS.keys())
        timeRec = list(RECORDED_PROGRAMS.values())

        for row in range(rowCount):
            for column in range(2):
                if column == 0:
                    self.tableView.setItem(row, column, QtWidgets.QTableWidgetItem(str(programNames[row])))
                
                if column == 1:
                    self.tableView.setItem(row, column, QtWidgets.QTableWidgetItem(str(math.ceil(timeRec[row]/60))))

    def updateLog(self):
        print("[+] Updating Log...")
        self.fileHandler.writeFile()

    def quit(self):
        print("[+] Quitting Program...")
        self.scanner.terminate()
        self.fileHandler.closeFile()
#===========================================================================================================================
def _exit():
    print("[+] At Exit Func Triggered...")
    main.quit()

if __name__ == "__main__":
    guiApplication = QtWidgets.QApplication(sys.argv)
    window = QtWidgets.QMainWindow()
    main = Main(window)
    atexit.register(_exit)

    stop = False
    while not stop:
        answer = input("Stop [Y/N]? ")
        if answer == "Y":
            stop = True
        time.sleep(0.5)    
    
    print("[+] Main Loop Exit hit...")
  • Why are you using atexit? Where is the application event loop started? Also, the code above seems incomplete (and, btw, far from *minimal*), as `exec_` is never declared. Besides, you're not supposed to edit pyuic generated code nor *directly* subclass its classes, especially like that and using threading (please follow the official guidelines about [using Designer](//www.riverbankcomputing.com/static/Docs/PyQt5/designer.html), any other approach is generally considered bad practice and highly discouraged). Finally it's actually unclear what part of your problem you're having issues with. – musicamante Aug 14 '22 at 01:58
  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Community Aug 14 '22 at 04:46
  • @musicamante I saw your profile. You are quite experienced in pyqt. Do you have a YouTube Channel or something I can watch? – Black Monkey Aug 14 '22 at 08:33
  • 1
    @BlackMonkey Sorry, no. I don't really believe a lot in video tutorials for programming (and skill education in general). Sooner or later I'll finish a website where I'll eventually post articles and documents that will expand some aspects generally unknown or misunderstood. – musicamante Aug 14 '22 at 08:48

1 Answers1

0

There are a couple ways you can do this. You can set it to hide windows with self.hide() and self.show() to show them again, or you can use the self.setVisible(False) on each window you want to hide. You can loop through them to hide all of them. You could either leave one window open with a push button to call the functions to show and hide them, or you can use another library like pynput to set keybinds to show or hide the window. Of course you should make a seperate function for this and call it by the push button or keybind, and you can choose what to do in the function by checking if self.isVisible(). The name doesn't have to be self, it can be each one you want to hide and you can loop through them and use a comprehension or lambda function.

Brandon Johnson
  • 172
  • 1
  • 6
  • I've to admit that it's quite unclear what the OP is having problems with, but in any case it doesn't seem to be what you're proposing, since (and that's probably the only specified aspect) they clearly say that "the GUI can be closed and opened anytime the user wants”. – musicamante Aug 14 '22 at 02:03
  • with opening and closing the GUI anytime the user wants, that's what using a keybind or one window staying open with a push button in my answer was for – Brandon Johnson Aug 14 '22 at 04:38
  • the OP could add all windows into a dict except the window for the push button, if that's what they choose, and loop through it to hide them. Alternatively they could use a keybind with their program to show/hide all windows – Brandon Johnson Aug 14 '22 at 04:39
  • either way, they could add a condition `if self.isVisible()` in their function to decide to show or hide the windows – Brandon Johnson Aug 14 '22 at 04:40
  • Where did you read that they need a keyboard shortcut or a button? A window closure can be done from the standard controls (existing window interface or hotkeys). The OP is probably asking about some kind of backend/frontend, not specifically focusing on a way to control the behavior of the windows. – musicamante Aug 14 '22 at 06:44