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>)
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 + ");")
How can i do a context menu action on a particular file?
I managed to open the explorer and get the list of files through python using pywinauto.
On that file I need to perform a context menu action, is it possible through pywinauto?
import pywinauto
path = "C:\\Users\\Vishnu\\Desktop\\DM-test\\"
pywinauto.Application().Start(r'explorer.exe')
explorer = pywinauto.Application().Connect(path='explorer.exe')
NewWindow = explorer.Window_(top_level_only=True, active_only=True, class_name='CabinetWClass')
NewWindow.AddressBandRoot.ClickInput()
NewWindow.TypeKeys(path+'{ENTER}', with_spaces=True, set_foreground=False)
The code above will open the explorer and navigate to the dir. This is the Context menu action required on the file:
I managed to find the reg value and changed my code to pass that action to the file, It works perfect!!
pywinauto.Application().start(r'"C:\Program Files (x86)\Qualcomm\QCAT 6.x\Bin\QCAT.exe" -txt "{}"'.format(fileName))
Arrgh! Nobody reads the docs... The example is provided in the main Readme: MS UI Automation Example. For your case it should look like that:
# no need to type the path, explorer.exe has a cmd param for that
pywinauto.Application().start(r'explorer.exe "{}"'.format(path))
# backend is important!!!
app = Application(backend="uia").connect(path="explorer.exe")
NewWindow = explorer.Window_(top_level_only=True, active_only=True, class_name='CabinetWClass')
file_item = NewWindow.ItemsView.get_item('dmlog20180517-121505slot0.dlf')
file_item.right_click_input()
app.ContextMenu["Convert to QCAT Text"].invoke()
# further actions depend on a process / dialog started...
More details about backends: Getting Started Guide.
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.
I want to build a cross-platform application ,I used a windows API called SHOpenFolderAndSelectItems(). Although I found example called it by pywin32,but pywin32 is not available on Linux , I don't want to call a Windows API on linux,just don't want to make another code version for Linux,so I wonder how to access it by ctypes? yes,this API cannot be called on Linux ,I just want to make it silent in the code so that I can freeze the Python scripts into executables by cx_Freeze without pywin32 module-missing error happend .
from win32com.shell import shell, shellcon
import os
def launch_file_explorer(path, files):
'''
Given a absolute base path and names of its children (no path), open
up one File Explorer window with all the child files selected
'''
folder_pidl = shell.SHILCreateFromPath(path,0)[0]
desktop = shell.SHGetDesktopFolder()
shell_folder = desktop.BindToObject(folder_pidl, None,shell.IID_IShellFolder)
name_to_item_mapping = dict([(desktop.GetDisplayNameOf(item, shellcon.SHGDN_FORPARSING|shellcon.SHGDN_INFOLDER), item) for item in shell_folder])
print(name_to_item_mapping)
to_show = []
for file in files:
if file in name_to_item_mapping:
to_show.append(name_to_item_mapping[file])
# else:
# raise Exception('File: "%s" not found in "%s"' % (file, path))
shell.SHOpenFolderAndSelectItems(folder_pidl, to_show, 0)
p=r'E:\aa'
print(os.listdir(p))
launch_file_explorer(p, os.listdir(p))