Затухание между 2 цветами в Curses (Python)
Я пытаюсь затушевывать цвет фона подокна Curses между 2 произвольными значениями RGB, переданными в функцию setupColor() в моем коде.
В некоторых случаях код ведет себя как ожидалось и может удовлетворительно исчезать между цветами, но большую часть времени он работает странно.
setupColor((0,0,0),(0,255,55))
Это исчезнет подокно между черным и аквамарином, и работает хорошо.
Однако, если я попытаюсь исчезнуть между, скажем, желтым и фиолетовым, вот так:
setupColor((255,200,0),(200,0,200))
Это будет циклически переключаться между двумя цветами в течение первых нескольких циклов, но, похоже, теряет синхронизацию и в итоге выдает заметно отличающиеся цвета от тех, которые были переданы функции.
Исходя из исходного кода на эскизе Arduino LED Fade, я искал, пытался ли кто-нибудь сделать что-то подобное с физическими светодиодами RGB, что привело к появлению этой темы: C++ Fade между цветами? (Ардуино).
Похоже, что решение, опубликованное в этой ветке, было бы идеальным для моих нужд, но я недостаточно хорошо знаю C++ или JavaScript, чтобы иметь возможность отслеживать, понимать и переносить код на Python.
Можно ли адаптировать мой код для правильного выцветания цветов, или стоит отказаться от него и начать все заново?
import curses,time
from curses import wrapper
def setupColor((red,green,blue),(red1,green1,blue1)):
global color1,color2 #initialise globals for use later on in the program.
global incrFloatRed,incrFloatGreen,incrFloatBlue
global minRed,minGreen,minBlue,maxRed,maxGreen,maxBlue
stepNumber = 60.00 # number of steps within each color transition.
color1 = (red,green,blue) # clone the input tuples for use later on...
color2 = (red1,green1,blue1)
differenceRed = red - red1 # subtract each channel of color1 from color2,
differenceGreen = green - green1 # this will return either a positive or negative float.
differenceBlue = blue - blue1
incrFloatRed = differenceRed / stepNumber # divide the difference between the 2 colors by the
incrFloatGreen = differenceGreen / stepNumber # step rate to obtain the color increments.
incrFloatBlue = differenceBlue / stepNumber
if red > red1: # if the red channel value of the 1st color is greater than
incrFloatRed = -incrFloatRed # that of the 2nd, invert the increment (to allow
maxRed = red # color subtraction), then set the top end of the range as
minRed = red1 # red 1st channel and the bottom as red 2nd channel.
else: # Else, perform the inverse operation.
incrFloatRed = abs(incrFloatRed)
maxRed = red1
minRed = red
if green > green1:
incrFloatGreen = -incrFloatGreen
maxGreen = green
minGreen = green1
elif green < green1:
incrFloatGreen = abs(incrFloatGreen)
maxGreen = green1
minGreen = green
if blue > blue1:
incrFloatBlue = -incrFloatBlue
maxBlue = blue
minBlue = blue1
else:
incrFloatBlue = abs(incrFloatBlue)
maxBlue = blue1
minBlue = blue
def main(stdscr):
global incrFloatRed,incrFloatGreen,incrFloatBlue
setupColor((0,0,0),(0,255,255))
red = color1[0] #red,green and blue are the variables used to control the fade.
green = color1[1] #To begin with, they is set to the colors contained in the first
blue = color1[2] #tuple that is passed to setupColor()
label = stdscr.subwin(10,50,1,4) # create a subwindow, draw a box around it, then add the string
label.box() # "Hello, World!" to the subwindow at position row 1, column 1.
label.addstr(1,1,"Hello, World!")
curses.curs_set(0) # Disable cursor visibility
while True:
red = red + incrFloatRed # color = colorValue + colorIncrement
green = green + incrFloatGreen
blue = blue + incrFloatBlue
if red <= minRed or red >= maxRed: # if color is less than the start of the color range or
incrFloatRed = -incrFloatRed # greater than end of the color range, invert the color increment
if green <= minGreen or green >= maxGreen:
incrFloatGreen = -incrFloatGreen
if blue <= minBlue or blue >= maxBlue:
incrFloatBlue = -incrFloatBlue
# curses.init_color takes a tuple of rgb values as it's argument,
cursesRed = int(int(red) / 0.255) # but uses funky 1000-point intensity values, instead of the usual
cursesGreen = int(int(green) / 0.255) # 255. e.g. rgb(1000,0,0) for full intensity red, instead of
cursesBlue = int(int(blue) / 0.255) # rgb(255,0,0).
# To convert between them, divide the integer of the color value float
# by 0.255, then obtain the integer of the resulting float.
if cursesRed >=1000: # Sometimes a color value is set to greater
cursesRed = 1000 # than 1k or less than 0. When a negative value or a value greater
if cursesGreen >=1000: # than 1k is passed to curses.init_color(), it will return ERR and
cursesGreen = 999 # Curses will crash.
if cursesBlue >=1000:
cursesBlue = 999
if cursesRed <=0:
cursesRed = 0
if cursesGreen <=0:
cursesGreen = 0
if cursesBlue <=0:
cursesBlue = 0
curses.init_color(1,cursesRed,cursesGreen,cursesBlue) # reassign Curses color (1) to the RGB1K color of the current step...
curses.init_pair(1,255,1) # then create a color pair with the dynamic value (1) as
# the BG color, and white (255) as the FG.
label.bkgd(curses.color_pair(1)) # set the background of the label subwindow to the current color pair..
label.refresh() # then refresh, so that we can see the change.
time.sleep(0.02) # Take a little snooze, then do it all again.
wrapper(main)
1 ответ
Я понял это сам в конце, это было относительно простым (но хакерским и не элегантным) решением. Я напечатал вывод каждого из цветовых каналов (переменные красный, зеленый и синий в приведенном выше коде) в этой точке программы:
if red <= minRed or red >= maxRed:
outFile.write("Red : " + str(int(red)) + "|Green : " + str(int(green)) + "|Blue : " + str(int(blue)) + "\n")
incrFloatRed = -incrFloatRed
Это выходные данные для первых трех циклов программы:
Red : 199|Green : 3 |Blue : 196
Red : 255|Green : 193|Blue : 0
Red : 199|Green : 9 |Blue : 196
Red : 255|Green : 186|Blue : 0
Red : 199|Green : 16 |Blue : 196
Red : 255|Green : 179|Blue : 0
Как видите, зеленый канал постепенно смещается по сравнению с другими, а значения красного и синего немного отличаются от значений, передаваемых в функцию setupColor().
Неточность значения цвета можно исправить с помощью операторов if для прямой установки значений:
while True:
red = red + incrFloatRed # color = colorValue + colorIncrement
green = green + incrFloatGreen
blue = blue + incrFloatBlue
##### Add the if statements after this ####
if red < minRed:
red = minRed
if red > maxRed:
red = maxRed
if blue < minBlue:
blue = minBlue
if blue > maxBlue:
blue = maxBlue
if green < minGreen:
green = minGreen
if green > maxGreen:
green = maxGreen
И проблема синхронизации / синхронизации может быть исправлена с помощью одного оператора if для изменения направления исчезновения цвета. Вместо использования выражения для каждого цвета, как это...
if green <= minGreen or green >= maxGreen:
incrFloatGreen = -incrFloatGreen
... установить их все так:
if green <= minGreen or green >=maxGreen:
incrFloatRed = -incrFloatRed
incrFloatGreen = -incrFloatGreen
incrFloatBlue = -incrFloatBlue
Орел наверняка заметил, что если оба параметра maxGreen и minGreen установлены в 0 (например, rgb(230,0100) и rgb(100,0,200)), ничего не произойдет. Если вы измените зеленый цвет на другой, он будет работать нормально.
Я сомневаюсь, что было бы трудно добавить некоторую логику, чтобы определить, какие цветовые каналы будут работать, но, учитывая, что этого можно избежать, просто передав 1 вместо 0, я не стал беспокоиться.
Другим хорошим шагом (с точки зрения эффективности) было бы сбросить все значения затухания в массив и установить цвета на его основе, вместо того чтобы каждый раз вычислять значения.