Deploying PyQT5 using FBS with a SQLITE Database - python
I am using these 'required' versions of:
Python 3.6.4
PyQt5 5.9.2
Running “fbs"
I have absolutely no problem getting my application to work through “fbs run”… the application runs perfectly
I complete my “fbs freeze”… no problem but after I run the “fbs installer” and I execute the .dmg file (I am on macOS - Catalina - 10.15.2) and drag it into my applications folder Everything appears to work, but when I try and launch the now installed application… it starts to launch the icon and the dock and then just shuts down.
When I comment out the DB-related code. it all works perfectly. That is I am able to fully launch my application
First way I configured my application.
In main.py:
appctxt.get_resource("../resources/plants.db”)
And I place my database file in:
"src/main/resources"
when I operate like this, I get the behavior I described above (app tries to launch and closes right down)
Second way I configured my application.
I started to poke around to see if I could fix the issue. So I found a "resources" directory in the target directory
I try to launch my application through the executable in the: target/testPlants.app/Contents/ directory
when I do that, the mac CMD window comes up and I see that it tells me, it can not find my DB at the path I specified in the “get_resource” method.
So I find the resources directory in the target’s directory is in UPPERCASE, like this:
target/testPlants.app/Contents/Resources
and I see that as expected my DB is stored in the “Resources”directory.
so I changed my appctxt.get_resource statement in main.py to:
appctxt.get_resource("../Resources/plants.db”)
and in fact, I no longer get the error when I try to launch it through the .exe in the: target/testPlants.app/Contents/ directory
and my application launches… (I don’t get my icons, but it launches!)
so I am thinking, I am getting somewhere.
So I go ahead and try and launch my application through double-clicking the application icon after running the installer with this path change (using “Resources” in uppercase)
and I now get a new error
Could not open database file: out of memory
(see below)
Screen Shot 2020-04-19 at 2.46.11 PM.png
Reproducible Sample
With the code as is... when I examine the resources directory in the target directory, that gets created as a result of the fbs freeze, there is no db in it, so the app tries to launch (after I run the installer)and just closes down. If I manually place the db in it, it gives me the "out of memory" error. Thanks for taking a look
from fbs_runtime.application_context.PyQt5 import ApplicationContext
from fbs_runtime.application_context import cached_property
import sys, csv
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
from PyQt5 import QtSql as qts
from PyQt5.QtPrintSupport import QPrintDialog, QPrinter, QPrintPreviewDialog
from PyQt5.Qt import QFileInfo
import sqlite3
from datetime import date
from PyQt5.QtGui import QPixmap
class MainWindow(qtw.QMainWindow):
def __init__(self):
super().__init__()
self.gridLayout = qtw.QGridLayout()
self.mainW = qtw.QWidget()
self.mainW.setLayout(self.gridLayout)
self.setCentralWidget(self.mainW)
# Connect to the database
db = qts.QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('plants.db')
if not db.open():
qtw.QMessageBox.critical(
None, 'DB Connection Error',
'Could not open database file: '
f'{db.lastError().text()}')
sys.exit(1)
#CREATE MODELS FOR EACH SQL TABLE
self.zone_model = qts.QSqlTableModel()
self.zone_model.setTable('zones')
self.loc_model = qts.QSqlTableModel()
self.loc_model.setTable('loc_rec')
self.indoor_seed_model = qts.QSqlTableModel()
self.indoor_seed_model.setTable('indoor_seed')
self.soil_rec_model = qts.QSqlTableModel()
self.soil_rec_model.setTable('soil_rec')
self.plant_type_model = qts.QSqlTableModel()
self.plant_type_model.setTable('plant_type')
self.nick_name_model = qts.QSqlTableModel()
self.plant_type_model.setTable('nick_name')
self.plants_model = qts.QSqlRelationalTableModel()
self.plants_model.setTable('plant_list')
self.plants_model.setRelation(
self.plants_model.fieldIndex('nickname_id'),
qts.QSqlRelation('nick_name', 'id', 'veggies')
)
self.plants_model.setRelation(
self.plants_model.fieldIndex('ans_id'),
qts.QSqlRelation('indoor_seed', 'id', 'ans')
)
self.plants_model.setRelation(
self.plants_model.fieldIndex('zone_id'),
qts.QSqlRelation('zones', 'id', 'code')
)
self.plants_model.setRelation(
self.plants_model.fieldIndex('soil_id'),
qts.QSqlRelation('soil_rec', 'id', 'soil_type')
)
self.plants_model.setRelation(
self.plants_model.fieldIndex('p_type_id'),
qts.QSqlRelation('plant_type', 'id', 'p_type')
)
self.plants_model.setRelation(
self.plants_model.fieldIndex('location_id'),
qts.QSqlRelation('loc_rec', 'id', 'loc')
)
self.UIComps() # call the UI components method
def UIComps(self):
# set headers for main table
fieldnames = ['ID', "Year Planted", "Real Name", "Nick Name", "Description",
"Seed Plant Rec","Garden Plant Rec", "Plant Notes", "Comments",
"Days to Germ", "Days to Harv","Reco Spring Frost","Actual Spring Frost", "Seed Plant Rec", "Garden Plant Rec",
"Actual Seed Plant", "Actual Garden Plant", "Harvest Date Plan", "Actual Harvest Date",
"Photo", "Location", "Zone", "Seed Indoor?", "Soil Type", "Plant Type" ]
c = 0
for f in fieldnames:
self.plants_model.setHeaderData(c, qtc.Qt.Horizontal, (fieldnames[c]))
c += 1
self.plants_model.setEditStrategy(qts.QSqlTableModel.OnFieldChange)
# self.plants_model.dataChanged.connect(print)
lbl2 = qtw.QLabel("View/Edit Plants", self)
lbl2.setFont(qtg.QFont("Helvetica", 25, 12))
self.gridLayout.layout().addWidget(lbl2, 6, 0,1,3, alignment=qtc.Qt.AlignCenter)
#PLANT LIST TABLE
self.plant_list = qtw.QTableView()
self.plant_list.setSelectionMode(qtw.QAbstractItemView.ExtendedSelection)
self.plant_list.setDragEnabled(True)
self.plant_list.setAcceptDrops(True)
self.plant_list.setDropIndicatorShown(True)
self.plant_list.setDragDropMode(qtw.QAbstractItemView.InternalMove)
self.plant_list.setModel(self.plants_model)
self.gridLayout.layout().addWidget(self.plant_list, 7, 0, 2, 3)
self.plant_list.horizontalHeader().setSectionsClickable(True)
self.plant_list.horizontalHeader().setSortIndicatorShown(True)
self.plant_list.setSortingEnabled(True) # this makes table sortable
self.plants_model.setEditStrategy(qts.QSqlTableModel.OnFieldChange)
self.plants_model.dataChanged.connect(print)
self.plants_model.select()
self.plant_list.setItemDelegate(qts.QSqlRelationalDelegate())
#adding toolbars
self.toolbar = self.addToolBar('Controls')
deleteCoffee = qtw.QAction(qtg.QIcon("close.png"), "Delete Record", self)
deleteCoffee.triggered.connect(self.delete_plant) #removes from table
self.toolbar.addAction(deleteCoffee )
addPlant = qtw.QAction(qtg.QIcon("add.png"), "Add A Plant", self)
addPlant.triggered.connect(self.add_plant)
self.toolbar.addAction(addPlant)
# SLOTS for Toolbar buttons
def delete_plant(self):
selected = self.plant_list.selectedIndexes()
for index in selected or []:
self.plants_model.removeRow(index.row())
self.plants_model.select()
def add_plant(self):
self.gridLayout.layout().addWidget(self.plant_list, 7, 0, 2, 3)
self.plants_model.insertRows(0, 1)
appctxt = ApplicationContext()
appctxt.get_resource("../resources/plants.db")
appctxt.get_resource("../resources/add.png")
mw = MainWindow()
mw.setGeometry(10, 10, 900, 650)
mw.show()
exit_code = appctxt.app.exec_()
sys.exit(exit_code)
UPDATE - I believe I fixed the DB problem, with the following changes to the code and configuration, but I am still not able to see my images after running "fbs freeze"
Here are the changes I made with respect to the DB:
First DB for me had to live in a sub-directory to "resources" as opposed to resources directly, so for me it is this:
src/main/resources/base
Next, I assumed the path in my 'main.py' would mimic that path above (in other words, where I placed the database (src/main/resources/base)). Instead, this is what appears to be working for me:
appctxt.get_resource("plants.db")
I now get no error about not being able to locate the DB,
However, when I run the executable, none of my images/icons are showing up, which for me, enable navigation of the application and the DB.
In reading the documentation it appears that images follow the same logic as the data files/database, but that does not appear to be working for me.
It works when I execute an "fbs run", but not after the "fbs freeze" when I launch the applications' executable. The application launches without error, but no images.
My configuration for the images is the same as for the DB:
I have loaded them into a sub-directory to "resources" called "base", like this
src/main/resources/base
And as for the statements in the "main.py" file, I have the following:
appctxt.get_resource("carrots.jpg")
appctxt.get_resource("csv.png")
appctxt.get_resource("csv2.png")
appctxt.get_resource("leeks.jpg")
appctxt.get_resource("list.png")
appctxt.get_resource("close.png")
appctxt.get_resource("mushrooms.jpg")
appctxt.get_resource("pdf.png")
appctxt.get_resource("potato.jpg")
appctxt.get_resource("Rosce.png")
appctxt.get_resource("seed.png")
appctxt.get_resource("tomato.jpg")
appctxt.get_resource("veggies.png")
appctxt.get_resource("year.png")
Anyone have luck with images? Is there a different way to configure them?
Thanks
FURTHER UPDATE
As I continue to try to figure this out, after 'fbs freeze', I can confirm that fbs transfers the database and the images to the:
'target//Contents/Resources' directory, but I don't think it is really seeing any of them.
Further, it does not seem to make a difference whether or not I include:
# appctxt.get_resource("add.png")
# appctxt.get_resource("carrots.jpg")
in main.py
I guess I don't fully understand under what conditions I would need to include the .get_resource() methods and exactly how to configure it. I have seen the example here, but not sure how to get that to work with my database or if I even need to. It seem logically that I do. I'll keep digging, but if anyone was has insight, I would appreciate any tips.
UPDATE - FIXED!
I finally figured it out and I hope this helps someone else.
I had this part right, the data files, images and database goes here:
src/main/resources/base
Here is how I got the code for the database to get it to be recognized:
db = qts.QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName(appctxt.get_resource("plants.db"))
if not db.open():
qtw.QMessageBox.critical(
None, 'DB Connection Error',
'Could not open database file: '
f'{db.lastError().text()}')
sys.exit(1)
I needed to place the get_resource() method inside the setDatabaseName() method from PyQt's QSqlDatabase class.
For the images I needed to do something like this:
ros = appctxt.get_resource("Rosce.png")
pixmap = qtg.QPixmap(ros)
OR if using the setStyle method, like this:
seed = appctxt.get_resource("seed.png")
self.btnSeed.setStyleSheet("image: url(" + seed + ");")
Related
Cannot click on tkinter button more than twice
I am a newbie to tkinter and have know idea what I am doing sometimes:D . I created a python script to get Corona Virus Stats from a GitHub post link The script has over 20 files in it so I thought I should just create one file to run all the other files. So what other better way to do it than creating a UI. And that's what I did I used tkinter to run all the files. If they clicked on x button then I would run abc. And I ended up running these files by importing them in a certain order(Don't know If that was the best way). So here is where I ran into an error. I don't exactly know if this error is because of how I imported my files or my tkinter code is just wrong. I just couldn't seem to click on a button twice. I would run my program and click on a button, it would run properly and then the next time I clicked on that same button It would just not work. There was no error and no output. Nothing would happen. Here is my code: #Import tkinter import tkinter as tk from tkinter import * from tkinter import simpledialog tk = tk.Tk() Max = 190 def RunDeaths(): #If they click on RunDeaths I will run this function #Check if they have already entered a path try: open('/Users/test/Documents/python/Py_Programs/Hackathon/DeathStats/Info.txt','r') from DeathStats import RunAll except: YourPath = simpledialog.askstring('Countries','''Please Enter Your Path To HACKATHON Folder: Example: \"/Users/Name/Documents/python/\" Note: Leave out the HACKATHON folder and you must put a slash at the end''',parent=tk) #Write this path to a file call Info.txt file = open('/Users/test/Documents/python/Py_Programs/Hackathon/DeathStats/Info.txt','w') file.write(str(YourPath)+'\n') file.write(str(Max)) file.close() #Run all the files that gather the data for Corona Virus Deaths from DeathStats import RunAll def RunRecoveredCases(): #If they click on RecoveredCases Run this #Check If they had already entered a path try: open('/Users/test/Documents/python/Py_Programs/Hackathon/RecoveredCases/Info.txt','r') from RecoveredCases import RunAll except: YourPath = simpledialog.askstring('Countries','''Please Enter Your Path To HACKATHON Folder: Example: \"/Users/Name/Documents/python/\" Note: Leave out the HACKATHON folder and you must put a slash at the end''',parent=tk) file = open('/Users/test/Documents/python/Py_Programs/Hackathon/RecoveredCases/Info.txt','w') #Write there path to a file file.write(str(YourPath)+'\n') file.write(str(Max)) file.close() #Run all the files that gather all the Recovered Cases from RecoveredCases import RunAll #* * Here is where I think I went wrong But Im not sure Deaths = Button(tk,height = 20, width = 30, text='Run Deaths',command = RunDeaths,highlightbackground='#000000') Recovered = Button(tk,height = 20, width = 30, text='Run Recovered Cases',command = RunRecoveredCases,highlightbackground='#000000') Deaths.pack() Recovered.pack() tk.mainloop() So my question and problem is: Why can I not click on a button more than twice? This has happened to me before and I could not fix it. Any help would be appreciated.(If you would like to run my program because my explanation was just not good enough here is a git hub repo GitHub) Thank You
It appears that you are assuming that from RecoveredCases import RunAll will run the code in RecoveredCases.py each time it is imported. That is a false assumption. Python caches code that is imported. The proper way to use code in a separate file is to put the code in a function or class, import the function or class exactly once, and then call the function or instantiate the class whenever you want to run the code.
QGIS3.14 Resource Sharing symbols are not appearing in atlas export via standalone python script
I created a standalone python script to export my atlas layouts. Everything is working great except that the SVG symbols that I am using from the Resource Sharing plugin are just question marks, assuming that it is having trouble locating them. However, if I run the script via the startup.py in the QGIS3 folder everything works like expected. I would really like to avoid using this method though as it prevents you from using QGIS until the script finishes, which takes about 2 hours. I am hoping that I just need to add a simple environmental variable to my .bat file so that it can locate the Resource Sharing plugin. Thanks in advance for any help! .bat file #ECHO off set OSGEO4W_ROOT=C:\OSGeo4W64 call "%OSGEO4W_ROOT%\bin\o4w_env.bat" call "%OSGEO4W_ROOT%\bin\qt5_env.bat" call "%OSGEO4W_ROOT%\bin\py3_env.bat" path %OSGEO4W_ROOT%\apps\qgis\bin;%PATH% set QGIS_PREFIX_PATH=%OSGEO4W_ROOT%\apps\qgis set GDAL_FILENAME_IS_UTF8=YES set VSI_CACHE=TRUE set VSI_CACHE_SIZE=1000000 set QT_PLUGIN_PATH=%OSGEO4W_ROOT%\apps\qgis\qtplugins;%OSGEO4W_ROOT%\apps\qt5\plugins SET PYCHARM="C:\Program Files\JetBrains\PyCharm 2019.2.3\bin\pycharm64.exe" set PYTHONPATH=%OSGEO4W_ROOT%\apps\qgis\python set PYTHONHOME=%OSGEO4W_ROOT%\apps\Python37 set PYTHONPATH=%OSGEO4W_ROOT%\apps\Python37\lib\site-packages;%PYTHONPATH% set QT_QPA_PLATFORM_PLUGIN_PATH=%OSGEO4W_ROOT%\apps\Qt5\plugins\platforms set QGIS_PREFIX_PATH=%OSGEO4W_ROOT%\apps\qgis start "PyCharm aware of QGIS" /B %PYCHARM% %* Python Script from qgis.core import QgsApplication, QgsProject, QgsLayoutExporter import os import sys def export_atlas(qgs_project_path, layout_name, outputs_folder): # Open existing project project = QgsProject.instance() project.read(qgs_project_path) print(f'Project in "{project.fileName()} loaded successfully') # Open prepared layout that as atlas enabled and set layout = project.layoutManager().layoutByName(layout_name) # Export atlas exporter = QgsLayoutExporter(layout) settings = QgsLayoutExporter.PdfExportSettings() exporter.exportToPdfs(layout.atlas(), outputs_folder, settings) def run(): # Start a QGIS application without GUI QgsApplication.setPrefixPath(r"C:\\OSGeo4W64\\apps\\qgis", True) qgs = QgsApplication([], False) qgs.initQgis() sys.path.append(r'C:\OSGeo4W64\apps\qgis\python\plugins') project_path = [project_path] output_folder = [export_location] layout_name_portrait = [portrait layout name] layout_name_landscape = [landscape laytout name] export_atlas(project_path, layout_name_portrait, output_folder) export_atlas(project_path, layout_name_landscape, output_folder) # Close the QGIS application qgs.exitQgis() run()
I guess that it might have something to do with the setting svg/searchPathsForSVG. QgsSettings().setValue('svg/searchPathsForSVG', <your path>)
Python Orange (Version 3.3.6) on Window 10 can't create demo widget
As per the documentation, I created the following files: setup.py (in folder C:\Python34\Lib\site-packages\Orange\widgets\orange-demo) from setuptools import setup setup(name="Demo", packages=["orangedemo"], package_data={"orangedemo": ["icons/*.svg"]}, classifiers=["Example :: Invalid"], # Declare orangedemo package to contain widgets for the "Demo" category entry_points={"orange.widgets": "Demo = orangedemo"}, ) and OWDataSamplerA.py (in folder C:\Python34\Lib\site-packages\Orange\widgets\orange-demo\orangedemo) import sys import numpy import Orange.data from Orange.widgets import widget, gui class OWDataSamplerA (widget.OWWidget): name = "Data Sampler" description = "Randomly selects a subset of instances from the data set" icon = "icons/DataSamplerA.svg" priority = 10 inputs = [("Data", Orange.data.Table, "set_data")] outputs = [("Sampled Data", Orange.data.Table)] want_main_area = False def __init__(self): super().__init__() # GUI box = gui.widgetBox(self.controlArea, "Info") self.infoa = gui.widgetLabel(box, 'No data on input yet, waiting to get something.') self.infob = gui.widgetLabel(box, '') def set_data(self, dataset): if dataset is not None: self.infoa.setText('%d instances in input data set' % len(dataset)) indices = numpy.random.permutation(len(dataset)) indices = indices[:int(numpy.ceil(len(dataset) * 0.1))] sample = dataset[indices] self.infob.setText('%d sampled instances' % len(sample)) self.send("Sampled Data", sample) else: self.infoa.setText('No data on input yet, waiting to get something.') self.infob.setText('') self.send("Sampled Data", None) I created a .svg icon and left the __init__.py file blank. After running pip install -e ., a Demo.egg-info directory is created and it includes several files, but no demo widget is created. After restarting Python Orange no visible changes occur at all. Any advice would be most welcome.
A separate version of Python 3.6 is bundled with Orange. To install a new widget, you need to have the proper Python instance in the path. On Windows, you can find a special shortcut "Orange Command Prompt". You will need to run it as Administrator to install new packages in newer version. Once in the appropriate directory, you can run your pip install.. command.
Python application, py2exe, Inno Setup, os.system() start function does not work
I have made some application with PyQt4, which contains os.system() start function in it as a button, which basically opens a csv file. import sqlite3 import sys import os ....... class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("MainWindow")) #### ............. #### # "CSV" button self.pushButton_4 = QtGui.QPushButton(self.centralwidget) self.pushButton_4.setObjectName(_fromUtf8("pushButton_4")) self.horizontalLayout_2.addWidget(self.pushButton_4) self.pushButton_4.clicked.connect(self.csv_button) #### def csv_button(self): import csv conn = sqlite3.connect('FamilyFinance_test.db') c=conn.cursor() myrow_in = c.execute("select * from Income_test order by date desc") with open('Income_test.csv', 'wb') as csvfile_in: s_in = csv.writer(csvfile_in, delimiter=' ') s_in.writerow(['Date']+['Income']) for i in myrow_in: s_in.writerow([str(i[0])]+[str(i[1])]) myrow_out = c.execute("select * from Outcome_test order by date desc") with open('Outcome_test.csv', 'wb') as csvfile_out: s_out = csv.writer(csvfile_out, delimiter=' ') s_out.writerow(['Date']+['ATM']+['Spent']+['Reason']+['Category']) for j in myrow_out: s_out.writerow([str(j[0])]+[str(j[1])]+[str(j[2])]+[str(j[3])]+[str(j[4])]) os.system("start "+'Income_test.csv') os.system("start "+'Outcome_test.csv') Within python itself this function works fine, as well as with .exe created with py2exe. However, after successful creation of installer by InnoSetup and installing the application, i find that the very same button does not work. Can someone give direction for resolving this problem? Is there something to be done additionally with InnoSetup compiler?
perhaps your InnoSetup install your program into C:\Program Files (x86) which requires UAC elevation to write into the folder. So, maybe either change your installation folder to some User folders or package your application with UAC option in py2exe which is explained here https://stackoverflow.com/a/1445547/566035.
icons in PyQt not showing properly
I have developed an app with system Tray having menu in Python 2.6.4 and PyQt4. Every client system has python installed locally, and accessing PyQt4 from network location. I set SystemTray and required icons for menu items as below. App folder has icons folder from where i am using. so i used os.getcwd() i kept this app folder in a network so that everyone can access. self.mnuItem_1 = QtGui.QAction(QtGui.QIcon(r'%s\icons\icon1.ico' % (os.getcwd())), "Menu Item 1", self) self.mnuItem_2 = QtGui.QAction(QtGui.QIcon(r'%s\icons\icon1.ico' % (os.getcwd())), "Menu Item 1", self) self.trayIconMenu = QtGui.QMenu(self) self.trayIconMenu.addAction(self.mnuItem_1) self.trayIconMenu.addAction(self.mnuItem_2) self.trayIcon = QtGui.QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu) TrayIcon = (r'%s\ShowTime_Addons\Media\showtimeIcon.ico' % (os.getcwd())) self.trayIcon.setIcon(QtGui.QIcon(TrayIcon)) self.trayIcon.setToolTip('Showtime') self.trayIcon.show() In some systems i could able to see the icons, but in some systems icons are not shown. For testing i placed .png and used and it worked. self.mnuItem_1 = QtGui.QAction(QtGui.QIcon(r'%s\icons\icon1.png' % (os.getcwd())), "Menu Item 1", self) self.mnuItem_2 = QtGui.QAction(QtGui.QIcon(r'%s\icons\icon1.png' % (os.getcwd())), "Menu Item 1", self) So came to an understanding that the issue is not with the path but something else.
No sure this is the solution, but try to not use os.getcwd() which gives you the current working directory and it might differ from your application directory. To determine application directory, use: base_dir = os.path.dirname(os.path.abspath(__file__)) Then use base_dir instead of os.getcwd() or do: os.chdir(base_dir)
I found out a solution for this scenario One approach is to set the paths in qt.config file and place this in the location of your executable.(In my case its C:\Python26) As i discribed in my Question that i am accessing PyQt4 from Network Location say \\somesystem\Share\PyQt4 We will find a qt.config file in \\somesystem\Share\PyQt4 Take it and put this below lines in qt.conf [Paths] Prefix = //somesystem/Share/PyQt4 Binaries = //somesystem/Share/PyQt4 Everything works fine, even sqldrivers will be loaded. No Need of using app.addLibraryPath