I want to know if there is any workaround to the missing flow() property "BottomToTop"?
I'm currently working on a little pet project. Simply a QListWidget, containing a custom made QWidget item, added by the user. No problem with that part. I just want the item to be listed "BottomToTop". Any suggestions?
Do just need to insert the new elements at the bottom instead of the top of your list?
I assume what you call bottom in the beginning for your list?
If so, you can use QListWidget::insertItem
yourListWidget->(0, yourNewWidgetItem);
This is my closest solution to what I've been asking (Credit to -ymoreau for the Spacer idea).
But I'm not completely happy with it. When enough items are added and the scrollbar appears, an annoying (to my eye) white-space appears at the bottom. Any solution to that? And ofc. is there a more elegant solution to this? Seems abit overkill to me...
from PyQt5.QtWidgets import (QApplication, QWidget, QListWidget,
QListWidgetItem, QVBoxLayout, QHBoxLayout,
QPushButton, QSpacerItem, QLabel, QSizePolicy)
from PyQt5.QtCore import QSize
from PyQt5 import QtCore
import sys
class SpacerWidget(QWidget):
def __init__(self, parent=None):
super(SpacerWidget, self).__init__(parent)
self.spacer = QSpacerItem(self.width(), self.height(),
QSizePolicy.Expanding,
QSizePolicy.Expanding)
self.setStyleSheet("border: none;")
self.layout = QHBoxLayout()
self.layout.setSpacing(0)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.addItem(self.spacer)
self.setLayout(self.layout)
class SpacerWidgetItem(QListWidgetItem):
def __init__(self, parent=None):
super(SpacerWidgetItem, self).__init__(parent)
def setSize(self, width, new_height):
if new_height < 10:
self.setSizeHint(QSize(0, 0))
else:
self.setSizeHint(QSize(width, new_height))
class ItemWidget(QWidget):
def __init__(self, parent=None):
super(ItemWidget, self).__init__(parent)
self.left_label = QLabel("Some info!")
self.left_label.setMaximumHeight(35)
self.left_label.setAlignment(QtCore.Qt.AlignLeft)
self.right_label = QLabel("Some other info!")
self.right_label.setMaximumHeight(35)
self.right_label.setAlignment(QtCore.Qt.AlignRight)
self.layout = QHBoxLayout()
self.layout.setSpacing(0)
self.layout.addWidget(self.left_label)
self.layout.addWidget(self.right_label)
self.setLayout(self.layout)
class Widget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.resize(300, 320)
self.itemsinlist = 0
self.layout = QVBoxLayout()
self.add_btn = QPushButton("Add Item")
self.add_btn.setMinimumSize(QSize(300, 50))
self.add_btn.clicked.connect(self.addListItem)
self.list_w = QListWidget()
self.list_w.setMinimumSize(300, 270)
self.list_w.resize(300, 270)
self.list_w.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.list_w.setResizeMode(1)
self.spacer = SpacerWidgetItem(self.list_w)
self.spacer.setSizeHint(QSize(self.list_w.minimumWidth(),
self.list_w.minimumHeight()))
self.spacer_widget = SpacerWidget()
self.list_w.addItem(self.spacer)
self.list_w.setItemWidget(self.spacer, self.spacer_widget)
self.layout.addWidget(self.list_w)
self.layout.addWidget(self.add_btn)
self.layout.setContentsMargins(1, 1, 1, 1)
self.layout.setSpacing(0)
self.setLayout(self.layout)
self.show()
def addListItem(self):
new_label = QListWidgetItem(self.list_w)
new_label.setSizeHint(QSize(self.list_w.minimumWidth(), 35))
new_label_widget = ItemWidget()
self.list_w.addItem(new_label)
self.list_w.setItemWidget(new_label, new_label_widget)
self.list_w.scrollToBottom()
self.itemsinlist += 1
self.spacer.setSize(self.width(),
self.list_w.height() -
(self.itemsinlist * 35)-2)
def resizeEvent(self, event):
self.spacer.setSizeHint(QSize(self.width(),
self.list_w.height() -
(self.itemsinlist * 35)-2))
self.list_w.scrollToBottom()
def main():
app = QApplication(sys.argv)
wid = Widget()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Related
PyQt6:
On Win10 Vertical header has only line numbers no lines.
On Linux KDE has both.
Did I miss something or bug?
I'm not able to test on MAC.
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableView
from PyQt6.QtCore import Qt, QAbstractTableModel
class TableModel(QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role):
if role == Qt.ItemDataRole.DisplayRole:
value = self._data[index.row()][index.column()]
return value
def rowCount(self, index):
# The length of the outer list.
return len(self._data)
def columnCount(self, index):
# The following takes the first sub-list, and returns
# the length (only works if all rows are an equal length)
return len(self._data[0])
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.table = QTableView()
data = [[7, 9, 2],
[1, -1, -1],
[3, 5.3, -5],
[3, 3, 2],
[7, 8, 23],]
self.model = TableModel(data)
self.table.setModel(self.model)
self.setCentralWidget(self.table)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
I got the emit signal working when it is in the Worker class - def run(self) method. Everything runs fine, the while loop is able to cycle and emit a signal every 1 second. Then the label will be updated after receiving the signal.
I decided to try out by placing the while loop in another class named loop, methodA. This is to try see if the emitted signal will be picked up by the MainWindow. Unfortunately, the signal is not picked up and the program hung up thereafter.
Did I miss any step that prevented the while loop's emitting signals from being picked up? Kindly point me in the right direction by making changes to my script.
Thanks.
import sys
import time
from PyQt5 import QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
from mydialog import Ui_mydialog
class Mainwindow(QtWidgets.QMainWindow, Ui_mydialog):
def __init__(self, *args, obj=None, **kwargs):
super(Mainwindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.thread = Worker()
self.loop = loop()
self.thread.sig.connect(self.updatelabel)
self.mypushbutton.clicked.connect(self.mypushbuttonclicked)
def mypushbuttonclicked(self):
self.thread.start()
def updatelabel(self, text):
self.mylabel.setText(text)
class Worker(QThread):
sig = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
# self.count = 0
self.loop = loop()
def run(self):
self.loop.methodA()
## Original code without being in class loop and method loopA
# while True:
# time.sleep(1)
# self.count += 1
# if (self.count % 1 == 0):
# self.sig.emit(f"Timer: {self.count} s")
# Newly added class with method "methodA"
class loop(object):
sig = pyqtSignal(str)
def __init__(self):
self.count = 0
def methodA(self):
while True:
time.sleep(1)
self.count += 1
if (self.count % 1 == 0):
self.sig.emit(f"Timer: {self.count} s")
app = QtWidgets.QApplication(sys.argv)
window = Mainwindow()
app.setStyle("Fusion")
window.show()
app.exec()
I had a similar problem.
I solved it by following this: http://zetcode.com/gui/pyqt5/eventssignals/
The idea is to create a class that hold all the signals, and pass the same communication class to all the classes as a parameter.
So your code might become:
import sys
import time
from PyQt5 import QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal, QObject
from mydialog import Ui_mydialog
class Communicate(QObject):
sig = pyqtSignal(str)
class Mainwindow(QtWidgets.QMainWindow, Ui_mydialog):
def __init__(self, *args, obj=None, **kwargs):
super(Mainwindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.communicate = Communicate()
self.communicate.sig[str].connect(self.updatelabel)
self.thread = Worker(communicate = self.communicate)
#self.loop = loop() # this seems useless to me here
self.mypushbutton.clicked.connect(self.mypushbuttonclicked)
def mypushbuttonclicked(self):
self.thread.start()
def updatelabel(self, text):
self.mylabel.setText(text)
class Worker(QThread):
def __init__(self, parent=None, communicate=Communicate()):
super(Worker, self).__init__(parent)
self.communicate = communicate
# self.count = 0
self.loop = loop(communicate= self.communicate)
def run(self):
self.loop.methodA()
## Original code without being in class loop and method loopA
# while True:
# time.sleep(1)
# self.count += 1
# if (self.count % 1 == 0):
# self.sig.emit(f"Timer: {self.count} s")
# Newly added class with method "methodA"
class loop(object):
def __init__(self, communicate=Communicate()):
self.count = 0
self.communicate = communicate
def methodA(self):
while True:
time.sleep(1)
self.count += 1
if (self.count % 1 == 0):
self.communicate.sig.emit(f"Timer: {self.count} s")
app = QtWidgets.QApplication(sys.argv)
window = Mainwindow()
app.setStyle("Fusion")
window.show()
app.exec()
I haven't tested this code, but I hope that you've got the idea.
After reading about finite state machines I found the the QState/QStateMachine API in QT.
But now I can't find out how to use it to run different code based on which state I'm currently in.
Let's take the following example:
import sys
from PySide2 import QtCore, QtWidgets
class Form(QtWidgets.QDialog):
def action_a(self):
print("I'm in mode A")
def action_b(self):
print("Mode B is the current mode")
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.button1 = QtWidgets.QPushButton("Run action")
self.button2 = QtWidgets.QPushButton("Change State")
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.button1)
self.layout.addWidget(self.button2)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
form = Form()
form.show()
state_a = QtCore.QState()
state_b = QtCore.QState()
state_a.assignProperty(form.button2, "text", "To state B")
state_b.assignProperty(form.button2, "text", "To state A")
state_a.addTransition(form.button2, QtCore.SIGNAL("clicked()"), state_b)
state_b.addTransition(form.button2, QtCore.SIGNAL("clicked()"), state_a)
machine = QtCore.QStateMachine()
machine.addState(state_a)
machine.addState(state_b)
machine.setInitialState(state_a)
machine.start()
sys.exit(app.exec_())
What would I have to add to this code so that button1.clicked connects to action_a when in state_a, but to action_b when in state_b?
I have a folder TreeView on the left layout and I want to drag a .txt file from there and drop it to the other layout. Hopefully, I want to load the data of that dropped file on a variable.
For the code’s needs, I used till now (to my “real” code) the np.loadtxt() to load the data, so I’d like to use it here too.
In case it matters, the .txt file contains 4 columns (coordinates).
I post my code. The program closes when I drop the file.
Thanks in advance!
import sys, time, os
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import numpy as np
import pylab as pl
import random
class Example(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.folderLayout = QWidget();
self.pathRoot = QDir.rootPath()
self.dirmodel = QFileSystemModel(self)
self.dirmodel.setRootPath(QDir.currentPath())
self.indexRoot = self.dirmodel.index(self.dirmodel.rootPath())
self.folder_view = QTreeView();
self.folder_view.setDragEnabled(True)
self.folder_view.setModel(self.dirmodel)
self.folder_view.setRootIndex(self.indexRoot)
self.selectionModel = self.folder_view.selectionModel()
self.left_layout = QVBoxLayout()
self.left_layout.addWidget(self.folder_view)
self.folderLayout.setLayout(self.left_layout)
splitter_filebrowser = QSplitter(Qt.Horizontal)
splitter_filebrowser.addWidget(self.folderLayout)
splitter_filebrowser.addWidget(Figure_Canvas(self))
splitter_filebrowser.setStretchFactor(1, 1)
hbox = QHBoxLayout(self)
hbox.addWidget(splitter_filebrowser)
self.centralWidget().setLayout(hbox)
self.setWindowTitle('Simple drag & drop')
self.setGeometry(750, 100, 600, 500)
class Figure_Canvas(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.setAcceptDrops(True)
blabla = QLineEdit()
self.right_layout = QVBoxLayout()
self.right_layout.addWidget(blabla)
self.buttonLayout = QWidget()
self.buttonLayout.setLayout(self.right_layout)
def dragEnterEvent(self, e):
if e.mimeData().hasFormat('text/uri-list'):
e.accept()
else:
e.ignore()
def dropEvent(self, e):
print("something")
data = np.loadtxt(e.mimeData())
print(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
app.exec_()
from PyQt4 import QtGui, QtCore
from selenium import webdriver
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
self.lbl = QtGui.QLabel(self)
menu1=QtGui.QComboBox(self)
for c in [" ","snak","python"]:
menu1.addItem(c)
menu1.activated[str].connect(self.b)
def b(self,a):
print (a) #it gives me the value selected in the menu option but the problem is i want to use the same value of "a" in another function
def handleButton(self):
driver = webdriver.Firefox()
driver.get('http://google.com')
driver.find_element_by_id("lst-ib").send_keys("a") # value of a should be same as value printed in function b
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())