'Can we add clickable text or button to terminal/console in python?
For example this is my function:
def get_sum(item1, item2):
print(int(item1) + int(item2))
But I want to trigger that function when user clicks at a certain item in command prompt or any terminal.
Main question:-
Is it possible to run functions by clicking in terminal?
Extra
And I also saw VS-Code extension development output in terminal. when I run yo code it shows very nice thing. And user can select the things with the arrow keys and press enter to run it. So could this also be possible in python?
Thank you!
Solution 1:[1]
edit: you can't trigger functions by clicking stuff in python, but i think you can in c++
I'm gonna be using the curses module, there is also a ported version for windows, but i'm not sure thats gonna work...
This code is a bit complicated
import curses
# define the menu function
def menu(title, classes, color='white'):
# define the curses wrapper
def character(stdscr,):
attributes = {}
# stuff i copied from the internet that i'll put in the right format later
icol = {
1:'red',
2:'green',
3:'yellow',
4:'blue',
5:'magenta',
6:'cyan',
7:'white'
}
# put the stuff in the right format
col = {v: k for k, v in icol.items()}
# declare the background color
bc = curses.COLOR_BLACK
# make the 'normal' format
curses.init_pair(1, 7, bc)
attributes['normal'] = curses.color_pair(1)
# make the 'highlighted' format
curses.init_pair(2, col[color], bc)
attributes['highlighted'] = curses.color_pair(2)
# handle the menu
c = 0
option = 0
while c != 10:
stdscr.erase() # clear the screen (you can erase this if you want)
# add the title
stdscr.addstr(f"{title}\n", curses.color_pair(1))
# add the options
for i in range(len(classes)):
# handle the colors
if i == option:
attr = attributes['highlighted']
else:
attr = attributes['normal']
# actually add the options
stdscr.addstr(f'> ', attr)
stdscr.addstr(f'{classes[i]}' + '\n', attr)
c = stdscr.getch()
# handle the arrow keys
if c == curses.KEY_UP and option > 0:
option -= 1
elif c == curses.KEY_DOWN and option < len(classes) - 1:
option += 1
return option
return curses.wrapper(character)
that was the menu function, now here's an example of how to use the function
print(f"output:", menu('TEST', ['this will return 0','this will return 1', 'this is just to show that you can do more options then just two'], 'blue'))
the syntax of the function goes like this
menu('title', ['options', 'optionssssssss'], 'color (optional)')
here are the available colors:
red
green
yellow
blue
magenta
cyan
white
the output of the example code looks like this:
you can install the version for windows using this command: python -m pip install windows-curses
but i don't think it even works with python 3
also sorry if this post is bad, its my first answer on stackoverflow
Solution 2:[2]
this can help somebody
from tkinter import colorchooser,Tk,messagebox
from colored import fg,bg,attr
import win32console
import threading
import win32gui
import win32api
import colorama
import atexit
import mouse
import time
import sys
import os
class console:
def __init__(self):
self._is_enabled = False
self._last_pos = (0,0)
self.hwnd = win32console.GetConsoleWindow()
atexit.register(lambda:self.set_input(True))
def _mouse_thread(self):
while True:
if self.get_mouse_position()[1] > 30:
if self.is_input():
self._set_input(False)
else:
if not self.is_input():
self._set_input(True)
def _focus_thread(self):
while True:
if win32gui.GetForegroundWindow() != self.hwnd:
win32gui.SetForegroundWindow(self.hwnd)
def run_focus_thread(self):
threading.Thread(target=self._focus_thread,daemon=1).start()
def run_moveable_thread(self):
threading.Thread(target=self._mouse_thread,daemon=1).start()
def get_position(self):
return win32gui.GetWindowRect(self.hwnd)[:2]
def get_mouse_position(self):
try:
pos = win32gui.GetCursorPos()
self._last_pos = pos
except:
pos = self._last_pos
return (pos[0]-self.get_position()[0]-7,
pos[1]-self.get_position()[1]-30)
def _set_input(self,value):
win32gui.EnableWindow(self.hwnd,value)
def set_input(self,value):
self._is_enabled = value
win32gui.EnableWindow(self.hwnd,value)
def is_input(self):
return win32gui.IsWindowEnabled(self.hwnd)
def set_focus(self):
win32gui.SetForegroundWindow(self.hwnd)
win32gui.SetFocus(self.hwnd)
def is_focus(self):
return win32gui.GetForegroundWindow() == self.hwnd and \
self.get_mouse_position()[1] >= 30 and \
self.get_mouse_position()[0] > 0 and \
self.get_mouse_position()[0] <= self.get_size()[0]-7 and \
self.get_mouse_position()[1] <= self.get_size()[1]
def get_size(self):
return (win32gui.GetWindowRect(self.hwnd)[2]-7,
win32gui.GetWindowRect(self.hwnd)[3]-30)
def set_size(self,w,h):
win32gui.MoveWindow(self.hwnd,*self.get_position(),int(w),int(h),True)
def set_position(self,x,y):
win32gui.MoveWindow(self.hwnd,int(x),int(y),*self.get_size(),True)
def set_rect(self,x,y,w,h):
win32gui.MoveWindow(self.hwnd,int(x),int(y),int(w),int(h),True)
def set_center(self):
self.set_position(get_screen_size()[0]/2-self.get_size()[0]/2,
get_screen_size()[1]/2-self.get_size()[1]/2)
def set_title(self,title):
win32gui.SetWindowText(self.hwnd,title)
console = console()
def get_screen_size():
return (win32api.GetSystemMetrics(0),
win32api.GetSystemMetrics(1))
font_size = [8.2,15.5]
class canvas:
def __init__(self,width,height):
self.sp = (width*font_size[0],height*font_size[1])
self.width, self.height = width, height
self.changed = True
self.pixels = [[" " for i in range(self.width)] for i in range(self.height)]
console.set_size(self.sp[0]+30,self.sp[1]+10)
def clear(self):
# os.system("cls")
print("\033[H\033[J",end="")
def fill(self,symbol=" ",update=True):
self.pixels = [[symbol for i in range(self.width)] for i in range(self.height)]
self.changed = True
if update: self.update()
def update(self):
self.changed = False
out = ""
for y in self.pixels:
for x in y:
out += x
out += "\n"
self.clear()
print(out,end="")
def set(self,x,y,symbol,update=True):
if x < self.width and y < self.height:
self.pixels[y][x] = symbol
self.changed = True
if update: self.update()
def write(self,x,y,text,update=True):
self.changed = True
for n,s in enumerate(text):
self.set(x+n,y,s,False)
if update: self.update()
def get(self,x,y):
return self.pixels[y][x]
def all(self):
return self.pixels
def get_mouse_position(self):
return (console.get_mouse_position()[0]/self.sp[0]*self.width,
(console.get_mouse_position()[1]/(self.sp[1]-font_size[1]*4)*self.height))
def color_symbol(color):
return fg(color)+bg(color)+"#"+attr('reset')
# ------- create canvas -------
console.set_title("hello world button") # set console title
console.run_moveable_thread() # run thread that prohibits clicking on the console
win = canvas(64,34) # creating canvas
# ------- create buttons -------
btns = []
def btn(text,x,y,cb):
btns.append([text,x,y,cb,False])
def btncallback():
Tk().withdraw()
messagebox.showinfo("hello world","Hello World!")
btn("click me!",25,15,btncallback)
# ------- create main view -------
for i in btns:
win.set(i[1],i[2],i[0][0],False)
win.write(i[1]+1,i[2],i[0][1:][:-1],False)
win.set(i[1]+len(i[0])-1,i[2],i[0][-1],False)
win.update()
# ------- main loop -------
run = True
while run:
if win.changed:
win.update()
if console.is_focus():
pos = win.get_mouse_position()
pos = (round(pos[0]),round(pos[1]))
if mouse.is_pressed("left"):
for i in btns:
if pos[0] >= i[1] and pos[0] <= i[1]+len(i[0]) \
and pos[1] == i[2]+1 and not i[4]:
win.set(i[1],i[2],fg("black")+bg("white")+i[0][0],False)
win.write(i[1]+1,i[2],i[0][1:][:-1],False)
win.set(i[1]+len(i[0])-1,i[2],i[0][-1]+attr("reset"),False)
i[4] = True
else:
for i in btns:
if i[4]:
win.set(i[1],i[2],i[0][0],False)
win.write(i[1]+1,i[2],i[0][1:][:-1],False)
win.set(i[1]+len(i[0])-1,i[2],i[0][-1],False)
i[4] = False
i[3]()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
