Browse Source

WalletSynchronizer had a race condition caused by calling the callback before the Qt event loop (or other initialisation) finished. Ergo we split initialisation and the running of the thread, then use Qt SIGNALs to yield back into the Qt event loop. This ensures that the callback for the servers_list_changed is not called until the main Qt event loop is actually running.

master
Amir Taaki 13 years ago
parent
commit
b3b910d926
  1. 3
      electrum
  2. 21
      lib/gui_lite.py
  3. 3
      lib/gui_qt.py
  4. 18
      lib/interface.py

3
electrum

@ -173,7 +173,8 @@ if __name__ == '__main__':
sys.exit("Error: Unknown GUI: " + options.gui) sys.exit("Error: Unknown GUI: " + options.gui)
gui = gui.ElectrumGui(wallet) gui = gui.ElectrumGui(wallet)
WalletSynchronizer(wallet,True).start() interface = WalletSynchronizer(wallet, True, gui.server_list_changed)
interface.start()
try: try:
found = wallet.file_exists found = wallet.file_exists

21
lib/gui_lite.py

@ -42,14 +42,18 @@ def resize_line_edit_width(line_edit, text_input):
text_input += "A" text_input += "A"
line_edit.setMinimumWidth(metrics.width(text_input)) line_edit.setMinimumWidth(metrics.width(text_input))
class ElectrumGui: class ElectrumGui(QObject):
def __init__(self, wallet): def __init__(self, wallet):
super(QObject, self).__init__()
self.wallet = wallet self.wallet = wallet
self.app = QApplication(sys.argv) self.app = QApplication(sys.argv)
def main(self, url): def main(self, url):
actuator = MiniActuator(self.wallet) actuator = MiniActuator(self.wallet)
self.connect(self, SIGNAL("updateservers()"),
actuator.update_servers_list)
# Should probably not modify the current path but instead # Should probably not modify the current path but instead
# change the behaviour of rsrc(...) # change the behaviour of rsrc(...)
old_path = QDir.currentPath() old_path = QDir.currentPath()
@ -74,6 +78,9 @@ class ElectrumGui:
self.app.exec_() self.app.exec_()
def server_list_changed(self):
self.emit(SIGNAL("updateservers()"))
def expand(self): def expand(self):
"""Hide the lite mode window and show pro-mode.""" """Hide the lite mode window and show pro-mode."""
self.mini.hide() self.mini.hide()
@ -495,16 +502,13 @@ class ReceivePopup(QDialog):
QCursor.setPos(center_mouse_pos) QCursor.setPos(center_mouse_pos)
self.show() self.show()
class MiniActuator(QObject): class MiniActuator:
"""Initialize the definitions relating to themes and """Initialize the definitions relating to themes and
sending/recieving bitcoins. sending/recieving bitcoins."""
"""
def __init__(self, wallet): def __init__(self, wallet):
"""Retrieve the gui theme used in previous session.""" """Retrieve the gui theme used in previous session."""
super(QObject, self).__init__()
self.wallet = wallet self.wallet = wallet
self.theme_name = self.wallet.theme self.theme_name = self.wallet.theme
self.themes = util.load_theme_paths() self.themes = util.load_theme_paths()
@ -549,11 +553,9 @@ class MiniActuator(QObject):
def set_servers_gui_stuff(self, servers_menu, servers_group): def set_servers_gui_stuff(self, servers_menu, servers_group):
self.servers_menu = servers_menu self.servers_menu = servers_menu
self.servers_group = servers_group self.servers_group = servers_group
self.connect(self, SIGNAL("updateservers()"), self.update_servers_list)
def populate_servers_menu(self): def populate_servers_menu(self):
interface = self.wallet.interface interface = self.wallet.interface
interface.servers_loaded_callback = self.server_list_changed
if not interface.servers: if not interface.servers:
print "No servers loaded yet." print "No servers loaded yet."
self.servers_list = [] self.servers_list = []
@ -583,9 +585,6 @@ class MiniActuator(QObject):
server_action.toggled.connect(delegate) server_action.toggled.connect(delegate)
self.servers_group.addAction(server_action) self.servers_group.addAction(server_action)
def server_list_changed(self):
self.emit(SIGNAL("updateservers()"))
def update_servers_list(self): def update_servers_list(self):
# Clear servers_group # Clear servers_group
for action in self.servers_group.actions(): for action in self.servers_group.actions():

3
lib/gui_qt.py

@ -1430,6 +1430,9 @@ class ElectrumGui:
if app is None: if app is None:
self.app = QApplication(sys.argv) self.app = QApplication(sys.argv)
def server_list_changed(self):
pass
def waiting_dialog(self): def waiting_dialog(self):
s = Timer() s = Timer()

18
lib/interface.py

@ -306,14 +306,15 @@ class TcpStratumInterface(Interface):
class WalletSynchronizer(threading.Thread): class WalletSynchronizer(threading.Thread):
def __init__(self, wallet, loop=False): def __init__(self, wallet, loop=False, servers_loaded_callback=None):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.daemon = True self.daemon = True
self.wallet = wallet self.wallet = wallet
self.loop = loop self.loop = loop
self.init_interface() self.init_interface()
self.servers_loaded_callback = servers_loaded_callback
def init_interface(self, servers_loaded_callback=None): def init_interface(self):
try: try:
host, port, protocol = self.wallet.server.split(':') host, port, protocol = self.wallet.server.split(':')
port = int(port) port = int(port)
@ -332,10 +333,8 @@ class WalletSynchronizer(threading.Thread):
InterfaceClass = TcpStratumInterface InterfaceClass = TcpStratumInterface
self.interface = InterfaceClass(host, port, self.wallet.debug_server) self.interface = InterfaceClass(host, port, self.wallet.debug_server)
self.interface.servers_loaded_callback = servers_loaded_callback
self.wallet.interface = self.interface self.wallet.interface = self.interface
def handle_response(self, r): def handle_response(self, r):
if r is None: if r is None:
return return
@ -364,8 +363,13 @@ class WalletSynchronizer(threading.Thread):
if ports and version: if ports and version:
servers.append((host, ports)) servers.append((host, ports))
self.interface.servers = servers self.interface.servers = servers
assert self.interface.servers_loaded_callback is not None # TODO: This assert fails with commands so it should be removed
self.interface.servers_loaded_callback() # after we've ascertained it never fails when running the GUI.
assert self.servers_loaded_callback is not None
# servers_loaded_callback is None for commands, but should
# NEVER be None when using the GUI.
if self.servers_loaded_callback is not None:
self.servers_loaded_callback()
elif method == 'blockchain.address.subscribe': elif method == 'blockchain.address.subscribe':
addr = params[0] addr = params[0]
@ -429,7 +433,7 @@ class WalletSynchronizer(threading.Thread):
if self.loop: if self.loop:
time.sleep(5) time.sleep(5)
# Server has been changed. Copy callback for new interface. # Server has been changed. Copy callback for new interface.
self.init_interface(self.interface.servers_loaded_callback) self.init_interface()
self.start_interface() self.start_interface()
continue continue
else: else:

Loading…
Cancel
Save