I have two python files, the first one is to handle database related stuff and the second one imports that file so I can use it with PyQt.
The problem is if I want to change the label in the first file it doesn't work the application just crashes.
The reason I want the first file to be able to change the label is if an error occurs when trying to connect to the DB.
Here is a short view of my code:
First file.
from mysql.connector import (connection)
from datetime import *
from RPI1_ui import Ui_Form
'''Handling of database related stuff'''
class DatabaseUtility(Ui_Form):
def __init__(self):
#DB Connection
self.cnx = None
self.cursor = None
def mysql_connect(self):
# Database connection
try:
self.cnx = connection.MySQLConnection(user='root', password='', host='127.0.0.1', database='spicadb')
self.cursor = self.cnx.cursor()
except connection.errors.InterfaceError as e:
self.lblShowInfo.setText(str(e)) # -> Try to change label
def get_table(self):
return self.run_command("SELECT * FROM tblseal")
def get_columns(self):
return self.run_command("SHOW COLUMNS FROM tblseal")
Second file.
from PyQt5.QtWidgets import QApplication, QWidget, QDialog
from datetime import *
from bs4 import BeautifulSoup as bs
import os
import sys
import DatabaseHandling
'''Convert UI file to Python'''
os.chdir("C:\\Users\Gianni Declercq\AppData\Local\Programs\Python\Python36-32\Scripts")
os.system("pyuic5.exe H:\QtProjects\\RPI1.ui -o H:\QtProjects\\RPI1_ui.py")
from RPI1_ui import Ui_Form # import after recreation of py file
from RPI1_Sec import SecondWindow
'''Main program'''
class MainWindow(QWidget, Ui_Form):
def __init__(self):
super(MainWindow, self).__init__()
# Initialize variables
self.dbu = DatabaseHandling.DatabaseUtility()
self.spica_reference = None
self.barcode = None
self.msl = None
self.package_body = None
self.window2 = None
# Get UI elements + resize window
self.setupUi(self)
self.resize(800, 480)
# Define what should happen on button click
self.btnQuit.clicked.connect(lambda: app.exit())
self.btnInsert.clicked.connect(lambda: self.get_entry())
self.btnTable.clicked.connect(lambda: self.new_window())
# Style buttons
self.btnQuit.setStyleSheet("background-color: red")
self.btnInsert.setStyleSheet("background-color: green")
self.btnTable.setStyleSheet("background-color: orange")
def get_entry(self):
try:
self.spica_reference = self.txtReferentie.text()
self.barcode = self.txtBarcode.text()
self.msl = self.txtMsl.text()
self.package_body = float(self.txtBodyPackage.text())
except ValueError:
self.lblShowInfo.setText("Please insert the correct values")
self.lblShowInfo.setStyleSheet('color: red')
else:
self.dbu.mysql_connect()
if self.dbu.cursor and self.dbu.cnx is not None:
self.dbu.add_entry(self.spica_reference, self.barcode, self.msl, self.package_body, self.calc_floor_life)
Give a "hook function" to the DatabaseUtility when initializing.
class MainWindow(QWidget, Ui_Form):
def __init__():
def ch_lab(text):
self.lblShowInfo.setText(text)
self.dbu = DatabaseHandling.DatabaseUtility(ch_lab)
class DatabaseUtility(Ui_Form):
def __init__(cb):
self.error_warn = cb
#.. error happening
self.error_warn('error')
Related
I am developing an EEL project, and I needed to create a file dialog on the python side in order to preprocess data before sending it to javascript.
I tried to use tk.filedialog.askopenfilename, but that somehow froze the javascript event loop.
I found an answer on StackOverflow that used wxpython to create a non-blocking file picker. However, when I run the code below, the file picker always starts minimized.
However, once you use the file picker once, it works perfectly the second time.
Any help appreciated.
import base64
import json
from tkinter import Tk
Tk().withdraw()
from tkinter.filedialog import askopenfilename
import PIL.Image
import eel
import numpy as np
import wx
# Reusable wxpython App instance for the creation of non-blocking popup dialogs
app=wx.App(None)
eel.init("public")
def encode(bts):
return base64.b64encode(bts)
def array_to_json(array):
return json.dumps({
"shape": list(array.shape),
"dtype": str(array.dtype),
"data":list(np.ravel(array).astype(float)) # not efficient but quite clean
})
#eel.expose
def load_image(path):
return array_to_json(np.asarray(PIL.Image.open(path)))
#eel.expose
def pick_image():
# return askopenfilename()
""" --- Adapted from https://stackoverflow.com/a/59177064/5166365"""
style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.STAY_ON_TOP | wx.DIALOG_NO_PARENT | wx.MAXIMIZE
dialog = wx.FileDialog(None, "Open File", wildcard="*", style=style)
dialog.Iconize(False)
dialog.Maximize()
dialog.Raise()
path = ""
if dialog.ShowModal() == wx.ID_OK:
path = dialog.GetPath()
else:
path = ""
return path
""" --- """
eel.start("index.html")
I was able to get it working with the following code. I used a regular window instead of a wx.Dialog or similar class.
class FancyFilePickerApplication:
def __init__(self):
self.app = wx.App()
self.frame = wx.Frame(None,title="Fancy File Picker")
self.build_ui()
##private
def build_ui(self):
self.vertical_sizer = wx.BoxSizer(wx.VERTICAL)
self.control_panel = wx.Panel(self.frame,wx.ID_ANY)
self.horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.control_panel.SetSizer(self.horizontal_sizer)
self.frame.SetSizer(self.vertical_sizer)
self.dir_ctrl = wx.GenericDirCtrl(self.frame,wx.ID_ANY,wx.EmptyString,wx.DefaultPosition,wx.Size(400,300))
self.vertical_sizer.Add(self.dir_ctrl,wx.SizerFlags().Expand())
self.vertical_sizer.Add(self.control_panel,wx.SizerFlags().Expand())
self.scale_factor_label = wx.StaticText(self.control_panel,wx.ID_ANY,"Scale Factor: ")
self.scale_factor_textbox = wx.TextCtrl(self.control_panel,wx.ID_ANY)
self.open_button = wx.Button(self.control_panel,wx.ID_ANY,"Open")
self.horizontal_sizer.Add(self.scale_factor_label,wx.SizerFlags().Expand())
self.horizontal_sizer.Add(self.scale_factor_textbox,wx.SizerFlags().Expand())
self.horizontal_sizer.Add(self.open_button,wx.SizerFlags().Expand())
self.open_button.Bind(wx.EVT_BUTTON,lambda evt:self.submit())
self.frame.Bind(wx.EVT_CLOSE,self.cancel)
def open(self, file_picked_callback):
self.file_picked_callback = file_picked_callback
self.frame.Fit()
self.frame.Center()
self.frame.Show()
self.frame.Raise()
self.frame.ToggleWindowStyle(wx.STAY_ON_TOP)
self.app.MainLoop()
##private
def submit(self):
filepath =self.dir_ctrl.GetFilePath()
scale_factor_text = self.scale_factor_textbox.GetValue()
scale_factor = 1.0 if not scale_factor_text.strip() else float(scale_factor_text)
self.file_picked_callback(filepath,scale_factor)
self.frame.Destroy()
##private
def cancel(self,evt):
self.file_picked_callback("",0)
self.frame.Destroy()
Please be kind. This is my first question. I have included a minimum listing that illustrates the problem.
from PyQt5 import QtWidgets, QtCore, QtGui, uic, QtSql
from PyQt5.QtCore import Qt
qtCreatorFile = "QuotesGui.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
class QuotesUI(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, model):
QtWidgets.QMainWindow.__init__(self)
uic.loadUi(qtCreatorFile,self)
self.setupUi(self)
self.model = model # View owns the model
self.tableView.setModel(self.model._formModel)
self.setWidgetMapping() # Map model to view widgets
authorSRD = QtSql.QSqlRelationalDelegate(self.authorComboBox)
self.authorComboBox.setModel(self.model._authorModel)
self.authorComboBox.setItemDelegate(authorSRD)
self.authorComboBox.setModelColumn(
self.model._formModel.fieldIndex("authorName"))
citationSRD = QtSql.QSqlRelationalDelegate(self.citationComboBox)
self.citationComboBox.setModel(self.model._citationModel)
self.citationComboBox.setItemDelegate(citationSRD)
self.citationComboBox.setModelColumn(
self.model._formModel.fieldIndex("citationText"))
self.mainMapper.toFirst()
self._updateButtons(0) # To disable the previous button at start up
self.saveBtn.setEnabled(False) # Since we can't edit, we can't save
self._connectSignals(self.model)
def setWidgetMapping(self):
self.mainMapper = QtWidgets.QDataWidgetMapper(self)
self.mainMapper.setModel(self.model._formModel)
self.mainMapper.setItemDelegate(QtSql.QSqlRelationalDelegate(self))
self.mainMapper.addMapping(self.authorComboBox,
self.model._formModel.fieldIndex("authorName"))
self.mainMapper.addMapping(self.citationComboBox,
self.model._formModel.fieldIndex("citationText"))
self.mainMapper.addMapping(self.quoteText,
self.model._formModel.fieldIndex("quoteText"))
def _connectSignals(self, model):
self.lastBtn.clicked.connect(self.mainMapper.toLast)
self.nextBtn.clicked.connect(self.mainMapper.toNext)
self.prevBtn.clicked.connect(self.mainMapper.toPrevious)
self.firstBtn.clicked.connect(self.mainMapper.toFirst)
self.mainMapper.currentIndexChanged.connect(
self._updateButtons)
def _updateButtons(self, row):
self.firstBtn.setEnabled(row > 0)
self.prevBtn.setEnabled(row > 0)
self.nextBtn.setEnabled(row < self.model._formModel.rowCount() - 1)
self.lastBtn.setEnabled(row < self.model._formModel.rowCount() - 1)
class QuoteModel():
def __init__(self, data):
# Make a database connection
self.db = self.createConnection(data)
# set models
self._setFormModel()
self._setAuthorComboTableLink()
self._setCitationComboTableLink()
# Connect signals
self._connectSignals()
# Define a model for the form
def _setFormModel(self):
self._formModel = QtSql.QSqlRelationalTableModel(
parent = None, db = self.db)
self._formModel.setEditStrategy(
QtSql.QSqlTableModel.OnManualSubmit)
self._formModel.setTable("Quote")
authorIdx = self._formModel.fieldIndex("authorId")
citationIdx = self._formModel.fieldIndex("citationId")
self._formModel.setJoinMode(1) # Left Join
self._formModel.setRelation(authorIdx, QtSql.QSqlRelation(
"Author", "authorId", "authorName"))
self._formModel.setRelation(citationIdx, QtSql.QSqlRelation(
"Citation", "citationId", "citationText"))
self._formModel.select()
# Define models and link tables for the two comboboxes
def _setAuthorComboTableLink(self):
self._authorModel = QtSql.QSqlTableModel(
parent = None, db = self.db)
self._authorModel.setTable("Author")
self._authorModel.setEditStrategy(
QtSql.QSqlTableModel.OnManualSubmit)
self._authorModel.select()
self._authorModel.sort(1,Qt.AscendingOrder)
def _setCitationComboTableLink(self):
self._citationModel = QtSql.QSqlTableModel(
parent = None, db = self.db)
self._citationModel.setTable("Citation")
self._citationModel.setEditStrategy(
QtSql.QSqlTableModel.OnManualSubmit)
self._citationModel.select()
self._citationModel.sort(1,Qt.AscendingOrder)
def _newData(self):
print('in _newData')
def _connectSignals(self):
self._authorModel.rowsInserted.connect(self._newData)
#######################################################
# A function to connect to a named database
def createConnection(self,dbfile):
db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName(dbfile)
db.open()
if not db.open():
QtWidgets.QMessageBox.critical(
None, QtWidgets.qApp.tr("Cannot open database"),
QtWidgets.qApp.tr("Unable to establish a database connection.\n"),
QtWidgets.QMessageBox.Cancel,
)
return False
return db
dbFile = "Quotations.db"
if __name__=='__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
m = QuoteModel(dbFile)
w = QuotesUI(m)
w.show()
sys.exit(app.exec_())
Still requires the upload of a 8kb .ui file and a 70kb sample database to run. So far I haven't been able to figure out how to do that....
When this is run, incorporating the rest of the necessary code, the author combobox works as expected, i.e.: the dropdown shows all the values in the author table and when I step the the records in the model, the correct author is displayed in the combobox.
The citation combobox does NOT function. As written here it neither shows values from the citation table, nor is it connected to the quotes table. Basically it shows nothing.
I think I need, somehow, a second delegate on the model. But I don't know how.
Any ideas how to get this functioning??
To make this work I had to change:
self.authorComboBox.setModel(self.model._authorModel)
to:
authorRel = self.model._formModel.relationModel(self.model.authorIdx)
self.authorComboBox.setModel(authorRel)
The QSqlRelationalTableModel has a virtual function "relationModel" that returns a QSqlTableModel. When you use this model as the model for the QComboBox, everything works.
I want the following code to get the request url starting with http://down.51en.com:88 during the web loading process , and then do other processing with the response object of the url .
In my program, once targetUrl is assigned a value , I want the function targetUrlGetter(url) to return it to the caller, however , the problem is that QApplication::exec() enters the main event loop so cannot execute code at the end of thetargetUrlGetter() function after the exec() call , thus the function cannot return , I have tried with qApp.quit() in interceptRequest(self, info) in order to tell the application to exit so that targetUrlGetter(url) can return , but the function still cannot return and the program even crashes on exit(tested on Win7 32bit), so how can I return the targetUrl to the caller program ?
The difficulties here are how to exit the Qt event loop without crash and return the request url to the caller.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtWebEngineCore import *
from PyQt5.QtCore import *
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
def __init__(self, parent=None):
super().__init__(parent)
self.page = parent
def interceptRequest(self, info):
if info.requestUrl().toString().startswith('http://down.51en.com:88'):
self.targetUrl = info.requestUrl().toString()
print('----------------------------------------------', self.targetUrl)
qApp.quit()
# self.page.load(QUrl(''))
def targetUrlGetter(url=None):
app = QApplication(sys.argv)
page = QWebEnginePage()
globalSettings = page.settings().globalSettings()
globalSettings.setAttribute(
QWebEngineSettings.PluginsEnabled, True)
globalSettings.setAttribute(
QWebEngineSettings.AutoLoadImages, False)
profile = page.profile()
webEngineUrlRequestInterceptor = WebEngineUrlRequestInterceptor(page)
profile.setRequestInterceptor(webEngineUrlRequestInterceptor)
page.load(QUrl(url))
# view = QWebEngineView()
# view.setPage(page)
# view.show()
app.exec_()
return webEngineUrlRequestInterceptor.targetUrl
url = "http://www.51en.com/news/sci/everything-there-is-20160513.html"
# url = "http://www.51en.com/news/sci/obese-dad-s-sperm-may-influence-offsprin.html"
# url = "http://www.51en.com/news/sci/mars-surface-glass-could-hold-ancient-fo.html"
targetUrl = targetUrlGetter(url)
print(targetUrl)
You should always initialize QApplication at the beginning of the program, and always call the QApplication::exec function at the end of the program.
Another thing is that QWebEngineUrlRequestInterceptor.interceptRequest is a callback function which is called asynchronously. Since info.requestUrl().toString() is called inside the callback function, there is no way to return the result it synchronously.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtWebEngineCore import *
from PyQt5.QtCore import *
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
def __init__(self, parent=None):
super().__init__(parent)
self.page = parent
def interceptRequest(self, info):
if info.requestUrl().toString().startswith('http://down.51en.com:88'):
self.targetUrl = info.requestUrl().toString()
print('----------------------------------------------', self.targetUrl)
# Don't do thatDo you really want to exit the whole program?
# qApp.quit()
# Do something here, rather than return it.
# It must be run asynchronously
# self.page.load(QUrl(''))
def targetUrlGetter(url=None):
page = QWebEnginePage()
globalSettings = page.settings().globalSettings()
globalSettings.setAttribute(
QWebEngineSettings.PluginsEnabled, True)
globalSettings.setAttribute(
QWebEngineSettings.AutoLoadImages, False)
profile = page.profile()
webEngineUrlRequestInterceptor = WebEngineUrlRequestInterceptor(page)
profile.setRequestInterceptor(webEngineUrlRequestInterceptor)
page.load(QUrl(url))
# view = QWebEngineView()
# view.setPage(page)
# view.show()
# Don't return this. It cannot be called synchronously. It must be called asynchronously.
# return webEngineUrlRequestInterceptor.targetUrl
app = QApplication(sys.argv) # always initialize QApplication at the beginning of the program
url = "http://www.51en.com/news/sci/everything-there-is-20160513.html"
# url = "http://www.51en.com/news/sci/obese-dad-s-sperm-may-influence-offsprin.html"
# url = "http://www.51en.com/news/sci/mars-surface-glass-could-hold-ancient-fo.html"
targetUrl = targetUrlGetter(url)
print(targetUrl)
app.exec_() # always call the QApplication::exec at the end of the program
I'm trying make an exe application from my scripts using py2exe. The problem is that if I run it by doubleclicking on exe file, it closes immediately after open. If I run it from cmd, it works perfect.
I've tried to make a table global but it did not helped. I've already tried to keep the reference - no effect.
Here is the code of GUI in case it helps:
from PyQt4.QtGui import QTableWidget, QTableWidgetItem,QApplication
import sys
from PyQt4.QtGui import *
import os
from meteo import mesiac,den
class MyTable(QTableWidget):
def __init__(self, data, *args):
QTableWidget.__init__(self, *args)
self.data = data
self.setmydata()
self.resizeColumnsToContents()
self.resizeRowsToContents()
def setmydata(self):
horHeaders = []
for n, key in enumerate(['max teplota','min teplota','max vlhkost','min vlhkost','max tlak',
'min tlak','avg teplota','avg vlhkost','avg tlak']):
horHeaders.append(key)
for m, item in enumerate(self.data[key]):
newitem = QTableWidgetItem(str(item))
self.setItem(m, n, newitem)
self.setHorizontalHeaderLabels(horHeaders)
vertical_headers = ['{}.'.format(x+1) for x in xrange(len(self.data.values()[0])-1)]+['Mesiac:']
self.setVerticalHeaderLabels(vertical_headers)
self.setWindowTitle('Meteo')
def show_table():
global table
table = MyTable(data, len(data.values()[0]), len(data.keys()))
table.setFixedHeight(len(data.values()[0])*20)
table.setFixedWidth(len(data.keys())*95)
table.show()
return table
if __name__ == '__main__':
data = {}
os.system("mode con: cols=100 lines=40")
filenames = next(os.walk('mesiac'))[2]
list_of_den = []
for filename in filenames:
list_of_den.append(den(filename,filename[:-4]))
m = mesiac(list_of_den)
for den in list_of_den:
for n,attr in enumerate(['max teplota','min teplota','max vlhkost','min vlhkost','max tlak',
'min tlak','avg teplota','avg vlhkost','avg tlak']):
try:
data[attr].append(den.to_list()[n])
except:
data[attr]=[den.to_list()[n]]
m_list = m.to_list()
for n,attr in enumerate(['max teplota','min teplota','max vlhkost','min vlhkost','max tlak',
'min tlak','avg teplota','avg vlhkost','avg tlak']):
try:
data[attr].append(m_list[n])
except:
data[attr]=[m_list[n]]
app = QApplication(sys.argv)
t = show_table()
sys.exit(app.exec_())
And for sure - here is a setup.py file:
from distutils.core import setup
import py2exe
options ={"py2exe":{"includes":["sip","PyQt4.QtGui","PyQt4.QtCore","meteo"],"dll_excludes": ["MSVCP90.dll", "HID.DLL", "w9xpopen.exe"]}}
setup(window=['table.py'],options=options)
How to make it visible?
I'm trying to append nodes of namedtuple object to a treeView but I'm not sure how to subclass QAbstractItem (if that's even the proper way). I'm still very new to Python so this is confusing to me. Here is my problem code:
Exercise = namedtuple('Exercise','name html list')
e_list = []
for i in range(1,6,1):
dummy_list = [1,2,3,'a','b','c']
ntup = Exercise("exercise{0}".format(i),'html here',dummy_list)
e_list.append(ntup)
for e in e_list:
item = QtGui.QStandardItem(e) # gives error
self.tree_model.appendRow(item) # doesnt execute
And here is the the whole program:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from collections import namedtuple
class MyWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.pushButton = QtGui.QPushButton(self)
self.pushButton.setText("Test 1 - doesn't work")
self.pushButton.clicked.connect(self.on_pushbutton)
self.pushButton2 = QtGui.QPushButton(self)
self.pushButton2.setText("Test 2 - works")
self.pushButton2.clicked.connect(self.on_pushbutton2)
self.treeView = QtGui.QTreeView(self)
self.treeView.clicked[QModelIndex].connect(self.on_clickitem)
self.tree_model = QtGui.QStandardItemModel()
self.treeView.setModel(self.tree_model)
self.layoutVertical = QtGui.QVBoxLayout(self)
self.layoutVertical.addWidget(self.pushButton)
self.layoutVertical.addWidget(self.pushButton2)
self.layoutVertical.addWidget(self.treeView)
def on_pushbutton(self):
Exercise = namedtuple('Exercise','name html list')
e_list = []
for i in range(1,3,1):
dummy_list = [1,2,3,'a','b','c']
ntup = Exercise("exercise{}".format(i),'html here',dummy_list)
e_list.append(ntup)
for e in e_list:
item = QtGui.QStandardItem(e) # gives error
self.tree_model.appendRow(item) # never occurs
def on_pushbutton2(self):
txt = 'hello world'
item = QtGui.QStandardItem(txt)
self.tree_model.appendRow(item)
def on_clickitem(self,index):
item = self.tree_model.itemFromIndex(index) # doesn't work
print "item name:",item.getName() # doesn't work
print "item html:",item.getHtml() # doesn't work
print "item list:",item.getList() # doesn't work
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.show()
sys.exit(app.exec_())
I want to append the nodes into a tree and when I click on an item I want to get the values of the namedtuple (i.e. the values of 'name', 'html', and 'alist'). Thanks for your help.
Paul
I just ended up using QTreeWidget and setting the tree item data like this:
item = QtGui.QTreeWidgetItem()
item.mydata = my_namedtuple
I found the answer here: QtGui QTreeWidgetItem setData holding a float number
I didn't know you could just dynamically make an attribute for the tree item.