why a thread does not end - python-3.x

I'm trying to make an app to interact with an usb rfid reader on a Raspberry pi.
it looks like the solution is using the trheading module (otherwise the data entry leads to a disfunction)
After some essays I made an app that works... the first time. After that, a message error says "threads can only be started once"... so the interpretation is that the thread does not end naturally...why??
import time
import sys
import os
from tkinter import *
from tkinter.ttk import *
import threading
def activa_fil(self):
print ('activant fil')
fil.start()
def prova2():
print('hola')
print(Control.get())
print(NumCorr.get())
print(time.strftime("%H:%M:%S"))
DisplayCorr.config(text=NumCorr.get())
Llistacorr.insert(0, (Control.get(),NumCorr.get(), time.strftime("%H:%M:%S")))
marc.contador +=1
Contador.config (text=marc.contador)
print(marc.contador)
file = open("prova.txt", "a")
file.write(Control.get())
file.write(NumCorr.get())
file.write (time.strftime("%H:%M:%S"))
file.write('\n')
file.close()
NumCorr.after(500, NumCorr.delete(0,END))
def tick():
time_string = time.strftime("%H:%M:%S")
Hora.config(text=time_string)
Hora.after(200, tick)
def canvia_focus(self):
NumCorr.focus()
fil=threading.Timer(0.5, prova2)
marc = Tk()
marc.geometry('480x320')
marc.bind_all(''<'Key-Return'>'', activa_fil)
marc.contador =0
Control = Combobox(marc)
Control['values']=('sortida','can cuias','can campanya','papiol','sortida papiol', 'can olivero', 'ullastrell', 'coll olesa','pla fideuer','aeri', 'arribada')
Control.config(font="helvetica 30", )
Control.grid(column='0', row='0', columnspan='2')
Llistacorr = Listbox(marc)
Llistacorr.config(font='arial')
Llistacorr.grid_propagate(0)
Llistacorr.grid(column='0', row='1', rowspan='3')
DisplayCorr=Label(marc, font='Arial 10')
DisplayCorr.grid(column='1', row='1')
NumCorr = Entry(marc)
NumCorr.config(font='helvetica 6')
Control.bind('<<ComboboxSelected>>', canvia_focus)
NumCorr.grid(column='0', row='4', columnspan='2')
Contador=Label(marc, font='arial 30')
Contador.grid(column='1', row='2')
Hora=Label(marc, font='arial 30')
Hora.grid(column='1', row='3')
tick()
marc.mainloop()

Related

How to control the behaviour of QGridLayout

Using PyQT6
I am trying to create and app that adds a new frame (stack1) whenever the check box is clicked. It all works well until the app hides the stack when you untick the checkbox.
Clearly what I'd like is for the app to return to the initial shape when I hide the stack. I am aware that since I have added a column to the QTGrid,the window has likely reshaped taking into account the extra space. I am getting the same behaviour using deleteLater. So my question is how can I avoid that? Is there a way to reset the gridLayout to go back to just 1 column?
Here is a reproducible example:
import sys
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
from PyQt6.QtWidgets import QWidget, QApplication, QVBoxLayout, QStackedWidget,
QLabel, QPushButton
import sys
from PyQt6.QtGui import QIcon
from PyQt6 import QtGui
class Window(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("Anticoagulant Dose calculator")
self.buttons = {'metrics': ['Age?', 'Height in cm?', 'Serum Creatinine?', 'Weight in kg?', 'Sex?'],
'anticoagulant': ['DOAC', 'warfarin, \nacenocumarol'],
'indication': ['VTE', 'Atrial Fibrilation', 'Mechanical Heart valve'],
'VTE': ['single VTE > 12 months', 'VTE 3-12 months or \n multiple, large volume PE or \n active cancer', 'VTE < 3 months \n or known antithrombin deficiency or \n antiphospholipid syndrome'],
'af': ['No',"Yes, within the past 12 months","Yes, within the past 3 months"],
'Mech': 'mech',
'sexo': ['Male', 'Female']}
self.dialogLayout = QVBoxLayout()
self.anticoagLayout = QVBoxLayout()
self.rejillaLayout = QGridLayout()
self.anticoagButtongroup = QButtonGroup()
self.indicationButtongroup = QButtonGroup()
self.UIcomponents()
self.setLayout(self.rejillaLayout)
self.stack1 = QWidget()
self.stack1UI()
def stack1UI(self):
for a in self.buttons['anticoagulant']:
self.anticoag_rb = QRadioButton(a)
self.anticoagButtongroup.addButton(self.anticoag_rb)
self.dialogLayout.addWidget(self.anticoag_rb)
for i in self.buttons['indication']:
self.indication_rb = QRadioButton(i)
self.indicationButtongroup.addButton(self.indication_rb)
self.dialogLayout.addWidget(self.indication_rb)
self.stack1.setLayout(self.dialogLayout)
def cb_onclicked(self):
self.cbox = self.bridgingCheckBox.sender()
try:
if self.cbox.isChecked():
print('Puenteando')
self.rejillaLayout.addWidget(self.stack1, 0, 2)
self.stack1.show()
else:
self.stack1.deleteLater()
print('No puenteando')
except RuntimeError:
pass
def UIcomponents(self):
self.entries = {}
formas = QFormLayout()
for m in self.buttons['metrics']:
if m != 'Sex?':
self.ent = QLineEdit()
formas.addRow(m, self.ent)
else:
print('Sexo?')
self.sComboBox = QComboBox()
self.sComboBox.addItems(self.buttons['sexo'])
sLabel = QLabel('Sex:?')
sLabel.setBuddy(self.sComboBox)
formas.addRow(sLabel, self.sComboBox)
self.entries[m] = self.ent
self.bridgingCheckBox = QCheckBox("Bridging dose")
self.bridgingCheckBox.toggled.connect(self.cb_onclicked)
formas.addRow(self.bridgingCheckBox, None)
self.rejillaLayout.addLayout(formas, 0, 0)
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.setGeometry(50, 50, 300, 160)
window.show()
sys.exit(app.exec())
Many thanks in advance.
Thanks yo #ekhumoro for the answer:
gridding the stack at the init stage then hiding it, and then adding the code to the checkbox connector does it.
def cb_onclicked(self):
self.cbox = self.bridgingCheckBox.sender()
self.stack1.setVisible(self.bridgingCheckBox.isChecked())
self.adjustSize()

Rotary encoder value looking sticky after using tkinter function " root.after"

I am quite new to python and tkinter ,I am working on a project where i give demand to slave device through rotary encoder value and reading feedback value from slave devices through communication.
I face issue when i use root.after () function .
Any suggestion regarding proper use of root.after () function so that my interrupt routine not affected welcome heartly.
Thank you.
Here is my code.
import pigpio
import tkinter as tk
from PIL import ImageTk,Image
import time
from RPi import GPIO
import serial
i="*00T%"
j="*01T%"
s=serial.Serial(port='/dev/ttyS0',
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
class decoder: # class for decoding rotary encoder
def __init__(self, pi, gpioA, gpioB, callback):
----------
---------------
----------------
if __name__ == "__main__":
import RPi.GPIO as GPIO
import time
import pigpio
import tkinter as tk
import board
import busio
rpm=0
tor=0
pow=0
pi = pigpio.pi()
st=1
pos=0
def callback(way): # interrupt event sense on pin no 17,18
global pos
global st
if st ==1:
pos += way
if pos >= 9999:
pos=9999
if pos <= 0:
pos=0
var.set(pos)
print("pos={}".format(pos))
def rpm_update():
global rpm
s.write(str.encode(i))
print(i)
time.sleep(0.2)
if s.inWaiting():
rpm=s.readline(s.inWaiting())
rpm=rpm.decode('utf-8',errors='ignore')
rpm=rpm[5:-1]
print(rpm)
var2.set(rpm)
def tor_update():
global tor
s.write(str.encode(j))
print(j)
time.sleep(0.2)
if s.inWaiting():
tor=s.readline(s.inWaiting())
tor=tor.decode('utf-8',errors='ignore')
tor=tor[4:-1]
print(tor)
var3.set(tor)
def pow_update():
global rpm,tor,pow
try:
rpm=float(rpm)
tor=float(tor)
except ValueError:
pass
pow=int(rpm*tor/5252)
var4.set(pow)
def update():
rpm_update()
time.sleep(0.5)
tor_update()
time.sleep(0.5)
pow_update()
root.after(1000,update)
path="/home/pi/logo.png"
root=tk.Tk()
img=ImageTk.PhotoImage(Image.open(path))
panel=tk.Label(root,image=img)
panel.pack()
panel.place(x=195,y=10,height=50,width=80)
root.title("Dynalec")
root.geometry("500x600")
{
body of tkinter screen
}
decoder=decoder(pi, 17, 18, callback)
root.after(1000,update)
root.mainloop()
everything is working fine before root.after() function " root.after(1000,update)
but i need it because i have to read slave values in every sec and update in gui screen.
Adding as a answer because long for comments, will remove soon.
Try this out:
def update():
root.after(500,rpm_update)
root.after(500,tor_update)
root.after(500,pow_update)
root.after(1000,update)
Do let me know.

running 4 functions periodically in the background without freezing the GUI

I made a pyqt5 script which uses python3.6
my scripts takes input from serial device and updates the GUI and the remote database and also take remote input from web browser and make necessary changes to the serial device and update the GUI accordingly.
I am using QTimer to run the 4 functions/methods in the background to achieve this and it's working. But the problem is that after receiving a manual input or remote input (from web browser) it takes almost 15-20 seconds to implement the necessary changes in the serial device and the GUI and it makes the GUI almost unresponsive (it responds after almost 15-20 seconds).
I tried QThread but it always results in multiple access on the serial port at the same time causing the script to crash. Please, help me in this regard.
from PyQt5.QtCore import QTimer, QThread
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.uic import loadUiType
import os
import sys
import time
import serial
import MySQLdb
from pathlib import Path
import json
ui, _ = loadUiType('myapplication.ui')
class MainApp(QMainWindow, ui):
def __init__(self):
QMainWindow.__init__(self)
self.setupUi(self)
def new_method1(self):
print("This is new_method1")
def new_method2(self):
print("This is new_method2")
def new_method3(self):
print("This is new_method3")
def new_method4(self):
print("This is new_method4")
def continuous_calling(self):
self.timer = QTimer()
self.timer.timeout.connect(self.new_method1)
self.timer.start(1000)
self.timer.timeout.connect(self.new_method2)
self.timer.start(1000)
self.timer.timeout.connect(self.new_method3)
self.timer.start(1000)
self.timer.timeout.connect(self.new_method4)
self.timer.start(1000)
def main():
app = QApplication(sys.argv)
window = MainApp()
window.move(0, -30)
window.show()
window.continuous_calling()
app.exec_()
if __name__ == '__main__':
main()
Here, i want to run new_method1, new_method2, new_method3 & new_method4 methods continuously in the background as long as the GUI is not exiting without freezing the GUI. Thanks.
additional information::
This is an example of other functions that manages direct user inputs from GUI(button clicks from the GUI). And these functions don't run in the background repeatedly. These functions are triggered only when buttons(in the GUI) are clicked.
def ser_conn_on_zero(self):
self.ser = serial.Serial(
port=self.comm_port,
baudrate=9600,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_EVEN,
stopbits=serial.STOPBITS_ONE,
timeout=1)
if self.ser.isOpen():
print("Open: " + self.ser.name)
self.ser.flushInput()
self.ser.flushOutput()
self.ser.close()
self.ser.open()
print('Connected to :' + self.comm_port)
print("Turning ON Relay Zero")
self.ser.write(b'\x46\x6D\x45\x63\x73\x4B\x6F\x6C\x00\x05\x00\x01\x00\x01\x00\r\n')
output = self.ser.readline().decode('iso-8859-1')
print("Serial Response: " + output)
self.ser.close()
print("Serial connection Closed for Relay Zero")
conn = MySQLdb.connect("XX.XX.XX.XX", "User2", "PASSWORD", "TestSite_001", use_unicode=True, charset="utf8")
cursor = conn.cursor()
try:
cursor.execute("update some_table set chn = %s where id = %s", (1, 1))
conn.commit()
except MySQLdb.Error as e:
print("ser_conn_on_zero: couldn't update the remote database : ", str(e))
cursor.close()
conn.close()

How would I constantly update a variable within a script/program?

I am attempting to make a thermostat of sorts. To do this, I am using a Pi3 with a DHT22 Temperature sensor, and Python3.
What I need is for the temperature to be polled and the corresponding variable to update on its own.
Attempting to do so with any sort of While True: statements results in the gui I'm testing with, not opening.
I'm lost (And yes, this code is hacked together from others. LOL)
#! python3
import time
import RPi.GPIO as GPIO
import string
import tkinter
import tkinter.ttk
import Adafruit_DHT
from tkinter import messagebox
from tkinter import *
root = Tk()
root.title('PiTEST')
root.configure(background='black')
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
sensor = Adafruit_DHT.DHT22
pin = 4
def PRINTTEST():
print(temperature, humidity)
TESTTEXT = Label(root,text="TESTING",fg="white",bg="black",font='Consolas 20 bold')
TESTTEXT.grid(row=1,column=1,sticky="W,S,E")
B1 = tkinter.Button(root,bd=5,text="TEST",bg="gray",fg="white",command=PRINTTEST,height=4,width=20)
B1.grid(row=2,column=1,sticky="N,S,E,W",padx=8,pady=8)
while True:
humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)
temperature = temperature * 9/5.0 + 32
root.mainloop()
GPIO.cleanup()
Here is a code example without your GPIO things:
#! python3
import time
import string
import tkinter
import random
from tkinter import messagebox
from tkinter import *
root = Tk()
root.title('PiTEST')
root.configure(background='black')
def PRINTTEST():
temperature = random.randint(0,100)
humidity = random.randint(0,100)
print(temperature, humidity)
root.after(1000, PRINTTEST)
TESTTEXT = Label(root,text="TESTING",fg="white",bg="black",font='Consolas 20 bold')
TESTTEXT.grid(row=1,column=1,sticky="W,S,E")
B1 = tkinter.Button(root,bd=5,text="TEST",bg="gray",fg="white",command=PRINTTEST,height=4,width=20)
B1.grid(row=2,column=1,sticky="N,S,E,W",padx=8,pady=8)
root.mainloop()
This will print 2 random integers every second in your terminal.

prompt toolkit run functions simultaneously

I am trying to run the testprt() function simultaneously with the prompt_toolkit but if I am trying it normally its waiting and after the testprt() functions is run the menu starts but I want it too start simultaneously. I tried it with normal threading and starting the thread in the run function. (This is the code without my threading code) how do I run the testprt() function simultaneously?
from __future__ import unicode_literals
from prompt_toolkit.application import Application
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout.containers import VSplit, HSplit, Window
from prompt_toolkit.layout.controls import FormattedTextControl, BufferControl
from prompt_toolkit.layout.layout import Layout
from prompt_toolkit.buffer import Buffer
from prompt_toolkit import prompt
from prompt_toolkit.eventloop.defaults import use_asyncio_event_loop
from prompt_toolkit.patch_stdout import patch_stdout
import threading
import time
## LOGIC
def testprt():
up_r_buffer.text = "1\n"
time.sleep(1)
up_r_buffer.text = "2\n"
time.sleep(1)
up_r_buffer.text = "3\n"
time.sleep(1)
up_r_buffer.text = "4\n"
time.sleep(1)
up_r_buffer.text = "5\n"
time.sleep(1)
up_r_buffer = Buffer()
up_l_buffer = Buffer()
down_buffer = Buffer()
# 1. The layout
top_text = (
"Menu.\n"
"[q] Quit [TAB] To switch."
)
left_top = Window(BufferControl(buffer=up_l_buffer))
right_top = Window(BufferControl(buffer=up_r_buffer))
down_buffer = Window(BufferControl(buffer=down_buffer))
body = HSplit([
Window(FormattedTextControl(top_text), height=2, style='reverse'),
Window(height=1, char='-'), # Horizontal line in the middle.
VSplit([
left_top,
Window(width=1, char='|'),
right_top
]),
Window(height=1, char='-'), # Horizontal line in the middle.
VSplit([
down_buffer
]),
])
# 2. Key bindings
kb = KeyBindings()
#kb.add('q')
def _(event):
" Quit application. "
event.app.exit()
#kb.add('tab')
def _(event):
event.app.layout.focus_next()
#kb.add('s-tab')
def _(event):
event.app.layout.focus_previous()
#kb.add('m')
def _(event):
up_r_buffer.text = "FuckOFF\n"
# 3. The `Application`
application = Application(
layout=Layout(body),
key_bindings=kb,
full_screen=True,
)
def run():
application.run()
if __name__ == '__main__':
run()

Resources