Search code examples
python-3.xmultithreadingimageclipboardstreamlit

How to use different threads in Streamlit to use the OS buffer response from a function to another in same trigger


I'm trying to copy an image to the Windows clipboard and I do it successfully. I also need to open a website, say for example Google using Selenium in python in Streamlit.

I need that just by pressing a button I copied an image to the clipboard and paste it in the right place using Selenium.

This is the model using ThreadPoolExecutor it doesn't work.

import time
import streamlit as st
import win32clipboard
import re
from PIL import Image
from base64 import b64decode
from io import BytesIO
import pandas as pd

from whats import Cliente
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import psutil
from streamlit_quill import st_quill as text_editor
from st_aggrid import AgGrid, DataReturnMode, GridUpdateMode, GridOptionsBuilder
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from concurrent.futures import ThreadPoolExecutor

def send_to_clipboard(img_path):
    image = Image.open(img_path)#path
    output = BytesIO()
    image.convert("RGB").save(output, "BMP")
    data = output.getvalue()[14:]
    #print(data)
    output.close()
    win32clipboard.OpenClipboard()
    win32clipboard.EmptyClipboard()
    win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)

def enviar_msg():
    opts = Options()
    opts.add_argument("--user-data-dir=C:\...\\AppData\\Local\\Google\\Chrome\\User Data\\Profile 4")
    driver = webdriver.Chrome(options=opts)#options=opts ---headless
    driver.get('https://google.com/')
    driver.maximize_window()
    wait = WebDriverWait(driver, 60)

    enviar = wait.until(EC.presence_of_element_located((By.XPATH,'//*[@id="main"]')))
    #TEXT_BOX
    enviar.send_keys('')

    time.sleep(5)

    actions = ActionChains(driver)
    actions.key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()

pool = ThreadPoolExecutor(max_workers=2)

with st.container():
    if st.button('Enviar'):
        pool.submit(send_to_clipboard, 'imagem-0.png')
        pool.submit(enviar_msg)

this 'works' but is not what I'm looking for

import time
import streamlit as st
import win32clipboard
from PIL import Image
from io import BytesIO
import strings as literais
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


st.set_page_config(
     page_title='MKT',
     layout='wide',
     initial_sidebar_state='expanded',
     page_icon=literais.apple_dragon_icon, #"favicon.png" #expanded / collapsed
     menu_items={
         'Get help': 'https://github.com/jvcss',
         'Report a bug': "https://github.com/jvcss",
         'About': "App para automação whatsapp"
    }
)

def send_to_clipboard(img_path):
    image = Image.open(img_path)#path
    output = BytesIO()
    image.convert("RGB").save(output, "BMP")
    data = output.getvalue()[14:]
    #print(data)
    output.close()
    win32clipboard.OpenClipboard()
    win32clipboard.EmptyClipboard()
    win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
st.markdown(literais.css_botao_boneca_russa, unsafe_allow_html=True)

with st.container():
    
    if st.button('Enviar'):#I NEED USE ONLY ONE BUTTON!!
        send_to_clipboard(f'imagem-0.png')
        st.info('imagem anexada')

    if st.button('confirma'):
        #send_to_clipboard(f'imagem-0.png') #this should be here! 
        opts = Options()
        opts.add_argument("--user-data-dir=C:\\...")
        driver = webdriver.Chrome(options=opts)#options=opts ---headless
        driver.get('https://google.com/')
        driver.maximize_window()
        wait = WebDriverWait(driver, 60)

        enviar = wait.until(EC.presence_of_element_located((By.XPATH,'//*[@id="main"]')))
        #TEXT_BOX_CHAT
        enviar .send_keys('')

        time.sleep(5)

        actions = ActionChains(driver)
        actions.key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()

the last example you could do by yourself, it's just this one above using only one button.


Solution

  • I found an alternative way, the on_click property of the st.button is executed every time the python script is executed. so it is possible to have this trigger to capture some information before the button is actually pressed and the content within the context of its response will be executed as well. now is to think of a way to prevent the re-activation of this function in on_click when the button is actually pressed and not just a routine update. Maybe the best method is with a local variable bool I'll still think about it but the main question now has more than one way to be solved unfortunately not in the way I wanted.

    with st.container():
        
        if st.button('Enviar', on_click=send_to_clipboard('imagem-0.png')):
            #build a logic to 
            st.info('imagem anexada')
    
        if st.button('confirma',):
            st.info('Executando Chrome')
            ui_automation()