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

Issue with Worker Class when used with QTables

Scheduled Pinned Locked Moved Unsolved Qt for Python
qtablemodelworkerthread
20 Posts 4 Posters 996 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 imissthecommandline
    24 Jul 2024, 14:24

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

    P Online
    P Online
    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 Online
        P Online
        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 Online
            P Online
            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
              • Christian EhrlicherC 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

                                  14/20

                                  24 Jul 2024, 21:50

                                  • Login

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