def place_cursor(self): rows = self.text.split("\n") prop = self.actor.GetTextProperty() cursor_top = self.top cursor_left = self.left max_width, total_height = text_dimensions(self.text, prop) _, before_h = text_dimensions("\n".join(rows[:self.row]), prop) if len(rows) > self.row + 1: _, after_h = text_dimensions("\n".join(rows[self.row + 1:]), prop) row_height = total_height - (after_h + before_h) else: row_height = total_height - before_h cursor_top += before_h row = rows[self.row] row_width, _ = text_dimensions(row, prop) before = row[:self.column] before_w, _ = text_dimensions(before, prop) cursor_left += before_w cursor_left += (max_width - row_width) / 2.0 if self.cursor: self.cursor.detach() del self.cursor self.cursor = Button(self.interactor, left=cursor_left, top=cursor_top, width=2, height=row_height, corner_radius=0, bgcolor=(0,0,0)) self.cursor.show()
def update(self): self.repr.SetNumberOfStates(len(self.states)) dpi = self.interactor.GetRenderWindow().GetDPI() max_width = 0 max_height = 0 for index, state in enumerate(self.states): # Set up attributes with defaults if nothing is set label_text = state.label if state.label else self.label image = state.image if state.image else self.image if image: # Image supersedes label w, h, _ = image.GetDimensions() # Use a 3 px padding for now max_height = max(max_height, h) max_width = max(max_width, w) elif label_text: l_w, l_h = text_dimensions( label_text, self.text_widget.actor.GetTextProperty(), dpi) max_height = max(max_height, l_h) max_width = max(max_width, l_w) # Pad the text max_width += 2 * BUTTON_MARGIN max_height += 2 * BUTTON_MARGIN for index, state in enumerate(self.states): image = state.image if state.image else self.image bgcolor = state.bgcolor if state.bgcolor else self.bgcolor # Opacity not yet supported by this code # opacity = state.opacity if state.opacity else self.opacity # Something weird happens when images of drastically different sizes are passed in; # not hunting down that fix right now. if image is not None: width, height, _ = image.GetDimensions() else: width = self.width if self.width else int(max_width) height = self.height if self.height else int(max_height) # Optimization can be done here; can use the same image for # everything with same bgcolor + h/w bg_image = rounded_rect(width, height, self.radius, bgcolor) if image is not None: image = pad_image(image, max_width, max_height) bg_image = combine_images(bg_image, image) # Should deal with opacity here-ish self.repr.SetButtonTexture(index, bg_image)
def place_cursor(self): rows = self.text.split("\n") prop = self.actor.GetTextProperty() cursor_top = self.top cursor_left = self.left max_width, total_height = text_dimensions(self.text, prop) _, before_h = text_dimensions("\n".join(rows[:self.row]), prop) if len(rows) > self.row + 1: _, after_h = text_dimensions("\n".join(rows[self.row + 1:]), prop) row_height = total_height - (after_h + before_h) else: row_height = total_height - before_h cursor_top += before_h row = rows[self.row] row_width, _ = text_dimensions(row, prop) before = row[:self.column] before_w, _ = text_dimensions(before, prop) cursor_left += before_w cursor_left += (max_width - row_width) / 2.0 if self.cursor: self.cursor.detach() del self.cursor self.cursor = Button(self.interactor, left=cursor_left, top=cursor_top, width=2, height=row_height, corner_radius=0, bgcolor=(0, 0, 0)) self.cursor.show()
def row_col_at_point(self, x, y): rows = self.text.split("\n") prop = self.actor.GetTextProperty() text_width, text_height = text_dimensions(self.text, prop) # Viewport coordinates of widget sw, sh = self.interactor.GetRenderWindow().GetSize() x0, y0 = self.left, sh - self.top # Adjust click coords to widget's bounds x = abs(x - x0) y = text_height - abs(y - y0) # Calculate the bounds of each row row_bounds = [] max_width = 0 # We're iterating in reverse because y goes from 0 at the bottom to 1 at the top row_at_point = None for row in rows[::-1]: if row == '': row = ' ' w, h = text_dimensions(row, prop) row_bounds.append((w, h)) if w > max_width: # Use for x offset calculations max_width = w if h < y and row_at_point is None: y = y - h else: row_at_point = rows.index(row) if row_at_point is None: row_at_point = len(rows) - 1 # List was assembled backwards row_bounds.reverse() # Now let's find the column clicked... row = row_bounds[row_at_point] text = rows[row_at_point] # If max_width == row[0], then offset is 0 and all of the calcs below still work x -= (max_width - row[0]) / 2.0 row_left = 0 row_right = row[0] if x < row_left: # Clicked to the left of the row return row_at_point, 0 if x > row_right: # Clicked to the right of the row return row_at_point, len(rows[row_at_point]) if row == '': # Clicked on the blank row (inserted a space when calculating width earlier, for height considerations) return row_at_point, 1 # OK, no easy answer; have to calc the width of each letter in the row till we find the column # Start at left or right depending on which side the click is closer to if x - row_left > row_right - x: # Start from right for reverse_index, c in enumerate(text[::-1]): w,_ = text_dimensions(c, prop) if row_right - w < x: return row_at_point, len(text) - reverse_index # "New" right side is one character back row_right -= w else: # Start from left for index, c in enumerate(text): w, _ = text_dimensions(c, prop) if row_left + w > x: return row_at_point, index # "New" left side is one character forward row_left += w # Return the very end of the box if we can't figure it out. return len(rows) - 1, len(rows[len(rows) - 1])
def row_col_at_point(self, x, y): rows = self.text.split("\n") prop = self.actor.GetTextProperty() dpi = self.interactor.GetRenderWindow().GetDPI() text_width, text_height = text_dimensions(self.text, prop, dpi) # Viewport coordinates of widget sw, sh = self.interactor.GetRenderWindow().GetSize() # Normalize to window space if x < 1 and y < 1: x = sw * x y = sh * y # Rotate the click out of box space x -= self.x y -= self.y x, y = rotate((x, y), -1 * prop.GetOrientation()) x += self.x y += self.y x0, y0 = self.left, sh - self.top # Adjust click coords to widget's bounds x = abs(x - x0) y = text_height - abs(y - y0) # Calculate the bounds of each row row_bounds = [] max_width = 0 # We're iterating in reverse because y goes from 0 at the bottom to 1 # at the top row_at_point = None for row in rows[::-1]: if row == '': dim_row = ' ' else: dim_row = row w, h = text_dimensions(dim_row, prop, dpi) row_bounds.append((w, h)) if w > max_width: # Use for x offset calculations max_width = w if h < y: y = y - h else: if row_at_point is None: row_at_point = rows.index(row) if row_at_point is None: row_at_point = 0 # List was assembled backwards row_bounds.reverse() # Now let's find the column clicked... row = row_bounds[row_at_point] text = rows[row_at_point] # If max_width == row[0], then offset is 0 and all of the calcs below # still work just = prop.GetJustificationAsString() if just == "Left": row_left = 0 elif just == "Centered": row_left = (max_width - row[0]) / 2. elif just == "Right": row_left = max_width - row[0] row_right = row_left + row[0] if x < row_left: # Clicked to the left of the row return row_at_point, 0 if x > row_right: # Clicked to the right of the row return row_at_point, len(rows[row_at_point]) if row == '': # Clicked on the blank row (inserted a space when calculating width # earlier, for height considerations) return row_at_point, 1 # OK, no easy answer; have to calc the width of each letter in the row till we find the column # Start from left w = 0 ind = 1 while row_left + w < x and ind < len(text): w, _ = text_dimensions(text[:ind], prop, dpi) ind += 1 return row_at_point, ind - 1
def place_cursor(self): # Find current position of the text actor x, y = self.left, self.top # Use to adjust all window-space numbers w, h = self.interactor.GetRenderWindow().GetSize() # Translate distance from top to distance from bottom y = h - y # Prep a text property for getting measurements prop = vtk.vtkTextProperty() prop.ShallowCopy(self.actor.GetTextProperty()) # Store for rotating the cursor's coords angle = prop.GetOrientation() # Reset so we get accurate dimensions prop.SetOrientation(0) rows = self.text.split("\n") dpi = self.interactor.GetRenderWindow().GetDPI() width, height = text_dimensions(self.text, prop, dpi) line_height = float(height) / len(rows) column_adjustment, _ = text_dimensions(rows[self.row][:self.column], prop, dpi) x += column_adjustment row_width, _ = text_dimensions(rows[self.row], prop, dpi) # Adjust for blank space caused by justifications halign = prop.GetJustificationAsString() if halign == "Centered": x += (width - row_width) / 2. elif halign == "Right": x += (width - row_width) + 1 # Adjust for some margin issues elif halign == "Left": x -= 3 # Adjust for some margin issues # Manual adjustments for justification artefacts valign = prop.GetVerticalJustificationAsString() if valign == "Top": y += 2 elif valign == "Centered": pass elif valign == "Bottom": y -= 2 # Get to the current row y -= line_height * self.row # Rotate both points to the orientation as the text y1 = y y2 = y - line_height x1 = x x2 = x x1, x2 = x1 - self.x, x2 - self.x y1, y2 = y1 - self.y, y2 - self.y x1, y1 = rotate((x1, y1), angle) x2, y2 = rotate((x2, y2), angle) x1 += self.x x2 += self.x y1 += self.y y2 += self.y self.cursor.point_1 = (x1, y1) self.cursor.point_2 = (x2, y2)
def row_col_at_point(self, x, y): rows = self.text.split("\n") prop = self.actor.GetTextProperty() text_width, text_height = text_dimensions(self.text, prop) # Viewport coordinates of widget sw, sh = self.interactor.GetRenderWindow().GetSize() x0, y0 = self.left, sh - self.top # Adjust click coords to widget's bounds x = abs(x - x0) y = text_height - abs(y - y0) # Calculate the bounds of each row row_bounds = [] max_width = 0 # We're iterating in reverse because y goes from 0 at the bottom to 1 at the top row_at_point = None for row in rows[::-1]: if row == '': row = ' ' w, h = text_dimensions(row, prop) row_bounds.append((w, h)) if w > max_width: # Use for x offset calculations max_width = w if h < y and row_at_point is None: y = y - h else: row_at_point = rows.index(row) if row_at_point is None: row_at_point = len(rows) - 1 # List was assembled backwards row_bounds.reverse() # Now let's find the column clicked... row = row_bounds[row_at_point] text = rows[row_at_point] # If max_width == row[0], then offset is 0 and all of the calcs below still work x -= (max_width - row[0]) / 2.0 row_left = 0 row_right = row[0] if x < row_left: # Clicked to the left of the row return row_at_point, 0 if x > row_right: # Clicked to the right of the row return row_at_point, len(rows[row_at_point]) if row == '': # Clicked on the blank row (inserted a space when calculating width earlier, for height considerations) return row_at_point, 1 # OK, no easy answer; have to calc the width of each letter in the row till we find the column # Start at left or right depending on which side the click is closer to if x - row_left > row_right - x: # Start from right for reverse_index, c in enumerate(text[::-1]): w, _ = text_dimensions(c, prop) if row_right - w < x: return row_at_point, len(text) - reverse_index # "New" right side is one character back row_right -= w else: # Start from left for index, c in enumerate(text): w, _ = text_dimensions(c, prop) if row_left + w > x: return row_at_point, index # "New" left side is one character forward row_left += w # Return the very end of the box if we can't figure it out. return len(rows) - 1, len(rows[len(rows) - 1])