I'm having a problem trying to do a very simple user interface. I made my UI with Qt Designer, and then with pyuic4 I got my python code. Then I programmed the function I needed, and compiled with Eclipse IDE.
The code I got from pyuic4 is:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'Dni.ui'
#
# Created: Sat Apr 14 02:44:34 2012
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(371, 217)
Dialog.setMinimumSize(QtCore.QSize(371, 217))
self.layoutWidget = QtGui.QWidget(Dialog)
self.layoutWidget.setGeometry(QtCore.QRect(30, 30, 311, 151))
self.layoutWidget.setObjectName(_fromUtf8("layoutWidget"))
self.gridLayout = QtGui.QGridLayout(self.layoutWidget)
self.gridLayout.setMargin(0)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
self.label = QtGui.QLabel(self.layoutWidget)
self.label.setObjectName(_fromUtf8("label"))
self.horizontalLayout.addWidget(self.label)
self.entrada = QtGui.QLineEdit(self.layoutWidget)
self.entrada.setObjectName(_fromUtf8("entrada"))
self.horizontalLayout.addWidget(self.entrada)
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
self.boton = QtGui.QPushButton(self.layoutWidget)
self.boton.setObjectName(_fromUtf8("boton"))
self.gridLayout.addWidget(self.boton, 1, 0, 1, 1)
self.horizontalLayout_2 = QtGui.QHBoxLayout()
self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
self.label_3 = QtGui.QLabel(self.layoutWidget)
self.label_3.setObjectName(_fromUtf8("label_3"))
self.horizontalLayout_2.addWidget(self.label_3)
self.salida = QtGui.QLineEdit(self.layoutWidget)
self.salida.setObjectName(_fromUtf8("salida"))
self.horizontalLayout_2.addWidget(self.salida)
self.gridLayout.addLayout(self.horizontalLayout_2, 2, 0, 1, 1)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("Dialog", "Introduzca su DNI", None, QtGui.QApplication.UnicodeUTF8))
self.boton.setText(QtGui.QApplication.translate("Dialog", "Hallar NIF", None, QtGui.QApplication.UnicodeUTF8))
self.label_3.setText(QtGui.QApplication.translate("Dialog", "NIF:", None, QtGui.QApplication.UnicodeUTF8))
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
Dialog = QtGui.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
And the code I made with the function I need:
from Dni import Ui_Dialog
from PyQt4 import QtCore, QtGui
LETRADNI = {0:'T', 1:'R', 2:'W', 3:'A', 4:'G', 5:'M', 6:'Y', 7:'F', 8:'P', 9:'D', 10:'X', 11:'B', 12:'N',
13: 'J', 14:'Z', 15:'S', 16:'Q', 17:'V', 18:'H', 19:'L', 20:'C', 21:'K', 22:'E'}
# Se hereda de la clase QtGui.QMainWindow
class Principal(QtGui.QMainWindow):
# Se define el constructor de la clase __init__
def __init__(self):
# Se llama al constructor de la clase padre
QtGui.QMainWindow.__init__(self)
# Se crea la instancia de Ui_Dialog
self.ventana = Ui_Dialog()
self.ventana.setupUi(self)
# Se conectan las señales con los slots
self.connect(self.ventana.boton,QtCore.SIGNAL('clicked()'), self.letradni)
def Calcula_letra_dni(dni):
'''Función Calcula_letra_dni:
Funcionamiento:
La función recibe el valor entero dni. Posteriormente calculará el resto de la división
por 23. Éste número se buscará en el diccionario 'LETRADNI' para obtener la letra correspondiente
a ese DNI.
Argumentos
dni -- número del documento nacional de identidad (int)
Devuelve:
Una cadena (string) -- DNI + letra preparado para salida por pantalla
'''
#if len(str(dni))>8 & len(str(dni))<7:
# raise ValueError('El dni debe tener entre 7 y 8 cifras')
num_letra = dni % 23.0
letra = LETRADNI[num_letra]
return '{0}-{1}'.format(dni,letra)
def letradni(self):
self.ventana.salida.setText(Calcula_letra_dni(self.ventana.entrada.text()))
The first one compiles and runs, it shows my ui perfectly.
Compiling the second one I get an error that says:
Description Resource Path Location Type
Undefined variable from import: QString Dni.py /Dni line 18 PyDev Problem
Can anyone help me?
Thanks in advance.
First off, I think your actual listed problem is related to Eclipse, pydev, and your projects PYTHONPATH. Review this to make sure you have properly set up everything and included PyQt4 in your pythonpath:
http://popdevelop.com/2010/04/setting-up-ide-and-creating-a-cross-platform-qt-python-gui-application/
After that, you seem to have some problems with your code beyond what you have mentioned...
First you define Principal class, then a Calcula_letra_dni function, but then you are defining what looks like a class instance method letradni which should be part of Principal:
class Principal(QtGui.QMainWindow):
# Se define el constructor de la clase __init__
def __init__(self):
...
def letradni(self):
...
def Calcula_letra_dni(dni):
...
Then it looks like you will raise an exception when you try to do math on a string (thanks #Avaris) and float:
num_letra = dni % 23.0
You should probably convert that string to a float first: num_letra = float(dni) % 23.0
And finally, I think you also forgot to define a main for your application. You have the one that is autogenerated in your Dni.py, but you didn't write one for your actual entry point script:
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
form = Principal()
form.show()
sys.exit(app.exec_())
Related
Going straight to the point: I would like to click on the MDFilRoundFlatButton button in the ContentButton class and execute the close_order function in the TelaClose class
Can anyone help me on this one?
I'm still a little confused about executing functions from other classes, especially with kivy.
I've come a long way in this matter, I could use root.get_screen(... but I get an error saying that my class doesn't have the object to be executed.
from kivy.utils import get_color_from_hex
from kivymd.uix.button import MDRoundFlatIconButton
from kivymd.color_definitions import colors
from kivymd.uix.button import MDFillRoundFlatButton, MDRaisedButton
from kivymd.font_definitions import theme_font_styles
from kivymd.uix.label import MDLabel
import json
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd import images_path
from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelOneLine
from kivy.uix.screenmanager import Screen
from kivymd.uix.list import (
ImageLeftWidget,
ThreeLineIconListItem,
OneLineListItem,
)
# Imports para trabalhar com Firebase
from config import config
import pyrebase
import requests
from datetime import date
from datetime import datetime
today = date.today()
today_now = datetime.now()
Builder.load_string("""
<Content>
adaptive_height: True
orientation: 'vertical'
# TwoLineIconListItem:
# text: "(050)-123-45-67"
# secondary_text: "Mobile"
#
# IconLeftWidget:
# icon: 'phone'
<ContentButton>
orientation: 'vertical'
adaptive_height: True
MDFillRoundFlatButton:
size_hint: .9, None
pos_hint: {'center_x': .5, 'center_y': .5}
text: "Fechar Pedido"
on_release: root.fechar_pedido()
<TelaFechamento>:
name: 'tela_fechamento'
ScrollView:
pos_hint: {"top": .89}
MDGridLayout:
id: box
cols: 1
adaptive_height: True
""")
class InvoicePanel(MDExpansionPanel):
pass
class Content(MDBoxLayout):
'''Custom content.'''
class ContentButton(MDBoxLayout):
'''Custom content.'''
# Classe criada para permitir alinhamento
class OneLineListItemAligned(OneLineListItem):
def __init__(self, halign, **kwargs):
super(OneLineListItemAligned, self).__init__(**kwargs)
self.ids._lbl_primary.halign = halign
class TelaFechamento(Screen):
def fechar_pedido(self):
# Inicializando o banco de dados.
firebase = pyrebase.initialize_app(config)
db = firebase.database()
app = MDApp.get_running_app()
user_profiles = db.child('UserProfile').child(str(app.logged_user)).get(
token=app.USER_MASTER)
idpedido = {}
# Preenchendo os dados das listas
for produto in user_profiles.each():
if 'Cart' in produto.key():
items = produto.val().get('Cart')
for k, v in items['Opened'].items():
idpedido[k] = v
chave = datetime.now().strftime("%Y%m%d%H%M%S%f")
#print(json.dumps(id_pedido, indent=4))
# ********************************************************************************************************************************
# Criar pedidos importados no WShop
db.child('UserProfile').child(str(app.logged_user)).child(produto.key()).child('Confirmed').child(
chave).set(idpedido, token=app.USER_MASTER)
# Aqui abaixo devo criar o código para excluir de Cart
# Code here, Code here, Code here, .....
def pedidos_abertos(self):
# Inicializando o banco de dados.
firebase = pyrebase.initialize_app(config)
db = firebase.database()
app = MDApp.get_running_app()
json_data = db.child('UserProfile').child(str(app.logged_user)).get(
token=app.USER_MASTER)
for produtos in json_data.each():
#print(json.dumps(produtos.key(), indent=4))
if produtos.key() == 'Cart':
cart = produtos
try:
openeds = cart.val()['Opened']
#print(json.dumps(openeds, indent=4), '...............')
# adicionar fatura como item principal
cw = Content() # preencha o conteúdo à medida que os dados são analisados
ep = InvoicePanel(icon="./images/pedidos/shopping-cart.png", panel_cls=MDExpansionPanelOneLine(text=f'Produtos no carrinho:'),
content=cw)
self.ids.box.add_widget(ep)
# -----------------------------------------------------
# Cria o Widget Especificamente para fechar o pedido.
# close_items = MDRaisedButton(
# text="[color=ffffff]FATURAR ESTES ITENS[/color]", size_hint=(.9, .9))
# cw.add_widget(close_items)
close_items = OneLineListItemAligned(
text='Fechar este pedido agora?',
md_bg_color=app.theme_cls.primary_dark,
halign="center", font_style="H6", on_press=self.fechar_pedido)
# close_items.bind(on_release=print("----------Click!"))
cw.add_widget(close_items)
# -----------------------------------------------------
cw.add_widget(ContentButton())
for key_item, value_item in openeds.items():
#print(json.dumps(key_item, indent=4), '************* key_item')
# Cria a imagem que será usada no TwoLineIconListItem.
image = ImageLeftWidget(
source=value_item['url']
)
# Cria o Widget conforme sua escolha.
items = ThreeLineIconListItem(text=value_item['product_code'],
secondary_text=value_item['product_description'],
tertiary_text='{} x R${} = R${}'.format(value_item['quantidade'], value_item['price'], value_item['total_item']), md_bg_color=get_color_from_hex('#FFFDE7'))
# Adiciona a imagem ao Widget acima.
items.add_widget(image)
cw.add_widget(items)
except:
print('Ainda não possui pedidos!')
def pedidos_faturados(self):
# Inicializando o banco de dados.
firebase = pyrebase.initialize_app(config)
db = firebase.database()
app = MDApp.get_running_app()
json_data = db.child('UserProfile').child(str(app.logged_user)).get(
token=app.USER_MASTER)
for produtos in json_data.each():
#print(json.dumps(produtos.key(), indent=4))
if produtos.key() == 'Cart':
cart = produtos
try:
invoices = cart.val()['Invoice']
for key_invoice, value_invoice in invoices.items():
#print(json.dumps(value_invoice, indent=4))
# adicionar fatura como item principal
cw = Content() # preencha o conteúdo à medida que os dados são analisados
ep = InvoicePanel(icon='./images/pedidos/invoice.png', panel_cls=MDExpansionPanelOneLine(text=f'Confirmado: {key_invoice}'),
content=cw)
self.ids.box.add_widget(ep)
for key_item, value_item in value_invoice.items():
#print(json.dumps(key_item, indent=4))
# Cria a imagem que será usada no TwoLineIconListItem.
image = ImageLeftWidget(
source=value_item['url']
)
# Cria o Widget conforme sua escolha.
items = ThreeLineIconListItem(text=value_item['product_code'],
secondary_text=value_item['product_description'],
tertiary_text='{} x R${} = R${}'.format(value_item['quantidade'], value_item['price'], value_item['total_item']))
# Adiciona a imagem ao Widget acima.
items.add_widget(image)
cw.add_widget(items)
except:
print('Ainda não possui pedidos!')
def on_enter(self):
self.pedidos_abertos()
self.pedidos_faturados()
def on_kv_post(self, base_widget):
app = MDApp.get_running_app()
I think I found the best approach to this.
We must start from the app and go navigating until we reach the window where the class is and after finding execute the function.
<ContentButton>
orientation: 'vertical'
adaptive_height: True
MDFillRoundFlatButton:
size_hint: .9, None
pos_hint: {'center_x': .5, 'center_y': .5}
text: "Fechar Pedido"
on_release: print(app.root.get_screen('main_screen').ids.screen_manager.get_screen('tela_fechamento').fechar_pedido())
I am currently working on OCR on pdf files. Here is my pipeline:
i first extract image from pdf (since my pdf contained scanned document) and convert in numpy array
then i read with tesseract
It works pretty well on most of my image but i have sevral whose i can't extract the image inside. I just gave an example and i can't find (see next) the scanned image containing the writing part (for OCR). It drive me crazy (where has it gone ??).
Perhaps you could help me to retrieve that image and understand why my way do not let me retrieve this image "fantôme" ?
NB: i noticed that thoses problematic images inside the pdf are in "jpx" format.
Edit: Since the image is unfindable in the pdf i tried an horrible trick (waiting for clever explanation :) ): converting whole pdf page in pix (PyMuPdf let do that) and then writing the PIX on disk in different format (PNG, TIFF). The quality is too much degraded compared with the original pdf (so we can forget a reasonnable reading with Tesseract).
Here is the pdf example file (if you have simpler hosting way i am curious): https://www.filehosting.org/file/details/906817/IB00058815877D0000000.pdf
Here are the 2 images i extract from the file (the second one should contain txt instead of garbage)
Here is my code to extract images:
import fitz
import os
import logging
import cv2
from PIL import Image
from .utils import lazyproperty,showpdf
from .imhelpers import show
from ..config import myconfig
from impocr import logger
import pytesseract
pytesseract.pytesseract.tesseract_cmd = myconfig.TESSERACT_CMD
class InvalidImage(Exception):
pass
class PDFParser():
"""
"""
def __init__(self,filepath,page_num=0):
self.filepath = filepath
self.filename = os.path.basename(self.filepath).split('.pdf')[0]
try:
self._doc = fitz.open(filepath)
self.page_num = page_num
self._page = self._doc[page_num]
except Exception as e:
print("Lecture PDF impossible. {}".format(e))
raise
#lazyproperty
def text(self):
return self._page.getText()
#lazyproperty
def _pixs(self):
imgs = self._doc.getPageImageList(self.page_num)
pixs =[]
for img in imgs:
xref = img[0]
pix = fitz.Pixmap(self._doc, xref)
pixs.append(pix)
return pixs
#lazyproperty
def _pixpage(self):
pix = self._page.getPixmap(colorspace=fitz.csGRAY)
return pix
#property
def img(self):
return self.imgs[0]
#property
def pageimg(self):
pix = self._pixpage
return self.pix2np(pix)
#lazyproperty
def imgs(self):
pixs = self._pixs
imgsarray = []
for pix in pixs:
img = self.pix2np(pix)
imgsarray.append(img)
return imgsarray
def find_first_valid_image(self):
img_valid = None
for i,img in enumerate(self.imgs):
try:
import ipdb;ipdb.set_trace()
res = pytesseract.image_to_osd(img)
img_valid = img
return img_valid
except pytesseract.TesseractError:
continue
if img_valid==None:
logger.warning('No readable image in page {} of the document {}'.format(self.page_num, self.filename))
raise InvalidImage('No readable image in page {} of the document {}'.format(self.page_num, self.filename))
def write(self,outputdir,fullpage=False):
try:
os.makedirs(outputdir)
logger.info("Directory {} is created".format(outputdir))
except FileExistsError:
pass
def _writepix(pix,filepath):
# This is GRAY or RGB
try:
pix.writePNG(filepath)
# CMYK: convert to RGB first
except:
pix = fitz.Pixmap(fitz.csRGB, pix)
pix.writePNG(filepath)
pix = None
if fullpage:
filepath = os.path.join(outputdir,'{}_p{}.png'.format(self.filename,self.page_num))
pix = self._pixpage
_writepix(pix,filepath)
return
pixs = self._pixs
for i,pix in enumerate(pixs):
filepath = os.path.join(outputdir,'{}_p{}_i{}.png'.format(self.filename,self.page_num,i))
_writepix(pix,filepath)
return
def pix2np(self,pix):
"""
Convert pixmap to image np.ndarray
https://stackoverflow.com/questions/53059007/python-opencv
param pix: pixmap
"""
import numpy as np
#https://stackoverflow.com/questions/22236749/numpy-what-is-the-difference-between-frombuffer-and-fromstring
im = np.frombuffer(pix.samples, dtype=np.uint8).reshape(pix.h, pix.w, pix.n)
try:
im = np.ascontiguousarray(im[..., [2, 1, 0]]) # rgb to bgr
except IndexError:
#Trick to convert Gray rto BGR, (im.reshape)
#logger.warning("Need to convert Gray to BGR [filepath: {}]".format(self.filepath))
im = cv2.cvtColor(im,cv2.COLOR_GRAY2RGB)
im = np.ascontiguousarray(im[..., [2, 1, 0]])
return im
if __name__ == "__main__":
filepath = r'data\inputs\test\impot_textpdf_with_one_logoimage.pdf'
###### Parse page 0 (first page) ######
pdf = PDFParser(filepath,0)
text = pdf.text
imgs = pdf.imgs
show(pdf.imgs[0])
show(pdf.imgs[1])
############### other functions ####################
class lazyproperty:
def __init__(self, func):
self.func = func
def __get__(self, instance, cls):
if instance is None:
return self
else:
value = self.func(instance)
setattr(instance, self.func.__name__, value)
return value
def show(image):
import matplotlib.pyplot as plt
fig,ax = plt.subplots(1)
ax.imshow(image)
plt.show()
My solution is not so good (but waiting for better ideas but here are my 2 cents idea: i first write the full page and read it with opencv (i changed the method first_valid_image as you can see attached).
from tmpfile import TemporaryDirectory
def find_first_valid_image(self):
#import ipdb;ipdb.set_trace()
img_valid = None
for i,img in enumerate(self.imgs):
try:
#import ipdb;ipdb.set_trace()
res = pytesseract.image_to_osd(img)
img_valid = img
return img_valid
except pytesseract.TesseractError:
continue
if img_valid==None:
logger.warning('No readable image in page {} of the document {}. Tried the fullpage.'.format(self.page_num, self.filename))
with TemporaryDirectory() as tmpdirname:
filepath = self.write(tmpdirname,fullpage=True)
img_fullpage =cv2.imread(filepath)
return img_fullpage
I think it degrade the quality of my original image; so when applying tesseract on the image i got a bad ocr as you can see attached.
"""DIRECTION GÉNÉRALE DE6 FNANCES PUBLIQUES\n\nAVIS D'IMPÔT 2017\nIMPÔT SUR LES REVENUS\nd Fannée 2016\n\n \n\nPour vos _ démarches,\npas besoin doiginal —\nMc d furir un —\nphotocopie, vérifiable sur —\nTmpots gouv vn\n\nVotre situation\n\n \n\nVos rétérences.\n\nPour accéder à votre espace partculior MONTANT À PAYER\nNuméro fiscal | | A us ario 15/00/2017 (41)\n\nN* daccès en ligne voirvouo déciaration | | Détail du montant à payer\nRevenu fiscal d référence Montart de vtr impôt su e revors\n# | Rétéronce de 'avis <VRRRRS | Versemens sur 1er acompte\nVersomontssur 26 acompto\n\nNuméro F —\n\nNuméro de rôle 016 A\nDate c'étaissement 2m0762017|\nDate de mise en recouvrement 3vo7æ2017|\n\n \n\n \n\n \n\n3899,00 €\n3893006\n\n \n\n \n\nLa somme que vous davez payer est supérieure à 2 000 €\nLa loirend obligatoie le paiement de cette somme par un des moyens suivants, à votre choix :\n\nur impots.gouv.fr: payez en igne ou adhérez au prélèvement à léchéance en vous connectant à vore\nspaco pariclor, pislissoz-vous guider\n\npartéléphone, courrir où couriel pour adhérer au prélèvement à échéanco (aux coordonnéesindiquées\ndansle cadre - Vos démarches »\n\nPour 2018,vous pourrez achérerau prélèvement mensue\n\x0c"""
I have a macro that make changes to all files in a folder in notepad++ but I am trying to automatizate the process with a python code but I do not find any solution for this. Maybe I can use the shortcuts.xml in some way but I am totally lost.
This is my final solution if someone needs it, you MUST set your macro to a shortcut, in this case 'ctr+alt+9' (comments are on spanish):
#
# Es necesario instalar las bibliotecas
# pip install pyautogui
# pip install pywin32
#
# ----------------------------------------------------------------
# Version: 1.3
# Date: 10/07/2019
#
# Resumen: Pone Notepad++ como pantalla activa y pulsa ctrl+alt+9
# y acepta los pop ups de la macro
#
# Autor: Pedro Antonio Fondevila Franco
# ----------------------------------------------------------------
import pyautogui
import win32gui
import win32con
import time
# Copiado de Stackoverflow, retorna lista de las ventanas activas
# stackoverflow.com/questions/16770909/python-win32gui-setforegroundwindow
def window_enum_handler(hwnd, resultList):
if win32gui.IsWindowVisible(hwnd) and win32gui.GetWindowText(hwnd) != '':
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
def get_app_list(handles=[]):
mlst=[]
win32gui.EnumWindows(window_enum_handler, handles)
for handle in handles:
mlst.append(handle)
return mlst
appwindows = get_app_list()
# ----------------------------------------------------------------
targetWindow = 0;
for window in appwindows:# Buscamos si notepad++ esta en la lista
if ("Notepad++" in window[1]):
targetWindow = window[0]
if(targetWindow != 0):
win32gui.ShowWindow(targetWindow,win32con.SW_RESTORE) # Por si esta minimizada la pantalla
win32gui.BringWindowToTop(targetWindow) # La ponemos delante
win32gui.SetForegroundWindow(targetWindow) # La ponemos como ventana principal
time.sleep(0.4) # Le damos tiempo a cambiar al notepad++
pyautogui.hotkey('ctrl', 'alt', '9') # Pulsa ctrl+alt+9
numberOfPopUps = 10
for i in range(1, numberOfPopUps + 1):
time.sleep(0.4)
pyautogui.press('left') # Pulsa izquierda
pyautogui.press('enter') # Pulsa enter
else:
print("Inicie Notepad++")
time.sleep(1.5)
You may want to use VBScript or PowerShell and Python in combination for your needs.
At Notepad++ Community is a thread How to run a notepad++ macro from vbscript or PowerShell.
Quoted from Yaron's answer:
objShell.run …
' Let NPP load and open the file. Adjust the number of milliseconds to your machine.
WScript.Sleep(100)
' "^+9" is Ctrl+Shift+9. Replace it with your preferred shortcut.
objShell.SendKeys("^+9")
Assign a shortcut to your macro via Settings -> Shortcut Mapper ->
Macros.
Add the above lines to the script.
You'll find a possible solution for the second part Executing a vbs file with arguments created by python on SO here.
I've run into a snag porting a program example from "Rapid GUI Programming with Python and Qt" from PyQt4 to PyQt5. The sample program demonstrates an MDI application from which multiple text edit windows can be run within the main window.
I used python 3.4.4 and PyQt 4.8.7 for the PyQt4 version. I used python 3.4.4 and PyQt 5.5.1 for the PyQt5 version.
I started by changing all old-style signal definitions to new style signals in the original PyQt4 program. New style signals were implemented in PyQt 4.5 so I was able to run the original program with these changes. The application ran successfully after updating all old-style signals to new-style signals.
The original program uses the PyQt4.QtGui.QWidget.QWorkspace class to implement the MDI workspace. QWorkspace was replaced by the PyQt5.QtWidgets.QMdiArea class in PyQt4.3. My problem surfaced in trying to modify the original code to work with QMdiArea.
Each text document is presented and edited using an instance of a custom TextEdit widget, a subclass of QTextEdit.
Minimal PyQt5 version of MDI application -- texteditor.py
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class TextEdit(QTextEdit):
NextId = 1
def __init__(self, filename="", parent=None):
print("TextEdit __init__")
super(TextEdit, self).__init__(parent)
self.setAttribute(Qt.WA_DeleteOnClose)
self.filename = filename
if not self.filename:
self.filename = "Unnamed-{}.txt".format(TextEdit.NextId)
TextEdit.NextId += 1
self.document().setModified(False)
self.setWindowTitle(QFileInfo(self.filename).fileName())
def load(self):
print("load - TextEdit")
exception = None
fh = None
try:
fh = QFile(self.filename)
if not fh.open(QIODevice.ReadOnly):
raise IOError(fh.errorString())
stream = QTextStream(fh)
stream.setCodec("UTF-8")
self.setPlainText(stream.readAll())
self.document().setModified(False)
except EnvironmentError as e:
exception = e
finally:
if fh is not None:
fh.close()
if exception is not None:
raise exception
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
__version__ = "1.0.0"
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.mdi = QMdiArea()
self.setCentralWidget(self.mdi)
fileOpenAction = QAction("&Open...", self)
fileOpenAction.setShortcut(QKeySequence.Open)
fileOpenAction.triggered.connect(self.fileOpen)
fileMenu = self.menuBar().addMenu("&File")
fileMenu.addAction(fileOpenAction)
settings = QSettings()
self.restoreGeometry(settings.value("MainWindow/Geometry",
QByteArray()))
self.restoreState(settings.value("MainWindow/State",
QByteArray()))
QTimer.singleShot(0, self.loadFiles)
def loadFiles(self):
if len(sys.argv) > 1:
for filename in sys.argv[1:31]: # Load at most 30 files
if QFileInfo(filename).isFile():
self.loadFile(filename)
QApplication.processEvents()
else:
settings = QSettings()
files = settings.value("CurrentFiles") or []
for filename in files:
if QFile.exists(filename):
self.loadFile(filename)
QApplication.processEvents() #todo What does this do?
def fileOpen(self):
filename, _ = QFileDialog.getOpenFileName(self,
"Text Editor -- Open File")
if filename:
for textEdit in self.mdi.subWindowList():
print(type(textEdit))
if textEdit.filename == filename:
self.mdi.setActiveSubWindow(textEdit)
break
else:
self.loadFile(filename)
def loadFile(self, filename):
textEdit = TextEdit(filename)
try:
textEdit.load()
except EnvironmentError as e:
QMessageBox.warning(self, "Text Editor -- Load Error",
"Failed to load {}: {}".format(filename, e))
textEdit.close()
del textEdit
else:
self.mdi.addSubWindow(textEdit)
textEdit.show()
app = QApplication(sys.argv)
app.setWindowIcon(QIcon(":/icon.png"))
app.setOrganizationName("Qtrac Ltd.")
app.setOrganizationDomain("qtrac.eu")
app.setApplicationName("Text Editor")
form = MainWindow()
form.show()
app.exec_()
The problem occurs in the fileOpen() method:
PyQt4 fileOpen() method
def fileOpen(self):
filename = QFileDialog.getOpenFileName(self,
"Text Editor -- Open File")
if filename:
for textEdit in self.mdi.windowList():
if textEdit.filename == filename:
self.mdi.setActiveWindow(textEdit)
break
else:
self.loadFile(filename)
PyQt5 fileOpen() method
def fileOpen(self):
filename, _ = QFileDialog.getOpenFileName(self,
"Text Editor -- Open File")
if filename:
for textEdit in self.mdi.subWindowList():
if textEdit.filename == filename:
self.mdi.setActiveSubWindow(textEdit)
break
else:
self.loadFile(filename)
windowList() is implemented in PyQt5 as subWindowList(). The problem is that in the PyQt4 version, when for textEdit in self.mdi.windowList(): is executed textEdit is of type TextEdit so the next line
if textEdit.filename == filename
works since TextEdit does have a filename parameter. and textEdit is a {TextEdit}textedit.TextEdit object, but in the PyQt5 version, after for textEdit in self.mdi.subWindowList(): is executed, the type of textEdit is QMdiSubWindow so, of course the traceback generates:
Traceback (most recent call last):
File "texteditor3.py", line 292, in fileOpen
if textEdit.filename == filename:
AttributeError: 'QMdiSubWindow' object has no attribute 'filename'
What really baffles me is how textEdit in the PyQt4 version becomes a TextEdit type. I would think it would be a str type.
I'am from Germany I found an answer. See the Code.
Sorry about the German Comments.
def fileOpen(self):
try:
# PSc QFileDialog.getOpenFileName gibt ein Tuple zurück
# für die weitere Verwendung filname[0] verwenden
filename = QFileDialog.getOpenFileName(self,
"Text Editor -- Open File")
if filename:
try:
# PSc wenn ein zweites Open durchgeführt wird erhält man die Fehlermeldung
# textEdit has no attribute fileName
# http://stackoverflow.com/questions/37800036/porting-pyqt4-qworkspace-to-pyqt5-qmdiarea-subwindowlist-method
# Lösung scheinbar hier gefunden Zeile 268 269
# http://nullege.com/codes/show/src%40p%40y%40pyqt5-HEAD%40examples%40mainwindows%40mdi%40mdi.py/168/PyQt5.QtWidgets.QMdiArea.subWindowActivated.connect/python
# Folgende Zeile dementsprechen geändert
# for textEdit in self.mdi.subWindowList():
for windows in self.mdi.subWindowList():
textEdit = windows.widget()
print('In File Open textEdit.filename: ' + textEdit.filename)
if textEdit.filename == filename[0]:
self.mdi.setActiveWindow(textEdit)
break
else:
# PSc filename Tuple daher filename[0] übergeben
self.loadFile(filename[0])
except:
e = sys.exc_info()
print('An exception occurred in def fileOpen if Filename : \n' + str(e) + '\n')
except:
e = sys.exc_info()
print('An exception occurred in def fileOpenin: \n' + str(e) + '\n')
I Have changed it everywher like:
def fileSave(self):
try:
# PSc PyQt4 Syntax
# textEdit = self.mdi.activeSubWindow()
# geändert laut Zeile 268,269
# nullege.com/codes/show/src%40p%40y%40pyqt5-HEAD%40examples%40mainwindows%40mdi%40mdi.py/168/PyQt5.QtWidgets.QMdiArea.subWindowActivated.connect/python
window = self.mdi.activeSubWindow()
textEdit = window.widget()
if textEdit is None or not isinstance(textEdit, QTextEdit):
return True
try:
textEdit.save()
return True
except EnvironmentError as e:
QMessageBox.warning(self, "Text Editor -- Save Error",
"Failed to save {}: {}".format(textEdit.filename, e))
return False
except Exception as error:
print('An exception occurred in fileSave: {}'.format(error))
I have a new question, I am designing an application and I don not know how to export items to an excel file, in the code I use a QTreeView with a QSortFilteredProxyModel, and I include a filter for text written in a QLineEdit, I need to export all of the items than can be seen after being filtered. But, how can i take or add those items to a list, for exporting them to an excel file?
if anyone can help me I would be so grateful.
sry my bad english
# -*- coding: utf-8 -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from dbFacades.profitFacade import ProfitFacade
class MainTabProfits(QGridLayout):
def __init__(self):
super(MainTabProfits, self).__init__()
self.screen = QDesktopWidget().screenGeometry()
self.profitList = ProfitFacade().allProfit()
self.addWidget(self.topGroupBox(),0,0,1,4)
self.addWidget(self.rightGroupBox(), 1, 0,1,4)
self.addWidget(self.botGroupBox(),2,3)
# -----------------------------------------------------
# Se define el grupo superior
def topGroupBox(self):
groupBox = QGroupBox()
groupBox.setTitle("Busqueda")
self.lineEdit1 = QLineEdit()
self.lineEdit1.setPlaceholderText("Ingrese una fecha con el siguiente formato -> dd/mm/aaaa")
self.lineEdit1.textChanged.connect(self.filterRegExpChanged)
self.lineEdit1.textChanged.connect(self.setSlashChanged)
self.lineEdit1.setMaxLength(10)
leftLayout = QHBoxLayout()
leftLayout.addWidget(self.lineEdit1)
leftLayout.stretch(1)
leftGroupBox = QGroupBox("Fecha Exacta")
leftGroupBox.setLayout(leftLayout)
hBoxLayout = QHBoxLayout()
hBoxLayout.addWidget(leftGroupBox)
groupBox.setLayout(hBoxLayout)
return groupBox
# -----------------------------------------------------
# Se define el grupo principal(izquierdo) de contenido
def rightGroupBox(self):
groupBox = QGroupBox()
groupBox.setTitle("Control de Finanzas y busqueda")
self.setHeaders()
self.proxyModel = QSortFilterProxyModel()
self.proxyModel.setDynamicSortFilter(True)
self.proxyModel.setSourceModel(self.model)
self.proxyModel.setFilterKeyColumn(0)
self.treeView = QTreeView()
self.treeView.setRootIsDecorated(False)
self.treeView.setAlternatingRowColors(True)
self.treeView.setModel(self.proxyModel)
self.treeView.setSortingEnabled(True)
self.treeView.sortByColumn(0, Qt.AscendingOrder)
self.treeView.resizeColumnToContents(0)
self.treeView.setEditTriggers(QTreeView.NoEditTriggers)
vBoxLayout = QVBoxLayout()
vBoxLayout.addWidget(self.treeView)
groupBox.setLayout(vBoxLayout)
self.setProfit()
return groupBox
def botGroupBox(self):
groupBox = QGroupBox()
groupBox.setTitle("Exportacion")
self.radioButton0 = QRadioButton("*.PDF")
self.radioButton0.setIcon(QIcon("icons/pdf.png"))
self.radioButton0.setIconSize(QSize(self.screen.width() / 50, self.screen.width() / 50))
self.radioButton1 = QRadioButton("*.Excel")
self.radioButton1.setIcon(QIcon("icons/excel.png"))
self.radioButton1.setIconSize(QSize(self.screen.width() / 50, self.screen.width() / 50))
pushButton = QPushButton(QIcon("icons/export.png"),"Exportar")
pushButton.released.connect(self.exportList)
pushButton.setIconSize(QSize(self.screen.width() / 50, self.screen.width() / 50))
hBoxLayout = QHBoxLayout()
hBoxLayout.addWidget(self.radioButton0)
hBoxLayout.addWidget(self.radioButton1)
hBoxLayout.addWidget(pushButton)
hBoxLayout.stretch(1)
groupBox.setLayout(hBoxLayout)
return groupBox
# ------------------------------------------------------
# ZONA METODOS
# Metodo que define las cabezeras del menu y crea el model
def setHeaders(self):
self.model = QStandardItemModel(0, 5, self)
self.model.setHeaderData(0, Qt.Horizontal, "Fecha")
self.model.setHeaderData(1, Qt.Horizontal, "Monto")
self.model.setHeaderData(2, Qt.Horizontal, "Mesa")
self.model.setHeaderData(3, Qt.Horizontal, "Tipo de Pago")
self.model.setHeaderData(4, Qt.Horizontal, "Codigo Operacion")
# Metodo para filtrar por texto escrito en line edit
def filterRegExpChanged(self):
regExp = QRegExp(self.lineEdit1.text(), Qt.CaseInsensitive)
self.proxyModel.setFilterRegExp(regExp)
# Metodo para el formato de fecha en LineEdit1
def setSlashChanged(self):
text = self.lineEdit1.text()
if(len(text)==2):
self.lineEdit1.setText(text+"-")
elif(len(text)==5):
self.lineEdit1.setText(text+"-")
self.lineEdit2.setText("")
self.lineEdit3.setText("")
# Metodo para el formato de fecha en lineEdit2 y 2
def inRangeChanged(self):
text = self.lineEdit2.text()
text1 = self.lineEdit3.text()
if(len(text)==2):
self.lineEdit2.setText(text+"-")
elif(len(text)==5):
self.lineEdit2.setText(text+"-")
elif(len(text1)==2):
self.lineEdit3.setText(text1+"-")
elif(len(text1)==5):
self.lineEdit3.setText(text1+"-")
self.lineEdit1.setText("")
# --------------------------------------------------------------------------------
# Metodo que establece los datos en la lista
def setProfit(self):
for profit in self.profitList:
self.model.insertRow(0)
self.model.setData(self.model.index(0,0), profit[0])
self.model.setData(self.model.index(0,1), profit[1])
self.model.setData(self.model.index(0,2), profit[2])
self.model.setData(self.model.index(0,3), profit[3])
self.model.setData(self.model.index(0,4), profit[4])
# --------------------------------------------------------------------------------
# Metodo para exportar a excel
def exportList(self):
if self.radioButton0.isChecked():
pass
if self.radioButton1.isChecked():
HERE I WANT EXPORT ITEMS FILTERED
HERE I WANT EXPORT ITEMS FILTERED
HERE I WANT EXPORT ITEMS FILTERED
HERE I WANT EXPORT ITEMS FILTERED
You can use the fallowing functions from QSortFilterProxyModel:
rowCount(), columnCount() and index(), and then use the returned index to get the data:
rowCnt = self.proxyModel.rowCount()
colCnt = self.proxyModel.columnCount()
for row in range(0, rowCnt):
for col in range(0, colCnt):
modelIndex = self.proxyModel.index(row, col)
print modelIndex.data().toString()