Python: How to create a separate module for the GUI class - python

This code is working fine. MyApp is the class doing all the work and MyGUI is the user interface showing and requesting data from MyApp.
class MyGUI(): # displays results from MyApp and sends request to MyApp (e.g. fetch prices new prices)
def __init__(self):
print("GUI running")
def user_request_price(self,ticker):
self.req_price(ticker)
# methods I request from MyApp
def req_price(self,ticker):
app.get_price(ticker)
# methods I receive from MyApp
def print_price(self,val,price):
print (val,":",price)
class MyApp(): # does a lot of stuff, e.g. fetch prices from a server
def __init__(self):
self.id = 0
self.gui = MyGUI() # start gui
# methods called by GUI
def get_price(self, ticker):
if ticker == "MSFT": price = 20.23
self.output_price(ticker,price)
# methods sent to GUI
def output_price(self,ticker,price):
self.gui.print_price(ticker,price)
if __name__ == "__main__":
app = MyApp()
app.gui.user_request_price("MSFT")
Now I want to put the GUI into a separate module so creating a module file gui.py and import that in the MyApp file:
from gui import *
and that's it. Where I struggle: how does gui.py look like and how can MyGUI() access MyApp methods? Is it wise to do this separation? Any other suggestions for structuring?

The gui.py file would just be
class MyGUI(): # displays results from MyApp and sends request to MyApp (e.g. fetch prices new prices)
def __init__(self):
print("GUI running")
def user_request_price(self,ticker):
self.req_price(ticker)
# methods I request from MyApp
def req_price(self,ticker):
app.get_price(ticker)
# methods I receive from MyApp
def print_price(self,val,price):
print (val,":",price)
Add the import to the top of myapp.py and everything should work fine.
I try and Seperate out my code into seperate files if it makes sense. It makes reading things much clearer.

in your gui.py
from myapp import MyApp
class MyGUI(): # displays results from MyApp and sends request to MyApp (e.g. fetch prices new prices)
app = MyApp()
def __init__(self):
print("GUI running")
def user_request_price(self,ticker):
self.req_price(ticker)
# methods I request from MyApp
def req_price(self,ticker):
app.get_price(ticker)
# methods I receive from MyApp
def print_price(self,val,price):
print (val,":",price)
And in your myapp.py (Notice the first line), And make sure both the files are in same directory or else you must change your import relatively.
from gui import MyGUI
class MyApp(): # does a lot of stuff, e.g. fetch prices from a server
def __init__(self):
self.id = 0
self.gui = MyGUI() # start gui
# methods called by GUI
def get_price(self, ticker):
if ticker == "MSFT": price = 20.23
self.output_price(ticker,price)
# methods sent to GUI
def output_price(self,ticker,price):
self.gui.print_price(ticker,price)
if __name__ == "__main__":
app = MyApp()
app.gui.user_request_price("MSFT")

Finally I did this - seems to be the best approach to have a clear separation and communication between app and gui.
Gui:
import queue
def __init__(self):
threading.Thread.__init__(self)
self.requests = queue.Queue() # request queue for App
self.start()
def queue_request(self,reqId,val):
self.requests.put([reqId,val])
APP:
import threading
import queue
def checkGUIQueue(self):
threading.Timer(1.0, self.checkGUIQueue).start() # check every 1 second
while not self.gui.requests.empty():
(id,value) = self.gui.requests.get()
... process request ...

Related

Access PyQt5 Objects from outside the MainWin Class

I am developing an application that reads MS Access DBs to produce extracts and data manipulation into SharePoint. One of my challenges is that I will need to write a lengthy GUI interface with many simple events. To keep the code organized I will be writing subroutines and import into the main program. As a result there will be code that needs to run outside the class and access objects inside the class. I am having a problem determining the proper syntax.
Or is there a way to import directly into the class to avoid the redefining issue?
In the InitUI there is a call to subroutine called TestIt() in that sub routine I print a message to verify the code is getting called. Then I try to set the text value of a Single Line Edit object from the main window. The question what is the proper way to call it? Do I need to redefine the variable.
The main program is this:
from PyQt5 import QtCore, QtGui, QtWidgets
from Main import Ui_MainWin
import sys, time, os, configparser
import pyodbc
from ServerQueue import *
class MainWindow_EXEC():
def __init__(self): # This section has to be laid out this way
app = QtWidgets.QApplication(sys.argv)
win = QtWidgets.QMainWindow()
self.ui = Ui_MainWin()
self.ui.setupUi(win)
self.initUI() # Inits that need to happen before start up
win.setWindowTitle("This is the Title!")
#win.resize(800,600)
win.show()
sys.exit(app.exec_())
def initUI(self):
TestIt()
self.ui.cmbAppCodes.currentIndexChanged.connect(self.selectionchange)
try:
self.conn = pyodbc.connect(r'Driver={Microsoft Access Driver
(*.mdb,*.accdb)};DBQ='+ dbPath + ';',autocommit=True)
self.cursor = self.conn.cursor()
except:
print("Error opening the database!")
qApp.quit()
print(dbPath)
self.loadAppCodes()
def loadAppCodes(self):
self.ui.cmbAppCodes.clear()
sql = "SELECT [App Code], [App Name] from [App List] ORDER BY [App Code];"
self.cursor.execute(sql)
res = self.cursor.fetchall()
for code in res:
self.ui.cmbAppCodes.addItem("{} - {}".format(code[0], code[1]))
def selectionchange(self,i):
App = self.ui.cmbAppCodes.itemText(i)
sql = "SELECT * FROM [LCM Application List] WHERE [App Code] = '" + App[0:3] + "';"
self.cursor.execute(sql)
res = self.cursor.fetchone()
self.ui.lneAPM.setText(res[3])
self.ui.lneTPM.setText(res[21])
self.ui.lneAPO.setText(res[4])
#-----------------------------------------------------------------------------
# Load up the init file
#-----------------------------------------------------------------------------
def iniSettings():
if sys.platform == "win32":
os.system('cls')
Home = os.path.expanduser('~') + "\\"
D = "W"
else:
os.system('clear')
Home = os.path.expanduser('~') + "/"
D = "O"
config = configparser.ConfigParser()
config.read(Home + 'LCM.ini') # Path to LCM.ini file
Configs = config['DEFAULT'] # Read the DEFAULT section
return [Home, Configs['LCMDB'], Configs['CR1'], D]
##########################################################################################
# Main Body
###########################################################################################
if __name__ == "__main__":
Paths = iniSettings() # This is to pull the ini data for paths and what not.
hmPath = Paths[0]
dbPath = Paths[1]
cr1Path = Paths[2]
myOS = Paths[3]
print("Home: {}\nCR1: {}\nDatabase: {}\n".format(hmPath, cr1Path, dbPath))
MainWindow_EXEC() # Becuase the init is laid out this way this is all that is needed
print("Application completed")enter code here
#This file is imported and is called ServerQueue.py
def TestIt():
print("TestIt")
self.ui.lneAPM.setText("Testing")
#-----------------------------------------------------------------------------
# Main Processing Loop
#-----------------------------------------------------------------------------
if __name__ == "__main__":
print("\n ********* \n This file is not meant to be run directly! \n ********* \n")
You are doing everything backwards. You are trying to get the elements of business logic to consume the GUI, but the correct logic is the reverse: The GUI consumes the information from the elements of business logic. Considering the above, the Test () function should not modify the GUI directly, but provide the information to the GUI, one way to implement that is to return the text.
def TestIt():
print("TestIt")
return "Testing"
# ...
def initUI(self):
self.ui.lneAPM.setText(TestIt())
# ...
If the above does not solve the problem then you probably have an XY problem so I recommend you to rewrite your question better providing a better MRE

How to pass arguments to a page class with web.py?

I have several classes in my program.
The main one called WebServer creates the web.py application itself, and calls to other classes for the webpages. Can I pass self.var1 for example to the search class __init__? Because I thought of just creating a method in the index class like set_var1 or something like that, then I don't know how to access the specific instance of this class the the web application creates.
The class:
import sys
import os
import web
from pages.search import search
from pages.index import index
class WebServer:
def __init__(self):
self.var1 = "test"
self.urls = (
'/', 'index',
'/search', 'search'
)
self.app = web.application(self.urls, globals())
self.app.run()
if __name__ == "__main__":
w = WebServer()
Not really, no. Specific instances of search and index are created by web.py in response to an incoming request. There are better / easier ways.
Also, putting this initialization in a WebServer class, while possible, isn't the common way of doing it with web.py. There's no need for the class to do this: it's a singleton and this file is essentially a startup / configuration file.
To have application-wide information available to your "response" classes (search, index, etc.), make that information either global, or hook it into web.config which is a web.Storage(). For example:
app = web.application(urs, globals())
web.config.update({"var1" : "test"})
app.run()
Which is then available to you responses. For example:
class search(object):
def GET(self):
if web.config.var1 == 'test':
return do_test_search()
return do_regular_search()

Is it possible to use the "app factory" pattern from Flask with Click CLI applications?

Imagine I have a big CLI application with many different commands (think, for example image-magick).
I wanted to organize this application into modules and etc. So, there would be a master click.group somewhere:
#main.py file
#click.group()
def my_app():
pass
if __name__ == "__main__":
my_app()
that can be imported in each module that define a command:
from main import my_app
# command_x.py
#my_app.command()
def command_x():
pass
The problem is that I run into a circular import problem, since the main.py file knows nothing about command_x.py and I would have to import it before calling the main section.
This happens in Flask too and is usually dealt with the app factory pattern. Usually you would have the app being created before the views:
app = Flask("my_app")
#my_app.route("/")
def view_x():
pass
if __name__ == "__main__":
app.run()
In the app factory pattern you postpone the "registration" of the blueprints:
# blueprints.py
blueprint = Blueprint(yaddayadda)
#blueprint.route("/")
def view_x():
pass
And make a factory that knows how to build the app and register the blueprints:
#app_factory.py
from blueprints import view_x
def create_app():
app = Flask()
view_x.init_app(app)
return app
And you can then create a script to run the app:
#main.py
from app_factory import create_app
if __name__ == "__main__":
app = create_app()
app.run()
Can a similar pattern be used with Click? Could I just create a "click app" (maybe extending click.Group) where I register the "controllers" which are the individual commands?
Maybe late, but I was also searching for a solution to put commands to separate modules. Simply use a decorator to inject commands from modules:
#main.py file
import click
import commands
def lazyloader(f):
# f is an instance of click.Group
f.add_command(commands.my_command)
return f
#lazyloader
#click.group()
def my_app():
pass
if __name__ == "__main__":
my_app()
The separated command can use the usual decorators from click.
#commands.py
import click
#click.command()
def my_command():
pass
Ok, so I thought a little and it seems that the following could work. It's probably not a final solution but it seems to be an initial step.
I can extend the MultiCommand class:
# my_click_classes.py
import click
class ClickApp(click.MultiCommand):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.commands = {}
def add_command(self, command_name, command):
self.commands.update({command_name: command})
def list_commands(self, ctx):
return [name for name, _ in self.commands.items()]
def get_command(self, ctx, name):
return self.commands.get(name)
And the Command class:
class MyCommand(click.Command):
def init_app(self, app):
return app.add_command(self.name, self)
def mycommand(*args, **kwargs):
return click.command(cls=MyCommand)
This allows you to have the commands defined in separated modules:
# commands.py
from my_click_classes import command
#command
def run():
print("run!!!")
#command
def walk():
print("walk...")
and the "app" in a separated module:
from my_click_classes import ClickApp
from commands import run, walk
app = ClickApp()
run.init_app(app)
walk.init_app(app)
if __name__ == '__main__':
app()
Or even use the "app factory" pattern.
It maybe not a definitive solution though. If you guys can see any way to improve it, please let me know.

How can i move python functions into different .py file?

i want to move functions because of lot of files, into separate python files.
But if i do it, it dont work.
I tried:
File: server.py:
import os, cherrypy, json
from customers.py import *
class application(object):
def get_webpage(self):
....
def get_data(self):
....
File: customers.py:
import os, cherrypy, json
def get_customer_data(self):
....
I use the python as server,
the data in the function: get_customer_data is in this case not processed, get a 404 Not Found,
means the function is not included in main file (server.py)
I removed the self from get_webpages() because it was not indented, which means it was not part of the class.
application.py:
class application(object):
def __init__(self):
pass
def get_webpage():
print('From application')
customers.py:
from application import *
get_webpage() # From application
You could indent get_webpages() and make it part of the class. The way you call it would change. (I put the self back and capitalized the name of the class.)
application.py:
class Application(object):
def __init__(self):
pass
def get_webpage(self):
print('From application')
customers.py:
from application import *
a = Application()
a.get_webpage() # From application

How do I shut down PyQt's QtApplication correctly?

I don't know the first thing about Qt, but I'm trying to be cheeky and borrow code from elsewhere (http://lateral.netmanagers.com.ar/weblog/posts/BB901.html#disqus_thread). ;)
I have a problem. When I run test() the first time, everything works swimmingly. However, when I run it the second time, I get nasty segfaults. I suspect that the problem is that I'm not ending the qt stuff correctly. What should I change about this program to make it work multiple times? Thanks in advance!
from PyQt4 import QtCore, QtGui, QtWebKit
import logging
logging.basicConfig(level=logging.DEBUG)
class Capturer(object):
"""A class to capture webpages as images"""
def __init__(self, url, filename, app):
self.url = url
self.app = app
self.filename = filename
self.saw_initial_layout = False
self.saw_document_complete = False
def loadFinishedSlot(self):
self.saw_document_complete = True
if self.saw_initial_layout and self.saw_document_complete:
self.doCapture()
def initialLayoutSlot(self):
self.saw_initial_layout = True
if self.saw_initial_layout and self.saw_document_complete:
self.doCapture()
def capture(self):
"""Captures url as an image to the file specified"""
self.wb = QtWebKit.QWebPage()
self.wb.mainFrame().setScrollBarPolicy(
QtCore.Qt.Horizontal, QtCore.Qt.ScrollBarAlwaysOff)
self.wb.mainFrame().setScrollBarPolicy(
QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff)
self.wb.loadFinished.connect(self.loadFinishedSlot)
self.wb.mainFrame().initialLayoutCompleted.connect(
self.initialLayoutSlot)
logging.debug("Load %s", self.url)
self.wb.mainFrame().load(QtCore.QUrl(self.url))
def doCapture(self):
logging.debug("Beginning capture")
self.wb.setViewportSize(self.wb.mainFrame().contentsSize())
img = QtGui.QImage(self.wb.viewportSize(), QtGui.QImage.Format_ARGB32)
painter = QtGui.QPainter(img)
self.wb.mainFrame().render(painter)
painter.end()
img.save(self.filename)
self.app.quit()
def test():
"""Run a simple capture"""
app = QtGui.QApplication([])
c = Capturer("http://www.google.com", "google.png", app)
c.capture()
logging.debug("About to run exec_")
app.exec_()
DEBUG:root:Load http://www.google.com
QObject::connect: Cannot connect (null)::configurationAdded(QNetworkConfiguration) to QNetworkConfigurationManager::configurationAdded(QNetworkConfiguration)
QObject::connect: Cannot connect (null)::configurationRemoved(QNetworkConfiguration) to QNetworkConfigurationManager::configurationRemoved(QNetworkConfiguration)
QObject::connect: Cannot connect (null)::configurationUpdateComplete() to QNetworkConfigurationManager::updateCompleted()
QObject::connect: Cannot connect (null)::onlineStateChanged(bool) to QNetworkConfigurationManager::onlineStateChanged(bool)
QObject::connect: Cannot connect (null)::configurationChanged(QNetworkConfiguration) to QNetworkConfigurationManager::configurationChanged(QNetworkConfiguration)
Process Python segmentation fault (this last line is comes from emacs)
You need to handle the QApplication outside of the test functions, sort of like a singleton (it's actually appropriate here).
What you can do is to check if QtCore.qApp is something (or if QApplication.instance() returns None or something else) and only then create your qApp, otherwise, use the global one.
It will not be destroyed after your test() function since PyQt stores the app somewhere.
If you want to be sure it's handled correctly, just setup a lazily initialized singleton for it.
A QApplication should only be initialized once!
It can be used by as many Capture instances as you like, but you should start them in the mainloop.
See: https://doc.qt.io/qt-4.8/qapplication.html
You could also try "del app" after "app.exec_", but I am unsure about the results.
(Your original code runs fine on my system)
I would use urllib instead of webkit:
import urllib
class Capturer:
def capture(self, s_url, s_filename):
s_file_out, httpmessage = urllib.urlretrieve(s_url, s_filename, self.report)
def report(self, i_count, i_chunk, i_size):
print('retrived %5d of %5d bytes' % (i_count * i_chunk, i_size))
def test():
c = Capturer()
c.capture("http://www.google.com/google.png", "google1.png")
c.capture("http://www.google.com/google.png", "google2.png")
if __name__ == '__main__':
test()

Categories