Script crashes when called from a second script - python

I have a report generation script (let's call this script A)that generates a PDF report. I want to import this in a new script (script B) and call the functions from within this new script.
The problem:
Script A crashes when called from Script B exactly at the creation of a QPixmap object. Python console restarts.
Script A runs fine when it is run on its own, but if I try to call it from Command Prompt python.exe crashes.
The following is Script B. All it does is it imports Script A, creates an instance of a class defined in A, and uses that to call a method of that object.
import Script A as et
a = "sample"
mdbPath = "C:\\Python27\\test.mdb"
directory = "C:\\Python27"
ui = et.HawkAutomationScript()
ui.Main(mdbPath,directory,a)
Script A contains the definition of a class called HawkAutomationScript. This object contains two functions to note: one Main function and one function to generate the report. These are the imported modules:
from random import*
import sys
import os
import subprocess
import time
from datetime import datetime
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import QRect
from PyQt4.QtGui import QTextDocument, QPrinter, QApplication, QPainter
from string import Template
import shutil
import time
import pyodbc
This is the definition of the class and the Main function:
doc = QTextDocument()
myCursor = QtGui.QTextCursor(doc)
class HawkAutomationScript(object):
def Main(self,mdb,directory,name):
Ui_FormObject = HawkAutomationScript()
## Filling in the Report ##
Ui_FormObject.fnGenerateReport(directory,name)
def fnGenerateReport(self,directory,name):
now = datetime.now()
dateString = now.strftime("%d-%b-%Y") # For getting Current Date
dirPath = "C:\\"
ResultName = "SelfTest_Results"
testName = name+".pdf"
subDirPath = dirPath + name
if(os.path.isdir(dirPath)):
if(os.path.isdir(subDirPath)):
subDirPath = subDirPath + testName
else:
os.mkdir(subDirPath)
subDirPath = subDirPath + testName
else:
os.mkdir(dirPath)
os.mkdir(subDirPath)
subDirPath = subDirPath + testName
pixmap = QtGui.QPixmap(doc.size().width(), doc.size().height()) ################
printer = QPrinter()
printer.setOutputFileName(subDirPath)
printer.setOutputFormat(QPrinter.PdfFormat)
doc.print_(printer)
# Create a QPainter to draw our content
painter = QPainter(pixmap)
painter.begin( printer )
doc.drawContents(painter)
painter.end()
This is all the relevant code and it is runnable.
If I run script B, A crashes at the QPixmap line marked in the code. But if I copy the lines from B and run A on its own, the PDF report is generated. But if I run A on its own from Command Prompt python.exe crashes.
Any help would be really appreciated. Thank you!!

Related

Importing a deck in anki python

I am working on an addon, for anki, which requires me to import a new deck using its python API. I found the function called test_apkg which seems to have parts which do that. I tried implimenting that below, I am getting no errors, however the deck does not get imported. How do I make my testFunction() import a deck for anki?
from aqt import mw
from aqt.utils import showInfo, qconnect
from aqt.qt import *
from anki.importing import *
import os
from anki.collection import Collection as aopen
#from anki.pylib.tests.shared import getEmptyCol
from .shared import getEmptyCol
def testFunction() -> None:
file = "flashcards.apkg"
col = getEmptyCol()
apkg = r"path_to_file/flashcards.apkg"
imp = AnkiPackageImporter(col, apkg)
imp.run()
# create a new menu item, "test"
action = QAction("Import", mw)
# set it to call testFunction when it's clicked
qconnect(action.triggered, testFunction)
# and add it to the tools menu
mw.form.menuTools.addAction(action)

How can I debug a single class?

I'm writing a code on PyCharm, but it is easier for me if I could debugg continually while I'm writing my code. I can't figure out, that how do I get this class debugged? :
from PyQt5.Qt import QGraphicsPixmapItem
from PyQt5.QtGui import QPixmap
class Level():
def __init__(self, scene, level):
self.scene = scene
self.level = open(level)
def add_item_to_scene(self):
for x in range(self.level):
for y in range(self.level[0]):
if y == 'X':
brick = QPixmap('brick.png')
self.scene.addItem(brick)
I have just begun and this is all what I've written. I'm doing a Platformer game with PyQt5, and I'm trying to set the levels now. Thank you for any help.
Use a convention to run code during development and not run the code on import as shown in the following code.
from PyQt5.Qt import QGraphicsPixmapItem
from PyQt5.QtGui import QPixmap
class Level():
def __init__(self, scene, level):
self.scene = scene
self.level = open(level)
def add_item_to_scene(self):
for x in range(self.level):
for y in range(self.level[0]):
if y == 'X':
brick = QPixmap('brick.png')
self.scene.addItem(brick)
# Use the following convention.
# In python the initial module run receives the name __main__.
if __name__ == '__main__':
print(f'Hello my name is {__name__=} because my module was started by '
f'pyhton. ')
# So while developing whenever you run this module commands here will run.
# Later on when you import the module the commands will not run.
# because __main__ will be the name of this module
l = Level('brick', 5)
l.add_item_to_scene()
else:
# Place any commands you want to run on import here.
print(f'Hello my name is {__name__=} because my module was imported by '
f'another module. ')
Demo Results:
If run directly from python
Hello my name is __name__='__main__' because my module was started by pyhton.
Traceback (most recent call last):
File "C:\Users\ctynd\OneDrive\CodeBase\StackOverflowActivity\OldScratches\scratch_2.py", line 27, in <module>
l = Level('brick', 5)
File "C:\Users\ctynd\OneDrive\CodeBase\StackOverflowActivity\OldScratches\scratch_2.py", line 9, in __init__
self.level = open(level)
OSError: [WinError 6] The handle is invalid
If imported:
import scratch_2
Results
Hello my name is __name__='scratch_2' because my module was imported by another module.

How can i re-use an initialized class in Python?

I'm trying to access a initialized class in the main application from other modules but don't know how to do it.
Background: i want to update a dataframe with data during the whole execution in the main application.
I have to following application structure (this is an simplified version of the code in my application):
constraints
- test_function.py (separate module which should be able to update the initialized class in the main app)
functions
- helper.py (the class which contains the dataframe logic)
main.py (my main application code)
main.py:
import functions.helper
gotstats = functions.helper.GotStats()
gotstats.add(solver_stat='This is a test')
gotstats.add(type='This is a test Type!')
print(gotstats.result())
import constraints.test_function
constraints.test_function.test_function()
helper.py:
class GotStats(object):
def __init__(self):
print('init() called')
import pandas as pd
self.df_got_statistieken = pd.DataFrame(columns=['SOLVER_STAT','TYPE','WAARDE','WAARDE_TEKST','LOWER_BOUND','UPPER_BOUND','OPTIMALISATIE_ID','GUROBI_ID'])
def add(self,solver_stat=None,type=None,waarde=None,waarde_tekst=None,lower_bound=None,upper_bound=None,optimalisatie_id=None,gurobi_id=None):
print('add() called')
self.df_got_statistieken = self.df_got_statistieken.append({'SOLVER_STAT': solver_stat,'TYPE': type, 'WAARDE': waarde, 'OPTIMALISATIE_ID': optimalisatie_id, 'GUROBI_ID': gurobi_id}, ignore_index=True)
def result(self):
print('result() called')
df_got_statistieken = self.df_got_statistieken
return df_got_statistieken
test_function.py:
import sys, os
sys.path.append(os.getcwd())
def test_function():
import functions.helper
gotstats = functions.helper.GotStats()
gotstats.add(solver_stat='This is a test from the seperate module')
gotstats.add(type='This is a test type from the seperate module!')
print(gotstats.result())
if __name__ == "__main__":
test_function()
In main i initialize the class with "gotstats = functions.helper.GotStats()". After that i can correctly use its functions and add dataframe rows by using the add function.
I would like that test_function() is able to add dataframe rows to that same object but i don't know how to do this (in current code the test_function.py just creates a new class in it's local namespace which i don't want). Do i need to extend the class object with an function to get the active one (like logging.getLogger(name))?
Any help in the right direction would be appreciated.
Make your test_function accept the instance as a parameter and pass it to the function when you call it:
main.py:
import functions.helper
from constraints.test_function import test_function
gotstats = functions.helper.GotStats()
gotstats.add(solver_stat='This is a test')
gotstats.add(type='This is a test Type!')
print(gotstats.result())
test_function(gotstats)
test_function.py:
import sys, os
import functions.helper
sys.path.append(os.getcwd())
def test_function(gotstats=None):
if gotstats is None:
gotstats = functions.helper.GotStats()
gotstats.add(solver_stat='This is a test from the seperate module')
gotstats.add(type='This is a test type from the seperate module!')
print(gotstats.result())

How to make Python wait while QGIS renders a shapefile

I have written code for my QGIS 2.8.1 which can successfully take screenshot of one shape file, but unfortunately it doesn't work when I try with multiple shapefiles.
So basically if I replace allFiles = ["C:/Shapefiles/Map_00721.shp"] in the code below with allFiles = ["C:/Shapefiles/Map_00721.shp", "C:/Shapefiles/Map_00711.shp", "C:/Shapefiles/Map_00731.shp", "C:/Shapefiles/Map_00791.shp", "C:/Shapefiles/Map_00221.shp"], the loop iterates over the array without waiting for the rendering and snapshot process to happen.
I have tried using time.sleep in the code below, but it stops the rendering of shapefiles too and the result doesn't came as expected.
import ogr,os
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from qgis.core import *
import qgis.utils
import glob
from time import sleep
import math
import processing
from processing.core.Processing import Processing
from PyQt4.QtCore import QTimer
Processing.initialize()
Processing.updateAlgsList()
OutputFileName = "ABC" # Temprory global placeholder for filename
canvas = iface.mapCanvas()
def startstuffs():
qgis.utils.iface.zoomToActiveLayer() # Zoom to Layer
scale=canvas.scale() # Get current Scale
scale = scale * 1.5
canvas.zoomScale(scale) # Zoomout a bit
QTimer.singleShot(2000,saveImg) # Jump to save img
def saveImg():
qgis.utils.iface.mapCanvas().saveAsImage(OutputFileName)
QgsMapLayerRegistry.instance().removeAllMapLayers()
# Add array of address below
allFiles = ["C:/Shapefiles/Map_00721.shp"]
filesLen = len(allFiles)
TexLayer = "C:/US_County_NAD27.shp"
for lop in range(filesLen):
currentShpFile = allFiles[lop]
currentShpFileName = currentShpFile.strip("C:/Shapefiles/")
OutputFileName = "C:/ImageOut/" + currentShpFileName + ".png"
wb = QgsVectorLayer(currentShpFile, currentShpFileName, 'ogr')
wbTex = QgsVectorLayer(TexLayer, 'CountyGrid', 'ogr')
QgsMapLayerRegistry.instance().addMapLayer(wb) # Add the shapefile
QgsMapLayerRegistry.instance().addMapLayer(wbTex) # Add the county shapefile
qgis.utils.iface.setActiveLayer(wb) # Makes wb as active shapefile
QTimer.singleShot(3000, startstuffs) # This start stuffs
print "Done!"
Avoid using time.sleep() since that will completely stall your entire program. Instead, use processEvents() which allows your program to render in the background.
import time
def spin(seconds):
"""Pause for set amount of seconds, replaces time.sleep so program doesn't stall"""
time_end = time.time() + seconds
while time.time() < time_end:
QtGui.QApplication.processEvents()
This method should work fine for a quick fix, but in the long term it may generate difficult problems to track. It is better to use a QTimer with a event loop for a permanent solution.

Using a dynamically loaded function as a target with multiprocessing.Pool

I am working on a GUI that needs to do some heavy computation in the background and then update the GUI when the calculation is complete. The multiprocessing module seems to be a good solution since I can use the *apply_async* method to specify the target and callback function. The callback function is used to update the GUI with the result. However I am having difficulty when trying to combine multiprocessing with a dynamically loaded module as in the following code. The error message is ImportError: No module named calc.
Is the error due to the fact that multiprocessing just doesn't work with dynamically loaded modules? If not, are there any ideas on a better approach?
from PySide.QtCore import *
from PySide.QtGui import *
import multiprocessing
import time
import sys
import os
import logging
import imp
PluginFolder = "plugins"
plugins = {}
def f(x):
y = x*x
time.sleep(2) #Simulate processing time.
return y
def load_plugin(name):
'''Load the python module 'name'
'''
location = os.path.join('.', PluginFolder)
info = imp.find_module(name, [location])
plugin = {"name": name, "info": info}
plugins[name] = imp.load_module(name, *plugin["info"])
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.pool = multiprocessing.Pool()
load_plugin('calc') #load ./plugins/calc.py
button1 = QPushButton('Calculate', self)
button1.clicked.connect(self.calculate)
button2 = QPushButton('Test', self)
button2.clicked.connect(self.run_test)
self.text = QTextEdit()
vbox1 = QVBoxLayout()
vbox1.addWidget(button1)
vbox1.addWidget(button2)
vbox1.addWidget(self.text)
myframe = QFrame()
myframe.setLayout(vbox1)
self.setCentralWidget(myframe)
self.show()
self.raise_()
def calculate(self):
#self.pool.apply_async(f, [10], callback=self.update_gui) #This works
#result = plugins['calc'].f(10) #this works
#self.update_gui(result)
self.pool.apply_async(plugins['calc'].f, [10], callback=self.update_gui) #This doesn't
def update_gui(self, result):
self.text.append('Calculation complete. Result = %d\n' % result)
def run_test(self):
self.text.append('Testing\n')
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = MainWindow()
app.exec_()
In ./plugins/calc.py, the function f is defined as in the above code.
This doesn't work since you're loading your calc module as a top-level module. Since no module calc is present in your sys.path or in current directory, it can't be found by import statement. Replacing import statement with following will do the trick:
plugins[name] = imp.load_module('{}.{}'.format(PluginFolder, name),
*plugin["info"])
For a plugin.calc being importable, plugins has to be a python module, i.e. contain a __init__.py file.
Any import <module> statement in your plugins files, such as in plugins/calc.py, will lead to a warning,
RuntimeWarning: Parent module 'plugins' not found while handling absolute import import <module>
The reason is that import process looks if parent module contains <module>, and while inside calc.py, can't find parent plugins module. You can rid of the error explicetely specifying plugins module location with, for example import plugins statement in main code.

Categories

Resources