Source code for neuropsydia.scale

# -*- coding: utf-8 -*-
from .path import *
from .core import *
from .image import *
from .write import *
from .miscellaneous import *


# ==============================================================================
# ==============================================================================
# ==============================================================================
# ==============================================================================
# ==============================================================================
# ==============================================================================
# ==============================================================================
# ==============================================================================
[docs]def scale_styles(): """ Returns available scale styles. Example ---------- >>> import neuropsydia as n >>> n.start() >>> print(n.scale_styles()) >>> n.close() Notes ---------- *Authors* - Dominique Makowski (https://github.com/DominiqueMakowski) """ styles = os.listdir(Path.binary()) styles = [x for x in styles if "cursor_" in x ] styles = [x for x in styles if not "_validated" in x ] styles = [x.replace('cursor_', '') for x in styles] styles = [x.replace('.png', '') for x in styles] return(styles)
# ============================================================================== # ============================================================================== # ============================================================================== # ============================================================================== # ============================================================================== # ============================================================================== # ============================================================================== # ==============================================================================
[docs]def scale(style='red', x=0, y=-3.3, anchors=None, anchors_space=2, anchors_size=0.7, edges=[0, 100], validation=True, analog=True, step=1, labels="numeric", labels_size=0.8, labels_rotation=0, labels_space=-1, labels_x=0, line_thickness=4, line_length=8, line_color="black", background="white", title=None, title_style="body", title_size=1, title_space=1.75, point_center=False, point_edges=True, reverse=False, force_separation=False, separation_labels=None, separation_labels_size=1, separation_labels_rotate=0, separation_labels_space=-1, show_result=False, show_result_shape="circle", show_result_shape_fill_color="white", show_result_shape_line_color="black", show_result_shape_size=0.8, show_result_space=1.25, show_result_size=0.5, show_result_color="black", show_result_decimals=1, cursor_size=1): """ Draw a scale. Parameters ---------- style : str style, check `neuropsydia.scale_styles()` function to see what's available. x : float Horizontal position of the center (from -10 (left) to 10 (right)). y : float Vertical position of the center (from -10 (bottom) to 10 (top)). anchors : list of two str a list of two propositions to be displayed on the sides of the scale (e.g., [not at all, very much]). anchors_space : float spacing betweeen the edge and the anchors. anchors_size : float size of the anchors' font. edges : list the underlying numerical edges of the scale. validation : bool confirm the response with a second left click or withdraw with a right click. analog : bool continuous (discrete) scale. step : int if analog is True, what are the step to go between the edges (determine the number of points on the scale). labels : str or list "num", "numeric" or "numbers" or list of actual text labels (e.g., ["not at all", "a bit", "very much"]). labels_size : float Size of labels. labels_rotation : float Labels rotation angle. labels_space : float Space between scale and labels. labels_x : float Horizontal dodging position. line_thickness : float Scale line thickness. line_length : float Scale line length. line_color : str Scale line color. background : str Scale background color. title : str Scale title/question to ask. title_style : str 'body', 'psychometry', 'psychometry_bold', 'light', 'bold'. You can also insert the name of a system font, or the path to a specific font. title_size : float Title size. title_space : float Space between scale and title. point_center : bool Place a point at the center. point_edges : bool Place points at the edges. reverse : bool The result is scored as inverse. force_separation : int Creates visual separations with points. separation_labels : list Place labels corresponding to the separations. separation_labels_size : float Separation labels size. separation_labels_rotate : float Separation labels rotation angle. separation_labels_space : float Space between scale and separation labels. show_result : bool Add a marker to show the value on scale. show_result_shape : str Shape of this marker. "circle" or "rectangle". show_result_shape_fill_color : str Fill color of the marker. show_result_shape_line_color : str Line color of the marker. show_result_shape_size : float Marker's shape size. show_result_space : float Space between scale and marker. show_result_size : float Results text size. show_result_color : str Results text color. show_result_decimals : int How many decimals to show. cursor_size : float Size of the circle cursor. Returns ---------- response : float Response value. Example ---------- >>> import neuropsydia as n >>> n.start() >>> response = n.scale() >>> n.close() Notes ---------- *Authors* - Dominique Makowski (https://github.com/DominiqueMakowski) *Dependencies* - pygame """ pygame.mouse.set_visible(True) # Debugging styles_list = scale_styles() if style not in styles_list: print("NEUROPSYDIA ERROR: scale(): wrong style (check available styles with the scale_styles() function") style = "red" # change the parameters according to the style cursor_name = 'cursor_' + style + ".png" if style == "absorption": anchors_space = 3 anchors = ["Très distant(e)", "Très absorbé(e)"] # Coordinates calculating line_length_raw = Coordinates.to_pygame(distance_x = line_length) scale_y = y # because y will be further used for the mouse position scale_y_raw = Coordinates.to_pygame(y = y) #expressed in pygame's coordinates scale_x = x # center x of the scale edge_left = x - line_length /2 edge_right = x + line_length /2 edge_left_raw = Coordinates.to_pygame(x = edge_left) edge_right_raw = Coordinates.to_pygame(x = edge_right) cursor_x = scale_x cursor_y = scale_y if analog == False: #initialize the position and label list point_list_range = range(len(range(edges[0],edges[1],step))+1) #is the number of elements in order points_position = [] points_position_raw = [] #positions for points in order to ease the fixation of the cursor label_list = [] #calculate them for i in point_list_range: points_position.append(edge_left + i*line_length/len(range(edges[0],edges[1],step))) points_position_raw.append(edge_left_raw + i*line_length_raw/len(range(edges[0],edges[1],step))) if labels == "numeric" or labels == "num" or labels == "numbers": label_list.append(str(i)) elif isinstance(labels, list): if len(labels) == len(point_list_range): label_list.append(labels[i]) else: print("NEUROPSYDIA ERROR: scale(): wrong labels length") else: print("NEUROPSYDIA ERROR: scale(): labels argument requires a list") if separation_labels != None: if isinstance(separation_labels, list): if force_separation is False: force_separation = len(separation_labels) def draw_all(): #Draw the mask rectangle(x=scale_x, y=scale_y, width=line_length + anchors_space + 8, height=2, line_color=background, thickness=0, fill_color=background) if analog is False: rectangle(x=scale_x, y=scale_y + labels_space, width=line_length + anchors_space + 1,height=3, line_color=background, thickness=0,fill_color=background) if title != None: rectangle(x=scale_x, y=scale_y + title_space, width=line_length*2,height=1,line_color=background,thickness=0,fill_color=background) if show_result is True: rectangle(x=scale_x, y=y-show_result_space,width=line_length + anchors_space + 8,height=show_result_shape_size+0.7, line_color=background, thickness=0, fill_color=background) # Draw the line pygame.draw.line(screen, color(line_color), [edge_left_raw,scale_y_raw], [edge_right_raw,scale_y_raw], line_thickness) if style == "absorption": image('absorption_man.png',x=scale_x, y=scale_y,size=1.3, path=Path.binary()) image('absorption_desk.png',x=edge_right,y=scale_y,size=1.3, path=Path.binary()) #draw the anchors if anchors != None: write(anchors[0], size = anchors_size, x = edge_left - anchors_space,y= scale_y) write(anchors[1], size = anchors_size, x = edge_right + anchors_space,y= scale_y) #display the points and the labels if analog == False: for i in point_list_range: image('scale_point.png', x=points_position[i], y=scale_y, size=0.25, path=Path.binary()) write(label_list[i], x=points_position[i]+labels_x, y=scale_y + labels_space, size=labels_size,rotate=labels_rotation) else: if point_center == True: image('scale_point.png', x=scale_x, y=scale_y, size=0.25, path=Path.binary()) if point_edges == True: image('scale_point.png', x=edge_left, y=scale_y, size=0.25, path=Path.binary()) image('scale_point.png', x=edge_right, y=scale_y, size=0.25, path=Path.binary()) if separation_labels != None: if isinstance(separation_labels, list): for i in range(len(separation_labels)): write(separation_labels[i], x=edge_left + line_length/force_separation*(i+1) - line_length/force_separation/2, y=scale_y+separation_labels_space, size=separation_labels_size, rotate=separation_labels_rotate) else: print("NEUROPSYDIA ERROR: scale(): separation_labels requires a list") if force_separation is False: if isinstance(force_separation, int): for i in range(force_separation-1): image('scale_point.png', x=edge_left + line_length/force_separation*(i+1), y=scale_y, size=0.25) else: print("NEUROPSYDIA ERROR: scale(): force_separation requires a integer") if title != None: write(title, title_style, size=title_size, x=scale_x, y=scale_y + title_space) image(cursor_name, x=cursor_x, y=scale_y, size=cursor_size, path=Path.binary()) if show_result is True: if show_result_shape == "circle": circle(x=cursor_x, y=y-show_result_space, size=show_result_shape_size, fill_color=show_result_shape_fill_color, line_color=show_result_shape_line_color) if show_result_shape == "rectangle": rectangle(x=cursor_x, y=y-show_result_space, width=show_result_shape_size, height=show_result_shape_size, fill_color=show_result_shape_fill_color, line_color=show_result_shape_line_color) if show_result_decimals == 0: write("%.0f" %(edges[0] + (cursor_x - edge_left)*(edges[1]-edges[0])/line_length), x=cursor_x, y=y-show_result_space, size=show_result_size, color=show_result_color) if show_result_decimals == 1: write("%.1f" %(edges[0] + (cursor_x - edge_left)*(edges[1]-edges[0])/line_length), x=cursor_x, y=y-show_result_space, size=show_result_size, color=show_result_color) if show_result_decimals == 2: write("%.2f" %(edges[0] + (cursor_x - edge_left)*(edges[1]-edges[0])/line_length), x=cursor_x, y=y-show_result_space, size=show_result_size, color=show_result_color) refresh() pygame.event.set_allowed(pygame.KEYDOWN) draw_all() loop=True while loop is True: for event in pygame.event.get(): #for each event... cursor_x_raw, cursor_y_raw = pygame.mouse.get_pos() #.get mouse position cursor_x, cursor_y = Coordinates.from_pygame(x=cursor_x_raw, y=cursor_y_raw) # convert the raw position if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: return(np.nan) if event.key in keys["normal"]: if keys["normal"][event.key] == "LEFT" or keys["normal"][event.key] == "RIGHT": pygame.event.set_blocked(pygame.KEYDOWN) return(keys["normal"][event.key]) if cursor_y < scale_y+2 and cursor_y > scale_y-2: if cursor_x < edge_right + 2 and cursor_x > edge_left - 2: #fix the x if cursor_x < edge_left: cursor_x = edge_left if cursor_x > edge_right: cursor_x = edge_right if analog == False: cursor_x = min(points_position, key=lambda a:abs(a-cursor_x)) draw_all() if pygame.mouse.get_pressed()==(1,0,0): #convert the real ratio into the reponse response = edges[0] + (cursor_x - edge_left)*(edges[1]-edges[0])/line_length #what happened when validation is on if validation == True: cursor_name = 'cursor_' + style + '_validated.png' draw_all() mouse_pressed = True while mouse_pressed == True: for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: return(np.nan) if event.key in keys["normal"]: if keys["normal"][event.key] == "LEFT" or keys["normal"][event.key] == "RIGHT": pygame.event.set_blocked(pygame.KEYDOWN) return(keys["normal"][event.key]) if pygame.mouse.get_pressed()==(1,0,0): time.wait(100) mouse_pressed = False loop = False if pygame.mouse.get_pressed()==(0,0,1): cursor_name = 'cursor_' + style + '.png' mouse_pressed = False if reverse == True: response = -1*(response - (edges[0]+edges[1])) pygame.mouse.set_visible(False) pygame.event.set_blocked(pygame.KEYDOWN) return(response)