Hi: im trying to do a simple app on kivy and have an error in a loop.
Explain: i have 10 screens in the ScreenManager and i need clear all widgets except 4 of them.
A button on_press call the update() method of the ScreenManager.
The error: in the update() method i have the following code:
self.static_screens = ['list', 'new', 'conf', 'edit'] # declared in __init__ method
def update():
for screen in self.screens:
if screen.name not in self.static_screens:
self.remove_widget(screen)
the execute normally but remove just one widget (screen) from the ScreenManager
I'm not familiar with kivy, but I suspect the issue is that you are mutating an object that you are iterating over. Many people have similar issues when trying to do things such as removing particular items in a list using a for loop.
Here is a possible refactoring that might fix your issue.
def update():
screens_to_remove = [screen in self.screens if
screen.name not in self.static_screens]
for screen in screens_to_remove:
self.remove_widget(screen)
(Note that I haven't tested this, as I'm not sure what the rest of the code looks like.)
Related
I am working on a Python application that plots data from a large file containing records from lots and lots of sources. One of the options I am trying to give the user is the option to only plot for a subset of these sources if so desired. I accomplish this by first reading the files, finding out how many unique things there are, and then creating a QCheckBox() for each, named after its source (each source has a unique name). In this particular case, the data file is parsed into a giant dictionary where the keys are the unique source. I want to connect to the stateChange() event for each checkbox and then disable plotting for that source when the box is unchecked. Which in this case would be adding/removing the source from a list of sources when the box is checked/unchecked. The problem I am running into is that all of my checkboxes end up connecting to the final source in my list.
Initially, the window that is created looks correct, each button is named appropriately. Every time a button gets pressed, the btnstate() is supposed to simply print the text associated with that button. The method works if you can explicitly define each button, as shown by the radio buttons in the example. If you click either, you will get the correct name of the button printed, but when unchecking/rechecking ANY of the check boxes, btnstate prints "test4".
What am I doing wrong?
Here is the code (sources changed to dummy values):
import sys
from PyQt4.QtGui import *
def btnstate(b):
print b.text()
def main():
app = QApplication([])
widget = QWidget()
layout = QVBoxLayout()
widget.setLayout(layout)
radio_layout = QHBoxLayout()
checkbox_layout = QHBoxLayout()
#setup radio buttons for config pop-up window
r1 = QRadioButton("Page Count")
r2 = QRadioButton("Date")
r1.toggled.connect(lambda:btnstate(r1))
r2.toggled.connect(lambda:btnstate(r2))
radio_layout.addWidget(r1)
radio_layout.addWidget(r2)
cbs = []
for idx, serial in enumerate(["test1", "test2", "test3", "test4"]):
temp = QCheckBox(serial)
temp.setText(serial)
temp.setChecked(True)
checkbox_layout.addWidget(temp)
temp.stateChanged.connect(lambda:btnstate(temp))
cbs.append(temp)
layout.addLayout(radio_layout)
layout.addLayout(checkbox_layout)
widget.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I think the reason that this is happening has to do with how Python binds and unbinds references inside the loop. Because temp is being redefined on each iteration, the slot is also being updated, so that it effectively calls the same lambda for every button. That's sort of hand-wavy, because my understanding of the details of Python referencing is not super deep. But I know that the Python bindings to Qt have a lot of problems with Python's referencing and garbage collection, e.g. when deleting widgets, because the Qt object hierarchy doesn't totally work with Python.
Anyway, more practically, there is a pretty easy fix. You can use the functools.partial method to define a partial function as the slot, rather than a lambda. Bind the button as the first object, leaving the button state (emitted as the signal argument) unbound. Like so:
import functools
def btnstate(button, state):
print button.text()
Then in the loop:
for idx, serial in enumerate(['test1', 'test2', 'test3', 'test4']):
temp = QCheckBox(serial)
checkbox_layout.addWidget(temp)
temp.stateChanged.connect(functools.partial(btnstate, serial))
Running this, I now get the correct labels printed when each box is check/unchecked.
Edit:
See this post for another example of Python's reference counting interacting with strange ways with Qt's object hierarchy.
I am getting too fancy for my own good and inheriting MlabSceneModel, encapsulating some parameters and interactions that are specific to my application. explicity..
MyScene(MlabSceneModel):
#This part is just to give a flavor of why I would want to do this, but not part of the problem
#on_trait_change('activated')
def scene_activated(self):
self.scene.picker.pointpicker.add_observer(...)
def picker_callback(...)
#This is where the problem is
traits_view=View(Item('scene',editor=SceneEditor(scene_class=MayaviScene)))
As far as I can tell, everything works correctly, except for clicking on the top left button with "view the mayavi pipeline" hovertext. (The button is also a picture of the mayavi symbol). When I click this, I get
/tvtk/pyface/ui/qt4/scene_editor.py, line 47, in init:
"The SceneModel may only have one active editor!" AssertionError: The
SceneModel may only have one active editor!
I have a small python project that requires data to be pulled from a network and displayed every second (which is how often it changes) for a scientific application. 3 of these things are simply numbers, while another is a 128x128 camera image, which is brought in as an ndarray and drawn using matplotlib's imshow to a tkinter canvas.
I've tried two methods - using aniamtion.FuncAnimation() and after(interval, function), and both have the same result, which is that while the frame updates, the window can't be moved. and it feels jerky.
I assume that's something that can't be overcome (and probably doesn't matter)? I thought that perhaps multi-threading might help so the main window is on one thread, while the updated data can be on another?
Thanks!
Below is the basic code which now includes threading, and the error when closing the window is fixed by adding an event to the window close function, and also a few break commands while getting data that checks if the window is closed before it tries to interact with a GUI item that doesn't exist.
import blah, blah, blah
global safe_shutdown, window_status, my_thread
safe_shutdown = False
window_status = True
window = tk.Tk()
def widow_close():
window_status=False
while True:
if safe_shutdown == True:
window.destroy
return False
def get_updates():
while True:
#code to go get data from network
if window_status == False:
safe_shutdown == True:
break
#more code to place data on the GUI
if window_status == False:
safe_shutdown == True:
break
#only get updates once a second
time.sleep(1)
print "thread complete"
my_thread= threading.Thread(target=get_updates, args=()).start()
window.protocol("WM_DELETE_WINDOW", window_close)
window.mainloop()
I'm not an expert, so I'm not sure if there's a better solution. But I've had success with two separate solutions:
1: While it is not recomended to have the tkinter loop in a thread, you are allowed to have the data updating the tkinter app in a thread. This has worked pretty good for me in the past simply using the threading package. The thread will then just set the various stuff in need of a refresh.
2: Call the update_idletasks() on the window to force it to update. This can then be added at various places in you code where it would make sense to update the view.
Solution 1 whould take care of all stuttering, while solution 2 might just make it a bit better. I suppose it depends on your implementation.
I'm quite new to wxpython, yet to learn process or threading.
I have been checking online for my problem with non -responsive window whenever the data is processed. I have seen quite a few examples online but I couldnt find a solution to my issue (probably i'm not getting the point) cos i do not use thread, its just a normal simple program that uses a while loop and it has to be inside the while loop for around 150secs, at the same time i have redirected the print text to the log window, which gets freezed as well the window shows not responding when i go to someother tab while this program is under process kindly suggest me the solution.
The code goes like this
i have used Mike's logic in redirecting the text.
class RedirectText(object):
def __init__(self,aWxTextCtrl):
self.out=aWxTextCtrl
def write(self,string):
self.out.WriteText(string)
Class GUI(wx.Frame):
def __init__(self, parent):
# All the frame work
# redirected print to the stdout
def run(self, input):
#calls another operation
self.set_BankID(BankID, serSCU,SerialLogResult)
def set_BankID(self,BankID, serSCU,SerialLogResult):
#while loop
iteration=0
While iteration < 3:
wx.Yield()
while process = 0:
wait 150 secs
#end of inner while loop
#condition met for iteration
#End of outer while loop
if __name__ == '__main__':
app=wx.App(False)
frame =GUI(None)
frame.Show()
app.MainLoop()
The problem is that you are blocking the main loop, so the GUI can no longer update itself. You'll want to put that long running process into a thread and use one of wxPython threadsafe methods to send updates to the GUI. Those methods are as follows:
wx.CallAfter / wx.CallLater
wx.PostEvent
You can read about various approaches at the following locations:
http://wiki.wxpython.org/LongRunningTasks
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
I prefer using wx.CallAfter as I think it's the simplest.
I'm currently working on a project using Qt 5.0.2 on an embedded linux (ARM Cortex A9).
The main UI interface is developped in QML but I need to be able to hide this view to show a QWebView directly in C++.
I coded a simple view controller in c++ who hide()/show() the QML view and the many instances of QWebView.
The hiding/showing method work fine but when i show back the QML view, it's very instable. QML object are visible (or not visible :p) when they should not and the focus are buggy too. Object are draw in the wrong position too.
I try several methods :
-Initialize the focus/visible property of the differents objects everytime I show the QML view.
-use .setSource() everytime before showing the view
-try to update() the differents object thank to rootObject() before showing the view.
Did anyone have a tips to make the QML view functionnal again after a switch to a c++ view ?
thank.
there is probably a better way but,
you could probably do something like this (I have not tested this):
note: if the slot implementation is wrong (bad math) it will result in infinite recursion.
//this code could probably be in the constructor
real widthOverHeightRatio = 2;//set this value to what you want, or what it is when user first presses shift depending on the use case.
QObject::connect(this, SIGNAL(widthChange()), this, SLOT(onWidthChanged()));
QObject::connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
//don't forget to define these slots in the header
//implemented slots
void MyClass::onWidthChanged()
{
if(width/height!=widthOverHeightRatio){
height = width/widthOverHeightRatio;
}
}
void MyClass::onHeightChanged()
{
if(width/height!=widthOverHeightRatio){
width = height*widthOverHeightRatio;
}
}