Reputation: 9
I'm trying to run a GPIO emulator in Python on my M2 MacBook Air. The same application works fine on Windows, but will not run on my MacBook.
Here's the error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSWindow should only be instantiated on the main thread!
First throw call stack:
(
0 CoreFoundation 0x000000019692ee80 __exceptionPreprocess + 176
1 libobjc.A.dylib 0x0000000196416cd8 objc_exception_throw + 88
2 CoreFoundation 0x0000000196953534 _CFBundleGetValueForInfoKey + 0
3 AppKit 0x000000019a436c4c -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 260
4 AppKit 0x000000019a436b3c -[NSWindow initWithContentRect:styleMask:backing:defer:] + 48
5 libtk8.6.dylib 0x0000000101c67b5c TkMacOSXMakeRealWindowExist + 496
6 libtk8.6.dylib 0x0000000101c6785c TkWmMapWindow + 56
7 libtk8.6.dylib 0x0000000101bd8ffc MapFrame + 76
8 libtcl8.6.dylib 0x0000000101b0c47c TclServiceIdle + 84
9 libtcl8.6.dylib 0x0000000101af05d0 Tcl_DoOneEvent + 296
10 libtk8.6.dylib 0x0000000101c5b81c TkpInit + 712
11 libtk8.6.dylib 0x0000000101bd2030 Initialize + 2372
12 _tkinter.cpython-311-darwin.so 0x0000000100fe63bc Tcl_AppInit + 80
13 _tkinter.cpython-311-darwin.so 0x0000000100fe0888 Tkapp_New + 592
14 _tkinter.cpython-311-darwin.so 0x0000000100fe025c _tkinter_create + 608
15 Python 0x00000001015e3e18 cfunction_vectorcall_FASTCALL + 80
16 Python 0x0000000101674950 _PyEval_EvalFrameDefault + 40904
17 Python 0x0000000101678e38 _PyEval_Vector + 116
18 Python 0x0000000101598c0c _PyObject_FastCallDictTstate + 96
19 Python 0x0000000101602654 slot_tp_init + 188
20 Python 0x00000001015fab34 type_call + 136
21 Python 0x0000000101598964 _PyObject_MakeTpCall + 128
22 Python 0x0000000101674a8c _PyEval_EvalFrameDefault + 41220
23 Python 0x0000000101678e38 _PyEval_Vector + 116
24 Python 0x000000010159beb0 method_vectorcall + 384
25 Python 0x000000010172d728 thread_run + 168
26 Python 0x00000001016ce110 pythread_wrapper + 48
27 libsystem_pthread.dylib 0x00000001967d42e4 _pthread_start + 136
28 libsystem_pthread.dylib 0x00000001967cf0fc thread_start + 8
)
libc++abi: terminating due to uncaught exception of type NSException
Here's the code of the application:
EmulatorGUI.py:
from tkinter import *
import tkinter as tk
from PIN import PIN
from TypeChecker import typeassert
import threading
import time
#http://www.tutorialspoint.com/python/tk_button.htm
dictionaryPins = {}
dictionaryPinsTkinter = {}
GPIONames=["14","15","18","23","24","25","8","7","12","16","20","21","2","3","4","17","27","22","10","9","11","5","6","13","19","26"]
class App(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.start()
def callback(self):
self.root.quit()
def run(self):
self.root = tk.Tk()
self.root.wm_title("GPIO EMULATOR")
self.root.protocol("WM_DELETE_WINDOW", self.callback)
#5V
pin2label = Label(text="5V", fg="red")
pin2label.grid(row=0, column=0, padx=(10, 10))
#5V
pin4label = Label(text="5V", fg="red")
pin4label.grid(row=0, column=1, padx=(10, 10))
#GND
pin6label = Label(text="GND", fg="black")
pin6label.grid(row=0, column=2, padx=(10, 10))
#GPIO14
pin8btn = Button(text="GPIO14\nOUT=0", command="14", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin8btn.grid(row=0, column=3, padx=(10, 10),pady=(5,5))
dictionaryPinsTkinter["14"] = pin8btn
#GPIO15
pin10btn = Button(text="GPIO15\nOUT=0", command="15", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin10btn.grid(row=0, column=4, padx=(10, 10))
dictionaryPinsTkinter["15"] =pin10btn
#GPIO18
pin12btn = Button(text="GPIO18\nOUT=0", command="18", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin12btn.grid(row=0, column=5, padx=(10, 10))
dictionaryPinsTkinter["18"] = pin12btn
#GND
pin14label = Label(text="GND", fg="black")
pin14label.grid(row=0, column=6, padx=(10, 10))
#GPIO23
pin16btn = Button(text="GPIO23\nOUT=0", command="23", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin16btn.grid(row=0, column=7, padx=(10, 10))
dictionaryPinsTkinter["23"] = pin16btn
#GPIO24
pin18btn = Button(text="GPIO24\nOUT=0",command="24", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin18btn.grid(row=0, column=8, padx=(10, 10))
dictionaryPinsTkinter["24"] = pin18btn
#GND
pin20label = Label(text="GND", fg="black")
pin20label.grid(row=0, column=9, padx=(10, 10))
#GPIO25
pin22btn = Button(text="GPIO25\nOUT=0", command="25", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin22btn.grid(row=0, column=10, padx=(10, 10))
dictionaryPinsTkinter["25"] = pin22btn
#GPIO08
pin24btn = Button(text="GPIO8\nOUT=0", command="8", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin24btn.grid(row=0, column=11, padx=(10, 10))
dictionaryPinsTkinter["8"] = pin24btn
#GPIO07
pin26btn = Button(text="GPIO7\nOUT=0", command="7", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin26btn.grid(row=0, column=12, padx=(10, 10))
dictionaryPinsTkinter["7"] = pin26btn
#ID_SC
pin28label = Label(text="ID_SC", fg="black")
pin28label.grid(row=0, column=13, padx=(10, 10))
#GND
pin30label = Label(text="GND", fg="black")
pin30label.grid(row=0, column=14, padx=(10, 10))
#GPIO12
pin32btn = Button(text="GPIO12\nOUT=0", command="12", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin32btn.grid(row=0, column=15, padx=(10, 10))
dictionaryPinsTkinter["12"] = pin32btn
#GND
pin34label = Label(text="GND", fg="black")
pin34label.grid(row=0, column=16, padx=(10, 10))
#GPIO16
pin36btn = Button(text="GPIO16\nOUT=0", command="16", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin36btn.grid(row=0, column=17, padx=(10, 10))
dictionaryPinsTkinter["16"] = pin36btn
#GPIO20
pin38btn = Button(text="GPIO20\nOUT=0", command="20", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin38btn.grid(row=0, column=18, padx=(10, 10))
dictionaryPinsTkinter["20"] = pin38btn
#GPIO21
pin40btn = Button(text="GPIO21\nOUT=0", command="21", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin40btn.grid(row=0, column=19, padx=(10, 10))
dictionaryPinsTkinter["21"] = pin40btn
#####bottom
#3V3
pin1label = Label(text="3V3", fg="dark orange")
pin1label.grid(row=1, column=0, padx=(10, 10), pady=(5,5))
#GPIO02
pin03btn = Button(text="GPIO2\nOUT=0",command="2", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin03btn.grid(row=1, column=1, padx=(10, 10),pady=(5,5))
dictionaryPinsTkinter["2"] =pin03btn
#GPIO03
pin05btn = Button(text="GPIO3\nOUT=0", command="3", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin05btn.grid(row=1, column=2, padx=(10, 10))
dictionaryPinsTkinter["3"] = pin05btn
#GPIO04
pin07btn = Button(text="GPIO4\nOUT=0", command="4", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin07btn.grid(row=1, column=3, padx=(10, 10))
dictionaryPinsTkinter["4"] = pin07btn
#gnd
pin09label = Label(text="GND", fg="black")
pin09label.grid(row=1, column=4, padx=(10, 10))
#GPIO17
pin11btn = Button(text="GPIO17\nOUT=0", command="17", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin11btn.grid(row=1, column=5, padx=(10, 10))
dictionaryPinsTkinter["17"] = pin11btn
#GPIO27
pin13btn = Button(text="GPIO27\nOUT=0", command="27", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin13btn.grid(row=1, column=6, padx=(10, 10))
dictionaryPinsTkinter["27"] = pin13btn
#GPIO22
pin15btn = Button(text="GPIO22\nOUT=0", command="22", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin15btn.grid(row=1, column=7, padx=(10, 10))
dictionaryPinsTkinter["22"] = pin15btn
#3V3
pin17label = Label(text="3V3", fg="dark orange")
pin17label.grid(row=1, column=8, padx=(10, 10))
#GPIO10
pin19btn = Button(text="GPIO10\nOUT=0", command="10", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin19btn.grid(row=1, column=9, padx=(10, 10))
dictionaryPinsTkinter["10"] = pin19btn
#GPIO09
pin21btn = Button(text="GPIO9\nOUT=0", command="9", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin21btn.grid(row=1, column=10, padx=(10, 10))
dictionaryPinsTkinter["9"] = pin21btn
#GPIO11
pin23btn = Button(text="GPIO11\nOUT=0", command="11", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin23btn.grid(row=1, column=11, padx=(10, 10))
dictionaryPinsTkinter["11"] = pin23btn
#gnd
pin25label = Label(text="GND", fg="black")
pin25label.grid(row=1, column=12, padx=(10, 10))
#ID_SD
pin27label = Label(text="ID_SD", fg="black")
pin27label.grid(row=1, column=13, padx=(10, 10))
#GPIO05
pin29btn = Button(text="GPIO5\nOUT=0", command="5", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin29btn.grid(row=1, column=14, padx=(10, 10))
dictionaryPinsTkinter["5"] = pin29btn
#GPIO06
pin31btn = Button(text="GPIO6\nOUT=0", command="6", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin31btn.grid(row=1, column=15, padx=(10, 10))
dictionaryPinsTkinter["6"]=pin31btn
#GPIO13
pin33btn = Button(text="GPIO13\nOUT=0", command="13", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin33btn.grid(row=1, column=16, padx=(10, 10))
dictionaryPinsTkinter["13"] = pin33btn
#GPIO19
pin35btn = Button(text="GPIO19\nOUT=0", command="19", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin35btn.grid(row=1, column=17, padx=(10, 10))
dictionaryPinsTkinter["19"] = pin35btn
#GPIO26
pin37btn = Button(text="GPIO26\nOUT=0", command="26", padx ="1px", pady="1px", bd="0px", fg="blue", relief="sunken", activeforeground="blue")
pin37btn.grid(row=1, column=18, padx=(10, 10))
dictionaryPinsTkinter["26"] = pin37btn
#gnd
pin39label = Label(text="GND", fg="black")
pin39label.grid(row=1, column=19, padx=(10, 10))
self.root.geometry('%dx%d+%d+%d' % (1300, 100, 0, 0))
self.root.mainloop()
## button1.unbind("<Button-1>")
app = App()
def toggleButton(gpioID):
#print(gpioID)
objBtn = dictionaryPinsTkinter[str(gpioID)]
objPin = dictionaryPins[str(gpioID)]
if(objPin.In == "1"):
objPin.In = "0"
elif(objPin.In == "0"):
objPin.In = "1"
objBtn["text"] = "GPIO" + str(gpioID) + "\nIN=" + str(objPin.In)
def buttonClick(self):
## print("clicked")
gpioID = (self.widget.config('command')[-1])
toggleButton(gpioID)
def buttonClickRelease(self):
## print("released")
gpioID = (self.widget.config('command')[-1])
toggleButton(gpioID)
def drawGPIOOut(gpioID):
global dictionaryPins
global dictionaryPinsTkinter
gpioID = str(gpioID)
objPin = dictionaryPins[gpioID]
objBtn = dictionaryPinsTkinter[gpioID]
if(objPin.SetMode == "OUT"):
objBtn["text"] = "GPIO" + str(gpioID) + "\nOUT=" + str(objPin.Out)
if(str(objPin.Out) == "1"):
objBtn.configure(background='tan2')
objBtn.configure(activebackground='tan2')
else:
objBtn.configure(background='DarkOliveGreen3')
objBtn.configure(activebackground='DarkOliveGreen3')
def drawBindUpdateButtonIn(gpioID,In):
objBtn = dictionaryPinsTkinter[gpioID]
objBtn.configure(background='gainsboro')
objBtn.configure(activebackground='gainsboro')
objBtn.configure(relief='raised')
objBtn.configure(bd="1px")
objBtn["text"] = "GPIO" + str(gpioID) + "\nIN=" + str(In)
objBtn.bind("<Button-1>", buttonClick)
objBtn.bind("<ButtonRelease-1>", buttonClickRelease)
class GPIO:
#constants
LOW = 0
HIGH = 1
OUT = 2
IN = 3
PUD_OFF = 4
PUD_DOWN = 5
PUD_UP = 6
BCM = 7
#flags
setModeDone = False
#Extra functions
def checkModeValidator():
if(GPIO.setModeDone == False):
raise Exception('Setup your GPIO mode. Must be set to BCM')
#GPIO LIBRARY Functions
@typeassert(int)
def setmode(mode):
time.sleep(1)
if(mode == GPIO.BCM):
GPIO.setModeDone = True
else:
GPIO.setModeDone = False
@typeassert(bool)
def setwarnings(flag):
pass
@typeassert(int,int,int,int)
def setup(channel, state, initial=-1,pull_up_down=-1):
global dictionaryPins
GPIO.checkModeValidator()
if str(channel) not in GPIONames:
raise Exception('GPIO ' + str(channel) + ' does not exist')
#check if channel is already setup
if str(channel) in dictionaryPins:
raise Exception('GPIO is already setup')
if(state == GPIO.OUT):
#GPIO is set as output, default OUT 0
objTemp = PIN("OUT")
if(initial == GPIO.HIGH):
objTemp.Out = "1"
dictionaryPins[str(channel)] =objTemp
drawGPIOOut(channel)
elif(state == GPIO.IN):
#set input
objTemp = PIN("IN")
if(pull_up_down == -1):
objTemp.pull_up_down = "PUD_DOWN" #by default pud_down
objTemp.In = "0"
elif(pull_up_down == GPIO.PUD_DOWN):
objTemp.pull_up_down = "PUD_DOWN"
objTemp.In = "0"
elif(pull_up_down == GPIO.PUD_UP):
objTemp.pull_up_down = "PUD_UP"
objTemp.In = "1"
drawBindUpdateButtonIn(str(channel),objTemp.In)
dictionaryPins[str(channel)] =objTemp
@typeassert(int,int)
def output(channel, outmode):
global dictionaryPins
channel = str(channel)
GPIO.checkModeValidator()
if channel not in dictionaryPins:
#if channel is not setup
raise Exception('GPIO must be setup before used')
else:
objPin = dictionaryPins[channel]
if(objPin.SetMode == "IN"):
#if channel is setup as IN and used as an OUTPUT
raise Exception('GPIO must be setup as OUT')
if(outmode != GPIO.LOW and outmode != GPIO.HIGH):
raise Exception('Output must be set to HIGH/LOW')
objPin = dictionaryPins[channel]
if(outmode == GPIO.LOW):
objPin.Out = "0"
elif(outmode == GPIO.HIGH):
objPin.Out = "1"
drawGPIOOut(channel)
@typeassert(int)
def input(channel):
global dictionaryPins
channel = str(channel)
GPIO.checkModeValidator()
if channel not in dictionaryPins:
#if channel is not setup
raise Exception('GPIO must be setup before used')
else:
objPin = dictionaryPins[channel]
if(objPin.SetMode == "OUT"):
#if channel is setup as OUTPUT and used as an INPUT
raise Exception('GPIO must be setup as IN')
objPin = dictionaryPins[channel]
if(objPin.In == "1"):
return True
elif(objPin.Out == "0"):
return False
def cleanup():
pass
PIN.py:
class PIN():
SetMode = "None" #IN/OUT/NONE
Out = "0"
pull_up_down = "PUD_OFF" #PUD_UP/PUD_DOWN/PUD_OFF
In = "1"
def __init__(self, SetMode):
self.SetMode = SetMode
self.Out = "0"
Test.py:
from EmulatorGUI import GPIO
# import RPi.GPIO as GPIO
import time
import traceback
def main():
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(4, GPIO.OUT)
GPIO.setup(17, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(18, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(21, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(15, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
while True:
if not GPIO.input(23):
GPIO.output(4, GPIO.HIGH)
GPIO.output(17, GPIO.HIGH)
time.sleep(1)
if GPIO.input(15):
GPIO.output(18, GPIO.HIGH)
GPIO.output(21, GPIO.HIGH)
time.sleep(1)
if GPIO.input(24):
GPIO.output(18, GPIO.LOW)
GPIO.output(21, GPIO.LOW)
time.sleep(1)
if GPIO.input(26):
GPIO.output(4, GPIO.LOW)
GPIO.output(17, GPIO.LOW)
time.sleep(1)
GPIO.cleanup() # this ensures a clean exit
main()
TypeChecker.py:
from inspect import signature
from functools import wraps
def typeassert(*ty_args, **ty_kwargs):
def decorate(func):
# If in optimized mode, disable type checking
if not __debug__:
return func
# Map function argument names to supplied types
sig = signature(func)
bound_types = sig.bind_partial(*ty_args, **ty_kwargs).arguments
@wraps(func)
def wrapper(*args, **kwargs):
bound_values = sig.bind(*args, **kwargs)
# Enforce type assertions across supplied arguments
for name, value in bound_values.arguments.items():
if name in bound_types:
if not isinstance(value, bound_types[name]):
raise TypeError(
'Argument {} must be {}'.format(name, bound_types[name])
)
return func(*args, **kwargs)
return wrapper
return decorate
The program runs from Test.py. I'm using Python 3.11.1 installed from Brew. Thanks in advance.
Upvotes: -1
Views: 23