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

Issue with Worker Class when used with QTables

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

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

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

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

      Is QTableModel only threadsafe if used once in a program?

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

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

      Is QTableModel only threadsafe if used once in a program?

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

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

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

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

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

        P 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
                  • C Christian Ehrlicher moved this topic from General and Desktop on 24 Jul 2024, 16:38
                  • S Offline
                    S Offline
                    SGaist
                    Lifetime Qt Champion
                    wrote on 24 Jul 2024, 19:15 last edited by
                    #11

                    Hi,

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

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

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

                      Hi,

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

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

                      @SGaist
                      ouch, sorry about that

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

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

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

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

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

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

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

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

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

                          @SGaist the connected function on line 210 should be

                          lambda: self.threadstarter(arbitrary_task))
                          

                          I messed up switching it out for the simplified version.

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

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

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

                          Thank you

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

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

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

                            Still not sure what the issue is though

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

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

                              Is your code based on that tutorial ?

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

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

                                Is your code based on that tutorial ?

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

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

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

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

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

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

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

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

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

                                      1 Reply Last reply
                                      0

                                      12/20

                                      24 Jul 2024, 19:36

                                      • Login

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