Open Toozig opened 4 months ago
The first draw of the plot happens to work via side-effects -- @render.plot
is capturing the matplotlib side-effects that happen when get_plot
gets executed (via get_cur_plot()
).
Part of where things have gone wrong here is that you've used @reative.calc
to decorate get_cur_plot()
, which doesn't return a value. A reactive calculation should always return
a value (because if it's return value doesn't change, downstream reactive calculations won't know to re-execute). If you intentionally don't want to return a value, you likely want a @reactive.effect
, not @reactive.calc
, since the former is used for it's side-effects, not it's return value.
All that being said, I don't think you need either of those decorators in this case, just do have your helper function (which doesn't need to return a value):
def draw_plot(family, peak, source):
# create a random matplotlib plot vased on chosen family
# plt.figure(figsize=(10, 5))
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y)
plt.title(f"Family {family}, source: {source}")
plt.ylabel(f"peaks {peak}")
Then call it in the @render.plot
:
@render.plot()
def track_plot():
draw_plot(input.family(), input.peak(), input.source())
I've hit a snag with plot rendering.
The issue is that whenever I resize the window, the plot disappears. However, setting
@render.plot(width=500, height=200)
keeps it stable.I've tried various solutions without success.
Full code
```python from shiny.express import input, render, ui from shiny import reactive, req import matplotlib.pyplot as plt import numpy as np import pandas as pd import random MAX_SCORE_TFBS = 'maximal score TFBS per site' SHOW_SEQ = 'Show sequence' FAMILY = 'family' PEAK = 'peak' SOURCE = 'source' CHECKBOX = 'checkbox' N_LINES = 'n_lines' SCORE_THRESHOLD = 'score_threshold' family_list = ["Alice", "Bob", "Charlie"] peak_list = ["Peak1", "Peak2", "Peak3"] source_list = ['JASPAR','HOMER'] checkbox_options = [MAX_SCORE_TFBS, SHOW_SEQ] def get_random_data2(family, peak, source): random_int = random.randint(0, 100) random_int2 = random.randint(0, 100) #create random DF with 4 rows return pd.DataFrame({ 'family': [family]*4, 'score': [random.randint(0, 100), random.randint(0, 100), random.randint(0, 100), random.randint(0, 100)], 'peak': [peak]*4, 'source': [source + ' ' + str(i) for i in range(4)], 'maximal score TFBS per site': [random_int2, random_int2, random_int2, random_int2], 'Show sequence': ['Show sequence']*4 }) def get_update_list(family): fam_index = family_list.index(family) return peak_list[fam_index:] def get_score_min_max(source_name, peak_id): random_int = random.randint(0, 100) return random_int, random_int + 100 def get_random_data(family): random_int = random.randint(0, 100) return pd.DataFrame({ 'family': [family], 'score': [random_int], 'peak': ['Peak1'], 'source': ['JASPAR'], 'maximal score TFBS per site': [random_int], 'Show sequence': ['Show sequence'] }) ##score threshold vars min_score = 0 max_score = 100 ui.page_opts(title="Family Viewer", fillable=True) with ui.sidebar(): # "Sidebar (input)" ui.input_selectize(FAMILY, "Family", family_list) ui.input_selectize(PEAK, 'Peak', peak_list) ui.input_selectize(SOURCE, 'Source', source_list) ui.input_slider(SCORE_THRESHOLD, 'Score Threshold', min=min_score, max=max_score, step=1, value=min_score + max_score // 2) ui.input_checkbox_group(CHECKBOX, 'Options', checkbox_options) ui.input_numeric(N_LINES, 'Number of lines', 2) # this is how to change the peak list based on the family selection @reactive.effect def change_peak_list(): choices = get_update_list(input.family()) ui.update_selectize(PEAK, choices=choices) # this is to update the slider based on the source and peak selection @reactive.effect def change_score_threshold(): min_score, max_score = get_score_min_max(input.source(), input.peak()) ui.update_slider(SCORE_THRESHOLD, min=min_score, max=max_score, value=min_score + max_score // 2) @reactive.calc def update_family(): cur_fam_df = get_random_data(input.family()) return cur_fam_df @reactive.calc def update_peak_data(): cur_fam_df = get_random_data(input.peak()) return cur_fam_df @reactive.calc def update_variant_list(): cur_fam_df = get_random_data2(input.family(), input.peak(), input.source()) return cur_fam_df def get_plot(family, peak, source): # create a random matplotlib plot vased on chosen family # plt.figure(figsize=(10, 5)) x = np.linspace(0, 10, 100) y = np.sin(x) plt.plot(x, y) plt.title(f"Family {family}, source: {source}") plt.ylabel(f"peaks {peak}") return plt @reactive.Calc def get_cur_plot(): get_plot(input.family(), input.peak(), input.source()) with ui.layout_columns(col_widths=[6, 6, 12]): with ui.card(full_screen=True): ui.card_header("family data") @render.data_frame def family_details(): return render.DataGrid(update_family(), row_selection_mode="multiple") with ui.card(full_screen=True): ui.card_header("peak data") @render.data_frame def peak_details(): return render.DataGrid(update_peak_data(), row_selection_mode="single") with ui.card(full_screen=True): # ui.card_header("peak data") @render.plot() def track_plot(): get_cur_plot() with ui.layout_columns(col_widths=[6, 6, 12]): with ui.card(full_screen=True): ui.card_header("Peak variants") @render.data_frame def variant_list(): return render.DataGrid(update_variant_list(), row_selection_mode="single") @reactive.calc def update_variant_information(): req(input.variant_list_selected_rows()) cur_fam_df = get_random_data(str(input.variant_list_selected_rows())) return cur_fam_df with ui.card(full_screen=True): ui.card_header("variant information") @render.data_frame def variant_information(): return render.DataGrid(update_variant_information(), row_selection_mode="single") ```