I am writing a program that should display some data that's going over a CanBus (data is being refreshed every 200ms), however when I make an objects from my class like this:
batteryDegree = PlaceInfo(root,img='battery.png',fotox=0.23,fotoy=0.35,foto_y_resize=180,foto_x_resize=180,anchor='center', text="100*C",textx=0.23,texty=0.38,canid=0x1000400,byte=2,size=30)
motorDegree = PlaceInfo(root,img='motor.png',fotox=0.75,fotoy=0.35,foto_y_resize=200,foto_x_resize=200,anchor='center', text="100*C",textx=0.76,texty=0.37,canid=0x1000400,byte=0,size=30)
Here is my class:
bus = can.interface.Bus(channel='can0')
root = Tk()
root.geometry("800x480")
root.config(bg='red')
canvas = Canvas(root,width=800,height=480,highlightthickness=0)
canvas.pack()
class PlaceInfo():
def __init__(self,root, **kwargs):
self.root = root
if 'img' in kwargs:
if 'foto_x_resize' in kwargs and 'foto_y_resize' in kwargs:
self.png = ImageTk.PhotoImage(Image.open('imgs/' + kwargs['img']).resize((kwargs['foto_x_resize'],kwargs['foto_y_resize']),Image.ANTIALIAS))
else:
self.png = ImageTk.PhotoImage(Image.open('imgs/' + kwargs['img']))
self.image = canvas.create_image(800 * kwargs['fotox'],480 * kwargs['fotoy'] ,image=self.png,anchor='center')
if 'text' in kwargs:
self.text = canvas.create_text(kwargs['textx'] * 800,
kwargs['texty'] * 480,
text=kwargs['text'],
fil=kwargs['kleur'] if 'kleur' in kwargs else 'white',
font=('arial',kwargs['size'] if 'size' in kwargs else 10))
if 'canid' in kwargs and 'index' not in kwargs:
self.changeData(kwargs['canid'], kwargs['byte'],kwargs['textx'], kwargs['texty'],kwargs['size'])
elif 'canid' in kwargs and 'index' in kwargs:
self.changeDataWithIndex(kwargs['canid'], kwargs['byte'],kwargs['textx'], kwargs['texty'],kwargs['size'],kwargs['index'])
def changeData(self,canid,byte,textx,texty,size):
try:
msg = bus.recv(1)
canvas.delete(self.text)
if msg.arbitration_id == canid:
self.data = msg.data[byte]
self.text = canvas.create_text(textx * 800,
texty * 480,
text=str(self.data),
fill='white',
font=('arial',size))
except Exception as e:
print(e)
self.root.after(250,lambda: self.changeData(canid,byte,textx,texty,size))
The program works for a few seconds but breaks after, what I mean is that it just stops displaying data and when I move my window around the colors are all over the place.
(When I print something in my functions it still prints)
Since you have more than one instance of PlaceInfo, therefore there is more than one after task reading the CanBus which may cause race condition. Also after task A may read the message for task B and vice versa.
You need to centralize the reading of the CanBus and perform the reading in a thread because recv() is a blocking task.
So you can create a CanReader class which extends from threading.Thread and call recv() in its run() function:
from threading import Thread
from queue import SimpleQueue
class CanReader(Thread):
def __init__(self, channel):
super().__init__(daemon=True)
self._bus = can.interface.Bus(channel=channel)
self._queues = {}
def register(self, canid):
if canid not in self._queues:
self._queues[canid] = []
queue = SimpleQueue()
self._queues[canid].append(queue)
return queue
def run(self):
while True:
msg = self._bus.recv()
for q in self._queues[msg.arbitration_id]:
q.put(msg)
Then you can create an instance of CanReader as below:
bus = CanReader('can0')
bus.start()
CanReader provides a function register() for PlaceInfo to register and get a SimpleQueue object holding the messages received by CanReader.
Below is modified PlaceInfo:
class PlaceInfo():
def __init__(self, root, **kwargs):
self.root = root
img = kwargs.get('img', None)
if img:
image = Image.open('images/'+img)
foto_x_resize, foto_y_resize = kwargs.get('foto_x_resize'), kwargs.get('foto_y_resize')
if foto_x_resize and foto_y_resize:
image = image.resize((foto_x_resize, foto_y_resize), Image.ANTIALIAS)
self.png = ImageTk.PhotoImage(image)
self.image = canvas.create_image(800*kwargs['fotox'], 480*kwargs['fotoy'], image=self.png, anchor='center')
textx = kwargs.get('textx', None)
texty = kwargs.get('texty', None)
fill = kwargs.get('kleur', 'white')
size = kwargs.get('size', 10)
text = kwargs.get('text', None)
if text:
self.text = canvas.create_text(textx*800, texty*480, text=text, fill=fill, font=('arial',size))
canid = kwargs.get('canid', None)
if canid:
self.queue = bus.register(canid) # register to CanReader using canid
if 'index' not in kwargs:
self.changeData(canid, kwargs['byte'])
else:
self.changeDataWithIndex(canid, kwargs['byte'], kwargs['index'])
def changeData(self, canid, byte):
try:
msg = self.queue.get_nowait() # read message
self.data = msg.data[byte]
canvas.itemconfigure(self.text, text=str(self.data))
except Exception as e:
print(e)
self.root.after(250, self.changeData, canid, byte)
Hope this help you solve the issue.
Related
Removing a QGraphicsPathItem from a QGraphicsScene raises "QGraphicsScene::removeItem: item 0x204f6a20880's scene (0x0) is different from this scene (0x204f7e519c0)".
I also printed the .scene() method of the item and printed the scene, both of which are the same - "<examples.example_calculator.nodes.operations.GraphScene object at 0x00000204F79E5C10>".
So I do not understand how the scene I am deleting the item from, is not the same as the one that the item is in, which is proved true by my two print statements, however the error still comes up.
Here is the code:
class GraphCurve(QGraphicsView):
def __init__(self, scene):
super().__init__()
self.scene = scene
self.setScene(scene)
self.setSceneRect(0,0,self.scene.sx, self.scene.sy)
self.pntrad = 10
self.selected=None
self.cpoints=[]
self.ccpoints=[]
self.points = []
self.paths = []
######WHERE THE ERROR COMES UP######
def updateCurve(self, mvpnt):
try:
x = self.cpoints.index(mvpnt)
mvpnt = self.points[x]
except:
try:
x = self.ccpoints.index(mvpnt)
mvpnt = self.points[x]
except:
pass
try:
ind = self.points.index(mvpnt)
######WHERE THE ERROR COMES UP##########################################################
print(str(self.scene)) #####MARK THIS AS A#####
print(str(self.paths[ind-1].scene())) #####SAME AS A#####
self.scene.removeItem(self.paths[ind-1])
except:
pass
try:
######WHERE THE ERROR COMES UP##########################################################
self.scene.removeItem(self.paths[ind])
except:
pass
points = [self.points[ind], self.points[ind-1]]
for x in points:
try:
drawpath = QPainterPath()
drawpath.moveTo(QPointF(x.pos().x()+self.pntrad/2, x.pos().y()+self.pntrad/2))
drawpath.cubicTo(QPointF(self.ccpoints[self.points.index(x)].pos().x()+(self.pntrad/2), self.ccpoints[self.points.index(x)].pos().y()+(self.pntrad/2)),
QPointF(self.cpoints[self.points.index(x)+1].pos().x()+(self.pntrad/2), self.cpoints[self.points.index(x)+1].pos().y()+(self.pntrad/2)),
QPointF(self.points[self.points.index(x)+1].pos().x()+(self.pntrad/2), self.points[self.points.index(x)+1].pos().y()+(self.pntrad/2)))
######WHERE THE ITEM IS CREATED#####################################################
path = self.scene.addPath(drawpath)
print(str(path.scene())) #####THIS IS SAME AS A#####
self.paths.append(path)
except:
pass
I have 2 QTreeViews both with a QFileSystemModel attached. One is a local directory and one is pointing at a server.
I am looking to change the colors of text on files based on its relation to the opposite location. For example:
If both server and local have the same modified time - set to green
If the server is more up to date - set local to red, server to orange. Etc etc.
I have had multiple working examples of this but I keep running into the same roadblock, sluggy UI response. I am using the data() function in both QFileSystemModels to achieve this, I tried threading the operation outside of data() to return the color back but it just makes all my files turn in to a light show. Files will randomly go green/red/orange based on me moving the mouse around.
I currently have the date modified column visible as a workaround but it doesn't help the user know if they have a file from the server locally already.
Has anyone got anything like this working without having the UI chug? Or even found a method to return the result from a thread and have it work for the correct entry?
I appreciate this may look a little messy but here is where I init the roots (some of these globals are used elsewhere)
def initRoots(self, loc):
'''
This function sets the starting directories for both server and local
Does not handle anything except top level
'''
global localMask
global serverMask
global serverRoot
global userRoot
global GserverModel
global GlocalModel
if loc == "Server":
self.serverRoot = self.cb_serverWorkspace.currentText()
serverRoot = self.serverRoot
self.serverModel = ServerFileSystemModel()
GserverModel = self.serverModel
self.tv_serverFiles.setModel(self.serverModel)
#self.serverModel.setRootPath(QtCore.QDir.rootPath())
self.serverModel.setRootPath("")
self.tv_serverFiles.setRootIndex(self.serverModel.index(self.serverRoot))
self.tv_serverFiles.setUniformRowHeights(True)
self.tv_serverFiles.setExpandsOnDoubleClick(True)
self.tv_serverFiles.hideColumn(1)
self.tv_serverFiles.hideColumn(2)
self.tv_serverFiles.setColumnWidth(0,400)
if loc == "Local":
self.userRoot = self.cb_localWorkspace.currentText()
userRoot = self.userRoot
self.localModel = LocalFileSystemModel()
GlocalModel = self.localModel
self.tv_localFiles.setModel(self.localModel)
#self.localModel.setRootPath(QtCore.QDir.rootPath())
self.localModel.setRootPath("")
self.tv_localFiles.setRootIndex(self.localModel.index(self.userRoot))
self.tv_serverFiles.setUniformRowHeights(True)
self.tv_localFiles.setExpandsOnDoubleClick(True)
self.tv_localFiles.hideColumn(1)
self.tv_localFiles.hideColumn(2)
self.tv_localFiles.setColumnWidth(0,400)
Then my two QFileSystemModels are pretty much this (This is my non-threaded example)
class ServerFileSystemModel(QtWidgets.QFileSystemModel):
def __init__(self, *args, **kwargs):
super(ServerFileSystemModel, self).__init__(*args, **kwargs)
def data(self, index, role=QtCore.Qt.DisplayRole):
try:
global GlocalModel
global GserverModel
serverPath = self.filePath(index)
localMask = userRoot + "/"
serverMask = serverRoot + "/"
newPath = serverPath.replace(serverMask, localMask)
serverIndex = GserverModel.index(serverPath)
localIndex = GlocalModel.index(newPath)
if role == QtCore.Qt.TextColorRole and serverPath.endswith(".fbx") and os.path.exists(newPath) and downloading == False:
if GlocalModel.lastModified(localIndex) == GserverModel.lastModified(serverIndex):
return QtGui.QColor("#58cd1c")
if GlocalModel.lastModified(localIndex) < GserverModel.lastModified(serverIndex):
return QtGui.QColor("#ed7011")
if GlocalModel.lastModified(localIndex) > GserverModel.lastModified(serverIndex):
return QtGui.QColor("#ed1111")
return super(ServerFileSystemModel, self).data(index, role)
except:
return super(ServerFileSystemModel, self).data(index, role)
Anything else just let me know.
EDIT******
I was asked to put minimal replicable code so here we go
MainWindow
class WorkspacerUI(QMainWindow, Ui_MainWindow):
def __init__(self, parent = None):
super(WorkspacerUI, self).__init__(parent)
self.setupUi(self)
self.userRoot = "D:/OCooke_Workspace"
self.serverRoot = "D:/OCooke_Server"
self.initRoots("Local")
self.initRoots("Server")
def initRoots(self, loc):
'''
This function sets the starting directories for both server and local
Does not handle anything except top level
'''
if loc == "Server":
self.serverModel = ServerFileSystemModel()
self.tv_serverFiles.setModel(self.serverModel)
#self.serverModel.setRootPath(QtCore.QDir.rootPath())
self.serverModel.setRootPath("")
self.tv_serverFiles.setRootIndex(self.serverModel.index(self.serverRoot))
self.tv_serverFiles.setUniformRowHeights(True)
self.tv_serverFiles.setExpandsOnDoubleClick(True)
self.tv_serverFiles.hideColumn(1)
self.tv_serverFiles.hideColumn(2)
self.tv_serverFiles.setColumnWidth(0,400)
if loc == "Local":
self.localModel = LocalFileSystemModel()
self.tv_localFiles.setModel(self.localModel)
#self.localModel.setRootPath(QtCore.QDir.rootPath())
self.localModel.setRootPath("")
self.tv_localFiles.setRootIndex(self.localModel.index(self.userRoot))
self.tv_serverFiles.setUniformRowHeights(True)
self.tv_localFiles.setExpandsOnDoubleClick(True)
self.tv_localFiles.hideColumn(1)
self.tv_localFiles.hideColumn(2)
self.tv_localFiles.setColumnWidth(0,400)
QRunnable with Signals
class WorkerSignals(QObject):
finished = Signal()
result = Signal(object)
progress = Signal(int)
class ColourWorker(QRunnable):
'''
Worker thread
Inherits from QRunnable to handler worker thread setup, signals and wrap-up.
:param callback: The function callback to run on this worker thread. Supplied args and
kwargs will be passed through to the runner.
:type callback: function
:param args: Arguments to pass to the callback function
:param kwargs: Keywords to pass to the callback function
'''
def __init__(self, fn, *args, **kwargs):
super(ColourWorker, self).__init__()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
# Add the callback to our kwargs
self.kwargs['progress_callback'] = self.signals.progress
self.kwargs['indexy']
#Slot()
def run(self):
'''
Initialise the runner function with passed args, kwargs.
'''
# Retrieve args/kwargs here; and fire processing using them
try:
result = self.fn(*self.args, **self.kwargs)
except:
traceback.print_exc()
exctype, value = sys.exc_info()[:2]
self.signals.error.emit((exctype, value, traceback.format_exc()))
else:
self.signals.result.emit(result) # Return the result of the processing
finally:
self.signals.finished.emit() # Done
Finally the 2 QSystemFileModels
class ServerFileSystemModel(QFileSystemModel):
def __init__(self, *args, **kwargs):
super(ServerFileSystemModel, self).__init__(*args, **kwargs)
self.threadpool = QThreadPool()
self.colour = None
def data(self, index, role=Qt.DisplayRole):
if role == Qt.TextColorRole:
worker = ColourWorker(self.execute_this_fn, indexy = index) # Any other args, kwargs are passed to the run function
worker.signals.result.connect(self.print_output)
worker.signals.finished.connect(self.thread_complete)
# Execute
self.threadpool.start(worker)
return self.colour
return super(ServerFileSystemModel, self).data(index, role)
#
def execute_this_fn(self, progress_callback, indexy):
serverPath = self.filePath(indexy)
localMask = "D:/OCooke_Workspace/"
serverMask = "D:/OCooke_Server/"
#create an expected path to the local using the server address
# D:/OCooke_Server/2020/file1 turns into D:/OCooke_Workspace/2020/file1
newPath = serverPath.replace(serverMask, localMask)
#Check the stat time between the two locations
if os.stat(newPath).st_mtime == os.stat(serverPath).st_mtime:
return QColor("#58cd1c")
if os.stat(newPath).st_mtime < os.stat(serverPath).st_mtime:
return QColor("#ed7011")
if os.stat(newPath).st_mtime > os.stat(serverPath).st_mtime:
return QColor("#ed1111")
else:
return None
def print_output(self, s):
#return the value of color
self.colour = s
def thread_complete(self):
#ive tried passing the color once the thread completes, no joy
pass
class LocalFileSystemModel(QFileSystemModel):
def __init__(self, *args, **kwargs):
super(LocalFileSystemModel, self).__init__(*args, **kwargs)
def data(self, index, role=Qt.DisplayRole):
return super(LocalFileSystemModel, self).data(index, role)
MAIN
def main():
app = QApplication(sys.argv)
MainWindow = WorkspacerUI()
MainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I created a dialog in blender 2.80 to be able to insert some values and send them through the "Ok" button.
Inside the "execute" function some operations are performed and, at the end, the window is closed. However, in case of exceptions, the problem must be handled by notifying the user and leaving the dialog box open. To do this I use a try-catch block but it does not work: in case of exception the dialog box closes whatever the returned value.
Is there any way to fix this?
#subscribe
class Test(Operator):
bl_idname = "bl_idname"
bl_label = "bl_label"
bl_description = "bl_description"
input_a = bpy.props.StringProperty(
name = "name_a",
default = "default"
)
input_b = bpy.props.StringProperty(
name = "name_b",
default = "default"
)
input_c = bpy.props.StringProperty(
name = "name_c",
default = "default"
)
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, width = 450)
def draw(self, context):
col = self.layout.column(align = True)
col.prop(self, "input_a")
col = self.layout.column(align = True)
col.prop(self, "input_b")
col = self.layout.column(align = True)
col.prop(self, "input_c")
def execute(self, context):
try:
#something...
return {"FINISHED"}
except Exception as e:
print(str(e))
return {"CANCELLED"} #I also tried RUNNING_MODAL or PASS_THROUGH
I've wrote an little app to scroll an stock market ticker into a Tkinter window and update the stock values every minute using a threading class.
If I closed the window the main process didn't stops, then I've implemented a method to close the window and stop the thread. But when I close the window the main process delay the stopping to the next call to the theading.run() method.
How can I get that an instant closing process?
The code:
# -*- coding: UTF-8 -*-
import tkinter as tk
import tkinter.messagebox
import time
import threading
from random import randint as randint, uniform as randlimit
# Here starts the program working process, until here was the GUI
# CONSTANTS
CHAR_UP = "\u25B2"
CHAR_DOWN = "\u25BC"
CHAR_EVEN = "="
UPDATE_TIME = 60
# INITIAL DATA, this must be changed to implement the load of a external source
stock_market = [["GOOG", "587.25", CHAR_UP, "(+12.14)"],
["AAPL", "237.14", CHAR_UP, "(+7.25)"],
["GTAT", "87.47", CHAR_DOWN, "(-1.18)"],
["KNDI", "167.32", CHAR_UP, "(+6.85)"],
["ORCL", "482.91", CHAR_DOWN, "(-24.65)"],
["FBOK", "327.67", CHAR_DOWN, "(-11.78)"],
["TWTR", "842.41", CHAR_UP, "(+15.45)"]]
class AplicationTkinter(tk.Frame):
"""
Class of tkinter.Frame subclass, Initializes the GUI
constants:
SPEED, the delay in millisecs of the scroll
attributes:
parent, the root Tk object
"""
SPEED = 250
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self._initGUI()
self._scroll_ticker()
self.parent.protocol("WM_DELETE_WINDOW",self.stop)
# or toplevel.protocol(...
def _initGUI(self):
"""
initGUI, draws the layout.
"""
# changes the window icon
self.parent.iconbitmap("tabla.ico")
self.parent.title("Stock Exchange Ticker")
# fix a status bar at the bottom of the window, for future improvements
self.status_bar = tk.Label(self.parent, text="", bd=1, relief=tk.SUNKEN,
anchor=tk.W)
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
# content Frame for entry, for future improvements
self.frame = tk.Frame(self.parent)
self.frame.pack()
self.var_entry = tk.StringVar()
self.entry = tk.Entry(self.frame, textvariable=self.var_entry)
self.entry.pack()
self.var_entry.set("a default value")
str_ent_1 = self.entry.get()
# content LabelFrame to show the ticker scrolling line of text
self.label_frame = tk.LabelFrame(self.parent,
text="Ventana de Resultados")
self.label_frame.pack()
# creates an instance of the StockMarket class for contents the data
self.market_one = StockMarket(stock_market)
# the scrolling line of Text for show the data
self.txt_ticker_widget = tk.Text(self.label_frame, background='black',
height=1, width=56, wrap="none")
self.txt_ticker_widget.pack(side=tk.TOP, fill=tk.X)
self.txt_ticker_widget.tag_configure("up", foreground="green")
self.txt_ticker_widget.tag_configure("down", foreground="red")
self.txt_ticker_widget.tag_configure("even", foreground="white")
self.tag = {CHAR_DOWN: "down", CHAR_EVEN: "even", CHAR_UP: "up"}
def _scroll_ticker(self):
"""
scroll_ticker, inserts character by character in the Text widget
"""
self.txt_ticker_widget.configure(state=tk.NORMAL)
self.txt_ticker_widget.insert(tk.END,
self.market_one.get_next_character(),
self.tag[self.market_one.get_tag()])
# TODO simplify
self.txt_ticker_widget.see(tk.END)
self.txt_ticker_widget.configure(state=tk.DISABLED)
self.txt_ticker_widget.after(self.SPEED, self._scroll_ticker)
# recursive each interval of millisecs, constant SPEED
def stop(self):
if tk.messagebox.askokcancel("Quit?", "Are you sure you want to quit?"):
# self.destroy()
print("STOPPING !!")
self.market_one.thread_updating.stop()
self.destroy()
self.parent.quit()
class StockTicker():
"""
Class StockTicker, handle each stock symbol and their data
attributes:
symbol, string, the abbreviature of the securitie
price, string, the current price of the securitie
direction, string(1), is a character that indicates its las fix price
went up, down or even
change, string, is the value of the last change surrounded by '()',
the first character is '+' or '-'
"""
def __init__(self, list_data):
self.symbol, self.price, self.direction, self.change = list_data
def update_ticker(self):
"""
update_ticker, update the securitie price, direction and
change with random values
"""
flt_price = float(self.price)
if randint(0, 9) == 0:
self.direction = CHAR_EVEN
else:
increase_percent = randlimit(-5, 5)
# TODO implementar normalvariate(0, 0.02) o gauss(0, 0.02)
flt_change = flt_price * increase_percent / 100
flt_new_price = flt_price + flt_change
self.price = "{:.2f}".format(flt_new_price)
if flt_change < 0:
self.direction = CHAR_DOWN
elif flt_change == 0:
self.direction = CHAR_EVEN
else:
self.direction = CHAR_UP
self.change = "({:+.2f})".format(flt_change)
def ticker_to_text(self):
"""
ticker_to_text, returns a formatted string with all the data of
the securitie.
"""
return " | {} {} {} {} ".format(self.symbol, self.price,
self.direction, self.change)
class StockMarket():
"""
Class StockMarket, creates and handle a list of StockTicker objects,
and provide to the GUI of stuff for the scroll ticker
attributes:
smarket, list of StockTicker objects
thread_actualizar, Thread object to update the stock market
each time interval
"""
def __init__(self, l_inicial):
self.tickers = []
self.index = 0
self._load(l_inicial)
self.current_ticker = self._get_one_ticker()
self.thread_updating = UpdateThread(self)
self.thread_updating.start()
def _load(self, l_inicial):
"""
load_market, load the list with StockTicker object taking the data from
the initial source data.
"""
for data_ticker in l_inicial:
simple_ticker = StockTicker(data_ticker)
self.tickers.append(simple_ticker)
def update_market(self):
"""
update_market, update the objects of the list
"""
for ticker in self.tickers:
ticker.update_ticker()
def _get_one_ticker(self):
"""
get_one_ticker, getter function to return one securitie data in text
format and rotates to the next one
"""
self.one_ticker = self.tickers.pop(0)
self.tickers.append(self.one_ticker)
self.index = 0
return self.one_ticker.ticker_to_text()
def get_next_character(self):
"""
get_next_character, returns a character of one securitie
(if the securitie data is exhausted retrieve another securitie)
data to the GUI.
"""
if self.index == len(self.current_ticker):
self.current_ticker = self._get_one_ticker()
self.index = 0
character_symbol = self.current_ticker[self.index:self.index+1]
self.index += 1
return character_symbol
def get_tag(self):
return self.one_ticker.direction
class UpdateThread(threading.Thread):
"""
Class UpdateThread(), subclass of Thread, handle the time to the next
update of the stock market values
args:
market_1, a StockMarket class object to update
attributes:
my_check, string for debugging purpouses, it'll be implemented the
source data management the_market, StockMarket object that will
be updated
"""
def __init__(self, market_1):
self.my_check = " CHECK " # TODO replace with initial source data.
self.the_market = market_1
self.is_quit = False
threading.Thread.__init__(self)
def run(self):
"""
run, overrides the Thread run method, and calls the update_market
method of StockMarket class each interval
"""
time.sleep(UPDATE_TIME)
self.the_market.update_market()
print(" UPDATED!!!") # for debugging
if not self.is_quit:
self.run()
def stop(self):
"""
stop, for stopping the thread when event occurs
"""
self.is_quit = True
# STARTS THE PROGRAM
def main():
the_window = tk.Tk()
aplication = AplicationTkinter(the_window)
# init the GUI process
the_window.mainloop()
if __name__ == '__main__':
main()
In my project i noticed that the main dialog freezes when it is setting the model of some ProxyModel, so i decided to create a new thread for this task to provide the responsiveness of the window, but right now it keeps popping an error that say:
TypeError: QTableView.setModel(QAbstractItemModel): argument 1 has unexpected type 'tuple'
and i don't know why...
Here's my code:
This is the QThread for updating the proxyModel with the arguments i provide
class ThreadedProxyModel(QThread):
def __init__(self, contacts, contactsProxyModel, groups, groupsProxyModel,
chatSession, chatSessionProxyModel, msgs, msgsProxyModel):
QThread.__init__(self)
self.contacts = contacts
self.contactsProxyModel = contactsProxyModel
self.groups = groups
self.groupsProxyModel = groupsProxyModel
self.chatSession = chatSession
self.chatSessionProxyModel = chatSessionProxyModel
self.msgs = msgs
self.msgsProxyModel = msgsProxyModel
def run(self):
self.contactsProxyModel.setSourceModel(recordsTableModel(self.contacts))
self.contactsProxyModel.setFilterKeyColumn(-1)
self.contactsProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
self.groupsProxyModel.setSourceModel(recordsTableModel(self.groups))
self.groupsProxyModel.setFilterKeyColumn(-1)
self.groupsProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
self.chatSessionProxyModel.setSourceModel(recordsTableModel(self.chatSession))
self.chatSessionProxyModel.setFilterKeyColumn(-1)
self.chatSessionProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
self.msgsProxyModel.setSourceModel(recordsTableModel(self.msgs))
self.msgsProxyModel.setFilterKeyColumn(-1)
self.msgsProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
def getContactsProxyModel(self):
return self.contactsProxyModel,
def getGroupsProxyModel(self):
return self.groupsProxyModel
def getChatSessionProxyModel(self):
return self.chatSessionProxyModel
def getMsgsProxyModel(self):
return self.msgsProxyModel
And this is the method calling the setProxyModel thread in the dialog class. Notice that all the data (contacts, groups, chatsession...) is fine:
def setProxyModel(self):
progress = QProgressDialog("Initializing UI ...", "Abort", 0, 0, self)
progress.setWindowTitle("WhatsApp Browser ...")
progress.setWindowModality(Qt.WindowModal)
progress.setMinimumDuration(0)
progress.setCancelButton(None)
progress.show()
queryTh = ThreadedProxyModel(self.contacts, self.contactsProxyModel, self.groups, self.groupsProxyModel,
self.chatSession, self.chatSessionProxyModel, self.msgs, self.msgsProxyModel,)
queryTh.start()
while queryTh.isRunning():
QApplication.processEvents()
self.contactsProxyModel = queryTh.getContactsProxyModel()
self.groupsProxyModel = queryTh.getGroupsProxyModel()
self.chatSessionProxyModel = queryTh.getChatSessionProxyModel()
self.msgsProxyModel = queryTh.getMsgsProxyModel()
progress.close()
And this is in the init method in my dialog, i create the proxymodels and call the method for updating them in the Thread and then i set them up in various QTableView:
self.contactsProxyModel = QSortFilterProxyModel(self)
self.groupsProxyModel = QSortFilterProxyModel(self)
self.groupMembersProxyModel = QSortFilterProxyModel(self)
self.chatSessionProxyModel = QSortFilterProxyModel(self)
self.chatMsgsProxyModel = QSortFilterProxyModel(self)
self.msgsProxyModel = QSortFilterProxyModel(self)
self.setProxyModel()
self.contactsTableView.setModel(self.contactsProxyModel)
self.contactsTableView.resizeColumnsToContents()
self.groupsTableView.setModel(self.groupsProxyModel)
self.groupsTableView.resizeColumnsToContents()
self.chatSessionTableView.setModel(self.chatSessionProxyModel)
self.chatSessionTableView.resizeColumnsToContents()
self.chatSessionTableView.clicked.connect(self.setChatMsgsProxyModel)
self.chatMsgsTableView.resizeColumnsToContents()
self.groupsTableView.clicked.connect(self.setGroupMembersProxyModel)
self.groupMembersTableView.resizeColumnsToContents()
self.msgsTableView.setModel(self.msgsProxyModel)
self.msgsTableView.resizeColumnsToContents()
Thank you for any advice, i'm pretty stuck...
Not sure, but it seems that
def getContactsProxyModel(self):
return self.contactsProxyModel,
return a tuple, try to delete the comma