You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySimpleGUI机器可用性GUI报错:TypeError: 'str'对象不可调用

问题

使用PySimpleGUI开发机器可用性查看GUI时,集成downtime函数触发错误。完整代码如下:

import PySimpleGUI as sg
import pyodbc
import databaseconfig as conf
from datetime import datetime, timedelta, time
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
sg.theme('GrayGrayGray')
# Connect to the SQL server
cnxn = pyodbc.connect(DRIVER= conf.driver,
                      SERVER= conf.server,
                      DATABASE= conf.database,
                      UID= conf.username,
                      PWD= conf.password)
cursor = cnxn.cursor()
# Selects the 'device' of database table
cursor.execute("SELECT device FROM kt_machines")
device = cursor.fetchall()
list_devices = [(row)[0] for row in device]
# Selects the 'Omschrijving' of database table
cursor.execute("SELECT Omschrijving FROM kt_machines")
omschrijving = cursor.fetchall()
omsch = [(row)[0] for row in omschrijving]

# This will remove everything that comes after '.'. now it prints like: 2023-02-10 07:20:10.747000. After this function it prints like: 2023-02-10 07:20:10
def removeAfterN(yourStr, nth, occurenceOf):
    return occurenceOf.join(yourStr.split(occurenceOf)[:nth])#print(removeAfterN(str(data[0]),1,"."))

# This function removes certain prefix
def my_removeprefix(omsch, prefix):
    if omsch.startswith(prefix):
        return omsch[len(prefix):]
    else:
        return omsch
    
# Removes prefix of your choosing
def removePrefix(num,prefixToDel):
    return my_removeprefix(omsch[num], prefixToDel)

# Gets timestamp from database
def timeStamp():
    cursor.execute(f"SELECT TS FROM kt_2010 WHERE id=(SELECT max(id) FROM kt_2010)")
    data = cursor.fetchone()
    if data[0] is not None:
        window[f'TS1'].update(data[0])
        return str(data[0])
    else:
        return 0

def timeStampDiff():
    # Get current time stamp. This shows the date and the time
    cursor.execute(f"SELECT TS FROM kt_2010 WHERE id=(SELECT max(id) FROM kt_2010)")
    data = cursor.fetchone()
    timestampp = datetime.now()
    difference = (timestampp - data[0])
    differenceMin = (difference.seconds / 60)
    differenceMinn = removeAfterN(str(differenceMin),1,".")
    return differenceMinn or 0

def createCircle():
    graph = window['graph0']
    circle = graph.draw_circle((88, 15), 7, fill_color='red', line_color='black')
    if int(timeStampDiff()) > 5:# If difference between current timestamp and latest timestamp from table is greater that 5 the circle will turn red.
            graph.TKCanvas.itemconfig(circle, fill='red')
    elif int(timeStampDiff()) < 5:# If difference between current timestamp and latest timestamp from table is less that 5 the circle will turn green.
        graph.TKCanvas.itemconfig(circle, fill='green')
    return circle or 0

def downtime():
    # Define when the workday starts, also define when the workday ends
    start_time = datetime.combine(datetime(2023,2,21), time(7, 30))
    end_time = datetime.combine(datetime(2023,2,21), time(16, 15))
    # Calculate how much minutes there are in the work day
    time_range_minutes = int((end_time - start_time).total_seconds() / 60)
    time_range_minutes = time_range_minutes - 45
    # Set the time threshold for downtime to 5 minutes (300 seconds)
    time_threshold = 300
    # Execute a SQL query to retrieve the timestamps of all the 1s from the sensor
    query = f"SELECT TS FROM kt_2010 WHERE message = 1 AND TS BETWEEN ? AND ? ORDER BY TS"
    cursor.execute(query, [start_time, end_time])
    # Initialize downtime counter
    downtime_counter = 0
    # Initialize previous timestamp variable
    prev_timestamp = None
    # Loop through the query results
    for row in cursor:
        # Extract the timestamp from the row
        timestampp = row[0]
        # If this is the first timestamp or if the previous timestamp was more than the time threshold
        if prev_timestamp is None or (timestampp - prev_timestamp).total_seconds() >= time_threshold:
            # Add the threshold to the downtime_counter
            downtime_counter += time_threshold
        # Otherwise: add the difference between the current timestamp and the previous one to the downtime counter
        else:
            downtime_counter += (timestampp - prev_timestamp).total_seconds()
        # This updates the prev_timestamp 
        prev_timestamp = timestampp
    # If the last '1' was seen before they day ends (16:15) add the remaining time until the day ends(16:15)
    if prev_timestamp is not None and (end_time - prev_timestamp).total_seconds() >= time_threshold:
        downtime_counter += (end_time - prev_timestamp).total_seconds()
    # Calculate the total downtime in minutes
    downtime_minutes = int(downtime_counter / 60 - 45)
    #Beschikbaarheid = str(int(downtime_counter / 60 - 45) / time_range_minutes * 100)
    Beschikbaarheid = str((downtime_counter / 60 - 45) / time_range_minutes * 100)
    Beschikbaarheid = Beschikbaarheid.split('.')
    Beschikbaarheid = Beschikbaarheid[0]
    #Beschikbaarheid = Beschikbaarheid_partss
    #print(f"Total downtime for kt_2010: {downtime_minutes} minutes out of {time_range_minutes} minutes")
    print(f'Beschikbaarheid: {Beschikbaarheid}')
    window['besch1'].update(Beschikbaarheid)
    return Beschikbaarheid

Frame_Row1 = [[sg.Frame("",[[sg.Text(list_devices[0], font=('Helvetica', 12))],[sg.Text(removePrefix(0,'2010 - '), font=('Helvetica', 12))],[sg.Text('', font=('Helvetica', 12), key='TS1')],[sg.Text('', font=('Helvetica', 12), key='kt_2010'), sg.Text('', font=('Helvetica', 12), key='besch1')],
                                  [sg.Graph(canvas_size=(350,100), graph_bottom_left=(0,0),graph_top_right=(100,50),enable_events=True,key='graph0')]],size=(350,250)),]]
layout = [Frame_Row1]

window= sg.Window('Status kantbanken', layout, size= (1900,800), element_justification='c', finalize=True, grab_anywhere=True)

stamptime = timeStamp()
diffStamp = timeStampDiff()
Circlecreation = createCircle()
timeDown = downtime()

#print(downtime(datetime(2023,2,21).date(),'kt_2010',1))
import threading
t = threading.Timer(2,timeDown)
t.start()

#timer_thread = threading.Thread(target=timeDown)
#timer_thread.start()
while True:
    event, values = window.read(timeout=500) # returns every 5sec
    if event == 'close' or event == sg.WIN_CLOSED:
        break
    # If timout=100 gets to 100 it will do the following actions: it wll refresh the latest timestamp for every table. It will also create a red or green circle (see function 'createCircle()')
    if event == sg.TIMEOUT_EVENT:
        # Updates the timestamp for 9 bending machines (the other ones dont have a sensor)
        timeStamp()
        # Creates a circle for 9 bending machines (the other ones dont have a sensor)
        createCircle()
    pass

# Close the cursor, connection and window
cursor.close()
cnxn.close()
window.close()

收到的错误信息:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Users\stagiair\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1038, in _bootstrap_inner
    self.run()
  File "C:\Users\stagiair\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1394, in run
    self.function(*self.args, **self.kwargs)
TypeError: 'str' object is not callable

解决方案

错误根源

你执行了timeDown = downtime(),这会直接调用downtime函数并将其返回值(一个字符串)赋值给timeDown。而threading.Timer的target参数需要传入可调用的函数对象,不是字符串,因此触发类型错误。

修复步骤

  1. 修正定时器参数
    先单独执行downtime()获取初始数据,然后将定时器的target设为函数对象downtime(不要加括号):

    # 先执行一次获取初始可用性数据
    downtime()
    # 启动定时器,传入函数对象而非执行结果
    import threading
    t = threading.Timer(2, downtime)
    t.start()
    
  2. 解决线程安全问题
    PySimpleGUI的GUI元素更新必须在主线程中进行,子线程直接调用window['besch1'].update()可能引发未知问题。修改downtime函数,通过事件通知主线程更新GUI:

    • 修改downtime函数的最后部分:
      # 替换原有的window['besch1'].update(Beschikbaarheid)
      window.write_event_value('-UPDATE_AVAILABILITY-', Beschikbaarheid)
      return Beschikbaarheid
      
    • 在主循环中处理该事件:
      while True:
          event, values = window.read(timeout=500)
          if event == 'close' or event == sg.WIN_CLOSED:
              break
          if event == sg.TIMEOUT_EVENT:
              timeStamp()
              createCircle()
          # 处理子线程发来的更新事件
          elif event == '-UPDATE_AVAILABILITY-':
              window['besch1'].update(values[event])
      

内容的提问来源于stack exchange,提问作者Kay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 18:09:21