Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Issue with Worker Class when used with QTables
QtWS25 Last Chance

Issue with Worker Class when used with QTables

Scheduled Pinned Locked Moved Unsolved Qt for Python
qtablemodelworkerthread
20 Posts 4 Posters 988 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • I Offline
    I Offline
    imissthecommandline
    wrote on 24 Jul 2024, 13:51 last edited by
    #2

    ok so the workaround i discovered is initializing new threads from tabs without QTables. What confuses me is that you seem to be allowed at least one thread per tab with a Qtable before the issues start

    1 Reply Last reply
    0
    • I Offline
      I Offline
      imissthecommandline
      wrote on 24 Jul 2024, 14:24 last edited by
      #3

      the workaround does not in fact work. unclear what is going on at this point

      P 1 Reply Last reply 24 Jul 2024, 14:54
      0
      • I imissthecommandline
        24 Jul 2024, 13:39

        I'm having an issue in my program where utilizing threading is causing lag, but only if there is a QTableModel on the tab in which the Threads are started. I'm using the full signals + slots framework, and I'm only experiencing this lag when starting several functions from that specific tab of the program. Removing that specific QTableModel seems to fix the issue entirely. The lag also only exists if that QTableModel is on the same tab where I start the new threads, and only if I'm either on that tab, or switching to a tab with a QTableModel.

        Is QTableModel only threadsafe if used once in a program?

        J Offline
        J Offline
        jsulm
        Lifetime Qt Champion
        wrote on 24 Jul 2024, 14:42 last edited by
        #4

        @imissthecommandline said in Issue with Worker Class when used with QTables:

        Is QTableModel only threadsafe if used once in a program?

        The documentation states that it is not: https://doc.qt.io/qt-6/qabstracttablemodel.html

        "What confuses me is that you seem to be allowed at least one thread per tab" - not sure what you mean here. UI may only be accessed from main/UI thread!

        https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        2
        • I imissthecommandline
          24 Jul 2024, 14:24

          the workaround does not in fact work. unclear what is going on at this point

          P Offline
          P Offline
          Pl45m4
          wrote on 24 Jul 2024, 14:54 last edited by
          #5

          @imissthecommandline said in Issue with Worker Class when used with QTables:

          unclear what is going on at this point

          To make things clear, you could post what you did exactly and let others have a look :)
          I smell bad design or unallowed/blocking UI access.


          If debugging is the process of removing software bugs, then programming must be the process of putting them in.

          ~E. W. Dijkstra

          I 1 Reply Last reply 24 Jul 2024, 15:34
          0
          • P Pl45m4
            24 Jul 2024, 14:54

            @imissthecommandline said in Issue with Worker Class when used with QTables:

            unclear what is going on at this point

            To make things clear, you could post what you did exactly and let others have a look :)
            I smell bad design or unallowed/blocking UI access.

            I Offline
            I Offline
            imissthecommandline
            wrote on 24 Jul 2024, 15:34 last edited by imissthecommandline
            #6

            @Pl45m4

            Here's all of the code, let me know what i messed up.

            Sorry if it's messy, this is my first time with pyqt

            # -*- coding: utf-8 -*-
            """
            Created on Wed Jul 24 11:28:27 2024
            
            @author: pierre
            """
            
            #i tried to remove all the excess stuff unrelated to the core gui
            
            #################
            #Library Imports#
            #################
            
            #libraries for array management and graphing
            import pandas as pd
            import numpy as np
            import matplotlib as plt
            
            #libraries for system access and gui foundation
            import sys 
            from PyQt6.QtWidgets import (
                QApplication,
                QLabel,
                QMainWindow,
                QStatusBar,
                QToolBar,
                QStackedWidget,
                QStackedLayout,
                QWidget,
                QTabWidget,
                QVBoxLayout,
                QGridLayout,
                QPushButton,
                QLineEdit,
                QTableView
            )
            
            from PyQt6 import QtCore, QtGui, QtWidgets
            from PyQt6.QtGui import *
            from PyQt6.QtWidgets import *
            from PyQt6.QtCore import *
            
            
            #placeholder for stuff i haven't implemented in full yet
            placeholder = "<unimplemented val!>"
            
            #Library Imports for core management program
            import socket
            import threading
            import time
            import pickle
            
            
            ###############
            #Global Values#
            ###############
            
            #so i can edit the stuff in thw window class elsewhere
            global window
            
            #server necessities
            server_ip = "0.0.0.0"
            core_connection = 0
            server_port = 9999
            server_connections = 4
            is_open = True
            
            #list of collected devices
            devices = []
            #list of device states
            device_states = []
            #controller of current mode for all devices
            current_mode = "waiting"
            #generic method of encoding
            code = "utf-8"
            #list of unauthorized devices in network setup
            unauthorized_devices = []
                
            #stuff for demo
            demo = []
            avg = 0
            
            #stuff for communication demo/tensorflow demo
            iterations = 5
            epochs = 2
            model_type = "Convolutional Neural Network"
            averaging_method = "All Layers"
            
            #stuff for tensorflow
            global weights
            tensorflow_demo = []
            
            #settings storage
            settings = [f"{server_port}",f"{server_connections}",f"{iterations}",f"{epochs}",f"{model_type}",f"{averaging_method}"]
            
            #################
            #program classes#
            #################
            
            #code taken from pyqt tutorial (link below)
            #https://www.pythonguis.com/tutorials/pyqt6-qtableview-modelviews-numpy-pandas/
            class table_model(QtCore.QAbstractTableModel):
                def __init__(self, data):
                    super(table_model, self).__init__()
                    self._data = data
            
                def data(self, index, role):
                    if role == Qt.ItemDataRole.DisplayRole:
                        value = self._data.iloc[index.row(), index.column()]
                        return str(value)
            
                def rowCount(self, index):
                    return self._data.shape[0]
            
                def columnCount(self, index):
                    return self._data.shape[1]
            
                def headerData(self, section, orientation, role):
                    # section is the index of the column/row.
                    if role == Qt.ItemDataRole.DisplayRole:
                        if orientation == Qt.Orientation.Horizontal:
                            return str(self._data.columns[section])
            
                        if orientation == Qt.Orientation.Vertical:
                            return str(self._data.index[section])
                
                def flags(self, index):
                    return Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsEditable
                    
                #append the dataframe
                def appendSelf(self,new_val):
                    self._data = pd.concat([self._data,new_val])
                    self.layoutChanged.emit()
                    return 0
                    
                #edit a specific value
                def editSelf(self,new_val,index,column):
                    self._data.at[index,column] = new_val
                    self.layoutChanged.emit()
                    return 0
                
                #remove a line
                def removeSelf(self,index):
                    self._data.set_index(index)
                    self._data.reset_index(drop=True)
                    self.layoutChanged.emit()
                    return 0
                    
            
            #Main GUI window coding
            class Window(QMainWindow):
                def __init__(self):
                    super().__init__(parent=None)
                    #values for window resolution
                    self.x_res = 640
                    self.y_res = 480
                    self.setWindowTitle("S.T.A.R.F.I.S.H")
                    #various set-up functions for the gui
                    self.menu_init()
                    self.statusbar_init() 
                    self.stack_init()
                    self.setGeometry(0, 30, self.x_res, self.y_res)
                    #setup for thread manager
                    self.threadpool = QThreadPool()
            
                def menu_init(self):
                    #setup generic options menu for window 
                    menu = self.menuBar().addMenu("&Menu")
                    menu.addAction("&Exit", self.close)
                    
                    #initialization for the resolutions menu
                    #lambda is used to circumvent constraints of addAction
                    #functions with arguments are otherwise deemed heretics and burned
                    res_menu = menu.addMenu("&Resolutions")
                    res_1 = res_menu.addAction("640 by 480")
                    res_1.triggered.connect(lambda: self.set_display("Nan", 640, 480))
                    res_2 = res_menu.addAction("1024 by 768")
                    res_2.triggered.connect(lambda: self.set_display("Nan", 1024, 768))
                    res_3 = res_menu.addAction("1280 by 720")
                    res_3.triggered.connect(lambda: self.set_display("Nan",1280, 720))
                    res_3 = res_menu.addAction("1366 by 768")
                    res_3.triggered.connect(lambda: self.set_display("Nan",1366, 768))
                    res_4 = res_menu.addAction("1920 by 1080")
                    res_4.triggered.connect(lambda: self.set_display("Nan",1920, 1080))
                    res_5 = res_menu.addAction("Fullscreen")
                    res_5.triggered.connect(lambda: self.set_display("fullscreen"))
            
                def statusbar_init(self):
                    status = QStatusBar()
                    status.showMessage("Currently Running: Nothing currently running...")
                    self.setStatusBar(status)
                    
                def stack_init(self):
                    #all possible screens are organized through tabs at the top
                    #all sub-tabs are part of a stack
                    
                    #basic setup
                    self.layout = QVBoxLayout(self)
                    self.main_tabs = QTabWidget(self)
                    self.main_tabs.resize(self.x_res - 70, self.y_res - 70) 
                    self.main_tabs.move(10, 40) 
                    
                    #custom tab init
                    self.home_tab = QWidget(self)
                    self.main_tabs.addTab(self.home_tab,"Home")
                    self.config_tab = QWidget(self)
                    self.main_tabs.addTab(self.config_tab,"Configuration")
                    self.conn_tab = QWidget(self)
                    self.main_tabs.addTab(self.conn_tab,"Connections")
                    self.conn_man_tab = QWidget(self)
                    self.main_tabs.addTab(self.conn_man_tab,"Connection Management")
                    self.run_tab = QWidget(self)
                    self.main_tabs.addTab(self.run_tab,"Run")
                    self.layout.addWidget(self.main_tabs) 
                    self.setLayout(self.layout) 
                    
                    #home tab setup
                    #label 1 formatting
                    self.home_tab.layout = QVBoxLayout(self)
                    self.top_label = QLabel()
                    self.top_label.setText("Welcome to S.T.A.R.F.I.S.H") 
                    self.home_tab.layout.addWidget(self.top_label)
                    #label 2 formatting
                    self.starfish_image = QLabel()
                    self.starfish_image.setPixmap(QPixmap('starfishe.png'))
                    self.home_tab.layout.addWidget(self.starfish_image)
                    #label 3 formatting
                    self.blurb = QLabel("To upload a config file, or to manually adjust settings, go to Configuration \n\nTo connect new devices to the server, or to check on the state of existing connections, go to Connections\n\nTo run a model of your choice, and to see the status of the current overall model, go to Run", self)
                    self.blurb.setWordWrap(True)
                    self.blurb.setStyleSheet("border: 2px solid blue;") 
                    self.home_tab.layout.addWidget(self.blurb)
                    self.home_tab.setLayout(self.home_tab.layout)
                    
                    #options tab setup
                    #each adjustable setting has a qlabel in column 0 saying what it is
                    #this what the value is and the current value
                    #a input box in column 4 lets you change the value
                    
                    self.config_tab.layout = QGridLayout(self)
                    self.upload_button = QPushButton("&Upload custom config", self)
                    self.config_tab.layout.addWidget(self.upload_button,0,0,1,5)
                    #columns 1,3,5 are thin for l'aesthétique :)
                    for i in range(3):
                        self.config_tab.layout.setColumnMinimumWidth(1 + (i*2), 10)
                        
                    #network settings start
                    #need to implement, not bothering yet because it's not really necessary
                    self.config_ops_1 = QLabel("Network settings", self)
                    self.config_ops_1.setStyleSheet("border: 2px solid blue;") 
                    self.config_tab.layout.addWidget(self.config_ops_1,1,0)
                    self.config_ops_note = QLabel("To change the value, use the adjacent input box!", self)
                    self.config_tab.layout.addWidget(self.config_ops_note,1,2)
                    self.network_op_1_1 = QLabel(f"Port: {settings[0]}", self)
                    self.config_tab.layout.addWidget(self.network_op_1_1,2,0)
                    self.network_op_1_2 = QLineEdit(self)
                    self.config_tab.layout.addWidget(self.network_op_1_2,2,2)
                    self.network_op_2_1 = QLabel(f"Number of Devices: {settings[1]}", self)
                    self.config_tab.layout.addWidget(self.network_op_2_1,3,0)
                    self.network_op_2_2 = QLineEdit(self)
                    self.config_tab.layout.addWidget(self.network_op_2_2,3,2)
                    #tensorflow demo settings start
                    self.config_ops_2 = QLabel("Tensorflow Demo Settings", self)
                    self.config_ops_2.setStyleSheet("border: 2px solid green;") 
                    self.config_tab.layout.addWidget(self.config_ops_2,4,0)
                    self.tensor_demo_op_1_1 = QLabel(f"Number of Iterations: {settings[2]}", self)
                    self.config_tab.layout.addWidget(self.tensor_demo_op_1_1,5,0)
                    self.tensor_demo_op_1_2 = QLineEdit(self)
                    self.config_tab.layout.addWidget(self.tensor_demo_op_1_2,5,2,1,1)
                    self.tensor_demo_op_2_1 = QLabel(f"Number of Epochs: {settings[3]}", self)
                    self.config_tab.layout.addWidget(self.tensor_demo_op_2_1,6,0)
                    self.tensor_demo_op_2_2 = QLineEdit(self)
                    self.config_tab.layout.addWidget(self.tensor_demo_op_2_2,6,2,1,1)
                    self.tensor_demo_op_3_1 = QLabel(f"Model Type: {settings[4]}", self)
                    self.config_tab.layout.addWidget(self.tensor_demo_op_3_1,7,0)
                    self.tensor_demo_op_3_2 = QLineEdit(self)
                    self.config_tab.layout.addWidget(self.tensor_demo_op_3_2,7,2,1,1)
                    self.tensor_demo_op_4_1 = QLabel(f"Specific Layer Avg: {settings[5]}", self)
                    self.config_tab.layout.addWidget(self.tensor_demo_op_4_1,8,0)
                    self.tensor_demo_op_4_2 = QLineEdit(self)
                    self.config_tab.layout.addWidget(self.tensor_demo_op_4_2,8,2,1,1)
                    self.config_tab.setLayout(self.config_tab.layout)
                    
                    #connections tab setup
                    #if nothing is connected, top button starts listener
                    #if listener is running, bottom bar says as such
                    #every time a device is connected, it is added to the list of devices
                    #once listener is done, button at the top disconnects devices instead
                    
                    self.conn_tab.layout = QGridLayout(self)
                    self.start_connecting = QPushButton("&Open Server for Connections", self)
                    self.start_connecting.clicked.connect(lambda: self.threadstarter(spinup))
                    self.conn_tab.layout.addWidget(self.start_connecting,0,0,1,2)
                    self.listener_running = QLabel("Listener is runnning...", self)
                    self.conn_tab.layout.addWidget(self.listener_running,0,0,1,2)
                    self.listener_running.hide()
                    self.start_connecting = QPushButton("&Close Server", self)
                    self.start_connecting.clicked.connect(lambda: self.close_server)
                    self.conn_tab.layout.addWidget(self.start_connecting,0,2,1,2)
                    #columns 1,3,5 are thin for l'aesthétique :)
                    for i in range(3):
                        self.config_tab.layout.setColumnMinimumWidth(1 + (i*2), 10)
                    self.device_list_start = QLabel("Connected Devices",self)
                    self.device_list_start.setStyleSheet("border: 2px solid blue;")
                    self.conn_tab.layout.addWidget(self.device_list_start,1,0,1,1)
                    #initially array for connected devices
                    self.conn_devices_table = QTableView()
                    self.connected_devices = pd.DataFrame([
                        [f"{server_ip}", f"{server_port}", "Connections Tab",f"{current_mode}"],
                        ],columns = ["Ipv4 Address", "Port", "Device State","Current Program"],
                        index = ["Server"]
                        )
                    self.device_list = table_model(self.connected_devices)
                    self.conn_devices_table.setModel(self.device_list)
                    self.conn_tab.layout.addWidget(self.conn_devices_table,2,0,5,5)
                    self.conn_tab.setLayout(self.conn_tab.layout)
                    
                    #connection management tab setup
                    
                    #button for authorizing all connections, button for clearing network
                    self.conn_man_tab.layout = QGridLayout(self)
                    self.authorize_connections = QPushButton("&Authorize all Connections", self)
                    self.authorize_connections.clicked.connect(lambda: self.connection_authorizer())
                    self.conn_man_tab.layout.addWidget(self.authorize_connections,0,0,1,2)
                    self.clear_network_button = QPushButton("&Clear Network", self)
                    self.clear_network_button.clicked.connect(lambda: self.clear_server())
                    self.conn_man_tab.layout.addWidget(self.clear_network_button,0,2,1,2)
                    self.conn_man_tab.setLayout(self.conn_man_tab.layout)
                    
                    #run tab setup
                    self.run_tab.layout = QGridLayout(self)
                    self.run_device_label = QLabel("Implemented Models",self)
                    self.run_device_label.setStyleSheet("border: 2px solid blue;")
                    self.run_tab.layout.addWidget(self.run_device_label,0,0,1,1)
                    self.tensorflow_demo_button = QPushButton("&Run Basic Tensorflow Demo", self)
                    self.tensorflow_demo_button.clicked.connect(lambda: self.threadstarter(tensorflow_basic_demo))
                    self.run_tab.layout.addWidget(self.tensorflow_demo_button,1,0,1,1)
                    self.run_device_label = QLabel("Connected Devices",self)
                    self.run_device_label.setStyleSheet("border: 2px solid green;")
                    self.run_tab.layout.addWidget(self.run_device_label,3,0,1,1)
                    self.run_device_table = QTableView()
                    self.connected_devices_run = pd.DataFrame([
                        ["Running",f"{current_mode}","N/A"],
                        ],columns = ["Device State","Current Program","Last Ping"],
                        index = ["Server"]
                    )
                    self.device_list_run = table_model(self.connected_devices_run)
                    self.run_device_table.setModel(self.device_list_run)
                    self.run_tab.layout.addWidget(self.run_device_table,4,0,5,5)
                    self.run_tab.setLayout(self.run_tab.layout)
                    
                def set_display(self, fit="Nan", x_res = 640, y_res = 480):
                    if (fit == "fullscreen"):
                        self.showMaximized() 
                        self.main_tabs.resize(x_res - 80, y_res - 80) 
                    else:
                        self.setGeometry(0, 30, x_res, y_res)
                        self.main_tabs.resize(x_res - 80, y_res - 80) 
                
                def threadstarter(self, function, *args):
                    new_thread = Worker(function, *args)
                    new_thread.signals.operations.connect(self.threadhandler)
                    self.threadpool.start(new_thread)
                    
                #note for any additions, is set to expect a tuple
                #sending an int closes the thread, unless the int is in a tuple (duh)
                def threadhandler(self, command_list):
                    task = command_list[0]
                    if (task[0] == "val_edit_conn"):
                        self.device_list.editSelf(task[1],task[2],task[3])
                    elif(task[0] == "val_edit_run"):
                        self.device_list_run.editSelf(task[1],task[2],task[3])
                    elif(task[0] == "append_conn"):
                        pd_array = command_list[1]
                        self.device_list.appendSelf(pd_array)
                    elif(task[0] == "append_run"):
                        pd_array = command_list[1]
                        self.device_list_run.appendSelf(pd_array)
                    elif(task[0] == "server_status"):
                        self.server_mode_update()
                    elif(task[0] == "set_status"):
                        status = QStatusBar()
                        status.showMessage(f"Currently Running: {task[1]}")
                        self.setStatusBar(status)
                    elif (task[0] == "tray_start"):
                        self.threadstarter(arbitrary_function,command_list[1],command_list[2])
                        
                def update_status(self,new_status):
                    status = QStatusBar()
                    status.showMessage(new_status)
                    self.setStatusBar(status)
                    pass
                
                def server_mode_update(self):
                    self.device_list_run.editSelf(f"{current_mode}","Server","Current Program")
                    current_time = time.localtime()
                    current_time = time.strftime("%H:%M:%S", current_time)
                    self.device_list_run.editSelf(current_time,"Server","Last Ping")
                    self.device_list.editSelf(f"{current_mode}","Server","Current Program")
                    
                #used as a workaround for performance issues with threads started by threads
                def connection_authorizer(self):
                    global unauthorized_devices
                    if (unauthorized_devices == []):
                        return 0
                    for unauthorized_device in unauthorized_devices:
                        current_device = unauthorized_device
                        self.threadstarter(arbitrary_function,current_device[0],current_device[1])
                    unauthorized_devices = []
                    return 0
                
                def close_server(self):
                    global is_open
                    is_open = False
                
                def clear_server(self):
                    global current_mode
                    current_mode = "newtwork_wipe"
                    
                def update_settings(self):
                    #why can't things just be command line
                    #: (
                    pass
                        
                        
            class connection:
                def __init__(self, ip, conn, name, port=9999):
                  self.ip = ip
                  self.conn = conn
                  self.name = name
                  self.port = port
                  
                #function for making sending strings slightly easier
                def send(self,message):
                    #added to prevent improperly formatted messages from throwing errors
                    message = str(message)
                    message_size = str(len(message))
                    self.conn.send(message_size.encode(code))
                    #delay to prevent signal interference
                    time.sleep(0.5)
                    self.conn.send(message.encode(code))
                    
                #function for recieving special datatypes
                #may add full file sending here, who knows
                def send_advanced(self,message,datatype="array"):
                    if datatype == "array":
                        #added to prevent improperly formatted messages from throwing errors
                        message = pickle.dumps(message)
                        message_size = str(len(message))
                        #print(message_size)
                        self.conn.send(message_size.encode(code))
                        #delay to prevent signal interference
                        time.sleep(0.5)
                        self.conn.send(message)
                        
                #function for making recieving strings slightly easier
                def recieve(self):
                    message_size = int(self.conn.recv(64).decode(code))
                    #delay to prevent signal interference
                    time.sleep(0.5)
                    message = self.conn.recv(message_size).decode(code)
                    return message
                
                #function for recieving special datatypes
                #may accept files in the future
                def recieve_advanced(self):
                    message_size = int(self.conn.recv(64).decode(code))
                    #delay to prevent signal interference
                    time.sleep(0.5)
                    message = self.conn.recv(message_size)
                    message = pickle.loads(message)
                    return message
                
                   
            #allows for utilization of threads in gui backend
            class Worker(QRunnable):
                def __init__(self, fn, *args):
                    super(Worker, self).__init__()
                    # Store constructor arguments (re-used for processing)
                    self.fn = fn
                    self.args = args
                    self.signals = WorkerSignals()
            
                @pyqtSlot()
                def run(self):
                    '''
                    Initialise the runner function with passed args, kwargs.
                    '''
                    self.fn(self, *self.args)
                    
            #implementation of Qobject for handling signals sent from worker threads
            class WorkerSignals(QObject):
                operations = pyqtSignal(tuple)
                
                
                
            ###################
            #program functions#
            ###################
            
            #print statements still exist for debuggings sake
            
            #it's a function like this because it's a thread
            def spinup(thread_manager):
                thread_manager.signals.operations.emit((["set_status","Connecting Devices..."],))
                client = setup(thread_manager)
                listener(client,thread_manager)
                thread_manager.signals.operations.emit((["set_status","Nothing Currently Running..."],))
                return 0
            
            #note to self: this needs to change to settings at some point
            def setup(thread_manager,port=server_port,connections=server_connections):
                global server_ip
                global core_connetion
                global current_mode
                current_mode = "waiting"
                client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
                ip = socket.gethostbyname(socket.gethostname())
                server_ip = ip
                thread_manager.signals.operations.emit((["val_edit_conn", f"{server_ip}", "Server", "Ipv4 Address"],))
                core_connection = client
                client.bind((ip,port))
                client.listen(connections)
                #print("Server set up is complete")
                #print(f"Server ip is {ip}")
                #print(f"Server port is {port}")
                #print(f"Server allows up to {connections} devices")
                return client
            
            def listener(client,thread_manager):
                global window
                global unauthorized_devices
                global is_open
                client.listen()
                dev_count = 0
                pi4_count = 0
                arduino_uno_count = 0
                is_open = True
                while (is_open):
                    if (dev_count<4):
                        print(f"\nListener is available for new connections {dev_count} connected\n")
                        dev_count = dev_count + 1
                    else:
                        break
                    conn, addr = client.accept()
                    name = f"pi-{dev_count}"
                    new_connection = connection(addr, conn, name)
                    new_connection.send("type")
                    dev_type = new_connection.recieve()
                    if (dev_type == "pi4"):
                        pi4_count = pi4_count + 1
                        new_connection.name = f"pi4-{pi4_count}"
                    elif (dev_type == "arduino uno"):
                        arduino_uno_count = arduino_uno_count + 1
                        new_connection.name = f"arduino_uno-{arduino_uno_count}"
                    name = new_connection.name
                    #thread_manager.signals.operations.emit((["tray_start"],new_connection,dev_count))
                    unauthorized_devices.append((new_connection,dev_count))
                    devices.append(new_connection)
                    device_states.append("awaiting authorization...")
                    demo.append(0)
                    tensorflow_demo.append(0)
                    current_time = time.localtime()
                    current_time = time.strftime("%H:%M:%S", current_time)
                    new_device = pd.DataFrame([
                        [f"{addr[0]}", f"{server_port}", f"{device_states[dev_count - 1]}",f"{current_mode}",f"{current_time}"],
                        ],columns = ['Ipv4 Address', "Port", "Device State","Current Program","Last Ping"],
                        index = [f'{name}']
                    )
                    thread_manager.signals.operations.emit((["append_conn"],new_device))
                    new_device = pd.DataFrame([
                        [f"{device_states[dev_count - 1]}",f"{current_mode}",f"{current_time}"],
                        ],columns = ["Device State","Current Program","Last Ping"],
                        index = [f'{name}']
                    )
                    thread_manager.signals.operations.emit((["append_run"],new_device))
                print("Max device count reached, server closing...")
                thread_manager.signals.operations.emit((["server_status"],))
                return 0
                    #print(f"pi-{pi_count} is waiting for instructions...")
            
            #function for ensuring device consensus accross the network
            def consensus(target_list,goal):
                #waits until at least one device reaches the state we want
                while(target_list[0] != goal):
                    pass
                #waits until all devices read the same state
                while(target_list.count(target_list[0]) != len(target_list)):
                    pass
                
            #resets global variables between mode executions
            def reset():
                global current_mode
                global avg
                global demo
                global iterations
                global tensorflow_demo
                for i in range(len(demo)):
                    demo[i] = 0
                for i in range(len(tensorflow_demo)):
                    tensorflow_demo[i] = 0
                avg = 0
                iterations = 0
                current_mode = "waiting"
                
            #arbitraty function for testing
            def arbitrary_function(thread_manager, *stuff):
                while(True):
                    pass
            
            
            #handles all changing of state *insert pun here*
            def state_changer(thread_manager,pi_count,name,new_state):
                device_states[pi_count-1] = new_state
                test = ["val_edit_run",f"{new_state}",f"{name}"]
                thread_manager.signals.operations.emit((["val_edit_run",f"{new_state}",f"{name}", "Device State"],))
                thread_manager.signals.operations.emit((["val_edit_conn",f"{current_mode}",f"{name}", "Current Program"],))
                thread_manager.signals.operations.emit((["val_edit_run",f"{current_mode}",f"{name}", "Current Program"],))
                current_time = time.localtime()
                current_time = time.strftime("%H:%M:%S", current_time)
                thread_manager.signals.operations.emit((["val_edit_conn",f"{current_time}",f"{name}", "Last Ping"],))
                
            def tensorflow_basic_demo(thread_manager):
                #not really related to the problem
                pass
            ###########
            #Main Loop#
            ###########
            
            def maine():
                global window
                app = QApplication([])
                window = Window()
                window.show()
                
                sys.exit(app.exec())
                
            #call main to start program
            maine()
            
            P 1 Reply Last reply 24 Jul 2024, 15:50
            0
            • I imissthecommandline
              24 Jul 2024, 15:34

              @Pl45m4

              Here's all of the code, let me know what i messed up.

              Sorry if it's messy, this is my first time with pyqt

              # -*- coding: utf-8 -*-
              """
              Created on Wed Jul 24 11:28:27 2024
              
              @author: pierre
              """
              
              #i tried to remove all the excess stuff unrelated to the core gui
              
              #################
              #Library Imports#
              #################
              
              #libraries for array management and graphing
              import pandas as pd
              import numpy as np
              import matplotlib as plt
              
              #libraries for system access and gui foundation
              import sys 
              from PyQt6.QtWidgets import (
                  QApplication,
                  QLabel,
                  QMainWindow,
                  QStatusBar,
                  QToolBar,
                  QStackedWidget,
                  QStackedLayout,
                  QWidget,
                  QTabWidget,
                  QVBoxLayout,
                  QGridLayout,
                  QPushButton,
                  QLineEdit,
                  QTableView
              )
              
              from PyQt6 import QtCore, QtGui, QtWidgets
              from PyQt6.QtGui import *
              from PyQt6.QtWidgets import *
              from PyQt6.QtCore import *
              
              
              #placeholder for stuff i haven't implemented in full yet
              placeholder = "<unimplemented val!>"
              
              #Library Imports for core management program
              import socket
              import threading
              import time
              import pickle
              
              
              ###############
              #Global Values#
              ###############
              
              #so i can edit the stuff in thw window class elsewhere
              global window
              
              #server necessities
              server_ip = "0.0.0.0"
              core_connection = 0
              server_port = 9999
              server_connections = 4
              is_open = True
              
              #list of collected devices
              devices = []
              #list of device states
              device_states = []
              #controller of current mode for all devices
              current_mode = "waiting"
              #generic method of encoding
              code = "utf-8"
              #list of unauthorized devices in network setup
              unauthorized_devices = []
                  
              #stuff for demo
              demo = []
              avg = 0
              
              #stuff for communication demo/tensorflow demo
              iterations = 5
              epochs = 2
              model_type = "Convolutional Neural Network"
              averaging_method = "All Layers"
              
              #stuff for tensorflow
              global weights
              tensorflow_demo = []
              
              #settings storage
              settings = [f"{server_port}",f"{server_connections}",f"{iterations}",f"{epochs}",f"{model_type}",f"{averaging_method}"]
              
              #################
              #program classes#
              #################
              
              #code taken from pyqt tutorial (link below)
              #https://www.pythonguis.com/tutorials/pyqt6-qtableview-modelviews-numpy-pandas/
              class table_model(QtCore.QAbstractTableModel):
                  def __init__(self, data):
                      super(table_model, self).__init__()
                      self._data = data
              
                  def data(self, index, role):
                      if role == Qt.ItemDataRole.DisplayRole:
                          value = self._data.iloc[index.row(), index.column()]
                          return str(value)
              
                  def rowCount(self, index):
                      return self._data.shape[0]
              
                  def columnCount(self, index):
                      return self._data.shape[1]
              
                  def headerData(self, section, orientation, role):
                      # section is the index of the column/row.
                      if role == Qt.ItemDataRole.DisplayRole:
                          if orientation == Qt.Orientation.Horizontal:
                              return str(self._data.columns[section])
              
                          if orientation == Qt.Orientation.Vertical:
                              return str(self._data.index[section])
                  
                  def flags(self, index):
                      return Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsEditable
                      
                  #append the dataframe
                  def appendSelf(self,new_val):
                      self._data = pd.concat([self._data,new_val])
                      self.layoutChanged.emit()
                      return 0
                      
                  #edit a specific value
                  def editSelf(self,new_val,index,column):
                      self._data.at[index,column] = new_val
                      self.layoutChanged.emit()
                      return 0
                  
                  #remove a line
                  def removeSelf(self,index):
                      self._data.set_index(index)
                      self._data.reset_index(drop=True)
                      self.layoutChanged.emit()
                      return 0
                      
              
              #Main GUI window coding
              class Window(QMainWindow):
                  def __init__(self):
                      super().__init__(parent=None)
                      #values for window resolution
                      self.x_res = 640
                      self.y_res = 480
                      self.setWindowTitle("S.T.A.R.F.I.S.H")
                      #various set-up functions for the gui
                      self.menu_init()
                      self.statusbar_init() 
                      self.stack_init()
                      self.setGeometry(0, 30, self.x_res, self.y_res)
                      #setup for thread manager
                      self.threadpool = QThreadPool()
              
                  def menu_init(self):
                      #setup generic options menu for window 
                      menu = self.menuBar().addMenu("&Menu")
                      menu.addAction("&Exit", self.close)
                      
                      #initialization for the resolutions menu
                      #lambda is used to circumvent constraints of addAction
                      #functions with arguments are otherwise deemed heretics and burned
                      res_menu = menu.addMenu("&Resolutions")
                      res_1 = res_menu.addAction("640 by 480")
                      res_1.triggered.connect(lambda: self.set_display("Nan", 640, 480))
                      res_2 = res_menu.addAction("1024 by 768")
                      res_2.triggered.connect(lambda: self.set_display("Nan", 1024, 768))
                      res_3 = res_menu.addAction("1280 by 720")
                      res_3.triggered.connect(lambda: self.set_display("Nan",1280, 720))
                      res_3 = res_menu.addAction("1366 by 768")
                      res_3.triggered.connect(lambda: self.set_display("Nan",1366, 768))
                      res_4 = res_menu.addAction("1920 by 1080")
                      res_4.triggered.connect(lambda: self.set_display("Nan",1920, 1080))
                      res_5 = res_menu.addAction("Fullscreen")
                      res_5.triggered.connect(lambda: self.set_display("fullscreen"))
              
                  def statusbar_init(self):
                      status = QStatusBar()
                      status.showMessage("Currently Running: Nothing currently running...")
                      self.setStatusBar(status)
                      
                  def stack_init(self):
                      #all possible screens are organized through tabs at the top
                      #all sub-tabs are part of a stack
                      
                      #basic setup
                      self.layout = QVBoxLayout(self)
                      self.main_tabs = QTabWidget(self)
                      self.main_tabs.resize(self.x_res - 70, self.y_res - 70) 
                      self.main_tabs.move(10, 40) 
                      
                      #custom tab init
                      self.home_tab = QWidget(self)
                      self.main_tabs.addTab(self.home_tab,"Home")
                      self.config_tab = QWidget(self)
                      self.main_tabs.addTab(self.config_tab,"Configuration")
                      self.conn_tab = QWidget(self)
                      self.main_tabs.addTab(self.conn_tab,"Connections")
                      self.conn_man_tab = QWidget(self)
                      self.main_tabs.addTab(self.conn_man_tab,"Connection Management")
                      self.run_tab = QWidget(self)
                      self.main_tabs.addTab(self.run_tab,"Run")
                      self.layout.addWidget(self.main_tabs) 
                      self.setLayout(self.layout) 
                      
                      #home tab setup
                      #label 1 formatting
                      self.home_tab.layout = QVBoxLayout(self)
                      self.top_label = QLabel()
                      self.top_label.setText("Welcome to S.T.A.R.F.I.S.H") 
                      self.home_tab.layout.addWidget(self.top_label)
                      #label 2 formatting
                      self.starfish_image = QLabel()
                      self.starfish_image.setPixmap(QPixmap('starfishe.png'))
                      self.home_tab.layout.addWidget(self.starfish_image)
                      #label 3 formatting
                      self.blurb = QLabel("To upload a config file, or to manually adjust settings, go to Configuration \n\nTo connect new devices to the server, or to check on the state of existing connections, go to Connections\n\nTo run a model of your choice, and to see the status of the current overall model, go to Run", self)
                      self.blurb.setWordWrap(True)
                      self.blurb.setStyleSheet("border: 2px solid blue;") 
                      self.home_tab.layout.addWidget(self.blurb)
                      self.home_tab.setLayout(self.home_tab.layout)
                      
                      #options tab setup
                      #each adjustable setting has a qlabel in column 0 saying what it is
                      #this what the value is and the current value
                      #a input box in column 4 lets you change the value
                      
                      self.config_tab.layout = QGridLayout(self)
                      self.upload_button = QPushButton("&Upload custom config", self)
                      self.config_tab.layout.addWidget(self.upload_button,0,0,1,5)
                      #columns 1,3,5 are thin for l'aesthétique :)
                      for i in range(3):
                          self.config_tab.layout.setColumnMinimumWidth(1 + (i*2), 10)
                          
                      #network settings start
                      #need to implement, not bothering yet because it's not really necessary
                      self.config_ops_1 = QLabel("Network settings", self)
                      self.config_ops_1.setStyleSheet("border: 2px solid blue;") 
                      self.config_tab.layout.addWidget(self.config_ops_1,1,0)
                      self.config_ops_note = QLabel("To change the value, use the adjacent input box!", self)
                      self.config_tab.layout.addWidget(self.config_ops_note,1,2)
                      self.network_op_1_1 = QLabel(f"Port: {settings[0]}", self)
                      self.config_tab.layout.addWidget(self.network_op_1_1,2,0)
                      self.network_op_1_2 = QLineEdit(self)
                      self.config_tab.layout.addWidget(self.network_op_1_2,2,2)
                      self.network_op_2_1 = QLabel(f"Number of Devices: {settings[1]}", self)
                      self.config_tab.layout.addWidget(self.network_op_2_1,3,0)
                      self.network_op_2_2 = QLineEdit(self)
                      self.config_tab.layout.addWidget(self.network_op_2_2,3,2)
                      #tensorflow demo settings start
                      self.config_ops_2 = QLabel("Tensorflow Demo Settings", self)
                      self.config_ops_2.setStyleSheet("border: 2px solid green;") 
                      self.config_tab.layout.addWidget(self.config_ops_2,4,0)
                      self.tensor_demo_op_1_1 = QLabel(f"Number of Iterations: {settings[2]}", self)
                      self.config_tab.layout.addWidget(self.tensor_demo_op_1_1,5,0)
                      self.tensor_demo_op_1_2 = QLineEdit(self)
                      self.config_tab.layout.addWidget(self.tensor_demo_op_1_2,5,2,1,1)
                      self.tensor_demo_op_2_1 = QLabel(f"Number of Epochs: {settings[3]}", self)
                      self.config_tab.layout.addWidget(self.tensor_demo_op_2_1,6,0)
                      self.tensor_demo_op_2_2 = QLineEdit(self)
                      self.config_tab.layout.addWidget(self.tensor_demo_op_2_2,6,2,1,1)
                      self.tensor_demo_op_3_1 = QLabel(f"Model Type: {settings[4]}", self)
                      self.config_tab.layout.addWidget(self.tensor_demo_op_3_1,7,0)
                      self.tensor_demo_op_3_2 = QLineEdit(self)
                      self.config_tab.layout.addWidget(self.tensor_demo_op_3_2,7,2,1,1)
                      self.tensor_demo_op_4_1 = QLabel(f"Specific Layer Avg: {settings[5]}", self)
                      self.config_tab.layout.addWidget(self.tensor_demo_op_4_1,8,0)
                      self.tensor_demo_op_4_2 = QLineEdit(self)
                      self.config_tab.layout.addWidget(self.tensor_demo_op_4_2,8,2,1,1)
                      self.config_tab.setLayout(self.config_tab.layout)
                      
                      #connections tab setup
                      #if nothing is connected, top button starts listener
                      #if listener is running, bottom bar says as such
                      #every time a device is connected, it is added to the list of devices
                      #once listener is done, button at the top disconnects devices instead
                      
                      self.conn_tab.layout = QGridLayout(self)
                      self.start_connecting = QPushButton("&Open Server for Connections", self)
                      self.start_connecting.clicked.connect(lambda: self.threadstarter(spinup))
                      self.conn_tab.layout.addWidget(self.start_connecting,0,0,1,2)
                      self.listener_running = QLabel("Listener is runnning...", self)
                      self.conn_tab.layout.addWidget(self.listener_running,0,0,1,2)
                      self.listener_running.hide()
                      self.start_connecting = QPushButton("&Close Server", self)
                      self.start_connecting.clicked.connect(lambda: self.close_server)
                      self.conn_tab.layout.addWidget(self.start_connecting,0,2,1,2)
                      #columns 1,3,5 are thin for l'aesthétique :)
                      for i in range(3):
                          self.config_tab.layout.setColumnMinimumWidth(1 + (i*2), 10)
                      self.device_list_start = QLabel("Connected Devices",self)
                      self.device_list_start.setStyleSheet("border: 2px solid blue;")
                      self.conn_tab.layout.addWidget(self.device_list_start,1,0,1,1)
                      #initially array for connected devices
                      self.conn_devices_table = QTableView()
                      self.connected_devices = pd.DataFrame([
                          [f"{server_ip}", f"{server_port}", "Connections Tab",f"{current_mode}"],
                          ],columns = ["Ipv4 Address", "Port", "Device State","Current Program"],
                          index = ["Server"]
                          )
                      self.device_list = table_model(self.connected_devices)
                      self.conn_devices_table.setModel(self.device_list)
                      self.conn_tab.layout.addWidget(self.conn_devices_table,2,0,5,5)
                      self.conn_tab.setLayout(self.conn_tab.layout)
                      
                      #connection management tab setup
                      
                      #button for authorizing all connections, button for clearing network
                      self.conn_man_tab.layout = QGridLayout(self)
                      self.authorize_connections = QPushButton("&Authorize all Connections", self)
                      self.authorize_connections.clicked.connect(lambda: self.connection_authorizer())
                      self.conn_man_tab.layout.addWidget(self.authorize_connections,0,0,1,2)
                      self.clear_network_button = QPushButton("&Clear Network", self)
                      self.clear_network_button.clicked.connect(lambda: self.clear_server())
                      self.conn_man_tab.layout.addWidget(self.clear_network_button,0,2,1,2)
                      self.conn_man_tab.setLayout(self.conn_man_tab.layout)
                      
                      #run tab setup
                      self.run_tab.layout = QGridLayout(self)
                      self.run_device_label = QLabel("Implemented Models",self)
                      self.run_device_label.setStyleSheet("border: 2px solid blue;")
                      self.run_tab.layout.addWidget(self.run_device_label,0,0,1,1)
                      self.tensorflow_demo_button = QPushButton("&Run Basic Tensorflow Demo", self)
                      self.tensorflow_demo_button.clicked.connect(lambda: self.threadstarter(tensorflow_basic_demo))
                      self.run_tab.layout.addWidget(self.tensorflow_demo_button,1,0,1,1)
                      self.run_device_label = QLabel("Connected Devices",self)
                      self.run_device_label.setStyleSheet("border: 2px solid green;")
                      self.run_tab.layout.addWidget(self.run_device_label,3,0,1,1)
                      self.run_device_table = QTableView()
                      self.connected_devices_run = pd.DataFrame([
                          ["Running",f"{current_mode}","N/A"],
                          ],columns = ["Device State","Current Program","Last Ping"],
                          index = ["Server"]
                      )
                      self.device_list_run = table_model(self.connected_devices_run)
                      self.run_device_table.setModel(self.device_list_run)
                      self.run_tab.layout.addWidget(self.run_device_table,4,0,5,5)
                      self.run_tab.setLayout(self.run_tab.layout)
                      
                  def set_display(self, fit="Nan", x_res = 640, y_res = 480):
                      if (fit == "fullscreen"):
                          self.showMaximized() 
                          self.main_tabs.resize(x_res - 80, y_res - 80) 
                      else:
                          self.setGeometry(0, 30, x_res, y_res)
                          self.main_tabs.resize(x_res - 80, y_res - 80) 
                  
                  def threadstarter(self, function, *args):
                      new_thread = Worker(function, *args)
                      new_thread.signals.operations.connect(self.threadhandler)
                      self.threadpool.start(new_thread)
                      
                  #note for any additions, is set to expect a tuple
                  #sending an int closes the thread, unless the int is in a tuple (duh)
                  def threadhandler(self, command_list):
                      task = command_list[0]
                      if (task[0] == "val_edit_conn"):
                          self.device_list.editSelf(task[1],task[2],task[3])
                      elif(task[0] == "val_edit_run"):
                          self.device_list_run.editSelf(task[1],task[2],task[3])
                      elif(task[0] == "append_conn"):
                          pd_array = command_list[1]
                          self.device_list.appendSelf(pd_array)
                      elif(task[0] == "append_run"):
                          pd_array = command_list[1]
                          self.device_list_run.appendSelf(pd_array)
                      elif(task[0] == "server_status"):
                          self.server_mode_update()
                      elif(task[0] == "set_status"):
                          status = QStatusBar()
                          status.showMessage(f"Currently Running: {task[1]}")
                          self.setStatusBar(status)
                      elif (task[0] == "tray_start"):
                          self.threadstarter(arbitrary_function,command_list[1],command_list[2])
                          
                  def update_status(self,new_status):
                      status = QStatusBar()
                      status.showMessage(new_status)
                      self.setStatusBar(status)
                      pass
                  
                  def server_mode_update(self):
                      self.device_list_run.editSelf(f"{current_mode}","Server","Current Program")
                      current_time = time.localtime()
                      current_time = time.strftime("%H:%M:%S", current_time)
                      self.device_list_run.editSelf(current_time,"Server","Last Ping")
                      self.device_list.editSelf(f"{current_mode}","Server","Current Program")
                      
                  #used as a workaround for performance issues with threads started by threads
                  def connection_authorizer(self):
                      global unauthorized_devices
                      if (unauthorized_devices == []):
                          return 0
                      for unauthorized_device in unauthorized_devices:
                          current_device = unauthorized_device
                          self.threadstarter(arbitrary_function,current_device[0],current_device[1])
                      unauthorized_devices = []
                      return 0
                  
                  def close_server(self):
                      global is_open
                      is_open = False
                  
                  def clear_server(self):
                      global current_mode
                      current_mode = "newtwork_wipe"
                      
                  def update_settings(self):
                      #why can't things just be command line
                      #: (
                      pass
                          
                          
              class connection:
                  def __init__(self, ip, conn, name, port=9999):
                    self.ip = ip
                    self.conn = conn
                    self.name = name
                    self.port = port
                    
                  #function for making sending strings slightly easier
                  def send(self,message):
                      #added to prevent improperly formatted messages from throwing errors
                      message = str(message)
                      message_size = str(len(message))
                      self.conn.send(message_size.encode(code))
                      #delay to prevent signal interference
                      time.sleep(0.5)
                      self.conn.send(message.encode(code))
                      
                  #function for recieving special datatypes
                  #may add full file sending here, who knows
                  def send_advanced(self,message,datatype="array"):
                      if datatype == "array":
                          #added to prevent improperly formatted messages from throwing errors
                          message = pickle.dumps(message)
                          message_size = str(len(message))
                          #print(message_size)
                          self.conn.send(message_size.encode(code))
                          #delay to prevent signal interference
                          time.sleep(0.5)
                          self.conn.send(message)
                          
                  #function for making recieving strings slightly easier
                  def recieve(self):
                      message_size = int(self.conn.recv(64).decode(code))
                      #delay to prevent signal interference
                      time.sleep(0.5)
                      message = self.conn.recv(message_size).decode(code)
                      return message
                  
                  #function for recieving special datatypes
                  #may accept files in the future
                  def recieve_advanced(self):
                      message_size = int(self.conn.recv(64).decode(code))
                      #delay to prevent signal interference
                      time.sleep(0.5)
                      message = self.conn.recv(message_size)
                      message = pickle.loads(message)
                      return message
                  
                     
              #allows for utilization of threads in gui backend
              class Worker(QRunnable):
                  def __init__(self, fn, *args):
                      super(Worker, self).__init__()
                      # Store constructor arguments (re-used for processing)
                      self.fn = fn
                      self.args = args
                      self.signals = WorkerSignals()
              
                  @pyqtSlot()
                  def run(self):
                      '''
                      Initialise the runner function with passed args, kwargs.
                      '''
                      self.fn(self, *self.args)
                      
              #implementation of Qobject for handling signals sent from worker threads
              class WorkerSignals(QObject):
                  operations = pyqtSignal(tuple)
                  
                  
                  
              ###################
              #program functions#
              ###################
              
              #print statements still exist for debuggings sake
              
              #it's a function like this because it's a thread
              def spinup(thread_manager):
                  thread_manager.signals.operations.emit((["set_status","Connecting Devices..."],))
                  client = setup(thread_manager)
                  listener(client,thread_manager)
                  thread_manager.signals.operations.emit((["set_status","Nothing Currently Running..."],))
                  return 0
              
              #note to self: this needs to change to settings at some point
              def setup(thread_manager,port=server_port,connections=server_connections):
                  global server_ip
                  global core_connetion
                  global current_mode
                  current_mode = "waiting"
                  client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
                  ip = socket.gethostbyname(socket.gethostname())
                  server_ip = ip
                  thread_manager.signals.operations.emit((["val_edit_conn", f"{server_ip}", "Server", "Ipv4 Address"],))
                  core_connection = client
                  client.bind((ip,port))
                  client.listen(connections)
                  #print("Server set up is complete")
                  #print(f"Server ip is {ip}")
                  #print(f"Server port is {port}")
                  #print(f"Server allows up to {connections} devices")
                  return client
              
              def listener(client,thread_manager):
                  global window
                  global unauthorized_devices
                  global is_open
                  client.listen()
                  dev_count = 0
                  pi4_count = 0
                  arduino_uno_count = 0
                  is_open = True
                  while (is_open):
                      if (dev_count<4):
                          print(f"\nListener is available for new connections {dev_count} connected\n")
                          dev_count = dev_count + 1
                      else:
                          break
                      conn, addr = client.accept()
                      name = f"pi-{dev_count}"
                      new_connection = connection(addr, conn, name)
                      new_connection.send("type")
                      dev_type = new_connection.recieve()
                      if (dev_type == "pi4"):
                          pi4_count = pi4_count + 1
                          new_connection.name = f"pi4-{pi4_count}"
                      elif (dev_type == "arduino uno"):
                          arduino_uno_count = arduino_uno_count + 1
                          new_connection.name = f"arduino_uno-{arduino_uno_count}"
                      name = new_connection.name
                      #thread_manager.signals.operations.emit((["tray_start"],new_connection,dev_count))
                      unauthorized_devices.append((new_connection,dev_count))
                      devices.append(new_connection)
                      device_states.append("awaiting authorization...")
                      demo.append(0)
                      tensorflow_demo.append(0)
                      current_time = time.localtime()
                      current_time = time.strftime("%H:%M:%S", current_time)
                      new_device = pd.DataFrame([
                          [f"{addr[0]}", f"{server_port}", f"{device_states[dev_count - 1]}",f"{current_mode}",f"{current_time}"],
                          ],columns = ['Ipv4 Address', "Port", "Device State","Current Program","Last Ping"],
                          index = [f'{name}']
                      )
                      thread_manager.signals.operations.emit((["append_conn"],new_device))
                      new_device = pd.DataFrame([
                          [f"{device_states[dev_count - 1]}",f"{current_mode}",f"{current_time}"],
                          ],columns = ["Device State","Current Program","Last Ping"],
                          index = [f'{name}']
                      )
                      thread_manager.signals.operations.emit((["append_run"],new_device))
                  print("Max device count reached, server closing...")
                  thread_manager.signals.operations.emit((["server_status"],))
                  return 0
                      #print(f"pi-{pi_count} is waiting for instructions...")
              
              #function for ensuring device consensus accross the network
              def consensus(target_list,goal):
                  #waits until at least one device reaches the state we want
                  while(target_list[0] != goal):
                      pass
                  #waits until all devices read the same state
                  while(target_list.count(target_list[0]) != len(target_list)):
                      pass
                  
              #resets global variables between mode executions
              def reset():
                  global current_mode
                  global avg
                  global demo
                  global iterations
                  global tensorflow_demo
                  for i in range(len(demo)):
                      demo[i] = 0
                  for i in range(len(tensorflow_demo)):
                      tensorflow_demo[i] = 0
                  avg = 0
                  iterations = 0
                  current_mode = "waiting"
                  
              #arbitraty function for testing
              def arbitrary_function(thread_manager, *stuff):
                  while(True):
                      pass
              
              
              #handles all changing of state *insert pun here*
              def state_changer(thread_manager,pi_count,name,new_state):
                  device_states[pi_count-1] = new_state
                  test = ["val_edit_run",f"{new_state}",f"{name}"]
                  thread_manager.signals.operations.emit((["val_edit_run",f"{new_state}",f"{name}", "Device State"],))
                  thread_manager.signals.operations.emit((["val_edit_conn",f"{current_mode}",f"{name}", "Current Program"],))
                  thread_manager.signals.operations.emit((["val_edit_run",f"{current_mode}",f"{name}", "Current Program"],))
                  current_time = time.localtime()
                  current_time = time.strftime("%H:%M:%S", current_time)
                  thread_manager.signals.operations.emit((["val_edit_conn",f"{current_time}",f"{name}", "Last Ping"],))
                  
              def tensorflow_basic_demo(thread_manager):
                  #not really related to the problem
                  pass
              ###########
              #Main Loop#
              ###########
              
              def maine():
                  global window
                  app = QApplication([])
                  window = Window()
                  window.show()
                  
                  sys.exit(app.exec())
                  
              #call main to start program
              maine()
              
              P Offline
              P Offline
              Pl45m4
              wrote on 24 Jul 2024, 15:50 last edited by
              #7

              @imissthecommandline

              Urgh, PyQt :)
              You should have mentioned that before or post that in Qt for Python category.
              Since I can't spot the issue from the first glance and I'm currently not able to run your code, I can't help you.


              If debugging is the process of removing software bugs, then programming must be the process of putting them in.

              ~E. W. Dijkstra

              1 Reply Last reply
              0
              • I Offline
                I Offline
                imissthecommandline
                wrote on 24 Jul 2024, 15:58 last edited by
                #8

                got it, posting over there

                thank you for looking!

                P 1 Reply Last reply 24 Jul 2024, 16:13
                0
                • I imissthecommandline
                  24 Jul 2024, 15:58

                  got it, posting over there

                  thank you for looking!

                  P Offline
                  P Offline
                  Pl45m4
                  wrote on 24 Jul 2024, 16:13 last edited by Pl45m4
                  #9

                  @imissthecommandline

                  Don't need to post it again.
                  Some mod (@moderators) might move this post there.


                  If debugging is the process of removing software bugs, then programming must be the process of putting them in.

                  ~E. W. Dijkstra

                  1 Reply Last reply
                  0
                  • I Offline
                    I Offline
                    imissthecommandline
                    wrote on 24 Jul 2024, 16:24 last edited by
                    #10

                    got it, thank you

                    1 Reply Last reply
                    0
                    • C Christian Ehrlicher moved this topic from General and Desktop on 24 Jul 2024, 16:38
                    • S Offline
                      S Offline
                      SGaist
                      Lifetime Qt Champion
                      wrote on 24 Jul 2024, 19:15 last edited by
                      #11

                      Hi,

                      This is more than 600 lines of pretty convoluted code that uses way too many globals.
                      If you want an answer please reduce it so that it can be used to reproduce your issue.

                      Interested in AI ? www.idiap.ch
                      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                      I 1 Reply Last reply 24 Jul 2024, 19:36
                      1
                      • S SGaist
                        24 Jul 2024, 19:15

                        Hi,

                        This is more than 600 lines of pretty convoluted code that uses way too many globals.
                        If you want an answer please reduce it so that it can be used to reproduce your issue.

                        I Offline
                        I Offline
                        imissthecommandline
                        wrote on 24 Jul 2024, 19:36 last edited by
                        #12

                        @SGaist
                        ouch, sorry about that

                        here's a version with pretty much all but the essentials removed:

                        # -*- coding: utf-8 -*-
                        """
                        Created on Wed Jul 24 15:22:55 2024
                        
                        @author: pierre
                        """
                        
                        #libraries for array management and graphing
                        import pandas as pd
                        import numpy as np
                        import matplotlib as plt
                        
                        #libraries for system access and gui foundation
                        import sys 
                        from PyQt6.QtWidgets import (
                            QApplication,
                            QLabel,
                            QMainWindow,
                            QStatusBar,
                            QToolBar,
                            QStackedWidget,
                            QStackedLayout,
                            QWidget,
                            QTabWidget,
                            QVBoxLayout,
                            QGridLayout,
                            QPushButton,
                            QLineEdit,
                            QTableView
                        )
                        
                        from PyQt6 import QtCore, QtGui, QtWidgets
                        from PyQt6.QtGui import *
                        from PyQt6.QtWidgets import *
                        from PyQt6.QtCore import *
                        
                        
                        #placeholde for stuff i haven't implemented in full yet
                        placeholder = "<unimplemented val!>"
                        
                        #Library Imports for core management program
                        import socket
                        import threading
                        import time
                        import pickle
                        
                        global window
                        
                        #allows for utilization of threads in gui backend
                        class Worker(QRunnable):
                            def __init__(self, fn, *args):
                                super(Worker, self).__init__()
                                # Store constructor arguments (re-used for processing)
                                self.fn = fn
                                self.args = args
                                self.signals = WorkerSignals()
                        
                            @pyqtSlot()
                            def run(self):
                                '''
                                Initialise the runner function with passed args, kwargs.
                                '''
                                self.fn(self, *self.args)
                                
                        #implementation of Qobject for handling signals sent from worker threads
                        class WorkerSignals(QObject):
                            operations = pyqtSignal(tuple)
                            
                        
                        #code taken from pyqt tutorial (link below)
                        #https://www.pythonguis.com/tutorials/pyqt6-qtableview-modelviews-numpy-pandas/
                        class table_model(QtCore.QAbstractTableModel):
                            def __init__(self, data):
                                super(table_model, self).__init__()
                                self._data = data
                        
                            def data(self, index, role):
                                if role == Qt.ItemDataRole.DisplayRole:
                                    value = self._data.iloc[index.row(), index.column()]
                                    return str(value)
                        
                            def rowCount(self, index):
                                return self._data.shape[0]
                        
                            def columnCount(self, index):
                                return self._data.shape[1]
                        
                            def headerData(self, section, orientation, role):
                                # section is the index of the column/row.
                                if role == Qt.ItemDataRole.DisplayRole:
                                    if orientation == Qt.Orientation.Horizontal:
                                        return str(self._data.columns[section])
                        
                                    if orientation == Qt.Orientation.Vertical:
                                        return str(self._data.index[section])
                            
                            def flags(self, index):
                                return Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsEditable
                                
                            #append the dataframe
                            def appendSelf(self,new_val):
                                self._data = pd.concat([self._data,new_val])
                                self.layoutChanged.emit()
                                return 0
                                
                            #edit a specific value
                            def editSelf(self,new_val,index,column):
                                self._data.at[index,column] = new_val
                                self.layoutChanged.emit()
                                return 0
                            
                            #remove a line
                            def removeSelf(self,index):
                                self._data.set_index(index)
                                self._data.reset_index(drop=True)
                                self.layoutChanged.emit()
                                return 0
                            
                        #Main GUI window coding
                        class Window(QMainWindow):
                            def __init__(self):
                                super().__init__(parent=None)
                                #values for window resolution
                                self.x_res = 640
                                self.y_res = 480
                                self.setWindowTitle("S.T.A.R.F.I.S.H")
                                #various set-up functions for the gui
                                self.stack_init()
                                self.setGeometry(0, 30, self.x_res, self.y_res)
                                #setup for thread manager
                                self.threadpool = QThreadPool()
                        
                                
                            def stack_init(self):
                                #all possible screens are organized through tabs at the top
                                #all sub-tabs are part of a stack
                                
                                #basic setup
                                self.layout = QVBoxLayout(self)
                                self.main_tabs = QTabWidget(self)
                                self.main_tabs.resize(self.x_res - 70, self.y_res - 70) 
                                self.main_tabs.move(10, 40) 
                                
                                #custom tab init
                                self.home_tab = QWidget(self)
                                self.main_tabs.addTab(self.home_tab,"Home")
                                self.config_tab = QWidget(self)
                                self.main_tabs.addTab(self.config_tab,"Configuration")
                                self.conn_tab = QWidget(self)
                                self.main_tabs.addTab(self.conn_tab,"Connections")
                                self.conn_man_tab = QWidget(self)
                                self.main_tabs.addTab(self.conn_man_tab,"Connection Management")
                                self.run_tab = QWidget(self)
                                self.main_tabs.addTab(self.run_tab,"Run")
                                self.layout.addWidget(self.main_tabs) 
                                self.setLayout(self.layout) 
                                
                                #home tab setup
                                #label 1 formatting
                                self.home_tab.layout = QVBoxLayout(self)
                                self.top_label = QLabel()
                                self.top_label.setText("BlaBlaBla this tab isn't the problem") 
                                self.home_tab.layout.addWidget(self.top_label)
                        
                                self.home_tab.setLayout(self.home_tab.layout)
                                
                                #options tab setup
                                #each adjustable setting has a qlabel in column 0 saying what it is
                                #this what the value is and the current value
                                #a input box in column 4 lets you change the value
                                
                                self.config_tab.layout = QGridLayout(self)
                                self.upload_button = QPushButton("&Upload custom config", self)
                                self.config_tab.layout.addWidget(self.upload_button,0,0,1,5)
                                #columns 1,3,5 are thin for l'aesthétique :)
                                for i in range(3):
                                    self.config_tab.layout.setColumnMinimumWidth(1 + (i*2), 10)
                                    
                                #network settings start
                                self.config_ops_1 = QLabel("Go to the connections tab", self)
                                self.config_ops_1.setStyleSheet("border: 2px solid blue;") 
                                self.config_tab.layout.addWidget(self.config_ops_1,1,0)
                                
                                self.config_tab.setLayout(self.config_tab.layout)
                                
                                #connections tab setup
                        
                                
                                self.conn_tab.layout = QGridLayout(self)
                                self.start_connecting = QPushButton("&Start a thread", self)
                                self.start_connecting.clicked.connect(lambda: self.threadstarter(arbitrary_task))
                                self.conn_tab.layout.addWidget(self.start_connecting,0,0,1,2)
                                #initially array for connected devices
                                self.conn_devices_table = QTableView()
                                self.connected_devices = pd.DataFrame([
                                    ["{server_ip}", "{server_port}", "Connections Tab","{current_mode}"],
                                    ],columns = ["Ipv4 Address", "Port", "Device State","Current Program"],
                                    index = ["Server"]
                                    )
                                self.device_list = table_model(self.connected_devices)
                                self.conn_devices_table.setModel(self.device_list)
                                self.conn_tab.layout.addWidget(self.conn_devices_table,2,0,5,5)
                                self.conn_tab.setLayout(self.conn_tab.layout)
                                
                                #connection management tab setup
                                
                                #button for authorizing all connections, button for clearing network
                                self.conn_man_tab.layout = QGridLayout(self)
                                self.authorize_connections = QPushButton("&Start a thread", self)
                                self.authorize_connections.clicked.connect(lambda: self.connection_authorizer(arbitrary_task))
                                self.conn_man_tab.layout.addWidget(self.authorize_connections,0,0,1,2)
                                self.conn_man_tab.setLayout(self.conn_man_tab.layout)
                                
                                #run tab setup
                                self.run_tab.layout = QGridLayout(self)
                                self.run_device_label = QLabel("Other Table I Guess",self)
                                self.run_device_label.setStyleSheet("border: 2px solid blue;")
                                self.run_tab.layout.addWidget(self.run_device_label,0,0,1,1)
                                
                                self.run_device_table = QTableView()
                                self.connected_devices_run = pd.DataFrame([
                                    ["Running","{current_mode}","N/A"],
                                    ],columns = ["Device State","Current Program","Last Ping"],
                                    index = ["Server"]
                                )
                                self.device_list_run = table_model(self.connected_devices_run)
                                self.run_device_table.setModel(self.device_list_run)
                                self.run_tab.layout.addWidget(self.run_device_table,4,0,5,5)
                                
                                self.run_tab.setLayout(self.run_tab.layout)
                            
                            def threadstarter(self, function, *args):
                                new_thread = Worker(function, *args)
                                new_thread.signals.operations.connect(self.threadhandler)
                                self.threadpool.start(new_thread)
                                
                            #note for any additions, is set to expect a tuple
                            #sending an int closes the thread, unless the int is in a tuple (duh)
                            def threadhandler(self, command_list):
                                pass
                        
                        def arbitrary_task(*args):
                            print("Started Thread")
                            while(True):
                                pass
                        
                        def main():
                            global window
                            app = QApplication([])
                            window = Window()
                            window.show()
                            sys.exit(app.exec())
                            
                        #call main to start program
                        main()
                        

                        it seems to only lag on tabs with tables, even though we aren't interacting with them. any ideas as to why this is?

                        1 Reply Last reply
                        0
                        • S Offline
                          S Offline
                          SGaist
                          Lifetime Qt Champion
                          wrote on 24 Jul 2024, 20:49 last edited by
                          #13

                          What is the procedure to reproduce your issue with that version ?
                          By the way it crashes on line 210 when clicking "Start a thread" on the "Connection management" tab.

                          Interested in AI ? www.idiap.ch
                          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                          I 1 Reply Last reply 24 Jul 2024, 21:50
                          0
                          • S SGaist
                            24 Jul 2024, 20:49

                            What is the procedure to reproduce your issue with that version ?
                            By the way it crashes on line 210 when clicking "Start a thread" on the "Connection management" tab.

                            I Offline
                            I Offline
                            imissthecommandline
                            wrote on 24 Jul 2024, 21:50 last edited by
                            #14

                            @SGaist the connected function on line 210 should be

                            lambda: self.threadstarter(arbitrary_task))
                            

                            I messed up switching it out for the simplified version.

                            If you click the start thread button a bunch of times, the program lags, but only when going to a tab with a table or from a tab with a table removing the connections tab table removes the lag.

                            To reproduce:
                            Run Program
                            Click on Connections Tab
                            Click the start thread button 8 or so times
                            Click around the tabs

                            I think pyqt6 might have a hard time managing threads and tables, but it's probably my implementation, as this is my first time with pyqt6

                            Thank you

                            1 Reply Last reply
                            0
                            • I Offline
                              I Offline
                              imissthecommandline
                              wrote on 26 Jul 2024, 14:38 last edited by
                              #15

                              Ok so i've figured out some interesting things about this:

                              1. Using the .hide() function on the table removes the lag. using .hide() on both of the tables before starting a new thread then .show() after the thread is started moderately reduces the lag.
                              2. Completely reinitializing the table by removing the widget from the layout then adding it again after starting a new thread reduces the lag more than method 1, but there's still some noticeable lag
                                3.Adding the tables AFTER starting the threads has no impact at all, the lag is just as bad

                              Still not sure what the issue is though

                              Here's the code for anyone insane enough to try and help

                              # -*- coding: utf-8 -*-
                              """
                              Created on Wed Jul 24 15:22:55 2024
                              
                              @author: pierre
                              """
                              
                              #libraries for array management and graphing
                              import pandas as pd
                              import numpy as np
                              import matplotlib as plt
                              
                              #libraries for system access and gui foundation
                              import sys 
                              from PyQt6.QtWidgets import (
                                  QApplication,
                                  QLabel,
                                  QMainWindow,
                                  QStatusBar,
                                  QToolBar,
                                  QStackedWidget,
                                  QStackedLayout,
                                  QWidget,
                                  QTabWidget,
                                  QVBoxLayout,
                                  QGridLayout,
                                  QPushButton,
                                  QLineEdit,
                                  QTableView
                              )
                              
                              from PyQt6 import QtCore, QtGui, QtWidgets
                              from PyQt6.QtGui import *
                              from PyQt6.QtWidgets import *
                              from PyQt6.QtCore import *
                              
                              
                              #placeholde for stuff i haven't implemented in full yet
                              placeholder = "<unimplemented val!>"
                              
                              #Library Imports for core management program
                              import socket
                              import threading
                              import time
                              import pickle
                              
                              global window
                              
                              #allows for utilization of threads in gui backend
                              class Worker(QRunnable):
                                  def __init__(self, fn, *args):
                                      super(Worker, self).__init__()
                                      # Store constructor arguments (re-used for processing)
                                      self.fn = fn
                                      self.args = args
                                      self.signals = WorkerSignals()
                              
                                  @pyqtSlot()
                                  def run(self):
                                      '''
                                      Initialise the runner function with passed args, kwargs.
                                      '''
                                      self.fn(self, *self.args)
                                      
                              #implementation of Qobject for handling signals sent from worker threads
                              class WorkerSignals(QObject):
                                  operations = pyqtSignal(tuple)
                                  
                              
                              #code taken from pyqt tutorial (link below)
                              #https://www.pythonguis.com/tutorials/pyqt6-qtableview-modelviews-numpy-pandas/
                              class table_model(QtCore.QAbstractTableModel):
                                  def __init__(self, data):
                                      super(table_model, self).__init__()
                                      self._data = data
                              
                                  def data(self, index, role):
                                      if role == Qt.ItemDataRole.DisplayRole:
                                          value = self._data.iloc[index.row(), index.column()]
                                          return str(value)
                              
                                  def rowCount(self, index):
                                      return self._data.shape[0]
                              
                                  def columnCount(self, index):
                                      return self._data.shape[1]
                              
                                  def headerData(self, section, orientation, role):
                                      # section is the index of the column/row.
                                      if role == Qt.ItemDataRole.DisplayRole:
                                          if orientation == Qt.Orientation.Horizontal:
                                              return str(self._data.columns[section])
                              
                                          if orientation == Qt.Orientation.Vertical:
                                              return str(self._data.index[section])
                                  
                                  def flags(self, index):
                                      return Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsEditable
                                      
                                  #append the dataframe
                                  def appendSelf(self,new_val):
                                      self._data = pd.concat([self._data,new_val])
                                      self.layoutChanged.emit()
                                      return 0
                                      
                                  #edit a specific value
                                  def editSelf(self,new_val,index,column):
                                      self._data.at[index,column] = new_val
                                      self.layoutChanged.emit()
                                      return 0
                                  
                                  #remove a line
                                  def removeSelf(self,index):
                                      self._data.set_index(index)
                                      self._data.reset_index(drop=True)
                                      self.layoutChanged.emit()
                                      return 0
                                  
                              #Main GUI window coding
                              class Window(QMainWindow):
                                  def __init__(self):
                                      super().__init__(parent=None)
                                      #values for window resolution
                                      self.x_res = 640
                                      self.y_res = 480
                                      self.setWindowTitle("S.T.A.R.F.I.S.H")
                                      #various set-up functions for the gui
                                      self.stack_init()
                                      self.setGeometry(0, 30, self.x_res, self.y_res)
                                      #setup for thread manager
                                      self.threadpool = QThreadPool()
                              
                                      
                                  def stack_init(self):
                                      #all possible screens are organized through tabs at the top
                                      #all sub-tabs are part of a stack
                                      
                                      #basic setup
                                      self.layout = QVBoxLayout(self)
                                      self.main_tabs = QTabWidget(self)
                                      self.main_tabs.resize(self.x_res - 70, self.y_res - 70) 
                                      self.main_tabs.move(10, 40) 
                                      
                                      #custom tab init
                                      self.home_tab = QWidget(self)
                                      self.main_tabs.addTab(self.home_tab,"Home")
                                      self.config_tab = QWidget(self)
                                      self.main_tabs.addTab(self.config_tab,"Configuration")
                                      self.conn_tab = QWidget(self)
                                      self.main_tabs.addTab(self.conn_tab,"Connections")
                                      self.conn_man_tab = QWidget(self)
                                      self.main_tabs.addTab(self.conn_man_tab,"Connection Management")
                                      self.run_tab = QWidget(self)
                                      self.main_tabs.addTab(self.run_tab,"Run")
                                      self.layout.addWidget(self.main_tabs) 
                                      self.setLayout(self.layout) 
                                      
                                      #home tab setup
                                      #label 1 formatting
                                      self.home_tab.layout = QVBoxLayout(self)
                                      self.top_label = QLabel()
                                      self.top_label.setText("BlaBlaBla this tab isn't the problem") 
                                      self.home_tab.layout.addWidget(self.top_label)
                              
                                      self.home_tab.setLayout(self.home_tab.layout)
                                      
                                      #options tab setup
                                      #each adjustable setting has a qlabel in column 0 saying what it is
                                      #this what the value is and the current value
                                      #a input box in column 4 lets you change the value
                                      
                                      self.config_tab.layout = QGridLayout(self)
                                      self.upload_button = QPushButton("&Upload custom config", self)
                                      self.config_tab.layout.addWidget(self.upload_button,0,0,1,5)
                                      #columns 1,3,5 are thin for l'aesthétique :)
                                      for i in range(3):
                                          self.config_tab.layout.setColumnMinimumWidth(1 + (i*2), 10)
                                          
                                      #network settings start
                                      self.config_ops_1 = QLabel("Go to the connections tab", self)
                                      self.config_ops_1.setStyleSheet("border: 2px solid blue;") 
                                      self.config_tab.layout.addWidget(self.config_ops_1,1,0)
                                      
                                      self.config_tab.setLayout(self.config_tab.layout)
                                      
                                      #connections tab setup
                              
                                      
                                      self.conn_tab.layout = QGridLayout(self)
                                      self.start_connecting = QPushButton("&Start a thread", self)
                                      self.start_connecting.clicked.connect(lambda: self.threadstarter(arbitrary_task))
                                      self.conn_tab.layout.addWidget(self.start_connecting,0,0,1,2)
                                      #initial array for connected devices
                                      self.conn_devices_table = QTableView()
                                      self.connected_devices = pd.DataFrame([
                                          ["{server_ip}", "{server_port}", "Connections Tab","{current_mode}"],
                                          ],columns = ["Ipv4 Address", "Port", "Device State","Current Program"],
                                          index = ["Server"]
                                          )
                                      self.device_list = table_model(self.connected_devices)
                                      self.conn_devices_table.setModel(self.device_list)
                                      self.conn_tab.layout.addWidget(self.conn_devices_table,2,0,5,5)
                                      self.conn_tab.setLayout(self.conn_tab.layout)
                                      
                                      #connection management tab setup
                                      
                                      #button for authorizing all connections, button for clearing network
                                      self.conn_man_tab.layout = QGridLayout(self)
                                      self.authorize_connections = QPushButton("&Tabletime", self)
                                      self.authorize_connections.clicked.connect(lambda: self.testingthing())
                                      self.conn_man_tab.layout.addWidget(self.authorize_connections,0,0,1,2)
                                      self.conn_man_tab.setLayout(self.conn_man_tab.layout)
                                      
                                      #run tab setup
                                      self.run_tab.layout = QGridLayout(self)
                                      self.run_device_label = QLabel("Other Table I Guess",self)
                                      self.run_device_label.setStyleSheet("border: 2px solid blue;")
                                      self.run_tab.layout.addWidget(self.run_device_label,0,0,1,1)
                                      
                                      self.run_device_table = QTableView()
                                      self.connected_devices_run = pd.DataFrame([
                                          ["Running","{current_mode}","N/A"],
                                          ],columns = ["Device State","Current Program","Last Ping"],
                                          index = ["Server"]
                                      )
                                      self.device_list_run = table_model(self.connected_devices_run)
                                      self.run_device_table.setModel(self.device_list_run)
                                      self.run_tab.layout.addWidget(self.run_device_table,4,0,5,5)
                                      self.run_tab.setLayout(self.run_tab.layout)
                                  
                                  def threadstarter(self, function, *args):
                                      #the mechanics of this frighten me
                                      self.conn_tab.layout.removeWidget(self.conn_devices_table)
                                      self.conn_tab.setLayout(self.conn_tab.layout)
                                      self.run_tab.layout.removeWidget(self.run_device_table)
                                      self.run_tab.setLayout(self.run_tab.layout)
                                      
                                      ##############################
                                      #normal thread starting stuff#
                                      ##############################
                                      new_thread = Worker(function, *args)
                                      new_thread.signals.operations.connect(self.threadhandler)
                                      self.threadpool.start(new_thread)
                                      #####################################
                                      #end of normal thread starting stuff#
                                      #####################################
                                      
                                      self.conn_devices_table = QTableView()
                                      self.device_list = table_model(self.connected_devices)
                                      self.conn_devices_table.setModel(self.device_list)
                                      self.conn_tab.layout.addWidget(self.conn_devices_table,2,0,5,5)
                                      self.conn_tab.setLayout(self.conn_tab.layout)
                                      
                                      self.run_device_table = QTableView()
                                      self.device_list_run = table_model(self.connected_devices_run)
                                      self.run_device_table.setModel(self.device_list_run)
                                      self.run_tab.layout.addWidget(self.run_device_table,4,0,5,5)
                                      self.run_tab.setLayout(self.run_tab.layout)
                                      
                                  def testingthing(self):
                                      #for adding the table after the threads
                                      
                                      #This is dumb
                                      self.conn_devices_table = QTableView()
                                      self.connected_devices = pd.DataFrame([
                                          ["{server_ip}", "{server_port}", "Connections Tab","{current_mode}"],
                                          ],columns = ["Ipv4 Address", "Port", "Device State","Current Program"],
                                          index = ["Server"]
                                          )
                                      self.device_list = table_model(self.connected_devices)
                                      self.conn_devices_table.setModel(self.device_list)
                                      self.conn_tab.layout.addWidget(self.conn_devices_table,2,0,5,5)
                                      self.conn_tab.setLayout(self.conn_tab.layout)
                                      
                                      
                                      self.run_device_table = QTableView()
                                      self.connected_devices_run = pd.DataFrame([
                                          ["Running","{current_mode}","N/A"],
                                          ],columns = ["Device State","Current Program","Last Ping"],
                                          index = ["Server"]
                                      )
                                      self.device_list_run = table_model(self.connected_devices_run)
                                      self.run_device_table.setModel(self.device_list_run)
                                      self.run_tab.layout.addWidget(self.run_device_table,4,0,5,5)
                                      self.run_tab.setLayout(self.run_tab.layout)
                              
                                      
                                  #note for any additions, is set to expect a tuple
                                  #sending an int closes the thread, unless the int is in a tuple (duh)
                                  def threadhandler(self, command_list):
                                      pass
                              
                              def arbitrary_task(*args):
                                  print("Started Thread")
                                  while(True):
                                      pass
                              
                              def main():
                                  global window
                                  app = QApplication([])
                                  window = Window()
                                  window.show()
                                  sys.exit(app.exec())
                                  
                              #call main to start program
                              main()
                              
                              1 Reply Last reply
                              0
                              • S Offline
                                S Offline
                                SGaist
                                Lifetime Qt Champion
                                wrote on 27 Jul 2024, 19:36 last edited by
                                #16

                                Is your code based on that tutorial ?

                                Interested in AI ? www.idiap.ch
                                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                                I 1 Reply Last reply 31 Jul 2024, 13:21
                                0
                                • S SGaist
                                  27 Jul 2024, 19:36

                                  Is your code based on that tutorial ?

                                  I Offline
                                  I Offline
                                  imissthecommandline
                                  wrote on 31 Jul 2024, 13:21 last edited by
                                  #17

                                  @SGaist in part yes. Do you have a better tutorial or resource you reccomend?

                                  1 Reply Last reply
                                  0
                                  • I Offline
                                    I Offline
                                    imissthecommandline
                                    wrote on 31 Jul 2024, 13:58 last edited by
                                    #18

                                    sorry, i used this one: https://www.pythonguis.com/tutorials/multithreading-pyqt6-applications-qthreadpool/

                                    1 Reply Last reply
                                    0
                                    • I Offline
                                      I Offline
                                      imissthecommandline
                                      wrote on 31 Jul 2024, 14:11 last edited by
                                      #19

                                      ok, so what is happening is that every thread is making several calls to the table model every time the tab is opened. is there a way you recommend to prevent this? @SGaist

                                      1 Reply Last reply
                                      0
                                      • S Offline
                                        S Offline
                                        SGaist
                                        Lifetime Qt Champion
                                        wrote on 2 Aug 2024, 18:57 last edited by
                                        #20

                                        You can implement a debouncer. For example, accumulate a certain amount of changes and only then signal that the model has changed to minimize the amount of time the views will query the model.
                                        Also, don't forget to properly use lock mechanism to avoid writing to the same object from multiple different threads.

                                        Interested in AI ? www.idiap.ch
                                        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                                        1 Reply Last reply
                                        0

                                        11/20

                                        24 Jul 2024, 19:15

                                        • Login

                                        • Login or register to search.
                                        11 out of 20
                                        • First post
                                          11/20
                                          Last post
                                        0
                                        • Categories
                                        • Recent
                                        • Tags
                                        • Popular
                                        • Users
                                        • Groups
                                        • Search
                                        • Get Qt Extensions
                                        • Unsolved