Related
i used a class from stackoverflow that applies the drag and drop feature to table rows but it doesn't save images from being removed from cells when dragging and dropping any row i want some help figuring out how to fix the code to do this jop right !
the code of the whole project with database:
database used: https://gofile.io/d/c0srQ0
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import (QApplication, QComboBox, QDialog,
QDialogButtonBox, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout,
QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, QSpinBox, QTextEdit,
QVBoxLayout, QStyledItemDelegate, QCheckBox, QTableWidgetItem, QAbstractItemView, QTableWidget)
import sqlite3
import random
import ast
#import qdarkstyle
class Main(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.cashed = []
self.setWindowTitle('Arabic Math Project')
self.setContentsMargins(20,20,20,20)
# create table:
#Options()
#self.setStyleSheet(qdarkstyle.load_stylesheet())
layout = QVBoxLayout()
self.searchBar = QtWidgets.QLineEdit()
self.searchBar.setAlignment(QtCore.Qt.AlignCenter)
self.searchBar.setPlaceholderText("search")
self.searchBar.textChanged.connect(self.startSearchThreading) #editingFinished()
#self.table = QtWidgets.QTableWidget()
self.table = TableWidgetDragRows()
conn = sqlite3.connect("math.db")
conn.text_factory = bytes
self.cur = conn.cursor()
data = self.cur.execute("select * from problems;").fetchall();conn.close()
self.dims = (lambda x: (len(x), len(x[0])))(data) #(rows number, col number)
[self.table.insertRow(i) for i in [i for i in range(self.dims[0])]]
[self.table.insertColumn(i) for i in [i for i in range(self.dims[1]+1)]]
#changing h-header names .
self.table.setHorizontalHeaderLabels(["Unique", "Image", "Test", "Year", "Lesson", "Page","Comment", "Options", "Options-advanced"])
for c in range(self.dims[1]):
for r in range(self.dims[0]):
if c!=1:self.table.setItem(r, c, QtWidgets.QTableWidgetItem(data[r][c].decode("utf-8")))
else:self.table.setCellWidget(r, c, self.getImage(data[r][c]))
for r in range(self.dims[0]):self.table.setCellWidget(r, self.dims[1], Options(ops=[data[r][self.dims[1]-1].decode("utf-8")], row=data[r]))
#table h & v auto - resizing .
header = self.table.horizontalHeader()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setSectionResizeMode(2, QtWidgets.QHeaderView.Interactive)
header.setSectionResizeMode(7, QtWidgets.QHeaderView.Interactive)
headerv = self.table.verticalHeader()
headerv.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
#hide the options column cuz it doesn't concern user to see it.
self.table.setColumnHidden(7, True)
#layout setting up.
layout.addWidget(self.searchBar)
layout.addWidget(self.table)
self.setLayout(layout)
def getImage(self, image):
imageLabel = ScaledPixmapLabel()
pixmap = QtGui.QPixmap()
pixmap.loadFromData(image, "jpg")
imageLabel.setPixmap(pixmap)
return imageLabel
def startSearchThreading(self):
self.table.setRowCount(0)
self.update = updateTable(data= self.searchBar.text())
self.update.new_signal.connect(self.Search)
self.update.start()
def create(self):
self.createFormGroupBox()
return self.formGroupBox
#QtCore.pyqtSlot(int, int, bytes, bool, bytes)
def Search(self, r, c, d, newRowOrder, ops):
if newRowOrder:self.table.insertRow(self.table.rowCount()) # create new row.
if c!=1:self.table.setItem(r, c, QtWidgets.QTableWidgetItem(d.decode("utf-8")))
else:self.table.setCellWidget(r, c, self.getImage(d))
self.cashed.append(d)
if c ==self.dims[1]-1:
self.table.setCellWidget(r, self.dims[1], Options(ops=[ops.decode("utf-8")], row=self.cashed))
self.cashed = []
class updateTable(QtCore.QThread):
def __init__(self, parent=None, data=True):
super(QtCore.QThread, self).__init__()
self.data = data
new_signal = QtCore.pyqtSignal(int, int, bytes, bool, bytes)
def run(self):
currRow = 0
conn = sqlite3.connect("math.db")
conn.text_factory = bytes
self.cur = conn.cursor()
searched = self.cur.execute(" SELECT * FROM problems WHERE text LIKE ('%' || ? || '%') ", (self.data,)).fetchall()
dims = (lambda x: (len(x), len(x[0])))(searched) if searched!=[] else (0, 0)
for r in range(dims[0]):
for c in range(dims[1]):
self.new_signal.emit(r, c, searched[r][c], True if c==0 else False, searched[r][dims[1]-1])
class ScaledPixmapLabel(QtWidgets.QLabel):
def __init__(self):
super().__init__()
self.setScaledContents(True)
def paintEvent(self, event):
if self.pixmap():
pm = self.pixmap()
originalRatio = pm.width() / pm.height()
currentRatio = self.width() / self.height()
if originalRatio != currentRatio:
qp = QtGui.QPainter(self)
pm = self.pixmap().scaled(self.size(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
rect = QtCore.QRect(0, 0, pm.width(), pm.height())
rect.moveCenter(self.rect().center())
qp.drawPixmap(rect, pm)
return
super().paintEvent(event)
class Options(QtWidgets.QWidget):
def __init__(self, parent=None,row=[], ops=[]):
super(Options, self).__init__(parent)
self.i = random.randint(1, 100)
self.row = [j.decode("utf-8") if i!=1 else "image" for i,j in enumerate(row)]
self.formLayout = QFormLayout()
self.setStyleSheet(open("styles.css").read())
ops = ast.literal_eval(ops[0]) if ops!=[] else {}
for i,j in ops.items():
widget = {"lineedit": QLineEdit(), "combobox": QComboBox(), "spinbox": QSpinBox(), "checkbox": QCheckBox()}
title = j.get("title", "")
combos = j.get("combos", [])
if i == "lineedit":widget[i].setText(j.get("default", ""))
elif i == "combobox":widget[i].addItems(combos)
elif i == "checkbox":
widget[i].setCheckState((lambda x: {1:2, 2:2, 0:0}[x])(j.get("default", 0)))
widget[i].setText(j.get("text", ""))
elif i == "spinbox":
widget[i].setMaximum(j.get("max", 100))
widget[i].setMinimum(j.get("min", 0))
self.formLayout.addRow(QLabel(title), widget[i])
self.btn = QtWidgets.QPushButton("Submit")
self.btn.clicked.connect(self.do)
self.formLayout.addWidget(self.btn)
self.formLayout.setFormAlignment(QtCore.Qt.AlignVCenter)
self.setLayout(self.formLayout)
def do(self):
#[STOPED HERE]
heads, tails = [], []
for i in range(self.formLayout.count()):
w = self.formLayout.itemAt(i).widget()
if i%2==0:heads.append(w.text())
elif isinstance(w, QLineEdit):tails.append(w.text())
elif isinstance(w, QComboBox):tails.append(w.currentText())
elif isinstance(w, QSpinBox):tails.append(w.value())
elif isinstance(w, QCheckBox):tails.append(w.checkState())
values = {i:j for i,j in zip(heads, tails)}
print(self.row[:-1]+[values])
class TableWidgetDragRows(QTableWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setDragEnabled(True)
self.setAcceptDrops(True)
self.viewport().setAcceptDrops(True)
self.setDragDropOverwriteMode(False)
self.setDropIndicatorShown(True)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
#self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setDragDropMode(QAbstractItemView.InternalMove)
def dropEvent(self, event: QDropEvent):
if not event.isAccepted() and event.source() == self:
drop_row = self.drop_on(event)
rows = sorted(set(item.row() for item in self.selectedItems()))
print(rows)
rows_to_move = [[QTableWidgetItem(self.item(row_index, column_index)) for column_index in range(self.columnCount())]
for row_index in rows]
for row_index in reversed(rows):
self.removeRow(row_index)
if row_index < drop_row:
drop_row -= 1
for row_index, data in enumerate(rows_to_move):
row_index += drop_row
self.insertRow(row_index)
for column_index, column_data in enumerate(data):
self.setItem(row_index, column_index, column_data)
event.accept()
for row_index in range(len(rows_to_move)):
self.item(drop_row + row_index, 0).setSelected(True)
self.item(drop_row + row_index, 1).setSelected(True)
super().dropEvent(event)
def drop_on(self, event):
index = self.indexAt(event.pos())
if not index.isValid():
return self.rowCount()
return index.row() + 1 if self.is_below(event.pos(), index) else index.row()
def is_below(self, pos, index):
rect = self.visualRect(index)
margin = 2
if pos.y() - rect.top() < margin:
return False
elif rect.bottom() - pos.y() < margin:
return True
# noinspection PyTypeChecker
return rect.contains(pos, True) and not (int(self.model().flags(index)) & Qt.ItemIsDropEnabled) and pos.y() >= rect.center().y()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
main.resize(600,600)
main.show()
app.exec_()
You are setting images using QLabels set as cell widgets. Setting cell widgets is only a feature used by the table UI: it is completely unrelated to the underlying model and the QTableWidgetItems know nothing about them also. So, it's completely up to you to check if a cell has a widget set and move that widget accordingly.
Semi pseudo-code:
def dropEvent(self, event: QDropEvent):
if not event.isAccepted() and event.source() == self:
drop_row = self.drop_on(event)
rows = sorted(set(item.row() for item in self.selectedItems()))
print(rows)
rows_to_move = []
images = []
for row_index in rows:
items = []
for column_index in range(self.columnCount()):
items.append(QTableWidgetItem(
self.item(row_index, column_index)))
widget = self.cellWidget(row_index, column_index)
if isinstance(widget, ScaledPixmapLabel):
images.append((row_index, column_index, widget.pixmap()))
rows_to_move.append(items)
# ...
for row, column, pixmap in widgets:
self.setCellWidget(row + drop_row, column,
ScaledPixmapLabel(pixmap))
As you can see I recreated the label instance each time, that's because when you set a cell widget, the table takes its ownership. While you could reparent the widget, the result might be undefined, so it's better to create a brand new instance based on the pixmap.
Tip: avoid using inline if/else or for loops: there's absolutely no benefit in doing it, and the only result is that it makes your code much less readable
I would like to load a file and runs it as well as I can run when it's recorded.
That's a recorder for mouse and keyboard together, there's the open code link: (you can run that for testing)
I already validated the buttons.setDisabled to display False or True for not returning else errors
I rather to solve the load file problem
I can't run my loaded file
If you guys have a better answer, don't be ashemed of sharing it. Wish you can help me
> # there goes the MAIN open source CODE which mine is based on
import sys
from time import sleep
import pandas as pd
from pynput import mouse, keyboard
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton,
QDesktopWidget, QGridLayout, QLabel,
QSpinBox, QFileDialog,
QTableWidget, QTableWidgetItem)
from PyQt5.QtGui import QColor
from PyQt5.QtCore import Qt
class App(QWidget):
def __init__(self, df=None):
super().__init__()
self.title = 'Clicker'
self.width = 800
self.height = 600
if df is None:
self.keyEvents = pd.DataFrame(columns=['Type', 'Button', 'Coordinates', 'WaitTime'])
else:
self.keyEvents = df
self.runTimes = 1
self.skipFirst = 0
self.mouseWait = 300
self.keyWait = 200
self.mListener = mouse.Listener(on_click=self.on_click)
self.kListener = keyboard.Listener(on_press=self.on_press, on_release=self.on_release)
self.initUI()
def initUI(self):
grid = QGridLayout()
grid.setRowStretch(12,1)
grid.setColumnStretch(4,1)
self.setLayout(grid)
self.recordButton = QPushButton('Start recording')
grid.addWidget(self.recordButton, 0, 0)
self.recordButton.clicked.connect(self.start_record)
self.recordLabel = QLabel('')
grid.addWidget(self.recordLabel, 0, 1)
self.stopButton = QPushButton('Stop/pause\nrecording')
grid.addWidget(self.stopButton, 1, 0)
self.stopButton.clicked.connect(self.stop_record)
self.playButton = QPushButton('Run')
grid.addWidget(self.playButton, 2, 0)
self.playButton.clicked.connect(self.play)
self.playBox = QSpinBox()
grid.addWidget(self.playBox, 3, 1)
self.playBox.setMinimum(1)
self.playBox.setMaximum(1313)
self.playBox.setValue(self.runTimes)
self.playBox.valueChanged.connect(self.runTimes_update)
grid.addWidget(QLabel('Run the commands .. times'), 3, 0)
grid.addWidget(QLabel('Do not include the first ..\n'
'commands when running\n'
'multiple times'), 4, 0)
self.skipBox = QSpinBox()
grid.addWidget(self.skipBox, 4, 1)
self.skipBox.setMinimum(0)
self.skipBox.setMaximum(0)
self.skipBox.setValue(self.skipFirst)
self.skipBox.valueChanged.connect(self.skipFirst_update)
grid.addWidget(QLabel('Default wait-time for\nmouseclicks'), 5, 0)
grid.addWidget(QLabel('ms'), 5, 2)
self.mouseBox = QSpinBox()
grid.addWidget(self.mouseBox, 5, 1)
self.mouseBox.setMinimum(0)
self.mouseBox.setMaximum(100000)
self.mouseBox.setSingleStep(50)
self.mouseBox.setValue(self.mouseWait)
self.mouseBox.valueChanged.connect(self.mouseWait_update)
grid.addWidget(QLabel('Default wait-time for\nkeyboard inputs'), 6, 0)
grid.addWidget(QLabel('ms'), 6, 2)
self.keyBox = QSpinBox()
grid.addWidget(self.keyBox, 6, 1)
self.keyBox.setMinimum(0)
self.keyBox.setMaximum(100000)
self.keyBox.setSingleStep(50)
self.keyBox.setValue(self.keyWait)
self.keyBox.valueChanged.connect(self.keyWait_update)
self.emptyButton = QPushButton('Delete all data')
grid.addWidget(self.emptyButton, 8, 0)
self.emptyButton.clicked.connect(self.empty_events)
self.emptyButton2 = QPushButton('Delete row:')
self.emptyButton2.setToolTip('Deletes this row number when pressed')
grid.addWidget(self.emptyButton2, 9, 0)
self.emptyButton2.clicked.connect(self.del_row)
self.delBox = QSpinBox()
grid.addWidget(self.delBox, 9, 1)
self.delBox.setMinimum(1)
self.saveButton = QPushButton('Save')
grid.addWidget(self.saveButton, 11, 0)
self.saveButton.clicked.connect(self.file_save)
self.loadButton = QPushButton('Load')
grid.addWidget(self.loadButton, 12, 0)
self.loadButton.clicked.connect(self.file_load)
self.table = QTableWidget(1, len(self.keyEvents.columns))
self.table.setHorizontalHeaderLabels(self.keyEvents.columns)
self.table.itemSelectionChanged.connect(self.change_table)
grid.addWidget(self.table, 0, 4, 12, 1)
self.update_table()
grid.addWidget(QLabel('Select another cell after changing a wait-time '
'otherwise it will not be registered!'), 12, 4)
self.setWindowTitle(self.title)
self.resize(self.width, self.height)
self.center()
self.show()
def keyWait_update(self):
oldV = self.keyWait
self.keyWait = self.keyBox.value()
for i, row in self.keyEvents.iterrows():
if (not type(row.Coordinates) is tuple and
row.WaitTime == oldV/1000 and
row.Type == 'Press'):
self.keyEvents.loc[i, 'WaitTime'] = self.keyWait/1000
self.update_table()
def mouseWait_update(self):
oldV = self.mouseWait
self.mouseWait = self.mouseBox.value()
for i, row in self.keyEvents.iterrows():
if (type(row.Coordinates) is tuple and
row.WaitTime == oldV/1000 and
row.Type == 'Press'):
self.keyEvents.loc[i, 'WaitTime'] = self.mouseWait/1000
self.update_table()
def file_save(self):
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
fileName, _ = QFileDialog.getSaveFileName(self,"QFileDialog.getSaveFileName()","","All Files (*);;CSV Files (*.csv)", options=options)
if fileName:
self.keyEvents.to_csv(fileName if fileName.endswith('.csv') else
fileName+'.csv', index=False)
def file_load(self):
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
files, _ = QFileDialog.getOpenFileNames(self,"QFileDialog.getOpenFileNames()", "","All Files (*);;CSV Files (*.csv)", options=options)
if len(files) == 1 and files[0].endswith('.csv'):
self.keyEvents = pd.read_csv(files[0])
self.update_table()
def change_table(self):
for i, row in self.keyEvents.iterrows():
self.keyEvents.loc[i, 'WaitTime'] = float(self.table.item(i, 3).text())
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def start_record(self):
self.recordLabel.setText('<font color="red"><b>Rec.</b></font>')
self.mListener.start()
self.kListener.start()
def stop_record(self):
if self.mListener.running or self.kListener.running:
self.recordLabel.setText('')
self.mListener.stop()
self.kListener.stop()
self.mListener = mouse.Listener(on_click=self.on_click)
self.kListener = keyboard.Listener(on_press=self.on_press, on_release=self.on_release)
self.keyEvents = self.keyEvents.iloc[:-2]
self.update_table()
def runTimes_update(self):
self.runTimes = self.playBox.value()
def skipFirst_update(self):
self.skipFirst = self.skipBox.value()
self.update_table()
def play(self):
if len(self.keyEvents) == 0:
print('There are no logged clicks/keypresses!')
self.runLabel.setText('')
return
if self.mListener.running or self.kListener.running:
self.stop_record()
kController = keyboard.Controller()
mController = mouse.Controller()
for run in range(self.runTimes):
rows = self.keyEvents[self.skipFirst:]
if run == 0:
rows = self.keyEvents
for i, row in rows.iterrows():
sleep(row.WaitTime)
if type(row.Coordinates) is tuple:
mController.position = row.Coordinates
if row.Type == 'Press':
mController.press(row.Button)
elif row.Type == 'Release':
mController.release(row.Button)
else:
if row.Type == 'Press':
kController.press(row.Button)
elif row.Type == 'Release':
kController.release(row.Button)
def empty_events(self):
if self.mListener.running or self.kListener.running:
self.stop_record()
self.keyEvents = self.keyEvents.iloc[0:0]
self.update_table()
def del_row(self):
self.keyEvents = self.keyEvents.drop(self.delBox.value()-1)
self.keyEvents = self.keyEvents.reset_index(drop=True)
self.update_table()
def on_click(self, x, y, button, pressed):
self.keyEvents = self.keyEvents.append(
{'Type': 'Press' if pressed else 'Release',
'Coordinates': (x, y),
'Button': button,
'WaitTime': self.mouseWait/1000 if pressed else 0
}, ignore_index=True)
def on_press(self, key):
self.keyEvents = self.keyEvents.append(
{'Type': 'Press',
'Button': key,
'WaitTime': self.keyWait/1000
}, ignore_index=True)
def on_release(self, key):
self.keyEvents = self.keyEvents.append(
{'Type': 'Release',
'Button': key,
'WaitTime': 0
}, ignore_index=True)
def update_table(self):
self.skipBox.setMaximum(max(0, len(self.keyEvents)-1))
self.delBox.setMaximum(max(1, len(self.keyEvents)))
self.table.setRowCount(len(self.keyEvents))
for i, row in self.keyEvents.iterrows():
for j, data in enumerate(row):
item = QTableWidgetItem(str(data))
if j != 3:
item.setFlags(Qt.ItemIsEnabled)
color = QColor(255,255,255)
if i < self.skipFirst:
color = QColor(255,0,0)
item.setBackground(color)
self.table.setItem(i, j, item)
def closeEvent(self, event):
self.stop_record()
event.accept()
if __name__ == '__main__':
if not QApplication.instance():
app = QApplication(sys.argv)
else:
app = QApplication.instance()
ex = App()
app.exec_()
I want to select a column on a right click and afterwards a context menu has to provide a delete entry for deleting the selected column.
With the transportet QPoint arg the mouse position is sent to the slot. But i need the clicked column.
How to fix it?
To obtain the column you must use the pressed item that is obtained using the itemAt() method.
It is selected using the setRangeSelected method by passing it QTableWidgetSelectionRange, which has the column as data.
Then you create the QMenu and the exec_() method you pass the position of the signal but you must convert it to global using mapToGlobal from the viewport.
Then you delete the column using the removeColumn() method.
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.table_widget = QtWidgets.QTableWidget(20, 10)
for i in range(self.table_widget.rowCount()):
for j in range(self.table_widget.columnCount()):
it = QtWidgets.QTableWidgetItem("{}-{}".format(i, j))
self.table_widget.setItem(i, j, it)
vlay = QtWidgets.QVBoxLayout(self)
vlay.addWidget(self.table_widget)
self.table_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.table_widget.customContextMenuRequested.connect(self.on_customContextMenuRequested)
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested(self, pos):
it = self.table_widget.itemAt(pos)
if it is None: return
c = it.column()
item_range = QtWidgets.QTableWidgetSelectionRange(0, c, self.table_widget.rowCount()-1 , c)
self.table_widget.setRangeSelected(item_range, True)
menu = QtWidgets.QMenu()
delete_column_action = menu.addAction("Delete column")
action = menu.exec_(self.table_widget.viewport().mapToGlobal(pos))
if action == delete_column_action:
self.table_widget.removeColumn(c)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Ok - based on your solution (thanks a lot) - i tried a view. Mostly it does what i want. If i click on the right mouse button on a cell in the table body the cell should be selected and a context menu should be shown - ok - works. If a row or column head is under the mouse cursor the whole row (column) should be selected - works.
BUT the selection remains and that shouldn't be so!
Here's my code for the view:
import logging
import sys
from PyQt5.QtWidgets import \
QApplication, QWidget, QVBoxLayout, QMenu, \
QTableWidget, QTableWidgetItem, QTableWidgetSelectionRange
from PyQt5.QtCore import \
QPoint, Qt
class View(QWidget):
def __init__(self, app, parent=None):
super().__init__(parent)
self.app = app
# gui elements
self.layout = None
self.tbl = None
self.tbl_selection = None
self.tbl_item = None
self.tbl_item_pos = [-1, -1]
def _setup_layout(self):
logging.debug("<View._setup_layout>")
self.setMinimumHeight(600)
self.setMinimumWidth(800)
self.layout = QVBoxLayout()
self.setLayout(self.layout)
def _setup_table(self):
logging.debug("<View._setup_table()>")
if self.app.data:
data = self.app.data
rows, cols = len(data), len(data[0])
self.tbl = tbl = QTableWidget(rows, cols)
tbl.setHorizontalHeaderLabels(data.pop(0))
for r, ds in enumerate(data):
for c, f in enumerate(ds):
item = QTableWidgetItem(f)
tbl.setItem(r, c, item)
def _setup_widgets(self):
logging.debug("<View._setup_widgets()>")
if self.app is not None:
self._setup_table()
else:
self.tbl = QTableWidget()
tbl = self.tbl
tbl.verticalHeader().setContextMenuPolicy(
Qt.CustomContextMenu)
tbl.horizontalHeader().setContextMenuPolicy(
Qt.CustomContextMenu)
self.layout.addWidget(self.tbl)
def _connect_evt_with_handler(self):
logging.debug("<View._connect_evt_with_handler()>")
tbl = self.tbl
tbl.setContextMenuPolicy(Qt.CustomContextMenu)
tbl.customContextMenuRequested.connect(self.on_tbl_rightclick)
# event for click on row heads
tbl.verticalHeader().customContextMenuRequested.connect(
self.on_tbl_rhead_rightclick)
# event for click on col heads
tbl.horizontalHeader().customContextMenuRequested.connect(
self.on_tbl_chead_rightclick)
# protected
def _get_clicked_item(self, qpoint: QPoint) -> bool:
logging.debug("<View._get_clicked_item(qpoint={})>".format(qpoint))
self.tbl_item = item = self.tbl.itemAt(qpoint)
self.tbl_item_pos = [item.row(), item.column()]
return self.tbl_item is not None
def _set_selection(self):
logging.debug("<View._set_selection()>")
r, c = self.tbl_item_pos
logging.debug("... row={}, col={}".format(r, c))
if r >= 0 and c >= 0:
self.tbl_selection = QTableWidgetSelectionRange(
r, c, r, c)
elif c < 0:
# row head
self.tbl_selection = QTableWidgetSelectionRange(
r, 0, r, self.tbl.columnCount() - 1)
elif r < 0:
# col head
self.tbl_selection = QTableWidgetSelectionRange(
0, c, self.tbl.rowCount() - 1, c)
self.tbl.setRangeSelected(self.tbl_selection, True)
def _build_ctx_mnu(self, qpoint: QPoint):
logging.debug("<View._build_ctx_mnu(qpoint={})>".format(qpoint))
r, c = self.tbl_item_pos
logging.debug("... row={}, col={}".format(r, c))
# build menu
action_del_col = None
action_del_row = None
menu = QMenu()
if c < 0:
# row head
action_del_row = menu.addAction("Delete row")
elif r < 0:
# col head
action_del_col = menu.addAction("Delete column")
else:
# table body
action_del_col = menu.addAction("Delete column")
action_del_row = menu.addAction("Delete row")
action = menu.exec_(self.tbl.viewport().mapToGlobal(qpoint))
if action is not None and action == action_del_col:
logging.debug("... action: {}".format(action_del_col.text()))
self.tbl.removeColumn(c)
elif action is not None and action == action_del_row:
logging.debug("... action: {}".format(action_del_row.text()))
self.tbl.removeRow(r)
# UI
def setup(self):
logging.debug("<View.setup()>")
self._setup_layout()
self._setup_widgets()
self._connect_evt_with_handler()
# events
def on_tbl_rightclick(self, qpoint: QPoint):
logging.debug("<View.on_tbl_rightclick(qpoint={})>".format(qpoint))
if self._get_clicked_item(qpoint):
self._set_selection()
self._build_ctx_mnu(qpoint)
def on_tbl_rhead_rightclick(self, qpoint: QPoint):
logging.debug(
"<View.on_tbl_rhead_rightclick(qpoint={})>".format(qpoint))
if self._get_clicked_item(qpoint):
self.tbl_item_pos[1] = -1
self._set_selection()
self._build_ctx_mnu(qpoint)
def on_tbl_chead_rightclick(self, qpoint: QPoint):
logging.debug(
"<View.on_tbl_chead_rightclick(qpoint={})>".format(qpoint))
if self._get_clicked_item(qpoint):
self.tbl_item_pos[0] = -1
self._set_selection()
self._build_ctx_mnu(qpoint)
And here some date - once more:
data = [f.strip().split(",") for f in """\
id,first_name,last_name,email,gender,ip_address
1,Hillard,Tasseler,htasseler0#google.com.br,Male,104.153.237.243
2,Tyrus,Oley,toley1#ft.com,Male,163.73.24.45
3,Kora,Covil,kcovil2#privacy.gov.au,Female,158.44.166.87
4,Phineas,McEntee,pmcentee3#rambler.ru,Male,71.82.246.45
5,Dottie,Spraging,dspraging4#berkeley.edu,Female,226.138.241.22
6,Andria,Ivatts,aivatts5#about.com,Female,57.5.76.78
7,Missy,Featherstone,mfeatherstone6#unblog.fr,Female,9.56.215.203
8,Anstice,Sargant,asargant7#about.me,Female,36.115.185.109
9,Teresita,Trounce,ttrounce8#myspace.com,Female,240.228.133.166
10,Sib,Thomke,sthomke9#ibm.com,Female,129.191.2.7
11,Amery,Dallander,adallandera#elpais.com,Male,4.115.194.100
12,Rourke,Rowswell,rrowswellb#bloomberg.com,Male,48.111.190.66
13,Cloe,Benns,cbennsc#slideshare.net,Female,142.48.24.44
14,Enos,Fery,eferyd#pen.io,Male,59.19.200.235
15,Russell,Capelen,rcapelene#fc2.com,Male,38.205.20.141""".split()]
And for testing: the app object and the main function:
class App:
def __init__(self, data=()):
self.view = View(app=self)
self.data = []
if isinstance(data, (list, tuple)) and len(data) > 0:
self.set(data)
def set(self, data):
logging.debug("<App.set(data)>")
self.data = data
def setup(self):
logging.debug("<App.setup()>")
self.view.setup()
def show(self):
logging.debug("<App.show()>")
self.view.show()
def main():
logging.basicConfig(level=logging.DEBUG)
qapp = QApplication(sys.argv)
app = App(data=data)
app.setup()
app.show()
sys.exit(qapp.exec_())
main()
In the code below, when I load the GridscanCanvas' pixmap with the function get_core_data it works perfectly fine, no crashes, and the image is then displayed in my app.
However, when I want to update that same pixmap in the function get_square_data, it crashes every time.
Both times, the original image is an np.array and the only difference between them is the size of the array. I've commented on the code where it crashes.
I'm very lost, since both functions are nearly identical. Any help would be greatly appreciated!
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import os
import numpy as np
from mdoc_class import Gridscan_mdoc
from mrc_class import Gridscan_mrc
from em_ss_find_gridsquares import find_gridsquares
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setGeometry(100,100,1000,1000)
self.main_widget = QWidget(self)
self.setCentralWidget(self.main_widget)
self.gridscan_canvas = GridscanCanvas(self)
self.init_menu()
self.pixel_size = 92.7
self.gridsquare_length = 85000
self.spacing_length = 40000
self.show()
def init_menu(self):
bar = self.menuBar()
self.file = bar.addMenu('File')
load_mdoc_action = QAction('Load mdoc', self)
self.file.addAction(load_mdoc_action)
self.file.triggered.connect(self.file_respond)
find_squares_action = QAction('Find squares', self)
self.file.addAction(find_squares_action)
def file_respond(self, q):
signal = q.text()
if signal == 'Load mdoc':
mdoc_file = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('Home'),
"MDOC(*.mdoc);;AllFiles(*.*)")[0]
if mdoc_file:
try:
self.load_core_data = LoadCoreData(mdoc_file)
except Exception as e:
print(e)
return
try:
self.load_core_data.new_output.connect(self.get_core_data)
except Exception as e:
print(e)
return
try:
self.load_core_data.start()
except Exception as e:
print(e)
return
elif signal == 'Find squares':
try:
self.find_squares = FindGridsquares(self.mrc.gridscan, self.pixel_size, self.gridsquare_length,
self.spacing_length)
except Exception as e:
print(e)
return
try:
self.find_squares.new_output.connect(self.get_square_data)
except Exception as e:
print(e)
return
try:
self.find_squares.start()
except Exception as e:
print(e)
return
else:
return
def get_core_data(self, core_data):
self.mrc = core_data['mrc']
self.mdoc = core_data['mdoc']
# This here works fine
self.gridscan_canvas.pixmap = QPixmap(QImage(self.mrc.gridscan, self.mrc.gridscan.shape[1],
self.mrc.gridscan.shape[0], QImage.Format_Grayscale8))
self.gridscan_canvas.update()
def get_square_data(self, square_data):
self.centres = square_data['centres']
self.aligned_centres = square_data['aligned_centres']
self.aligned_gridscan = square_data['aligned_gridscan']
self.rot_angle = square_data['rot_angle']
self.sqrd_centres = square_data['sqrd_centres']
# This here crashes is where it crashes
self.gridscan_canvas.pixmap = QPixmap(QImage(self.aligned_gridscan, self.aligned_gridscan.shape[1],
self.aligned_gridscan.shape[0], QImage.Format_Grayscale8))
self.gridscan_canvas.update()
class GridscanCanvas(QWidget):
DELTA = 10
def __init__(self, *args, **kwargs):
super(GridscanCanvas, self).__init__(*args, **kwargs)
self.draggin_idx = -1
self.points_old = np.array([[v * 5, v * 5] for v in range(75)], dtype=np.float)
self.width = 700
self.height = 700
self.setFixedSize(self.width, self.height)
self.pixmap = None
self.points = None
self.scaling_factor = [1, 1]
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
if self.pixmap is not None:
self.pixmap = self.pixmap.scaled(self.width, self.height, transformMode=Qt.SmoothTransformation)
qp.drawPixmap(0, 0, self.width, self.height, self.pixmap)
else:
self.scaling_factor = [1, 1]
if self.points is not None:
self.drawPoints(qp)
qp.end()
def drawPoints(self, qp):
pen = QPen()
pen.setWidth(5)
pen.setColor(Qt.red)
qp.setPen(pen)
for x,y in self.points:
qp.drawPoint(x,y)
class LoadCoreData(QThread):
new_output = pyqtSignal(object)
def __init__(self, mdoc_filepath):
QThread.__init__(self)
self.isRunning = True
self.mdoc_filepath = mdoc_filepath
def __del__(self):
self.wait()
def run(self):
mdoc = Gridscan_mdoc(self.mdoc_filepath)
mrc = Gridscan_mrc(mdoc)
output = {'mdoc': mdoc, 'mrc': mrc}
self.new_output.emit(output)
class FindGridsquares(QThread):
new_output = pyqtSignal(object)
def __init__(self, gridscan, pixel_size, gridsquare_length, spacing_length):
QThread.__init__(self)
self.isRunning = True
self.gridscan = gridscan
self.pixel_size = pixel_size
self.gridsquare_length = gridsquare_length
self.spacing_length = spacing_length
def __del__(self):
self.wait()
def run(self):
centres, aligned_centres, aligned_gridscan, rot_angle, sqrd_centres =find_gridsquares(self.gridscan, self.pixel_size, self.gridsquare_length, self.spacing_length)
output = {'centres': centres, 'aligned_centres': aligned_centres, 'aligned_gridscan': aligned_gridscan, 'rot_angle': rot_angle, 'sqrd_centres': sqrd_centres}
self.new_output.emit(output)
app = QApplication([])
window = MainWindow()
app.exec_()
So I'm trying to make a simple file downloader in Python 3.4.2 and PyQt5
QThreads seems to be the way but there's no tutorials online or examples that I could understand for PyQt5. All I could find was Qt5 Reference for C/C++ and bunch of PyQt4 tutorials that don't work for PyQt5 and Python 3
Here's the GUI screenshot: http://i.imgur.com/KGjqRRK.png
And here's my code:
#!usr/bin/env python3
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from string import Template
import urllib.request
import sys
class Form(QWidget):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
lblUrl= QLabel("File URL:")
self.txtURL = QLineEdit()
self.bttDL = QPushButton("&Download")
self.pbar = QProgressBar()
self.pbar.setMinimum(0)
buttonLayout1 = QVBoxLayout()
buttonLayout1.addWidget(lblUrl)
buttonLayout1.addWidget(self.txtURL)
buttonLayout1.addWidget(self.bttDL)
buttonLayout1.addWidget(self.pbar)
self.bttDL.clicked.connect(self.bttPush)
mainLayout = QGridLayout()
mainLayout.addLayout(buttonLayout1, 0, 1)
self.setLayout(mainLayout)
self.setWindowTitle("pySFD")
def bttPush(self):
# check if the download is already running or just disable the button
# while it's running
url = self.txtURL.text()
if url == "":
QMessageBox.information(self, "Empty URL",
"Please enter the URL of the file you want to download.")
return
else:
filename = str(QFileDialog.getSaveFileName(self, 'Choose the download location and file name', '.'))
filename = filename[:-6]
filename = filename.split("('",maxsplit=1)[1]
self.dlThread = downloaderThread()
self.dlThread.connect(dlThread.run)
self.dlThread.start()
self.dlThread.emit(url)
class downloaderThread(QThread):
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.wait()
def run(self, dlLink):
while dlLink.length == 0:
QThread.sleep(1)
pass
def report(block_count, block_size, total_size):
if block_count == 0:
self.pbar.setValue(0)
if (block_count * block_size) == total_size:
QMessageBox.information(self,"Done!","Download finished!")
return
self.pbar.setValue(self.pbar.value() + block_size)
urllib.request.urlretrieve(dlLink, filename, reporthook=report)
self.terminate()
if __name__ == '__main__':
app = QApplication(sys.argv)
screen = Form()
screen.show()
sys.exit(app.exec_())
I've tried a lot of ways and it just doesn't seem to work.
How do I make the progress bar (self.pbar) show the download progress in real time?
PS. This downloader is on my GitHub: https://github.com/dKatara/pySFD
Fixed it, it's not done yet, but threading works great!
Most recent version is on my GitHub here: https://github.com/dKatara/pySFD
#!usr/bin/env python3
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import urllib.request
import sys
import threading
dlThread = 0
hWindow = 0
fProgressCounter = 0.0
class Form(QWidget):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
global hWindow
hWindow = self
lblUrl = QLabel("File URL:")
self.txtURL = QLineEdit()
self.bttDL = QPushButton("&Download")
self.pbar = QProgressBar()
self.pbar.setMinimum(0)
self.pbar.setMaximum(100)
buttonLayout1 = QVBoxLayout()
buttonLayout1.addWidget(lblUrl)
buttonLayout1.addWidget(self.txtURL)
buttonLayout1.addWidget(self.bttDL)
buttonLayout1.addWidget(self.pbar)
self.bttDL.clicked.connect(self.bttPush)
mainLayout = QGridLayout()
mainLayout.addLayout(buttonLayout1, 0, 1)
self.setLayout(mainLayout)
self.setWindowTitle("pySFD")
def bttPush(self):
global dlThread
hSignals = sigHandling()
hSignals.dlProgress_update.connect(hSignals.pbar_incrementer)
hSignals.dlProgress_done.connect(hSignals.dlDone)
url = self.txtURL.text()
if url == "":
QMessageBox.information(self, "Empty URL",
"Please enter the URL of the file you want to download.")
return
else:
filename = str(QFileDialog.getSaveFileName(self, 'Choose the download location and file name', '.')) ## DETECT A CANCEL
filename = filename[:-6]
filename = filename.split("('",maxsplit=1)[1]
self.bttDL.setEnabled(False)
dlThread = threading.Thread(target=hSignals.runDL,args=(url, filename))
dlThread.start()
return
def pbarIncValue(self, val):
global fProgressCounter
#print("pbarIncValue({0})\nfProgressCounter={1}".format(val,fProgressCounter))
if self.pbar.value() >= 100:
self.dlProgress_done.emit()
return
if fProgressCounter > 1.0: # FIX
self.pbar.setValue(self.pbar.value() + 1)
fProgressCounter -= 1.0
fProgressCounter += val
else:
fProgressCounter += val
class sigHandling(QObject):
dlProgress_update = pyqtSignal(float)
dlProgress_done = pyqtSignal()
#pyqtSlot(float)
def pbar_incrementer(self, val):
hWindow.pbarIncValue(val)
#pyqtSlot()
def dlDone(self):
print("in dlDone")
hWindow.pbar.setValue(100)
hWindow.bttDL.setEnabled(True)
def runDL(self, dlLink, filename):
#print("in run")
global dlThread, hWindow
def report(block_count, block_size, total_size):
if block_count == 0:
#print("block_count == 0")
self.dlProgress_update.emit(0)
if (block_count * block_size) == total_size:
self.dlProgress_done.emit()
incAmount = float((100*block_size) / total_size)
#print("BS={0} TS={1} incAmount={2}".format(block_size,total_size,incAmount))
self.dlProgress_update.emit(incAmount)
urllib.request.urlretrieve(dlLink, filename, reporthook=report)
#print("emit dlProgress_done")
self.dlProgress_done.emit()
#print("about to leave dlThread")
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
screen = Form()
screen.show()
sys.exit(app.exec_())