Pyqt5 MainWIndow - simple gui to connect with sql server - python

I need to make simple GUI which can connect with sql.
How can I pass variables from one window -> second?
My problem is:
I have window with login panel (QLogin.py), which is testing connection with sql server. If it can cannect - good, log in. If not, return error. It works
In my main window (QApp.py) i need to do sql queries and return result in my qtablewidget (etc.)
I wanted pass variables:login = self.username.text() & pwd = self.password.text() from QLogin to QApp to make new connection to do queries, but it doesnt work.
Maybe someone sees better option how to solve it?
To sum up: After entering IMEI in QApp.py i would like to do sql query by using credentials & connection engine from QLogin.py.
Enter sql login & password in QLogin.py
Enter IMEI and press button -> run sql query
QLogin.py
# -- coding: utf-8 --
from PyQt5.QtWidgets import QLineEdit,QDialogButtonBox,QFormLayout,QDialog,QMessageBox
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5 import QtGui
import qdarkstyle
import sqlalchemy
class LoginDialog(QDialog):
def __init__(self, parent=None):
super(LoginDialog,self).__init__(parent)
self.init_ui()
def init_ui(self):
### delete question mark
self.setWindowFlags(self.windowFlags()
^ Qt.WindowContextHelpButtonHint)
### login & password fields
self.username = QLineEdit(self)
self.password = QLineEdit(self)
self.password.setEchoMode(QLineEdit.Password)
loginLayout = QFormLayout()
loginLayout.addRow("Username", self.username)
loginLayout.addRow("Password", self.password)
self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttons.accepted.connect(self.control)
self.buttons.rejected.connect(self.reject)
layout = QtWidgets.QVBoxLayout(self)
layout.addLayout(loginLayout)
layout.addWidget(self.buttons)
self.setLayout(layout)
### set window title & stylesheet
self.setWindowTitle('Login Box')
self.setWindowIcon(QtGui.QIcon('dc1.png'))
self.setStyleSheet((qdarkstyle.load_stylesheet_pyqt5()))
###lock resize
self.setSizeGripEnabled(False)
self.setFixedSize(self.sizeHint())
###log by usins sql credentials
def control(self):
ser = ########
base = #########
login = self.username.text()
pwd = self.password.text()
timeout = 5
self.engine = sqlalchemy.create_engine(
"mssql+pyodbc://{username}:{pwd}#10.96.5.17\dqinstance/{dbname}?driver=SQL+Server+Native+Client+11.0".format(
dbname=base, username=login, pwd=pwd), connect_args={'timeout': timeout})
try:
connection = self.engine.connect()
connection.close()
self.accept()
except:
QMessageBox.warning(self, 'Error', "Wrong username or password! \n\n"
"Please use the SQL Server credentials ")
It looks like:
QApp.py
# -- coding: utf-8 --
from PyQt5 import QtCore, QtGui, QtWidgets
from QLogin import LoginDialog
import qdarkstyle
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow,self).__init__(parent)
self.main_frame()
self.center() #center frame
self.layout_init() #widgets layout
def main_frame(self):
### actions on meenubar
exitAct = QtWidgets.QAction('&Exit', self)
exitAct.setShortcut('Ctrl+Q')
exitAct.setStatusTip('Exit application')
exitAct.triggered.connect(self.close)
self.statusBar()
### menubar
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(exitAct)
fileMenu = menubar.addMenu('&Help')
### basic geometry and color
self.setWindowTitle('Test Window')
self.setWindowIcon(QtGui.QIcon('dc1.png'))
self.setStyleSheet((qdarkstyle.load_stylesheet_pyqt5()))
def layout_init(self):
grid = QtWidgets.QGridLayout()
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
### widgets
self.tablewidget = QtWidgets.QTableWidget() #frame
self.textbox = QtWidgets.QLineEdit()
self.textbox.setPlaceholderText('IMEI')
self.textbox.setEnabled(True)
self.textbox.setValidator(QtGui.QDoubleValidator())
self.pb = QtWidgets.QPushButton(self.tr("Run process"))
self.pb.setDisabled(True)
self.textbox.textChanged.connect(self.disableButton)
self.pb.clicked.connect(self.on_clicked)
### make vidgets alive
self.centralWidget().setLayout(grid)
grid.addWidget(self.textbox)
grid.addWidget(self.tablewidget)
grid.addWidget(self.pb)
### center main window
def center(self):
qr = self.frameGeometry()
cp = QtWidgets.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def disableButton(self):
if len(self.textbox.text())> 0:
self.pb.setDisabled(False)
else:
self.pb.setDisabled(True)
### run process button
def on_clicked(self):
if len(self.textbox.text())== 0:
pass
else:
print('test')
def sql_query(self):
self.sql = "SELECT " \
"FI.FileNameTransformed, " \
"FI.OrderItemCode, " \
"FIR.Imported," \
"FR.Row " \
"FROM[PROD_WAREX2].[dbo].[FileRows] FR " \
"JOIN[PROD_WAREX2].[dbo].[FileImportRows]FIR ON FR.RowId = FIR.RowId" \
"JOIN[PROD_WAREX2].[dbo].[FileImports] FI ON FIR.FileImportId = FI.Id" \
"WHERE FR.Row LIKE : e1"
self.e1 = '%'&self.textbox.text()&'%'
QMain.py
import sys
from PyQt5.QtWidgets import QApplication
from QLogin import LoginDialog
from QApp import MainWindow
if __name__ == '__main__':
app = QApplication(sys.argv)
login = LoginDialog()
if not login.exec_():
sys.exit(-1)
window = MainWindow()
window.show()
sys.exit(app.exec_())

The solution is simple, get credentials from login object and then pass it to window object:
class LoginDialog(QDialog):
# ...
def credentials():
return self.username.text(), self.password.text()
# ...
class MainWindow(QtWidgets.QMainWindow):
# ...
def setCredentials(self, credentials):
self._credentials = credentials
# ...
def sql_query(self):
base = #########
username, pwd = self._credentials
self.engine = sqlalchemy.create_engine(
"mssql+pyodbc://{username}:{pwd}#10.96.5.17\dqinstance/{dbname}?driver=SQL+Server+Native+Client+11.0".format(
dbname=base, username=username, pwd=pwd), connect_args={'timeout': timeout})
try:
connection = self.engine.connect()
# ...
except:
QMessageBox.warning(self, 'Error', "Wrong username or password! \n\n"
"Please use the SQL Server credentials ")
if __name__ == '__main__':
app = QApplication(sys.argv)
login = LoginDialog()
if login.exec_() != QtWidgets.QDialog.Accepted:
sys.exit(-1)
window = MainWindow()
window.setCredentials(login.credentials()) # <----
window.show()
sys.exit(app.exec_())

Related

populate combobox editable username and line edit password from sqlite3

I have 1 combobox editable username and 1 line edit password i want load user list from database sqlite3 to combobox. After load list i want action on itemlist combobox get password current selected user from database.
pyqt5 application
table columns
table records
######Combo Box 2 - PM's
self.groupBox_2 = QtWidgets.QGroupBox(self.tab)
self.groupBox_2.setGeometry(QtCore.QRect(10, 140, 191, 51))
self.groupBox_2.setObjectName("groupBox_2")
self.comboBox_3 = QtWidgets.QComboBox(self.groupBox_2)
self.comboBox_3.setGeometry(QtCore.QRect(10, 20, 171, 22))
self.comboBox_3.setObjectName("comboBox_3")
self.comboBox_3.addItems(self.pm_Combo)
def pm_Combo(self):
conn = sqlite3.connect('testdb.db')
c = conn.cursor()
c.execute("SELECT DISTINCT projectmanager FROM testtable2")
pmList = c.fetchall()
conn.commit()
conn.close()
Assuming that the table is called "user" then you can use the userData to save the information and set it in the QLineEdit when the currentIndex of the QComboBox changes:
import os
import sqlite3
import sys
from PyQt5 import QtWidgets, QtSql
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.username_combo = QtWidgets.QComboBox()
self.password_lineedit = QtWidgets.QLineEdit()
button_login = QtWidgets.QPushButton(self.tr("Login"))
lay = QtWidgets.QFormLayout(self)
lay.addRow(self.tr("Username:"), self.username_combo)
lay.addRow(self.tr("Password:"), self.password_lineedit)
lay.addRow(button_login)
self.username_combo.currentIndexChanged.connect(self.update_password)
self.load_usernames()
def load_usernames(self):
current_dir = os.path.dirname(os.path.realpath(__file__))
db_path = os.path.join(current_dir, "testdb.db")
self.username_combo.clear()
with sqlite3.connect(db_path) as conn:
cursor = conn.execute("SELECT DISTINCT username, password FROM User")
for result in cursor.fetchall():
username, password = result
self.username_combo.addItem(username, password)
def update_password(self):
self.password_lineedit.setText(self.username_combo.currentData())
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Another possible solution is to use QSqlQueryModel with QDataWidgetMapper:
import os
import sys
from PyQt5 import QtWidgets, QtSql
def create_connection():
current_dir = os.path.dirname(os.path.realpath(__file__))
db_path = os.path.join(current_dir, "testdb.db")
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(db_path)
if not db.open():
print("error: {}".format(db.lastError().text()))
return False
return True
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.username_combo = QtWidgets.QComboBox()
self.password_lineedit = QtWidgets.QLineEdit()
button_login = QtWidgets.QPushButton(self.tr("Login"))
lay = QtWidgets.QFormLayout(self)
lay.addRow(self.tr("Username:"), self.username_combo)
lay.addRow(self.tr("Password:"), self.password_lineedit)
lay.addRow(button_login)
self.load_data()
def load_data(self):
self.model = QtSql.QSqlQueryModel()
self.model.setQuery("""SELECT DISTINCT username, password FROM User""")
self.mapper = QtWidgets.QDataWidgetMapper()
self.mapper.setModel(self.model)
self.mapper.addMapping(self.password_lineedit, 1)
self.username_combo.currentIndexChanged.connect(self.mapper.setCurrentIndex)
self.username_combo.setModel(self.model)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
if not create_connection():
sys.exit(-1)
w = Widget()
w.show()
sys.exit(app.exec_())

PyQt Messagebox is crashing

I'm working on a file transfer application including its server. When I try to send a file between two clients, I want to ensure that the receiving client will get a message box like "x user wants to send a file. Do you accept?". I accomplished this so far but when I clicked the "Yes" button for testing purposes, the Yes button disappear and the receiving client collapses. When I tried to view the error on console, I saw that it's "QObject::setParent: Cannot set parent, new parent is in a different thread" on the receiving client. I searched the error on the site but couldn't make sense out of the solutions. Could you explain to me how I can solve this?
Code:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
import socket
import json
import base64
from threading import Thread
from time import sleep
username = "admin"
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("144.122.86.204",5000)) #Fill here later
s.send(username.encode("utf-8"))
app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle("File Sharing")
window.setGeometry(0,0,500,350)
contactlist= QListWidget(window)
contactlist.setGeometry(5,5,100,250)
def add():
user, ok = QInputDialog.getText(window, "Add a contact","Enter a
username:")
if ok:
contactlist.addItem(str(user))
def send():
target_user=contactlist.currentItem().text()
name = QFileDialog.getOpenFileName(window, 'Open File')
file = open(name[0], 'rb')
base64_bytes = base64.b64encode(file.read())
base64_string = base64_bytes.decode('utf-8')
data= {"FRM": username, "TO": target_user, "DATA": base64_string}
encoded_data = json.dumps(data).encode()
s.sendall(encoded_data)
def receive():
while True:
data = s.recv(1024)
if data:
if 'wants to send you a file. Do you accept that?' in
data.decode('utf-8'):
choice = QMessageBox.question(window, 'FileTransfer',
data.decode('utf-8'),QMessageBox. Yes|QMessageBox. No)
if choice == QMessageBox.Yes:
s.send("CONFIRMED".encode('utf-8'))
t = Thread(target=receive)
t.start()
add_contact = QPushButton("Add a Contact", window)
add_contact.setGeometry(5,270,100,50)
add_contact.clicked.connect(add)
send_file = QPushButton("Send a File",window)
send_file.setGeometry(110,270,200,50)
send_file.clicked.connect(send)
window.show()
sys.exit(app.exec())
The problem in your case is that QMessageBox is being created in another thread which Qt prohibits, and its parent window lives in the main thread what Qt also forbids. The general approach in this case is to send the information of the secondary thread to the main thread through signals, events, metaobjects, etc. that are thread-safe and in the main thread is to create the GUI (in your case QMessageBox).
In this case it can be complicated so instead of using the above it is best to use the QtNetwork module of Qt that will send you the information received through signals making the while loop unnecessary and therefore the use of threads.
import json
import base64
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.initUi()
self.m_username = "admin"
self.m_socket = QtNetwork.QTcpSocket(self)
self.m_socket.connected.connect(self.onConnected)
self.m_socket.readyRead.connect(self.onReadyRead)
self.m_socket.connectToHost("144.122.86.204", 5000)
#QtCore.pyqtSlot()
def onConnected(self):
username = "admin"
self.m_socket.write(self.m_username.encode("utf-8"))
self.m_socket.flush()
#QtCore.pyqtSlot()
def onReadyRead(self):
data = self.m_socket.readAll().data()
text = data.decode("utf-8")
if "wants to send you a file. Do you accept that?" in text:
choice = QtWidgets.QMessageBox.question(
self,
"FileTransfer",
text,
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
)
if choice == QtWidgets.QMessageBox.Yes:
self.m_socket.write("CONFIRMED".encode("utf-8"))
#QtCore.pyqtSlot()
def add_contant(self):
user, ok = QtWidgets.QInputDialog.getText(
self, "Add a contact", "Enter a username:"
)
if ok:
self.m_listwidget.addItem(user)
#QtCore.pyqtSlot()
def send_file(self):
if self.m_socket.state() != QtNetwork.QAbstractSocket.ConnectedState:
print("Socket not connected")
return
item = self.m_listwidget.currentItem()
if item is None:
print("Not current item")
return
target_user = item.text()
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Open File")
with open(filename, "rb") as file:
base64_bytes = base64.b64encode(file.read())
base64_string = base64_bytes.decode("utf-8")
data = {"FRM": self.m_username, "TO": target_user, "DATA": base64_string}
encoded_data = json.dumps(data).encode()
self.m_socket.write(encoded_data)
self.m_socket.flush()
def initUi(self):
self.m_listwidget = QtWidgets.QListWidget()
self.m_listwidget.setFixedWidth(100)
self.m_add_button = QtWidgets.QPushButton("Add a Contact")
self.m_add_button.clicked.connect(self.add_contant)
self.m_add_button.setFixedSize(100, 50)
self.m_send_file = QtWidgets.QPushButton("Send a File")
self.m_send_file.clicked.connect(self.send_file)
self.m_send_file.setFixedSize(200, 50)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(self.m_listwidget)
hlay.addStretch()
hlay2 = QtWidgets.QHBoxLayout()
hlay2.addWidget(self.m_add_button)
hlay2.addWidget(self.m_send_file)
hlay2.addStretch()
vlay = QtWidgets.QVBoxLayout(self)
vlay.addLayout(hlay)
vlay.addLayout(hlay2)
self.resize(500, 350)
def closeEvent(self, event):
if self.m_socket.state() == QtNetwork.QAbstractSocket.ConnectedState:
self.m_socket.disconnectFromHost()
super(Widget, self).closeEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec())

gui not responding during process?

I have sql query. My push button connects with sql - it takes long time. During it, my GUI is not responding - is it possible to make it responds?
QtCore.QCoreApplication.processEvents() doesnt work.
QApp.py
# -- coding: utf-8 --
from PyQt5 import QtGui, QtWidgets, QtCore
import qdarkstyle
import pyodbc
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow,self).__init__(parent)
self.main_frame()
self.center() #center frame
self.layout_init() #widgets layout
def main_frame(self):
### actions on meenubar
exitAct = QtWidgets.QAction('&Exit', self)
exitAct.setShortcut('Ctrl+Q')
exitAct.setStatusTip('Exit application')
exitAct.triggered.connect(self.close)
self.statusBar()
moreinfo = QtWidgets.QAction('&Help',self)
moreinfo.setStatusTip('More information')
moreinfo.triggered.connect(self.information)
self.statusBar()
### menubar
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(exitAct)
fileMenu = menubar.addMenu('&Help')
fileMenu.addAction(moreinfo)
### basic geometry and color
self.setWindowTitle('Villain')
self.setWindowIcon(QtGui.QIcon('dc1.png'))
self.setStyleSheet((qdarkstyle.load_stylesheet_pyqt5()))
def layout_init(self):
self.grid = QtWidgets.QGridLayout()
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
### widgets
self.tablewidget = QtWidgets.QTableWidget()
self.tablewidget.setColumnCount(4)
self.tablewidget.setHorizontalHeaderLabels(["FileNameTransformed", "OrderItemCode", "Imported", "Row"])
self.tablewidget.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.tablewidget.horizontalHeader().setStretchLastSection(True)
self.tablewidget.resizeColumnsToContents()
self.tablewidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.tablewidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
#self.tablewidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.textbox = QtWidgets.QLineEdit()
self.textbox.setPlaceholderText('IMEI')
self.textbox.setEnabled(True)
regexp = QtCore.QRegExp('^(0\d+|[1-9][0-9]+)$') #IMEI = only int
self.textbox.setValidator(QtGui.QRegExpValidator(regexp))
self.pb = QtWidgets.QPushButton(self.tr("Run process"))
self.pb.setDisabled(True)
self.textbox.textChanged.connect(self.disableButton)
self.pb.clicked.connect(self.on_clicked_pb)
self.clearbutton = QtWidgets.QPushButton(self.tr("Clear all"))
self.clearbutton.setDisabled(True)
self.clearbutton.clicked.connect(self.on_clicked_clear)
### make vidgets alive
self.centralWidget().setLayout(self.grid)
self.grid.addWidget(self.textbox)
self.grid.addWidget(self.tablewidget)
self.grid.addWidget(self.pb)
self.grid.addWidget(self.clearbutton)
### center main window
def center(self):
qr = self.frameGeometry()
cp = QtWidgets.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def information(self):
QtWidgets.QMessageBox.information(self,'Information','Version: 1.19.11.18\n'\
'This is the prototype of the application\n\n'\
'Please, contact karol.chojnowski#digitalcaregroup.com for comments and suggestions\n\n'\
'Digital Care - Data Processing Team')
def disableButton(self):
if len(self.textbox.text())> 0:
self.pb.setDisabled(False)
self.clearbutton.setDisabled(False)
else:
self.pb.setDisabled(True)
self.clearbutton.setDisabled(True)
### run process button
#QtCore.pyqtSlot()
def on_clicked_pb(self):
if len(self.textbox.text()) == 0:
pass
else:
self.sql_query()
### clear all
#QtCore.pyqtSlot()
def on_clicked_clear(self):
if len(self.textbox.text())> 0:
self.textbox.clear()
self.tablewidget.setRowCount(0)
self.tablewidget.setColumnWidth(3, 200)
def setCredentials(self, credentials):
self._credentials = credentials
def sql_query(self):
ser = "10.96.6.14"
base = "PROD_WAREX2"
username, pwd = self._credentials
QtCore.QCoreApplication.processEvents()
try:
self.connection = pyodbc.connect(driver='{SQL Server}', server=ser, database=base,
user=username, password=pwd)
cursor = self.connection.cursor()
self.res = cursor.execute(""" SELECT FI.FileNameTransformed,
FI.OrderItemCode,
FIR.Imported,
FR.Row
FROM [FileRows] AS FR
JOIN [FileImportRows] AS FIR ON FR.RowId = FIR.RowId
JOIN [FileImports] AS FI ON FIR.FileImportId = FI.Id
WHERE FR.Row LIKE ? """, ('%' + self.textbox.text() + '%'))
if not cursor.rowcount:
QtWidgets.QMessageBox.information(self, 'IMEI', "No items found")
cursor.close()
pass
else:
self.tablewidget.setRowCount(0)
for row, form in enumerate(self.res):
self.tablewidget.insertRow(row)
for column, item in enumerate(form):
newitem = QtWidgets.QTableWidgetItem(str(item))
self.tablewidget.setItem(row, column, newitem)
cursor.close()
self.table_performance()
self.tablewidget.sortItems(0, order=QtCore.Qt.DescendingOrder)
except:
QtWidgets.QMessageBox.warning(self, 'Error', "Something went wrong\n\n"\
"Contact karol.chojnowski#digitalcaregroup.com")
QtWidgets.QMessageBox.setStyleSheet((qdarkstyle.load_stylesheet_pyqt5()))
def table_performance(self):
self.tablewidget.resizeColumnsToContents()
self.tablewidget.setColumnWidth(3, 2500)
self.tablewidget.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
main .py:
Villain.py
# -- coding: utf-8 --
import sys
from PyQt5 import QtWidgets,QtGui
from QLogin import LoginDialog
from QApp import MainWindow
import os
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
login = LoginDialog()
login.setWindowIcon(QtGui.QIcon(resource_path('dc1.png')))
if login.exec_() != QtWidgets.QDialog.Accepted:
sys.exit(-1)
window = MainWindow()
window.setWindowIcon(QtGui.QIcon(resource_path('dc1.png')))
window.setGeometry(500, 150, 800, 500)
window.setCredentials(login.credentials()) # <----
window.show()
sys.exit(app.exec_())
The use of processEvents() implies in most cases a bad design. If you have a heavy task, do not execute it in the main thread, execute it in another thread and send the necessary data to the main thread by means of signals or with QMetaObject::invokeMethod() (this last option will be used since it is not necessary so much connection).
On the other hand you should only add the stylesheet once, if there are new widgets they will take as a basis the stylesheet.
# -- coding: utf-8 --
import threading
from PyQt5 import QtGui, QtWidgets, QtCore
import qdarkstyle
import pyodbc
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow,self).__init__(parent)
self.main_frame()
self.center() #center frame
self.layout_init() #widgets layout
def main_frame(self):
### actions on meenubar
exitAct = QtWidgets.QAction('&Exit', self, shortcut='Ctrl+Q', statusTip='application')
exitAct.triggered.connect(self.close)
moreinfo = QtWidgets.QAction('&Help',self, statusTip='More information')
moreinfo.triggered.connect(self.information)
### menubar
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(exitAct)
fileMenu = menubar.addMenu('&Help')
fileMenu.addAction(moreinfo)
### basic geometry and color
self.setWindowTitle('Villain')
self.setWindowIcon(QtGui.QIcon('dc1.png'))
self.setStyleSheet((qdarkstyle.load_stylesheet_pyqt5()))
def layout_init(self):
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
### widgets
self.tablewidget = QtWidgets.QTableWidget()
self.tablewidget.setColumnCount(4)
self.tablewidget.setHorizontalHeaderLabels(["FileNameTransformed", "OrderItemCode", "Imported", "Row"])
self.tablewidget.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.tablewidget.horizontalHeader().setStretchLastSection(True)
self.tablewidget.resizeColumnsToContents()
self.tablewidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.tablewidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
#self.tablewidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.textbox = QtWidgets.QLineEdit()
self.textbox.setPlaceholderText('IMEI')
self.textbox.setEnabled(True)
regexp = QtCore.QRegExp('^(0\d+|[1-9][0-9]+)$') #IMEI = only int
self.textbox.setValidator(QtGui.QRegExpValidator(regexp))
self.pb = QtWidgets.QPushButton(self.tr("Run process"))
self.pb.setDisabled(True)
self.textbox.textChanged.connect(self.disableButton)
self.pb.clicked.connect(self.on_clicked_pb)
self.clearbutton = QtWidgets.QPushButton(self.tr("Clear all"))
self.clearbutton.setDisabled(True)
self.clearbutton.clicked.connect(self.on_clicked_clear)
### make vidgets alive
grid = QtWidgets.QGridLayout(central_widget)
grid.addWidget(self.textbox)
grid.addWidget(self.tablewidget)
grid.addWidget(self.pb)
grid.addWidget(self.clearbutton)
self.table_performance()
### center main window
def center(self):
qr = self.frameGeometry()
cp = QtWidgets.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def information(self):
QtWidgets.QMessageBox.information(self,'Information','Version: 1.19.11.18\n'\
'This is the prototype of the application\n\n'\
'Please, contact karol.chojnowski#digitalcaregroup.com for comments and suggestions\n\n'\
'Digital Care - Data Processing Team')
#QtCore.pyqtSlot()
def disableButton(self):
val = bool(self.textbox.text())
self.pb.setDisabled(not val)
self.clearbutton.setDisabled(not val)
### run process button
#QtCore.pyqtSlot()
def on_clicked_pb(self):
if self.textbox.text():
threading.Thread(target=self.sql_query, daemon=True).start()
### clear all
#QtCore.pyqtSlot()
def on_clicked_clear(self):
if self.textbox.text():
self.textbox.clear()
self.tablewidget.setRowCount(0)
self.tablewidget.setColumnWidth(3, 200)
def setCredentials(self, credentials):
self._credentials = credentials
def table_performance(self):
self.tablewidget.resizeColumnsToContents()
self.tablewidget.setColumnWidth(3, 2500)
self.tablewidget.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
#QtCore.pyqtSlot(str, str)
def show_warning(self, title, msg):
QtWidgets.QMessageBox.information(self, title, msg)
#QtCore.pyqtSlot()
def clear_items(self):
self.tablewidget.setRowCount(0)
#QtCore.pyqtSlot(int, int, str)
def add_item(self, row, column, val):
if row >= self.tablewidget.rowCount():
self.tablewidget.insertRow(self.tablewidget.rowCount())
newitem = QtWidgets.QTableWidgetItem(val)
self.tablewidget.setItem(row, column, newitem)
#QtCore.pyqtSlot()
def sort_items(self):
self.tablewidget.sortItems(0, order=QtCore.Qt.DescendingOrder)
def sql_query(self):
ser = "10.96.6.14"
base = "PROD_WAREX2"
username, pwd = self._credentials
try:
connection = pyodbc.connect(driver='{SQL Server}', server=ser, database=base,
user=username, password=pwd)
cursor = connection.cursor()
res = cursor.execute(""" SELECT FI.FileNameTransformed,
FI.OrderItemCode,
FIR.Imported,
FR.Row
FROM [FileRows] AS FR
JOIN [FileImportRows] AS FIR ON FR.RowId = FIR.RowId
JOIN [FileImports] AS FI ON FIR.FileImportId = FI.Id
WHERE FR.Row LIKE ? """, ('%' + self.textbox.text() + '%'))
if not cursor.rowcount:
QtCore.QMetaObject.invokeMethod(self, "show_warning",
QtCore.Qt.QueuedConnection,
QtCore.Q_ARG(str, 'IMEI'), QtCore.Q_ARG(str, "No items found"))
else:
QtCore.QMetaObject.invokeMethod(self, "clear_items", QtCore.Qt.QueuedConnection)
QtCore.QThread.msleep(10)
for row, form in enumerate(res):
for column, item in enumerate(form):
QtCore.QMetaObject.invokeMethod(self, "add_item",
QtCore.Qt.QueuedConnection,
QtCore.Q_ARG(int, row), QtCore.Q_ARG(int, column), QtCore.Q_ARG(str, str(item)))
QtCore.QThread.msleep(10)
QtCore.QMetaObject.invokeMethod(self, "sort_items", QtCore.Qt.QueuedConnection)
cursor.close()
except:
QtCore.QMetaObject.invokeMethod(self, "show_warning",
QtCore.Qt.QueuedConnection,
QtCore.Q_ARG(str, 'Error'), QtCore.Q_ARG(str, "Something went wrong\n\n"\
"Contact karol.chojnowski#digitalcaregroup.com"))

PyQt4 Questions and Login WIndow to Main Window in Python

I'm new to PyQt and Python as a whole (more familiar with Java) and I'm trying to make an application where data is inserted and retrieved into the database. For the connection to the database, I'm using mysql connector. I'm able to insert and retrieve data just fine but I'm not sure how to implement the GUI portion of my application. What I want to do is have a login window that connected to the database for the main window to then insert data read from the file into the database and retrieve data based on what was selected by the user. Clicking "Sign in" should close the Login Window and open the Main Windows which shows a progress bar while inserting and should display results which users can sort by ( have not implemented it yet.
What are ways I could improve my program? My program sometimes hangs.
How could I go about my approach?
Is there an equivalent to Java's JFrame.dispose() in Python which closes then Window and clicking on a button?
The Login Window:
import sys
from PyQt4 import QtGui, QtCore
from PyQt4.Qt import QPushButton, QLabel
from PyQt4.QtGui import QPlainTextEdit
from PyQt4.QtGui import QLineEdit
from MainGUI import MainGUI
import time
class LoginGUI(QtGui.QMainWindow):
def __init__(self):
super(LoginGUI, self).__init__()
self.setGeometry(730, 350, 500, 300)
self.setWindowTitle("Login")
self.initGUI()
def initGUI(self):
titleLabel = QLabel("Login", self)
titleLabel.move(200, 20)
titleLabel.setFont(QtGui.QFont("", 20))
loginLabel = QLabel("Username: ", self)
loginLabel.move(135, 120)
passwordLabel = QLabel("Password: ", self)
passwordLabel.move(135, 150)
loginText = QPlainTextEdit("root", self)
loginText.move(195, 120)
passwordText = QLineEdit("",self)
passwordText.move(195, 150)
passwordText.setEchoMode(QtGui.QLineEdit.Password)
loginBtn = QtGui.QPushButton("Sign in", self)
loginBtn.clicked.connect(lambda:
self.connectToDB(loginText.toPlainText(), passwordText.text()))
loginBtn.resize(loginBtn.sizeHint())
loginBtn.move(170, 250)
quitBtn = QtGui.QPushButton("Quit", self)
quitBtn.clicked.connect(QtCore.QCoreApplication.instance().quit)
quitBtn.resize(quitBtn.sizeHint())
quitBtn.move(245,250)
self.show()
def connectToDB(self,username,password):
pmg = MainGUI()
pmg.prep("D:\\folder\\data.csv", username, password)
pmg.run()
#logonGUI.close()
#mainApp = QtGui.QApplication(sys.argv)
#mainGUI = MainGUI()
#sys.exit(app.exec_())
#return pmg
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
logonGUI = LoginGUI()
sys.exit(app.exec_())
The Main Window:
other imports
import sys
from PyQt4 import QtGui, QtCore
from PyQt4.Qt import QPushButton, QLabel
from PyQt4.QtGui import QPlainTextEdit
from PyQt4.QtCore import QEventLoop
from viewer.DataSource import DataSource
class MainGUI(QtGui.QMainWindow):
theFile = None
username = None
password = None
source = None
con = None
rowsInserted = 0
progressBar = None
completed = 0
def __init__(self):
#app = QtGui.QApplication(sys.argv)
#mainGUI = MainGUI()
#sys.exit(app.exec_()).
super(MainGUI, self).__init__()
self.setGeometry(730, 350, 1000, 600)
self.setWindowTitle("MainWindow")
self.initGUI()
#self.show()
def prep(self, x, username, password):
try:
self.theFile = open(x, "r")
self.username = username
self.password = password
#Connect to db and pass connection to each class.
#Close connection at the end in a Finally statement.
self.source = DataSource()
self.con = self.source.getConnection(username, password)
except FileNotFoundError:
print("No file of {} found.".format(x))
def initGUI(self):
titleLabel = QLabel("MainWindow", self)
titleLabel.resize(200, 20)
titleLabel.move(450, 30)
titleLabel.setFont(QtGui.QFont("", 20))
quitBtn = QtGui.QPushButton("Quit", self)
quitBtn.clicked.connect(QtCore.QCoreApplication.instance().quit)
quitBtn.resize(quitBtn.sizeHint())
quitBtn.move(800,550)
self.progressBar = QtGui.QProgressBar(self)
self.progressBar.setGeometry(200, 80, 250, 20)
def run(self):
with self.theFile as data:
lines = data.readlines()[1:]
for line in lines:
QtCore.QCoreApplication.processEvents(flags=QEventLoop.AllEvents)
#Line with QtCore supposed to be indented.
cleanDataFromDB(self.con)
insertData(self.con)
dao.retrieve(userInput)
try:
if self.con != None:
self.con.close()
except:
print("Error closing the database.")
I have found my solution.
I have made my instance of the MainGUI global so that it doesn't close.
def connectToDB(self,username,password):
global pmg
pmg = MainGUI()
pmg.prep("D:\\folder\\data.csv", username, password)
pmg.run()
self.close()
I have made some changes to my MainGUI itself by adding some show() statements to the title and button for before in initGUI() and after the loop of run(), and having repaint() after the show() after the loop.
def initGUI(self):
titleLabel = QLabel("MainWindow", self)
titleLabel.resize(200, 20)
titleLabel.move(450, 30)
titleLabel.setFont(QtGui.QFont("", 20))
titleLabel.hide()
quitBtn = QtGui.QPushButton("Quit", self)
quitBtn.clicked.connect(QtCore.QCoreApplication.instance().quit)
quitBtn.resize(quitBtn.sizeHint())
quitBtn.move(800,550)
quitBtn.hide()
self.progressBar = QtGui.QProgressBar(self)
self.progressBar.setGeometry(200, 80, 250, 20)
self.progressBar.show()
def run(self):
with self.theFile as data:
lines = data.readlines()[1:]
for line in lines:
QtCore.QCoreApplication.processEvents()
cleanDataFromDB(self.con)
insertData(self.con)
progressBar.hide()
titleLabel.show()
quitBtn.show()
self.repaint()
Thanks for taking your time to help me. :D

How to communicate or switch between two windows in PyQt?

I am developing an application using python and Qt.
I have designed 2 Main windows ie..QMainWindow (Not QWidget or QDialog) using Qt.
Let it be.
1.LoginWindow -- LoginUI(Qt)
2.StuffWindow --- StuffUI
First i should display Login Window.
Then i should pass the username to StaffWindow (username needed for managing stuffs)
StaffWindow should be shown and LoginWindow Should be Closed..
How can i achieve this..? Help me..
Regardless of your description, I think your LoginWindow should be a QDialog, and your StuffWIndow be the MainWindow, and function like this...
Your StuffWindow MainWindow should be created (not shown)
Call a login() method that creates and exec_() your login QDialog as a application MODAL dialog
Start the app.exec_() event loop now, and wait for the user to interact with login
User interacts with login dialog, and the result of the dialog closing will then allow your app to check its values and choose to show its main interface.
Here is a quick outline:
class MainWindow():
def login():
loginDialog = LoginDialog()
# this is modal. wait for it to close
if loginDialog.exec_():
# dialog was accepted. check its values and maybe:
self.show()
else:
# maybe reshow the login dialog if they rejected it?
loginDialog.exec_()
if __name__ == "__main__":
app = QApp
win = MainWindow()
win.login()
app.exec_()
I do agree most of the points jdi raised, but I prefer a slightly different approach.
LoginWindow should be a QDialog started as MODAL.
Check the return of exec_() (i.e. accept/reject) for login or cancel/quit.
Check the login inside the LoginWindow
If successful login, launch MainWindow with parameters supplied
I started coding a simple example before seeing jdi's answer. I might as well put it here.
import sys
from PyQt4 import QtGui, QtCore
class LoginDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(LoginDialog, self).__init__(parent)
self.username = QtGui.QLineEdit()
self.password = QtGui.QLineEdit()
loginLayout = QtGui.QFormLayout()
loginLayout.addRow("Username", self.username)
loginLayout.addRow("Password", self.password)
self.buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
self.buttons.accepted.connect(self.check)
self.buttons.rejected.connect(self.reject)
layout = QtGui.QVBoxLayout()
layout.addLayout(loginLayout)
layout.addWidget(self.buttons)
self.setLayout(layout)
def check(self):
if str(self.password.text()) == "12345": # do actual login check
self.accept()
else:
pass # or inform the user about bad username/password
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.label = QtGui.QLabel()
self.setCentralWidget(self.label)
def setUsername(self, username):
# do whatever you want with the username
self.username = username
self.label.setText("Username entered: %s" % self.username)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
login = LoginDialog()
if not login.exec_(): # 'reject': user pressed 'Cancel', so quit
sys.exit(-1)
# 'accept': continue
main = MainWindow()
main.setUsername(login.username.text()) # get the username, and supply it to main window
main.show()
sys.exit(app.exec_())
Although this is not directly relevant to your question, you should always set QLineEdit.EchoMode for the password field as follows (see here):
self.password.setEchoMode(QtGui.QLineEdit.Password)
This is PyQt5 updated version of Avaris. Some exception handling was added to show how to catch a few errors (while coding your thing. Enjoy!
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Ref to this OP question. https://stackoverflow.com/questions/9689053/how-to-communicate-or-switch-between-two-windows-in-pyqt4
import sys
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QDialog, QDialogButtonBox, QFormLayout, QLabel, QLineEdit, QWidget, QVBoxLayout
class LoginDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(LoginDialog, self).__init__(parent)
self.username = QLineEdit()
self.password = QLineEdit()
loginLayout = QFormLayout()
loginLayout.addRow("Username", self.username)
loginLayout.addRow("Password", self.password)
self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttons.accepted.connect(self.check)
self.buttons.rejected.connect(self.reject)
layout = QVBoxLayout()
layout.addLayout(loginLayout)
layout.addWidget(self.buttons)
self.setLayout(layout)
def check(self):
if str(self.password.text()) == "12345": # do actual login check
self.accept()
else:
pass # or inform the user about bad username/password
def my_exception_hook(exctype, value, traceback):
# Print the error and traceback
print(exctype, value, traceback)
# Call the normal Exception hook after
sys._excepthook(exctype, value, traceback)
sys.exit(1)
# Back up the reference to the exceptionhook
sys._excepthook = sys.excepthook
# Set the exception hook to our wrapping function
sys.excepthook = my_exception_hook
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.label = QLabel()
self.setCentralWidget(self.label)
def setUsername(self, username):
# do whatever you want with the username
self.username = username
self.label.setText("Username entered: %s" % self.username)
if __name__ == "__main__":
app = QApplication(sys.argv)
login = LoginDialog()
if not login.exec_(): # 'reject': user pressed 'Cancel', so quit
sys.exit(-1) # instead of -1 another action can be triggered here.
# 'accept': continue
main = MainWindow()
# get the username, and supply it to main window
main.setUsername(login.username.text())
main.show()
sys.exit(app.exec_())
to match username and password.
import sys
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QDialog, QDialogButtonBox, QFormLayout, QLabel, QLineEdit, QWidget, QVBoxLayout, QMessageBox
class LoginDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(LoginDialog, self).__init__(parent)
self.username = QLineEdit()
self.password = QLineEdit()
loginLayout = QFormLayout()
loginLayout.addRow("Username", self.username)
loginLayout.addRow("Password", self.password)
self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttons.accepted.connect(self.check)
self.buttons.rejected.connect(self.reject)
layout = QVBoxLayout()
layout.addLayout(loginLayout)
layout.addWidget(self.buttons)
self.setLayout(layout)
def check(self):
if str(self.username.text()) == "foo" and str(self.password.text()) == "bar": # do actual login check
self.accept()
else:
QMessageBox.warning(
self, 'Error', 'Bad user or password')
pass # or inform the user about bad username/password
def my_exception_hook(exctype, value, traceback):
# Print the error and traceback
print(exctype, value, traceback)
# Call the normal Exception hook after
sys._excepthook(exctype, value, traceback)
sys.exit(1)
# Back up the reference to the exceptionhook
sys._excepthook = sys.excepthook
# Set the exception hook to our wrapping function
sys.excepthook = my_exception_hook
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.label = QLabel()
self.setCentralWidget(self.label)
def setUsername(self, username):
# do whatever you want with the username
self.username = username
self.label.setText("%s%s%s" % ("Username entered: ", self.username, "\npassword ok!"))
if __name__ == "__main__":
app = QApplication(sys.argv)
login = LoginDialog()
if not login.exec_(): # 'reject': user pressed 'Cancel', so quit
sys.exit(-1) # instead of -1 another action can be triggered here.
# 'accept': continue
main = MainWindow()
# get the username, and supply it to main window
main.setUsername(login.username.text())
main.show()
sys.exit(app.exec_())

Categories

Resources