Perform multi-threading on a python program - python

I want to execute 2 function in the same time,
(im new in python, and development in general)
i tried to create a minecraft server setup assistant :
(the code is in development too)
import os
import tkinter as tk
from tkinter import ttk
import threading
import wget
import json
from pathlib import Path
...
def startndstop(x):
os.chdir(newpath)
if x == "start":
start = str("java" + ram() + namejar + " nogui")
print("Demarrage du serveur", name, "!")
os.system(str(start))
elif x == "stop":
os.system("stop")
root = tk.Tk()
root.title("Yann Installer")
root.iconbitmap("./minecraft_icon_132202.ico")
root.geometry("640x390")
root.resizable(width=False, height=False)
root.configure(bg="#0f800f")
bgimage = tk.PhotoImage(file="background.gif"))
l = tk.Label(root, image=bgimage)
l.pack()
installbutton = tk.Button(root, text="Installer", command=lambda :threading.Thread(target=install("1.8.3")).start())
installbutton.lift(aboveThis=l)
installbutton.place(x=10, y=10)
startbutton = tk.Button(root, text="Demarrer", command=lambda :threading.Thread(target=startndstop("start")).start())
startbutton.lift(aboveThis=l)
startbutton.place(x=10, y=40)
listeVersion = ttk.Combobox(root, values=versionlist())
listeVersion.current(0)
listeVersion.lift(aboveThis=l)
listeVersion.place(x=80, y=10, width=100)
threading.Thread(target=root.mainloop).start()
threading.Thread(target=startndstop,args=('start',)).start()
But when I execute the script , the second thread start only when i close the tkinter window.
edit: now the function are called correctly
Could you help me ?

Thread is not being called correctly. You are calling root.mainloop() and passing its return value to the threads as target, requiring it to run to completion in order to return. Instead, pass the function object itself as target by removing the parentheses. The second thread has the same issue, but needs to pass arguments as a separate args tuple.
Use this:
threading.Thread(target=root.mainloop).start() # start the tkinter interface
threading.Thread(target=startndstop,args=("start",)).start() # start the minecraft server

Related

How to implement Queues for Threading with Tkinter GUI

I'm working on a GUI for a simulation software and need to have two processes running at the same time. One for the simulation, and one for displaying information for the user. BOTH threads need to make changes to the GUI. When I run my code, I get the error: RuntimeError: main thread is not in main loop.
After some research, I think that there is no way you can access the GUI from a different thread than the main thread. I also think that I probably should use Queues, but I am not sure how to do this.
Here's my simplified code:
import tkinter as tk
from tkinter import END
import threading
import time
def write_to_entry1():
for i in range(20):
entry1.delete(0, END)
entry1.insert(0, i)
entry1.update()
time.sleep(1)
def write_to_entry2():
for i in range(20):
entry2.delete(0, END)
entry2.insert(0, i)
entry2.update()
time.sleep(1)
# Creating the GUI
root = tk.Tk()
root.geometry("200x100")
label1 = tk.Label(root)
label1.configure(text="Thread 1:")
label1.grid(column='0', row='0')
entry1 = tk.Entry(root)
entry1.grid(column='1', row='0')
label2 = tk.Label(root)
label2.configure(text="Thread 2:")
label2.grid(column='0', row='1')
entry2 = tk.Entry(root)
entry2.grid(column='1', row='1')
root.update()
t = threading.Thread(target=write_to_entry2)
t.daemon = True
t.start()
write_to_entry1()
root.mainloop()
Thanks for any answers.

NameError: name 'tk' is not defined repl.it

hey beginner programmer here, making a to do list for a high school assignment and cant figure out this error. its also my first time using python as I usually use java and JavaScript for projects. Im trying to add a dark and light mode to my program. Am I doing this correctly? btw, i'm using the online ide repl.it
here's my code for reference
import tkinter
import tkinter.messagebox
from ttkthemes import ThemedStyle
import tkinter.ttk as ttk
import pickle
root = tkinter.Tk()
root.title("To-Do List")
def add_task():
task = entry_task.get()
if task != "":
listbox_tasks.insert(tkinter.END, task)
entry_task.delete(0, tkinter.END)
else:
tkinter.messagebox.showwarning(title="Warning", message="Please enter a task")
def delete_task():
try:
task_index = listbox_tasks.curselection()[0]
listbox_tasks.delete(task_index)
except:
tkinter.messagebox.showwarning(title="Warning", message="Please select a task first")
def load_tasks():
try:
tasks = pickle.load(open("tasks.dat", "rb"))
listbox_tasks.delete(0, tkinter.END)
for task in tasks:
listbox_tasks.insert(tkinter.END, task)
except:
tkinter.messagebox.showwarning(title="Warning", message="Cant find saved task file")
def save_tasks():
tasks = listbox_tasks.get(0, listbox_tasks.size())
pickle.dump(tasks, open("tasks.dat", "wb"))
# Dark and light modes
app = tk.Tk()
app.geometry("200x400")
app.title("Changing Themes")
# Setting Theme
style = ThemedStyle(app)
style.set_theme("scidgrey")
# Button Widgets
Def_Btn = tk.Button(app,text='Default Button')
Def_Btn.pack()
Themed_Btn = ttk.Button(app,text='Themed button')
Themed_Btn.pack()
# Scrollbar Widgets
Def_Scrollbar = tk.Scrollbar(app)
Def_Scrollbar.pack(side='right',fill='y')
Themed_Scrollbar = ttk.Scrollbar(app,orient='horizontal')
Themed_Scrollbar.pack(side='top',fill='x')
# Entry Widgets
Def_Entry = tk.Entry(app)
Def_Entry.pack()
Themed_Entry = ttk.Entry(app)
Themed_Entry.pack()
# Create GUI
frame_tasks = tkinter.Frame(root)
frame_tasks.pack()
listbox_tasks = tkinter.Listbox(frame_tasks, height=10, width=50)
listbox_tasks.pack(side=tkinter.LEFT)
scrollbar_tasks = tkinter.Scrollbar(frame_tasks)
scrollbar_tasks.pack(side=tkinter.RIGHT, fill=tkinter.Y)
listbox_tasks.config(yscrollcommand=scrollbar_tasks.set)
scrollbar_tasks.config(command=listbox_tasks.yview)
entry_task = tkinter.Entry(root, width=50)
entry_task.pack()
button_add_task = tkinter.Button(root, text="Add a task", width=48, command=add_task)
button_add_task.pack()
button_delete_task = tkinter.Button(root, text="Delete a task", width=48, command=delete_task)
button_delete_task.pack()
button_load_tasks = tkinter.Button(root, text="Load a task list", width=48, command=load_tasks)
button_load_tasks.pack()
button_save_tasks = tkinter.Button(root, text="Save your task list", width=48, command=save_tasks)
button_save_tasks.pack()
root = tkinter()
root.mainloop()
repl.it has nothing to do with the issue. You have lines referencing "tk", like app = tk.Tk(), but you never defined anything called tk. It looks like some of your code expects you to have imported Tkinter via import tkinter as tk, in which case tk would be valid. But you also have code expecting it to be called tkinter, like root = tkinter.Tk(). It seems like your code was inspired from multiple sources, some of which had Tkinter imported as tk, and some where it was imported as tkinter. All you have to do is replace all tks with tkinter. For example, this line:
Def_Btn = tk.Button(app,text='Default Button')
would become:
Def_Btn = tkinter.Button(app,text='Default Button')
The problem is probably coming from your import
I observed that you did not import Tkinter as tk but you keep on using the tk. try importing it
Since you have:
import tkinter
Python looks for tkinter not tk since you have not told python that you want to use the module with the alias 'tk'
Your solution is:
import tkinter as tk
You probably forgot that you were using tkinter instead of tk.
The problem is from your import statement, you imported tkinter as ttk but you kept on using tk

Starting another script from button press on tkinter

so I am trying to create a button on tkinter that runs another script. I'm not sure whether this is the most optimal way rather than just adding whatever I have onto the script I want to run but I want to see if this is possible for future references. This is what I have on my script so far. Any ideas on how to approach this?
import tkinter as tk
from tkinter import *
from tkinter import simpledialog
import uploadtest as ut
class Initial(simpledialog.Dialog):
def body(self, master):
#input fields for username and passwords
Label(master, text="Scripts").grid(row=1, column=1),
#Buttons
self.utz = ut
self.b1 = Button(master, text = "Script1", bg="grey", command=self.utz)
self.b1.grid(row=7, column=1, ipadx=75)
root = tk.Tk()
root.withdraw()
d = Initial(root)
Any help would be greatly appreciated. I know I can just add this class to the other script and use the button command and it would be easier, but I was just curious if this method was possible since I am going to be adding multiple scripts to this and I would like to have my scripts separated. It seems like when I try to import uploadtest.py on this script, it just runs that script instead of this one.
if you want to use it again for future references you can modify your uploadtest.py to be a function
def ut():
print("your")
print("script")
print("here")
then to use it in the script do from uploadtest import * and call it like a normal function that's in your script
import tkinter as tk
from tkinter import *
from tkinter import simpledialog
from uploadtest import *
class Initial(simpledialog.Dialog):
def body(self, master):
#input fields for username and passwords
Label(master, text="Scripts").grid(row=1, column=1),
#Buttons
self.utz = ut
self.b1 = Button(master, text = "Script1", bg="grey", command=self.utz)
self.b1.grid(row=7, column=1, ipadx=75)
root = tk.Tk()
root.withdraw()
d = Initial(root)
please comment if this is not what your looking for

I am writing a UI for an external program using Tkinter. How could I redirect any print statements to widget in tkinter? Almost like a live log

This is the current UI script I have written...
import tkinter as ttk
import subprocess
import sys
import time
import os
import tkinter.font as font
from tkinter.ttk import *
app = ttk.Tk()
app.geometry("400x400")
app.configure(bg='gray')
photo = ttk.PhotoImage(file=r"C:\Users\ex\ex_button_active.png")
myFont = font.Font(family='Helvetica', size=20, weight='normal')
ttk.Label(app, text='Ex', bg='gray', font=(
'Verdana', 15)).pack(side=ttk.TOP, pady=10)
app.iconbitmap(r'C:\Users\ex\ex_icon.ico')
def ex_activation():
global pro
print("Running program!")
pro = subprocess.Popen("python programex.py", shell=True)
def ex_stop():
global pro
print("Stopping Program... Please Wait!")
os.kill(pro.pid, 0)
ex_activation_button = ttk.Button(app, bg='black', image=photo, width=120, height=120, command=ex_activation)
ex_stop_button = ttk.Button(app, bg='Gray', text='Stop Program', width=12, command=ex_stop, height=3)
ex_stop_button['font'] = myFont
app.title("Ex")
ex_activation_button.pack(side=ttk.TOP)
ex_stop_button.pack(side=ttk.LEFT)
# app.mainloop()
while True:
try:
app.update()
app.update_idletasks()
except KeyboardInterrupt:
pass
The main goal is to take ANY print statements from the external script and have them printed to a tkinter widget line by line. I wanted to use the stdout method. Here is a post I Had Found that May Help Thank you in advance to anyone that can help me achieve this goal.
You could use a ttk.Label() to show the last printed line on your GUI. You could try this:
# Insert a label on your window
lab = ttk.Label(root, text="Some text")
lab.pack()
# Change the label text
def update_label(print_output):
lab.configure(text=print_output)
If you prefer having all the lines, you could use a textarea instead of a Label and append the lines instead of replace them.
To get the program output you could do it in multiple ways:
Overload the python print() function, and add a line to call update_label(text). See more here, but it's a little bit tricky.
Replace all print() to your_print() so you can add the line above in a separate function, like this:
# You will need to make sure to only give one argument, and it should be a string.
def your_print(text):
update_label(text)
print(text)
The last one will come useful if you can't modify the other program, let's say it's already compiled. You could run the output of that program into a file (SomeCommand > SomeFile.txt), and then every second or so, your tkinter app could call update_label(). An example:
## REPLACE root.mainloop() for this:
# These three lines are the equivalent of mainloop()
while True:
root.update()
root.update_idletasks()
with open('somefile.txt') as filein:
last_line = filein.read().split('\n')[-1]
update_label(last_line)

tkinter python program structure

I'm new to python programming, and I have a bit of trouble with the program structure:
When I make my GUI in the main python part then the code works:
import tkinter as tk
root = tk.Tk()
root.overrideredirect(True)
root.geometry("800x480")
def cb_Gebruiker():
btnUser["text"]= "changed"
btnUser = tk.Button(root, text="User",command = cb_Gebruiker)
btnUser.place(x=1,y=1,width="300",height="73")
root.mainloop()
When I make my GUI in a function, the btn variable is local, so this doesn't work
def MakeBtn():
btnUser = tk.Button(root, text="User",command = cb_Gebruiker)
btnUser.place(x=1,y=1,width="300",height="73")
def cb_Gebruiker():
btnUser["text"]= "changed"
MakeBtn()
root.mainloop()
Now I have a program that's rather large and I want my GUI in a separate file, but then I can't access my GUI components...
And I can't seem to be able to find a tutorial on how to structure a program (python has to many possibilities: script, module, object oriented,..)
How do I solve this?
You will need a lambda to delay the call to the command with a parameter.
def MakeBtn():
btnUser = tk.Button(root, text="User", command = lambda: cb_Gebruiker(btnUser))
btnUser.place(x=1, y=1, width="300", height="73")
def cb_Gebruiker(btnUser):
btnUser["text"] = "changed"
MakeBtn()
root.mainloop()

Categories

Resources