Pyside program locks up in response to a checkbox click - python

I am having a very strange problem. Whenever the QCheckBox is checked it calls drawall as expected. However when drawall is finished it completely hangs. I have tried directly calling drawall (Version 2) when clicked but with no luck the result is the same.
scene = QGraphicsScene(0, 0, 500, 500)
class SurrogateBeat(QGraphicsItem):
def __init__(self,beat,top):
super(SurrogateBeat, self).__init__()
print "Init"
class Test(QWidget):
def __init__(self):
self.drawAll = QCheckBox("Draw all frames on screen",self)
self.drawAll.stateChanged.connect(self.onDrawAllClicked)
def onDrawAllClicked(self): #Version 1
QTimer.singleShot(0, self.drawall)
def onDrawAllClicked(self): #Version 2 (neither work)
self.drawall()
def drawall(self):
if self.drawAll.checkState() == Qt.CheckState.Checked:
self.surrogates=[]
for b in range(0,len(self.item.beats)):
print "Loop"
surrogate = SurrogateBeat(b, self.item)
scene.addItem(surrogate)
self.surrogates.append(surrogate)
scene.update()
print "Update"
Loop prints out 16 times, the init for SurrogateBeat prints out so it is being called, but after "Update" prints out, the program is hung.

QGraphicsItem is an abstract base class.
As a minimum, your SurrogateBeat subclass will need to reimplement the boundingRect and paint functions.

Related

As soon as python class receives QMessageBox.Information as argument, resizing by stylesheet won't work

In python, I created a class “ScrollMessageBoxShowRC“ , which receives three arguments (see below):
result = ScrollMessageBoxShowRC(QMessageBox.Information, '', '')
result.exec_()
Originally, I instantiated the class with solely "None" as argument. As long as it received only "None", I could resize the class as follows: self.setStyleSheet("QScrollArea{min-width:410 px; min-height: 600px}"), see below:
class ScrollMessageBoxShowRC(QMessageBox):
def __init__(self, *args, **kwargs):
QMessageBox.__init__(self, *args, **kwargs)
self.setWindowTitle("Contacts to view or to delete.")
scroll = QScrollArea(self)
scroll.setWidgetResizable(True)
self.content = QWidget()
scroll.setWidget(self.content)
lay = QVBoxLayout(self.content)
lay.setStyleSheet("min-width: 100px;");
dlts = {}
self.x = {}
for rc in dbaccess.allRC():
dlt = QCheckBox('delete', self)
dlt.stateChanged.connect(partial(self.btnstateDel, dlt, dlts))
dlt.setObjectName(rc[9])
qb = QPushButton(rc[9], self)
qb.released.connect(partial(self.button_releasedRC, rc[9]))
lay.addWidget(qb)
lay.addWidget(dlt)
self.buttonClicked.connect(self.msgButtonClickDel)
self.layout().addWidget(scroll, 0, 0, 1, self.layout().columnCount())
self.setStyleSheet("QScrollArea{min-width:410 px; min-height: 600px}")
def btnstateDel(self, dlt, dlts):
dlts[dlt.objectName()] = False
if dlt.isChecked:
dlts[dlt.objectName()] = True
self.x = dlts
def msgButtonClickDel(self, i):
if i.text() == "OK":
dbaccess.deleteRCs(self.x)
def button_releasedRC(self, nameshow):
pass
Since I changed the arguments to QMessageBox.Information, '', '' the stylesheet setting the size of the Widget seems no longer to have been in vigour. I could not find out why this is the case. Could anybody give me a hint what I might have overlooked?
QMessageBox is a special type of QDialog that creates its own layout on execution. When created without arguments, it behaves almost like a basic QDialog, but as soon as elements are added (most importantly, the icon), the internal layout takes precedence and in certain cases is also completely deleted a recreated.
Since QMessageBox is generally intended as a convenience simple class, and it seems that the only actual specific feature used here is the icon, there's no need to use it at all.
A basic QDialog can be used instead, and the icon can be loaded using the style, just like QMessageBox does. Then you can add buttons using QDialogButtonBox.
class ScrollMessageBoxShowRC(QDialog):
def __init__(self, *args, **kwargs):
QDialog.__init__(self, *args, **kwargs)
layout = QGridLayout(self)
icon = self.style().standardIcon(
QStyle.SP_MessageBoxInformation, None, self)
iconSize = self.style().pixelMetric(
QStyle.PM_MessageBoxIconSize, None, self)
self.iconLabel = QLabel(pixmap=icon.pixmap(iconSize, iconSize))
layout.addWidget(self.iconLabel, 0, 0,
alignment=Qt.AlignLeft|Qt.AlignTop)
self.scroll = QScrollArea()
layout.addWidget(self.scroll, 0, 1)
# ...
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
layout.addWidget(self.buttonBox,
layout.rowCount(), 0, 1, layout.columnCount())
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
def accept(self):
dbaccess.deleteRCs(self.x)
super().accept()
The icons of QMessageBox are listed in the StandardPixmap enum of QStyle.
Further important notes:
you should not use string matching for buttons, especially if the names are set by the library: "OK" can be a different string in other systems (for instance, on my computer the "k" is lowercase);
even if you're using the local dlts in partial, you're actually always updating self.x, which makes it pointless to have two dictionaries and also sending them to the connected function;
the released signal is emitted whenever a button is released, which can also happen when the user presses the mouse on the button and moves the mouse outside it while keeping the mouse pressed; use clicked instead;
isChecked is a function, you should use if dlt.isChecked():, or, better dlts[dlt.objectName()] = dlt.isChecked(), instead of setting it to False every time before actually checking;
stateChanged is used for three state check boxes that can be partially checked; if you only need a bool value, use toggled;
for basic signals that are directly (not queued, like in threads) connected to functions for which the behavior is known and specific for those signal senders, you can use self.sender() instead of partials; in this way, you can also directly use the signal arguments instead of querying the property; if you're not interested in the argument, ignore it in the function signature; always use sender() with care, though;
# ...
self.dlts = {}
for rc in dbaccess.allRC():
dlt = QCheckBox('delete', self)
dlt.toggled.connect(self.btnstateDel)
dlt.setObjectName(rc[9])
qb = QPushButton(rc[9], self)
qb.clicked.connect(self.button_releasedRC)
lay.addWidget(qb)
lay.addWidget(dlt)
def btnstateDel(self, checked):
self.dlts[self.sender().objectName()] = checked
def button_releasedRC(self):
nameshow = self.sender()
# ...

Python interpreter crashes when using callback from thread

i have written a Gui-App with pyqt5.
I Use threading.Thread to create a Thread, which does some calculating.
The thread gets name, target, args, kwargs and a callback-function which is member of my app class.
The callback works several times, except for the last run.
For example, the first 99 calls out of 100 do well, but no. 100 causes the error.
After returning from the callback the last time, the interpreter crashes after about a second with an Windows Event, event code 0xc0000005
The callback also has **kwargs as parameters
There is no Traceback from python itsself.
Anyone an idea on what could be the cause or what I do wrong?
Environment:
Windows 10,
Python 3.9.0,
PyQt5 5.15.1
A minimal Example of both classes (probably will not work):
from Controller.filecontroller import Files_Controller as filecontroller
class DialogBatch(QDialog):
def __init__(self, parent= None):
# Initialize super-class
super(DialogBatch, self).__init__()
# Load the GUI Description File, created with QtDesigner
uic.loadUi('.\\GUI_dialog_batch_ui\\dialog_batch.ui', self)
# QProgressBars
self.progressBar_batch_progress = self.findChild(QProgressBar, 'progressBar_batch_progress')
# QPushButtons
self.pushButton_batch_start = self.findChild(QPushButton, 'pushButton_batch_start')
....
# Filecontroller for file-operations
self.filecontroller = filecontroller()
# Thread for executing high-level batch operations;
self.__batchthread = None
# Show GUI
self.show()
....
# Callback
def __update_GUI(self, **kwargs):
"""! Hidden method for updating the GUI
#param key-only progress: [int] progress of the task executed
#return: [int] 1 after finishing
"""
test = deepcopy(kwargs)
print("Callback entered")
if "progress" in test:
self.progressBar_batch_progress.setValue(test["progress"])
print("__update_GUI: Callback called!")
else:
print("__update_GUI: No Parameter!")
print("update GUI finished")
return 1
....
def slot_pushbutton_start_clicked(self):
...
self.__batchthread = threading.Thread(\
name='Batchthread', \
target=self.filecontroller.batch_folder,\
args=(self.input[0], self.signals[0], self.databases),\
kwargs={"callback":self.__update_GUI})
self.__batchthread.start()
...
class Files_Controller(object):
## Constructor
# #param self
def __init__(self):
...
self.__killbatchthread = False
self._func_callback = None
...
...
def batch_folder(self, files, signals, databases, *args, **kwargs):
...
self.__params = {}
files_read = 0
files_painted = 0
progress = 0
...
for each_file in allfiles:
...
....
files_read +=1
# Cancel
if self.get_killbatchthreat(): return -1
# Callback for Update
if self._func_callback is not None:
progress = int((files_read+files_painted)*50/number_of_files)
self.__params.clear()
self.__params={"progress":progress}
self._func_callback(**self.__params)
...
files_painted +=1
# Callback for GUI update
if self._func_callback is not None:
progress = int((files_read+files_painted)*50/number_of_files)
self.__params.clear()
self.__params={"progress":progress}
print("Returnvalue: %i" %(self._func_callback(**self.__params)))
for i in range(10):
print("Sleeptime: %i" %i)
time.sleep(1)
....
The last time calling _func_callback with the last file produces this output:
Callback entered
__update_GUI: Callback called!
update GUI finished
Returnvalue: 1
Sleeptime: 0
Sleeptime: 1
After Sleeptime: 1
The python interpreter crashes.
I could solve it myself:
With a change in design the problem disappeared.
The problem was using a callback into the gui class (in the gui thread) from a nonw-gui-thread-function.
After using signals instead of the callback, it worked fine.
Conclusion:
Never call a gui-thread-function from a none-gui-thread function.

Why does running mainloop() cause my whole computer to freeze?

I'm building a large, complicated program, one half of which involves a GUI, which I'm building using Tkinter.
Previous iterations of this GUI seemed to work as intended. However, in the latest version, when I try to run a demonstration (see the demo() function in the code below), my whole computer freezes, and my only option is to carry out a hard reset.
Does anyone have any ideas as to why this might be happening? Some points which might be useful:
If I run the code below with the line self.gui.mainloop() commented out, the desired window appears on the screen for long enough for the toaster message to be displayed, and then closes without any freezing.
The ArrivalsManagerDaughter and DeparturesManagerDaughter objects transmit data wirelessly to another device, but they shouldn't be doing anything, other than being initialised, in the code which is causing the freezing. I don't believe that these are the cause of the problem, although I could well be wrong.
Here's the whole Python file which I'm trying to run. I'm happy to post more code if requested.
"""
This code holds a class which manages transitions between the
"recommendations" and "custom placement" windows, and also oversees their
interactions with Erebus.
"""
# GUI imports.
from tkinter import *
# Non-standard imports.
import ptoaster
# Custom imports.
from erebus.arrivals_manager_daughter import ArrivalsManagerDaughter
from erebus.departures_manager_daughter import DeparturesManagerDaughter
# Local imports.
from charon.custom_placement_window import Custom_Placement_Window
from charon.recommendations_window import Recommendations_Window
# Local constants.
REFRESH_INTERVAL = 1000
# ^^^ in miliseconds ^^^
##############
# MAIN CLASS #
##############
class Comptroller:
""" The class in question. """
def __init__(self, welcome=False, delete_existing_ledger=False,
internal=False, diagnostics=False, path_to_icon=None):
self.close_requested = False
self.path_to_icon = path_to_icon
self.recommendations = dict()
self.arrivals_manager = ArrivalsManagerDaughter(self,
diagnostics=diagnostics)
self.departures_manager = DeparturesManagerDaughter(
delete_existing=delete_existing_ledger, internal=internal,
diagnostics=diagnostics)
self.gui = Tk()
self.top = Frame(self.gui)
self.window = Recommendations_Window(self)
self.is_on_recommendations_window = True
self.arrange()
if welcome:
print_welcome()
def add_recommendation(self, ticket, epc, column, row):
""" Add a recommendation to the dictionary. """
recommendation = dict()
recommendation["ticket"] = ticket
recommendation["epc"] = epc
recommendation["column"] = column
recommendation["row"] = row
self.recommendations[ticket] = recommendation
def remove_recommendation(self, ticket):
""" Delete a recommendation from the dictionary. """
del self.recommendations[ticket]
def get_top(self):
""" Return the top-level GUI object. """
return self.top
def arrange(self):
""" Arrange the widgets. """
self.window.get_top().pack()
self.top.pack()
def switch_to_custom_placement(self, ticket, epc):
""" Switch window from "Recommendations" to "Custom Placement". """
columns = self.arrivals_manager.columns
rows = self.arrivals_manager.rows
self.window.get_top().pack_forget()
self.window = Custom_Placement_Window(self, ticket, epc, columns,
rows)
self.window.get_top().pack()
self.is_on_recommendations_window = False
def switch_to_recommendations(self):
""" Switch window from "Custom Placement" to "Recommendations". """
self.window.get_top().pack_forget()
self.window = Recommendations_Window(self)
self.window.get_top().pack()
self.is_on_recommendations_window = True
def refresh(self):
""" Refresh the "recommendations" window, as necessary. """
if (self.is_on_recommendations_window and
self.arrivals_manager.clear_quanta()):
self.window.refresh_rec_table()
self.departures_manager.clear_backlog()
if self.close_requested:
self.kill_me()
else:
self.gui.after(REFRESH_INTERVAL, self.refresh)
def simulate_recommendation(self, ticket, epc, column, row):
""" Simulate receiving a transmission from the Pi. """
self.add_recommendation(ticket, epc, column, row)
self.window.refresh_rec_table()
def request_close(self):
self.close_requested = True
def run_me(self):
""" Run the "mainloop" method on the GUI object. """
self.gui.after(REFRESH_INTERVAL, self.refresh)
self.gui.title("Charon")
if self.path_to_icon:
self.gui.iconphoto(True, PhotoImage(file=self.path_to_icon))
self.gui.protocol("WM_DELETE_WINDOW", self.request_close)
self.gui.mainloop()
def kill_me(self):
""" Kill the mainloop process, and shut the window. """
self.gui.destroy()
####################
# HELPER FUNCTIONS #
####################
def print_welcome():
""" Print a welcome "toaster" message. """
message = ("Notifications about boxes leaving the coldstore will be "+
"posted here.")
ptoaster.notify("Welcome to Charon", message,
display_duration_in_ms=REFRESH_INTERVAL)
def print_exit(epc):
""" Print an exit "toaster" message. """
message = "Box with EPC "+epc+" has left the coldstore."
ptoaster.notify("Exit", message)
###########
# TESTING #
###########
def demo():
""" Run a demonstration. """
comptroller = Comptroller(welcome=True, delete_existing_ledger=True,
internal=True, diagnostics=True)
comptroller.simulate_recommendation(1, "rumpelstiltskin", 0, 0)
comptroller.simulate_recommendation(2, "beetlejuice", 0, 0)
comptroller.run_me()
###################
# RUN AND WRAP UP #
###################
def run():
demo()
if __name__ == "__main__":
run()
FOUND THE PROBLEM: the culprit was actually calling a ptoaster function from within a Tkinter GUI. Which leads me on to my next question: Is it possible to combine ptoaster and Tkinter in an elegant fashion, and, if so, how?
The problem ocurred when calling print_welcome(), which in turn calls one of the ptoaster functions. It seems that Tkinter and ptoaster do not play together nicely. Removing the reference to print_welcome() from the Comptroller class put a stop to any freezing.
(On a side note: I'd be very grateful to anyone who could suggest an elegant method of combing ptoaster with Tkinter.)
Try changing
def demo():
""" Run a demonstration. """
comptroller = Comptroller(welcome=True, delete_existing_ledger=True,
internal=True, diagnostics=True)
comptroller.simulate_recommendation(1, "rumpelstiltskin", 0, 0)
comptroller.simulate_recommendation(2, "beetlejuice", 0, 0)
comptroller.run_me()
###################
# RUN AND WRAP UP #
###################
def run():
demo()
if __name__ == "__main__":
run()
To simply
if __name__ == "__main__":
""" Run a demonstration. """
comptroller = Comptroller(welcome=True, delete_existing_ledger=True,
internal=True, diagnostics=True)
comptroller.simulate_recommendation(1, "rumpelstiltskin", 0, 0)
comptroller.simulate_recommendation(2, "beetlejuice", 0, 0)
comptroller.run_me()
As to why this might happening, since you're creating an object by instantiating Comptroller inside a regular function demo, the object is not being "retained" after the demo exits.
EDIT
If you would like to still maintain demo and run you could create a class Demo and store the instance globally.
Or maybe a simple global variable inside demo to retain a "reference" to the instance Comptroller.

Raspberry Pi - Python - joystick

I am using Raspberry Pi.
while True:
if joystick.get_button(0) == 1:
print ("stop")
else
print ("start")
The purpose of this code is :
I want to interrupt some action while I press a button.
while running the code, it ignores me when I press the button and keep giving me "start". However, if I change the code to :
if joystick.get_button(0) == 0:
the program gives me "stop" at once. (0 is the default value of get_button(0), 1 means I press the button)
The cycle itself seems ok, so I would think that the problem is how your get_button() method acts. Be sure that it is returning the right value and that infinite loop and status checking are not running in the same thread.
Anyway, I would suggest you to use the Observer Pattern.
Basically, it allows you to create a reaction over your joystick button without using infinite loops.
The following code should fit your needs. Joystick class call methods from Player class every time the button is pressed, so every time Joystick change its state.
//OBSERVABLE OBJECT
class Joystick(object):
def __init__(self):
self._button = 0
self._observers = []
def get_button(self):
return self._button
def set_button(self, value):
for callback in self._observers:
callback(self._button)
def bind_to(self, callback):
self._observers.append(callback)
//OBSERVER OBJECT
class Player(object):
def __init__(self, controller):
self._state = 0; //1 - stop, 0 - play
self.controller.bind_to(self.change_state)
def change_state(self, new_state):
self_state = new_state
if(new_state == 0)
print 'play'
else
print 'stop'
This solution will print 'play' and 'stop' once, on every state change.
Then in your code you will create an observable instance:
joystick = new Joystick()
and pass it to an observer:
player = new Player(joystick)
in this way, when you launch your setter function:
joystick.set_button(0)
joystick will automatically change the status in player instance.

Python GTK3 - TreeView not being updated properly

I have issue with GTK's TreeView with ListStore. Records are updated, but sometime only when I hover it.
Bigger problem are new records - It's like it stops to displaying new ones unless I scroll to bottom all the time - which is weird.
I use Glade.
My code (slightly simplified)
class UI(SampleUI):
gladefile = 'ui.glade'
iterdict = {}
def __init__(self, module):
super().__init__(module)
def start(self):
self.fetch_widgets()
self.connect_events()
self.window.show()
def fetch_widgets(self):
self.window = self.builder.get_object('window')
self.liststore = self.builder.get_object('store')
def connect_events(self):
handlers = {
"on_window_close" : Gtk.main_quit,
"on_start_working": self.start_working,
"on_stop_working": self.stop_working
}
self.builder.connect_signals(handlers)
self.module.url_pending_action = self.url_pending
self.module.url_succeed_action = self.url_update
def start_working(self, button):
self.module.start()
def stop_stop(self, button):
self.module.stop()
def url_pending(self, data):
self.iterdict[data['url']] = self.liststore.append([0, data['url'], 0, '', data['text']])
def url_update(self, data):
_iter = self.iterdict[data['url']]
self.liststore[_iter][1] = data['some_field1']
self.liststore[_iter][2] = data['some_field2']
Methods self.url_pending and self.url_update are called by threads (at most 30 running at the same time) created in self.module
I checked and new records are correctly appended into ListStore - I can read data from it. Window is working fine, but there are no new items at the bottom.
Ideas?
Ok, I made research and I figured out that GTK don't like being called from outside of main thread.
There was methods in Gdk to lock GTK calls
Gtk.threads_enter()
...
Gtk.threads_leave()
But now it's deprecated and GTK documentation says that every GTK call should be in main thread.
My workaround:
# [...]
def start(self):
# [...]
GObject.timeout_add(100, self.query_do)
# [...]
def query_do(self):
while len(self.gtk_calls):
self.gtk_calls.pop()()
GObject.timeout_add(100, self.query_do)
And I'm just adding into query
self.gtk_calls.insert(0, lambda: anything())
The code isn't as clear as it was before, but works perfect.

Categories

Resources