Transfer of data between Python files - python-3.x

Need some help please to explain why the following does not work.
Environment: Python 3.4, Gtk3.0, limited experience of Python
File selectcontact.py contains code to select one of a number of records and pass its key back to its parent process for use in one of at least three other actions.
Code snippet from the parent class:
….
self.cindex = 0
….
def editcontact_clicked (self, menuitem):
import selectcontact
selectcontact.SelectContactGUI(self)
print ('From Manage ', self.cindex)
if self.cindex > 0:
import editcontact
editcontact.EditContactGUI(self.db, self.cindex)
….
Code snippet from selectcontact:
class SelectContactGUI:
def init(self, parent_class):
self.builder = Gtk.Builder()
self.builder.add_from_file(UI_FILE)
self.builder.connect_signals(self)
self.parent_class = parent_class
self.db = parent_class.db
self.cursor = self.db.cursor(cursor_factory = psycopg2.extras.NamedTupleCursor)
self.contact_store = self.builder.get_object('contact_store')
self.window = self.builder.get_object('window1')
self.window.show_all()
def select_contact_path(self, path):
self.builder.get_object('treeview_selection1').select_path(path)
def contact_treerow_changed (self, treeview):
selection = self.builder.get_object('treeview_selection1')
model, path = selection.get_selected()
if path != None:
self.parent_class.cindex = model[path][0]
print ('From select ', self.parent_class.cindex)
self.window.destroy()
….
window1 is declared as “modal”, so I was expecting the call to selectcontact to act as a subroutine, so that editcontact wouldn’t be called until control was passed back to the parent. The parent_class bit works because the contact_store is correctly populated. However the transfer back to the parent appears not to work, and the two print statements occur in the wrong order:
From Manage 0
From select 2
Comments gratefully received.
Graeme

"Modal" refers to windows only. That is, a modal window prevents accessing the parent window.
It has little to do with what code is running. I am not familiar with this particular windowing framework, but any I have worked with has had a separate thread for GUI and at least one for processing, to keep the GUI responsive, and message loops running in all active windows, not just the one currently with the focus. The modal dialog has no control over what code in other threads are executed when.
You should be able to break into the debugger and see what threads are running and what is running in each thread at any given time.

Related

Multiprocessing - tkinter pipeline communication

I have a question on multiprocessing and tkinter. I am having some problems getting my process to function parallel with the tkinter GUI. I have created a simple example to practice and have been reading up to understand the basics of multiprocessing. However when applying them to tkinter, only one process runs at the time. (Using Multiprocessing module for updating Tkinter GUI) Additionally, when I added the queue to communicate between processes, (How to use multiprocessing queue in Python?), the process won't even start.
Goal:
I would like to have one process that counts down and puts the values in the queue and one to update tkinter after 1 second and show me the values.
All advice is kindly appreciated
Kind regards,
S
EDIT: I want the data to be available when the after method is being called. So the problem is not with the after function, but with the method being called by the after function. It will take 0.5 second to complete the calculation each time. Consequently the GUI is unresponsive for half a second, each second.
EDIT2: Corrections were made to the code based on the feedback but this code is not running yet.
class Countdown():
"""Countdown prior to changing the settings of the flows"""
def __init__(self,q):
self.master = Tk()
self.label = Label(self.master, text="", width=10)
self.label.pack()
self.counting(q)
# Countdown()
def counting(self, q):
try:
self.i = q.get()
except:
self.label.after(1000, self.counting, q)
if int(self.i) <= 0:
print("Go")
self.master.destroy()
else:
self.label.configure(text="%d" % self.i)
print(i)
self.label.after(1000, self.counting, q)
def printX(q):
for i in range(10):
print("test")
q.put(9-i)
time.sleep(1)
return
if __name__ == '__main__':
q = multiprocessing.Queue()
n = multiprocessing.Process(name='Process2', target=printX, args = (q,))
n.start()
GUI = Countdown(q)
GUI.master.mainloop()
Multiprocessing does not function inside of the interactive Ipython notebook.
Multiprocessing working in Python but not in iPython As an alternative you can use spyder.
No code will run after you call mainloop until the window has been destroyed. You need to start your other process before you call mainloop.
You are calling wrong the after function. The second argument must be the name of the function to call, not a call to the function.
If you call it like
self.label.after(1000, self.counting(q))
It will call counting(q) and wait for a return value to assign as a function to call.
To assign a function with arguments the syntax is
self.label.after(1000, self.counting, q)
Also, start your second process before you create the window and call counting.
n = multiprocessing.Process(name='Process2', target=printX, args = (q,))
n.start()
GUI = Countdown(q)
GUI.master.mainloop()
Also you only need to call mainloop once. Either position you have works, but you just need one
Edit: Also you need to put (9-i) in the queue to make it count down.
q.put(9-i)
Inside the printX function

Keep GIF animation running while doing calculations

I am trying to improve the user experience by showing a load mask above the active QMainWindow/QDialog when performing tasks that takes some time. I have managed to get it working as I want it, except for a moving GIF when performing the task. If I leave the load mask on after the task is complete, the GIF starts moving as it should.
My class for the load mask:
from PyQt4 import QtGui, QtCore
from dlgLoading_view import Ui_dlgLoading
class dlgLoading(QtGui.QDialog, Ui_dlgLoading):
def __init__(self,parent):
QtGui.QDialog.__init__(self,parent)
self.setupUi(self)
self.setWindowFlags(QtCore.Qt.WindowFlags(QtCore.Qt.FramelessWindowHint))
self.setGeometry(0, 0, parent.frameGeometry().width(), parent.frameGeometry().height())
self.setStyleSheet("background-color: rgba(255, 255, 255, 100);")
movie = QtGui.QMovie("loader.gif")
self.lblLoader.setMovie(movie)
movie.start()
def showEvent(self, event):
QtGui.qApp.processEvents()
super(dlgLoading, self).showEvent(event)
def setMessage(self,message):
self.lblMessage.setText(message)
The Ui_dlgLoading contains two labels and some vertical spacers: lblLoader (will contain the gif) and lblMessage (will contain a message if needed)
I create the load mask with this code:
loadmask = dlgLoading(self)
loadmask.setMessage('Reading data... Please wait')
loadmask.show()
I figured I needed some multithreading/multiprocessing, but I can't for the life of me figure out how to do it. I read somewhere that you can't tamper with the GUIs threading, so I would need to move the heavy task there instead, but I'm still blank.
As a simple example, let's say I am trying to load a huge file into memory:
file = open(dataFilename, 'r')
self.dataRaw = file.read()
file.close()
Around that I would create and close my load mask dialog. How do I start the file read without halting the GIF animation?
The GUI is for running some heavy external exe files, so it should work with that too.
I ended up doing this:
class runthread(threading.Thread):
def __init__(self, commandline, cwd):
self.stdout = None
self.stderr = None
self.commandline = commandline
self.cwd = cwd
self.finished = False
threading.Thread.__init__(self)
def run(self):
subprocess.call(self.commandline, cwd=self.cwd)
self.finished = True
class command()
def __init__(self):
...
def run():
...
thread = runthread("\"%s\" \"%s\"" % (os.path.join(self.__caller.exefolder, "%s.exe" % self.__cmdtype), self.__name), self.__caller.exeWorkdir)
thread.start()
count = 0
sleeptime = 0.5
maxcount = 60.0/sleeptime
while True:
time.sleep(sleeptime)
QtWidgets.qApp.processEvents()
count += 1
if thread.finished:
break
if count >= maxcount:
results = QtWidgets.QMessageBox.question(self.__caller, "Continue?", "The process is taking longer than expected. Do you want to continue?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
if results == QtWidgets.QMessageBox.Yes:
count == 0
else:
QtWidgets.QMessageBox.warning(self.__caller, "Process stopped", "The process was stopped")
return False
It actually doesn't directly answer my question, but it worked for me, so I'm posting the answer if others want to do something similar.
I call a process (in this case Pythons subprocess.call) through a thread and track when the process is actually finished. A continuous loop checks periodically if the process is done and updates the GUI (processEvents - this is what triggers the GIF to update). To avoid an infinite loop I offer the user an option to exit after some time.

Getting attribute error on non-attribute variable

Im working on a game and getting a strange error where instance variable isn't saved properly (as far as I can tell). This happens when i try to access self.game from the RunGame instance. By the way the classes are in separate modules, and im not showing all code. The game runs fine, but when switching level it just crashes
class RunGame(object):
def __init__(self):
self.makeTk()
self.currentLevel = 0
self.bricksLayout = [ #for debugging: a layout of bricks. 3d array with each level
[
[0,1,0,1,1,1,1,1],
[0,0,0,0,4,1,1,1],
[4,4,4,4,4,4,4,4]
],
[
[4,0,0,1,0,1,0,1],
[0,0,0,0,0,1,0,1],
[4,4,4,4,4,4,4,4]
]
]
self.game = GameInstance(self.bricksLayout,self)
self.game.run = False #this does nothing for some reason, no error though
def switchLevel(self):
print("switching level")
self.game.run = False #calling this will give no attribute error
#self.game.clearCanvas()
#self.game.canvas.destroy()
self.currentLevel+=1
#self.game = Game(self.bricksLayout,self)
def makeTk(self):
self.root = Tk()
self.root.title("PrakeOut")
self.screen_width = self.root.winfo_screenwidth(); #monitor size
self.screen_height = self.root.winfo_screenheight();
#root.wm_attributes("-fullscreen",True) #this gives fullscreen, but removes the menu
self.root.resizable(0,0) #cant be resized
self.root.configure(background="Black") #bg color of root windw
#root.geometry("%ix%i+0+0" % (SCREEN_WIDTH, SCREEN_HEIGHT)) #maximizes screen. First two are width and height, other two are x1 and y1
#root.wm_attributes("-topmost",1); #places window in foreground
run = RunGame()
class GameInstance(object):
def __init__(self, bricksLayout,gameHandler):
self.bricksLayout = bricksLayout
self.gameHandler = gameHandler
self.run = True #game loop
self.makeCanvas()
self.brickList = [] #the list of all da created bricks
self.makeBricks()
self.makeBallAndPaddle()
self.gameLoop()
self.root.mainloop(); #needs to run main loop otherwise windows doesnt show
It gives this error:
File "C:\Users\Bollen\Desktop\python projects\breakout\PythonApplication1\PythonApplication1\GameHandler.py", line 30, in switchLevel
self.game.run = False #calling this will give no attribute error
AttributeError: 'RunGame' object has no attribute 'game'
Press any key to continue . . .
We can't use your code to reproduce the problem, but there is one very obvious logic error in your code. The problem is that neither of your classes is allowed to be fully created before they are being used.
Take a look at GameInstance: because you call mainloop within the initializer, that class will never be fully instantiated until after the root window has been created and then destroyed. Therefore, any other classes which try to work with this instance will be working with a not-fully-formed instance.
Likewise, with GameRunner you are creating an instance of GameInstance. Because GameInstance calls mainloop, the creation of the GameRunner object cannot be finished before GameInstance has been created, and GameInstance won't be finished until the window is destroyed.
The first step is to remove the call to mainloop from GameInstance.__init__. Instead, move that into a method that GameRunner can call once it is fully created.
You should also separate the creation of the two objects into two steps. Your main logic would then look something like the following./ I'm not sure if you should create the runner first or the game first, but either way is acceptable depending on how you define the classes.
game = GameInstance(...)
runner = GameRunner(..., game)
runner.run()
In the above, runner.run() would look something like this:
class GameRunner(...):
def run(self):
self.game.run()
... and game.run would look like this:
class GameInstance(...):
def run(self):
self.gameLoop()
self.root.mainloop()

Stopping, restarting and changing variables in a thread from the main program (Python 3.5)

I'm very new to threading am and still trying to get my head around how to code most of it. I am trying to make what is effectively a text editor-type input box and so, like every text editor I know, I need a cursor-bar thing to indicate the location at which the text is being typed to. Thus I also want to be able to flicker/blink the cursor, which i thought would also prove good practice for threading.
I have a class cursor that creates a rectangle on the canvas based on the bounding box of my canvas text, but I then need to change it's location as more characters are typed; stop the thread and instantaneously hide the cursor rectangle when the user clicks outside of the input box; and lastly restart the thread/a loop within the thread (once again, sharing a variable) - the idea here being that the cursor blinks 250 times and after then, disappears (though not necessary, I thought it would make a good learning exercise).
So assuming that I have captured the events needed to trigger these, what would be the best way to go about them? I have some code, but I really don't think it will work, and just keeps getting messier. My idea being that the blinking method itself was the thread. Would it be better to make the whole class a thread instead? Please don't feel restricted by the ideas in my code and feel free to improve it. I don't think that the stopping is working correctly because every time I alt+tab out of the window (which i have programmed to disengage from the input box) the Python shell and tkinter GUI stop responding.
from tkinter import *
import threading, time
class Cursor:
def __init__(self, parent, xy):
self.parent = parent
#xy is a tuple of 4 integers based on a text object's .bbox()
coords = [xy[2]] + list(xy[1:])
self.obj = self.parent.create_rectangle(coords)
self.parent.itemconfig(self.obj, state='hidden')
def blink(self):
blinks = 0
while not self.stop blinks <= 250:
self.parent.itemconfig(self.obj, state='normal')
for i in range(8):
time.sleep(0.1)
if self.stop: break
self.parent.itemconfig(self.obj, state='hidden')
time.sleep(0.2)
blinks += 1
self.parent.itemconfig(self.obj, state='hidden')
def startThread(self):
self.stop = False
self.blinking = threading.Thread(target=self.blink, args=[])
self.blinking.start()
def stopThread(self):
self.stop = True
self.blinking.join()
def adjustPos(self, xy):
#I am not overly sure if this will work because of the thread...
coords = [xy[2]] + list(xy[1:])
self.parent.coords(self.obj, coords)
#Below this comment, I have extracted relevant parts of classes to global
#and therefore, it may not be completely syntactically correct nor
#specifically how I initially wrote the code.
def keyPress(e):
text = canvas.itemcget(textObj, text)
if focused:
if '\\x' not in repr(e.char) and len(e.char)>0:
text += e.char
elif e.keysym == 'BackSpace':
text = text[:-1]
canvas.itemconfig(textObj, text=text)
cursor.adjustPos(canvas.bbox(textObj))
def toggle(e):
if cursor.blinking.isAlive(): #<< I'm not sure if that is right?
cursor.stopThread()
else:
cursor.startThread()
if __name__=="__main__":
root = Tk()
canvas = Canvas(root, width=600, height=400, borderwidth=0, hightlightthickness=0)
canvas.pack()
textObj = canvas.create_text(50, 50, text='', anchor=NW)
root.bind('<Key>', keyPress)
cursor = Cursor(canvas, canvas.bbox(textObj))
#Using left-click event to toggle thread start and stop
root.bind('<ButtonPress-1', toggle)
#Using right-click event to somehow restart thread or set blinks=0
#root.bind('<ButtonPress-3', cursor.dosomething_butimnotsurewhat)
root.mainloop()
If there is a better way to do something written above, please also tell me.
Thanks.

Separate user interaction from programmical change: PyQt, QComboBox

I have several QComboBoxes in my PyQt4/Python3 GUI and they are filled with some entries from a database during the initialisation. Initial CurrentIndex is set to 0. There is also a tick box which changes the language of the items in my combo boxes. To preserve current user selection I backup index of the current item and setCurrentIndex to this number after I fill in ComboBox with translated items. All those actions emit currentIndexChanged signal.
Based on the items selected in QComboBoxes some plot is displayed. The idea is to redraw the plot online - as soon as the user changes any of ComboBox current item. And here I have a problem since if I redraw the plot every time signal currentIndexChanged is emited, I redraw it also several times during initialization and if the translation tick box selection was changed.
What is the best way to separate these cases? In principle I need to separate programmical current Index Change from the user, and update the plot only in the later case (during GUI initialisation I can programically call update plot function once). Should I write/rewrite any signal? If so, I never did that before and would welcome any hint or a good example. Use another signal? Or maybe there is a way to temporary block all signals?
There are a few different things you can try.
Firstly, you can make sure you do all your initialization before you connect up the signals.
Secondly, you could use the activated signal, which is only sent whenever the user selects an item. (But note that, unlike currentIndexChanged, this signal is sent even if the index hasn't changed).
Thirdly, you could use blockSignals to temporarily stop any signals being sent while the current index is being changed programmatically.
Here's a script that demonstrates these possibilities:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
layout = QtGui.QVBoxLayout(self)
self.combo = QtGui.QComboBox()
self.combo.setEditable(True)
self.combo.addItems('One Two Three Four Five'.split())
self.buttonOne = QtGui.QPushButton('Change (Default)', self)
self.buttonOne.clicked.connect(self.handleButtonOne)
self.buttonTwo = QtGui.QPushButton('Change (Blocked)', self)
self.buttonTwo.clicked.connect(self.handleButtonTwo)
layout.addWidget(self.combo)
layout.addWidget(self.buttonOne)
layout.addWidget(self.buttonTwo)
self.changeIndex()
self.combo.activated['QString'].connect(self.handleActivated)
self.combo.currentIndexChanged['QString'].connect(self.handleChanged)
self.changeIndex()
def handleButtonOne(self):
self.changeIndex()
def handleButtonTwo(self):
self.combo.blockSignals(True)
self.changeIndex()
self.combo.blockSignals(False)
def changeIndex(self):
index = self.combo.currentIndex()
if index < self.combo.count() - 1:
self.combo.setCurrentIndex(index + 1)
else:
self.combo.setCurrentIndex(0)
def handleActivated(self, text):
print('handleActivated: %s' % text)
def handleChanged(self, text):
print('handleChanged: %s' % text)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Resources