0

I have an old question looking for a fresh answer. I've tried the recipes presented in the similar but somewhat aged question "Start a flask application in a seperate thread", and some other similar solutions found in other posts.

The long and short of it is, I need to start a flask application in a 'background' thread, such that a wxPython GUI can run in the foreground. The solutions presented here seem to no longer have the desired effect. The flask app starts and the GUI never runs.

My suspicion is, the existing answers are out of date. That said, I'm open to the possibility that I've mangled something else that's hosing it up, please have a peek and advise accordingly.

Thanks for your eyeballs and brain cycles :)

My code follows.

#!/usr/bin/env python
"""
integrator.py (the app)
"""
 
import wx
from pubsub import pub
from flask import Flask
from flask_graphql import GraphQLView
from models import db_session
from schema import schema
from models import engine, db_session, Base, Idiom
 
flaskapp = Flask(__name__)
flaskapp.debug = True
flaskapp.add_url_rule(
        '/graphql',
        view_func=GraphQLView.as_view(
            'graphql',
            schema=schema,
            graphiql=True
        )
)
 
flaskapp.run(threaded=True,use_reloader=False)
 
@flaskapp.teardown_appcontext
def shutdown_session(exception=None):
    db_session.remove()
 
 
class IntegratorTarget(wx.TextDropTarget):
    def __init__(self, object):
        wx.DropTarget.__init__(self)
        self.object = object  
       
    def OnDropText(self, x, y, data):
        print(f"<publish>{data}</publish>")
        pub.sendMessage('default', arg1=data)
        return True
 
       
class IntegratorFrame(wx.Frame):
    def __init__(self, parent, title):
        super(IntegratorFrame, self).__init__(parent, title = title,size = wx.DisplaySize())  
        self.panel = wx.Panel(self)
        box = wx.BoxSizer(wx.HORIZONTAL)  
             
        dropTarget = IntegratorTarget(self.panel)
        self.panel.SetDropTarget(dropTarget)
        pub.subscribe(self.catcher, 'default')
       
        self.panel.SetSizer(box)
        self.panel.Fit()
        self.Centre()
        self.Show(True)
 
    def catcher(self,arg1):
        data = arg1
        print(f"<subscribed>{data}</subscribed>\n\n")
        return
     
       
ex = wx.App()
 
Base.metadata.create_all(bind=engine)
 
IntegratorFrame(None,'Praxis:Integrator')
ex.MainLoop()
 
-- eof --
 
""" models.py """
 
from sqlalchemy import *
from sqlalchemy.orm import (scoped_session, sessionmaker, relationship, backref)
from sqlalchemy.ext.declarative import declarative_base
 
engine = create_engine('sqlite:///.praxis/lexicon/unbound.db3', convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
 
Base = declarative_base()
# We will need this for querying
Base.query = db_session.query_property()
 
class Idiom(Base):
    __tablename__ = "idiomae"
    id = Column(Integer, primary_key=True)
    src = Column(String)                        # the text of the drag/paste operation
    taxonomy = Column(String)                   # the type of resource referenced in the drag/paste operation
    localblob = Column(String)                  # local path to media referenced in 'src'
    timestamp = Column(DateTime)                # date and time of capture
 
-- eof --
 
 
"""  schema.py  """
 
import graphene
from graphene import relay
from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField
from models import db_session, Idiom as IdiomaticModel
 
 
class Idiom(SQLAlchemyObjectType):
    class Meta:
        model = IdiomaticModel
        interfaces = (relay.Node, )
 
 
class Query(graphene.ObjectType):
    node = relay.Node.Field()
    # Allows sorting over multiple columns, by default over the primary key
    all_idioms = SQLAlchemyConnectionField(Idiom.connection)
    # Disable sorting over this field
    # all_departments = SQLAlchemyConnectionField(Department.connection, sort=None)
 
schema = graphene.Schema(query=Query)

1 Answers1

0

I see where you tell Flask to be multi-threaded, but I don't see where you're starting up the Flask app in a thread. I expected to see something like

app = Flask(__name__)
# any extra configuration

def webserver():
    app.run(use_reloader=False)

web_thread = threading.Thread(target=webserver)
web_thread.start()

... continue on with the main thread

I have a working example you can crib from here. Note the need to use appropriate locking of any data structures shared between the primary thread and the threads running Flask.

Dave W. Smith
  • 24,318
  • 4
  • 40
  • 46
  • Hey Dave! Thanks man, you win the day! I did upvote your answer, but due to my (lack of) rep or clout or whatever, it does not show. Frankly, just between us and the fencepost, this site and all the reputation requirements to use it are a goddamn pain. It's no real mystery that so much of the user supplied content is *so old*. I'm sure a lot of people end up just walking away from it rather than spend months or years to jump through all the hoops to develop enough clout to even comment. Cheers and Thanks Again! – James Stallings Jul 17 '20 at 12:31
  • Oh one other thing Dave, you may wish to edit your answer for a small typo: you have '```Flask(__name___)```' which should be '```Flask(__name__)'```. Cheers! – James Stallings Jul 17 '20 at 12:51
  • Oh man y'all, mea culpa. As it turns out, I had a flask app setup/invocation at the head of my source code, and one at the tail. Of course the one in the tail took precedence, and I was likely editing and testing on the one in the head. Of course this led to an appearance of chaos and confusion. – James Stallings Jul 17 '20 at 13:08
  • Fixed the typo. Thanks. – Dave W. Smith Jul 17 '20 at 16:58