I am making a Qt GUI with python and i am getting the error: QObject::startTimer: timers cannot be started from another thread. It occurs when I run the readModemSnap method. I've been working on this for almost a week trying many different design patterns for threading in Qt that i've found on the web but nothign works.
class ModemScopeWindow(QMainWindow, Ui_ModemScope):
def __init__(self, parent=None):
super(ModemScopeWindow, self).__init__(parent)
# Set up the user interface from Designer.
self.setupUi(self)
self.thread = MainThread()
"""
signal connections
"""
self.thread.newSnap.connect(self.updateScene)
self.thread.updateStatus.connect(self.setStatus)
self.thread.connectionLock.lock()
self.thread.runLock.lock()
self.connect(self.runButton, SIGNAL("clicked()"), self.thread.runLock.unlock, Qt.QueuedConnection)
self.connect(self.connectButton, SIGNAL("clicked()"), self.thread.connectionLock.unlock, Qt.QueuedConnection)
class MainThread(QThread):
newSnap = pyqtSignal(QGraphicsScene)
updateStatus = pyqtSignal(str)
initConnect = pyqtSignal()
def __init__(self, parent = None):
super(MainThread, self).__init__(parent)
self.samples = []
self.connectionLock = QMutex()
self.runLock = QMutex()
self.cliMute = QMutex()
self._displayCrosshairs = True
self._displayGrid = True
self.persistantMode = False
self.sampleDepth = 1
self._currentHaam = "4"
color = QColor(10,255,71)
self.plotPen = QPen(color)
self._leftXscene = -VIEW_SIZE/2
self._topYscene = -VIEW_SIZE/2
self._rightXscene = VIEW_SIZE/2
self._bottomYscene = VIEW_SIZE/2
self._leftXworld = -10.0
self._topYworld = 10.0
self._rightXworld = 10.0
self._bottomYworld = -10.0
self._scene = QGraphicsScene(self._leftXscene, self._topYscene, VIEW_SIZE, VIEW_SIZE, self)
self.start(QThread.HighestPriority)
def run(self):
self.updateStatus.emit("Enter target IP address and press Connect")
self.connectionLock.lock()
self.connectModem()
while(1):
self.runLock.lock()
#compile scene
self.readModemSnap()
self.newSnap.emit(self._scene)
self.runLock.unlock()
def readModemSnap(self):
self.updateStatus.emit("Reading Modem Snap...")
print len(self.samples)
if len(self.samples) >= self.sampleDepth:# and not self.persistantMode:
self.samples.pop(0)
self.cliMute.lock()
temp = cli.getModemSnap()
self.cliMute.unlock()
self.samples.append(temp)
self.cliMute.lock()
modType = cli.modemRead(80)
self.cliMute.unlock()
if((modType | 0x0FFFFFFF) == 0x0FFFFFFF):
modType = "0";
else:
modType = "%x"%modType
modType = str(modType)
modType = "0"
self.updateStatus.emit("Done")
self.refresh()
self._currentHaam = modType[0]
if self._displayGrid:
self.plotModulation(self._currentHaam)
self.handleSnapshotResponse()
self.updateStatus.emit("Ready to Run")
def refresh(self):
#delete scene
items = self._scene.items()
for x in items:
self._scene.removeItem(x)
#repaint the crosshairs
if self._displayCrosshairs:
self.plotLine(-VIEW_SIZE,0,+VIEW_SIZE,0, self.plotPen)
self.plotLine(0, -VIEW_SIZE,0, +VIEW_SIZE, self.plotPen)
self.plotScaleTicks()
#repaint grid
if self._displayGrid:
self.plotModulation(self._currentHaam)
self.newSnap.emit(self._scene)
def handleSnapshotResponse(self):
for x in range(len(self.samples)):
for sample in self.samples[x]:
upper = (sample >> 16) & 0xffff;
lower = sample & 0xffff
if (upper & 0x8000):
upper -= 0x10000
if (lower & 0x8000):
lower -= 0x10000
upper = float(upper)/128.0
lower = float(lower)/128.0
self.plot(upper, lower)
as you can see Im not starting any thread from another thread. i use the main to start the UI which creates a MainThread that starts itself upon construction. When i commented lines out to localize the problem I found that its when i call self.refresh() and self.handleSnapshotResponse() in the readModemSnap method. Can anyone point me in the direction of what im doing wrong? or any tutorials on QThreading? thanks in advance
This is the rule: you cannot call any GUI functions from any thread other than the main thread running the Qt event loop. When you see errors about QTimer, it's probably because something in the GUI uses a timer internally and it is being triggered from another thread.
The most likely culprit in your case is that you are operating on a QGraphicsScene from the worker thread. I would try rearranging such that the code in MainThread.reload is called in response to the newSnap signal, rather than before it.
Related
I am using PyQt6 6.1.0 on Python 3.9.6 and I would like to get the parent QMainWindow of the parent QTreeWidget of parents QTreeWidgetItems of a QTreeWidgetItem, because I need to modify the window from within the QTreeWidgetItem.
The full code is very long, over 1800 lines and over 60 KiB in size, when I put it in one file it works without problem, but when in one file its readability is very low, and when split into files the global variables can be hard to access from within classes.
Basically my idea is to call .parent() repetitively until the object's class name is 'MainWindow'.
def getWindow(obj):
window = obj.parent()
while True:
if type(window).__name__ == 'MainWindow':
break
window = window.parent()
return window
I didn't use the class directly because the class is defined in the main file.
Here are two classes defined in a file named groupA.py:
class TreeNode(QTreeWidgetItem):
def __init__(self, texts):
super().__init__()
self.isSongNode = False
self.setFlags(
FLAG.ItemIsAutoTristate
| FLAG.ItemIsEnabled
| FLAG.ItemIsSelectable
| FLAG.ItemIsUserCheckable
)
self.setCheckState(0, Qt.CheckState.Unchecked)
self.data = dict(zip(fields, texts))
self.song = None
for i in ("title", "album", "artist"):
if i in self.data:
if i == "title":
self.setTextAlignment(0, Qt.AlignmentFlag.AlignLeft)
self.setText(0, self.data["title"])
self.setToolTip(0, self.data["title"])
self.setText(1, self.data["duration"])
self.setText(2, self.data["instrumental"])
self.setText(3, self.data["downloaded"])
self.setText(4, ",".join(self.data["language"]))
self.setText(5, self.data["artistgender"])
for j in range(1, 6):
self.setTextAlignment(j, Qt.AlignmentFlag.AlignCenter)
self.song = song(*[self.data[i] for i in cells])
self.isSongNode = True
else:
self.setText(0, self.data[i])
self.setToolTip(0, self.data[i])
break
def detail(self):
print(self)
print(self.parent())
print(self.parent().parent())
print(self.parent().parent().parent())
window = getWindow(self)
if self.childCount() != 0:
for i in range(self.childCount()):
self.child(i).detail()
else:
if self.song not in detailed_items and self.isSongNode:
widget = SongPage(self.data)
window.detailArea.scrollArea.Layout.addWidget(widget)
widget.autoResize()
detailed_items.append(self.song)
class Tree(QTreeWidget):
def __init__(self):
super().__init__()
self.init()
def init(self):
self.setColumnCount(len(HEADERS))
self.setHeaderLabels(HEADERS)
font = Font(9)
self.setFont(font)
self.header().setFont(font)
self.setColumnWidth(0, 900)
fontRuler = QFontMetrics(font)
for i in range(1, len(HEADERS)):
Width = fontRuler.size(0, HEADERS[i]).width() + 16
self.setColumnWidth(i, Width)
self.setAutoScroll(True)
self.setIndentation(32)
self.setAlternatingRowColors(True)
self.setUniformRowHeights(True)
self.itemClicked.connect(self.onItemClicked)
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
#pyqtSlot(QTreeWidgetItem)
def onItemClicked(self, item):
window = getWindow(self)
nodes = self.findItems(
"", Qt.MatchFlag.MatchContains | Qt.MatchFlag.MatchRecursive
)
for node in nodes:
if (
node.checkState(0) == Qt.CheckState.Unchecked
and node.song in detailed_items
):
i = detailed_items.index(node.song)
detailed_items.remove(node.song)
layoutitem = window.detailArea.scrollArea.Layout.itemAt(i)
widget = layoutitem.widget()
window.detailArea.scrollArea.Layout.removeItem(layoutitem)
window.detailArea.scrollArea.Layout.removeWidget(widget)
elif (
node.childCount() == 0
and node.checkState(0) == Qt.CheckState.Checked
and node.isSongNode
):
node.detail()
if item.childCount() == 0 and item.isSongNode:
if item.song not in detailed_items:
item.detail()
index = detailed_items.index(item.song)
dim_others(window, index)
widget = window.detailArea.scrollArea.Layout.itemAt(index).widget()
QTimer.singleShot(
25, lambda: window.detailArea.scrollArea.ensureWidgetVisible(
widget)
)
widget.highlight()
setButtonsStatus(window)
The tree is filled by this:
def add_nodes(tree, cls, data):
indexes.clear()
for artist, albums in data.items():
artist_node = cls([artist])
indexes.add([artist])
for album, songs in albums.items():
album_node = cls([artist, album])
indexes.add([artist, album])
for song in songs:
song_node = cls([artist, album, *song])
indexes.add([artist, album, song[0]])
album_node.addChild(song_node)
artist_node.addChild(album_node)
tree.addTopLevelItem(artist_node)
And this is the start of code execution:
if __name__ == '__main__':
mysql80 = psutil.win_service_get("MySQL80").as_dict()
if mysql80["status"] != "running":
os.system("net start MySQL80")
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(u'Asceteria')
songs = fetchdata()
entries = treefy(songs)
app = QApplication(sys.argv)
app.setStyle("Fusion")
tree = Tree()
add_nodes(tree, TreeNode, entries)
filterArea = FilterArea()
Window = MainWindow()
print(tree)
print(tree.parent())
print(tree.parent().parent())
Window.showMaximized()
app.setWindowIcon(QIcon(ICON))
app.exec()
When I check any QTreeWidgetItem, the program crashes, because the top level QTreeWidgetItems don't have parents:
<groupA.Tree object at 0x0000022ABCFE9430>
<PyQt6.QtWidgets.QWidget object at 0x0000022AC10DBAF0>
<__main__.MainWindow object at 0x0000022AC10DBA60>
<groupA.TreeNode object at 0x0000022ABE087EE0>
<groupA.TreeNode object at 0x0000022ABE087E50>
<groupA.TreeNode object at 0x0000022ABE087DC0>
None
Traceback (most recent call last):
File "D:\Asceteria\groupA.py", line 148, in onItemClicked
node.detail()
File "D:\Asceteria\groupA.py", line 90, in detail
window = getWindow(self)
File "D:\Asceteria\functions.py", line 112, in getWindow
window = window.parent()
AttributeError: 'NoneType' object has no attribute 'parent'
How can I fix this?
It is not necessary to implement any method to obtain the window associated with a widget since Qt provides the window() method for it:
QWidget *QWidget::window() const Returns the window for this widget,
i.e. the next ancestor widget that has (or could have) a window-system
frame.
If the widget is a window, the widget itself is returned.
Typical usage is changing the window title:
aWidget->window()->setWindowTitle("New Window Title");
In your case use:
window = self.treeWidget().window()
Note: If you want to verify that an object is an instance of a class or its ancestor then you must use isinstace:
isinstance(window, MainWindow)
Note: The relationship between QTreeWidgetItem is different from that of QWidgets.
Is is a piece of my code . I have two classes CheckerScene and Checkers . CHesckers - is my main window . I can't realize EndGameSignal defined in CheckerScene class . When it emits , pySlot can't catch it in class Checkers , as i want . When my EndGameSignal emmits - i want to see a dialog message on my main screen (pyQtSlots functions realized in my code), not on the scene . How can i correct my program to do it .
class CheckerScene(QtWidgets.QGraphicsScene):
EndGameSignal=QtCore.pyqtSignal('QString')
def init(self):
QtWidgets.QGraphicsScene.init(self)
# scene congifuratios
self.setSceneRect(margin, margin, gridCount * gridSlotSize, gridCount * gridSlotSize)
self.addRect(self.sceneRect())
# create signal . It will be emit() from blackboard.crash()
self.signaldel.connect(self.del_item)
#choosing the visual checker and its coordinates
self.current = None
#list of grids and checkers
self.grid = []
self.white_checkers = []
self.black_checkers = []
for row in range(8):
for column in range(8):
# this is a "trick" to make the grid creation easier: it creates
# a grid square only if the row is odd and the column is even,
# and viceversa.
if (not row & 1 and column & 1) or (row & 1 and not column & 1):
# create a gridItem with a rectangle that uses 0-based
# coordinates, *then* we set its position
gridItem = self.addRect(0, 0, gridSlotSize, gridSlotSize)
gridItem.setPos(margin + column * gridSlotSize, margin + row * gridSlotSize)
gridItem.setBrush(QtGui.QColor(QtCore.Qt.lightGray))
self.grid.append(gridItem)
if 3 <= row <= 4:
# don't add checkers in the middle
continue
# create checkers being careful to assign them the gridItem
# as a *parent*; their coordinate will *always* be relative
# to the parent, so that if we change it, they will always
# be centered
if row < 3:
self.black_checkers.append(CheckerItem(0, gridItem))#!
else:
self.white_checkers.append(CheckerItem(1, gridItem))#!
self.additionsl__init__()
self.EndGameSignal.connect(Checkers.handler_EndGameSignal)
self.EndGameSignal.emit('=NAME')
class Checkers(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.Initialization()
def Initialization(self):
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
self.player2Label = QtWidgets.QLabel('Player 2')
layout.addWidget(self.player2Label)
self.player2Label.setAlignment(QtCore.Qt.AlignCenter)
self.checkerView = QtWidgets.QGraphicsView()
layout.addWidget(self.checkerView)
self.checkerScene = CheckerScene()
self.checkerView.setScene(self.checkerScene)
self.checkerView.setFixedSize(gridSize, gridSize)
# set the Antialiasing render hints to paint "smoother" shapes
self.checkerView.setRenderHints(QtGui.QPainter.Antialiasing)
self.player1Label = QtWidgets.QLabel('Player 1')
layout.addWidget(self.player1Label)
self.player1Label.setAlignment(QtCore.Qt.AlignCenter)
#QtCore.pyqtSlot(str)
def handler_EndGameSignal(self, result):
result=QtWidgets.QMessageBox.question(self,f"Выиграл {result}","Сиграть еще раз ?",QtWidgets.QMessageBox.Yes |
QtWidgets.QMessageBox.No,QtWidgets.QMessageBox.No)
if result == QtWidgets.QMessageBox.Yes :
self.close()
else :
pass
print(f"WINNER {result}")
#QtCore.pyqtSlot(bool)
def handler_EndGameSignal(self, result):
result = QtWidgets.QMessageBox.question(self, f"НИЧЬЯ !", "Сиграть еще раз ?",
QtWidgets.QMessageBox.Yes |
QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
if result == QtWidgets.QMessageBox.Yes:
self.close()
else:
pass
print("DRAW")
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
checkers = Checkers()
checkers.show()
sys.exit(app.exec_())
As with your previous question, you're still confusing classes and instancies. In your code you connect the signal to the class, while you have to connect it to the instance.
Since you have no reference with the receiver (the Checker instance) from the sender (the scene), you'll have to connect it from the former:
class Checkers(QtWidgets.QWidget):
def Initialization(self):
# ...
self.checkerScene.EndGameSignal.connect(self.handler_EndGameSignal)
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()
I'm trying to find a way to use libavg's event handlers from an embedded serial output. My understanding is that I need to create my own Publisher that I will call when I process serial commands. All I need is a way to create 10 different triggers given different serial inputs. An analogy of what I am trying to do would be to use libavg's keyboard handling to process different keyboard inputs.
I want the custom publisher to take the 10 serial outputs and pass a event.serialid parameter to various subscribers similarly to what event.keystring does.
Here is some nonfunctional code that I have that I think has the basics of what needs to be done.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from libavg import avg, statemachine, player
class Test():
PRESSED = avg.Publisher.genMessageID()
RELEASED = avg.Publisher.genMessageID()
def __init__(self, parent=None, **kwargs):
self.registerInstance(self, parent)
self.publish(self.PRESSED)
self.publish(self.RELEASED)
def isActive(self):
self.notifySubscribers(Test.PRESSED, [])
def isInactive(self):
self.notifySubscribers(Test.RELEASED, [])
def onKeyDown(event):
global node
if event.serialid == '1':
#serialid isn't implemented anywhere but this is what ideally I would like to have happen
node.color = "FF8000"
def onKeyUp(event):
global node
node.color = "FFFFFF"
player = avg.Player.get()
canvas = player.createMainCanvas(size=(640,480))
rootNode = player.getRootNode()
node = avg.WordsNode(pos=(10,10), font="arial", text="Hello World", parent=rootNode)
vbutton=Test()
node.subscribe(vbutton.PRESSED, onKeyDown)
node.subscribe(vbutton.RELEASED, onKeyUp)
player.play()
examples of custom publishers from here:
class _ButtonBase(avg.DivNode):
PRESSED = avg.Publisher.genMessageID()
RELEASED = avg.Publisher.genMessageID()
def __init__(self, parent=None, **kwargs):
super(_ButtonBase, self).__init__(**kwargs)
self.registerInstance(self, parent)
self.publish(self.PRESSED)
self.publish(self.RELEASED)
def _setActiveArea(self, upNode, activeAreaNode, fatFingerEnlarge):
self.__activeAreaNode = activeAreaNode
if fatFingerEnlarge:
if self.__activeAreaNode != None:
raise(RuntimeError(
"Button: Can't specify both fatFingerEnlarge and activeAreaNode"))
size = upNode.size
minSize = 20*player.getPixelsPerMM()
size = avg.Point2D(max(minSize, size.x), max(minSize, size.y))
self.__activeAreaNode = avg.RectNode(size=size, opacity=0, parent=self)
else:
if self.__activeAreaNode == None:
self.__activeAreaNode = self
else:
self.appendChild(self.__activeAreaNode)
self._tapRecognizer = gesture.TapRecognizer(self.__activeAreaNode,
possibleHandler=self._onDown,
detectedHandler=self._onTap,
failHandler=self._onTapFail)
You can pass arbitrary parameters through the publish-subscribe interface. The parameter(s) are passed as a list:
self.notifySubscribers(Test.PRESSED, [serialID])
And in the handler:
def onKeyDown(serialID):
global node
if serialid == '1':
node.color = "FF8000"
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