Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No display name and no $DISPLAY environment variable using tkinter on raspberry Pi B+ from boot shell script

my first post so apologies if I do anything wrong.

I have written a python script using tkinter on python3 that runs totally fine from within IDLE3. I wanted this script to run when i started up the Pi so did the following procedure to run @reboot using a cron job. http://www.instructables.com/id/Raspberry-Pi-Launch-Python-script-on-startup/step4/Add-to-your-crontab/

Initially I got an initialisation error so I added in '/bin/sleep 120 ;' to the @reboot line so it now reads as follows and seems to wait long enough for everything to initialise after boot. @reboot /bin/sleep 120; sh /home/pi/launcher.sh >/home/pi/logs/cronlog 2>&1

But following this my cronlog shows the following error:

Traceback (most recent call last): File "walk.py", line 29, in MyGUI = Tk() File "/usr/lib/python3.2/tkinter/init.py", line 1701, in init self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use) _tkinter.TclError: no display name and no $DISPLAY environment variable

Now I have seen this error on other threads and many seem to relate to initialising libraries that I don't use. I want to add that my code runs fine from idle, fine from terminal, I have made launcher.sh and made it executable and if I run 'sh launcher.sh' from terminal it works fine so the launcher.sh shell script seems good. but on boot I get the above error and i have no idea why? Please help.

I do not have a copy of the code on this machine but I will try to copy it across and post it as a comment if anyone thinks that would help.

Thanks in advance Paul

FULL CODE BELOW


import sys
import tkinter #might not be needed
from tkinter import *
import pymysql
import os.path
import datetime
import json

#Define local database connection
try:
    localdb = pymysql.connect(host="localhost", user="root", passwd="p054169q", db="walk")
except Exception as e:
    sys.exit('Can not locate localdb')

lcur = localdb.cursor()

#Define web database connection (Maybe add later for easy sync)

#Initialise GUI using Tkinter 
MyGUI = Tk()

#Remove title bar from GUI window
#(BUG: Causes window not to have focus for scanning)
#MyGUI.overrideredirect(1)
#Doing it this way works but disable until working on small screen, this just sets window as fullscreen
#MyGUI.attributes('-fullscreen', True)

#Set window to resolution of screen 800x420 pixels
MyGUI.geometry('800x420')
#Set window title (Perhaps later remove title bar if possible)
MyGUI.title('Island Walk Tracker')

#Set window background colour to black
MyGUI.configure(background='black')



##GLOBAL VARIABLES ##

#Create variable for RFID code
global RFIDcode
RFIDcode = ''

#Create variable for checkpoint number
global checkpoint_number
checkpoint_number = 2

#Create variable for photo filepath
global photo_path
photo_path = ''




## DECLARE ALL TKINTER VARIABLES ##
#Declare like this for a tkinter variable to update dynamically on GUI (A global var will not do this)
rfid_codeTK = StringVar()
rfid_codeTK.set('')

walk_numberTK = IntVar()
walk_numberTK.set(0)

fullnameTK = StringVar()
fullnameTK.set('')

classTK = StringVar()
classTK.set('')




## DEFINE LABELS AND HOW/WHERE THEY APPEAR ON THE SCREEN ##
#USE 'textvariable' instead of text for labels below to link to a live variable (See info here http://www.tutorialspoint.com/python/tk_label.htm)

#Spacer label just adds blank label of fixed width to set width of column and space down one row from top of page
lblSpacer = Label (text = '                                                                                         ',fg='white',bg='black',font = 'Helvetica 8 bold')
lblSpacer.grid(row=0,column=0)

#TEST CODE: Display variable RFIDcode in a label for testing. Comment out to hide rfid code.
#lblRFID = Label (MyGUI,textvariable = rfid_codeTK ,fg='white',bg='black',font = 'Helvetica 20 bold')
#lblRFID.grid(row=3,column=1)

#Display Walk number in a label
lblWalkNumber = Label(MyGUI,textvariable = walk_numberTK ,fg='white',bg='black',font = 'Helvetica 115 bold')
#Unlike other TK variables this is not placed in grid until after scan as this stops a big '0' appearing on screen
#lblWalkNumber.grid(row=2,column=0)

#Display Walker name in a label
lblWalkerName = Label (MyGUI,textvariable = fullnameTK ,fg='white',bg='black',font = 'Helvetica 25 bold')
lblWalkerName.grid(row=3,column=0)

#Display class in a label
lblWalkerClass = Label(MyGUI,textvariable = classTK ,fg='white',bg='black',font = 'Helvetica 25 bold')
lblWalkerClass.grid(row=4,column=0)

#Display photo on the screen
#TEST CODE: later on change 'Blank' for the field in database that represents Student_ID
#photo_path = 'WALK_PHOTOS/'+ 'Blank' +'.gif'
#pic = PhotoImage(file= photo_path )
#ImgWalkerPhoto = Label(MyGUI,image = pic)
#ImgWalkerPhoto.grid(row=2,column=1)




## EVENT OCCURS WHEN RFID CODE IS ENTERED (following 'enter' keypress) ##
def Update_Walker_Screen(event):
    global RFIDcode
    global photo_path
    global checkpoint_number

    #Search database for matching RFIDcode from localdb and SELECT all data from that row
    lcur.execute("SELECT walk_number, forename, surname, class, student_id FROM walkers WHERE rfid_code = '"+ RFIDcode +"'")
    row = lcur.fetchall()


## SET POSITIONS OF THINGS ON THE TKINTER FORM ##

    #Display walker number in its label (done here after the scan event as if done above with other TK variables a big '0' appears on screen because it is an int so has a '0' not null value when initialised.
    lblWalkNumber.grid(row=2,column=0)

    pic = PhotoImage(file='WALK_PHOTOS/'+ 'Blank' +'.gif')
    ImgWalkerPhoto = Label(MyGUI,image = pic)
    #Hide image for testing
    #ImgWalkerPhoto.grid(row=2,column=1)


## SET VARIABLES TO DISPLAY ON SCREEN (WILL AUTO UPDATE SCREEN WHEN TK VARIABLES CHANGE) ## 

    #Set rfid_codeTK variable to be RFIDcode from the code entered from scan.
    rfid_codeTK.set(RFIDcode)

    #Set walk_numberTK variable to be walk_number from the fetched database record
    walk_numberTK.set(row[0][0])

    #Set fullnameTK variable to be forename + surname from the fetched database record
    fullnameTK.set(row[0][1] + ' ' + row[0][2])

    #Set classTK variable to be class from the fetched database record
    classTK.set(row[0][3])

    #Display photo on the screen after scan

    photo_path = 'WALK_PHOTOS/'+ row[0][4] +'.gif'

    if os.path.isfile(photo_path) == True:
        #show photo with filename as
        photo_path = 'WALK_PHOTOS/'+ row[0][4] +'.gif'
    else:
        #show blank photo
        photo_path = 'WALK_PHOTOS/Blank.gif'

    #Display image
    #ImgWalkerPhoto.grid(row=2,column=1)

    #This should update screen items (does flash picture up but not sure if its any use)
    #MyGUI.update_idletasks()

    #Look up current time
    walkers_time = datetime.datetime.now().strftime("%H:%M:%S")

    #Log time into database in correct checkpoint column
    if checkpoint_number == 1:
        #sqlquery = """INSERT INTO walkers('ckpt_1') VALUES({0})""".format(json.dumps(walkers_time))
        #lcur.execute("INSERT INTO walkers('ckpt_1') VALUES ()
        print('Checkpoint 1 selected')
    elif checkpoint_number == 2:
        sqlquery = "INSERT INTO walkers('ckpt_1') VALUES({0})".format(json.dumps(walkers_time))
        print(sqlquery)
        print('Checkpoint 2 selected')
    elif checkpoint_number == 3:
        print('Checkpoint 3 selected')
    elif checkpoint_number == 4:
        print('Checkpoint 4 selected')
    elif checkpoint_number == 5:
        print('Checkpoint 5 selected')
    elif checkpoint_number == 6:
        print('Checkpoint 6 selected')
    elif checkpoint_number == 7:
        print('Checkpoint 7 selected')
    elif checkpoint_number == 8:
        print('Checkpoint 8 selected')
    elif checkpoint_number == 9:
        print('Checkpoint 9 selected')
    elif checkpoint_number == 10:
        print('Checkpoint 10 selected')
    elif checkpoint_number == 11:
        print('Checkpoint 11 selected')
    elif checkpoint_number == 12:
        print('Checkpoint 12 selected')
    else:
        #Checkpoint not correctly selected so ask them to restart and select a new checkpoint number
        print('Please select a checkpoint number') 


    #Write the scanned walker details into text file ready to email. If file does not exist then create it, else append to existing file.
    #file = open('emaildata.txt', 'a')
    with open('emaildata.txt', 'a') as myfile:
        myfile.write(str(row[0][0]) + ',' + str(walkers_time) + ',' + 'Y' + '|')#walk_numberTK.get())

    #Clear global variable RFIDcode ready for next scan.
    RFIDcode = ''


#This function detects any keypresses and then adds them intothe RFIDcode global string variable
#(Ignores Enter key as thats handled earlier in code so never reaches it. Add extra ignores if needed above.)
def keypress(event):
    global RFIDcode
    RFIDcode = RFIDcode + str(event.char)


#Bind any key press to run the keypress function.
MyGUI.bind('<Key>',keypress)

#Bind an 'Enter' key press to run the Update_Walker_Screen function. 
MyGUI.bind('<Return>',Update_Walker_Screen)


MyGUI.mainloop()
like image 419
Paul Simon Avatar asked Apr 24 '15 10:04

Paul Simon


2 Answers

I don't think a problem with your code is causing your error. Especially since you said:

I want to add that my code runs fine from idle, fine from terminal

Let me suggest that while cron is an excellent tool for scheduling recurring jobs, which I use constantly (every minute forever, actually), it may not be the best tool to meet your needs here.

Since you want to run your program when your PI boots, and your program runs in X, why don't you add it to the LXDE autostart file? Using nano, or the text editor of your choice, edit the following file:
For older Raspbian builds:

sudo nano /etc/xdg/lxsession/LXDE/autostart

Or for current Rasbian builds:

sudo nano /etc/xdg/lxsession/LXDE-pi/autostart

Add the following line, and then save the file:

@sh /home/pi/launcher.sh

Or if you want a terminal window also, for debugging:

@lxterminal --command='sh /home/pi/launcher.sh'


Also, you can make shell scripts and python scripts executable with two simple steps.
  1. Add a hashbang to the very first line of the script.
    Python:   #!/usr/bin/python2   or   #!/usr/bin/python3

    Shell:   #!/bin/sh   or maybe   #!/bin/bash
  2. Mark the script as executable. From the command line enter:

    chmod +X ./myscript.sh  or  sudo chmod 755 ./myscript.sh

like image 153
S. Adam Nissley Avatar answered Sep 22 '22 16:09

S. Adam Nissley


Adam's answer is great, but a recent change in Raspian means you need to edit a different LXDE-pi file:

sudo nano ~/.config/lxsession/LXDE-pi/autostart

I did that and now it works for me.

like image 41
stardustJerry Avatar answered Sep 22 '22 16:09

stardustJerry