How to scroll text in terminal window with Python/Curses? [duplicate] - python

In my Python script which uses Curses, I have a subwin to which some text is assigned. Because the text length may be longer than the window size, the text should be scrollable.
It doesn't seem that there is any CSS-"overflow" like attribute for Curses windows. The Python/Curses docs are also rather cryptic on this aspect.
Does anybody here have an idea how I can code a scrollable Curses subwindow using Python and actually scroll through it?
\edit: more precise question

OK with window.scroll it was too complicated to move the content of the window. Instead, curses.newpad did it for me.
Create a pad:
mypad = curses.newpad(40,60)
mypad_pos = 0
mypad.refresh(mypad_pos, 0, 5, 5, 10, 60)
Then you can scroll by increasing/decreasing mypad_pos depending on the input from window.getch() in cmd:
if cmd == curses.KEY_DOWN:
mypad_pos += 1
mypad.refresh(mypad_pos, 0, 5, 5, 10, 60)
elif cmd == curses.KEY_UP:
mypad_pos -= 1
mypad.refresh(mypad_pos, 0, 5, 5, 10, 60)

Right, I was a bit confused on how to utilize pads (in order to scroll text), and still couldn't figure it out after reading this post; especially since I wanted to use it in a context of the content being an existing "array of lines". So I prepared a small example, that shows similarities (and differences) between newpad and subpad:
#!/usr/bin/env python2.7
import curses
# content - array of lines (list)
mylines = ["Line {0} ".format(id)*3 for id in range(1,11)]
import pprint
pprint.pprint(mylines)
def main(stdscr):
hlines = begin_y = begin_x = 5 ; wcols = 10
# calculate total content size
padhlines = len(mylines)
padwcols = 0
for line in mylines:
if len(line) > padwcols: padwcols = len(line)
padhlines += 2 ; padwcols += 2 # allow border
stdscr.addstr("padhlines "+str(padhlines)+" padwcols "+str(padwcols)+"; ")
# both newpad and subpad are <class '_curses.curses window'>:
mypadn = curses.newpad(padhlines, padwcols)
mypads = stdscr.subpad(padhlines, padwcols, begin_y, begin_x+padwcols+4)
stdscr.addstr(str(type(mypadn))+" "+str(type(mypads)) + "\n")
mypadn.scrollok(1)
mypadn.idlok(1)
mypads.scrollok(1)
mypads.idlok(1)
mypadn.border(0) # first ...
mypads.border(0) # ... border
for line in mylines:
mypadn.addstr(padhlines-1,1, line)
mypadn.scroll(1)
mypads.addstr(padhlines-1,1, line)
mypads.scroll(1)
mypadn.border(0) # second ...
mypads.border(0) # ... border
# refresh parent first, to render the texts on top
#~ stdscr.refresh()
# refresh the pads next
mypadn.refresh(0,0, begin_y,begin_x, begin_y+hlines, begin_x+padwcols)
mypads.refresh()
mypads.touchwin()
mypadn.touchwin()
stdscr.touchwin() # no real effect here
#stdscr.refresh() # not here! overwrites newpad!
mypadn.getch()
# even THIS command erases newpad!
# (unless stdscr.refresh() previously):
stdscr.getch()
curses.wrapper(main)
When you run this, at first you will get something like (newpad left, subpad right):
┌────────────────────────┐ ┌────────────────────────┐
│Line 1 Line 1 Line 1 ───│ │Line 1 Line 1 Line 1 ───│
│Line 2 Line 2 Line 2 │ │Line 2 Line 2 Line 2 │
│Line 3 Line 3 Line 3 │ │Line 3 Line 3 Line 3 │
│Line 4 Line 4 Line 4 │ │Line 4 Line 4 Line 4 │
│Line 5 Line 5 Line 5 │ │Line 5 Line 5 Line 5 │
│Line 6 Line 6 Line 6 │
│Line 7 Line 7 Line 7 │
│Line 8 Line 8 Line 8 │
│Line 9 Line 9 Line 9 │
│Line 10 Line 10 Line 10 │
└────────────────────────┘
Some notes:
Both newpad and subpad should have their width/height sized to the content (num lines/max line width of the array of lines) + eventual border space
In both cases, you could allow extra lines with scrollok() - but not extra width
In both cases, you basically "push" a line at the bottom of the pad; and then scroll() up to make room for the next
The special refresh method that newpad has, then allows for just a region of this "whole content" to be shown on screen; subpad more-less has to be shown in the size it was instantiated in
If you draw the borders of the pads before adding content strings - then the borders will scroll up too (that is the ─── piece shown at the ...Line 1 ───│ part).
Useful links:
(n)curses pad in python not working
http://threeseas.net/vic/vic/terminal.py
How do I delete a curse window in python and restore background window?

Set the window.scrollok(True).
Documentation

I wanted to use a scrolling pad to display content of some large text files but this didn't work well because texts can have line breaks and it was pretty hard to figure out how many characters to display at a time to fit the good number of columns and rows.
So I decided to first split my text files in lines of exactly COLUMNS characters, padding with spaces when lines were too short. Then scrolling the text become more easy.
Here is a sample code to display any text file:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import curses
import locale
import sys
def main(filename, filecontent, encoding="utf-8"):
try:
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
curses.curs_set(0)
stdscr.keypad(1)
rows, columns = stdscr.getmaxyx()
stdscr.border()
bottom_menu = u"(↓) Next line | (↑) Previous line | (→) Next page | (←) Previous page | (q) Quit".encode(encoding).center(columns - 4)
stdscr.addstr(rows - 1, 2, bottom_menu, curses.A_REVERSE)
out = stdscr.subwin(rows - 2, columns - 2, 1, 1)
out_rows, out_columns = out.getmaxyx()
out_rows -= 1
lines = map(lambda x: x + " " * (out_columns - len(x)), reduce(lambda x, y: x + y, [[x[i:i+out_columns] for i in xrange(0, len(x), out_columns)] for x in filecontent.expandtabs(4).splitlines()]))
stdscr.refresh()
line = 0
while 1:
top_menu = (u"Lines %d to %d of %d of %s" % (line + 1, min(len(lines), line + out_rows), len(lines), filename)).encode(encoding).center(columns - 4)
stdscr.addstr(0, 2, top_menu, curses.A_REVERSE)
out.addstr(0, 0, "".join(lines[line:line+out_rows]))
stdscr.refresh()
out.refresh()
c = stdscr.getch()
if c == ord("q"):
break
elif c == curses.KEY_DOWN:
if len(lines) - line > out_rows:
line += 1
elif c == curses.KEY_UP:
if line > 0:
line -= 1
elif c == curses.KEY_RIGHT:
if len(lines) - line >= 2 * out_rows:
line += out_rows
elif c == curses.KEY_LEFT:
if line >= out_rows:
line -= out_rows
finally:
curses.nocbreak(); stdscr.keypad(0); curses.echo(); curses.curs_set(1)
curses.endwin()
if __name__ == '__main__':
locale.setlocale(locale.LC_ALL, '')
encoding = locale.getpreferredencoding()
try:
filename = sys.argv[1]
except:
print "Usage: python %s FILENAME" % __file__
else:
try:
with open(filename) as f:
filecontent = f.read()
except:
print "Unable to open file %s" % filename
else:
main(filename, filecontent, encoding)
The main trick is the line:
lines = map(lambda x: x + " " * (out_columns - len(x)), reduce(lambda x, y: x + y, [[x[i:i+out_columns] for i in xrange(0, len(x), out_columns)] for x in filecontent.expandtabs(4).splitlines()]))
First, tabulations in the text are converted to spaces, then I used splitlines() method to convert my text in array of lines.
But some lines may be longer than our COLUMNS number, so I splitted each line in chunk of COLUMNS characters and then used reduce to transform the resulting list in a list of lines.
Finally, I used map to pad each line with trailing spaces so that its length is exactly COLUMNS characters.
Hope this helps.

This is the answer of this question:
How to make a scrolling menu in python-curses
This code allows you to create a little scrolling menu in a box from a list of strings.
You can also use this code getting the list of strings from a sqlite query or from a csv file.
To edit the max number of rows of the menu you just have to edit max_row.
If you press enter the program will print the selected string value and its position.
from __future__ import division #You don't need this in Python3
import curses
from math import *
screen = curses.initscr()
curses.noecho()
curses.cbreak()
curses.start_color()
screen.keypad( 1 )
curses.init_pair(1,curses.COLOR_BLACK, curses.COLOR_CYAN)
highlightText = curses.color_pair( 1 )
normalText = curses.A_NORMAL
screen.border( 0 )
curses.curs_set( 0 )
max_row = 10 #max number of rows
box = curses.newwin( max_row + 2, 64, 1, 1 )
box.box()
strings = [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "l", "m", "n" ] #list of strings
row_num = len( strings )
pages = int( ceil( row_num / max_row ) )
position = 1
page = 1
for i in range( 1, max_row + 1 ):
if row_num == 0:
box.addstr( 1, 1, "There aren't strings", highlightText )
else:
if (i == position):
box.addstr( i, 2, str( i ) + " - " + strings[ i - 1 ], highlightText )
else:
box.addstr( i, 2, str( i ) + " - " + strings[ i - 1 ], normalText )
if i == row_num:
break
screen.refresh()
box.refresh()
x = screen.getch()
while x != 27:
if x == curses.KEY_DOWN:
if page == 1:
if position < i:
position = position + 1
else:
if pages > 1:
page = page + 1
position = 1 + ( max_row * ( page - 1 ) )
elif page == pages:
if position < row_num:
position = position + 1
else:
if position < max_row + ( max_row * ( page - 1 ) ):
position = position + 1
else:
page = page + 1
position = 1 + ( max_row * ( page - 1 ) )
if x == curses.KEY_UP:
if page == 1:
if position > 1:
position = position - 1
else:
if position > ( 1 + ( max_row * ( page - 1 ) ) ):
position = position - 1
else:
page = page - 1
position = max_row + ( max_row * ( page - 1 ) )
if x == curses.KEY_LEFT:
if page > 1:
page = page - 1
position = 1 + ( max_row * ( page - 1 ) )
if x == curses.KEY_RIGHT:
if page < pages:
page = page + 1
position = ( 1 + ( max_row * ( page - 1 ) ) )
if x == ord( "\n" ) and row_num != 0:
screen.erase()
screen.border( 0 )
screen.addstr( 14, 3, "YOU HAVE PRESSED '" + strings[ position - 1 ] + "' ON POSITION " + str( position ) )
box.erase()
screen.border( 0 )
box.border( 0 )
for i in range( 1 + ( max_row * ( page - 1 ) ), max_row + 1 + ( max_row * ( page - 1 ) ) ):
if row_num == 0:
box.addstr( 1, 1, "There aren't strings", highlightText )
else:
if ( i + ( max_row * ( page - 1 ) ) == position + ( max_row * ( page - 1 ) ) ):
box.addstr( i - ( max_row * ( page - 1 ) ), 2, str( i ) + " - " + strings[ i - 1 ], highlightText )
else:
box.addstr( i - ( max_row * ( page - 1 ) ), 2, str( i ) + " - " + strings[ i - 1 ], normalText )
if i == row_num:
break
screen.refresh()
box.refresh()
x = screen.getch()
curses.endwin()
exit()

Another way is to print the visible items using for-loop on list with slice notation. That is, you choose a specific part of the list to display, then add or subtract an increment to change the range every time you press a key up or down.
like list[ y : y + coverage ] where y is the start and coverage determines how many items in the list you'd like to display.
from curses import wrapper
import curses
def main(stdscr):
mY = curses.LINES
win = curses.newwin(100,50,0,50)
win.keypad(True)
numbers = [n for n in range(0,1001)]
ylen = len(numbers)
iny = 0
border_y = mY-5
def scroll(window):
[window.addstr(y, 0, f'{b} \n') for y, b in enumerate(numbers[iny:iny+border_y])]
window.refresh()
scroll(win)
### KEY PRESS ###
while(True):
ch = win.getkey()
if ch == 'KEY_UP':
if(iny>0):
iny-=1
scroll(win)
elif ch == 'KEY_DOWN':
if(iny<ylen-border_y):
iny+=1
scroll(win)
elif ch == 'q':
break
wrapper(main)

Related

Python Convert Inches and Feet into Inches

I am trying to convert arrays that contain both Inches and Feet into Inches. The feet are denoted with a single quote (') and inches are denoted with double quotes ("). The data comes in the following forms:
[8'x10']
[60\" x 72\"]
[5'x8',5x8,60\"x92\"]
[8'10\"x12']
What I want:
["96x120"]
["60x72"]
["60x96","60x96","60x96","60x92"]
["106x144"]
What I have:
def ft_inch(numbers):
if str(numbers).find("x") > -1:
numbers=numbers.replace('[','').replace('"','').replace('[','').replace(']','')
try:
nom = numbers.split("x")[0]
nom=nom.replace(r'\\|\"|\]|\[','')
nom_one = nom.split("'")[0]
nom_two = nom.split("'")[1]
den = numbers.split("x")[1]
den=den.replace(r'\\|\"|\[|\]','')
den_one = den.split("'")[0]
den_two = den.split("'")[1]
ft=int(nom_one)*12
inch=nom_two.replace(r'\"| |\\','')
try:
inch=int(inch)
except:
print('B')
tmp = int(ft)+int(inch)
fts=int(den_one)*12
inchs=den_two.replace(r'\"| |\\','')
try:
inchs=int(inchs)
except:
print('B')
tmp_two = int(fts)+int(inch)
return f'["{tmp}x{tmp_two}"]'
except:
return numbers
else:
return numbers
x="[5'1x8'1]"
ft_inch(x)
This works for a single array as long as it has both feet and inches but fails if its only feet [8'x8']. If anyone has a simpler solution please let me know
A regex-based approach:
import re
inputs = [["8'1x10'1"], ["60\" x 72\""], ["5'x8'", "5x8", "60\"x92\""], ["8'10\"x12'"]]
for inpt in inputs:
sub_output = []
for measurement in inpt:
m = re.match(r"(\d+['\"]?)(\d+['\"]?)?x(\d+['\"]?)(\d+['\"]?)?",
"".join(measurement.split()))
groups = [m.groups()[:2], m.groups()[2:]]
result_inches = [0, 0]
for i, group in enumerate(groups):
for raw_val in group:
if raw_val == None:
continue
if '"' in raw_val:
result_inches[i] += int(raw_val[:-1])
elif "'" in raw_val:
result_inches[i] += int(raw_val[:-1])*12
else:
result_inches[i] += int(raw_val)*12
sub_output.append(result_inches)
print([f"{x}x{y}" for x, y in sub_output])
Output:
['108x132']
['60x72']
['60x96', '60x96', '60x92']
['106x144']
I saw your edit and included the ["8'1x10'1"] case :)
I rewrote the entire thing, but this seems to work:
input_ = ["8'x10'", "60\" x 72\"", "5'x8'","5x8","60\"x92\"", "8'10\"x12'", "8'1x10'1\""]
inches_only = []
for s in input_:
s.replace(" ", "")
sides = s.split("x")
new_sides = []
for side in sides:
inches = 0
split1 = side.split("'")
if len(split1) > 1 or (len(split1) == 1 and not side.__contains__('"')):
inches = int(split1[0]) * 12
split2 = side.split('"')
if len(split2) > 1:
inches += int(split2[0].split("'")[-1])
elif len(split2) == 1 and len(split1) > 1 and len(split1[1]) > 0:
inches += int(split1[1])
new_sides.append(str(inches) + '"')
inches_only.append("x".join(new_sides))
print(inches_only)
Output:
['96"x120"', '60"x72"', '60"x96"', '60"x96"', '60"x92"', '106"x144"', '97"x121"']

how to print in color a selected element from input

I have a list of data (extracted from a .cv) and I want to print a list of average with the inputed data in other color. I was able to do everything, but the color is messing me up.
Can an angel help me on how to put in this code?
(i just paste the final print part)
import colorama
from colorama import Fore, Back, Style
prefage = []
prefcheck = input("Choose a prefecture: ")
print("MINORITY RATIO IN DESCENDING ORDER WITH SELECTED PREFECTURE IN RED")
for pref in range(0, len(prefage)) :
print(prefage[pref][0].rjust(10), end=" ")
for minor in range(1, len(prefage[0])) :
print(prefage[pref][minor].rjust(10))
else:
print("Sorry, no prefecture with that name.")
Before this idea i was just printing the result in a very simple way:
for x in prefage:
if prefcheck in x:
print(x)
break
(removed second question)
EDIT:
the entire code is this:
import colorama
from colorama import Fore, Back, Style
table = []
f = open("population.csv", "r")
for line in f :
line = line.rstrip("\r\n")
field = line.split(",")
table.append(field) ##### field is a list
f.close()
#AVERAGE AGE MATH FOR EACH PREFECTURE
agelist = [ "Age" ]
ag = 2.5
for i in range(1, len(table[1])) :
agelist.append(ag)
ag += 5
#PRINT AVERAGE AGE LIST
prefage = []
for pref in range(1, len(table)) :
sum = 0
asum = 0
ysum = 0
for age in range(1, len(table[1])):
sum += int(table[pref][age])
asum += int(table[pref][age]) * agelist[age]
average = round(asum / sum, 1)
for age in range(1, 5):
ysum += int(table[pref][age])
yave = round(ysum*100 / sum, 1)
prefage.append([(table[pref][0]), str(yave)])
#SORT AVARAGE IN DESCENDING ORDER
n = len(prefage)
while n > 1 :
for p in range(0, n - 1) :
if prefage[p][1] < prefage[p+1][1] :
prefage[p], prefage[p+1] = prefage[p+1], prefage[p]
n = n -1
print("MINORITY RATIO IN DESCENDING ORDER:")
for pref in range(0, len(prefage)) :
print(prefage[pref][0].rjust(10), end=" ")
for avnum in range(1, len(prefage[0])) :
print(prefage[pref][avnum].rjust(10))
print()
prefcheck = input("Choose a prefecture: ")
for x in prefage:
if prefcheck in x:
print(x)
break
else:
print("Sorry, no prefecture with that name")
You can do
print(Fore.RED, x, Fore.RESET)
but it will add spaces between value and colors so better use f-string
print( f"{Fore.RED}{x}{Fore.RESET}" )
And then you can use f-string to justify: {x:>10}, {x:<10}, {x:^10}
from colorama import Fore, Back, Style
x = 10
print('|', x, '|') # without color
print('|', Fore.RED, x, Fore.RESET, '|') # with color
print( f'|{Fore.RED}{x}{Fore.RESET}|' ) # f-string
print( f'|{Fore.RED}{x:>10}{Fore.RESET}|' )
print( f'|{Fore.RED}{x:<10}{Fore.RESET}|' )
print( f'|{Fore.RED}{x:^10}{Fore.RESET}|' )
Result:
| 10 | # without colors
| 10 | # with colors
|10| # f-string with colors
| 10| # x:>10
|10 | # x:<10
| 10 | # x:^10
You can also use variable to set justify - with nested { }
max_len = 10
print( f'|{Fore.RED}{x:^{max_len}}{Fore.RESET}|' )
EDIT:
If you want bright colors then you can add Style.BRIGHT and remove it with Style.NORMAL
print( f'|{Fore.RED+Style.BRIGHT}{x:^10}{Fore.RESET+Style.NORMAL}|' )
You may also use variables
start_color = Fore.RED+Style.BRIGHT
reset_color = Fore.RESET+Style.NORMAL
print( f'|{start_color}{x:^10}{reset_color}|' )
So you can use it with if/else
if prefcheck in x:
start_color = Fore.RED+Style.BRIGHT
reset_color = Fore.RESET+Style.NORMAL
else:
start_color = ''
reset_color = ''
print( f'|{start_color}{x:^10}{reset_color}|' )
Or define shorter names
CR = Fore.RED+Style.BRIGHT # Color Red
CG = Fore.GREEN+Style.BRIGHT # Color Green
CX = Fore.RESET+Style.NORMAL # Color reset
if prefcheck in x:
start_color = CR
reset_color = CX
else:
start_color = ''
reset_color = ''
print( f'|{start_color}{x:^10}{reset_color}|' )

Is it possible to embed custom text in python tkcalendar's date

I am creating a shift table by tkcalendar.
I would like to make the selected dates show their own timetable, as illustrated in the picture.
Is it possible to achieve this by any methods provided by tkcalendar? Or the only way I can do this is created a custom calendar from zero? I went through the document, but hardly finding something useful.
NOTE: I don't want tooltip(pop-up text)
It is not possible from tkcalendar's methods to display text below the day number in the Calendar. However, you don't have to start from scratch, you can create an Agenda class inheriting from Calendar and only rewrite the methods displaying the events to put them inside the day's label instead of in a popup.
To be precise, 3 methods need to be modified: _display_days_without_othermonthdays(), _display_days_with_othermonthdays() and _show_event(), see code below. The code might seem long but I actually only modified a couple of lines in each methods compared to there original version in tkcalendar.
from tkcalendar import Calendar
class Agenda(Calendar):
def __init__(self, master=None, **kw):
Calendar.__init__(self, master, **kw)
# change a bit the options of the labels to improve display
for i, row in enumerate(self._calendar):
for j, label in enumerate(row):
self._cal_frame.rowconfigure(i + 1, uniform=1)
self._cal_frame.columnconfigure(j + 1, uniform=1)
label.configure(justify="center", anchor="n", padding=(1, 4))
def _display_days_without_othermonthdays(self):
year, month = self._date.year, self._date.month
cal = self._cal.monthdays2calendar(year, month)
while len(cal) < 6:
cal.append([(0, i) for i in range(7)])
week_days = {i: 'normal.%s.TLabel' % self._style_prefixe for i in range(7)} # style names depending on the type of day
week_days[self['weekenddays'][0] - 1] = 'we.%s.TLabel' % self._style_prefixe
week_days[self['weekenddays'][1] - 1] = 'we.%s.TLabel' % self._style_prefixe
_, week_nb, d = self._date.isocalendar()
if d == 7 and self['firstweekday'] == 'sunday':
week_nb += 1
modulo = max(week_nb, 52)
for i_week in range(6):
if i_week == 0 or cal[i_week][0][0]:
self._week_nbs[i_week].configure(text=str((week_nb + i_week - 1) % modulo + 1))
else:
self._week_nbs[i_week].configure(text='')
for i_day in range(7):
day_number, week_day = cal[i_week][i_day]
style = week_days[i_day]
label = self._calendar[i_week][i_day]
label.state(['!disabled'])
if day_number:
txt = str(day_number)
label.configure(text=txt, style=style)
date = self.date(year, month, day_number)
if date in self._calevent_dates:
ev_ids = self._calevent_dates[date]
i = len(ev_ids) - 1
while i >= 0 and not self.calevents[ev_ids[i]]['tags']:
i -= 1
if i >= 0:
tag = self.calevents[ev_ids[i]]['tags'][-1]
label.configure(style='tag_%s.%s.TLabel' % (tag, self._style_prefixe))
# modified lines:
text = '%s\n' % day_number + '\n'.join([self.calevents[ev]['text'] for ev in ev_ids])
label.configure(text=text)
else:
label.configure(text='', style=style)
def _display_days_with_othermonthdays(self):
year, month = self._date.year, self._date.month
cal = self._cal.monthdatescalendar(year, month)
next_m = month + 1
y = year
if next_m == 13:
next_m = 1
y += 1
if len(cal) < 6:
if cal[-1][-1].month == month:
i = 0
else:
i = 1
cal.append(self._cal.monthdatescalendar(y, next_m)[i])
if len(cal) < 6:
cal.append(self._cal.monthdatescalendar(y, next_m)[i + 1])
week_days = {i: 'normal' for i in range(7)} # style names depending on the type of day
week_days[self['weekenddays'][0] - 1] = 'we'
week_days[self['weekenddays'][1] - 1] = 'we'
prev_m = (month - 2) % 12 + 1
months = {month: '.%s.TLabel' % self._style_prefixe,
next_m: '_om.%s.TLabel' % self._style_prefixe,
prev_m: '_om.%s.TLabel' % self._style_prefixe}
week_nb = cal[0][1].isocalendar()[1]
modulo = max(week_nb, 52)
for i_week in range(6):
self._week_nbs[i_week].configure(text=str((week_nb + i_week - 1) % modulo + 1))
for i_day in range(7):
style = week_days[i_day] + months[cal[i_week][i_day].month]
label = self._calendar[i_week][i_day]
label.state(['!disabled'])
txt = str(cal[i_week][i_day].day)
label.configure(text=txt, style=style)
if cal[i_week][i_day] in self._calevent_dates:
date = cal[i_week][i_day]
ev_ids = self._calevent_dates[date]
i = len(ev_ids) - 1
while i >= 0 and not self.calevents[ev_ids[i]]['tags']:
i -= 1
if i >= 0:
tag = self.calevents[ev_ids[i]]['tags'][-1]
label.configure(style='tag_%s.%s.TLabel' % (tag, self._style_prefixe))
# modified lines:
text = '%s\n' % date.day + '\n'.join([self.calevents[ev]['text'] for ev in ev_ids])
label.configure(text=text)
def _show_event(self, date):
"""Display events on date if visible."""
w, d = self._get_day_coords(date)
if w is not None:
label = self._calendar[w][d]
if not label.cget('text'):
# this is an other month's day and showothermonth is False
return
ev_ids = self._calevent_dates[date]
i = len(ev_ids) - 1
while i >= 0 and not self.calevents[ev_ids[i]]['tags']:
i -= 1
if i >= 0:
tag = self.calevents[ev_ids[i]]['tags'][-1]
label.configure(style='tag_%s.%s.TLabel' % (tag, self._style_prefixe))
# modified lines:
text = '%s\n' % date.day + '\n'.join([self.calevents[ev]['text'] for ev in ev_ids])
label.configure(text=text)
if __name__ == '__main__':
import tkinter as tk
root = tk.Tk()
root.geometry("800x500")
agenda = Agenda(root, selectmode='none')
date = agenda.datetime.today() + agenda.timedelta(days=2)
agenda.calevent_create(date, 'Hello World', 'message')
agenda.calevent_create(date, 'Reminder 2', 'reminder')
agenda.calevent_create(date + agenda.timedelta(days=-7), 'Reminder 1', 'reminder')
agenda.calevent_create(date + agenda.timedelta(days=3), 'Message', 'message')
agenda.calevent_create(date + agenda.timedelta(days=3), 'Another message', 'message')
agenda.tag_config('reminder', background='red', foreground='yellow')
agenda.pack(fill="both", expand=True)
root.mainloop()

Python Script for Art Museum Installation intermittently locks up, Removing Thermal Camera Sensor read function seems to work?

I have a python script for an installation in an art museum that is meant to run continuously playing sounds, driving an LED matrix, and sensing people via OpennCV and a thermal camera.
Each of the parts of the script work and all of them work together but randomly the script locks up and I need to restart it. I want to script to not lock up so no one has to reset it during the exhibition.
I have the code running on a spare Raspberry Pi and a spare LED matrix and it continues to cycle through fine. The only changes that I made were commenting out the start of a thread to check the IR sensor and a call to a function to get the max temp from the sensor.
To be clear, if I leave these bits of code in the script runs fine 1 -3 or sometimes 10 times. But it seems to lock up in the first "state" when IRcount = 0
I am stuck. Any help is greatly appreciated.
```
#!/usr/bin/python
import glob
import queue
import sys
import pygame
import cv2
import random
import math
import colorsys
import time
from rpi_ws281x import *
from PIL import Image
import numpy as np
import threading
global thresh
sys.path.insert(0, "/home/pi/irpython/build/lib.linux-armv7l-3.5")
import MLX90640 as mlx
currentTime = int(round(time.time() * 1000))
InflateWait = int(round(time.time() * 1000))
minTime = 6000
maxTime = 12000
lineHeight1 = 0
lineHue1 = float(random.randrange(1,360))/255
# IR Functions
# Function to just grab the Max Temp detected. If over threshold then start
# the sequence, if not stay in state 0
def maxTemp():
mlx.setup(8) #set frame rate of MLX90640
f = mlx.get_frame()
mlx.cleanup()
# get max and min temps from sensor
# v_min = min(f)
v_max = int(max(f))
return v_max
# Function to detect individual people's heat blob group of pixels
# run in a thread only at the end of the script
def irCounter():
img = Image.new( 'L', (24,32), "black") # make IR image
mlx.setup(8) #set frame rate of MLX90640
f = mlx.get_frame()
mlx.cleanup()
for x in range(24):
row = []
for y in range(32):
val = f[32 * (23-x) + y]
row.append(val)
img.putpixel((x, y), (int(val)))
# convert raw temp data to numpy array
imgIR = np.array(img)
# increase the 24x32 px image to 240x320px for ease of seeing
bigIR = cv2.resize(depth_uint8, dsize=(240,320), interpolation=cv2.INTER_CUBIC)
# Use a bilateral filter to blur while hopefully retaining edges
brightBlurIR = cv2.bilateralFilter(bigIR,9,150,150)
# Threshold the image to black and white
retval, threshIR = cv2.threshold(brightBlurIR, 26, 255, cv2.THRESH_BINARY)
# Define kernal for erosion and dilation and closing operations
kernel = np.ones((5,5),np.uint8)
erosionIR = cv2.erode(threshIR,kernel,iterations = 1)
dilationIR = cv2.dilate(erosionIR,kernel,iterations = 1)
closingIR = cv2.morphologyEx(dilationIR, cv2.MORPH_CLOSE, kernel)
# Detect countours
contours, hierarchy = cv2.findContours(closingIR, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# Get the number of contours ( contours count when touching edge of image while blobs don't)
ncontours = str(len(contours))
# Show images in window during testing
cv2.imshow("Combined", closingIR)
cv2.waitKey(1)
#initialize pygame
pygame.init()
pygame.mixer.init()
pygame.mixer.set_num_channels(30)
print("pygame initialized")
# assign sound chennels for pygame
channel0 = pygame.mixer.Channel(0)
channel1 = pygame.mixer.Channel(1)
channel2 = pygame.mixer.Channel(2)
channel3 = pygame.mixer.Channel(3)
channel4 = pygame.mixer.Channel(4)
channel5 = pygame.mixer.Channel(5)
channel6 = pygame.mixer.Channel(6)
channel7 = pygame.mixer.Channel(7)
channel8 = pygame.mixer.Channel(8)
channel9 = pygame.mixer.Channel(9)
channel10 = pygame.mixer.Channel(10)
channel11 = pygame.mixer.Channel(11)
channel12 = pygame.mixer.Channel(12)
channel13 = pygame.mixer.Channel(13)
channel14 = pygame.mixer.Channel(14)
channel15 = pygame.mixer.Channel(15)
channel16 = pygame.mixer.Channel(16)
channel17 = pygame.mixer.Channel(17)
channel18 = pygame.mixer.Channel(18)
channel19 = pygame.mixer.Channel(19)
channel20 = pygame.mixer.Channel(20)
channel21 = pygame.mixer.Channel(21)
channel22 = pygame.mixer.Channel(22)
channel23 = pygame.mixer.Channel(23)
channel24 = pygame.mixer.Channel(24)
channel25 = pygame.mixer.Channel(25)
channel26 = pygame.mixer.Channel(26)
channel27 = pygame.mixer.Channel(27)
channel28 = pygame.mixer.Channel(28)
# load soundfiles
echoballs = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/echo balls FIX.ogg")
organbounce = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/ORGAN BOUNCE fix.ogg")
jar = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/jar whoop fix.ogg")
garland = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/GARLAND_fix.ogg")
dribble= pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/dribble.ogg")
cowbell = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/cowbell fix.ogg")
clackyballs = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/clacky balls boucne.ogg")
burpees = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/burpees_fix.ogg")
brokensynth = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/broken synth bounce.ogg")
woolballs = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/wool balls in jar FIX.ogg")
wiimoye = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/wiimoye_fix.ogg")
warpyorgan = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/warpy organ bounce#.2.ogg")
vibrate = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/vibrate fix.ogg")
turtlesbounce = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/turtles fix.ogg")
timer = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/timer.ogg")
tape = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/tape fix.ogg")
tambourine = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/TAMBOURINE.ogg")
springybounce = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/springy bounce.ogg")
smash3 = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/smash fix.ogg")
bristle2 = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/BRISTLE FIX.ogg")
blackkeys = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/black keys FIX.ogg")
zipper = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/zipper.ogg")
presatisfactionsweep = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/pre-satisfaction sweep .ogg")
satisfaction = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/SATISFACTION.ogg")
altsatisfaction = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/alt_satisfaction_trimmed.ogg")
solosatisfaction = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/SOLO_SATISFACTION.ogg")
print("sound files loaded")
# initializing sounds list
soundsList = [echoballs, organbounce, zipper, jar, garland, dribble, cowbell, clackyballs, burpees, brokensynth, woolballs,
wiimoye, warpyorgan, vibrate, turtlesbounce, timer, tambourine, springybounce, smash3, bristle2, blackkeys, zipper ]
IRcount = 0 # define initial state for main loop
pygame.display.set_mode((32, 8))
print("pygame dispaly open")
# LED strip configuration:
LED_COUNT = 256 # Number of LED pixels.
LED_PIN = 18 # GPIO pin connected to the pixels (18 uses PWM!).
#LED_PIN = 10 # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0).
LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz)
LED_DMA = 10 # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 100 # Set to 0 for darkest and 255 for brightest
LED_INVERT = False # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL = 0 # set to '1' for GPIOs 13, 19, 41, 45 or 53
# Define functions which animate LEDs in various ways.
# PNG to LED function used to shuffle througfh folders of numbered PNGs exported
# from animations created
def pngToLED (strip, pngfile):
RGBimage = Image.open(pngfile).convert('RGB')
np_image = np.array(RGBimage)
colours = [Color(x[0],x[1],x[2]) for rows in np_image for x in rows]
colours2d = np.reshape(colours, (32, 8), order='F')
colours2d[1::2, :] = colours2d[1::2, ::-1]
pic = colours2d.flatten('C')
for i in range( 0, strip.numPixels(), 1 ):# iterate over all LEDs - range(start_value, end_value, step)
strip.setPixelColor(i, int(pic[ i ]))
strip.show()
def colorWipe(strip, color,wait_ms=10):
"""Wipe color across display a pixel at a time."""
for i in range(strip.numPixels()):
strip.setPixelColor(i, color)
strip.show()
time.sleep(1)
def theaterChase(strip, color, wait_ms, iterations=10):
"""Movie theater light style chaser animation."""
for j in range(iterations):
for q in range(3):
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, color)
strip.show()
time.sleep(wait_ms/1000.0)
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, 0)
def wheel(pos):
"""Generate rainbow colors across 0-255 positions."""
if pos < 85:
return Color(pos * 3, 255 - pos * 3, 0)
elif pos < 170:
pos -= 85
return Color(255 - pos * 3, 0, pos * 3)
else:
pos -= 170
return Color(0, pos * 3, 255 - pos * 3)
def rainbow(strip, wait_ms=20, iterations=1):
"""Draw rainbow that fades across all pixels at once."""
for j in range(256*iterations):
for i in range(strip.numPixels()):
strip.setPixelColor(i, wheel((i+j) & 255))
strip.show()
time.sleep(wait_ms/1000.0)
def rainbowCycle(strip, wait_ms=20, iterations=5):
"""Draw rainbow that uniformly distributes itself across all pixels."""
for j in range(256*iterations):
for i in range(strip.numPixels()):
strip.setPixelColor(i, wheel((int(i * 256 / strip.numPixels()) + j) & 255))
strip.show()
time.sleep(wait_ms/1000.0)
def theaterChaseRainbow(strip, wait_ms=90):
"""Rainbow movie theater light style chaser animation."""
for j in range(256):
for q in range(3):
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, wheel((i+j) % 255))
strip.show()
time.sleep(wait_ms/1000.0)
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, 0)
# Plasma LED Function from Root 42
def plasmaLED (plasmaTime):
h = 8
w = 32
out = [ Color( 0, 0, 0 ) for x in range( h * w ) ]
plasmaBright = 100.0
for x in range( h ):
for y in range( w ):
hue = (4.0 + math.sin( plasmaTime + x ) + math.sin( plasmaTime + y / 4.5 ) \
+ math.sin( x + y + plasmaTime ) + math.sin( math.sqrt( ( x + plasmaTime ) ** 2.0 + ( y + 1.5 * plasmaTime ) ** 2.0 ) / 4.0 ))/8
hsv = colorsys.hsv_to_rgb( hue , 1, 1 )
if y % 2 == 0: #even
out[ x + (h * y)] = Color( *[ int( round( c * plasmaBright ) ) for c in hsv ] )
else: #odd
out[ (y * h) + (h -1 -x) ] = Color( *[ int( round( c * plasmaBright ) ) for c in hsv ] )
for i in range( 0, strip.numPixels(), 1 ):# iterate over all LEDs - range(start_value, end_value, step)
strip.setPixelColor(i, out[ i ]) # set pixel to color in picture
strip.show()
# variables for plasma
plasmaTime = 5.0 # time
plasmaSpeed = 0.05 # speed of time
# thread for IRcounter function
class TempTask:
def __init__(self):
self.ir_temp = 0
self.lock = threading.Lock() #control concurrent access for safe multi thread access
self.thread = threading.Thread(target=self.update_temp)
def update_temp(self):
while True:
with self.lock:
self.ir_temp = irCounter()
time.sleep(0.1)
def start(self):
self.thread.start()
# Millis timer count function
def CheckTime( lastTime, wait):
if currentTime - lastTime >= wait:
lastTime += wait
return True
return False
# Main program logic follows:
if __name__ == '__main__':
# not currently starting the trhead because program is locking up without it
# want to figure out initial problem first
#start thread
#task = TempTask()
#task.start()
# Create NeoPixel object with appropriate configuration.
strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
# Intialize the library (must be called once before other functions).
strip.begin()
print ('Press Ctrl-C to quit.')
try:
while True:
currentTime = int(round(time.time() * 1000))
if IRcount == 0:
#random solid color
colorWipe(strip, Color(random.randint(60,255), random.randint(60,255), random.randint(60,255)))
# use random.sample() to shuffle sounds list
shuffledSounds = random.sample(soundsList, len(soundsList))
if pygame.mixer.Channel(0).get_busy() == False: channel0.play(shuffledSounds[0],loops = -1)
thresh = 0
'''
# the threshold check below is the only thing I have taken out of
# Program on my test Raspberry Pi. It seems to not lock up without it
# not sure why this would be a problem.
thresh = int(maxTemp())
print (thresh)
if thresh >= 27:
InflateWait = int(round(time.time() * 1000))
print (thresh)
IRcount = 1
print("Threshold Temp Detected: Begin Sound Sequence")
else:
IRcount = 0
'''
if CheckTime(InflateWait,random.randint(minTime, maxTime)):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 1:
LEDimages = glob.glob("/home/pi/ruff-wavs/Crystal_Mirror/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(1).get_busy() == False:
channel1.play(shuffledSounds[1],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 2:
LEDimages = glob.glob("/home/pi/ruff-wavs/Mercury_Loop/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(2).get_busy() == False:
channel2.play(shuffledSounds[2],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 3:
LEDimages = glob.glob("/home/pi/ruff-wavs/Pink_Lava/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(3).get_busy() == False:
channel3.play(shuffledSounds[3],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 4:
LEDimages = glob.glob("/home/pi/ruff-wavs/Horiz_Mosaic/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(4).get_busy() == False:
channel4.play(shuffledSounds[4],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 5:
plasmaLED()
plasmaTime = plasmaTime + plasmaSpeed # increment plasma time
if pygame.mixer.Channel(5).get_busy() == False:
channel5.play(shuffledSounds[5],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 6:
LEDimages = glob.glob("/home/pi/ruff-wavs/Radio_Loop/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(6).get_busy() == False:
channel6.play(shuffledSounds[6],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 7:
LEDimages = glob.glob("/home/pi/ruff-wavs/Star_Loop/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(7).get_busy() == False:
channel7.play(shuffledSounds[7],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
elif IRcount == 14:
plasmaLED()
plasmaTime = plasmaTime + plasmaSpeed # increment plasma time
if pygame.mixer.Channel(14).get_busy() == False:
channel14.play(shuffledSounds[14],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
print (thresh)
elif IRcount == 15:
plasmaLED()
plasmaTime = plasmaTime + plasmaSpeed # increment plasma time
if pygame.mixer.Channel(15).get_busy() == False:
channel15.play(shuffledSounds[15],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 16:
# random color theater chase increment random ms to speed up with sounds
theaterChase(strip, Color(random.randint(1,255), random.randint(1,255), random.randint(1,255)), random.randint(40,50))
pygame.mixer.fadeout(45000)
if pygame.mixer.Channel(22).get_busy() == False:
channel22.play(presatisfactionsweep)
IRcount = 17
print(IRcount)
print("sweep end start")
elif IRcount == 18:
# random color theater chase increment random ms to speed up with sounds
theaterChase(strip, Color(random.randint(1,255), random.randint(1,255), random.randint(1,255)), random.randint(30,40))
if pygame.mixer.Channel(22).get_busy() == False:
pygame.mixer.stop()
channel23.play(satisfaction)
IRcount = 19
print(IRcount)
print("Play Satisfaction Sount")
elif IRcount == 19:
rainbowCycle(strip, 5)
if pygame.mixer.Channel(23).get_busy() == False: IRcount = 0
except KeyboardInterrupt:
colorWipe(strip, Color(0,0,0), 1)
pygame.mixer.stop()
pygame.quit()
```
Update 1 - Suspected Function(s)
When I left the script run overnight and came to the exhibit in the morning it would be stuck in the 1st state IRcount = 0 The only things that happen in that state is the maxTemp() function to get the max temp, the LED color wipe function to cycle colors.
When I would come in in the morning it would be stuck, playing a single sound from pygame, as it should, but it would not be cycling colors. I removed the maxTemp() from my test Pi and it has been working fine.
def maxTemp():
mlx.setup(8) #set frame rate of MLX90640
f = mlx.get_frame()
mlx.cleanup()
# get max and min temps from sensor
# v_min = min(f)
v_max = int(max(f))
return v_max
Update # 2
I thought that the thread might be the problem so I commented out the thread start call. That is why I made the simpler maxTemp() function to see if that would work better than the thread. So when I was using the max temp then the thread wasn't being called.
I don't understand threads very well. Is it possible to have the max temp variable update continuously and have the simple OpenCV numPy manipulations running continuously? That would be ideal. When I originally added the thread it seemed to stop after a few cycles.
I do not have a join on the thread. I know threads don't "restart" but do I need to call it again as the state machine starts again?
# not currently starting the thread because program is locking up without it
# want to figure out initial problem first
#start thread
#task = TempTask()
#task.start()
Update #3
I Uploaded new code that eliminated the duplicate functions. Everything is handled in the thread temp.task now. That seems to work fine. I also put the github suggestion of polling the thermal sensor if the image is a duplicate but that has not happened.
I left the program run over night and when I came in in the morning it was locked up. The SD card is set to read only mode. I ssh'd into the pi. I have my auto start python script in /etc/profile
It seems to start the script each time I log into ssh. When I logged in this morning to see if the pi was still up it game an out of memory error.
```
Traceback (most recent call last):
File "/home/pi/ruff-wavs/shufflewavdemo.py", line 210, in <module>
altsatisfaction = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/alt_satisfaction_trimmed.ogg")
pygame.error: Unable to open file '/home/pi/ruff-wavs/sounds/alt_satisfaction_trimmed.ogg'
OSError: [Errno 28] No space left on device
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.5/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.5/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/usr/local/lib/python3.5/dist-packages/virtualenvwrapper/hook_loader.py", line 223, in <module>
main()
File "/usr/local/lib/python3.5/dist-packages/virtualenvwrapper/hook_loader.py", line 145, in main
output.close()
OSError: [Errno 28] No space left on device
-bash: cannot create temp file for here-document: No space left on device
Could that be because it is in read only mode?
I used this script to switch from writable to read only and back.
[https://github.com/JasperE84/root-ro][1]
[1]: https://github.com/JasperE84/root-ro
I suspect the issue is that you're accessing the mlx device both in the main thread via maxTemp() as well as in the irCounter() thread. The fact that it works when you take out the maxTemp call, and that that call happens in the if IRcount == 0: state supports this.
I would add the maxTemp functionality to the irCounter thread, so that accessing it from only a single thread; and update a global variable (protected by a lock) with the maxTemp results if you need to retain this functionality.

Python 3 - Christmas tree code - syntax errors

I'm very new to coding and I'm trying (and failing) to make a code that prints a Christmas-tree shape of "*" in whatever size instructed, and don't know where I'm going wrong, but I keep getting a variety of syntax errors all over my code.
size_raw = input("Size of tree?")
def spaces(length, size):
Space = " "
new_len = size / 2
new_len -= length
space_s = int(Space) * int(new_len)
return space_s
def segment(h, tw, bw, s):
line = tw
star = "*"
while line <= bw:
stars = line * star
print (spaces(int(line), int(s)) + (stars) + (spaces(int(line), int(s))
line += 2
def tree(size):
Topwidth = 1
height = 3
while Topwidth <= size:
bottom_width = Topwidth + height
segment(int(height), int(Topwidth), int(bottom_width), int(size)
height += 2
if size_raw == "Very Big":
tree(100)
elif size_raw == "Massive":
tree(1000)
else:
tree(int(size_raw))
I expect it to work smoothly in this current state, however it responds with an error of a new for in a new location with each new attempt.
Your parenthesis are not closed properly.
Your code
print (spaces(int(line), int(s)) + (stars) + (spaces(int(line), int(s))
segment(int(height), int(Topwidth), int(bottom_width), int(size)
My version
print (spaces(int(line), int(s))) + (stars) + (spaces(int(line), int(s)))
segment(int(height), int(Topwidth), int(bottom_width), int(size))
Got it to work with this fiddle, where i also change the indention to be with 4 spaces which is the most common indention.

Categories