class botclass(QtCore.QObject):
progressChanged = QtCore.pyqtSignal(int)
def bot(self):
example_error_message = QListWidgetItem("Error")
example_error_message.setForeground(Qt.red)
self.loglist.insertItem(0, error)
class MainPage(QMainWindow):
def __init__(self, *args, **kwargs):
QtWidgets.QWidget.__init__(self, *args, **kwargs)
loadUi("uifile.ui", self)
example_working_message = QListWidgetItem("Working")
example_working_message.setForeground(Qt.green)
self.loglist.insertItem(0, example_working_message)
self.thread = QtCore.QThread()
self.botwork = botclass()
self.botwork.moveToThread(self.thread)
self.thread.started.connect(self.botwork.bot)
self.botwork.clicked.connect(self.thread.start)
Error is "AttributeError: 'botclass' object has no attribute 'loglist'"
I'm getting this error when using PyQt5 in Python, how can I solve it? "botclass" will be used with selenium.
How can fix this?
You must not access the GUI from another thread, what you must do is create a signal that sends the text and then in the GUI you create the item:
class BotClass(QtCore.QObject):
progressChanged = QtCore.pyqtSignal(int)
messageChanged = QtCore.pyqtSignal(str)
def bot(self):
self.messageChanged.emit("Error")
class MainPage(QMainWindow):
def __init__(self, *args, **kwargs):
QtWidgets.QMainWindow.__init__(self, *args, **kwargs)
loadUi("uifile.ui", self)
self.add_item("Working", Qt.green)
self.bot_thread = QtCore.QThread()
self.botwork = BotClass()
self.botwork.messageChanged.connect(self.handle_message_changed)
self.botwork.moveToThread(self.bot_thread)
self.bot_thread.started.connect(self.botwork.bot)
self.<some_button>.clicked.connect(self.bot_thread.start)
def handle_message_changed(self, message):
self.add_item(message, Qt.red)
def add_item(self, message, color):
item = QListWidgetItem(message)
item.setForeground(color)
self.loglist.insertItem(0, item)
To fix this you need to declare loglist in the same class as botclass.
It would be:
class botclass(QtCore.QObject):
def __init__(self):
self.loglist = #what you want loglist to be
progressChanged = QtCore.pyqtSignal(int)
def bot(self):
example_error_message = QListWidgetItem("Error")
example_error_message.setForeground(Qt.red)
self.loglist.insertItem(0, error)
If loglist is already a variable before the class, then do this:
class botclass(QtCore.QObject):
def __init__(self):
global loglist
self.loglist = loglist
progressChanged = QtCore.pyqtSignal(int)
def bot(self):
example_error_message = QListWidgetItem("Error")
example_error_message.setForeground(Qt.red)
self.loglist.insertItem(0, error)
This makes self.loglist = to the global variable loglist.
Related
In my plugin there is a main dialog, showing list data, and a button that opens another dialog to edit the data in. When you finish the editing the data is saved to a file to be accessed later but a refresh needs to be triggered in the main dialog.
Main Dialog has a refresh function but how can I trigger it from the another "subDialog"?
Main Class
class MainDialog(QDialog):
def __init__(self, iface, parent=None, flags=Qt.WindowFlags()):
QDialog.__init__(self, parent, flags)
uic.loadUi(UI_PATH, self)
self.AnotherClass_instance = AnotherClass(self.iface)
#code to open another dialog when edit is pressed
self.refreshDatabaseData()
def opne_the_other_dialog(self):
self.AnotherClass_instance.execDialog()
def refreshDatabaseData(self):
#code to read txt file and change list view in UI
class MainClass(object):
def __init__(self, iface):
self.act.triggered.connect(self.execDialog)
def initGui(self, menu=None):
if menu is not None:
menu.addAction(self.act)
else:
self.iface.addToolBarIcon(self.act)
def execDialog(self):
self.dialog = MainDialog(self.iface, self.iface.mainWindow())
self.dialog.show()
def quitDialog(self):
self.dialog = None
self.act.setEnabled(True)
self.cancel = False
def execTool(self):
#Do something related to plugin
self.quitDialog()
The other class:
class AnotherClassDialog(QDialog):
def __init__(self, iface, parent=None, flags=Qt.WindowFlags()):
QDialog.__init__(self, parent, flags)
uic.loadUi(UI_PATH, self)
self.iface = iface
class AnotherClass(object):
def __init__(self, iface):
self.iface = iface
self.dialog = None
self.cancel = False
self.act.triggered.connect(self.execDialog)
#connect here the buttons to functions, e.g. "OK"-button to execTool
def execDialog(self):
self.dialog = AnotherClassDialog(self.iface, self.iface.mainWindow())
self.dialog.show()
def scheduleAbort(self):
self.cancel = True
def refreshMainPlugin(self):
#need to execute this correctly
self.refreshDatabaseData()
Edit:
I tried passing the list view in self.AnotherClass_instance.execDialog(here), It works but then I can't pass it from AnotherClass to AnotherClassDialog (it changes from Class Object to QMainWindow).
I have a simple pyside QMenu which is being populated with QActions when the application starts. Each menu action represents a class object. How can I create a new instance of the class object based on the Menu Item clicked, and append that new object to a list, which in this example is called
ACTIVE_BAKERS = []
import sys
from PySide import QtGui, QtCore
################################################################################
# Bakers
################################################################################
class Baker(QtGui.QWidget):
def __init__(self, name):
self.name = name
class Baker_John(Baker):
def __init__(self):
Baker.__init__(self, name='John')
class Baker_Amy(Baker):
def __init__(self):
Baker.__init__(self, name='Amy')
class Baker_Makela(Baker):
def __init__(self):
Baker.__init__(self, name='Makela')
class Baker_Jeff(Baker):
def __init__(self):
Baker.__init__(self, name='Jeff')
################################################################################
# Action
################################################################################
class MyAction(QtGui.QAction):
on_action = QtCore.Signal(dict)
def __init__(self, user_info, *args, **kwargs):
super(MyAction, self).__init__(*args, **kwargs)
self.ui = user_info
self.triggered.connect(self.on_triggered)
def on_triggered(self):
print('UI:', self.ui)
self.on_action.emit(self.ui)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(200, 300)
# OBJECTS - variable containing list of class objects created
ACTIVE_BAKERS = []
# CONTROLS
self.ui_items = QtGui.QListView()
self.ui_items.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui_items.customContextMenuRequested.connect(self.open_tasks_contextmenu)
self.setCentralWidget(self.ui_items)
self.create_context_menu_ui()
# dynamically create the menu
def create_context_menu_ui(self):
self.add_baker = QtGui.QMenu("Add")
AVAILABLE_BAKERS = [Baker_John(), Baker_Amy(), Baker_Makela(), Baker_Jeff()]
for x in AVAILABLE_BAKERS:
new_action = MyAction(x, x.name, self)
self.add_baker.addAction(new_action)
self._cmenu = QtGui.QMenu()
self._cmenu.addMenu(self.add_baker)
def open_tasks_contextmenu(self, position):
self._cmenu.exec_(QtGui.QCursor.pos())
def main():
app = QtGui.QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
the exec_() method of QMenu returns the selected QAction, through that QAction that is a MyAction that has as attribute ui that gives us the associated Barker object, using the Barker we can access the class through __class__ and create another one object:
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(200, 300)
# OBJECTS - variable containing list of class objects created
self.ACTIVE_BAKERS = []
# CONTROLS
self.ui_items = QtGui.QListView()
self.ui_items.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui_items.customContextMenuRequested.connect(self.open_tasks_contextmenu)
self.setCentralWidget(self.ui_items)
self.create_context_menu_ui()
# dynamically create the menu
def create_context_menu_ui(self):
self.add_baker = QtGui.QMenu("Add")
AVAILABLE_BAKERS = [Baker_John(), Baker_Amy(), Baker_Makela(), Baker_Jeff()]
for x in AVAILABLE_BAKERS:
new_action = MyAction(x, x.name, self)
self.add_baker.addAction(new_action)
self._cmenu = QtGui.QMenu()
self._cmenu.addMenu(self.add_baker)
def open_tasks_contextmenu(self, position):
action = self._cmenu.exec_(QtGui.QCursor.pos())
if isinstance(action, MyAction):
obj = action.ui.__class__()
if obj not in self.ACTIVE_BAKERS:
self.ACTIVE_BAKERS.append(obj)
Can anyone tell me how to call add_row method from def test(self): method in User class ?
Now i am using code for call Rows().add_row().I print something in def add_row(self): then it prints but not add widget in screen.
test.py
class User(Popup):
def __init__(self, obj, **kwargs):
super(User, self).__init__(**kwargs)
def test(self):
Rows().add_row()
class narration(BoxLayout):
col_data = ListProperty(["?", "?", "?"])
button_text = StringProperty("")
def __init__(self, **kwargs):
super(narration, self).__init__(**kwargs)
class Rows(BoxLayout):
row_count = 0
def __init__(self, **kwargs):
super(Rows, self).__init__(**kwargs)
def add_row(self):
self.row_count += 1
self.add_widget(narration(button_text=str(self.row_count)))
class User(Popup):
def __init__(self, obj, **kwargs):
super(User, self).__init__(**kwargs)
self.row_obj = obj
def test(self):
self.row_obj.add_row()
class narration(BoxLayout):
col_data = ListProperty(["?", "?", "?"])
button_text = StringProperty("")
def __init__(self, **kwargs):
super(narration, self).__init__(**kwargs)
class Rows(BoxLayout):
row_count = 0
def __init__(self, **kwargs):
super(Rows, self).__init__(**kwargs)
def add_row(self):
self.row_count += 1
self.add_widget(narration(button_text=str(self.row_count)))
rows_obj = Rows()
user_obj = User(rows_obj)
It it not a working code it's just an example But your approach should be something like that.If you don't want to pass object of "Rows" class than make "User" class as a child of "Rows" class using inheritance concept.
class User(Rows,Popup):
I changed your code a little to show you how it's done.
in tmp.py
class Rows(object):
row_count = 0
def __init__(self, **kwargs):
super(Rows, self).__init__(**kwargs)
def add_row(self):
self.row_count += 1
print "Hello"
# self.add_widget(narration(button_text=str(self.row_count)))
class User(Rows):
def __init__(self, obj, **kwargs):
super(User, self).__init__(**kwargs)
def test(self):
self.add_row()
class narration(object):
# col_data = ListProperty(["?", "?", "?"])
# button_text = StringProperty("")
def __init__(self, **kwargs):
super(narration, self).__init__(**kwargs)
I had to comment out some lines in order to get it working.
now, in tmp2.py
from tmp import User
if __name__ == '__main__':
temp = User("H")
temp.test()
# outputs : Hello
What we did is instanciating the User class such as :
class User(Rows):, so when you will call User class, with the proper super() statement, you will be able to use the methods of Rows as you would use it as if it was defined in User class.
So you will call (it has been modified in the code) : self.add_row(), even if add_row() is a method from Rows.
So i am using Pyqt for creating GUI program using Python programming language. And i am trying to get the value of Qslider when clicking it, but i don't understand how...
self.slide.mouseDoubleClickEvent= lambda event: self.slideclicked()
This is how i declare the method when the slide is clicked, and this is the method :
def slideclicked(self):
print(self.slide.value())
I am hoping to get the value where the mouse clicked, but instead i am just getting current value of the Qslider.
A possible solution is to overwrite the mouseDoubleClickEvent method and create a signal that sends that information:
class Slider(QSlider):
pointClicked = pyqtSignal(QPoint)
def mouseDoubleClickEvent(self, event):
self.pointClicked.emit(event.pos())
class Widget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.setLayout(QVBoxLayout())
self.slider = Slider()
self.layout().addWidget(self.slider)
self.slider.pointClicked.connect(lambda p: print(p.x(), p.y()))
If you can not overwrite that method you could use eventFilter:
class ClickedHelper(QObject):
pointClicked = pyqtSignal(QPoint)
def __init__(self, widget, *args, **kwargs):
QObject.__init__(self, parent=widget)
self.obj = widget
self.obj.installEventFilter(self)
def eventFilter(self, obj, event):
if obj == self.obj and event.type() == QEvent.MouseButtonDblClick:
self.pointClicked.emit(event.pos())
return QObject.eventFilter(self, obj, event)
class Widget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.setLayout(QVBoxLayout())
self.slider = QSlider()
self.layout().addWidget(self.slider)
helper = ClickedHelper(self.slider)
helper.pointClicked.connect(lambda p: print(p.x(), p.y()))
I have a PySide (Qt) GUI which spawns multiple threads. The threads sometimes need to update the GUI. I have solved this in the following way:
class Signaller(QtCore.QObject) :
my_signal = QtCore.Signal(QListWidgetItem, QIcon)
signaller = Signaller()
class MyThread(threading.Thread):
def __init__(self):
super(IconThread, self).__init__()
# ...
def run(self) :
# ...
# Need to update the GUI
signaller.my_signal.emit(self.item, icon)
#
# MAIN WINDOW
#
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
# ...
# Connect signals
signaller.my_signal.connect(self.my_handler)
#QtCore.Slot(QListWidgetItem, QIcon)
def my_handler(self, item, icon):
item.setIcon(icon)
def do_something(self, address):
# ...
# Start new thread
my_thread = MyThread(newItem)
my_thread.start()
# ...
Is there an easier way? Creating the signals, handlers and connect them requires a few lines of code.
I started coding with PySide recently and I needed a equivalent of PyGObject's GLib.idle_add behaviour. I based the code off of your answer ( https://stackoverflow.com/a/11005204/1524507 ) but this one uses events instead of using a queue ourselves.
from PySide import QtCore
class InvokeEvent(QtCore.QEvent):
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
def __init__(self, fn, *args, **kwargs):
QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
self.fn = fn
self.args = args
self.kwargs = kwargs
class Invoker(QtCore.QObject):
def event(self, event):
event.fn(*event.args, **event.kwargs)
return True
_invoker = Invoker()
def invoke_in_main_thread(fn, *args, **kwargs):
QtCore.QCoreApplication.postEvent(_invoker,
InvokeEvent(fn, *args, **kwargs))
Which is used the same way in the above answer link.
This is what I have so far. I wrote the following code somewhere in a helper module:
from Queue import Queue
class Invoker(QObject):
def __init__(self):
super(Invoker, self).__init__()
self.queue = Queue()
def invoke(self, func, *args):
f = lambda: func(*args)
self.queue.put(f)
QMetaObject.invokeMethod(self, "handler", QtCore.Qt.QueuedConnection)
#Slot()
def handler(self):
f = self.queue.get()
f()
invoker = Invoker()
def invoke_in_main_thread(func, *args):
invoker.invoke(func,*args)
Then my threads can very easily run code to update the GUI in the main thread. There is no need to create and connect signals for every operation.
class MyThread(threading.Thread):
def __init__(self):
super(IconThread, self).__init__()
# ...
def run(self) :
# ...
# Need to update the GUI
invoke_in_main_thread(self.item.setIcon, icon)
I think something like this is quite nice.