I'm using PyQt5 (Python) to build my application, and I don't know C++ at all unfortunately, even though this forum is primarily for C++ users. I hope it's not too much to ask for more Pythonic or at least language agnostic explanations or solutions, as I'm not too sure about the similarities between Python and C++ on threading.

When I say "thread" here, I mean a thread as in a forum thread, but my application DOES use threads to "thread" my thread objects.
My application essentially requires background objects to be continuously refreshed using a refresh() method, at specific moments in time, for an undetermined period of time, with an undetermined period of time in between each refresh.
For example, one thread object may be created, added to the list of threads that need constant refreshing, and then refreshed in 10 second, and then once done refreshing, 30s later, then 15s, then 60s, then 120s before it eventually is removed for it's own reasons.

All of this has to occur in the background using thread and thread workers, or some kind of smart implementation that allows the user to continue interacting with the GUI.
Additionally, since my Thread objects are connected to a database, I cannot store the scheduled times on the objects themselves, they have to rely on the GUI to either store these times, or functionality that can implement "waiting" of sorts.

Since this is my second and most complicated threading application, and my first time using PyQt, I am struggling to write code that I can be proud of, and I have zero idea who to turn to for better ideas.
I did create some example code to show in essence how my application should function.
Check it out on Gist: https://gist.github.com/Xevion/8a66b...b73abbcef7fdbc

Here's a couple snippets of how it works. This is only a small fraction of how the entire thing works, so if it doesn't make any sense, open up the Gist.

Qt Code:
  1. class MainWindow(QMainWindow):
  2. def __init__(self, *args, **kwargs):
  3. super(MainWindow, self).__init__(*args, **kwargs)
  4.  
  5. ... # Define all widgets, setup worker threadpool etc.
  6.  
  7. # Refresh threads occasionally - My bad implementation
  8. self.rtimer = QTimer()
  9. self.rtimer.setInterval(1_000)
  10. self.rtimer.timeout.connect(self.refreshThreads) # Recheck for threads that need to be refresh()'d every second.
  11. # Auto GUI List Refreshing
  12. self.ltimer = QTimer()
  13. self.ltimer.setInterval(100)
  14. self.ltimer.timeout.connect(self.refreshList) # Refresh GUI list every 0.1 seconds
  15. # Start the timers.
  16. self.rtimer.start()
  17. self.ltimer.start()
  18.  
  19. # Thread objects (not execution threads!)
  20. # self.threads - A list of None and Thread objects which represent the 'active' threads in the list.
  21. # self.schedule - A dictionary of the UNIX timestamps the threads should be refreshed at.
  22. # self.working - A dictionary of bool values representing whether a thread is currently being refresh()'d.
  23. self.threads, self.schedule, self.working = [], {}, {}
  24. self.addThreads(n=10) # Add 10 threads to the list.
  25.  
  26. def refreshThreads(self):
  27. # Schedules new workers.
  28.  
  29. workers = []
  30. now = time.time()
  31. threads = filter(lambda t: t is not None and not self.working.get(t.id) and self.schedule[t.id] <= time.time(),
  32. self.threads)
  33. for thread in threads:
  34. self.working[thread.id] = True
  35. print(f'Starting {thread}.refresh')
  36. worker = Worker(thread.refresh)
  37. worker.signals.result.connect(
  38. self.processThreadRefresh) # Once finished, the result of the refresh should be sent back.
  39. worker.signals.finished.connect(self.refreshList)
  40. workers.append(worker)
  41.  
  42. # Add all the Workers to the threadpool and begin working.
  43. if workers:
  44. # print(f'Threaded {len(workers)} Workers')
  45. for worker in workers:
  46. self.threadpool.start(worker)
  47.  
  48. def processThreadRefresh(self, result):
  49. # Used as a way to remove threads from the running if they need to.
  50. # Thread callback once it returns it's result/decision.
  51. active, id = result
  52. self.working[id] = False # Stop 'working' on a thread
  53. if active:
  54. print(f'Rescheduling {self.threads[id]}')
  55. self.schedule[id] = self.threads[id].getRefreshTime()
  56. else:
  57. print(f'Removing {self.threads[id]}')
  58. self.threads[
  59. id] = None # Not really sure what to do here. double dictionary? Is a infinite list of None's okay?
  60. del self.schedule[id]
To copy to clipboard, switch view to plain text mode 

If you run my code, and then start looking at it more deeply, there are some major and minor problems with it.
1) Actually doing this would be horrible for the database. It's not feasible to keep using ORM queries to check the database every single second to see if they match my filter.
2) Threads won't be refreshed at the right moment. They'll miss it by a couple seconds if I check for "ready" thread objects at a speed the database should be more comfortable with.
3) My implementation feels awful. I don't like this code, and it feels as though PyQt5 should have something that does what I want, but I don't know of it.
4) The ListWidget updates too slowly for me. My most ideal form would be updating in literal real-time as all of the numbers count down. Maybe this isn't even possible.

Since this question isn't that exact, I can't say I expect any answers, but hopefully by posting here instead of StackOverflow I can get some more dedicated answers. Just not quite ready to give up on this project.
This looks pretty complicated, but at the heart of the problem, it's really not that complicated, and I'm really just searching for a system of threading and/or timers that I haven't yet found in any online examples.
Thanks for viewing my question.