{"spec_id":"bar-horizontal","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nbar-horizontal: Horizontal Bar Chart\nLibrary: bokeh 3.9.2 | Python 3.13.14\nQuality: 93/100 | Updated: 2026-08-05\n\"\"\"\n\nimport os\nimport sys\nimport time\nfrom pathlib import Path\n\n\nif sys.path and sys.path[0] in (\"\", \".\"):\n    sys.path.pop(0)\n\nfrom bokeh.io import output_file, save\nfrom bokeh.models import ColumnDataSource, HoverTool, LabelSet, PrintfTickFormatter\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Data - Top Programming Languages by Developer Popularity (%)\ncategories = [\"JavaScript\", \"Python\", \"TypeScript\", \"Java\", \"C#\", \"C++\", \"PHP\", \"Go\", \"Rust\", \"Kotlin\"]\nvalues = [65.6, 49.3, 38.5, 33.3, 28.7, 22.4, 18.2, 14.3, 13.1, 9.2]\n\n# Sort by value (smallest to largest for bottom-to-top display)\nsorted_data = sorted(zip(categories, values, strict=True), key=lambda x: x[1])\ncategories_sorted = [x[0] for x in sorted_data]\nvalues_sorted = [x[1] for x in sorted_data]\nlabels_sorted = [f\"{v:.1f}%\" for v in values_sorted]\n\n# Split the leader (top bar, highest value) from the rest so it can carry a\n# focal-point emphasis (thicker edge stroke + bolder, larger value label)\n# without introducing a second hue into the single-series palette.\nrest_source = ColumnDataSource(\n    data={\"categories\": categories_sorted[:-1], \"values\": values_sorted[:-1], \"labels\": labels_sorted[:-1]}\n)\nleader_source = ColumnDataSource(\n    data={\"categories\": categories_sorted[-1:], \"values\": values_sorted[-1:], \"labels\": labels_sorted[-1:]}\n)\n\n# Create figure with categorical y-axis (3200 x 1800 px, the canonical landscape canvas)\np = figure(\n    width=3200,\n    height=1800,\n    y_range=categories_sorted,\n    x_axis_label=\"Developer Popularity (%)\",\n    title=\"bar-horizontal · python · bokeh · anyplot.ai\",\n    toolbar_location=None,  # bokeh's default toolbar adds ~30-50px above the plot,\n    # which would shrink the saved screenshot below the 3200x1800 target.\n    min_border_bottom=160,  # room for 34pt x-tick labels + 42pt x-axis label\n    min_border_left=320,  # room for 34pt category tick labels (up to \"TypeScript\")\n    min_border_top=110,  # room for 50pt title\n    min_border_right=80,  # room for value labels near the right edge\n)\n\n# Draw horizontal bars with Okabe-Ito color #009E73; the leader (top bar) gets\n# a heavier edge stroke as a focal-point device to sharpen the ranking story\nrest_bars = p.hbar(\n    y=\"categories\",\n    right=\"values\",\n    height=0.7,\n    source=rest_source,\n    color=\"#009E73\",\n    line_color=INK,\n    line_width=2,\n    alpha=0.9,\n)\nleader_bars = p.hbar(\n    y=\"categories\",\n    right=\"values\",\n    height=0.7,\n    source=leader_source,\n    color=\"#009E73\",\n    line_color=INK,\n    line_width=4,\n    alpha=1.0,\n)\n\n# End-of-bar value labels for direct, at-a-glance reading of each value; the\n# leader's label is bolder and larger, reinforcing it as the headline insight\nvalue_labels = LabelSet(\n    x=\"values\",\n    y=\"categories\",\n    text=\"labels\",\n    source=rest_source,\n    x_offset=12,\n    text_font_size=\"28pt\",\n    text_color=INK_SOFT,\n    text_baseline=\"middle\",\n)\np.add_layout(value_labels)\n\nleader_label = LabelSet(\n    x=\"values\",\n    y=\"categories\",\n    text=\"labels\",\n    source=leader_source,\n    x_offset=12,\n    text_font_size=\"34pt\",\n    text_font_style=\"bold\",\n    text_color=INK,\n    text_baseline=\"middle\",\n)\np.add_layout(leader_label)\n\n# Distinctive bokeh interactivity: formatted hover tooltips on both HTML renders\np.add_tools(\n    HoverTool(\n        renderers=[rest_bars, leader_bars], tooltips=[(\"Language\", \"@categories\"), (\"Popularity\", \"@values{0.0}%\")]\n    )\n)\n\n# Style title\np.title.text_font_size = \"50pt\"\np.title.align = \"center\"\np.title.text_color = INK\n\n# Style axes for the 3200x1800 canvas\np.xaxis.axis_label_text_font_size = \"42pt\"\np.yaxis.axis_label_text_font_size = \"42pt\"\np.xaxis.major_label_text_font_size = \"34pt\"\np.yaxis.major_label_text_font_size = \"34pt\"\np.xaxis.axis_label_standoff = 20\np.yaxis.axis_label_standoff = 20\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_color = INK\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_color = INK_SOFT\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.major_tick_line_color = INK_SOFT\np.yaxis.major_tick_line_color = INK_SOFT\np.xaxis.formatter = PrintfTickFormatter(format=\"%d%%\")\n\n# Configure grid\np.xgrid.grid_line_color = INK\np.xgrid.grid_line_alpha = 0.12\np.ygrid.grid_line_alpha = 0\n\n# Theme-adaptive background\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\n# Drop the default four-sided box outline; the x/y axis lines already draw a\n# refined L-shaped frame (left + bottom) without boxing the plot area in\np.outline_line_color = None\n\n# Set x-axis range starting from 0, with headroom for the end-of-bar labels\np.x_range.start = 0\np.x_range.end = 78\n\n# Save as HTML for interactivity\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\n# Screenshot with headless Chrome\nW, H = 3200, 1800\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\n# Headless Chrome's --window-size sets the OUTER window, which still reserves a\n# phantom title-bar height even headless — pin the viewport exactly via CDP so\n# the screenshot lands at exactly W x H instead of coming out short.\ndriver.execute_cdp_cmd(\n    \"Emulation.setDeviceMetricsOverride\", {\"width\": W, \"height\": H, \"deviceScaleFactor\": 1, \"mobile\": False}\n)\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}