python-3.x - 在树莓派 B+ 上使用 tkinter 从引导 shell 脚本没有显示名称和 $DISPLAY 环境变量

标签 python-3.x tkinter cron raspberry-pi boot

我的第一篇文章很抱歉,如果我做错了什么。

我已经在 python3 上使用 tkinter 编写了一个 python 脚本,它在 IDLE3 中运行得非常好。我希望在启动 Pi 时运行此脚本,因此执行以下过程以使用 cron 作业运行 @reboot。
http://www.instructables.com/id/Raspberry-Pi-Launch-Python-script-on-startup/step4/Add-to-your-crontab/

最初我遇到了初始化错误,所以我添加了'/bin/sleep 120;'到@reboot 行,因此它现在读取如下,并且似乎等待足够长的时间以在启动后初始化所有内容。
@reboot/bin/sleep 120; sh/home/pi/launcher.sh >/home/pi/logs/cronlog 2>&1

但是在此之后,我的 cronlog 显示以下错误:

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



现在我在其他线程上看到了这个错误,许多似乎与我不使用的初始化库有关。我想补充一点,我的代码在空闲状态下运行良好,从终端运行良好,我已经制作了 launcher.sh 并使其可执行,如果我从终端运行 'sh launcher.sh' 它工作正常,所以 launcher.sh shell 脚本看起来不错.但是在启动时出现上述错误,我不知道为什么?请帮忙。

我在这台机器上没有代码的副本,但如果有人认为这会有所帮助,我会尝试将其复制并作为评论发布。

提前致谢
保罗

完整代码如下
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()

最佳答案

我不认为您的代码有问题导致了您的错误。尤其是因为你说:

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



让我建议同时 cron是用于安排重复性工作的出色工具,我经常使用它(实际上是每分钟永远),但它可能不是满足您需求的最佳工具。

由于您想在 PI 启动时运行您的程序,并且您的程序在 中运行。 X ,你为什么不把它添加到 LXDE 自动启动文件中?使用 nano 或您选择的文本编辑器,编辑以下文件:
对于较旧的 Raspbian 构建:
sudo nano /etc/xdg/lxsession/LXDE/autostart

或者对于当前的 Rasbian 版本:
sudo nano /etc/xdg/lxsession/LXDE-pi/autostart

添加以下行,然后保存文件:
@sh /home/pi/launcher.sh

或者,如果您还想要一个终端窗口,用于调试:
@lxterminal --command='sh /home/pi/launcher.sh'

此外,您可以通过两个简单的步骤使 shell 脚本和 python 脚本可执行。
  • 在脚本的第一行添加一个 hashbang。
    Python: #!/usr/bin/python2#!/usr/bin/python3
    shell : #!/bin/sh或者也许 #!/bin/bash
  • 将脚本标记为可执行。从命令行输入:
    chmod +X ./myscript.shsudo chmod 755 ./myscript.sh
  • 关于python-3.x - 在树莓派 B+ 上使用 tkinter 从引导 shell 脚本没有显示名称和 $DISPLAY 环境变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29845108/

    相关文章:

    python - 使用 tkinter 的网格内的可滚动列表框

    python - 在 Ubuntu 中重新启动崩溃的 Python 脚本?

    javascript - cron 作业 node.js : job is repeating infinitely

    python-3.x - Scikit-learn 对于 MLR 的正确性?

    python - 使用正则表达式 Python 3 捕获两个已知单词之间的所有多行文本

    python - 是否有一种有效的算法可以在不重复计算的情况下找到多个 2d 点之间的距离?

    python-2.7 - Tkinter 列表框、箭头键与鼠标单击的行为差异

    python - 使用 tkinter 中的按钮更改变量

    php - 如何在 Godaddy 虚拟主机服务器中使用 cron 作业运行 Yii2 的控制台命令

    windows-7 - Ephem 的 Python 包 *localtime()* 函数的奇怪行为