Trouble using np.genfromtxt in python method - python

I am writing a python script that uses PyQt and Matplotlib to graph a 2D CSV file. Im still learning python so im having trouble working through some of the errors im getting. One in particular that is troubling me is
Error:
Traceback (most recent call last):
File "C:/Users/jonesza/Documents/Python Scripts/2D-Graph/Qt_2D_Plot.py", line 62, in update_graph
l, v = self.parse_file(self.mpllineEdit.text())
File "C:/Users/jonesza/Documents/Python Scripts/2D-Graph/Qt_2D_Plot.py", line 53, in parse_file
names=['time','temperature'])
File "C:\WinPython\python-2.7.5.amd64\lib\site-packages\numpy\lib\npyio.py", line 1356, in genfromtxt
first_values = split_line(first_line)
File "C:\WinPython\python-2.7.5.amd64\lib\site-packages\numpy\lib_iotools.py", line 208, in _delimited_splitter
line = line.strip(asbytes(" \r\n"))
AttributeError: 'QString' object has no attribute 'strip'
Source code:
# used to parse files more easily
from __future__ import with_statement
# Numpy module
import numpy as np
# for command-line arguments
import sys
# Qt4 bindings for core Qt functionalities (non-Gui)
from PyQt4 import QtCore
# Python Qt4 bindings for GUI objects
from PyQt4 import QtGui
# import the MainWindow widget from the converted .ui files
from qtdesigner import Ui_MplMainWindow
class DesignerMainWindow(QtGui.QMainWindow, Ui_MplMainWindow):
"""Customization for Qt Designer created window"""
def __init__(self, parent = None):
# initialization of the super class
super(DesignerMainWindow, self).__init__(parent)
# setup the GUI --> function generated by pyuic4
self.setupUi(self)
# connect the signals with the slots
QtCore.QObject.connect(self.mplpushButton, QtCore.
SIGNAL("clicked()"), self.update_graph)
QtCore.QObject.connect(self.mplactionOpen, QtCore.
SIGNAL("triggered()"), self.select_file)
QtCore.QObject.connect(self.mplactionQuit, QtCore.
SIGNAL("triggered()"), QtGui.qApp, QtCore.SLOT("quit()"))
def select_file(self):
"""opens a file select dialog"""
# open the dialog and get the selected file
file = QtGui.QFileDialog.getOpenFileName()
# if a file is selected
if file:
# update the lineEdit text with the selected filename
self.mpllineEdit.setText(file)
def parse_file(self, filename):
"""gets first two columns from .csv uploaded"""
#import data from .csv
data = np.genfromtxt(filename, delimiter=',',
names=['time','temperature'])
x = data['time']
y = data['temperature']
return x,y
def update_graph(self):
"""Updates the graph with new letteers frequencies"""
# get the axes for the 2D graph
l, v = self.parse_file(self.mpllineEdit.text())
# clear the Axes
self.mpl.canvas.ax.clear()
# plot the axes
self.mpl.canvas.ax.plot(l,v)
# force an image redraw
self.mpl.canvas.draw()
# create the GUI application
app = QtGui.QApplication(sys.argv)
# instantiate the main window
dmw = DesignerMainWindow()
# show it
dmw.show()
# start the Qt main loop execution, exiting from this script
# with the same return code of Qt application
sys.exit(app.exec_())
Thanks ahead of time for any help.

I assume self.mpllineEdit.text() produces a QString. You need to explore, either in PyQt documentation, or an interactive shell, what methods it has, and if you need need to do anything to convert it to a regular Python string. genfromtxt is attempting to split that string into lines, and then strip off the line ending characters, so it can then parse the line.
Try:
self.parse_file(str(self.mpllineEdit.text()))
That might convert the Qstring to a regular Python string.

Related

PyQt5 Plotly viewer creates multiple save file dialogs

I've created a lightweight browser to view plotly .html files within a PyQt5 application instead of in the default browser, using QWebEngineView based on other questions such as this: Using a local file in html for a PyQt5 webengine
The viewer works, but when multiple windows are open with several plots, attempting to save one of the plots as .png file causes several save file dialogs to open (one for every window that has been open since the program started running).
I tried debugging this, after the download request it seems to jump to sys.exit(app.exec_()), then back to the download request again. Although several dialogs are open, only one plot is actually saved.
Is there a way to ensure only one dialog is created?
To reproduce, run the following code and click plot the button 2 or more times, creating several windows. Use the plotly "download plot as png" option and after saving the plot, one or more additional save file dialogs are presented.
import os
import sys
from pathlib import Path
import plotly
import plotly.express as px
from PyQt5 import QtCore, QtWidgets
from PyQt5 import QtWebEngineWidgets, QtGui
user_profile = Path(os.environ.get("USERPROFILE"))
APP_DATA_FOLDER = user_profile / "AppData" / "Local" / "program"
APP_DATA_FOLDER.mkdir(parents=True, exist_ok=True)
class PlotlyViewer(QtWebEngineWidgets.QWebEngineView):
"""A lightweight browser used to view Plotly
figures without relying on an external browser.
"""
def __init__(
self, fig, title="Plot Viewer", count=0, download_directory=None
):
super().__init__()
self.windows = []
# Create a temporary html file containing the plot
self.file_path = str(APP_DATA_FOLDER / f"temp{count}.html")
plotly.offline.plot(
fig, filename=self.file_path, auto_open=False,
)
# Open the html file with the PlotlyViewer
self.load(QtCore.QUrl.fromLocalFile(self.file_path))
self.setWindowTitle(title)
self.resize(1000, 600)
# When a downloadRequest is received, run the download_file method
self.page().profile().downloadRequested.connect(self.download_file)
def closeEvent(self, event):
# When the plot is closed, delete the temporary file
os.remove(self.file_path)
#QtCore.pyqtSlot(QtWebEngineWidgets.QWebEngineDownloadItem)
def download_file(self, download):
# Get a save_file_dialog... For some reason this happens twice!
plot_path, _ = QtWidgets.QFileDialog.getSaveFileName(
None, "Save Plot As...", str(user_profile), "Image (*.png)"
)
if plot_path:
download.setPath(plot_path)
download.accept()
#staticmethod
def save_file_dialog(export_dir):
file_path, _ = QtWidgets.QFileDialog.getSaveFileName(
None, "Save Plot As...", export_dir, "Image (*.png)"
)
return file_path
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.setFixedSize(150, 100)
MainWindow.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.btn_plot = QtWidgets.QPushButton(self.centralwidget)
self.btn_plot.setGeometry(QtCore.QRect(0, 0, 70, 23))
self.btn_plot.setObjectName("btn_plot")
self.btn_plot.setText("plot")
self.connect_slots()
def connect_slots(self):
self.btn_plot.clicked.connect(self.create_plot)
def create_plot(self):
fig = px.scatter(x=[0, 1, 2, 3, 4], y=[0, 1, 4, 9, 16])
browser_title = "a window title"
plot_window = PlotlyViewer(fig, browser_title, download_directory=user_profile)
plot_window.windows.append(plot_window)
plot_window.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
By default, all QWebEnginePages share the same QWebEngineProfile, meaning that self.page().profile() will always return the same profile object.
Qt connections are always cumulative, even for the same target slot/function: connecting a signal to the same function twice results in calling that function twice for every time the signal is emitted.
Since you're connecting to the signal of the same profile every time a new PlotlyViewer instance is created, download_file will be called for each instance every time a download is requested.
You have three possibilities:
connect the signal just once using the defaultProfile(), and externally from the PlotlyViewer class;
create a new standalone profile (similar to the "private mode") for each view:
self.profile = QWebEngineProfile()
self.setPage(QWebEnginePage(profile))
self.profile.downloadRequested.connect(self.download_file)
check that the page() of the download request belongs to the page of the view:
def download_file(self, download):
if download.page() != self.page():
return
# ...
Further important notes:
most widgets override event handlers, and it's always good practice to call the base implementation (unless you really know what you're doing); you must call super().closeEvent(event) in the closeEvent override;
since you probably don't intend to reuse closed views, you should always delete them, otherwise their resources will unnecessarily occupy memory (and a lot); add self.setAttribute(Qt.WA_DeleteOnClose) in the __init__, or call self.deleteLater() in closeEvent();
self.windows is an instance attribute, adding the plot_window instance to it is completely useless, since it will always contain one object (the instance itself); if you want to keep track of all existing windows you should create the list as an attribute of the parent object (ie. the main window) or as a class attribute (for PlotlyViewer); also, considering the point above, you should delete the reference whenever the view is closed and destroyed;
you shall not edit pyuic generated files; unless you did this for the sake of the example, be aware that it's considered bad practice, and you should instead follow the official guidelines about using Designer;

How to reference & change a PyQt5 window object from another window class or from an extra .py file such as init_events_ui.py

Is it possible to add another .py file (via import) and use it as an initialisation and event handler for a PyQt5 Multiwindow project other window & main .py files?
I have a project that consists of the following files:
main.py - the main app which imports the Ui...py files below & the ui_init_events.py file also
main.py - the main program
Ui_main_window_ui.py - a complied Qt UI to display the main window
Ui_frmConsoleLog_ui.py - a complied Qt UI to display a window with a textEdit & comboBox objects
ui_init_events.py - I want this file to have functions for setting each windows Ui object fields as well as contain the Ui windows object events such as btn.clicked.connect(self.SomeotherFunc)
I have found both these posts helpful but am stuck as I do not know how to reference objects other than the self object and can't find where this is explained. See: PyQt: Modifying Widget Object from another Function
Multiple Windows in PyQt4
In the 2nd post they are using multi-windows (via the QDialog) object also, however they are only using a single .py file. I am using Visual Studio Code & have built the Ui files then complied them. They are works in progress so I expect to make more changes meaning they wil be overwritten so I do not want to edit these files.
I cannot work out how to reference and change the properties in another window for initialisation purposes. The thread is:
Here is the main bits. Currently I call a function to return some data using self.combobox.additems (see #1) but I think calling this over and over again from main.py somewhat decreases code readability.
(#2) Therefore I would like advice how to move all the initialisation parts of an existing PyQt window (being an imported .py file used to generate the window + controls) into a separate .py file (eg named ui_init_events.py).
However in trying & researching this I do not know nor can find an example how to reference the objects using their full hierarchical naming convention. I have tried Application. QWindow., etc. I only know how to use self.... and it isn't working (of course as that would be the referring to the function itself I understand that its being called in, not the PyQt window I'm wanting to reference). See (#3) for what is not working.
Any ideas please on resources available to assist with understanding how PyQt labels its objects from the root of the object tree is appreciated as well as an example of how to set the comboBox_Selector in the Ui_frmConsoleLog window from app.py or another .py file other than from the Ui_frmConsoleLog_ui.py or within the class definition under --init--(self, parent=None): ?
The desired file structure is like this:
+-- main.py (or app.py)
|
+--- Ui_main_window_ui.py (has setupui & adds objects for main window)
|
+--- Ui_main_window_init.py (has init code for Ui_main_window_ui.py)
|
+--- Ui_main_window_events.py (has event code for Ui_main_window_ui.py)
|
+--- Ui_frmConsoleLog_ui.py (has setupui & adds objects for 2nd window)
|
+--- Ui_frmConsoleLog_init.py (has init code for Ui_frmConsoleLog_ui.py)
|
+--- Ui_frmConsoleLog_events.py (has event code for Ui_frmConsoleLog_ui.py)
main.py file contents
# Default imports
import sys, os, io, sched, time
import ui_resources_rc
from PyQt5.QtCore import QObject, QThread, pyqtSignal
from configparser import ConfigParser
...a portion of code here uses a ini config file for retrieving/saving some settings of the UI & I want to set the properties for each object accordingly...
from PyQt5.QtWidgets import (QApplication, QDialog, QMainWindow, QMessageBox, QWidget)
from PyQt5.uic import loadUi
from Ui_main_window_ui import Ui_MainWindow
from Ui_frmConsoleLog_ui import Ui_frmConsoleLog
from ui_init_events import (funcUpdateFormCombos)
...todo... open a network connection to the system (its an inverter)
# This window will be used for filtering network poll event types
class ConsoleLogWindow(QWidget, Ui_frmConsoleLog):
def __init__(self):
super().__init__()
self.setWindowTitle('Console Log')
class AppWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.win = None # No Console Log window visible yet.
self.setupUi(self)
#(1) Here is where I will have to call this a lot of times to
# populate the vaious window objects which I dont want to do
self.comboBox_Selector.addItems(funcUpdateFormCombos())
self.action_Console.triggered.connect(self.OpenConsoleLogWindow)
self.action_About.triggered.connect(self.about)
# Populate the controls as per the config file
#initialise() <-- (2) Here is where I want to call a
# function in ui_init_events.py to setup
# the form's objects as created in QtDesigner
# & exported as a .py file. I want to continue
# to edit each PyQt window later so I dont want
# to add to the Ui_... files themselves but in
# main.py call a function that has the code to
# set the object fields like checked, combobox
# list contents, etc.
def OpenConsoleLogWindow(self, checked):
if self.win is None:
self.win = ConsoleLogWindow()
self.win.show()
else:
self.win.close() # Close window.
self.win = None # Discard reference.
if __name__ == "__main__":
app = QApplication(sys.argv)
win = AppWindow()
win.show()
print('HERE')
sys.exit(app.exec())
ui_init_events.py file contents (this is the file I want to house all window object setup without editing the PyQt UI converted .ui to .py files such as Ui_main_window_u.py & Ui_frmConsoleLog_ui.py which I don't want to edit at all.
# Contains the initialisation of widgets fields
def funcUpdateFormCombos():
LIST = ['Amps', 'Volts', 'Watts']
LIST.sort()
return tuple(LIST)
# Would prefer to have a single file per Ui_...py file that houses
# all the events that get triggered for each Ui windows.
# How is this done? eg: <not working> (3)
def initialise()
frmConsoleLog.self.comboBox_Selector.addItems('Watts','Amps','Volts')
Contents of Ui_frmConsoleLog_ui.py (has the combobox I want to populate)
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_frmConsoleLog(object):
def setupUi(self, frmConsoleLog):
frmConsoleLog.setObjectName("frmConsoleLog")
frmConsoleLog.resize(640, 468)
frmConsoleLog.setToolTip("")
frmConsoleLog.setStatusTip("")
frmConsoleLog.setAccessibleName("")
self.horizontalLayout = QtWidgets.QHBoxLayout(frmConsoleLog)
self.horizontalLayout.setContentsMargins(0, 1, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.comboBox_Selector = QtWidgets.QComboBox(frmConsoleLog)
self.comboBox_Selector.setObjectName("comboBox_Selector")
self.horizontalLayout.addWidget(self.comboBox_Selector)
self.textEdit_ConsoleLog = QtWidgets.QTextEdit(frmConsoleLog)
self.textEdit_ConsoleLog.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.textEdit_ConsoleLog.sizePolicy().hasHeightForWidth())
self.textEdit_ConsoleLog.setSizePolicy(sizePolicy)
self.textEdit_ConsoleLog.setObjectName("textEdit_ConsoleLog")
self.horizontalLayout.addWidget(self.textEdit_ConsoleLog)
self.retranslateUi(frmConsoleLog)
QtCore.QMetaObject.connectSlotsByName(frmConsoleLog)
def retranslateUi(self, frmConsoleLog):
_translate = QtCore.QCoreApplication.translate
frmConsoleLog.setWindowTitle(_translate("frmConsoleLog", "Console Log"))
Worked it out, thanks to an answer in my previous post (that the poster removed?), here's the snippet that works and allows your functions in another .py file to set MainWindow pyQt object control properties in the parent main.py file for a much tidier project. I honestly don't care that professional devs think that is not good practice (if they do they are very closed minded).
In app.py (or main.py - I had two files and was playing with names)
class AppWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.win = None # No Console Log window visible yet.
self.setupUi(self)
from init_Ui_main_window import (initMainWindow)
initMainWindow(self)
...
In init_Ui_main_window.py:
def initMainWindow(window):
funcLoadConfig()
# Populate the controls in the main window
window.line_Settings_Simple_Default_Selection.setText(str(SETTINGS_SIMPLE_DEFAULT_SELECTOR))
Note: SETTINGS_SIMPLE_DEFAULT_SELECTOR is a globally defined static variable that wont ever be changed as its read from a config ini file. The user will be able to change the line value if they want to override the default, but it's nice to have a default value populated.
Was that easy as when initMainWindow(self) is called self from the previous object context is passed across to the function and set in function variable window, so to reference the main windows objects its just a case of referencing window.[object name as one would in main.py]

Import .ui during definition of QMainWindow

i'm trying to load a "mw.ui" file (made with Qt creator) as the design to be used on my Main Window.
I successfully load a class with:
from pyqtgraph.Qt import QtGui, QtCore, uic
mw = uic.loadUi("mw.ui")
but i can't define my class since I already have my object built. I tried to compile the mw.py during execution with
uic.compileUi("mw.ui", "mw.py")
but i have the following error:
Traceback (most recent call last):
File "myFile.py", line 26, in
uic.compileUi("mw.ui", "mw.py")
File "C:\Users\giovanni\AppData\Local\Programs\Python\Python36\lib\site-packages\PyQt5\uic__init__.py", line 162, in compileUi
pyfile.write(_header % (uifname, PYQT_VERSION_STR))
AttributeError: 'str' object has no attribute 'write'
I read that i can use pyuic4 to convert the ui to a py file but doing so every small change need a long work (compiling, etc)
I tried to "save as" with qtCreator but i can't find the PY in the infinite list of available formats
Is there a way to load the UI file in "class definition phase" without converting with pyuic4 every single changes? For me it will be ok to implement a script that compile at the begin with uic.compileUi or something similar
Thanks
I get it with
class MainW(QtGui.QMainWindow):
def __init__(self, *args):
super(MainW, self).__init__(*args)
uic.loadUi("mw.ui", self)
from: http://pyqt.sourceforge.net/Docs/PyQt4/designer.html
PyQt4.uic.loadUi(uifile[, baseinstance=None[, package=”[, resource_suffix=’_rc’]]])
baseinstance – the optional instance of the Qt base class. If
specified then the user interface is created in it. Otherwise a new
instance of the base class is automatically created.

Signal emitting in Python causes exit code 139

Im trying to make function, which passes 8 values to the progress bars in window app made with QtDesigner.
That is there is a function which actually generates this values. I want to execute it every second in order to get theese values and update progress bars which displays values.
I combined tutorials about making a graphic app with Python and making an app with dynamically updating progess bars:
Python Qt Development: https://www.youtube.com/watch?v=eD91nE8q8Nk
PyQt Progress bar with Thread: https://www.youtube.com/watch?v=ivcxZSHL7jM
The problem is that values are passed correctly, but when it comes to send the signal, the whole app crashes with message:
Process finished with exit code 139 (interrupted by signal 11:
SIGSEGV)
Here is my code:
Module which generates the values (probkowanie.py):
import time
from random import randint
import threading
def getSignals():
time.sleep(1)
signals = (randint(0, 100), randint(0, 100), randint(0, 100), randint(0, 100), randint(0, 100), randint(0, 100), randint(0, 100), randint(0, 100), )
return signals
and here is main program code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from PySide.QtCore import *
from PySide.QtGui import *
from PyQt4 import QtCore
import sys
import time
import signalPreview # window library
import probkowanie # values generating library
class MainDialog(QDialog, signalPreview.Ui_mainDialog):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadClass()
self.threadclass.start()
self.connect(self.threadclass, QtCore.SIGNAL('MYO_SIGNALS'), self.updateProgressBars)
def updateProgressBars(self, signals):
self.progressBar.setValue(signals[0])
class ThreadClass(QtCore.QThread):
def __init__(self, parent = None):
super(ThreadClass, self).__init__(parent)
def run(self):
while 1:
signals = probkowanie.getSignals()
self.emit(QtCore.SIGNAL('MYO_SIGNALS'), 5) # this line causes crash
print signals # it works correctly
app = QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()
I think it may be caused by mixing libraries. I use QtCore from PySide even though tutorial number 2 is based on PyQt4. My decision is because tutorial number 1 is based on PySide.
I tried changing:
from PySide import QtCore
to
from PyQt4 import QtCore
but then I get another bunch of errors, which I don't know what to do with:
Traceback (most recent call last):
File "/usr/lib/pycharm/helpers/pydev/pydevd.py", line 1580, in <module>
globals = debugger.run(setup['file'], None, None, is_module)
File "/usr/lib/pycharm/helpers/pydev/pydevd.py", line 964, in run
pydev_imports.execfile(file, globals, locals) # execute the script
File "/mnt/Grubas/Projekty/Biomed/MYO/myo.py", line 36, in <module>
form = MainDialog()
File "/mnt/Grubas/Projekty/Biomed/MYO/myo.py", line 20, in __init__
self.connect(self.threadclass, QtCore.SIGNAL('MYO_SIGNALS'), self.updateProgressBars)
TypeError: 'PySide.QtCore.QObject.connect' called with wrong argument types:
PySide.QtCore.QObject.connect(ThreadClass, str, instancemethod)
Supported signatures:
PySide.QtCore.QObject.connect(PySide.QtCore.QObject, str, callable, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection)
PySide.QtCore.QObject.connect(PySide.QtCore.QObject, PySide.QtCore.QMetaMethod, PySide.QtCore.QObject, PySide.QtCore.QMetaMethod, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection)
PySide.QtCore.QObject.connect(PySide.QtCore.QObject, str, PySide.QtCore.QObject, str, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection)
PySide.QtCore.QObject.connect(PySide.QtCore.QObject, str, str, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection) PySide.QtCore.QObject.connect(str, callable, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection)
PySide.QtCore.QObject.connect(str, PySide.QtCore.QObject, str, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection)
#EDIT
After providing changes proposed by #ekhumoro script doens't crash, but now I'm getting another error:
QObject::connect: Cannot queue arguments of type 'object' (Make sure
'object' is registered using qRegisterMetaType().)
I tried to look for solution on my own, but I didn't find the exact code which I need. I also tried transforming type of signal from (object) to (tuple) or (list), but it leads to another errors:
TypeError: Unknown type used to call meta function (that may be a
signal): tuple
The most solutions I found are based on PyQT. Is there an easy way to rewrite it for PySide?
Here is an example of solution, which seems to be correct but is made using PyQT:
https://stackoverflow.com/a/2595607/2550466
You are right to think that mixing PySide and PyQt will cause problems, so you will need to remove one of them. However, the crash itself is probably caused by a bug in PySide. There is an SO question with a similar problem shown here:
How to send None with Signals across threads?
So you also need to change the way you define and emit MYO_SIGNALS.
Below is a fixed version of your script (the changed lines are commented):
# from PySide.QtCore import *
# from PySide.QtGui import *
# from PyQt4 import QtCore
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
import time
import signalPreview # window library
import probkowanie # values generating library
class MainDialog(QDialog):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadClass()
self.threadclass.start()
# self.connect(self.threadclass, QtCore.SIGNAL('MYO_SIGNALS'), self.updateProgressBars)
self.connect(self.threadclass, SIGNAL('MYO_SIGNALS'), self.updateProgressBars)
def updateProgressBars(self, signals):
self.progressBar.setValue(signals[0])
# class ThreadClass(QtCore.QThread):
class ThreadClass(QThread):
def __init__(self, parent = None):
super(ThreadClass, self).__init__(parent)
def run(self):
while 1:
signals = probkowanie.getSignals()
# self.emit(QtCore.SIGNAL('MYO_SIGNALS'), 5) # this line causes crash
self.emit(SIGNAL('MYO_SIGNALS'), signals)
print signals # it works correctly
app = QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()

Python Script with QGis - Python.exe Stopped Working

I purchased this book called Building Mapping Applications with QGIS and I am trying to work through one of the exercises. There is one script that I try to run that crashes python, generating the error message "python.exe has stopped working".
import sys
import os
from qgis.core import *
from qgis.gui import *
from PyQt4.QtGui import *
from PyQt4.QtCore import Qt
#############################################################################
class MapViewer(QMainWindow):
def __init__(self, shapefile):
QMainWindow.__init__(self)
self.setWindowTitle("Map Viewer")
canvas = QgsMapCanvas()
canvas.useImageToRender(False)
canvas.setCanvasColor(Qt.white)
canvas.show()
layer = QgsVectorLayer(shapefile, "layer1", "ogr")
if not layer.isValid():
raise IOError("Invalid shapefile")
QgsMapLayerRegistry.instance().addMapLayer(layer)
canvas.setExtent(layer.extent())
canvas.setLayerSet([QgsMapCanvasLayer(layer)])
layout = QVBoxLayout()
layout.addWidget(canvas)
contents = QWidget()
contents.setLayout(layout)
self.setCentralWidget(contents)
#############################################################################
def main():
""" Our main program.
"""
QgsApplication.setPrefixPath(os.environ['QGIS_PREFIX'], True)
QgsApplication.initQgis()
app = QApplication(sys.argv)
viewer = MapViewer("C:/folder/shapefile.shp")
viewer.show()
app.exec_()
QgsApplication.exitQgis()
#############################################################################
if __name__ == "__main__":
main()
I don't know a whole lot about Python with QGIS so I'm not too sure what is causing python to crash. I am positive that all of the modules are importing correctly because if I define my paths and then import the modules in the script using the OSGeo4W Shell, there are no error messages.
This is how my paths are defined:
SET OSGEO4W_ROOT=C:\OSGeo4W64
SET QGIS_PREFIX=%OSGEO4W_ROOT%\apps\qgis
SET PATH=%PATH%;%QGIS_PREFIX%\bin
SET PYTHONPATH=%QGIS_PREFIX%\python;%PYTHONPATH%
Given all of this, I think there has to be something wrong in the script. However, when I check the script using http://pep8online.com/ there are no errors that I can fix that will result in python not crashing.
Note that I have tried I have tried SET PATH=%QGIS_PREFIX%\bin;%PATH% instead of SET PATH=%PATH%;%QGIS_PREFIX%\bin with no success.
I was fortunate enough to get in touch with the author of the book so I will share his response here:
I suspect I may know what the problem is...after looking at this
reader's problems in more depth, I've discovered that something has
changed in newer versions of QGIS, and the example code no longer
works as it is written. In technical terms, it seems that you now
need to instantiate the QApplication object before making the call to
QgsApplication.initQgis() -- the example program in the book
instantiates the QApplication object after calling
QgsApplication.initQgis(), which causes the program to crash. To fix
this, change the main() function to look like the following:
def main():
""" Our main program.
"""
app = QApplication(sys.argv)
QgsApplication.setPrefixPath(os.environ['QGIS_PREFIX'],True)
QgsApplication.initQgis()
viewer = MapViewer("C:/folder/shapefile.shp")
viewer.show()
app.exec_()
QgsApplication.exitQgis()
As you can see, I've moved the "app = QApplication(sys.argv)" line to the top.
Important Note: Make sure that forward slashes are used in viewer = MapViewer("C:/folder/shapefile.shp") - using a backslash will result in an error message stating that the shapefile is invalid.
I also thought it would be worth mentioning that none of the above fixes (comments on the question) were necessary. So, the script will work if the paths are defined as follows:
SET OSGEO4W_ROOT=C:\OSGeo4W64
SET QGIS_PREFIX=%OSGEO4W_ROOT%\apps\qgis
SET PATH=%PATH%;%QGIS_PREFIX%\bin
SET PYTHONPATH=%QGIS_PREFIX%\python;%PYTHONPATH%
Then, the entire script looks like this:
import sys
import os
from qgis.core import *
from qgis.gui import *
from PyQt4.QtGui import *
from PyQt4.QtCore import Qt
#############################################################################
class MapViewer(QMainWindow):
def __init__(self, shapefile):
QMainWindow.__init__(self)
self.setWindowTitle("Map Viewer")
canvas = QgsMapCanvas()
canvas.useImageToRender(False)
canvas.setCanvasColor(Qt.white)
canvas.show()
layer = QgsVectorLayer(shapefile, "layer1", "ogr")
if not layer.isValid():
raise IOError("Invalid shapefile")
QgsMapLayerRegistry.instance().addMapLayer(layer)
canvas.setExtent(layer.extent())
canvas.setLayerSet([QgsMapCanvasLayer(layer)])
layout = QVBoxLayout()
layout.addWidget(canvas)
contents = QWidget()
contents.setLayout(layout)
self.setCentralWidget(contents)
#############################################################################
def main():
""" Our main program.
"""
app = QApplication(sys.argv)
QgsApplication.setPrefixPath(os.environ['QGIS_PREFIX'],True)
QgsApplication.initQgis()
viewer = MapViewer("C:/folder/shapefile.shp")
viewer.show()
app.exec_()
QgsApplication.exitQgis()
#############################################################################
if __name__ == "__main__":
main()
Execute it in the OSGEO4W Shell using the following command:
python "C:\script.py"
Lastly, note that at the time of this writing, the script works properly and launches a viewer showing the shapefile referenced, but returns a few errors in the shell that do not seem to be problematic:
ERROR: Opening of authentication db FAILED
ERROR: Unable to establish authentication database connection
ERROR: Auth db could not be created and opened
QSqlDatabasePrivate::database: unable to open database: "unable to open database file Error opening database"
ERROR: Opening of authentication db FAILED
Much thanks to the author Erik Westra for providing me with this solution.
One thing that seems suspect is that you're creating a gui element without giving it a parent - QgsMapCanvas() - and then trying to manually show() it before adding it to a layout. You should never have to call show() on subwidgets, and all subwidgets should be parented to the main widget (or one of its other subwidgets).
Also, you should store persistent references to the python objects; otherwise, it's possible the underlying C++ object with get garbage collected and cause your program to crash. You do this by assigning your widgets and layouts to an attribute on self
Ex.
self.layout = QVBoxLayout(...
self.layer = ...
You should be adding the canvas like this, you should not need to call .show()
self.canvas = QgsMapCanvas(self)
layout.addWidget(self.canvas)

Categories

Resources