Итак, у меня есть несколько проблем с кодом в разделе:
run = False
def press():
global run
while True:
if keyboard.read_key(hotCombo.get()):
print("You Pressed It")
run = not run
keyboard.wait(hotCombo.get())
if run == True:
status["text"] = "Working"
else:
status["text"] = "Not Working"
def process():
while run == True:
print("runnning")
Повозился с ним и нашел больше проблем
Я закончил с этим, но пока он печатает run
, я не могу его остановить
def process():
global run
while True:
if keyboard.read_key(hotCombo.get()):
print("kijanbdsjokn")
run = not run
keyboard.wait(hotCombo.get())
if run == True:
status["text"] = "Working"
else:
status["text"] = "Not Working"
while run == True:
print("run")
time.sleep(1)
Это не похоже на настоящую tkinter
программу. Добавление собственного цикла событий внутри (как выглядит) обратного вызова, как правило, не очень хорошая идея.
@RolandSmith Да, несколько человек сказали мне, что в разногласиях по python сказали мне это, но на самом деле они не указали мне правильный путь. Могу я спросить, почему я не могу просто интегрировать tkinter в работающий скрипт python, используя потоки?
@TimRoberts Нет, я на самом деле нигде не вызываю autofisher, я просто предположил, что то, что внутри, будет работать, потому что функция press
запускается без необходимости ее вызова. Есть ли способ запустить его только при нажатии горячей клавиши, а не внутри while True
@OGXirvin Ответ на этот вопрос настолько длинный, что я превратил его в ответ ниже. :-)
press
не будет работать, если вы его не вызовете. Я гарантирую это. Либо вы его вызываете, либо передаете в качестве параметра какой-либо другой функции, которая его вызывает.
@TimRoberts Я не уверен, но я использую потоки, вызывает ли это функцию?
Если делать x = threading.Thread(target=press)
и x.start()
, то да, конечно, будет вызываться.
@TimRoberts да, у меня есть эта настройка, вы можете увидеть весь мой код в stackoverflow.com/questions/71543616/…
Один из способов обработки вашего ключа — превратить его в двухфазный цикл:
def press():
global run
while True:
while not keyboard.read_key(hotCombo.get()):
time.sleep(0.2)
run = True
status["text"] = "Working"
while keyboard.read_key(hotCombo.get()):
print("running")
time.sleep(0.2)
run == False
status["text"] = "Not Working"
Вообще не работает, лол, также я не уверен, что это что-то с моим tkinter, но любая клавиша, которую я нажимаю, заканчивается печатью runnning
Также это не работает, потому что я хочу, чтобы запуск переключался только с помощью горячей клавиши, и часть, которую вы написали, печатается runnning
каждый раз, когда я нажимаю горячую клавишу
Can I ask why I cant just integrate tkinter into a working python script using threading?
Сценарий Python обычно линейный. Вы делаете что-то последовательно, а затем уходите.
В программе tkinter
ваш код состоит из трех вещей.
tkinter
, когда они находятся в mainloop
.Таким образом, в tkinter
программе большая часть вашего кода представляет собой гость в mainloop
. Где он исполняется небольшими кусочками в ответ на события. Это совсем другая программа. Это называлось программированием событийный или сообщение на основе задолго до того, как это стало крутым в веб-серверах и фреймворках.
Итак, можете ли вы интегрировать скрипт в программу tkinter
? Да, это возможно.
Есть в основном три способа сделать это;
after
тайм-ауты. Это включает в себя наибольшую реорганизацию вашего кода. Чтобы графический интерфейс оставался отзывчивым, обработчики событий (например, тайм-ауты) не должны занимать слишком много времени; 50 мс кажется разумным верхним пределом.threading.Thread
и multiprocessing.Process
почти одинаковы по дизайну). Самая большая разница заключается в том, что связь между процессами должна осуществляться явно, например, через Queue
или Pipe
.Есть некоторые вещи, которые вы должны учитывать при использовании дополнительных потоков, особенно в программе tkinter
.
1) версия Python
Вам нужно использовать Python 3. Это не будет хорошо работать в Python 2 по причинам, которые выходят за рамки этого ответа. Лучшее вытеснение потоков в Python 3 — большая часть этого.
2) Многопоточная сборка tkinter
tkinter
(или, скорее, базовый интерпретатор tcl
) должен быть построен с включенной поддержкой многопоточности. Я так понимаю, что официальные python.org
сборки для ms-windows есть, но кроме YMMV. В некоторых UNIX-подобных системах, таких как Linux или *BSD, системы пакетов/портов предоставляют вам выбор.
3) Превратите свой код в функцию
Вам нужно обернуть ядро вашего исходного скрипта в функцию, чтобы вы могли запускать его в потоке.
4) Сделайте функцию удобной для потоков
Вы, вероятно, захотите иметь возможность прерывать для этого потока, если это занимает слишком много времени. Таким образом, вы должны адаптировать его, чтобы регулярно проверять, должно ли оно продолжаться. Проверка того, является ли глобальный объект с именем run
True
, является одним из методов. Обратите внимание, что API threading
позволяет нет просто завершить поток.
5 Обычные опасности многопоточности
Вы должны быть осторожны с одновременным изменением виджетов или глобальных переменных из обоих потоков.
На момент написания этой статьи вам помогал Python GIL. Поскольку это гарантирует, что только один поток за раз выполняет байт-код Python, любое изменение, которое может быть сделано в одном байт-коде, является безопасным для многопоточности в качестве побочного эффекта.
Например, посмотрите на модификацию глобальной функции modify
:
In [1]: import dis
In [2]: data = []
Out[2]: []
In [3]: def modify():
...: global data
...: newdata = [1,2,3]
...: data = newdata
...:
In [4]: dis.dis(modify)
3 0 BUILD_LIST 0
2 LOAD_CONST 1 ((1, 2, 3))
4 LIST_EXTEND 1
6 STORE_FAST 0 (newdata)
4 8 LOAD_FAST 0 (newdata)
10 STORE_GLOBAL 0 (data)
12 LOAD_CONST 0 (None)
14 RETURN_VALUE
Посмотрите, как новый список строится отдельно, и только когда он завершен, он назначается глобальному. (Это было не случайно.)
Требуется всего одна инструкция байт-кода (STORE_GLOBAL
), чтобы установить глобальную переменную во вновь созданный список. Таким образом, ни в коем случае значение data
не может быть двусмысленным.
Но многие вещи требуют более одного байт-кода. Таким образом, есть шанс, что один поток будет вытеснен другим, пока он модифицирует переменную или виджет. Насколько велик этот шанс, зависит от того, как часто такие ситуации случаются и сколько времени они занимают.
IIRC, в настоящее время поток прерывается каждые 15 мс. Таким образом, изменение, которое занимает больше времени, является гарантировано для вытеснения. Как и любая задача, которая отбрасывает GIL для ввода-вывода.
Поэтому, если вы видите, что происходят странные вещи, обязательно используйте Lock
для регулирования доступа к общим ресурсам.
Это помогает, если, например. виджет или переменная — это только модифицированный из одна нить и только читать из всех остальных потоков.
Я предполагаю, что вы звоните
autofisher
куда-то. Подумайте о том, что происходит, когда вы это делаете. Посколькуrun
имеет значение False, цикл немедленно завершится, и функция вернется. Вам нужно было бы иметь это в отдельном потоке, чтобы он работал, и вам нужно, чтобы он былwhile True:
, чтобы работать вечно. Что, конечно, ужасная идея, потому что она сожрет 100% процессора. Никакой другой код Python не будет работать.