title: Execute Python function in main thread inside a Django view
body: <p>I created the Django view 'graph' with the aim of displaying an image created from the matplotlib.pyplot module. I wrote my function <em>plot_bubbles</em> (returns a matplotlib.figure.Figure object) in the script <em>data_analysis.py</em> which is imported in the <em>views.py</em> script.</p>
  
  <p>Tkinter can only run on the main thread, my webpage works as expected the first time that I request it but it does not display the image when refreshed or requested again. My understanding is that Django operates the code on a new thread when the view is requested again.</p>
  
  <p>I tried to use the Queue class and populate it with the function that returns my figure as described in this answer <a href="https://stackoverflow.com/questions/18989446/execute-python-function-in-main-thread-from-call-in-dummy-thread/">Execute Python function in Main thread from call in Dummy thread</a> . Here is my views script.</p>
  
  <pre><code>from django.http import HttpResponse
  from . import data_analysis
  import Queue
  import threading
  
  q = Queue.Queue()
  
  def graph(request):
      parties = ["Conservative Party", "Labour Party", "Green Party", "UKIP"]
  
      def from_other_thread(graph_function):
          q.put(graph_function)
  
      def main_thread_execute():
          callback = q.get()
          fig = callback
          return fig
  
      def grapher(arguments, area_variable):
          data_analysis.plt.close('all')
          from_other_thread(data_analysis.plot_bubbles(arguments, area_variable))
  
      t = threading.Thread(target = grapher, args=(parties, data_analysis.all_data['2015 Pay']))
      t.start()
      t.join()
      fig = main_thread_execute()
      response = HttpResponse(content_type='image/png')
      fig.savefig(response, format='png')
      return response
  </code></pre>
  
  <p>The aim is to run the function in the main thread so that Tkinter can actually work and the image be created I want the image to be created every time that the url is requested as I will let the user choose the variables that he wants to visualize via a form, passing them as the arguments of the plot_bubbles function. </p>
  
  <p>I am a beginner in django and never used multi-threading in my code, thank you for reading this. Any explanation on your solution is greatly appreciated.</p>
  
  <h1>EDIT</h1>
  
  <p>Threading is not necessarily needed. The problem was originating from the way that my data_analysis script was generating the plot. In particular the code called for the method matplotlib.pyplot.subplots <a href="http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.subplots" rel="nofollow noreferrer">http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.subplots</a> to generate a figure with 4 axes objects as in here</p>
  
  <pre><code>import matplotlib.pyplot as plt
  fig, ax = plt.subplots(2,2, figsize(15,12))
  </code></pre>
  
  <p>This seemed to cause Tkinter to not operate on the main loop, I have not fully understood the reason yet. This is how the code looks now</p>
  
  <pre><code>#!/usr/bin/python
  # -*- coding: utf-8 -*-
  import pandas as pd
  import numpy as np
  from pandas import Series, DataFrame
  from pylab import figure, axes, plot, title, subplots
  import statsmodels.api as sm
  from sqlalchemy import create_engine
  from matplotlib.backends.backend_agg import FigureCanvasAgg
  import matplotlib
  
  # Load data from database into dataframe
  
  engine = create_engine("postgresql://user_name:password@localhost:5432/brexit")
  
  all_data = pd.read_sql('''SELECT * FROM records;''', engine,     index_col='Borough')
  
  # Bubble Plot function creation
  
  colors = np.random.rand(len(all_data.index))
  
  area = []
  
  def plot_bubbles(arguments, area_variable, space=0):
  
      ah = iter(arguments)
  
      eh = iter(arguments)
  
      ih = iter(arguments)
  
      kh = iter(arguments)
  
      th = iter(arguments)
  
      zh = iter(arguments)
  
      mh = iter(arguments)
  
      fig = figure(figsize=(30, 25))
  
      ax1 = fig.add_subplot(2,2,1)
  
      ax2 = fig.add_subplot(2,2,2)
  
      ax3 = fig.add_subplot(2,2,3)
  
      ax4 = fig.add_subplot(2,2,4)
  
      collection = [ax1, ax2, ax3, ax4]
  
      for x in area_variable:
          #want the bubbles to have an average area of 40, add a factor to     increase the variability in size
          factor = ((x-area_variable.mean())**2/400)
          area.append(factor*x*(40/area_variable.mean()))
      for ax in collection:
          orient = all_data[ah.next()]
          ax.set_ylabel('Leave %')
          ax.set_xlim([max(0, all_data[zh.next()].min()-all_data[mh.next()].min()/3),
       all_data[ih.next()].max()+all_data[th.next()].max()/7])
          results = sm.OLS(all_data['Leave votes'], sm.add_constant(orient)).fit()
          X_plot = np.linspace(orient.min()-0.05, orient.max(), 100)
          ax.plot(X_plot, X_plot*results.params[1] + results.params[0], )
          for label, ori, leave in zip(all_data.index, orient, all_data['Leave votes']):
              ax.annotate(label, xy=(ori, leave), xytext=(ori, leave+0.05), 
              arrowprops={'facecolor':'black', 'connectionstyle':'arc3,rad=0.3', 'arrowstyle':'simple'})
          ax.scatter(orient, all_data['Leave votes'], s=area, c=colors, alpha=0.6)
          ax.set_title(kh.next())
      fig.subplots_adjust(hspace=space, wspace=space)
      return fig
  </code></pre>
  
  <p>With the change in the way the figure and axes are created the problem is solved. Would be interesting if someone had an explanation as to why this is the case.</p>