OS : Ubuntu 9.04
Python : 2.6.2

I want to print a "while" loop into a secondary wx.Frame named "Results" here's the code :

#!/usr/bin/env python
# -*- coding: utf8 -*-

import random
import wx
import os                   # Operating System dependent call
import sys                   
import cPickle as p
import csv
import thread


# Define dialog component
ID_EXIT = 98
ID_CALCULATE = 99
ID_PLOT = 100
ID_CLOSE = 101
ID_CLEAR_TERM = 102
ID_RESET = 103
ID_CALCULATE_SINGLE = 104


def formatta(valore):
        tmp = str("%.1f" % valore)
        if tmp == "-0.0": 
                return tmp.replace("-","")
        else: 
                return tmp


class MainWindow(wx.Frame):
	def __init__(self, parent, id, title):
		wx.Frame.__init__(self, parent, wx.ID_ANY, title, size=(600, 600),pos = (200,100))
	
		#Creates a menu
		filemenu1=wx.Menu()
		filemenu1.AppendSeparator()
		filemenu1.Append(ID_EXIT, "&Exit", "Terminate the program")
		# Creating the menubar
		menuBar=wx.MenuBar()
		menuBar.Append(filemenu1, "&File") #Adding the filemenu to the MenuBar
		self.SetMenuBar(menuBar) #Adding the MenuBar to the Frame content
		#self.control = wx.TextCtrl(self, 1, style=wx.TE_MULTILINE)
		self.CreateStatusBar() # A statusbar in the bottom of the window
	
		#mod = ['B-ASK','4-ASK','8-ASK','BPSK','Q-PSK','8-PSK','Sunde FSK','QAM-16','QAM-64','QAM-256']
		incr = ['1.0', '0.5', '0.2','0.1']
		
		#wx.StaticText(self, -1, "Scegliere la modulazione :",(50,30))
		#self.combo1 = wx.ComboBox(self, -1, pos=(50, 70), size=(150, -1), choices=mod, value='B-ASK',
		#	                        style=wx.CB_READONLY)
		
		#wx.StaticText(self, -1, "Scegliere i valori minimo e massimo di Eb/N0 (db):",(50,120))
		wx.StaticText(self, -1, "Min :",(50,175))
		wx.StaticText(self, -1, "Max :",(210,175))
		self.txt1 = wx.TextCtrl(self, -1, '0',(120,170))
		
		self.txt2 = wx.TextCtrl(self, -1, '10',(290,170))
		
		wx.StaticText(self, -1, "Choose increase :",(50,240))
		self.combo2 = wx.ComboBox(self, -1, pos=(50, 280), size=(150, -1), choices=incr, value='1.0', 
			                        style=wx.CB_READONLY)
		
		#wx.Button(self, ID_CLOSE, 'Close', (500, 540))
		self.btn1 = wx.Button(self, ID_CALCULATE, 'Calculate', (50, 340))
		self.btn2 = wx.Button(self, ID_PLOT, 'Plot', (150, 340))
		self.btn3 = wx.Button(self, ID_RESET, 'Reset', (250, 340))
		self.btn4 = wx.Button(self, ID_CLEAR_TERM, 'Clear Terminal', (350, 340))
		
		#wx.StaticText(self, -1, "CALCOLO DI UN SOLO VALORE DEL BER :",(50,400))
		#wx.StaticText(self, -1, "Inserire il valore di Eb/N0 (db) :",(50,430))
		#wx.StaticText(self, -1, "Eb/N0 (db) :",(50,475))
		#self.txt3 = wx.TextCtrl(self, -1, '0',(150,470))
		#self.btn5 = wx.Button(self, ID_CALCULATE_SINGLE, 'Calculate single', (250, 465))
		
		#self.rb2 = wx.RadioButton(self, -1, 'Off', (400, 395), style=wx.RB_GROUP)
		#self.rb1 = wx.RadioButton(self, -1, 'On', (350, 395),)
		#self.Bind(wx.EVT_RADIOBUTTON, self.disable_all, id=self.rb1.GetId())
		#self.Bind(wx.EVT_RADIOBUTTON, self.enable_all, id=self.rb2.GetId())
		
		wx.EVT_MENU(self, ID_EXIT, self.OnClose)
		
		wx.EVT_BUTTON(self, ID_CALCULATE, self.miothread1)
		#wx.EVT_BUTTON(self, ID_PLOT, self.miothread2)
		#wx.EVT_BUTTON(self, ID_RESET, self.reset)
		wx.EVT_BUTTON(self, ID_CLEAR_TERM, self.clear_term)
		#wx.EVT_BUTTON(self, ID_CALCULATE_SINGLE, self.miothread3)
		
		self.enable_all(True)
		
		self.frame2 = Risultati(None,-1,"Results")
		self.frame2.Show(True)


	
	def disable_all(self,e):
		self.combo1.Disable()
		self.combo2.Enable()
		self.txt1.Enable()
		self.txt2.Enable()
		self.btn1.Disable()
		self.btn2.Disable()
		#self.btn3.Disable()
		#self.btn4.Disable()
		
		self.txt3.Enable()
		self.btn5.Enable()
	
	def enable_all(self,e):
		#self.combo1.Disable()
		self.combo2.Enable()
		self.txt1.Enable()
		self.txt2.Enable()
		self.btn1.Enable()
		self.btn2.Disable()
		self.btn3.Disable()
		#self.btn4.Enable()
	    
		#self.txt3.Disable()
		#self.btn5.Disable()
    
	def OnClose(self, e):
		self.Close(True)
		self.frame2.Close(True)
	
	#def calcolo(self,objMod,DeMod,titolo,minimum,maximum,incremento):
	def calcolo(self,e):
		i = minimum = float(self.txt1.GetValue())
		maximum = float(self.txt2.GetValue())
		incremento = float(self.combo2.GetValue())
	    
		
		print 'min value = '+str(float(minimum))
		print 'max value = '+str(float(maximum))
		print 'increase = '+str(incremento)
		print '---------------Results---------------'
		
		self.frame2.log.AppendText('min value = '+str(float(minimum))+'\n'+'max value = '+str(float(maximum))+'\n'+'increase = '+str(incremento)+'\n'+'---------------Results---------------\n')
		
		while True:		
			if i <= maximum:
   			
				self.frame2.LogMessage2(formatta(i))
				print i		    
				i += incremento

			else:
				break
		    
		#print 'Ber calculation done !'
		#self.frame2.LogMessage_single('Ber calculation done !')
		#self.frame2.log.AppendText('Ber caculation done !')
		    
    
	    
	def clear_term(self,e):
		cmd = 'clear'
		os.system(cmd)
		self.frame2.log.Clear()
	    
	def reset(self,e):
		os.system('rm ./sys/incremento.csv')
		os.system('rm ./sys/titolo.csv')
		os.system('rm ./risultati.csv')
		os.system('rm ./db.csv')
		#os.system('rm ./risultati.txt')
		#os.system('rm ./db.txt')
		    
	def miothread1(self,e):
		thread.start_new_thread(self.calcolo, (0,))
	

class Risultati(wx.Frame):
	def __init__(self, parent, id, title):
		wx.Frame.__init__(self, parent, id, title)
		
		panel = wx.Panel(self)
		self.log = wx.TextCtrl(panel, -1, "",style=wx.TE_MULTILINE)
		self.log.SetEditable(False)
		
		box = wx.BoxSizer(wx.VERTICAL)
		box.Add(self.log, 1, wx.EXPAND|wx.ALL, 5)
		#self.SetSizer(box)
		panel.SetSizer(box) 

    
	def LogMessage(self, msg1, msg2):
		self.log.AppendText(msg1+' : '+msg2+'\n')
    
	def LogMessage2(self, msg1):
		self.log.AppendText(msg1+'\n')
	
	
#class MyApp(wx.App):
    #def OnInit(self):
	#frame=MainWindow(None, -1, "BER Tool AWGN")
	#frame.Show(True)
	##frame2 = Risultati(None,-1,"Risultati")
	##frame2.Show(True)
	#self.SetTopWindow(frame)
	#return True
	
app = wx.App()
frame=MainWindow(None, -1, "Print Data")
frame.Show(True)
app.MainLoop()

By pressing the Calculate button, a while loop is printed in the TextCtrl of the Results frame. The while loop is threaded with the class thread and doing so i get often the following errors in the 50% of cases:

(python:11242): Gtk-CRITICAL **: gtk_text_layout_wrap_loop_start: assertion `layout->one_style_cache == NULL' failed

(python:11242): Gtk-CRITICAL **: gtk_text_layout_wrap_loop_end: assertion `layout->wrap_loop_count > 0' failed

or

Segmentation Fault

i think it's the same problem of this thread

http://www.daniweb.com/forums/thread273160.html

hovewer i can't solve it. could you please help me ?

thx in advance

Recommended Answers

All 6 Replies

You should never update (or touch) the GUI from a non-GUI thread. You can consider the GUI thread to be the same thread on which you called app.MainLoop(). Your calcolo method executes on a different thread, not on your GUI thread. Therefore, you should not touch the GUI elements inside calcolo. By touching the GUI I am talking about such calls as self.txt1.GetValue() and self.frame2.log.AppendText().

You should get all the values from your GUI elements and either pass them to the calcolo method, or store them in some object (self.data for example), which could be accessed from inside calcolo. The calcolo method would then buffer its output and possibly store it in some object--maybe even the same object from which it got its input data. As the very last thing, calcolo should call a function similar to wx.CallAfter, which arranges for code to run on the GUI thread so that the GUI can be updated safely.

class MainWindow(wx.Frame):
	def __init__(self):
		# code to create GUI
		pass
	
	# This method never touches GUI elements.
	def calcolo(self):
		i = miminum = self.data['min']
		maximum = self.data['max']
		increment = self.data['incr']
		self.data['log'] = 'min: %s, max: %s, incr: %s' % (minimum, maximum, increment)
		result = ''
		while i <= maximum:
			result += formatta(str(i))
			i += increment
		self.data['result'] = result
		# This will cause self.updateGUI to be called on GUI thread.
		wx.CallAfter(self.updateGUI)
	
	# Guaranteed to be called on the GUI thread because of wx.CallAfter,
	# so this method can safely update the GUI.
	def updateGUI(self):
		self.frame2.log.AppendText(self.data['log'])
		self.frame2.LogMessage2(self.data['result'])
	
	# Called from GUI thread because it is called in response to a button click.
	def miothread1(self):
		# This dict object is used as data buffer
		# between GUI thread and the worker thread.
		self.data = dict()
		self.data['min'] = float(self.txt1.GetValue())
		self.data['max'] = float(self.txt2.GetValue())
		self.data['incr'] = float(self.combo2.GetValue())
		# Call calcolo on a new thread.
		Thread(target=self.calcolo).start()

Hope this helps.

commented: he helped me to solve a big and important problem i had +0

It seems you would use the CallAfter method, as illustrated by sergb.
Just remember CallAfter calls its target after the function exits, not immediately.

Really great....i modified a little the code because i was looking for a vertical print of the elements....i'll soon post my final version. Thank you very much, you two were very helpful to me...:D Now i have to nest more complex code in this basis you give me....

thx a lot again :D

i updated the code in this way

class MainWindow(wx.Frame):
	def __init__(self):
		# code to create GUI
		pass
	
	# This method never touches GUI elements.
	def calcolo(self,e):
		i = miminum = self.data['min']
		maximum = self.data['max']
		increment = self.data['incr']
		self.data['log'] = 'min: %s, max: %s, incr: %s' % (minimum, maximum, increment)
		result = ''
		while i <= maximum:
		    #result += formatta(i)
		    wx.CallAfter(self.frame2.LogMessage2,formatta(i))
		    i += increment
	    #self.data['result'] = result
	    # This will cause self.updateGUI to be called on GUI thread.
	    #wx.CallAfter(self.updateGUI)
	
	# Guaranteed to be called on the GUI thread because of wx.CallAfter,
	# so this method can safely update the GUI.
	def updateGUI(self):
		self.frame2.log.AppendText(self.data['log'])
		self.frame2.LogMessage2(self.data['result'])
	
	# Called from GUI thread because it is called in response to a button click.
	def miothread1(self,e):
		# This dict object is used as data buffer
		# between GUI thread and the worker thread.
		self.data = dict()
		self.data['min'] = float(self.txt1.GetValue())
		self.data['max'] = float(self.txt2.GetValue())
		self.data['incr'] = float(self.combo2.GetValue())
	        #Thread(target=self.calcolo).start()
	        thread.start_new_thread(self.calcolo, (None,))

using the class thread instead of threading.Thread beacuse it worked for my purposes better than the last one and i wanted the elements printed one by one and not in one single row. You were really helpful to me....thx very much again ;)

The code looks a lot better now. It's very smart to only handle text writing via one function/thread, this will hopefully prevent any further errors. Clever use of CallAfter too.

thx very much to you and sergb and jcao219 . You were all awesome :D

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.