Mostrando entradas con la etiqueta Python 3. Mostrar todas las entradas
Mostrando entradas con la etiqueta Python 3. Mostrar todas las entradas

miércoles, 12 de agosto de 2015

Tutorial de Python 3 y PyQt5 (Interfaz Gráfica de Usuarios)


Completo tutorial de Python 3 y PyQt5 para el desarrollo de aplicaciones con interfaz gráfica de usuarios.

martes, 28 de julio de 2015

24 - Python PyQt (Interfaz gráfica) - SMTP Email (Hotmail, Gmail, Yahoo, ...)




En este capítulo del tutorial de Python 3 más PyQt5 crearemos una sencilla aplicación que nos permita enviar correos con servidores que dispongan del protocolo SMTP (protocolo para transferencia simple de correo) tales como Hotmail, Gmail, Yahoo, ...

El programa permitirá al usuario configurar sus preferencias tales como el servidor STMP, puerto, credenciales, uso opcional del protocolo starttls, adjuntar multiples archivos (QFileDialog), agregar múltiples destinatarios, etc.

El programa mostrará en una etiqueta QLabel el proceso de envío del correo así como cualquier excepción que pudiera ocurrir.

Configuración para servidores SMTP clásicos:

Hotmail:
– SMTP - smtp.live.com (TLS enabled, port 25)


GMail by Google:
– SMTP - smtp.gmail.com (TLS enabled, port 587)


Yahoo! Mail:
– SMTP - smtp.mail.yahoo.com (port 25)


Lycos Mail:
– SMTP - smtp.mail.lycos.com (port 25)


AOL Mail Settings:
– SMTP - smtp.aol.com (port 25)




smtp.pyw


import sys, smtplib, email
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from PyQt5.QtWidgets import QApplication, QDialog, QGridLayout, QMessageBox, QPushButton, QLabel, QLineEdit, QFileDialog, QTextEdit, QCheckBox


class FileDialog(QFileDialog):
 def __init__(self):
  QFileDialog.__init__(self)
  self.setFileMode(QFileDialog.ExistingFiles) #Selección múltiple
  self.files = [] #Lista que guardará las rutas de los archivos
  self.filesSelected.connect(self.getFiles) #Activar la señal al seleccionar archivos
 def getFiles(self):
  self.files = self.selectedFiles() #Agregar las rutas de archivos a la lista

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  self.setWindowTitle("Enviar email SMTP")
  self.resize(450, 600)
  #Crear objetos en el cuadro de diálogo
  self.layout = QGridLayout()
  self.setLayout(self.layout)
  self.label_host = QLabel("Host SMTP:")
  self.txt_host = QLineEdit()
  self.chk_tls = QCheckBox("STARTTLS")
  self.chk_tls.setChecked(True)
  self.label_port = QLabel("Puerto:")
  self.txt_port = QLineEdit()
  self.label_user = QLabel("Usuario:")
  self.txt_user = QLineEdit()
  self.label_pass = QLabel("Password:")
  self.txt_pass = QLineEdit()
  self.txt_pass.setEchoMode(QLineEdit.Password)
  self.label_from = QLabel("Remitente:")
  self.txt_from = QLineEdit()
  self.label_to = QLabel("Destinatario/s:")
  self.txt_to = QLineEdit()
  self.label_subject = QLabel("Asunto:")
  self.txt_subject = QLineEdit()
  self.label_dialog = QLabel("Adjunto:")
  self.btn_dialog = QPushButton("Seleccionar ...")
  self.label_body = QLabel("Mensaje: ")
  self.txt_body = QTextEdit()
  self.label_state = QLabel("")
  self.btn_send = QPushButton("Enviar")
  #Crear objeto de la clase FileDialog()
  self.fileDialog = FileDialog()
  
  #Agregar objetos al layout grid
  self.layout.addWidget(self.label_host)
  self.layout.addWidget(self.txt_host)
  self.layout.addWidget(self.chk_tls)
  self.layout.addWidget(self.label_port)
  self.layout.addWidget(self.txt_port)
  self.layout.addWidget(self.label_user)
  self.layout.addWidget(self.txt_user)
  self.layout.addWidget(self.label_pass)
  self.layout.addWidget(self.txt_pass)
  self.layout.addWidget(self.label_from)
  self.layout.addWidget(self.txt_from)
  self.layout.addWidget(self.label_to)
  self.layout.addWidget(self.txt_to)
  self.layout.addWidget(self.label_subject)
  self.layout.addWidget(self.txt_subject)
  self.layout.addWidget(self.label_dialog)
  self.layout.addWidget(self.btn_dialog)
  self.layout.addWidget(self.label_body)
  self.layout.addWidget(self.txt_body)
  self.layout.addWidget(self.label_state)
  self.layout.addWidget(self.btn_send)
  
  #Señal para abrir el cuadro de diálogo de selección de archivos
  self.btn_dialog.clicked.connect(self.openDialog)
  #Señal para proceder a enviar los correos
  self.btn_send.clicked.connect(self.sendMail)
  
 #Método encargado de abrir el cuadro de diálogo de selección de archivos
 def openDialog(self):
  self.fileDialog.open()
  
 #Método encargado de procesar el envío de correos
 def sendMail(self):
  To = self.txt_to.text().split(", ") #Lista con los destinatarios
  #Capturar cualquier posible excepción y mostrarlo en el label label_state
  try:
   #Recorrer los destinatarios
   for destinatario in To:
    #Guardar los valores del formulario
    Host = self.txt_host.text()
    Port = self.txt_port.text()
    User = self.txt_user.text()
    Pass = self.txt_pass.text()
    From = self.txt_from.text()
    Subject = self.txt_subject.text()
    Body = self.txt_body.toPlainText()
    
    #Objeto para procesar el envío de correo
    smtp = smtplib.SMTP(Host, Port)
    #Si el servidor utilizar el protocolo starttls activarlo
    if self.chk_tls.isChecked(): smtp.starttls()
    #Credenciales
    smtp.login(User, Pass)
    #Iniciar la cabecera
    header = MIMEMultipart()
    header['From'] = From
    header['To'] = destinatario
    header["Subject"] = Subject
    msg = MIMEText(Body, 'html') #Content-type:text/html
    header.attach(msg)
    #Lista para agregrar en la cabecera los archivos a enviar
    parts = []
    for file in self.fileDialog.files:
     parts.append(MIMEBase('application', "octet-stream"))
    
    #Nos permite indexar la ruta de los archivos que se encuentran
    #en el atributo files del objeto fileDialog
    x = 0
    #Recorrer archivo a archivo
    for part in parts:
     #Lectura del archivo
     part.set_payload(open(self.fileDialog.files[x], "rb").read())
     #Codificar el archivo en base64
     encoders.encode_base64(part)
     #Extracción del nombre del archivo
     explode = self.fileDialog.files[x].split("/")
     filename = explode[len(explode)-1]
     #Añadir archivo a la cabecera
     part.add_header('Content-Disposition', 'attachment; filename="'+filename+'"')
     header.attach(part)
     x = x + 1
    #Enviar el correo al destinatario
    smtp.sendmail(From, destinatario, header.as_string())
    #Terminar la conexión con el servidor
    smtp.quit()
    #Mostrar el proceso
    self.label_state.setText("Enviando a " + destinatario + " ... ")
    
  except Exception as e: 
   #Capturar cualquier posible excepción y mostrarla
   self.label_state.setText(str(e))
    
  parts[:] = [] #Limpiar la lista de archivos
  self.fileDialog.files[:] = [] #Limpiar la lista de archivos
  self.label_state.setText("Envío finalizado")
     
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()


sábado, 25 de julio de 2015

23 - Python PyQt (Interfaz gráfica) - QtSql MySQL y QTableWidget (Eliminar registros)




En este capítulo del tutorial de Python 3 más PyQt5 seguimos con la continuación de la aplicación que estamos desarrollando desde los dos capítulos anteriores:


En esta ocasión veremos como eliminar fila/s, para esta tarea incluiremos un botón al cuadro de diálogo que al hacer click sobre él, entrará en acción un método llamado Eliminar el cual contendrá el proceso de búsqueda del index de la/s fila/s seleccionada/s por el usuario y su posterior eliminación tanto del objeto table como de la tabla usuarios.


administrar.pyw


import sys
from PyQt5.QtWidgets import QApplication, QDialog, QGridLayout, QMessageBox, QTableWidget, QTableWidgetItem, QPushButton
from PyQt5 import uic
from PyQt5.QtSql import QSqlDatabase, QSqlQuery

class Dialogo(QDialog):
	def __init__(self):
		QDialog.__init__(self)
		self.setWindowTitle("Administrar usuarios") #Título
		self.resize(800, 600) #Tamaño inicial
		self.layout = QGridLayout() #Crear un layout grid
		self.setLayout(self.layout) #Agregar el layout al cuadro de diálogo
		self.table = QTableWidget() #Crear la tabla
		self.btn_eliminar = QPushButton("Eliminar fila/s")
		self.layout.addWidget(self.btn_eliminar)
		self.layout.addWidget(self.table) #Agregar la tabla al layout
		#Establecer conexión a la base de datos MySql
		self.db = QSqlDatabase.addDatabase('QMYSQL')
		self.db.setHostName("localhost")
		self.db.setDatabaseName("usuarios")
		self.db.setUserName("root")
		self.db.setPassword("password")	
		self.Seleccionar()
		self.table.itemChanged.connect(self.Actualizar)
		self.btn_eliminar.clicked.connect(self.Eliminar)
		
	def Seleccionar(self):
		estado = self.db.open()
		if estado == False:
			QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
		else:
			self.table.setColumnCount(3)
			self.table.setHorizontalHeaderLabels(['id', 'nombre', 'edad'])
			row = 0
			sql = "SELECT * FROM usuarios"
			query = QSqlQuery(sql)
			while query.next():
				self.table.insertRow(row)
				id = QTableWidgetItem(str(query.value(0)))
				nombre = QTableWidgetItem(str(query.value(1)))
				edad = QTableWidgetItem(str(query.value(2)))
				self.table.setItem(row, 0, id)
				self.table.setItem(row, 1, nombre)
				self.table.setItem(row, 2, edad)
				row = row + 1
		self.db.close()
		
	def Actualizar(self):
		estado = self.db.open()
		if estado == False:
			QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
		else:
			column = self.table.currentColumn()
			row = self.table.currentRow()
			id = self.table.item(row, 0).text()
			value = self.table.currentItem().text()
			columns = ['id', 'nombre', 'edad']
			query = QSqlQuery()
			sql = "UPDATE usuarios SET " + columns[column] + "=" + ":value WHERE id=:id"
			query.prepare(sql)
			query.bindValue(":id", id)
			query.bindValue(":value", value)
			estado = query.exec_()
			if estado == False:
				QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
		self.db.close()
	
	def Eliminar(self):
		estado = self.db.open()
		if estado == False:
			QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
		else:
			rows = self.table.selectionModel().selectedRows()
			index = []
			for i in rows:
				index.append(i.row())
			index.sort(reverse=True)
			for i in index:
				id = self.table.item(i, 0).text()
				self.table.removeRow(i)
				sql = "DELETE FROM usuarios WHERE id=:id"
				query = QSqlQuery()
				query.prepare(sql)
				query.bindValue(":id", id)
				estado = query.exec_()
				if estado == False:
					QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
		self.db.close()
					
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()


viernes, 24 de julio de 2015

22 - Python PyQt (Interfaz gráfica) - QtSql MySQL y QTableWidget (Actualizar registros)




En este capítulo del tutorial de Python 3 más PyQt5 continuamos realizando consultas a bases de datos MySQL, basándonos en la aplicación del capítulo anterior: http://jquery-manual.blogspot.com.es/2015/07/21-python-pyqt-interfaz-grafica-qtsql.html donde pudimos ver como seleccionar los registros de la tabla usuarios para incluirlos en un objeto de la clase QTableWidget, este objeto es de gran utilidad para mantener un enlace directo con la base de datos, ya que permite la edición de campos (items). El objetivo es actualizar un determinado campo cada vez que el usuario ha realizado una modificación en el mismo, y para ello haremos uso del evento itemChanged de la clase QTableWidget.



administrar.pyw


import sys
from PyQt5.QtWidgets import QApplication, QDialog, QGridLayout, QMessageBox, QTableWidget, QTableWidgetItem
from PyQt5 import uic
from PyQt5.QtSql import QSqlDatabase, QSqlQuery

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  self.setWindowTitle("Administrar usuarios") #Título
  self.resize(800, 600) #Tamaño inicial
  self.layout = QGridLayout() #Crear un layout grid
  self.setLayout(self.layout) #Agregar el layout al cuadro de diálogo
  self.table = QTableWidget() #Crear la tabla
  self.layout.addWidget(self.table) #Agregar la tabla al layout
  #Establecer conexión a la base de datos MySql
  self.db = QSqlDatabase.addDatabase('QMYSQL')
  self.db.setHostName("localhost")
  self.db.setDatabaseName("usuarios")
  self.db.setUserName("root")
  self.db.setPassword("password") 
  self.Seleccionar()
  self.table.itemChanged.connect(self.Actualizar)
  
 def Seleccionar(self):
  estado = self.db.open()
  if estado == False:
   QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
  else:
   self.table.setColumnCount(3)
   self.table.setHorizontalHeaderLabels(['id', 'nombre', 'edad'])
   row = 0
   sql = "SELECT * FROM usuarios"
   query = QSqlQuery(sql)
   while query.next():
    self.table.insertRow(row)
    id = QTableWidgetItem(str(query.value(0)))
    nombre = QTableWidgetItem(str(query.value(1)))
    edad = QTableWidgetItem(str(query.value(2)))
    self.table.setItem(row, 0, id)
    self.table.setItem(row, 1, nombre)
    self.table.setItem(row, 2, edad)
    row = row + 1
  self.db.close()
  
 def Actualizar(self):
  estado = self.db.open()
  if estado == False:
   QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
  else:
   column = self.table.currentColumn()
   row = self.table.currentRow()
   id = self.table.item(row, 0).text()
   value = self.table.currentItem().text()
   columns = ['id', 'nombre', 'edad']
   query = QSqlQuery()
   sql = "UPDATE usuarios SET " + columns[column] + "=" + ":value WHERE id=:id"
   query.prepare(sql)
   query.bindValue(":id", id)
   query.bindValue(":value", value)
   estado = query.exec_()
   if estado == False:
    QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
  self.db.close()
     
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()


21 - Python PyQt (Interfaz gráfica) - QtSql MySQL y QTableWidget (Seleccionar registros)




En este capítulo del tutorial de Python 3 más PyQt5 continuamos viendo como realizar consultas a bases de datos MySQL, en el capítulo anterior: http://jquery-manual.blogspot.com/2015/07/20-python-pyqt-interfaz-grafica-qtsql.html ya vimos como insertar registros en una tabla llamada 'usuarios' y en esta ocasión empezaremos con la construcción de una aplicación que nos permita administrar dicha tabla, es decir, seleccionar, editar y eliminar. Concretamente en este capítulo veremos como seleccionar todos los registros e incluir fila a fila en un objeto de la clase QTableWidget con sus respectivas columnas, filas y campos.

Más información sobre la clase QTableWidget: http://pyqt.sourceforge.net/Docs/PyQt4/qtablewidget.html 

Más información sobre la clase QTableWidgetItem: http://pyqt.sourceforge.net/Docs/PyQt4/qtablewidgetitem.html



administrar.pyw

import sys
from PyQt5.QtWidgets import QApplication, QDialog, QGridLayout, QMessageBox, QTableWidget, QTableWidgetItem
from PyQt5 import uic
from PyQt5.QtSql import QSqlDatabase, QSqlQuery

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  self.setWindowTitle("Administrar usuarios") #Título
  self.resize(800, 600) #Tamaño inicial
  self.layout = QGridLayout() #Crear un layout grid
  self.setLayout(self.layout) #Agregar el layout al cuadro de diálogo
  self.table = QTableWidget() #Crear la tabla
  self.layout.addWidget(self.table) #Agregar la tabla al layout
  #Establecer conexión a la base de datos MySql
  self.db = QSqlDatabase.addDatabase('QMYSQL')
  self.db.setHostName("localhost")
  self.db.setDatabaseName("usuarios")
  self.db.setUserName("root")
  self.db.setPassword("password") 
  self.Seleccionar()
  
 def Seleccionar(self):
  estado = self.db.open()
  if estado == False:
   QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
  else:
   self.table.setColumnCount(3)
   self.table.setHorizontalHeaderLabels(['id', 'nombre', 'edad'])
   row = 0
   sql = "SELECT * FROM usuarios"
   query = QSqlQuery(sql)
   while query.next():
    self.table.insertRow(row)
    id = QTableWidgetItem(str(query.value(0)))
    nombre = QTableWidgetItem(str(query.value(1)))
    edad = QTableWidgetItem(str(query.value(2)))
    self.table.setItem(row, 0, id)
    self.table.setItem(row, 1, nombre)
    self.table.setItem(row, 2, edad)
    row = row + 1
  self.db.close()
     
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()


domingo, 12 de julio de 2015

20 - Python PyQt (Interfaz gráfica) - QtSql MySQL (Insertar datos)




En este capítulo del tutorial de Python 3 más PyQt5 veremos como conectar a bases de datos MySQL y generaremos una consulta para insertar registros en una determinada tabla, para ello utilizaremos las clases que proporciona el módulo QtSql.

Para el ejemplo crearemos una base de datos Mysql llamada usuarios, a la cuál agregaremos una tabla también llamada usuarios con las siguientes columnas:
  • id : del tipo INT, Autoincrementable y clave primaria
  • nombre : del tipo VARCHAR y con una longitud de 50
  • edad : del tipo INT

Del módulo QtSql utilizaremos las dos siguiente clases:

QSqlDatabase: para establecer la conexión a la base de datos
QSqlQuery: para generar la consulta





insertar.pyw


import sys
from PyQt5.QtWidgets import QApplication, QDialog, QGridLayout, QMessageBox, QLabel, QPushButton, QLineEdit, QSpinBox
from PyQt5 import uic
from PyQt5.QtSql import QSqlDatabase, QSqlQuery

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  self.setWindowTitle("Insertar datos") #Título
  self.resize(300, 300) #Tamaño inicial
  self.setMinimumSize(300, 300) #Tamaño mínimo
  self.setMaximumSize(300, 300) #Tamaño máximo
  self.layout = QGridLayout() #Crear un layout grid
  self.setLayout(self.layout) #Agregar el layout al cuadro de diálogo
  self.label_nombre = QLabel("Nombre:") #Etiqueta nombre
  self.txt_nombre = QLineEdit() #Campo para ingresar el nombre
  self.label_edad = QLabel("Edad:") #Etiqueta edad
  self.txt_edad = QSpinBox() #Campo para ingresar la edad
  #Botones
  self.btn_insertar = QPushButton("Insertar")
  self.btn_cancelar = QPushButton("Cancelar")
  #Agregar elementos al layout divido en dos columnas
  self.layout.addWidget(self.label_nombre, 1, 1)
  self.layout.addWidget(self.txt_nombre, 1, 2)
  self.layout.addWidget(self.label_edad, 2, 1)
  self.layout.addWidget(self.txt_edad, 2, 2)
  self.layoutButton = QGridLayout() #Layout para agrupar los botones
  #Agregar los botones al layoutButton
  self.layoutButton.addWidget(self.btn_insertar, 1, 1)
  self.layoutButton.addWidget(self.btn_cancelar, 1, 2)
  #Agregar el layoutButton en la fila 3 columna 2
  self.layout.addLayout(self.layoutButton, 3, 2)
  
  #Establecer conexión a la base de datos MySql
  self.db = QSqlDatabase.addDatabase('QMYSQL')
  self.db.setHostName("localhost")
  self.db.setDatabaseName("usuarios")
  self.db.setUserName("root")
  self.db.setPassword("password")
  
  self.btn_insertar.clicked.connect(self.Insertar)
  self.btn_cancelar.clicked.connect(self.Cancelar)
  
 def Insertar(self):
  estado = self.db.open()
  if estado == False:
   QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
  else:
   nombre = self.txt_nombre.text()
   edad = self.txt_edad.text()
   sql = "INSERT INTO usuarios(nombre, edad) VALUES (:nombre, :edad)"
   consulta = QSqlQuery()
   consulta.prepare(sql)
   consulta.bindValue(":nombre", nombre)
   consulta.bindValue(":edad", edad)
   estado = consulta.exec_()
   if estado == True:
    QMessageBox.information(self, "Correcto", "Datos guardados", QMessageBox.Discard)
   else:
    QMessageBox.warning(self, "Error", self.db.lastError().text(), QMessageBox.Discard)
    
   self.db.close()
 
 def Cancelar(self):
  self.close()
     
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()


sábado, 11 de julio de 2015

19 - Python PyQt (Interfaz gráfica) - menuBar (Barra de Herramientas)




En este capítulo del tutorial de Python 3 más PyQt5 verermos como crear barras de herramientas personalizadas para nuestros programas. La Barra de Herramientas se puede estructurar en Menús, Submenús y elementos que pueden llevar asociada una determinada acción, como por ejemplo en un editor común encontramos acciones como abrir un archivo, cerrar un archivo, guarda un archivo, ...

La clase QMainWindow nos permite crear un objeto de la clase QMenuBar() a través del método menuBar().

Para agregar menús o submenús según el caso utilizaremos el método addMenu(QString title) de la clase QMenuBar.

Para agregar acciones a los menús o submenús, crearemos objetos de la clase QAction(QIcon icon, QString text, QObject parent) y posteriormente los agregaremos a través del método addAction(QAction action).

Clase QMenuBar: http://pyqt.sourceforge.net/Docs/PyQt4/qmenubar.html
Clase QAction: http://pyqt.sourceforge.net/Docs/PyQt4/qaction.html




menubar.pyw

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, QMessageBox
from PyQt5 import uic
from PyQt5.QtGui import QIcon

class Window(QMainWindow):
 def __init__(self):
  QMainWindow.__init__(self)
  self.resize(800, 500) #Tamaño inicial de la ventana 800x500
  #Barra de estado
  self.statusBar().showMessage("Bienvenid@")
  #Objeto menuBar
  menu = self.menuBar()
  #Menú padre
  menu_archivo = menu.addMenu("&Archivo")
  #Menú padre
  menu_editar = menu.addMenu("&Editar")
  
  #Agregar un elemento acción al menu_archivo
  menu_archivo_abrir = QAction(QIcon(), "&Abrir", self)
  menu_archivo_abrir.setShortcut("Ctrl+o") #Atajo de teclado
  menu_archivo_abrir.setStatusTip("Abrir") #Mensaje en la barra de estado
  menu_archivo_abrir.triggered.connect(self.menuArchivoAbrir) #Lanzador
  menu_archivo.addAction(menu_archivo_abrir)
  
  #Agregar un elemento acción al menu_archivo
  menu_archivo_cerrar = QAction(QIcon(), "&Cerrar", self)
  menu_archivo_cerrar.setShortcut("Ctrl+w") #Atajo de teclado
  menu_archivo_cerrar.setStatusTip("Cerrar") #Mensaje en la barra de estado
  menu_archivo_cerrar.triggered.connect(self.menuArchivoCerrar) #Lanzador
  menu_archivo.addAction(menu_archivo_cerrar)
  
  #Agregar un submenú al menú menu_editar
  menu_editar_opciones = menu_editar.addMenu("&Opciones")
  menu_editar_opciones_buscar = QAction(QIcon(), "&Buscar", self)
  menu_editar_opciones_buscar.setShortcut("Ctrl+f") #Atajo de teclado
  menu_editar_opciones_buscar.setStatusTip("Buscar") #Mensaje en la barra de estado
  menu_editar_opciones_buscar.triggered.connect(self.menuEditarOpcionesBuscar)
  menu_editar_opciones.addAction(menu_editar_opciones_buscar)
  
 def menuArchivoAbrir(self):
  QMessageBox.information(self, "Abrir", "Acción Abrir", QMessageBox.Discard)
  
 def menuArchivoCerrar(self):
  QMessageBox.information(self, "Cerrar", "Acción Cerrar", QMessageBox.Discard)
  
 def menuEditarOpcionesBuscar(self):
  QMessageBox.information(self, "Buscar", "Acción Buscar", QMessageBox.Discard)
  
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec_()



jueves, 9 de julio de 2015

17 - Python PyQt (Interfaz gráfica) - Dibujar objetos (addLine, addRect & addEllipse)




En este capítulo del tutorial Python 3 más PyQt5 veremos como agregar objetos como pueden ser líneas, objetos rectángulares y elipses al plano de dibujo (QGraphicsView), este capítulo básicamente es la continuación del capítulo anterior: http://jquery-manual.blogspot.com/2015/07/16-python-pyqt-interfaz-grafica.html

Para poder agregar dichos objetos a la escena utilizaremos los siguientes métodos de la clase QGraphicsScene:

Agrega una línea: addLine(x1, y1, x2, y2, QPen) 
Agrega un objeto rectángular: addRect(x, y, width, height, QPen, QBrush)
Agrega un objeto elíptico: addEllipse(x, y, width, height, QPen, QBrush)

La posición inicial x e y será obtenida por el evento MousePressEvent, es decir, cuando el usuario haga click en el plano de dibujo.

La posición final x e y será obtenida por el evento MouseReleaseEvent, es decir, cuando el usuario suelte el mouse en el plano de dibujo.


graficos.pyw


import sys
from PyQt5.QtWidgets import QApplication, QDialog, QGraphicsView, QGraphicsScene, QGridLayout, QPushButton, QComboBox
from PyQt5.QtCore import QPointF, QRectF, Qt
from PyQt5.QtGui import QPen, QBrush
from PyQt5 import uic

class Paint(QGraphicsView):
 def __init__(self):
  QGraphicsView.__init__(self)
  self.setSceneRect(QRectF(self.viewport().rect()))
  self.scene = QGraphicsScene()
  self.isPaint = False
  self.isDelete = False
  self.isClear = False
  self.isObject = None
  self.startX = None
  self.startY = None
  
 def tools(self, e):
  if self.isPaint == True:
   pen = QPen(Qt.black)
   brush = QBrush(Qt.SolidPattern)
   self.scene.addItem(self.scene.addEllipse(e.x(), e.y(), 3, 3, pen, brush))
   self.setScene(self.scene)
  if self.isDelete == True:
   items = self.items(e.x(), e.y())
   for item in items:
    self.scene.removeItem(item)
    
 def paintObject(self, e):
  if self.isObject != None:
   object = self.isObject
   if object == 1: #Line
    pen = QPen(Qt.black)
    self.scene.addItem(self.scene.addLine(self.startX, self.startY, e.x(), e.y(), pen))
    self.setScene(self.scene)
   elif object == 2: #Rect
    pen = QPen(Qt.black)
    brush = QBrush(Qt.SolidPattern)
    self.scene.addItem(self.scene.addRect(self.startX, self.startY, e.x()-self.startX, e.y()-self.startY, pen, brush))
    self.setScene(self.scene)
   elif object == 3: #Ellipse
    pen = QPen(Qt.black)
    brush = QBrush(Qt.SolidPattern)
    self.scene.addItem(self.scene.addEllipse(self.startX, self.startY, e.x()-self.startX, e.y()-self.startY, pen, brush))
    self.setScene(self.scene)
    
    
 def mousePressEvent(self, event):
  e = QPointF(self.mapToScene(event.pos()))
  self.tools(e)
  self.startX = e.x()
  self.startY = e.y()
  
 def mouseReleaseEvent(self, event):
  e = QPointF(self.mapToScene(event.pos()))
  self.paintObject(e)
  
 def mouseMoveEvent(self, event):
  e = QPointF(self.mapToScene(event.pos()))
  self.tools(e)

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  self.resize(500, 500)
  self.layout = QGridLayout()
  self.setLayout(self.layout)
  self.paint = Paint()
  self.btn_paint = QPushButton("Dibujar")
  self.combo_object = QComboBox()
  self.combo_object.addItem("Seleccionar")
  self.combo_object.addItem("Line")
  self.combo_object.addItem("Rect")
  self.combo_object.addItem("Ellipse")
  self.btn_delete = QPushButton("Borrar")
  self.btn_clear = QPushButton("Clear")
  self.layout.addWidget(self.btn_paint)
  self.layout.addWidget(self.combo_object)
  self.layout.addWidget(self.btn_delete)
  self.layout.addWidget(self.btn_clear)
  self.layout.addWidget(self.paint)
  self.btnDefault = "background-color: grey; border: 0; padding: 10px"
  self.btnActive = "background-color: orange; border: 0; padding: 10px"
  
  self.btn_paint.setStyleSheet(self.btnDefault)
  self.combo_object.setStyleSheet(self.btnDefault)
  self.btn_delete.setStyleSheet(self.btnDefault)
  self.btn_clear.setStyleSheet(self.btnDefault)
  
  self.btn_paint.clicked.connect(self.isPaint)
  self.combo_object.currentIndexChanged.connect(self.isObject)
  self.btn_delete.clicked.connect(self.isDelete)
  self.btn_clear.clicked.connect(self.isClear)
  
 def resizeEvent(self, event):
  self.paint.setSceneRect(QRectF(self.paint.viewport().rect()))
   
 def isPaint(self):
  if self.paint.isPaint == False:
   self.paint.isPaint = True
   self.btn_paint.setStyleSheet(self.btnActive)
  else:
   self.paint.isPaint = False
   self.btn_paint.setStyleSheet(self.btnDefault)
  
  self.paint.isObject = None  
  self.paint.isDelete = False
  self.paint.isClear = False
  self.btn_delete.setStyleSheet(self.btnDefault)
  self.btn_clear.setStyleSheet(self.btnDefault)
  
 def isObject(self):
  object = self.combo_object.currentIndex()
  self.paint.isObject = object
  self.paint.isPaint = False
  self.paint.isDelete = False
  self.paint.isClear = False
  self.btn_paint.setStyleSheet(self.btnDefault)
  self.btn_delete.setStyleSheet(self.btnDefault)
  self.btn_clear.setStyleSheet(self.btnDefault)
  
   
 def isDelete(self):
  if self.paint.isDelete == False:
   self.paint.isDelete = True
   self.btn_delete.setStyleSheet(self.btnActive)
  else:
   self.paint.isDelete = False
   self.btn_delete.setStyleSheet(self.btnDefault)
   
  self.paint.isObject = None 
  self.paint.isPaint = False
  self.paint.isClear = False
  self.btn_paint.setStyleSheet(self.btnDefault)
  self.btn_clear.setStyleSheet(self.btnDefault)
  
 def isClear(self):
  if self.paint.isClear == False:
   self.paint.isClear = True
   self.btn_clear.setStyleSheet(self.btnActive)
  else:
   self.paint.isClear = False
   self.btn_clear.setStyleSheet(self.btnDefault)
   
  self.paint.isObject = None 
  self.paint.isPaint = False
  self.paint.isDelete = False
  self.btn_paint.setStyleSheet(self.btnDefault)
  self.btn_delete.setStyleSheet(self.btnDefault)
  self.paint.scene.clear()
  
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()


16 - Python PyQt (Interfaz gráfica) - QGraphicsView (Herramientas de dibujo)




En este capítulo del tutorial de Python 3 más PyQt5 veremos un ejemplo sencillo de trabajar con gráficos, concretamente, intentaremos crear un programa que de un modo simple emule la herramienta de dibujo de un programa como puede ser Paint de Windows. Este programa nos permitirá dibujar, borrar y limpiar el dibujo realizado. Para ello nos basaremos principalmente en las clases QGraphicsView (Plano de dibujo) y QGraphicsScene (Escenas o capas).

Referencia a la clase QGraphicsViewhttp://pyqt.sourceforge.net/Docs/PyQt4/qgraphicsview.html

Referencia a la clase QGraphicsScene: http://pyqt.sourceforge.net/Docs/PyQt4/qgraphicsscene.html



graficos.pyw

import sys
from PyQt5.QtWidgets import QApplication, QDialog, QGraphicsView, QGraphicsScene, QGridLayout, QPushButton
from PyQt5.QtCore import QPointF, QRectF, Qt
from PyQt5.QtGui import QPen, QBrush
from PyQt5 import uic

class Paint(QGraphicsView):
 def __init__(self):
  QGraphicsView.__init__(self)
  self.setSceneRect(QRectF(self.viewport().rect()))
  self.scene = QGraphicsScene()
  self.isPaint = False
  self.isDelete = False
  self.isClear = False
  
 def tools(self, e):
  if self.isPaint == True:
   pen = QPen(Qt.black)
   brush = QBrush(Qt.SolidPattern)
   self.scene.addItem(self.scene.addEllipse(e.x(), e.y(), 3, 3, pen, brush))
   self.setScene(self.scene)
  if self.isDelete == True:
   items = self.items(e.x(), e.y())
   for item in items:
    self.scene.removeItem(item)
  
 def mousePressEvent(self, event):
  e = QPointF(self.mapToScene(event.pos()))
  self.tools(e)
  
 def mouseMoveEvent(self, event):
  e = QPointF(self.mapToScene(event.pos()))
  self.tools(e)

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  self.resize(500, 500)
  self.layout = QGridLayout()
  self.setLayout(self.layout)
  self.paint = Paint()
  self.btn_paint = QPushButton("Dibujar")
  self.btn_delete = QPushButton("Borrar")
  self.btn_clear = QPushButton("Clear")
  self.layout.addWidget(self.btn_paint)
  self.layout.addWidget(self.btn_delete)
  self.layout.addWidget(self.btn_clear)
  self.layout.addWidget(self.paint)
  self.btnDefault = "background-color: grey; border: 0; padding: 10px"
  self.btnActive = "background-color: orange; border: 0; padding: 10px"
  
  self.btn_paint.setStyleSheet(self.btnDefault)
  self.btn_delete.setStyleSheet(self.btnDefault)
  self.btn_clear.setStyleSheet(self.btnDefault)
  
  self.btn_paint.clicked.connect(self.isPaint)
  self.btn_delete.clicked.connect(self.isDelete)
  self.btn_clear.clicked.connect(self.isClear)
  
 def resizeEvent(self, event):
  self.paint.setSceneRect(QRectF(self.paint.viewport().rect()))
   
 def isPaint(self):
  if self.paint.isPaint == False:
   self.paint.isPaint = True
   self.btn_paint.setStyleSheet(self.btnActive)
  else:
   self.paint.isPaint = False
   self.btn_paint.setStyleSheet(self.btnDefault)
   
  self.paint.isDelete = False
  self.paint.isClear = False
  self.btn_delete.setStyleSheet(self.btnDefault)
  self.btn_clear.setStyleSheet(self.btnDefault)
   
 def isDelete(self):
  if self.paint.isDelete == False:
   self.paint.isDelete = True
   self.btn_delete.setStyleSheet(self.btnActive)
  else:
   self.paint.isDelete = False
   self.btn_delete.setStyleSheet(self.btnDefault)
   
  self.paint.isPaint = False
  self.paint.isClear = False
  self.btn_paint.setStyleSheet(self.btnDefault)
  self.btn_clear.setStyleSheet(self.btnDefault)
  
 def isClear(self):
  if self.paint.isClear == False:
   self.paint.isClear = True
   self.btn_clear.setStyleSheet(self.btnActive)
  else:
   self.paint.isClear = False
   self.btn_clear.setStyleSheet(self.btnDefault)
   
  self.paint.isPaint = False
  self.paint.isDelete = False
  self.btn_paint.setStyleSheet(self.btnDefault)
  self.btn_delete.setStyleSheet(self.btnDefault)
  self.paint.scene.clear()
  
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()



lunes, 6 de julio de 2015

15 - Python PyQt (Interfaz gráfica) - Progress Bar (Descarga de archivos)




En este capítulo del tutorial de Python 3 más PyQt5, veremos como trabajar con objetos Progress Bar (Barra de progreso) que pertenecen a la clase QProgressBar, concretamente veremos como crear una aplicación que nos permita descargar archivos de servidores remotos con la ayuda del módulo QtNetwork que nos proporciona una serie de clases que serán de gran ayuda para ir controlando el proceso de descarga e ir viendo en el Progress Bar el progreso de la misma, todo ello gracias a una serie de señales (eventos) y métodos predefinidos.

Crear un cuadro de diálogo en Designer y agregar los siguiente objetos:

  • Un objeto Label con el texto "Ruta del archivo a descargar:"
  • Un objeto Line Edit con el nombre ruta
  • Un objeto Progress Bar con el nombre progressBar
  • Un objeto Push Button con el texto "Descargar" y el nombre btn_download
  • Un objeto Push Button con el texto "Cancelar" y el nombre btn_cancel
  • Un objeto Label con el nombre labelState y sin texto
  • Guardar el archivo como progressbar.ui


progressbar.ui

import sys
from PyQt5.QtWidgets import QApplication, QDialog
from PyQt5 import uic
from PyQt5.QtCore import QUrl, QFileInfo, QFile, QIODevice
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  uic.loadUi("progressbar.ui", self)
  #Almacena la url del archivo a descargar
  self.url = None
  #Almacena el manejador del fichero a crear
  self.file = None
  #almacena el nombre del archivo a descargar
  self.filename = None
  #almacena en su caso el error en un string
  self.errorString = None
  #almacena en su caso el número de error
  self.errorCode = None
  #Objeto para establecer la conexión y crear el objeto QNetworkReply
  self.http = QNetworkAccessManager(self)
  #Desactivar los botones de descargar y cancelar en un principio
  self.btn_download.setEnabled(False)
  self.btn_cancel.setEnabled(False)
  #Establece si los botones están activos o no
  self.btn_active = False
  
  #Inicia todo el proceso de descarga
  self.btn_download.clicked.connect(self.download)
  #Cancela la descarga
  self.btn_cancel.clicked.connect(self.cancel_download)
  #Detectar el cambio de texto en el campo de texto para activar el botón de descarga
  self.ruta.textChanged.connect(self.btn_enabled)
  
 #Detectar el cambio de texto en el campo de texto para activar el botón de descarga
 def btn_enabled(self):
  if self.ruta.text() != "":
   self.btn_active = True
   self.btn_download.setEnabled(True)
  else:
   self.btn_active = False
 
 #Inicia todo el proceso de descarga
 def download(self):
  if self.btn_active == True:
   #Ruta indicada por el usuario
   ruta = self.ruta.text()
   self.url = QUrl(ruta)
   fileinfo = QFileInfo(self.url.path())
   self.filename = fileinfo.fileName()
   #Manejador del fichero
   self.file = QFile(self.filename)
   #Si no es posible crear el fichero
   if not self.file.open(QIODevice.WriteOnly): 
    self.labelState.setText("No se pudo crear el archivo")
    self.file.close()
   else: #Entonces llamar al método que inicia la descarga del archivo
    self.start_download()

 
 #Inicia el proceso de descarga y controla las diferente señales (eventos) durante la misma
 def start_download(self):
  #Objeto QNetworkReply
  self.reply = self.http.get(QNetworkRequest(self.url))
  self.labelState.setText("Iniciando la descarga ...")
  #Empieza la lectura del archivo remoto y escritura local
  self.reply.readyRead.connect(self.ready_read)
  #Señal predefinida para obtener los bytes en el proceso descarga y asignarlos al progressBar
  self.reply.downloadProgress.connect(self.updateDataReadProgress)
  #Señal para capturar posibles errores durante la descarga
  self.reply.error.connect(self.error_download)
  #Finalización de la descarga
  self.reply.finished.connect(self.finished_download)
 
 #Ocurre durante la escritura del archivo
 def ready_read(self):
  #Escritura del archivo local
  self.file.write(self.reply.readAll())
  self.labelState.setText("Descargando ...")
  #Activación del botón de cancelar
  self.btn_cancel.setEnabled(True)
 
 #Método predefinido en la clase QNetworkReply para leer el progreso de descarga
 def updateDataReadProgress(self, bytesRead, totalBytes):
  self.progressBar.setMaximum(totalBytes)
  self.progressBar.setValue(bytesRead)
 
 #Si ha ocurrido algún error durante el proceso de descarga
 def error_download(self, error):
  #Si ha ocurrido un error, mostrar el error e eliminar el archivo en el método finished_download
  self.errorString = self.reply.errorString()
  self.errorCode = error
 
 #Ocurre cuando la descarga ha finalizado
 def finished_download(self):
  #Si existe un error
  if self.errorCode is not None:
   #Poner a 0 el progressBar
   self.progressBar.setValue(0)
   self.labelState.setText(str(self.errorCode) + ": " + self.errorString)
   #Eliminar el archivo
   self.file.remove()
  else: 
   self.labelState.setText("Descarga completada")
  
  #Cerrar el fichero
  self.file.close()
  #Desactivar el botón de cancelar ya que la descarga ha finalizado
  self.btn_cancel.setEnabled(False)
  #Restaurar a None los valores de los atributos de error
  self.errorString = None
  self.errorCode = None
    
 #Cancelar la descargar durante su ejecución
 def cancel_download(self):
  #Abortar la descarga
  self.reply.abort()
  #Desconectar del servidor
  self.reply.close()
  #Eliminar el fichero
  self.file.remove()
  #Cerrar el fichero
  self.file.close()
 
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()



jueves, 2 de julio de 2015

13 - Python PyQt (Interfaz gráfica) - WebKit (Navegador Web con pestañas de navegación)




En este capítulo del tutorial de Python 3 más PyQt5 veremos lo útil que es trabajar con objetos de la clase QTabWidget, para ello nos basaremos en crear un navegador con las herramientas de WebKit y apoyándonos en un Tab Widget para ir agregando o eliminando páginas de navegación mediante los conocidos tabs o pestañas.


Crearemos un archivo ui con Designer en una ventana principal (Main Window) al cual agregaremos:

  • Un objeto Tab Widget con nombre tabWidget que es el que viene por defecto.
  • Un objeto Push Button con nombre btn_mas y como texto el signo "+" para agregar páginas.
  • Un objeto Push Button con nombre btn_menos y como texto el signo "-" para eliminar páginas.
  • Un objeto Line Edit con nombre url para la navegación.
  • Agrupar los elementos en un Lay Out in a Grid.
  • Guardar el archivo como webkit-tab.ui

webkit-tab.pyw


import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QUrl
from PyQt5.QtWebKitWidgets import QWebView
from PyQt5 import uic

class Navegador(QMainWindow):
 def __init__(self):
  QMainWindow.__init__(self)
  uic.loadUi("webkit-tab.ui", self)
  self.pages = [] #array donde se guardará cada página objeto
  self.index = 0 #Al crear o eliminar tabs incrementar o disminuir el index
  self.add_tab() #Crear un tab por defecto
  
  #Agregar un nuevo tab con página
  self.btn_mas.clicked.connect(self.add_tab)
  #Eliminar un tab
  self.btn_menos.clicked.connect(self.remove_tab)
  #Cargar información de la página activa al terminar de cargarla
  self.pages[self.tabWidget.currentIndex()].loadFinished.connect(self.getInfo)
  #Actualizar información de la página del tab activo
  self.tabWidget.currentChanged.connect(self.current_changed)
  #Buscar la url indicada por el usuario
  self.url.returnPressed.connect(self.search)
 
 
 #Agregar un nuevo tab con página
 def add_tab(self):
  #Comprobar el número de páginas/tabs e incrementar en 1 para el index del nuevo elemento
  if len(self.pages) > 0:
   for i in range(len(self.pages)):
    self.index = i + 1
  #Crear objeto página
  pagina = QWebView()
  #Agregar objeto página al array pages
  self.pages.append(pagina)
  #Cargar la url por defecto
  self.pages[self.index].load(QUrl("http://www.google.com"))
  #Agregar el objeto página a un nuevo tab
  self.tabWidget.addTab(self.pages[self.index], "")
  #Poner el foco en el nuevo tab
  self.tabWidget.setCurrentIndex(self.index)
  
 def getInfo(self):
  #Index del tab que tiene el foco
  index = self.tabWidget.currentIndex()
  #Título de la página web
  title = self.pages[index].title()
  #Agregar título al tab
  self.tabWidget.setTabText(index, title)
  #Agregar título a la ventana
  self.setWindowTitle(title)
  #Actualizar la url del buscador
  url = self.pages[index].url()
  self.url.setText(url.toString())
  
  
 def current_changed(self):
  #Index del tab que tiene el foco
  index = self.tabWidget.currentIndex()
  #Actualizar la información de la página si un tab obtiene el foco
  self.pages[index].loadFinished.connect(self.getInfo)
  #Título de la página web
  title = self.pages[index].title()
  #Agregar título al tab
  self.tabWidget.setTabText(index, title)
  #Agregar título a la ventana
  self.setWindowTitle(title)
  #Actualizar la url del buscador
  url = self.pages[index].url()
  self.url.setText(url.toString())
  
 def search(self):
  #Url del campo de búsqueda
  url = QUrl(self.url.text())
  #Obtener el index del tab que tiene el foco
  index = self.tabWidget.currentIndex()
  #Navegar a la url
  self.pages[index].setUrl(url)
  
 def remove_tab(self):
  #Obtener el index del tab que tiene el foco
  index = self.tabWidget.currentIndex()
  #Eliminar el tab
  self.tabWidget.removeTab(index)
  #Eliminar la página objeto del array pages
  self.pages.pop(index)
  #Actualizar el valor del atributo index
  if self.index > 0: self.index = self.index - 1
  #Actualizar la información del tab que obtendrá el foco tras la eliminación del seleccionado
  self.current_changed()
  
app = QApplication(sys.argv)
navegador = Navegador()
navegador.show()
app.exec_()


miércoles, 1 de julio de 2015

12 - Python PyQt (Interfaz gráfica) - WebKit (Navegador Web)




En este capítulo del tutorial de Python 3 más PyQt5 veremos como crear un simple navegador web creando un objeto de la clase QWebView en el Designer de Qt, esta clase pertenece al módulo QtWebKit.

El navegador además de contener un objeto WebView también contendrá un campo de búsqueda (Line Edit) y un botón (QPush Putton) para retroceder en el historial.

Más información de la clase QWebView: http://pyqt.sourceforge.net/Docs/PyQt4/qwebview.html

  • Crearemos un formulario Main Window con Designer al cual llamaremos webkit.ui
  • Agregaremos un objeto QWebView al cual llamaremos navegador y en la propiedad url desactivar la url que viene por defecto haciendo click en el botón que se encuentra más a la derecha.
  • Agregaremos un objeto Line Edit al cual llamaremos url
  • Agregraremos un objeto Push Button al cual llamaremos btn_back y le incluiremos el texto "back"
  • Agrupar los elementos en un Lay Out in a Grid


webkit.pyw


import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QUrl
from PyQt5 import uic

class Navegador(QMainWindow):
 def __init__(self):
  QMainWindow.__init__(self)
  uic.loadUi("webkit.ui", self)
  #url por defecto
  default_url = "http://google.com"
  #Navegar a la url por defecto
  self.navegador.setUrl(QUrl(default_url))
  #Agregar al buscador la url por defecto
  self.url.setText(default_url)
  #Desactivar botón back hasta que no haya historial
  self.btn_back.setEnabled(False)
  
  #Retroceder a la página anterior
  self.btn_back.clicked.connect(self.navegador.back)
  self.url.returnPressed.connect(self.navegar)
  self.navegador.urlChanged.connect(self.url_changed)
  
 #Navegar a la url indicada en el buscador al pulsar la tecla enter
 def navegar(self):
  url = QUrl(self.url.text())
  self.navegador.setUrl(url)
  
 #Detectar el cambio de url de navegación
 def url_changed(self):
  #Crear un objeto de la página para acceder al historial
  page = self.navegador.page()
  history = page.history()
  #Si hay historial activar el botón back
  if history.canGoBack():
   self.btn_back.setEnabled(True)
  else:
   self.btn_back.setEnabled(False)
  #Agregar el cambio de url al campo de búsqueda
  url = self.navegador.url()
  self.url.setText(url.toString())
  
  
app = QApplication(sys.argv)
navegador = Navegador()
navegador.show()
app.exec_()



martes, 30 de junio de 2015

11 - Python PyQt (Interfaz gráfica) - Tree Widget Signals (Acceso y control de datos)




Este capítulo del tutorial de Python 3 más PyQt5 es la continuación del capítulo anterior, en el cual vimos como agregar filas al un objeto Tree Widget, concretamente listando directorios. 

En este capítulo agregaremos un nuevo método a la clase llamado getElement, que nos permitirá navegar a un directorio o abrir un archivo con su programa por defecto, todo ello tras hacer doble click (itemDoubleClicked Signal) sobre el elemento. 

Más información sobre la clase QTreeWidget: http://pyqt.sourceforge.net/Docs/PyQt4/qtreewidget.html




treewidget.pyw


import sys, time
from PyQt5.QtWidgets import QApplication, QDialog, QTreeWidgetItem
from PyQt5 import uic
from os import listdir, path, stat, startfile
from mimetypes import MimeTypes

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  uic.loadUi("treewidget.ui", self)
  self.boton.clicked.connect(self.getDir)
  self.directorio.itemDoubleClicked.connect(self.openElement)
  
 def getDir(self):
  #Eliminar todas las filas de la búsqueda anterior
  self.directorio.clear()
  #Ruta indicada por el usuario
  dir = self.ruta.text()
  #Si es un directorio
  if path.isdir(dir):
   #Recorrer sus elementos
   for element in listdir(dir):
    name = element
    pathinfo = dir + "\\" + name
    informacion = stat(pathinfo)
    #Si es un directorio
    if path.isdir(pathinfo):
     type = "Carpeta de archivos"
     size = ""
    else:
     mime = MimeTypes()
     type = mime.guess_type(pathinfo)[0]
     size = str(informacion.st_size) + " bytes"
    #Fecha de modificación
    date = str(time.ctime(informacion.st_mtime))
    #Crear un array para crear la fila con los items
    row = [name, date, type, size]
    #Insertar la fila
    self.directorio.insertTopLevelItems(0, [QTreeWidgetItem(self.directorio, row)])
 def openElement(self):
  #Obtener el item seleccionado por el usuario
  item = self.directorio.currentItem()
  #Crear la ruta accediendo al nombre del elemento (carpeta o archivo)
  elemento = self.ruta.text() + "\\" + item.text(0)
  #Si es un directorio navegar a su interior
  if path.isdir(elemento):
   self.ruta.setText(elemento)
   self.getDir()
  else: #Si es un archivo abrirlo con el programa que lo abre por defecto en Windows
   startfile(elemento)
  
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()



lunes, 29 de junio de 2015

9 - Python PyQt (Interfaz gráfica) - List Widget (Selección de elementos)




En este capítulo del tutorial de Python 3 más PyQt5, veremos un ejemplo con el objeto List Widget, este objeto nos permite crear una lista de items, los cuales pueden ser seleccionados por el usuario.

En el capítulo veremos como obtener los items seleccionados por el usuario, como agregar nuevos items y como eliminar items.

El ejemplo lo basaremos en un archivo ui creado con Designer llamado listwidget.ui

  • Agregaremos un objeto List Widget con 4 items: Python - PHP - Perl - Ruby
  • Al objeto List Widget le daremos el nombre lenguajes y haremos que sea posible la multiselección de items, modificando la propiedad selectionMode a MultiSelection e indicaremos un item seleccionado por defecto, modificando la propiedad currentRow con el valor 0, para seleccionar al item Python por defecto.
  • Agregar un objeto Push Button con el texto "Enviar" y el nombre boton.
  • Agregar un objeto Label con el nombre labelMensaje y le quitaremos el texto que viene por defecto.


listwidget.pyw


import sys
from PyQt5.QtWidgets import QApplication, QDialog
from PyQt5 import uic

class Dialogo(QDialog):
	def __init__(self):
		QDialog.__init__(self)
		uic.loadUi("listwidget.ui", self)
		self.boton.clicked.connect(self.getItems)
		#Agregar un nuevo item
		#self.lenguajes.addItem("Visual Basic")
		#Eliminar un item
		#self.deleteItem("Python")
		
	def deleteItem(self, label):
		#Array para almacenar cada item objeto
		items = []
		#Recorrer item a item
		for x in range(self.lenguajes.count()):
			item = self.lenguajes.item(x)
			items.append(item)
		#este array almacena el texto de cada item
		labels = [i.text() for i in items]
		#Recorrer item a item el array labels
		for x in range(len(labels)):
			#Si el item existe
			if labels[x] == label:
				#Eliminar
				item = self.lenguajes.indexFromItem(self.lenguajes.item(x))
				self.lenguajes.model().removeRow(item.row())
		
	def getItems(self):
		items = self.lenguajes.selectedItems()
		#Array para guardar los items seleccionados
		selected = []
		for x in range(len(items)):
			selected.append(self.lenguajes.selectedItems()[x].text())
		self.labelLenguajes.setText("Seleccionados: " + "-".join(selected))
			
		
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()


sábado, 27 de junio de 2015

8 - Python PyQt (Interfaz gráfica) - Combo Box (Selección de elementos)




En este capítulo del tutorial de Python 3 con PyQt5 veremos como manejar el elemento Combo Box, este objeto nos permite crear una lista de elementos (items), donde el usuario podrá seleccionar un determinado elemento (item).

En el ejemplo veremos como obtener el valor del item seleccionado por el usuario con el método currentText(), como agregar un nuevo item con el método addItem(item) y como eliminar un item con el método removeItem(index del item).

Crearemos un archivo .ui con Designer llamado combobox.ui

Crearemos un cuadro de diálogo y agregaremos:
  • Un Combo Box con nombre lenguajes y agregaremos cuatros items: Python, PHP, Perl y Ruby
  • Un Push Button con nombre boton y texto "Enviar"
  • Y finalmente un Label con nombre labelLenguajes y le quitaremos el texto que viene por defecto.





combobox.pyw


import sys
from PyQt5.QtWidgets import QApplication, QDialog
from PyQt5 import uic

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  uic.loadUi("combobox.ui", self)
  self.boton.clicked.connect(self.getItem)
  
  #Agregar un nuevo item
  #self.lenguajes.addItem("C++")
  
  #Eliminar un item
  #self.lenguajes.removeItem(0)
  
 def getItem(self):
  item = self.lenguajes.currentText()
  self.labelLenguajes.setText("Has seleccionado: " + item)
  
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()


viernes, 26 de junio de 2015

7 - Python PyQt (Interfaz gráfica) - Radio Button y Checkbox




En este capítulo del tutorial de Python 3 con PyQt5, veremos un sencillo ejemplo de como manejar los elementos Radio Button y Checkbox.

El formulario los diseñaremos con Designer, que será un cuadro de diálogo. Los Radio Buttons los agruparemos en un elemento Group Box, cada Radio Button hará referencia a un lenguaje de programación (python, php, perl y ruby), el Group Box nos permite que el usuario sólo pueda seleccionar uno de los Radio Buttons. También agregaremos un elemento Check Box con el típico ejemplo de "Aceptar los términos".

Para cuando el usuario seleccione un Radio Button mostraremos un mensaje en un elemento Label con el lenguaje seleccionado, igualmente haremos con el Check Box, su estado será mostrado en otra etiqueta Label.

El archivo .ui se llama radio-checkbox.ui y sus elementos son:

  • Un Group Box con el texto "Lenguajes de programacion:"

  • En el interior del Group Box agregaremos cuatro Radio Buttons:
  1. texto "Python", nombre python y seleccionado por defecto (En la propiedad checked)
  2.  texto "PHP" y nombre php
  3. texto "Perl" y nombre perl
  4. texto "Ruby" y nombre ruby

  • Agregaremos un elemento Check Box, con texto "Aceptar los términos" y nombre terminos

  • Agregaremos un elemento Push Button, con texto "Aceptar" y nombre boton

  • Y Finalmente agregaremos dos elementos Label con nombres, labelLenguaje y labelTerminos.



radio-checkbox.pyw

import sys
from PyQt5.QtWidgets import QApplication, QDialog
from PyQt5 import uic

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  uic.loadUi("radio-checkbox.ui", self)
  
  self.radio_value()
  self.boton.clicked.connect(self.radio_value)
  self.checkbox_state()
  self.boton.clicked.connect(self.checkbox_state)
  
 def radio_value(self):
  if self.python.isChecked():
   self.labelLenguaje.setText("Python ha sido seleccionado")
  elif self.php.isChecked():
   self.labelLenguaje.setText("PHP ha sido seleccionado")
  elif self.perl.isChecked():
   self.labelLenguaje.setText("Perl ha sido seleccionado")
  elif self.ruby.isChecked():
   self.labelLenguaje.setText("Ruby ha sido seleccionado")
  else:
   self.labelLenguaje.setText("No hay seleccionado ningún lenguaje")
 
 def checkbox_state(self):
  if self.terminos.isChecked():
   self.labelTerminos.setText("Has aceptado los términos")
  else:
   self.labelTerminos.setText("No has aceptado los términos")
   
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()


6 - Python PyQt (Interfaz gráfica) - Validar formularios con expresiones regulares




En este capítulo del tutorial de Python 3 con PyQt5 veremos como podemos ir validando los campos de los formularios utilizando expresiones regulares.

Crearemos un archivo .ui con Designer llamado validacion.ui. El formulario será un cuadro de diálogo sin botones (Dialog without Buttons).

  • Agregaremos un campo Line Edit al cual llamaremos nombre y con su respectiva etiqueta Label.
  • Agregaremos un campo Line Edit al cual llamaremos email y con su respectiva etiqueta Label.
  • Agregaremos un campo Spin Box al cual llamaremos edad para incluir un rango de números enteros y con su respectiva etiqueta Label.
  • Y finalmente agregaremos un botón Push Button al cual llamaremos boton y tendrá el texto Validar.


Los campos nombre y email se irán validando al mismo tiempo que su texto cambia (textChanged), cada cual con su respectivo método de validación validar_nombre() y validar_email(), ambos métodos regresan un valor boolean, si es True es una validación correcta, si es False no.

Al hacer click sobre el botón (clicked), comprobaremos a través de un método llamado validar_formulario() si los métodos validar_nombre() y validar_email() regresan el valor True, de ser así es que el formulario es correcto y mostraremos un mensaje informando de que el formulario es correcto, de lo contrario, mostraremos un mensaje advirtiendo de que es incorrecto.

validacion.pyw

import sys, re
from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox
from PyQt5 import uic

class Dialogo(QDialog):
	def __init__(self):
		QDialog.__init__(self)
		uic.loadUi("validacion.ui", self)
		self.nombre.textChanged.connect(self.validar_nombre)
		self.email.textChanged.connect(self.validar_email)
		self.boton.clicked.connect(self.validar_formulario)
		
	def validar_nombre(self):
		nombre = self.nombre.text()
		validar = re.match('^[a-z\sáéíóúàèìòùäëïöüñ]+$', nombre, re.I)
		if nombre == "":
			self.nombre.setStyleSheet("border: 1px solid yellow;")
			return False
		elif not validar:
			self.nombre.setStyleSheet("border: 1px solid red;")
			return False
		else:
			self.nombre.setStyleSheet("border: 1px solid green;")
			return True
			
	def validar_email(self):
		email = self.email.text()
		validar = re.match('^[a-zA-Z0-9\._-]+@[a-zA-Z0-9-]{2,}[.][a-zA-Z]{2,4}$', email, re.I)
		if email == "":
			self.email.setStyleSheet("border: 1px solid yellow;")
			return False
		elif not validar:
			self.email.setStyleSheet("border: 1px solid red;")
			return False
		else:
			self.email.setStyleSheet("border: 1px solid green;")
			return True
		
	def validar_formulario(self):
		if self.validar_nombre() and self.validar_email():
			QMessageBox.information(self, "Formulario correcto", "Validación correcta", QMessageBox.Discard)
		else:
			QMessageBox.warning(self, "Formulario incorrecto", "Validación incorrecta", QMessageBox.Discard)
		
		
app = QApplication(sys.argv)
dialogo = Dialogo()
dialogo.show()
app.exec_()



jueves, 25 de junio de 2015

5 - Python PyQt (Interfaz gráfica) - Diseñar formularios con Layouts y StyleSheet




En este capítulo del tutorial de Python 3 con PyQt5 trabajaremos con el diseñador (Designer) para diseñar un cuadro de dialogo (QDialog), veremos como ir aplicando estilos CSS a los elementos del mismo a través de la propiedad stylesheet del elemento padre Dialog. Una vez diseñado cargaremos el archivo .ui en la clase Dialogo del script abrirDialogo.pyw del capítulo anterior.

5 - Python PyQt (Interfaz gráfica) - Diseñar formularios con Layouts y StyleSheet




Estilos CSS del archivo stylesheet.ui


QDialog{
background-color: green;
font-family: arial, verdana;
}

QLineEdit{
border: 0;
font-size: 14px;
height: 30px;
}

QLabel{
color: #fff;
font-size: 14px;
}

QPushButton#boton{
background-color: #000;
color: #fff;
font-size: 16px;
border: 0;
}

QPushButton#boton:hover{
background-color: orange;
}


miércoles, 24 de junio de 2015

4 - Python PyQt (Interfaz gráfica) - Abrir cuadro de diálogo desde la ventana principal




En este capítulo del tutorial de Python 3 con PyQt5 veremos un ejemplo muy útil cuando se trata de abrir cuadros de diálogo desde la ventana principal, el ejemplo será bastante básico, crearemos un clase para crear un objeto QDialog (Cuadro de diálogo) y una clase para crear un objeto QMainWindow (Ventana principal). En la ventana principal agregaremos un botón (QPushButton) para que cuando el usuario haga click sobre él, se abra el cuadro de diálogo manteniendo la ventana principal como fondo.

abrirDialogo.pyw

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog, QPushButton, QLabel

class Dialogo(QDialog):
 def __init__(self):
  QDialog.__init__(self)
  self.resize(300, 300)
  self.setWindowTitle("Cuadro de diálogo")
  self.etiqueta = QLabel(self)
  
class Ventana(QMainWindow):
 def __init__(self):
  QMainWindow.__init__(self)
  self.resize(600, 600)
  self.setWindowTitle("Ventana principal")
  self.boton = QPushButton(self)
  self.boton.setText("Abrir cuadro de diálogo")
  self.boton.resize(200, 30)
  self.dialogo = Dialogo()
  self.boton.clicked.connect(self.abrirDialogo)
  
 def abrirDialogo(self):
  self.dialogo.etiqueta.setText("Diálogo abierto desde la ventana principal")
  self.dialogo.exec_()
  
app = QApplication(sys.argv)
ventana = Ventana()
ventana.show()
app.exec_()


martes, 23 de junio de 2015

2 - Python PyQt (Interfaz gráfica) - Acceso a propiedades de elementos




En este capítulo modificaremos algunas propiedades del objeto MainWindow del archivo .ui generado en el capítulo anterior y también veremos como acceder a sus elementos internos a través de su nombre, por ejemplo a un elemento QPushButton para modificar sus estilos CSS.

MainWindow.pyw


import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import uic
from PyQt5.QtGui import QFont
from PyQt5.QtCore import Qt
import ctypes #GetSystemMetrics

#Clase heredada de QMainWindow (Constructor de ventanas)
class Ventana(QMainWindow):
 #Método constructor de la clase
 def __init__(self):
  #Iniciar el objeto QMainWindow
  QMainWindow.__init__(self)
  #Cargar la configuración del archivo .ui en el objeto
  uic.loadUi("MainWindow.ui", self)
  self.setWindowTitle("Cambiando el título de la ventana")
  #Mostrar la ventana maximizada
  self.showMaximized()
  #Fijar el tamaño de la ventana
  #Fijar el tamaño mínimo
  self.setMinimumSize(500, 500)
  #Fijar el tamaño máximo
  self.setMaximumSize(500, 500)
  #Mover la ventana y centrarla en el escritorio
  resolucion = ctypes.windll.user32
  resolucion_ancho = resolucion.GetSystemMetrics(0)
  resolucion_alto = resolucion.GetSystemMetrics(1)
  left = (resolucion_ancho / 2) - (self.frameSize().width() / 2)
  top = (resolucion_alto / 2) - (self.frameSize().height() / 2)
  self.move(left, top)
  #Desactivar la ventana
  #self.setEnabled(False)
  #Asignar un tipo de fuente
  qfont = QFont("Arial", 12, QFont.Bold)
  self.setFont(qfont)
  #Asignar un tipo de cursor
  self.setCursor(Qt.SizeAllCursor)
  #Asignar estilos CSS
  #self.setStyleSheet("background-color: #000; color: #fff;")
  #Modificar estilos de uno de los elementos de la ventana
  self.boton.setStyleSheet("background-color: #000; color: #fff; font-size: 14px;")
  
  
#Instancia para iniciar una aplicación
app = QApplication(sys.argv)
#Crear un objeto de la clase
_ventana = Ventana()
#Mostra la ventana
_ventana.show()
#Ejecutar la aplicación
app.exec_()